From 8cec498f1b4a54af6681c76513ba1daa86b105ad Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 14 Jul 2025 09:42:53 +0000 Subject: [PATCH 001/225] bitwise determinism FJ progress --- .../mip/feasibility_jump/feasibility_jump.cu | 66 ++++----- .../mip/feasibility_jump/feasibility_jump.cuh | 2 +- .../feasibility_jump_kernels.cu | 40 +++-- cpp/tests/mip/feasibility_jump_tests.cu | 139 +++++++++++------- 4 files changed, 145 insertions(+), 102 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 5043d77801..93738d9442 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -625,11 +625,11 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream bool use_graph) { raft::common::nvtx::range scope("run_step_device"); - auto [grid_setval, blocks_setval] = setval_launch_dims; - auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; - auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; - auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; - auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; + // auto [grid_setval, blocks_setval] = setval_launch_dims; + // auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; + // auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; + // auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; + // auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; auto& data = *climbers[climber_idx]; auto v = data.view(); @@ -699,16 +699,16 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream if (is_binary_pb) { cudaLaunchCooperativeKernel( (void*)compute_mtm_moves_kernel, - grid_resetmoves_bin, - blocks_resetmoves_bin, + dim3(256), + dim3(256), reset_moves_args, 0, climber_stream); } else { cudaLaunchCooperativeKernel( (void*)compute_mtm_moves_kernel, - grid_resetmoves, - blocks_resetmoves, + dim3(256), + dim3(256), reset_moves_args, 0, climber_stream); @@ -731,18 +731,18 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream } #endif - cudaLaunchKernel((void*)update_lift_moves_kernel, - grid_lift_move, - blocks_lift_move, - kernel_args, - 0, - climber_stream); - cudaLaunchKernel((void*)update_breakthrough_moves_kernel, - grid_lift_move, - blocks_lift_move, - kernel_args, - 0, - climber_stream); + // cudaLaunchKernel((void*)update_lift_moves_kernel, + // grid_lift_move, + // blocks_lift_move, + // kernel_args, + // 0, + // climber_stream); + // cudaLaunchKernel((void*)update_breakthrough_moves_kernel, + // grid_lift_move, + // blocks_lift_move, + // kernel_args, + // 0, + // climber_stream); } // compaction kernel @@ -755,32 +755,24 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream pb_ptr->n_variables, climber_stream); - cudaLaunchKernel((void*)select_variable_kernel, - dim3(1), - dim3(256), - kernel_args, - 0, - climber_stream); + cudaLaunchKernel( + (void*)select_variable_kernel, dim3(1), dim3(1), kernel_args, 0, climber_stream); cudaLaunchCooperativeKernel((void*)handle_local_minimum_kernel, - grid_update_weights, - blocks_update_weights, + dim3(256), + dim3(256), kernel_args, 0, climber_stream); raft::copy(data.break_condition.data(), data.temp_break_condition.data(), 1, climber_stream); cudaLaunchKernel((void*)update_assignment_kernel, - grid_setval, - blocks_setval, + dim3(256), + dim3(256), update_assignment_args, 0, climber_stream); - cudaLaunchKernel((void*)update_changed_constraints_kernel, - 1, - blocks_setval, - kernel_args, - 0, - climber_stream); + cudaLaunchKernel( + (void*)update_changed_constraints_kernel, 1, 1, kernel_args, 0, climber_stream); } if (use_graph) { diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh index a55f115f19..5bc3b1258b 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh @@ -30,7 +30,7 @@ #include #define FJ_DEBUG_LOAD_BALANCING 0 -#define FJ_SINGLE_STEP 0 +#define FJ_SINGLE_STEP 1 namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 213d31bc5a..58483c3345 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -729,6 +729,31 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t // Update the LHSs of all involved constraints. auto [offset_begin, offset_end] = fj.pb.reverse_range_for_var(var_idx); + if (blockIdx.x == 0) { + for (auto i = offset_begin; i < offset_end; i += 1) { + cuopt_assert(i < (i_t)fj.pb.reverse_constraints.size(), ""); + auto cstr_idx = fj.pb.reverse_constraints[i]; + auto cstr_coeff = fj.pb.reverse_coefficients[i]; + f_t old_lhs = fj.incumbent_lhs[cstr_idx]; + f_t new_lhs = old_lhs + cstr_coeff * fj.jump_move_delta[var_idx]; + f_t old_cost = fj.excess_score(cstr_idx, old_lhs); + f_t new_cost = fj.excess_score(cstr_idx, new_lhs); + + if (threadIdx.x == 0) { + cuopt_assert(*fj.constraints_changed_count >= 0 && + *fj.constraints_changed_count <= fj.pb.n_constraints, + ""); + f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx); + if (new_cost < -cstr_tolerance && !fj.violated_constraints.contains(cstr_idx)) + fj.constraints_changed[atomicAdd(fj.constraints_changed_count, 1)] = + (cstr_idx << 1) | CONSTRAINT_FLAG_INSERT; + else if (!(new_cost < -cstr_tolerance) && fj.violated_constraints.contains(cstr_idx)) + fj.constraints_changed[atomicAdd(fj.constraints_changed_count, 1)] = + cstr_idx << 1 | CONSTRAINT_FLAG_REMOVE; + } + } + } + for (auto i = offset_begin + blockIdx.x; i < offset_end; i += gridDim.x) { cuopt_assert(i < (i_t)fj.pb.reverse_constraints.size(), ""); @@ -740,19 +765,6 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t f_t old_cost = fj.excess_score(cstr_idx, old_lhs); f_t new_cost = fj.excess_score(cstr_idx, new_lhs); - if (threadIdx.x == 0) { - cuopt_assert( - *fj.constraints_changed_count >= 0 && *fj.constraints_changed_count <= fj.pb.n_constraints, - ""); - f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx); - if (new_cost < -cstr_tolerance && !fj.violated_constraints.contains(cstr_idx)) - fj.constraints_changed[atomicAdd(fj.constraints_changed_count, 1)] = - (cstr_idx << 1) | CONSTRAINT_FLAG_INSERT; - else if (!(new_cost < -cstr_tolerance) && fj.violated_constraints.contains(cstr_idx)) - fj.constraints_changed[atomicAdd(fj.constraints_changed_count, 1)] = - cstr_idx << 1 | CONSTRAINT_FLAG_REMOVE; - } - __syncthreads(); cuopt_assert(isfinite(fj.jump_move_delta[var_idx]), "delta should be finite"); @@ -1076,6 +1088,8 @@ DI void update_changed_constraints(typename fj_t::climber_data_t::view if (blockIdx.x == 0) { if (threadIdx.x == 0) { + // sort changed constraints to guarantee determinism + for (i_t i = 0; i < *fj.constraints_changed_count; ++i) { i_t idx = fj.constraints_changed[i]; if ((idx & 1) == CONSTRAINT_FLAG_INSERT) { diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index 1fafb830e6..3b1ed98a71 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -182,6 +183,32 @@ static bool run_fj_check_objective(std::string test_instance, int iter_limit, do return !solution.get_feasible() ? false : solution.get_user_objective() <= obj_target; } +static bool run_fj_check_determinism(std::string test_instance, int iter_limit) +{ + detail::fj_settings_t fj_settings; + fj_settings.time_limit = 30.; + fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; + fj_settings.n_of_minimums_for_exit = 20000 * 1000; + fj_settings.update_weights = true; + fj_settings.feasibility_run = false; + fj_settings.termination = detail::fj_termination_flags_t::FJ_TERMINATION_ITERATION_LIMIT; + fj_settings.iteration_limit = iter_limit; + fj_settings.load_balancing_mode = detail::fj_load_balancing_mode_t::ALWAYS_OFF; + fj_settings.seed = 42; + cuopt::seed_generator::set_seed(42); + + auto state = run_fj(test_instance, fj_settings); + auto& solution = state.solution; + + CUOPT_LOG_DEBUG("%s: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", + test_instance.c_str(), + solution.get_feasible(), + solution.get_user_objective(), + solution.get_objective()); + + return true; +} + static bool run_fj_check_feasible(std::string test_instance) { detail::fj_settings_t fj_settings; @@ -220,59 +247,69 @@ static bool run_fj_check_feasible(std::string test_instance) return true; } -TEST(mip_solve, feasibility_jump_obj_test) +// TEST(mip_solve, feasibility_jump_obj_test) +// { +// std::vector> test_cases = { +// {"50v-10.mps", 7800, 100000}, +// {"fiball.mps", 140, 25000}, +// {"gen-ip054.mps", 7500, 20000}, +// {"sct2.mps", 100, 50000}, +// {"uccase9.mps", 4000000, 50000}, +// // unstable, prone to failure on slight weight changes +// //{"drayage-25-23.mps", 300000, 50000}, +// {"tr12-30.mps", 300000, 50000}, +// {"neos-3004026-krka.mps", +std::numeric_limits::infinity(), 35000}, // feasibility +// //{"nursesched-medium-hint03.mps", 12000, 50000}, // too large +// {"ns1208400.mps", 2, 60000}, +// {"gmu-35-50.mps", -2300000, 25000}, +// {"n2seq36q.mps", 158800, 25000}, +// {"seymour1.mps", 440, 50000}, +// {"rmatr200-p5.mps", 7000, 10000}, +// {"cvs16r128-89.mps", -50, 10000}, +// // TEMPORARY: occasional cusparse transpose issues on ARM in CI +// #ifndef __aarch64__ +// {"thor50dday.mps", 250000, 1000} +// #endif +// }; + +// for (auto [instance, obj_target, iter_limit] : test_cases) { +// bool result = run_fj_check_objective(instance, iter_limit, obj_target); +// // Abort early +// if (!result) { +// printf("failure"); +// exit(0); +// } +// } +// } + +// TEST(mip_solve, feasibility_jump_feas_test) +// { +// for (const auto& instance : {"tr12-30.mps", +// "sct2.mps" +// #ifndef __aarch64__ +// , +// "thor50dday.mps" +// #endif +// }) { +// run_fj_check_feasible(instance); +// } +// } + +// TEST(mip_solve, feasibility_jump_obj_runoff_test) +// { +// for (const auto& instance : {"minrep_inf.mps", "sct2.mps", "uccase9.mps", +// /*"buildingenergy.mps"*/}) { +// run_fj_check_no_obj_runoff(instance); +// } +// } + +TEST(mip_solve, feasibility_jump_determinism) { - std::vector> test_cases = { - {"50v-10.mps", 7800, 100000}, - {"fiball.mps", 140, 25000}, - {"gen-ip054.mps", 7500, 20000}, - {"sct2.mps", 100, 50000}, - {"uccase9.mps", 4000000, 50000}, - // unstable, prone to failure on slight weight changes - //{"drayage-25-23.mps", 300000, 50000}, - {"tr12-30.mps", 300000, 50000}, - {"neos-3004026-krka.mps", +std::numeric_limits::infinity(), 35000}, // feasibility - //{"nursesched-medium-hint03.mps", 12000, 50000}, // too large - {"ns1208400.mps", 2, 60000}, - {"gmu-35-50.mps", -2300000, 25000}, - {"n2seq36q.mps", 158800, 25000}, - {"seymour1.mps", 440, 50000}, - {"rmatr200-p5.mps", 7000, 10000}, - {"cvs16r128-89.mps", -50, 10000}, - // TEMPORARY: occasional cusparse transpose issues on ARM in CI -#ifndef __aarch64__ - {"thor50dday.mps", 250000, 1000} -#endif - }; - - for (auto [instance, obj_target, iter_limit] : test_cases) { - bool result = run_fj_check_objective(instance, iter_limit, obj_target); - // Abort early - if (!result) { - printf("failure"); - exit(0); - } - } -} - -TEST(mip_solve, feasibility_jump_feas_test) -{ - for (const auto& instance : {"tr12-30.mps", - "sct2.mps" -#ifndef __aarch64__ - , - "thor50dday.mps" -#endif - }) { - run_fj_check_feasible(instance); - } -} - -TEST(mip_solve, feasibility_jump_obj_runoff_test) -{ - for (const auto& instance : {"minrep_inf.mps", "sct2.mps", "uccase9.mps", + for (const auto& instance : {"gen-ip054.mps", "50v-10.mps", /*"buildingenergy.mps"*/}) { - run_fj_check_no_obj_runoff(instance); + for (int i = 0; i < 10; i++) { + run_fj_check_determinism(instance, 200); + } } } From a559d79d00d71fcb7433fcd6ff9cdc371a368476 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 15 Jul 2025 05:34:09 -0700 Subject: [PATCH 002/225] FJ determinism for non balanced codepath --- .../mip/feasibility_jump/feasibility_jump.cu | 99 +++++++++++++------ .../mip/feasibility_jump/feasibility_jump.cuh | 2 +- .../feasibility_jump_kernels.cu | 55 ++++++----- cpp/src/mip/feasibility_jump/utils.cuh | 12 +++ cpp/src/mip/utils.cuh | 17 ++++ cpp/tests/mip/feasibility_jump_tests.cu | 23 +++-- 6 files changed, 146 insertions(+), 62 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 93738d9442..5861955007 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -413,6 +413,11 @@ void fj_t::climber_init(i_t climber_idx, const rmm::cuda_stream_view& climber->violation_score.set_value_to_zero_async(climber_stream); climber->weighted_violation_score.set_value_to_zero_async(climber_stream); init_lhs_and_violation<<<256, 256, 0, climber_stream.value()>>>(view); + climber->violated_constraints.sort(climber_stream); + + // printf("init: Violated constraints hash: %x\n", compute_hash( + // make_span(climber->violated_constraints.contents, 0, + // climber->violated_constraints.set_size.value(climber_stream)), climber_stream)); // initialize the best_objective values according to the initial assignment f_t best_obj = compute_objective_from_vec( @@ -625,11 +630,11 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream bool use_graph) { raft::common::nvtx::range scope("run_step_device"); - // auto [grid_setval, blocks_setval] = setval_launch_dims; - // auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; - // auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; - // auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; - // auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; + auto [grid_setval, blocks_setval] = setval_launch_dims; + auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; + auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; + auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; + auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; auto& data = *climbers[climber_idx]; auto v = data.view(); @@ -681,6 +686,10 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream data.cub_storage_bytes.resize(compaction_temp_storage_bytes, climber_stream); } + // printf("before step: Violated constraints hash: %x\n", compute_hash( + // make_span(data.violated_constraints.contents, 0, + // data.violated_constraints.set_size.value(climber_stream)), climber_stream)); + if (use_graph) { cudaGraphCreate(&graph, 0); cudaStreamBeginCapture(climber_stream, cudaStreamCaptureModeThreadLocal); @@ -699,16 +708,16 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream if (is_binary_pb) { cudaLaunchCooperativeKernel( (void*)compute_mtm_moves_kernel, - dim3(256), - dim3(256), + grid_resetmoves_bin, + blocks_resetmoves_bin, reset_moves_args, 0, climber_stream); } else { cudaLaunchCooperativeKernel( (void*)compute_mtm_moves_kernel, - dim3(256), - dim3(256), + grid_resetmoves, + blocks_resetmoves, reset_moves_args, 0, climber_stream); @@ -731,18 +740,18 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream } #endif - // cudaLaunchKernel((void*)update_lift_moves_kernel, - // grid_lift_move, - // blocks_lift_move, - // kernel_args, - // 0, - // climber_stream); - // cudaLaunchKernel((void*)update_breakthrough_moves_kernel, - // grid_lift_move, - // blocks_lift_move, - // kernel_args, - // 0, - // climber_stream); + cudaLaunchKernel((void*)update_lift_moves_kernel, + grid_lift_move, + blocks_lift_move, + kernel_args, + 0, + climber_stream); + cudaLaunchKernel((void*)update_breakthrough_moves_kernel, + grid_lift_move, + blocks_lift_move, + kernel_args, + 0, + climber_stream); } // compaction kernel @@ -755,24 +764,53 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream pb_ptr->n_variables, climber_stream); - cudaLaunchKernel( - (void*)select_variable_kernel, dim3(1), dim3(1), kernel_args, 0, climber_stream); - + cudaLaunchKernel((void*)select_variable_kernel, + dim3(1), + dim3(256), + kernel_args, + 0, + climber_stream); cudaLaunchCooperativeKernel((void*)handle_local_minimum_kernel, - dim3(256), - dim3(256), + grid_update_weights, + blocks_update_weights, kernel_args, 0, climber_stream); raft::copy(data.break_condition.data(), data.temp_break_condition.data(), 1, climber_stream); cudaLaunchKernel((void*)update_assignment_kernel, - dim3(256), - dim3(256), + grid_setval, + blocks_setval, update_assignment_args, 0, climber_stream); - cudaLaunchKernel( - (void*)update_changed_constraints_kernel, 1, 1, kernel_args, 0, climber_stream); + + // { + // printf("Changed constraints hash: %x, size: %d\n", compute_hash( + // make_span(data.constraints_changed, 0, + // data.constraints_changed_count.value(climber_stream)), climber_stream), + // data.constraints_changed_count.value(climber_stream)); + + // printf("before update: Violated constraints hash: %x, size: %d\n", compute_hash( + // make_span(data.violated_constraints.contents, 0, + // data.violated_constraints.set_size.value(climber_stream)), climber_stream), + // data.violated_constraints.set_size.value(climber_stream)); + // } + + cudaLaunchKernel((void*)update_changed_constraints_kernel, + 1, + blocks_setval, + kernel_args, + 0, + climber_stream); + + // data.violated_constraints.sort(climber_stream); + + // { + // printf("Violated constraints hash: %x, size: %d\n", compute_hash( + // make_span(data.violated_constraints.contents, 0, + // data.violated_constraints.set_size.value(climber_stream)), climber_stream), + // data.violated_constraints.set_size.value(climber_stream)); + // } } if (use_graph) { @@ -815,6 +853,7 @@ void fj_t::refresh_lhs_and_violation(const rmm::cuda_stream_view& stre data.violation_score.set_value_to_zero_async(stream); data.weighted_violation_score.set_value_to_zero_async(stream); init_lhs_and_violation<<<4096, 256, 0, stream>>>(v); + data.violated_constraints.sort(stream); } template diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh index 5bc3b1258b..a55f115f19 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh @@ -30,7 +30,7 @@ #include #define FJ_DEBUG_LOAD_BALANCING 0 -#define FJ_SINGLE_STEP 1 +#define FJ_SINGLE_STEP 0 namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 58483c3345..3adc27cabe 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -24,6 +24,8 @@ #include +#include + #include namespace cg = cooperative_groups; @@ -729,31 +731,6 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t // Update the LHSs of all involved constraints. auto [offset_begin, offset_end] = fj.pb.reverse_range_for_var(var_idx); - if (blockIdx.x == 0) { - for (auto i = offset_begin; i < offset_end; i += 1) { - cuopt_assert(i < (i_t)fj.pb.reverse_constraints.size(), ""); - auto cstr_idx = fj.pb.reverse_constraints[i]; - auto cstr_coeff = fj.pb.reverse_coefficients[i]; - f_t old_lhs = fj.incumbent_lhs[cstr_idx]; - f_t new_lhs = old_lhs + cstr_coeff * fj.jump_move_delta[var_idx]; - f_t old_cost = fj.excess_score(cstr_idx, old_lhs); - f_t new_cost = fj.excess_score(cstr_idx, new_lhs); - - if (threadIdx.x == 0) { - cuopt_assert(*fj.constraints_changed_count >= 0 && - *fj.constraints_changed_count <= fj.pb.n_constraints, - ""); - f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx); - if (new_cost < -cstr_tolerance && !fj.violated_constraints.contains(cstr_idx)) - fj.constraints_changed[atomicAdd(fj.constraints_changed_count, 1)] = - (cstr_idx << 1) | CONSTRAINT_FLAG_INSERT; - else if (!(new_cost < -cstr_tolerance) && fj.violated_constraints.contains(cstr_idx)) - fj.constraints_changed[atomicAdd(fj.constraints_changed_count, 1)] = - cstr_idx << 1 | CONSTRAINT_FLAG_REMOVE; - } - } - } - for (auto i = offset_begin + blockIdx.x; i < offset_end; i += gridDim.x) { cuopt_assert(i < (i_t)fj.pb.reverse_constraints.size(), ""); @@ -765,6 +742,19 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t f_t old_cost = fj.excess_score(cstr_idx, old_lhs); f_t new_cost = fj.excess_score(cstr_idx, new_lhs); + if (threadIdx.x == 0) { + cuopt_assert( + *fj.constraints_changed_count >= 0 && *fj.constraints_changed_count <= fj.pb.n_constraints, + ""); + f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx); + if (new_cost < -cstr_tolerance && !fj.violated_constraints.contains(cstr_idx)) + fj.constraints_changed[atomicAdd(fj.constraints_changed_count, 1)] = + (cstr_idx << 1) | CONSTRAINT_FLAG_INSERT; + else if (!(new_cost < -cstr_tolerance) && fj.violated_constraints.contains(cstr_idx)) + fj.constraints_changed[atomicAdd(fj.constraints_changed_count, 1)] = + cstr_idx << 1 | CONSTRAINT_FLAG_REMOVE; + } + __syncthreads(); cuopt_assert(isfinite(fj.jump_move_delta[var_idx]), "delta should be finite"); @@ -1089,6 +1079,10 @@ DI void update_changed_constraints(typename fj_t::climber_data_t::view if (blockIdx.x == 0) { if (threadIdx.x == 0) { // sort changed constraints to guarantee determinism + // TODO: horribly slow as it is + thrust::sort(thrust::seq, + fj.constraints_changed.begin(), + fj.constraints_changed.begin() + *fj.constraints_changed_count); for (i_t i = 0; i < *fj.constraints_changed_count; ++i) { i_t idx = fj.constraints_changed[i]; @@ -1359,6 +1353,14 @@ __global__ void select_variable_kernel(typename fj_t::climber_data_t:: good_var_count); #endif cuopt_assert(fj.jump_move_scores[selected_var].valid(), ""); + } else { +#if FJ_SINGLE_STEP + DEVICE_LOG_INFO("=[%d]---- FJ: no var selected, obj is %g, viol %d, out of %d\n", + *fj.iterations, + *fj.incumbent_objective, + fj.violated_constraints.size(), + good_var_count); +#endif } } } @@ -1469,6 +1471,9 @@ DI thrust::tuple::move_score_t> best_random_mt raft::random::PCGenerator rng(fj.settings->seed + *fj.iterations, 0, 0); i_t cstr_idx = fj.violated_constraints.contents[rng.next_u32() % fj.violated_constraints.size()]; + cuopt_assert(fj.excess_score(cstr_idx, fj.incumbent_lhs[cstr_idx]) < 0, + "constraint isn't violated"); + auto [offset_begin, offset_end] = fj.pb.range_for_constraint(cstr_idx); return gridwide_reduce_best_move( diff --git a/cpp/src/mip/feasibility_jump/utils.cuh b/cpp/src/mip/feasibility_jump/utils.cuh index 212591fc9e..f876e9493b 100644 --- a/cpp/src/mip/feasibility_jump/utils.cuh +++ b/cpp/src/mip/feasibility_jump/utils.cuh @@ -19,6 +19,7 @@ #include "feasibility_jump.cuh" +#include #include #include #include @@ -142,6 +143,17 @@ struct contiguous_set_t { validity_bitmap.resize(size, stream); } + void sort(const rmm::cuda_stream_view& stream) + { + thrust::sort( + rmm::exec_policy(stream), contents.begin(), contents.begin() + set_size.value(stream)); + thrust::fill(rmm::exec_policy(stream), index_map.begin(), index_map.end(), -1); + thrust::for_each(rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(set_size.value(stream)), + [v = view()] __device__(i_t idx) { v.index_map[v.contents[idx]] = idx; }); + } + struct view_t { i_t* set_size; i_t* lock; diff --git a/cpp/src/mip/utils.cuh b/cpp/src/mip/utils.cuh index b48ad21b64..534c67cc7e 100644 --- a/cpp/src/mip/utils.cuh +++ b/cpp/src/mip/utils.cuh @@ -355,4 +355,21 @@ bool has_variable_bounds_violation(const raft::handle_t* handle_ptr, }); } +template +inline uint32_t compute_hash(raft::device_span values, rmm::cuda_stream_view stream) +{ + // FNV-1a hash + + uint32_t hash = 2166136261u; // FNV-1a 32-bit offset basis + auto h_contents = cuopt::host_copy(values, stream); + RAFT_CHECK_CUDA(stream); + std::vector byte_contents(h_contents.size() * sizeof(i_t)); + std::memcpy(byte_contents.data(), h_contents.data(), h_contents.size() * sizeof(i_t)); + for (size_t i = 0; i < byte_contents.size(); ++i) { + hash ^= byte_contents[i]; + hash *= 16777619u; + } + return hash; +} + } // namespace cuopt::linear_programming::detail diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index 3b1ed98a71..0a77347c13 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -185,6 +185,8 @@ static bool run_fj_check_objective(std::string test_instance, int iter_limit, do static bool run_fj_check_determinism(std::string test_instance, int iter_limit) { + int seed = std::getenv("FJ_SEED") ? std::stoi(std::getenv("FJ_SEED")) : 42; + detail::fj_settings_t fj_settings; fj_settings.time_limit = 30.; fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; @@ -194,8 +196,8 @@ static bool run_fj_check_determinism(std::string test_instance, int iter_limit) fj_settings.termination = detail::fj_termination_flags_t::FJ_TERMINATION_ITERATION_LIMIT; fj_settings.iteration_limit = iter_limit; fj_settings.load_balancing_mode = detail::fj_load_balancing_mode_t::ALWAYS_OFF; - fj_settings.seed = 42; - cuopt::seed_generator::set_seed(42); + fj_settings.seed = seed; + cuopt::seed_generator::set_seed(fj_settings.seed); auto state = run_fj(test_instance, fj_settings); auto& solution = state.solution; @@ -206,6 +208,10 @@ static bool run_fj_check_determinism(std::string test_instance, int iter_limit) solution.get_user_objective(), solution.get_objective()); + static auto first_val = solution.get_user_objective(); + + if (abs(solution.get_user_objective() - first_val) > 1) exit(0); + return true; } @@ -305,10 +311,15 @@ static bool run_fj_check_feasible(std::string test_instance) TEST(mip_solve, feasibility_jump_determinism) { - for (const auto& instance : {"gen-ip054.mps", "50v-10.mps", - /*"buildingenergy.mps"*/}) { - for (int i = 0; i < 10; i++) { - run_fj_check_determinism(instance, 200); + for (const auto& instance : { + //"thor50dday.mps", + //"gen-ip054.mps", + "50v-10.mps", + //"seymour1.mps", + }) { + // for (int i = 0; i < 10; i++) + while (true) { + run_fj_check_determinism(instance, 1000); } } } From 3269d870cd009495c8ed62bd8bcd4f69203fc64f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 17 Jul 2025 08:30:59 +0000 Subject: [PATCH 003/225] deterministic tests for more parts of the solver --- cpp/src/linear_programming/pdlp.cu | 4 + cpp/src/linear_programming/solve.cu | 1 + .../adaptive_step_size_strategy.cu | 3 +- .../mip/feasibility_jump/feasibility_jump.cu | 1 + .../mip/feasibility_jump/feasibility_jump.cuh | 6 +- .../feasibility_jump_kernels.cu | 4 +- .../mip/feasibility_jump/load_balancing.cuh | 9 +- .../feasibility_pump/feasibility_pump.cu | 16 +- .../feasibility_pump/feasibility_pump.cuh | 7 +- .../local_search/rounding/constraint_prop.cu | 9 +- cpp/src/mip/presolve/multi_probe.cu | 8 +- cpp/src/mip/problem/problem.cu | 23 ++ cpp/src/mip/problem/problem.cuh | 2 + cpp/src/mip/problem/problem_helpers.cuh | 99 +++++++ cpp/src/mip/relaxed_lp/relaxed_lp.cu | 5 +- cpp/src/mip/relaxed_lp/relaxed_lp.cuh | 1 + cpp/src/mip/solve.cu | 1 + cpp/src/mip/utils.cuh | 24 +- cpp/src/utilities/timer.hpp | 14 +- cpp/tests/mip/CMakeLists.txt | 6 + cpp/tests/mip/feasibility_jump_tests.cu | 78 +++--- cpp/tests/mip/local_search_test.cu | 247 ++++++++++++++++++ cpp/tests/mip/multi_probe_test.cu | 50 +++- cpp/tests/mip/presolve_test.cu | 208 +++++++++++++++ 24 files changed, 747 insertions(+), 79 deletions(-) create mode 100644 cpp/tests/mip/local_search_test.cu create mode 100644 cpp/tests/mip/presolve_test.cu diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index 7acadae50d..de68466746 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -258,6 +258,10 @@ static bool time_limit_reached(const std::chrono::high_resolution_clock::time_po auto elapsed = std::chrono::duration_cast(current_time - start_time).count(); + if (elapsed >= (seconds * 1000.0)) { + CUOPT_LOG_ERROR("**** PDLP Time limit reached: %f *****", seconds); + } + return elapsed >= (seconds * 1000.0); } diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index c3ba4f2c7d..8d090624e4 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -582,6 +582,7 @@ optimization_problem_solution_t solve_lp(optimization_problem_t diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 5861955007..37567dd7d3 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -770,6 +770,7 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream kernel_args, 0, climber_stream); + cudaLaunchCooperativeKernel((void*)handle_local_minimum_kernel, grid_update_weights, blocks_update_weights, diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh index a55f115f19..6dbf139777 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh @@ -135,8 +135,8 @@ struct fj_move_t { // as we dont need them to be floating point per the FJ2 scoring scheme // sizeof(fj_staged_score_t) <= 8 is needed to allow for atomic loads struct fj_staged_score_t { - float base{-std::numeric_limits::infinity()}; - float bonus{-std::numeric_limits::infinity()}; + int32_t base{std::numeric_limits::lowest()}; + int32_t bonus{std::numeric_limits::lowest()}; HDI bool operator<(fj_staged_score_t other) const noexcept { @@ -154,7 +154,7 @@ struct fj_staged_score_t { HDI static fj_staged_score_t invalid() { - return {-std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; + return {std::numeric_limits::lowest(), std::numeric_limits::lowest()}; } HDI static fj_staged_score_t zero() { return {0, 0}; } diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 3adc27cabe..ee67f55358 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -79,7 +79,7 @@ DI thrust::pair move_objective_score( } template -DI std::pair feas_score_constraint( +DI std::pair feas_score_constraint( const typename fj_t::climber_data_t::view_t& fj, i_t var_idx, f_t delta, @@ -167,7 +167,7 @@ DI std::pair feas_score_constraint( } } - return {base_feas, bonus_robust}; + return {round(base_feas), round(bonus_robust)}; } template diff --git a/cpp/src/mip/feasibility_jump/load_balancing.cuh b/cpp/src/mip/feasibility_jump/load_balancing.cuh index f3515e5de0..caa09e133c 100644 --- a/cpp/src/mip/feasibility_jump/load_balancing.cuh +++ b/cpp/src/mip/feasibility_jump/load_balancing.cuh @@ -134,7 +134,8 @@ __global__ void load_balancing_prepare_iteration(const __grid_constant__ // alternate codepath in the case of a small related_var/total_var ratio if (!full_refresh && fj.pb.related_variables.size() > 0 && fj.pb.n_variables / fj.work_ids_for_related_vars[*fj.selected_var] >= - fj.settings->parameters.old_codepath_total_var_to_relvar_ratio_threshold) { + fj.settings->parameters.old_codepath_total_var_to_relvar_ratio_threshold && + fj.settings->load_balancing_mode != fj_load_balancing_mode_t::ALWAYS_ON) { auto range = fj.pb.range_for_related_vars(*fj.selected_var); for (i_t i = blockIdx.x + range.first; i < range.second; i += gridDim.x) { @@ -533,8 +534,8 @@ __launch_bounds__(TPB_loadbalance, 16) __global__ auto& score_info = candidate.score; - f_t base_feas = 0; - f_t bonus_robust = 0; + int32_t base_feas = 0; + int32_t bonus_robust = 0; // same as for the binary var kernel, compute each score compoenent per thread // and merge then via a wapr reduce @@ -650,7 +651,7 @@ __global__ void load_balancing_sanity_checks(const __grid_constant__ if (!(score_1 == score_1.invalid() && score_2 == score_2.invalid()) && !(v.pb.integer_equal(score_1.base, score_2.base) && v.pb.integer_equal(score_1.bonus, score_2.bonus))) { - printf("(iter %d) [%d, int:%d]: delta %g/%g was %f/%f, is %f/%f\n", + printf("(iter %d) [%d, int:%d]: delta %g/%g was %d/%d, is %d/%d\n", *v.iterations, var_idx, v.pb.is_integer_var(var_idx), diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index c8aa874334..d18d29a5a5 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -221,7 +221,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t::round(solution_t& solution) { bool result; CUOPT_LOG_DEBUG("Rounding the point"); - timer_t bounds_prop_timer(std::min(2., timer.remaining_time())); - const f_t lp_run_time_after_feasible = std::min(3., timer.remaining_time() / 20.); + timer_t bounds_prop_timer(std::min(config.bounds_prop_timer_min, timer.remaining_time())); + const f_t lp_run_time_after_feasible = + std::min(config.lp_run_time_after_feasible_min, timer.remaining_time() / 20.); result = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); cuopt_func_call(solution.test_variable_bounds(true)); // copy the last rounding @@ -290,9 +291,9 @@ bool feasibility_pump_t::run_fj_cycle_escape(solution_t& sol fj.settings.update_weights = true; fj.settings.feasibility_run = true; fj.settings.n_of_minimums_for_exit = 5000; - fj.settings.time_limit = std::min(3., timer.remaining_time()); - fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_TIME_LIMIT; - is_feasible = fj.solve(solution); + fj.settings.time_limit = std::min(config.fj_cycle_escape_time_limit, timer.remaining_time()); + fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_TIME_LIMIT; + is_feasible = fj.solve(solution); // if FJ didn't change the solution, take last incumbent solution if (!is_feasible && cycle_queue.check_cycle(solution)) { CUOPT_LOG_DEBUG("cycle detected after FJ, taking last incumbent of fj"); @@ -542,9 +543,8 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s // if the solution is almost on polytope else if (last_distances[0] < distance_to_check_for_feasible) { // run the linear projection with full precision to check if it actually is feasible - const f_t lp_verify_time_limit = 5.; relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_verify_time_limit; + lp_settings.time_limit = config.lp_verify_time_limit; lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; lp_settings.return_first_feasible = true; lp_settings.save_state = true; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh index 8b7891b04b..54dda9d3fe 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh @@ -108,6 +108,11 @@ struct fp_config_t { bool check_distance_cycle = true; int first_stage_kk = 70; double cycle_distance_reduction_ration = 0.1; + double bounds_prop_timer_min = 2.; + double lp_run_time_after_feasible_min = 3.; + double linproj_time_limit = 5.; + double fj_cycle_escape_time_limit = 3.; + double lp_verify_time_limit = 5.; }; template @@ -116,7 +121,6 @@ class feasibility_pump_t { feasibility_pump_t() = delete; feasibility_pump_t(mip_solver_context_t& context, fj_t& fj, - // fj_tree_t& fj_tree_, constraint_prop_t& constraint_prop_, lb_constraint_prop_t& lb_constraint_prop_, line_segment_search_t& line_segment_search_, @@ -149,7 +153,6 @@ class feasibility_pump_t { mip_solver_context_t& context; // keep a reference from upstream local search fj_t& fj; - // fj_tree_t& fj_tree; line_segment_search_t& line_segment_search; cycle_queue_t cycle_queue; constraint_prop_t& constraint_prop; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 33a631d298..37fb4b92f1 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -920,7 +920,8 @@ bool constraint_prop_t::find_integer( sol, orig_sol.problem_ptr, var_probe_vals, &set_count, unset_integer_vars, probing_config); if (!(n_failed_repair_iterations >= max_n_failed_repair_iterations) && rounding_ii && !timeout_happened) { - timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; + // timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; + timer_t repair_timer{timer.remaining_time() / 5}; save_bounds(sol); // update bounds and run repair procedure bool bounds_repaired = @@ -994,6 +995,7 @@ bool constraint_prop_t::find_integer( lp_settings.tolerance = orig_sol.problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; + lp_settings.iteration_limit = 13000; run_lp_with_vars_fixed(*orig_sol.problem_ptr, orig_sol, orig_sol.problem_ptr->integer_indices, @@ -1015,8 +1017,9 @@ bool constraint_prop_t::apply_round( raft::common::nvtx::range fun_scope("constraint prop round"); // this is second timer that can continue but without recovery mode - const f_t max_time_for_bounds_prop = 5.; - max_timer = timer_t{max_time_for_bounds_prop}; + f_t max_time_for_bounds_prop = 5.; + max_time_for_bounds_prop = timer.remaining_time() / 10.0; + max_timer = timer_t{max_time_for_bounds_prop}; if (check_brute_force_rounding(sol)) { return true; } recovery_mode = false; rounding_ii = false; diff --git a/cpp/src/mip/presolve/multi_probe.cu b/cpp/src/mip/presolve/multi_probe.cu index 699a5f1dd4..4f1579b3a6 100644 --- a/cpp/src/mip/presolve/multi_probe.cu +++ b/cpp/src/mip/presolve/multi_probe.cu @@ -158,7 +158,7 @@ bool multi_probe_t::calculate_bounds_update(problem_t& pb, <<get_stream()>>>(pb.view(), upd_1.view()); RAFT_CHECK_CUDA(handle_ptr->get_stream()); i_t h_bounds_changed_1 = upd_1.bounds_changed.value(handle_ptr->get_stream()); - CUOPT_LOG_TRACE("Bounds changed upd 1 %d", h_bounds_changed_1); + // CUOPT_LOG_TRACE("Bounds changed upd 1 %d", h_bounds_changed_1); skip_1 = (h_bounds_changed_1 == zero); } else if (skip_1) { upd_0.bounds_changed.set_value_async(zero, handle_ptr->get_stream()); @@ -166,7 +166,7 @@ bool multi_probe_t::calculate_bounds_update(problem_t& pb, <<get_stream()>>>(pb.view(), upd_0.view()); RAFT_CHECK_CUDA(handle_ptr->get_stream()); i_t h_bounds_changed_0 = upd_0.bounds_changed.value(handle_ptr->get_stream()); - CUOPT_LOG_TRACE("Bounds changed upd 0 %d", h_bounds_changed_0); + // CUOPT_LOG_TRACE("Bounds changed upd 0 %d", h_bounds_changed_0); skip_0 = (h_bounds_changed_0 == zero); } else { upd_0.bounds_changed.set_value_async(zero, handle_ptr->get_stream()); @@ -176,9 +176,9 @@ bool multi_probe_t::calculate_bounds_update(problem_t& pb, pb.view(), upd_0.view(), upd_1.view()); RAFT_CHECK_CUDA(handle_ptr->get_stream()); i_t h_bounds_changed_0 = upd_0.bounds_changed.value(handle_ptr->get_stream()); - CUOPT_LOG_TRACE("Bounds changed upd 0 %d", h_bounds_changed_0); + // CUOPT_LOG_TRACE("Bounds changed upd 0 %d", h_bounds_changed_0); i_t h_bounds_changed_1 = upd_1.bounds_changed.value(handle_ptr->get_stream()); - CUOPT_LOG_TRACE("Bounds changed upd 1 %d", h_bounds_changed_1); + // CUOPT_LOG_TRACE("Bounds changed upd 1 %d", h_bounds_changed_1); skip_0 = (h_bounds_changed_0 == zero); skip_1 = (h_bounds_changed_1 == zero); diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index ea162cdfba..06ae72596f 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -296,6 +296,8 @@ problem_t::problem_t(const problem_t& problem_, bool no_deep template void problem_t::compute_transpose_of_problem() { + csrsort_cusparse(coefficients, variables, offsets, n_constraints, n_variables, handle_ptr); + RAFT_CUBLAS_TRY(raft::linalg::detail::cublassetpointermode( handle_ptr->get_cublas_handle(), CUBLAS_POINTER_MODE_DEVICE, handle_ptr->get_stream())); RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsesetpointermode( @@ -1147,6 +1149,7 @@ problem_t problem_t::get_problem_after_fixing_vars( cuopt_assert(n_variables == assignment.size(), "Assignment size issue"); problem_t problem(*this, true); CUOPT_LOG_DEBUG("Fixing %d variables", variables_to_fix.size()); + CUOPT_LOG_DEBUG("Model fingerprint before fixing: 0x%x", get_fingerprint()); // we will gather from this and scatter back to the original problem variable_map.resize(assignment.size() - variables_to_fix.size(), handle_ptr->get_stream()); // compute variable map to recover the assignment later @@ -1195,6 +1198,7 @@ problem_t problem_t::get_problem_after_fixing_vars( time_taken, total_time_taken / total_calls, total_time_taken); + CUOPT_LOG_DEBUG("Model fingerprint after fixing: 0x%x", problem.get_fingerprint()); return problem; } @@ -1588,6 +1592,25 @@ f_t problem_t::get_user_obj_from_solver_obj(f_t solver_obj) return presolve_data.objective_scaling_factor * (solver_obj + presolve_data.objective_offset); } +template +uint32_t problem_t::get_fingerprint() const +{ + // CSR representation should be unique and sorted at this point + + std::vector hashes = { + detail::compute_hash(coefficients, handle_ptr->get_stream()), + detail::compute_hash(variables, handle_ptr->get_stream()), + detail::compute_hash(offsets, handle_ptr->get_stream()), + detail::compute_hash(objective_coefficients, handle_ptr->get_stream()), + detail::compute_hash(variable_lower_bounds, handle_ptr->get_stream()), + detail::compute_hash(variable_upper_bounds, handle_ptr->get_stream()), + detail::compute_hash(constraint_lower_bounds, handle_ptr->get_stream()), + detail::compute_hash(constraint_upper_bounds, handle_ptr->get_stream()), + detail::compute_hash(variable_types, handle_ptr->get_stream()), + }; + return detail::compute_hash(hashes); +} + #if MIP_INSTANTIATE_FLOAT template class problem_t; #endif diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index cc29ce0e65..c0b9c01954 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -111,6 +111,8 @@ class problem_t { void get_host_user_problem( cuopt::linear_programming::dual_simplex::user_problem_t& user_problem) const; + uint32_t get_fingerprint() const; + void write_as_mps(const std::string& path); struct view_t { diff --git a/cpp/src/mip/problem/problem_helpers.cuh b/cpp/src/mip/problem/problem_helpers.cuh index dfcebc9d34..a7b9b0c355 100644 --- a/cpp/src/mip/problem/problem_helpers.cuh +++ b/cpp/src/mip/problem/problem_helpers.cuh @@ -27,10 +27,55 @@ #include #include +#include #include #include namespace cuopt::linear_programming::detail { + +template +struct cusparse_data_type { + static_assert(sizeof(T) == 0, "cusparse_data_type mapping not defined for this type"); +}; +template +struct cusparse_index_type { + static_assert(sizeof(T) == 0, "cusparse_index_type mapping not defined for this type"); +}; + +template <> +struct cusparse_data_type<__half> { + static constexpr cudaDataType value = CUDA_R_16F; +}; +template <> +struct cusparse_data_type<__nv_bfloat16> { + static constexpr cudaDataType value = CUDA_R_16BF; +}; +template <> +struct cusparse_data_type { + static constexpr cudaDataType value = CUDA_R_32F; +}; +template <> +struct cusparse_data_type { + static constexpr cudaDataType value = CUDA_R_64F; +}; +template <> +struct cusparse_data_type { + static constexpr cudaDataType value = CUDA_R_8I; +}; +template <> +struct cusparse_data_type { + static constexpr cudaDataType value = CUDA_R_32I; +}; + +template <> +struct cusparse_index_type { + static constexpr cusparseIndexType_t value = CUSPARSE_INDEX_32I; +}; +template <> +struct cusparse_index_type { + static constexpr cusparseIndexType_t value = CUSPARSE_INDEX_64I; +}; + template struct transform_bounds_functor { __device__ thrust::tuple operator()(const thrust::tuple& input) const @@ -307,4 +352,58 @@ static bool check_bounds_sanity(const detail::problem_t& problem) check_constraint_bounds_sanity(problem); } +static void check_cusparse_status(cusparseStatus_t status) +{ + if (status != CUSPARSE_STATUS_SUCCESS) { + throw std::runtime_error("CUSPARSE error: " + std::string(cusparseGetErrorString(status))); + } +} + +template +static void csrsort_cusparse(rmm::device_uvector& values, + rmm::device_uvector& indices, + rmm::device_uvector& offsets, + i_t rows, + i_t cols, + const raft::handle_t* handle_ptr) +{ + auto stream = offsets.stream(); + cusparseHandle_t handle; + cusparseCreate(&handle); + cusparseSetStream(handle, stream); + + i_t nnz = values.size(); + i_t m = rows; + i_t n = cols; + + cusparseMatDescr_t matA; + cusparseCreateMatDescr(&matA); + cusparseSetMatIndexBase(matA, CUSPARSE_INDEX_BASE_ZERO); + cusparseSetMatType(matA, CUSPARSE_MATRIX_TYPE_GENERAL); + + size_t pBufferSizeInBytes = 0; + check_cusparse_status(cusparseXcsrsort_bufferSizeExt( + handle, m, n, nnz, offsets.data(), indices.data(), &pBufferSizeInBytes)); + rmm::device_uvector pBuffer(pBufferSizeInBytes, stream); + cuopt_assert(((intptr_t)pBuffer.data() % 128) == 0, + "CUSPARSE buffer size is not aligned to 128 bytes"); + rmm::device_uvector P(nnz, stream); + thrust::sequence(handle_ptr->get_thrust_policy(), P.begin(), P.end()); + + check_cusparse_status(cusparseXcsrsort( + handle, m, n, nnz, matA, offsets.data(), indices.data(), P.data(), pBuffer.data())); + + // apply the permutation to the values + rmm::device_uvector values_sorted(nnz, stream); + thrust::gather( + handle_ptr->get_thrust_policy(), P.begin(), P.end(), values.begin(), values_sorted.begin()); + thrust::copy( + handle_ptr->get_thrust_policy(), values_sorted.begin(), values_sorted.end(), values.begin()); + + cusparseDestroyMatDescr(matA); + cusparseDestroy(handle); + + check_csr_representation(values, offsets, indices, handle_ptr, cols, rows); +} + } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index 604f759df2..9119f924a5 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -55,6 +55,7 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.tolerances.relative_primal_tolerance = settings.tolerance / 100.; pdlp_settings.tolerances.relative_dual_tolerance = settings.tolerance / 100.; pdlp_settings.time_limit = settings.time_limit; + pdlp_settings.iteration_limit = settings.iteration_limit; if (settings.return_first_feasible) { pdlp_settings.per_constraint_residual = true; } pdlp_settings.first_primal_feasible = settings.return_first_feasible; pdlp_solver_t lp_solver(op_problem, pdlp_settings); @@ -106,7 +107,9 @@ optimization_problem_solution_t get_relaxed_lp_solution( CUOPT_LOG_DEBUG("feasible solution found with LP objective %f", solver_response.get_objective_value()); } else { - CUOPT_LOG_DEBUG("LP returned with reason %d", solver_response.get_termination_status()); + CUOPT_LOG_DEBUG("LP returned with reason %d, %d iterations", + solver_response.get_termination_status(), + solver_response.get_additional_termination_information().number_of_steps_taken); } return solver_response; diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cuh b/cpp/src/mip/relaxed_lp/relaxed_lp.cuh index def1b7fa2c..367afe32bd 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cuh +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cuh @@ -29,6 +29,7 @@ namespace cuopt::linear_programming::detail { struct relaxed_lp_settings_t { double tolerance = 1e-4; double time_limit = 1.0; + int iteration_limit = std::numeric_limits::max(); bool check_infeasibility = true; bool return_first_feasible = false; bool save_state = true; diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index dcfcdd0b1e..93a99b16c6 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -108,6 +108,7 @@ mip_solution_t run_mip(detail::problem_t& problem, CUOPT_LOG_INFO("Objective offset %f scaling_factor %f", problem.presolve_data.objective_offset, problem.presolve_data.objective_scaling_factor); + CUOPT_LOG_INFO("Model fingerprint: 0x%x", problem.get_fingerprint()); cuopt_assert(problem.original_problem_ptr->get_n_variables() == scaled_problem.n_variables, "Size mismatch"); cuopt_assert(problem.original_problem_ptr->get_n_constraints() == scaled_problem.n_constraints, diff --git a/cpp/src/mip/utils.cuh b/cpp/src/mip/utils.cuh index 534c67cc7e..c4df8215d4 100644 --- a/cpp/src/mip/utils.cuh +++ b/cpp/src/mip/utils.cuh @@ -356,13 +356,11 @@ bool has_variable_bounds_violation(const raft::handle_t* handle_ptr, } template -inline uint32_t compute_hash(raft::device_span values, rmm::cuda_stream_view stream) +inline uint32_t compute_hash(std::vector h_contents) { // FNV-1a hash - uint32_t hash = 2166136261u; // FNV-1a 32-bit offset basis - auto h_contents = cuopt::host_copy(values, stream); - RAFT_CHECK_CUDA(stream); + uint32_t hash = 2166136261u; // FNV-1a 32-bit offset basis std::vector byte_contents(h_contents.size() * sizeof(i_t)); std::memcpy(byte_contents.data(), h_contents.data(), h_contents.size() * sizeof(i_t)); for (size_t i = 0; i < byte_contents.size(); ++i) { @@ -372,4 +370,22 @@ inline uint32_t compute_hash(raft::device_span values, rmm::cuda_stream_vie return hash; } +template +inline uint32_t compute_hash(raft::device_span values, + rmm::cuda_stream_view stream = rmm::cuda_stream_default) +{ + auto h_contents = cuopt::host_copy(values, stream); + RAFT_CHECK_CUDA(stream); + return compute_hash(h_contents); +} + +template +inline uint32_t compute_hash(const rmm::device_uvector& values, + rmm::cuda_stream_view stream = rmm::cuda_stream_default) +{ + auto h_contents = cuopt::host_copy(values, stream); + RAFT_CHECK_CUDA(stream); + return compute_hash(h_contents); +} + } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/utilities/timer.hpp b/cpp/src/utilities/timer.hpp index 9833cb05a1..30d1a2dd6c 100644 --- a/cpp/src/utilities/timer.hpp +++ b/cpp/src/utilities/timer.hpp @@ -44,7 +44,19 @@ class timer_t { elapsed_time()); } - bool check_time_limit() const noexcept { return elapsed_time() >= time_limit; } + bool check_time_limit(const char* caller = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) const noexcept + { + bool elapsed = elapsed_time() >= time_limit; + if (elapsed) + printf("************ TIME LIMIT (%.2gs) REACHED BY %s:%d: %s() ***\n", + time_limit, + file, + line, + caller); + return elapsed; + } bool check_half_time() const noexcept { return elapsed_time() >= time_limit / 2; } diff --git a/cpp/tests/mip/CMakeLists.txt b/cpp/tests/mip/CMakeLists.txt index ef3b16d954..816b9a8171 100644 --- a/cpp/tests/mip/CMakeLists.txt +++ b/cpp/tests/mip/CMakeLists.txt @@ -48,4 +48,10 @@ ConfigureTest(FEASIBILITY_JUMP_TEST ) ConfigureTest(MIP_TERMINATION_STATUS_TEST ${CMAKE_CURRENT_SOURCE_DIR}/termination_test.cu +) +ConfigureTest(PRESOLVE_TEST + ${CMAKE_CURRENT_SOURCE_DIR}/presolve_test.cu +) +ConfigureTest(LOCAL_SEARCH_TEST + ${CMAKE_CURRENT_SOURCE_DIR}/local_search_test.cu ) \ No newline at end of file diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index 0a77347c13..07f625e3ec 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -183,38 +183,6 @@ static bool run_fj_check_objective(std::string test_instance, int iter_limit, do return !solution.get_feasible() ? false : solution.get_user_objective() <= obj_target; } -static bool run_fj_check_determinism(std::string test_instance, int iter_limit) -{ - int seed = std::getenv("FJ_SEED") ? std::stoi(std::getenv("FJ_SEED")) : 42; - - detail::fj_settings_t fj_settings; - fj_settings.time_limit = 30.; - fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; - fj_settings.n_of_minimums_for_exit = 20000 * 1000; - fj_settings.update_weights = true; - fj_settings.feasibility_run = false; - fj_settings.termination = detail::fj_termination_flags_t::FJ_TERMINATION_ITERATION_LIMIT; - fj_settings.iteration_limit = iter_limit; - fj_settings.load_balancing_mode = detail::fj_load_balancing_mode_t::ALWAYS_OFF; - fj_settings.seed = seed; - cuopt::seed_generator::set_seed(fj_settings.seed); - - auto state = run_fj(test_instance, fj_settings); - auto& solution = state.solution; - - CUOPT_LOG_DEBUG("%s: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", - test_instance.c_str(), - solution.get_feasible(), - solution.get_user_objective(), - solution.get_objective()); - - static auto first_val = solution.get_user_objective(); - - if (abs(solution.get_user_objective() - first_val) > 1) exit(0); - - return true; -} - static bool run_fj_check_feasible(std::string test_instance) { detail::fj_settings_t fj_settings; @@ -253,6 +221,38 @@ static bool run_fj_check_feasible(std::string test_instance) return true; } +static bool run_fj_check_determinism(std::string test_instance, int iter_limit) +{ + int seed = std::getenv("FJ_SEED") ? std::stoi(std::getenv("FJ_SEED")) : 42; + + detail::fj_settings_t fj_settings; + fj_settings.time_limit = 30.; + fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; + fj_settings.n_of_minimums_for_exit = 20000 * 1000; + fj_settings.update_weights = true; + fj_settings.feasibility_run = false; + fj_settings.termination = detail::fj_termination_flags_t::FJ_TERMINATION_ITERATION_LIMIT; + fj_settings.iteration_limit = iter_limit; + fj_settings.load_balancing_mode = detail::fj_load_balancing_mode_t::ALWAYS_ON; + fj_settings.seed = seed; + cuopt::seed_generator::set_seed(fj_settings.seed); + + auto state = run_fj(test_instance, fj_settings); + auto& solution = state.solution; + + CUOPT_LOG_DEBUG("%s: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", + test_instance.c_str(), + solution.get_feasible(), + solution.get_user_objective(), + solution.get_objective()); + + static auto first_val = solution.get_user_objective(); + + if (abs(solution.get_user_objective() - first_val) > 1) exit(0); + + return true; +} + // TEST(mip_solve, feasibility_jump_obj_test) // { // std::vector> test_cases = { @@ -311,12 +311,14 @@ static bool run_fj_check_feasible(std::string test_instance) TEST(mip_solve, feasibility_jump_determinism) { - for (const auto& instance : { - //"thor50dday.mps", - //"gen-ip054.mps", - "50v-10.mps", - //"seymour1.mps", - }) { + for (const auto& instance : {//"thor50dday.mps", + //"gen-ip054.mps", + //"50v-10.mps", + //"seymour1.mps", + //"rmatr200-p5.mps" + //"tr12-30.mps", + //"sct2.mps", + "uccase9.mps"}) { // for (int i = 0; i < 10; i++) while (true) { run_fj_check_determinism(instance, 1000); diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu new file mode 100644 index 0000000000..3147d6eaf2 --- /dev/null +++ b/cpp/tests/mip/local_search_test.cu @@ -0,0 +1,247 @@ +/* + * 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 "../linear_programming/utilities/pdlp_test_utilities.cuh" +#include "mip_utils.cuh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace cuopt::linear_programming::test { + +void init_handler(const raft::handle_t* handle_ptr) +{ + // Init cuBlas / cuSparse context here to avoid having it during solving time + RAFT_CUBLAS_TRY(raft::linalg::detail::cublassetpointermode( + handle_ptr->get_cublas_handle(), CUBLAS_POINTER_MODE_DEVICE, handle_ptr->get_stream())); + RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsesetpointermode( + handle_ptr->get_cusparse_handle(), CUSPARSE_POINTER_MODE_DEVICE, handle_ptr->get_stream())); +} + +static void setup_device_symbols(rmm::cuda_stream_view stream_view) +{ + raft::common::nvtx::range fun_scope("Setting device symbol"); + detail::set_adaptive_step_size_hyper_parameters(stream_view); + detail::set_restart_hyper_parameters(stream_view); + detail::set_pdlp_hyper_parameters(stream_view); +} + +struct fj_tweaks_t { + double objective_weight = 0; +}; + +struct fj_state_t { + detail::solution_t solution; + std::vector solution_vector; + int minimums; + double incumbent_objective; + double incumbent_violation; +}; + +// Helper function to setup MIP solver and run FJ with given settings and initial solution +static uint32_t run_fp(std::string test_instance, + const detail::fj_settings_t& fj_settings, + fj_tweaks_t tweaks = {}, + std::vector initial_solution = {}) +{ + const raft::handle_t handle_{}; + std::cout << "Running: " << test_instance << std::endl; + + auto path = cuopt::test::get_rapids_dataset_root_dir() + ("/mip/" + test_instance); + cuopt::mps_parser::mps_data_model_t mps_problem = + cuopt::mps_parser::parse_mps(path, false); + handle_.sync_stream(); + auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); + problem_checking_t::check_problem_representation(op_problem); + + init_handler(op_problem.get_handle_ptr()); + // run the problem constructor of MIP, so that we do bounds standardization + detail::problem_t problem(op_problem); + problem.preprocess_problem(); + + setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); + + detail::pdhg_solver_t pdhg_solver(problem.handle_ptr, problem); + detail::pdlp_initial_scaling_strategy_t scaling(&handle_, + problem, + 10, + 1.0, + pdhg_solver, + problem.reverse_coefficients, + problem.reverse_offsets, + problem.reverse_constraints, + true); + + auto settings = mip_solver_settings_t{}; + settings.time_limit = 30.; + auto timer = cuopt::timer_t(30); + detail::mip_solver_t solver(problem, settings, scaling, timer); + problem.tolerances = settings.get_tolerances(); + + rmm::device_uvector lp_optimal_solution(problem.n_variables, + problem.handle_ptr->get_stream()); + + detail::lp_state_t& lp_state = problem.lp_state; + // resize because some constructor might be called before the presolve + lp_state.resize(problem, problem.handle_ptr->get_stream()); + detail::relaxed_lp_settings_t lp_settings{}; + lp_settings.time_limit = std::numeric_limits::max(); + lp_settings.tolerance = 1e-6; + lp_settings.return_first_feasible = false; + lp_settings.save_state = true; + auto lp_result = + detail::get_relaxed_lp_solution(problem, lp_optimal_solution, lp_state, lp_settings); + EXPECT_EQ(lp_result.get_termination_status(), pdlp_termination_status_t::Optimal); + clamp_within_var_bounds(lp_optimal_solution, &problem, problem.handle_ptr); + + detail::local_search_t local_search(solver.context, lp_optimal_solution); + + detail::solution_t solution(problem); + + printf("Model fingerprint: 0x%x\n", problem.get_fingerprint()); + + local_search.fp.config.bounds_prop_timer_min = std::numeric_limits::max(); + local_search.fp.config.lp_run_time_after_feasible_min = std::numeric_limits::max(); + local_search.fp.config.linproj_time_limit = std::numeric_limits::max(); + local_search.fp.config.fj_cycle_escape_time_limit = std::numeric_limits::max(); + local_search.fp.config.lp_verify_time_limit = std::numeric_limits::max(); + // local_search.fp.timer = timer_t{std::numeric_limits::max()}; + + bool is_feasible = false; + int iterations = 0; + while (true) { + is_feasible = local_search.fp.run_single_fp_descent(solution); + printf("fp_loop it %d, is_feasible %d\n", iterations, is_feasible); + // if feasible return true + if (is_feasible) { + break; + } + // if not feasible, it means it is a cycle + else { + is_feasible = local_search.fp.restart_fp(solution); + if (is_feasible) { break; } + } + iterations++; + } + + std::vector hashes; + hashes.push_back(detail::compute_hash(solution.get_host_assignment())); + printf("hashes: 0x%x, hash of the hash: 0x%x\n", hashes[0], detail::compute_hash(hashes)); + + return detail::compute_hash(hashes); + // return {host_copy(solution_vector, problem.handle_ptr->get_stream()), iterations}; +} + +static uint32_t run_fp_check_determinism(std::string test_instance, int iter_limit) +{ + int seed = std::getenv("FJ_SEED") ? std::stoi(std::getenv("FJ_SEED")) : 42; + + detail::fj_settings_t fj_settings; + fj_settings.time_limit = 30.; + fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; + fj_settings.n_of_minimums_for_exit = 20000 * 1000; + fj_settings.update_weights = true; + fj_settings.feasibility_run = false; + fj_settings.termination = detail::fj_termination_flags_t::FJ_TERMINATION_ITERATION_LIMIT; + fj_settings.iteration_limit = iter_limit; + fj_settings.load_balancing_mode = detail::fj_load_balancing_mode_t::ALWAYS_ON; + fj_settings.seed = seed; + cuopt::seed_generator::set_seed(fj_settings.seed); + + return run_fp(test_instance, fj_settings); + + // auto state = run_fp(test_instance, fj_settings); + // auto& solution = state.solution; + + // CUOPT_LOG_DEBUG("%s: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", + // test_instance.c_str(), + // solution.get_feasible(), + // solution.get_user_objective(), + // solution.get_objective()); + + // static auto first_val = solution.get_user_objective(); + + // if (abs(solution.get_user_objective() - first_val) > 1) exit(0); +} + +TEST(local_search, feasibility_pump_determinism) +{ + cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); + + for (const auto& instance : { + //"thor50dday.mps", + //"gen-ip054.mps", + //"50v-10.mps", + //"seymour1.mps", + //"rmatr200-p5.mps" + //"tr12-30.mps", + "sct2.mps", + //"uccase9.mps" + }) { + // for (int i = 0; i < 10; i++) + // while (true) { + // run_fp_check_determinism(instance, 1000); + // } + + unsigned long seed = std::random_device{}(); + std::cerr << "Tested with seed " << seed << "\n"; + uint32_t gold_hash = 0; + for (int i = 0; i < 10; ++i) { + uint32_t hash = run_fp_check_determinism(instance, 1000); + if (i == 0) { + gold_hash = hash; + printf("Gold hash: 0x%x\n", gold_hash); + } else { + ASSERT_EQ(hash, gold_hash); + printf("Hash: 0x%x\n", hash); + } + } + } +} + +} // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index 63cf93c792..f491b2100c 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -53,9 +54,10 @@ void init_handler(const raft::handle_t* handle_ptr) } std::tuple, std::vector, std::vector> select_k_random( - detail::problem_t& problem, int sample_size) + detail::problem_t& problem, + int sample_size, + unsigned long seed = std::random_device{}()) { - auto seed = std::random_device{}(); std::cerr << "Tested with seed " << seed << "\n"; problem.compute_n_integer_vars(); auto v_lb = host_copy(problem.variable_lower_bounds); @@ -146,7 +148,7 @@ multi_probe_results( std::move(h_lb_0), std::move(h_ub_0), std::move(h_lb_1), std::move(h_ub_1)); } -void test_multi_probe(std::string path) +uint32_t test_multi_probe(std::string path, unsigned long seed = std::random_device{}()) { auto memory_resource = make_async(); rmm::mr::set_current_device_resource(memory_resource.get()); @@ -173,7 +175,7 @@ void test_multi_probe(std::string path) detail::bound_presolve_t bnd_prb_1(solver.context); detail::multi_probe_t multi_probe_prs(solver.context); - auto probe_tuple = select_k_random(problem, 100); + auto probe_tuple = select_k_random(problem, 100, seed); auto bounds_probe_vals = convert_probe_tuple(probe_tuple); auto [bnd_lb_0, bnd_ub_0, bnd_lb_1, bnd_ub_1] = @@ -191,6 +193,16 @@ void test_multi_probe(std::string path) auto mlp_min_act_1 = host_copy(multi_probe_prs.upd_1.min_activity); auto mlp_max_act_1 = host_copy(multi_probe_prs.upd_1.max_activity); + std::vector hashes; + hashes.push_back(detail::compute_hash(bnd_min_act_0)); + hashes.push_back(detail::compute_hash(bnd_min_act_1)); + hashes.push_back(detail::compute_hash(bnd_max_act_0)); + hashes.push_back(detail::compute_hash(bnd_max_act_1)); + hashes.push_back(detail::compute_hash(bnd_lb_0)); + hashes.push_back(detail::compute_hash(bnd_ub_0)); + hashes.push_back(detail::compute_hash(bnd_lb_1)); + hashes.push_back(detail::compute_hash(bnd_ub_1)); + for (int i = 0; i < (int)bnd_min_act_0.size(); ++i) { EXPECT_DOUBLE_EQ(bnd_min_act_0[i], mlp_min_act_0[i]); EXPECT_DOUBLE_EQ(bnd_max_act_0[i], mlp_max_act_0[i]); @@ -204,17 +216,39 @@ void test_multi_probe(std::string path) EXPECT_DOUBLE_EQ(bnd_lb_1[i], m_lb_1[i]); EXPECT_DOUBLE_EQ(bnd_ub_1[i], m_ub_1[i]); } + + // return a composite hash of all the hashes to check for determinism + return detail::compute_hash(hashes); } -TEST(presolve, multi_probe) +// TEST(presolve, multi_probe) +// { +// std::vector test_instances = { +// "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; +// for (const auto& test_instance : test_instances) { +// std::cout << "Running: " << test_instance << std::endl; +// auto path = make_path_absolute(test_instance); +// test_multi_probe(path); +// } +// } + +TEST(presolve, multi_probe_deterministic) { std::vector test_instances = { "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; for (const auto& test_instance : test_instances) { std::cout << "Running: " << test_instance << std::endl; - auto path = make_path_absolute(test_instance); - test_multi_probe(path); + unsigned long seed = std::random_device{}(); + auto path = make_path_absolute(test_instance); + uint32_t gold_hash = 0; + for (int i = 0; i < 10; ++i) { + auto hash = test_multi_probe(path, seed); + if (i == 0) { + gold_hash = hash; + } else { + EXPECT_EQ(hash, gold_hash); + } + } } } - } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu new file mode 100644 index 0000000000..317047aec3 --- /dev/null +++ b/cpp/tests/mip/presolve_test.cu @@ -0,0 +1,208 @@ +/* + * 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 "../linear_programming/utilities/pdlp_test_utilities.cuh" +#include "mip_utils.cuh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace cuopt::linear_programming::test { + +inline auto make_async() { return std::make_shared(); } + +void init_handler(const raft::handle_t* handle_ptr) +{ + // Init cuBlas / cuSparse context here to avoid having it during solving time + RAFT_CUBLAS_TRY(raft::linalg::detail::cublassetpointermode( + handle_ptr->get_cublas_handle(), CUBLAS_POINTER_MODE_DEVICE, handle_ptr->get_stream())); + RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsesetpointermode( + handle_ptr->get_cusparse_handle(), CUSPARSE_POINTER_MODE_DEVICE, handle_ptr->get_stream())); +} + +std::tuple, std::vector, std::vector> select_k_random( + detail::problem_t& problem, + int sample_size, + unsigned long seed = std::random_device{}()) +{ + std::cerr << "Tested with seed " << seed << "\n"; + problem.compute_n_integer_vars(); + auto v_lb = host_copy(problem.variable_lower_bounds); + auto v_ub = host_copy(problem.variable_upper_bounds); + auto int_var_id = host_copy(problem.integer_indices); + int_var_id.erase(std::remove_if(int_var_id.begin(), + int_var_id.end(), + [v_lb, v_ub](auto id) { + return !(std::isfinite(v_lb[id]) && std::isfinite(v_ub[id])); + }), + int_var_id.end()); + sample_size = std::min(sample_size, static_cast(int_var_id.size())); + std::vector random_int_vars; + std::mt19937 m{seed}; + std::sample( + int_var_id.begin(), int_var_id.end(), std::back_inserter(random_int_vars), sample_size, m); + std::vector probe_0(sample_size); + std::vector probe_1(sample_size); + for (int i = 0; i < static_cast(random_int_vars.size()); ++i) { + if (i % 2) { + probe_0[i] = v_lb[random_int_vars[i]]; + probe_1[i] = v_ub[random_int_vars[i]]; + } else { + probe_1[i] = v_lb[random_int_vars[i]]; + probe_0[i] = v_ub[random_int_vars[i]]; + } + } + return std::make_tuple(std::move(random_int_vars), std::move(probe_0), std::move(probe_1)); +} + +std::pair>, std::vector>> +convert_probe_tuple(std::tuple, std::vector, std::vector>& probe) +{ + std::vector> probe_first; + std::vector> probe_second; + for (size_t i = 0; i < std::get<0>(probe).size(); ++i) { + probe_first.emplace_back(thrust::make_pair(std::get<0>(probe)[i], std::get<1>(probe)[i])); + probe_second.emplace_back(thrust::make_pair(std::get<0>(probe)[i], std::get<2>(probe)[i])); + } + return std::make_pair(std::move(probe_first), std::move(probe_second)); +} + +uint32_t test_probing_cache_determinism(std::string path, + unsigned long seed = std::random_device{}()) +{ + auto memory_resource = make_async(); + rmm::mr::set_current_device_resource(memory_resource.get()); + const raft::handle_t handle_{}; + cuopt::mps_parser::mps_data_model_t mps_problem = + cuopt::mps_parser::parse_mps(path, false); + handle_.sync_stream(); + auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); + problem_checking_t::check_problem_representation(op_problem); + detail::problem_t problem(op_problem); + mip_solver_settings_t default_settings{}; + default_settings.mip_scaling = false; // we're not checking scaling determinism here + detail::pdhg_solver_t pdhg_solver(problem.handle_ptr, problem); + detail::pdlp_initial_scaling_strategy_t scaling(&handle_, + problem, + 10, + 1.0, + pdhg_solver, + problem.reverse_coefficients, + problem.reverse_offsets, + problem.reverse_constraints, + true); + detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); + detail::bound_presolve_t bnd_prb(solver.context); + + // rely on the iteration limit + compute_probing_cache(bnd_prb, problem, timer_t(std::numeric_limits::max())); + std::vector, 2>>> cached_values( + bnd_prb.probing_cache.probing_cache.begin(), bnd_prb.probing_cache.probing_cache.end()); + std::sort(cached_values.begin(), cached_values.end(), [](const auto& a, const auto& b) { + return a.first < b.first; + }); + + std::vector probed_indices; + std::vector intervals; + std::vector interval_types; + + std::vector var_to_cached_bound_keys; + std::vector var_to_cached_bound_lb; + std::vector var_to_cached_bound_ub; + for (const auto& a : cached_values) { + probed_indices.push_back(a.first); + intervals.push_back(a.second[0].val_interval.val); + intervals.push_back(a.second[1].val_interval.val); + interval_types.push_back(a.second[0].val_interval.interval_type); + interval_types.push_back(a.second[1].val_interval.interval_type); + + auto sorted_map = std::map>( + a.second[0].var_to_cached_bound_map.begin(), a.second[0].var_to_cached_bound_map.end()); + for (const auto& [var_id, cached_bound] : sorted_map) { + var_to_cached_bound_keys.push_back(var_id); + var_to_cached_bound_lb.push_back(cached_bound.lb); + var_to_cached_bound_ub.push_back(cached_bound.ub); + } + } + + std::vector hashes; + hashes.push_back(detail::compute_hash(probed_indices)); + hashes.push_back(detail::compute_hash(intervals)); + hashes.push_back(detail::compute_hash(interval_types)); + hashes.push_back(detail::compute_hash(var_to_cached_bound_keys)); + hashes.push_back(detail::compute_hash(var_to_cached_bound_lb)); + hashes.push_back(detail::compute_hash(var_to_cached_bound_ub)); + + // return a composite hash of all the hashes to check for determinism + return detail::compute_hash(hashes); +} + +// TEST(presolve, multi_probe) +// { +// std::vector test_instances = { +// "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; +// for (const auto& test_instance : test_instances) { +// std::cout << "Running: " << test_instance << std::endl; +// auto path = make_path_absolute(test_instance); +// test_multi_probe(path); +// } +// } + +TEST(presolve, probing_cache_deterministic) +{ + std::vector test_instances = {"mip/50v-10-free-bound.mps", + "mip/neos5-free-bound.mps", + "mip/neos5.mps", + "mip/gen-ip054.mps", + "mip/rmatr200-p5.mps"}; + for (const auto& test_instance : test_instances) { + std::cout << "Running: " << test_instance << std::endl; + unsigned long seed = std::random_device{}(); + std::cerr << "Tested with seed " << seed << "\n"; + auto path = make_path_absolute(test_instance); + uint32_t gold_hash = 0; + for (int i = 0; i < 10; ++i) { + auto hash = test_probing_cache_determinism(path, seed); + if (i == 0) { + gold_hash = hash; + std::cout << "Gold hash: " << gold_hash << std::endl; + } else { + EXPECT_EQ(hash, gold_hash); + } + } + } +} +} // namespace cuopt::linear_programming::test From 20277f26b6eb47b55e791c258bdb3bc52947c7b9 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 21 Jul 2025 12:04:12 +0000 Subject: [PATCH 004/225] random spin kernel to perturb determinism tests --- cpp/tests/mip/local_search_test.cu | 42 +++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index 3147d6eaf2..518b6b254c 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -71,6 +71,18 @@ static void setup_device_symbols(rmm::cuda_stream_view stream_view) detail::set_pdlp_hyper_parameters(stream_view); } +__global__ void spin_kernel(volatile int* flag, unsigned long long timeout_clocks = 10000000) +{ + long long int start_clock, sample_clock; + start_clock = clock64(); + + while (!*flag) { + sample_clock = clock64(); + + if (sample_clock - start_clock > timeout_clocks) { break; } + } +} + struct fj_tweaks_t { double objective_weight = 0; }; @@ -133,12 +145,15 @@ static uint32_t run_fp(std::string test_instance, lp_settings.time_limit = std::numeric_limits::max(); lp_settings.tolerance = 1e-6; lp_settings.return_first_feasible = false; - lp_settings.save_state = true; + lp_settings.save_state = false; + // lp_settings.iteration_limit = 5; auto lp_result = detail::get_relaxed_lp_solution(problem, lp_optimal_solution, lp_state, lp_settings); EXPECT_EQ(lp_result.get_termination_status(), pdlp_termination_status_t::Optimal); clamp_within_var_bounds(lp_optimal_solution, &problem, problem.handle_ptr); + // return detail::compute_hash(lp_optimal_solution); + detail::local_search_t local_search(solver.context, lp_optimal_solution); detail::solution_t solution(problem); @@ -209,18 +224,39 @@ static uint32_t run_fp_check_determinism(std::string test_instance, int iter_lim // if (abs(solution.get_user_objective() - first_val) > 1) exit(0); } +void launch_spin_kernel_stream_thread(rmm::cuda_stream_view stream_view) +{ + rmm::device_scalar flag(stream_view); + while (true) { + int blocks = rand() % 64 + 1; + int threads = rand() % 1024 + 1; + spin_kernel<<>>(flag.data()); + cudaStreamSynchronize(stream_view); + std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 1000 + 1)); + } +} + +static void launch_spin_kernel_stream(rmm::cuda_stream_view stream_view) +{ + std::thread spin_thread(launch_spin_kernel_stream_thread, stream_view); + spin_thread.detach(); +} + TEST(local_search, feasibility_pump_determinism) { cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); + rmm::cuda_stream spin_stream; + launch_spin_kernel_stream(spin_stream); + for (const auto& instance : { //"thor50dday.mps", //"gen-ip054.mps", - //"50v-10.mps", + "50v-10.mps", //"seymour1.mps", //"rmatr200-p5.mps" //"tr12-30.mps", - "sct2.mps", + //"sct2.mps", //"uccase9.mps" }) { // for (int i = 0; i < 10; i++) From 55126d9de3320a026205ca1655c9f86621562779 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 21 Jul 2025 14:15:44 +0000 Subject: [PATCH 005/225] Fix uninitialized multiprobe buffer bug in constraint prop --- .../local_search/rounding/constraint_prop.cu | 1 + cpp/src/mip/presolve/bounds_update_data.cu | 29 ++++++++ cpp/src/mip/solution/solution.cu | 6 ++ cpp/src/mip/solution/solution.cuh | 1 + cpp/tests/mip/determinism_utils.cuh | 56 ++++++++++++++ cpp/tests/mip/local_search_test.cu | 73 +++++++------------ cpp/tests/mip/multi_probe_test.cu | 10 ++- cpp/tests/mip/presolve_test.cu | 5 ++ 8 files changed, 132 insertions(+), 49 deletions(-) create mode 100644 cpp/tests/mip/determinism_utils.cuh diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 37fb4b92f1..7c04ced7c4 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -807,6 +807,7 @@ bool constraint_prop_t::is_problem_ii(problem_t& problem) { bounds_update.calculate_activity_on_problem_bounds(problem); bounds_update.calculate_infeasible_redundant_constraints(problem); + multi_probe.calculate_activity(problem, problem.handle_ptr); bool problem_ii = bounds_update.infeas_constraints_count > 0; return problem_ii; } diff --git a/cpp/src/mip/presolve/bounds_update_data.cu b/cpp/src/mip/presolve/bounds_update_data.cu index a1616b1d91..acc2a14988 100644 --- a/cpp/src/mip/presolve/bounds_update_data.cu +++ b/cpp/src/mip/presolve/bounds_update_data.cu @@ -45,6 +45,35 @@ void bounds_update_data_t::resize(problem_t& problem) changed_constraints.resize(problem.n_constraints, problem.handle_ptr->get_stream()); next_changed_constraints.resize(problem.n_constraints, problem.handle_ptr->get_stream()); changed_variables.resize(problem.n_variables, problem.handle_ptr->get_stream()); + + thrust::fill(problem.handle_ptr->get_thrust_policy(), + min_activity.begin(), + min_activity.end(), + std::numeric_limits::signaling_NaN()); + thrust::fill(problem.handle_ptr->get_thrust_policy(), + max_activity.begin(), + max_activity.end(), + std::numeric_limits::signaling_NaN()); + thrust::fill(problem.handle_ptr->get_thrust_policy(), + lb.begin(), + lb.end(), + std::numeric_limits::signaling_NaN()); + thrust::fill(problem.handle_ptr->get_thrust_policy(), + ub.begin(), + ub.end(), + std::numeric_limits::signaling_NaN()); + thrust::fill(problem.handle_ptr->get_thrust_policy(), + changed_constraints.begin(), + changed_constraints.end(), + -1); + thrust::fill(problem.handle_ptr->get_thrust_policy(), + next_changed_constraints.begin(), + next_changed_constraints.end(), + -1); + thrust::fill(problem.handle_ptr->get_thrust_policy(), + changed_variables.begin(), + changed_variables.end(), + -1); } template diff --git a/cpp/src/mip/solution/solution.cu b/cpp/src/mip/solution/solution.cu index 74ec4c41cf..3dd52d5c58 100644 --- a/cpp/src/mip/solution/solution.cu +++ b/cpp/src/mip/solution/solution.cu @@ -614,6 +614,12 @@ mip_solution_t solution_t::get_solution(bool output_feasible } } +template +uint32_t solution_t::get_hash() +{ + return compute_hash(assignment); +} + #if MIP_INSTANTIATE_FLOAT template class solution_t; #endif diff --git a/cpp/src/mip/solution/solution.cuh b/cpp/src/mip/solution/solution.cuh index 729a5c0e55..0b0080ad66 100644 --- a/cpp/src/mip/solution/solution.cuh +++ b/cpp/src/mip/solution/solution.cuh @@ -104,6 +104,7 @@ class solution_t { f_t compute_max_constraint_violation(); f_t compute_max_int_violation(); f_t compute_max_variable_violation(); + uint32_t get_hash(); struct view_t { // let's not bloat the class for every simple getter and setters diff --git a/cpp/tests/mip/determinism_utils.cuh b/cpp/tests/mip/determinism_utils.cuh new file mode 100644 index 0000000000..3e71e0885a --- /dev/null +++ b/cpp/tests/mip/determinism_utils.cuh @@ -0,0 +1,56 @@ +/* + * 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 + +#include +#include + +namespace cuopt::linear_programming::test { + +static __global__ void spin_kernel(volatile int* flag, unsigned long long timeout_clocks = 10000000) +{ + long long int start_clock, sample_clock; + start_clock = clock64(); + + while (!*flag) { + sample_clock = clock64(); + + if (sample_clock - start_clock > timeout_clocks) { break; } + } +} + +static void launch_spin_kernel_stream_thread(rmm::cuda_stream_view stream_view) +{ + rmm::device_scalar flag(0, stream_view); + while (true) { + int blocks = rand() % 64 + 1; + int threads = rand() % 1024 + 1; + spin_kernel<<>>(flag.data()); + cudaStreamSynchronize(stream_view); + std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 1000 + 1)); + } +} + +static inline void launch_spin_kernel_stream(rmm::cuda_stream_view stream_view) +{ + std::thread spin_thread(launch_spin_kernel_stream_thread, stream_view); + spin_thread.detach(); +} +} // namespace cuopt::linear_programming::test \ No newline at end of file diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index 518b6b254c..fcd8df4811 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -16,6 +16,7 @@ */ #include "../linear_programming/utilities/pdlp_test_utilities.cuh" +#include "determinism_utils.cuh" #include "mip_utils.cuh" #include @@ -71,18 +72,6 @@ static void setup_device_symbols(rmm::cuda_stream_view stream_view) detail::set_pdlp_hyper_parameters(stream_view); } -__global__ void spin_kernel(volatile int* flag, unsigned long long timeout_clocks = 10000000) -{ - long long int start_clock, sample_clock; - start_clock = clock64(); - - while (!*flag) { - sample_clock = clock64(); - - if (sample_clock - start_clock > timeout_clocks) { break; } - } -} - struct fj_tweaks_t { double objective_weight = 0; }; @@ -135,22 +124,25 @@ static uint32_t run_fp(std::string test_instance, detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); - rmm::device_uvector lp_optimal_solution(problem.n_variables, - problem.handle_ptr->get_stream()); - - detail::lp_state_t& lp_state = problem.lp_state; - // resize because some constructor might be called before the presolve - lp_state.resize(problem, problem.handle_ptr->get_stream()); - detail::relaxed_lp_settings_t lp_settings{}; - lp_settings.time_limit = std::numeric_limits::max(); - lp_settings.tolerance = 1e-6; - lp_settings.return_first_feasible = false; - lp_settings.save_state = false; - // lp_settings.iteration_limit = 5; - auto lp_result = - detail::get_relaxed_lp_solution(problem, lp_optimal_solution, lp_state, lp_settings); - EXPECT_EQ(lp_result.get_termination_status(), pdlp_termination_status_t::Optimal); - clamp_within_var_bounds(lp_optimal_solution, &problem, problem.handle_ptr); + // only compute the LP optimal once + static rmm::device_uvector lp_optimal_solution(0, problem.handle_ptr->get_stream()); + + if (lp_optimal_solution.size() == 0) { + lp_optimal_solution.resize(problem.n_variables, problem.handle_ptr->get_stream()); + detail::lp_state_t& lp_state = problem.lp_state; + // resize because some constructor might be called before the presolve + lp_state.resize(problem, problem.handle_ptr->get_stream()); + detail::relaxed_lp_settings_t lp_settings{}; + lp_settings.time_limit = std::numeric_limits::max(); + lp_settings.tolerance = 1e-6; + lp_settings.return_first_feasible = false; + lp_settings.save_state = false; + // lp_settings.iteration_limit = 5; + auto lp_result = + detail::get_relaxed_lp_solution(problem, lp_optimal_solution, lp_state, lp_settings); + EXPECT_EQ(lp_result.get_termination_status(), pdlp_termination_status_t::Optimal); + clamp_within_var_bounds(lp_optimal_solution, &problem, problem.handle_ptr); + } // return detail::compute_hash(lp_optimal_solution); @@ -159,6 +151,7 @@ static uint32_t run_fp(std::string test_instance, detail::solution_t solution(problem); printf("Model fingerprint: 0x%x\n", problem.get_fingerprint()); + printf("LP optimal hash: 0x%x\n", detail::compute_hash(lp_optimal_solution)); local_search.fp.config.bounds_prop_timer_min = std::numeric_limits::max(); local_search.fp.config.lp_run_time_after_feasible_min = std::numeric_limits::max(); @@ -224,30 +217,14 @@ static uint32_t run_fp_check_determinism(std::string test_instance, int iter_lim // if (abs(solution.get_user_objective() - first_val) > 1) exit(0); } -void launch_spin_kernel_stream_thread(rmm::cuda_stream_view stream_view) -{ - rmm::device_scalar flag(stream_view); - while (true) { - int blocks = rand() % 64 + 1; - int threads = rand() % 1024 + 1; - spin_kernel<<>>(flag.data()); - cudaStreamSynchronize(stream_view); - std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 1000 + 1)); - } -} - -static void launch_spin_kernel_stream(rmm::cuda_stream_view stream_view) -{ - std::thread spin_thread(launch_spin_kernel_stream_thread, stream_view); - spin_thread.detach(); -} - TEST(local_search, feasibility_pump_determinism) { cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); - rmm::cuda_stream spin_stream; - launch_spin_kernel_stream(spin_stream); + rmm::cuda_stream spin_stream_1; + rmm::cuda_stream spin_stream_2; + launch_spin_kernel_stream(spin_stream_1); + launch_spin_kernel_stream(spin_stream_2); for (const auto& instance : { //"thor50dday.mps", diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index f491b2100c..158205ca74 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -16,6 +16,7 @@ */ #include "../linear_programming/utilities/pdlp_test_utilities.cuh" +#include "determinism_utils.cuh" #include "mip_utils.cuh" #include @@ -234,8 +235,15 @@ uint32_t test_multi_probe(std::string path, unsigned long seed = std::random_dev TEST(presolve, multi_probe_deterministic) { + rmm::cuda_stream spin_stream_1; + launch_spin_kernel_stream(spin_stream_1); + std::vector test_instances = { - "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; + "mip/50v-10-free-bound.mps", + "mip/neos5-free-bound.mps", + "mip/neos5.mps", + "mip/50v-10.mps", + }; for (const auto& test_instance : test_instances) { std::cout << "Running: " << test_instance << std::endl; unsigned long seed = std::random_device{}(); diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index 317047aec3..e9810b3e72 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -16,6 +16,7 @@ */ #include "../linear_programming/utilities/pdlp_test_utilities.cuh" +#include "determinism_utils.cuh" #include "mip_utils.cuh" #include @@ -183,9 +184,13 @@ uint32_t test_probing_cache_determinism(std::string path, TEST(presolve, probing_cache_deterministic) { + rmm::cuda_stream spin_stream_1; + launch_spin_kernel_stream(spin_stream_1); + std::vector test_instances = {"mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps", + "mip/50v-10.mps", "mip/gen-ip054.mps", "mip/rmatr200-p5.mps"}; for (const auto& test_instance : test_instances) { From 58d9a421c2771f6bcc4e5ae71fb6ad5ed21b896c Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 22 Jul 2025 08:19:00 +0000 Subject: [PATCH 006/225] local search determinism tests --- cpp/src/linear_programming/pdlp.cu | 1 + cpp/src/mip/local_search/local_search.cu | 3 +- .../local_search/rounding/constraint_prop.cu | 1 + cpp/src/utilities/timer.hpp | 4 +- cpp/tests/mip/determinism_utils.cuh | 41 +++-- cpp/tests/mip/local_search_test.cu | 140 ++++++++++-------- cpp/tests/mip/multi_probe_test.cu | 3 +- cpp/tests/mip/presolve_test.cu | 3 +- 8 files changed, 118 insertions(+), 78 deletions(-) diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index de68466746..8e49f73aff 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -260,6 +260,7 @@ static bool time_limit_reached(const std::chrono::high_resolution_clock::time_po if (elapsed >= (seconds * 1000.0)) { CUOPT_LOG_ERROR("**** PDLP Time limit reached: %f *****", seconds); + cuopt_assert(false, "unexpected timer"); } return elapsed >= (seconds * 1000.0); diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 655b07fa9d..efda50647b 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -167,7 +167,8 @@ template bool local_search_t::run_fj_line_segment(solution_t& solution, timer_t timer) { rmm::device_uvector starting_point(solution.assignment, solution.handle_ptr->get_stream()); - bool feas = line_segment_search.search_line_segment(solution, + line_segment_search.settings.recombiner_mode = false; + bool feas = line_segment_search.search_line_segment(solution, starting_point, lp_optimal_solution, /*n_points_to_search=*/5, diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 7c04ced7c4..b04fa6cae1 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -1020,6 +1020,7 @@ bool constraint_prop_t::apply_round( // this is second timer that can continue but without recovery mode f_t max_time_for_bounds_prop = 5.; max_time_for_bounds_prop = timer.remaining_time() / 10.0; + max_time_for_bounds_prop = 30.0; max_timer = timer_t{max_time_for_bounds_prop}; if (check_brute_force_rounding(sol)) { return true; } recovery_mode = false; diff --git a/cpp/src/utilities/timer.hpp b/cpp/src/utilities/timer.hpp index 30d1a2dd6c..1a4efa33ee 100644 --- a/cpp/src/utilities/timer.hpp +++ b/cpp/src/utilities/timer.hpp @@ -49,12 +49,14 @@ class timer_t { int line = __builtin_LINE()) const noexcept { bool elapsed = elapsed_time() >= time_limit; - if (elapsed) + if (elapsed) { printf("************ TIME LIMIT (%.2gs) REACHED BY %s:%d: %s() ***\n", time_limit, file, line, caller); + cuopt_assert(false, "unexpected timer"); + } return elapsed; } diff --git a/cpp/tests/mip/determinism_utils.cuh b/cpp/tests/mip/determinism_utils.cuh index 3e71e0885a..28e8c4b51b 100644 --- a/cpp/tests/mip/determinism_utils.cuh +++ b/cpp/tests/mip/determinism_utils.cuh @@ -19,38 +19,59 @@ #include +#include + #include #include +#include + namespace cuopt::linear_programming::test { -static __global__ void spin_kernel(volatile int* flag, unsigned long long timeout_clocks = 10000000) +static __global__ void spin_kernel(int* flag, unsigned long long timeout_clocks = 10000000) { + cuda::atomic_ref flag_ref(*flag); + long long int start_clock, sample_clock; start_clock = clock64(); - while (!*flag) { + while (flag_ref.load() == 0) { sample_clock = clock64(); if (sample_clock - start_clock > timeout_clocks) { break; } } } -static void launch_spin_kernel_stream_thread(rmm::cuda_stream_view stream_view) +static void launch_spin_kernel_stream_thread(rmm::cuda_stream_view stream_view, int* flag) { - rmm::device_scalar flag(0, stream_view); while (true) { int blocks = rand() % 64 + 1; int threads = rand() % 1024 + 1; - spin_kernel<<>>(flag.data()); + spin_kernel<<>>(flag); cudaStreamSynchronize(stream_view); + if (host_copy(flag, 1, stream_view)[0] != 0) { break; } std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 1000 + 1)); } } -static inline void launch_spin_kernel_stream(rmm::cuda_stream_view stream_view) -{ - std::thread spin_thread(launch_spin_kernel_stream_thread, stream_view); - spin_thread.detach(); -} +class spin_stream_raii_t { + public: + spin_stream_raii_t() + : flag(0, stream), spin_thread(launch_spin_kernel_stream_thread, stream.view(), flag.data()) + { + } + + ~spin_stream_raii_t() + { + int one = 1; + flag.set_value_async(one, stream); + spin_thread.join(); + } + + private: + rmm::cuda_stream stream; + rmm::device_scalar flag; + std::thread spin_thread; +}; + } // namespace cuopt::linear_programming::test \ No newline at end of file diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index fcd8df4811..a3ef1abedd 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -84,11 +84,16 @@ struct fj_state_t { double incumbent_violation; }; +enum local_search_mode_t { + FP = 0, + STAGED_FP, + FJ_LINE_SEGMENT, + FJ_ON_ZERO, + FJ_ANNEALING, +}; + // Helper function to setup MIP solver and run FJ with given settings and initial solution -static uint32_t run_fp(std::string test_instance, - const detail::fj_settings_t& fj_settings, - fj_tweaks_t tweaks = {}, - std::vector initial_solution = {}) +static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) { const raft::handle_t handle_{}; std::cout << "Running: " << test_instance << std::endl; @@ -124,57 +129,69 @@ static uint32_t run_fp(std::string test_instance, detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); - // only compute the LP optimal once - static rmm::device_uvector lp_optimal_solution(0, problem.handle_ptr->get_stream()); - - if (lp_optimal_solution.size() == 0) { - lp_optimal_solution.resize(problem.n_variables, problem.handle_ptr->get_stream()); - detail::lp_state_t& lp_state = problem.lp_state; - // resize because some constructor might be called before the presolve - lp_state.resize(problem, problem.handle_ptr->get_stream()); - detail::relaxed_lp_settings_t lp_settings{}; - lp_settings.time_limit = std::numeric_limits::max(); - lp_settings.tolerance = 1e-6; - lp_settings.return_first_feasible = false; - lp_settings.save_state = false; - // lp_settings.iteration_limit = 5; - auto lp_result = - detail::get_relaxed_lp_solution(problem, lp_optimal_solution, lp_state, lp_settings); - EXPECT_EQ(lp_result.get_termination_status(), pdlp_termination_status_t::Optimal); - clamp_within_var_bounds(lp_optimal_solution, &problem, problem.handle_ptr); - } + rmm::device_uvector lp_optimal_solution(0, problem.handle_ptr->get_stream()); + + lp_optimal_solution.resize(problem.n_variables, problem.handle_ptr->get_stream()); + detail::lp_state_t& lp_state = problem.lp_state; + // resize because some constructor might be called before the presolve + lp_state.resize(problem, problem.handle_ptr->get_stream()); + detail::relaxed_lp_settings_t lp_settings{}; + lp_settings.time_limit = std::numeric_limits::max(); + lp_settings.tolerance = 1e-6; + lp_settings.return_first_feasible = false; + lp_settings.save_state = false; + // lp_settings.iteration_limit = 5; + auto lp_result = + detail::get_relaxed_lp_solution(problem, lp_optimal_solution, lp_state, lp_settings); + EXPECT_EQ(lp_result.get_termination_status(), pdlp_termination_status_t::Optimal); + clamp_within_var_bounds(lp_optimal_solution, &problem, problem.handle_ptr); // return detail::compute_hash(lp_optimal_solution); detail::local_search_t local_search(solver.context, lp_optimal_solution); detail::solution_t solution(problem); + solution.assign_random_within_bounds(); + solution.compute_feasibility(); printf("Model fingerprint: 0x%x\n", problem.get_fingerprint()); printf("LP optimal hash: 0x%x\n", detail::compute_hash(lp_optimal_solution)); + printf("running mode: %d\n", mode); local_search.fp.config.bounds_prop_timer_min = std::numeric_limits::max(); local_search.fp.config.lp_run_time_after_feasible_min = std::numeric_limits::max(); local_search.fp.config.linproj_time_limit = std::numeric_limits::max(); local_search.fp.config.fj_cycle_escape_time_limit = std::numeric_limits::max(); local_search.fp.config.lp_verify_time_limit = std::numeric_limits::max(); - // local_search.fp.timer = timer_t{std::numeric_limits::max()}; - - bool is_feasible = false; - int iterations = 0; - while (true) { - is_feasible = local_search.fp.run_single_fp_descent(solution); - printf("fp_loop it %d, is_feasible %d\n", iterations, is_feasible); - // if feasible return true - if (is_feasible) { - break; - } - // if not feasible, it means it is a cycle - else { - is_feasible = local_search.fp.restart_fp(solution); - if (is_feasible) { break; } + local_search.fp.timer = timer_t{600}; + + if (mode == local_search_mode_t::STAGED_FP) { + timer_t timer(std::numeric_limits::max()); + bool early_exit = false; + local_search.run_staged_fp(solution, timer, early_exit); + } else if (mode == local_search_mode_t::FP) { + bool is_feasible = false; + int iterations = 0; + while (true) { + is_feasible = local_search.fp.run_single_fp_descent(solution); + printf("fp_loop it %d, is_feasible %d\n", iterations, is_feasible); + // if feasible return true + if (is_feasible) { + break; + } + // if not feasible, it means it is a cycle + else { + is_feasible = local_search.fp.restart_fp(solution); + if (is_feasible) { break; } + } + iterations++; } - iterations++; + } else if (mode == local_search_mode_t::FJ_LINE_SEGMENT) { + local_search.run_fj_line_segment(solution, timer); + } else if (mode == local_search_mode_t::FJ_ON_ZERO) { + local_search.run_fj_on_zero(solution, timer); + } else if (mode == local_search_mode_t::FJ_ANNEALING) { + local_search.run_fj_annealing(solution, timer); } std::vector hashes; @@ -185,23 +202,12 @@ static uint32_t run_fp(std::string test_instance, // return {host_copy(solution_vector, problem.handle_ptr->get_stream()), iterations}; } -static uint32_t run_fp_check_determinism(std::string test_instance, int iter_limit) +static uint32_t run_fp_check_determinism(std::string test_instance, local_search_mode_t mode) { int seed = std::getenv("FJ_SEED") ? std::stoi(std::getenv("FJ_SEED")) : 42; + cuopt::seed_generator::set_seed(seed); - detail::fj_settings_t fj_settings; - fj_settings.time_limit = 30.; - fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; - fj_settings.n_of_minimums_for_exit = 20000 * 1000; - fj_settings.update_weights = true; - fj_settings.feasibility_run = false; - fj_settings.termination = detail::fj_termination_flags_t::FJ_TERMINATION_ITERATION_LIMIT; - fj_settings.iteration_limit = iter_limit; - fj_settings.load_balancing_mode = detail::fj_load_balancing_mode_t::ALWAYS_ON; - fj_settings.seed = seed; - cuopt::seed_generator::set_seed(fj_settings.seed); - - return run_fp(test_instance, fj_settings); + return run_fp(test_instance, mode); // auto state = run_fp(test_instance, fj_settings); // auto& solution = state.solution; @@ -217,23 +223,25 @@ static uint32_t run_fp_check_determinism(std::string test_instance, int iter_lim // if (abs(solution.get_user_objective() - first_val) > 1) exit(0); } -TEST(local_search, feasibility_pump_determinism) +class LocalSearchTestParams : public testing::TestWithParam> {}; + +TEST_P(LocalSearchTestParams, local_search_operator_determinism) { cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); - rmm::cuda_stream spin_stream_1; - rmm::cuda_stream spin_stream_2; - launch_spin_kernel_stream(spin_stream_1); - launch_spin_kernel_stream(spin_stream_2); + spin_stream_raii_t spin_stream_1; + spin_stream_raii_t spin_stream_2; + + auto mode = std::get<0>(GetParam()); for (const auto& instance : { //"thor50dday.mps", //"gen-ip054.mps", - "50v-10.mps", + //"50v-10.mps", //"seymour1.mps", - //"rmatr200-p5.mps" + //"rmatr200-p5.mps", //"tr12-30.mps", - //"sct2.mps", + "sct2.mps", //"uccase9.mps" }) { // for (int i = 0; i < 10; i++) @@ -245,7 +253,7 @@ TEST(local_search, feasibility_pump_determinism) std::cerr << "Tested with seed " << seed << "\n"; uint32_t gold_hash = 0; for (int i = 0; i < 10; ++i) { - uint32_t hash = run_fp_check_determinism(instance, 1000); + uint32_t hash = run_fp_check_determinism(instance, mode); if (i == 0) { gold_hash = hash; printf("Gold hash: 0x%x\n", gold_hash); @@ -257,4 +265,12 @@ TEST(local_search, feasibility_pump_determinism) } } +INSTANTIATE_TEST_SUITE_P(LocalSearchTests, + LocalSearchTestParams, + testing::Values(std::make_tuple(local_search_mode_t::FP), + std::make_tuple(local_search_mode_t::STAGED_FP), + std::make_tuple(local_search_mode_t::FJ_LINE_SEGMENT), + std::make_tuple(local_search_mode_t::FJ_ON_ZERO), + std::make_tuple(local_search_mode_t::FJ_ANNEALING))); + } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index 158205ca74..b4a4a1c6c3 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -235,8 +235,7 @@ uint32_t test_multi_probe(std::string path, unsigned long seed = std::random_dev TEST(presolve, multi_probe_deterministic) { - rmm::cuda_stream spin_stream_1; - launch_spin_kernel_stream(spin_stream_1); + spin_stream_raii_t spin_stream_1; std::vector test_instances = { "mip/50v-10-free-bound.mps", diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index e9810b3e72..a043e2d74a 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -184,8 +184,7 @@ uint32_t test_probing_cache_determinism(std::string path, TEST(presolve, probing_cache_deterministic) { - rmm::cuda_stream spin_stream_1; - launch_spin_kernel_stream(spin_stream_1); + spin_stream_raii_t spin_stream_1; std::vector test_instances = {"mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", From 6b9324c3de2d6b5f2618e33861e0fc0853efe9b3 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 22 Jul 2025 10:28:42 +0000 Subject: [PATCH 007/225] tmp, fixing bug in constraint prop randomness --- cpp/src/mip/diversity/diversity_manager.cu | 17 +- cpp/src/mip/diversity/diversity_manager.cuh | 6 + .../local_search/rounding/constraint_prop.cu | 26 ++- cpp/src/mip/solution/solution.cu | 2 +- cpp/src/mip/solution/solution.cuh | 2 +- cpp/tests/mip/CMakeLists.txt | 3 + cpp/tests/mip/diversity_test.cu | 194 ++++++++++++++++++ cpp/tests/mip/feasibility_jump_tests.cu | 3 +- cpp/tests/mip/local_search_test.cu | 3 +- cpp/tests/mip/presolve_test.cu | 87 +++++++- 10 files changed, 321 insertions(+), 22 deletions(-) create mode 100644 cpp/tests/mip/diversity_test.cu diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 874230a298..617c89b600 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -273,7 +273,7 @@ void diversity_manager_t::generate_initial_solutions() population.var_threshold); population.print(); auto new_sol_vector = population.get_external_solutions(); - if (!fj_only_run) { recombine_and_ls_with_all(new_sol_vector); } + if (!settings.fj_only_run) { recombine_and_ls_with_all(new_sol_vector); } } template @@ -353,9 +353,12 @@ void diversity_manager_t::run_fj_alone(solution_t& solution) ls.fj.settings.n_of_minimums_for_exit = 20000 * 1000; ls.fj.settings.update_weights = true; ls.fj.settings.feasibility_run = false; - ls.fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_TIME_LIMIT; - ls.fj.settings.time_limit = timer.remaining_time(); - ls.fj.solve(solution); + // ls.fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_TIME_LIMIT; + + ls.fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_ITERATION_LIMIT; + ls.fj.settings.iteration_limit = 10000; + ls.fj.settings.time_limit = timer.remaining_time(); + // ls.fj.solve(solution); CUOPT_LOG_INFO("FJ alone finished!"); } @@ -396,7 +399,7 @@ solution_t diversity_manager_t::run_solver() std::min(max_time_on_probing, time_limit * time_ratio_of_probing_cache); timer_t probing_timer{time_for_probing_cache}; if (check_b_b_preemption()) { return population.best_feasible(); } - if (!fj_only_run) { + if (!settings.fj_only_run) { compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); } // careful, assign the correct probing cache @@ -412,7 +415,7 @@ solution_t diversity_manager_t::run_solver() lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; lp_settings.return_first_feasible = false; lp_settings.save_state = true; - if (!fj_only_run) { + if (!settings.fj_only_run) { auto lp_result = get_relaxed_lp_solution(*problem_ptr, lp_optimal_solution, lp_state, lp_settings); ls.lp_optimal_exists = true; @@ -442,7 +445,7 @@ solution_t diversity_manager_t::run_solver() population.best_feasible().get_user_objective(); } - if (fj_only_run) { + if (settings.fj_only_run) { run_fj_alone(population.best_feasible()); return population.best_feasible(); } diff --git a/cpp/src/mip/diversity/diversity_manager.cuh b/cpp/src/mip/diversity/diversity_manager.cuh index c45b831075..981bffc737 100644 --- a/cpp/src/mip/diversity/diversity_manager.cuh +++ b/cpp/src/mip/diversity/diversity_manager.cuh @@ -36,6 +36,11 @@ namespace cuopt::linear_programming::detail { template class diversity_manager_t { + public: + struct diversity_settings_t { + bool fj_only_run = false; + }; + public: diversity_manager_t(mip_solver_context_t& context); bool run_presolve(f_t time_limit); @@ -82,6 +87,7 @@ class diversity_manager_t { i_t current_step{0}; solver_stats_t& stats; std::vector> initial_sol_vector; + diversity_settings_t settings; // Enhanced statistics structure for UCB with exponential recency weighting struct mab_arm_stats_t { diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index b04fa6cae1..4661f0906f 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -225,6 +225,8 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t return bounds_interval_1 < bounds_interval_2; } }); + + CUOPT_LOG_DEBUG("hash vars 0x%x", detail::compute_hash(vars)); // now do the suffling, for that we need to assign some random values to rnd array // we will sort this rnd array and the vars in subsections, so that each subsection will be // shuffled in total we will have 3(binary, ternary and rest) x 7 intervals = 21 subsections. @@ -277,7 +279,10 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t } } }); + + CUOPT_LOG_DEBUG("hash subsection_offsets 0x%x", detail::compute_hash(subsection_offsets)); auto random_vector = get_random_uniform_vector((i_t)vars.size(), rng); + CUOPT_LOG_DEBUG("hash random_vector 0x%x", detail::compute_hash(random_vector)); rmm::device_uvector device_random_vector(random_vector.size(), sol.handle_ptr->get_stream()); raft::copy(device_random_vector.data(), random_vector.data(), @@ -748,6 +753,7 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob timer_t& timer, const raft::handle_t* handle_ptr) { + CUOPT_LOG_DEBUG("Running repair procedure"); // select the first probing value i_t select = 0; multi_probe.set_updated_bounds(problem, select, handle_ptr); @@ -822,7 +828,9 @@ bool constraint_prop_t::find_integer( { using crit_t = termination_criterion_t; auto& unset_integer_vars = unset_vars; - std::mt19937 rng(cuopt::seed_generator::get_seed()); + i_t seed = cuopt::seed_generator::get_seed(); + CUOPT_LOG_DEBUG("seed 0x%x", seed); + std::mt19937 rng(seed); lb_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); ub_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); assignment_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); @@ -846,6 +854,7 @@ bool constraint_prop_t::find_integer( sol.problem_ptr->integer_indices.data(), sol.problem_ptr->n_integer_vars, sol.handle_ptr->get_stream()); + CUOPT_LOG_DEBUG("sol hash 0x%x", sol.get_hash()); CUOPT_LOG_DEBUG("Bounds propagation rounding: unset vars %lu", unset_integer_vars.size()); if (unset_integer_vars.size() == 0) { CUOPT_LOG_ERROR("No integer variables provided in the bounds prop rounding"); @@ -855,6 +864,7 @@ bool constraint_prop_t::find_integer( } // this is needed for the sort inside of the loop bool problem_ii = is_problem_ii(*sol.problem_ptr); + CUOPT_LOG_DEBUG("is problem ii %d\n", problem_ii); // if the problem is ii, run the bounds prop in the beginning if (problem_ii) { bool bounds_repaired = @@ -871,16 +881,23 @@ bool constraint_prop_t::find_integer( } // do the sort if the problem is not ii. crossing bounds might cause some issues on the sort order else { + CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x, sol 0x%x before sort\n", + detail::compute_hash(unset_integer_vars), + sol.get_hash()); // this is a sort to have initial shuffling, so that stable sort within will keep the order and // some randomness will be achieved sort_by_interval_and_frac(sol, make_span(unset_integer_vars), rng); + CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x sol 0x%x after sort\n", + detail::compute_hash(unset_integer_vars), + sol.get_hash()); } set_host_bounds(sol); size_t set_count = 0; bool timeout_happened = false; i_t n_failed_repair_iterations = 0; while (set_count < unset_integer_vars.size()) { - CUOPT_LOG_TRACE("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); + CUOPT_LOG_DEBUG("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); + CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x\n", detail::compute_hash(unset_integer_vars)); update_host_assignment(sol); if (max_timer.check_time_limit()) { CUOPT_LOG_DEBUG("Second time limit is reached returning nearest rounding!"); @@ -915,6 +932,9 @@ bool constraint_prop_t::find_integer( unset_integer_vars.data() + set_count, n_vars_to_set, sol.handle_ptr->get_stream()); + + printf("host_vars_to_set hash 0x%x\n", detail::compute_hash(host_vars_to_set)); + auto var_probe_vals = generate_bulk_rounding_vector(sol, orig_sol, host_vars_to_set, probing_config); probe( @@ -951,7 +971,7 @@ bool constraint_prop_t::find_integer( make_span(orig_sol.problem_ptr->variable_upper_bounds), make_span(sol.assignment)}); i_t n_fixed_vars = (iter - (unset_vars.begin() + set_count)); - CUOPT_LOG_TRACE("After repair procedure, number of additional fixed vars %d", n_fixed_vars); + CUOPT_LOG_DEBUG("After repair procedure, number of additional fixed vars %d", n_fixed_vars); set_count += n_fixed_vars; } } diff --git a/cpp/src/mip/solution/solution.cu b/cpp/src/mip/solution/solution.cu index 3dd52d5c58..60b657992e 100644 --- a/cpp/src/mip/solution/solution.cu +++ b/cpp/src/mip/solution/solution.cu @@ -615,7 +615,7 @@ mip_solution_t solution_t::get_solution(bool output_feasible } template -uint32_t solution_t::get_hash() +uint32_t solution_t::get_hash() const { return compute_hash(assignment); } diff --git a/cpp/src/mip/solution/solution.cuh b/cpp/src/mip/solution/solution.cuh index 0b0080ad66..51ee449d36 100644 --- a/cpp/src/mip/solution/solution.cuh +++ b/cpp/src/mip/solution/solution.cuh @@ -104,7 +104,7 @@ class solution_t { f_t compute_max_constraint_violation(); f_t compute_max_int_violation(); f_t compute_max_variable_violation(); - uint32_t get_hash(); + uint32_t get_hash() const; struct view_t { // let's not bloat the class for every simple getter and setters diff --git a/cpp/tests/mip/CMakeLists.txt b/cpp/tests/mip/CMakeLists.txt index 816b9a8171..0052b29493 100644 --- a/cpp/tests/mip/CMakeLists.txt +++ b/cpp/tests/mip/CMakeLists.txt @@ -54,4 +54,7 @@ ConfigureTest(PRESOLVE_TEST ) ConfigureTest(LOCAL_SEARCH_TEST ${CMAKE_CURRENT_SOURCE_DIR}/local_search_test.cu +) +ConfigureTest(DIVERSITY_TEST + ${CMAKE_CURRENT_SOURCE_DIR}/diversity_test.cu ) \ No newline at end of file diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu new file mode 100644 index 0000000000..c749173f6c --- /dev/null +++ b/cpp/tests/mip/diversity_test.cu @@ -0,0 +1,194 @@ +/* + * 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 "../linear_programming/utilities/pdlp_test_utilities.cuh" +#include "determinism_utils.cuh" +#include "mip_utils.cuh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace cuopt::linear_programming::test { + +void init_handler(const raft::handle_t* handle_ptr) +{ + // Init cuBlas / cuSparse context here to avoid having it during solving time + RAFT_CUBLAS_TRY(raft::linalg::detail::cublassetpointermode( + handle_ptr->get_cublas_handle(), CUBLAS_POINTER_MODE_DEVICE, handle_ptr->get_stream())); + RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsesetpointermode( + handle_ptr->get_cusparse_handle(), CUSPARSE_POINTER_MODE_DEVICE, handle_ptr->get_stream())); +} + +static void setup_device_symbols(rmm::cuda_stream_view stream_view) +{ + raft::common::nvtx::range fun_scope("Setting device symbol"); + detail::set_adaptive_step_size_hyper_parameters(stream_view); + detail::set_restart_hyper_parameters(stream_view); + detail::set_pdlp_hyper_parameters(stream_view); +} + +static uint32_t test_initial_population_determinism(std::string path, + unsigned long seed = std::random_device{}()) +{ + const raft::handle_t handle_{}; + + cuopt::mps_parser::mps_data_model_t mps_problem = + cuopt::mps_parser::parse_mps(path, false); + handle_.sync_stream(); + auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); + problem_checking_t::check_problem_representation(op_problem); + + init_handler(op_problem.get_handle_ptr()); + // run the problem constructor of MIP, so that we do bounds standardization + detail::problem_t problem(op_problem); + problem.preprocess_problem(); + + setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); + + detail::pdhg_solver_t pdhg_solver(problem.handle_ptr, problem); + detail::pdlp_initial_scaling_strategy_t scaling(&handle_, + problem, + 10, + 1.0, + pdhg_solver, + problem.reverse_coefficients, + problem.reverse_offsets, + problem.reverse_constraints, + true); + + auto settings = mip_solver_settings_t{}; + settings.time_limit = 30.; + auto timer = cuopt::timer_t(30); + detail::mip_solver_t solver(problem, settings, scaling, timer); + problem.tolerances = settings.get_tolerances(); + + detail::diversity_manager_t diversity_manager(solver.context); + diversity_manager.settings.fj_only_run = true; + // run with FJ only on the initial population, no recombining + diversity_manager.run_solver(); + + std::vector hashes; + + auto pop = diversity_manager.get_population_pointer(); + for (const auto& sol : pop->population_to_vector()) { + hashes.push_back(sol.get_hash()); + } + + return detail::compute_hash(hashes); +} + +// TEST(presolve, probing_cache_deterministic) +// { +// spin_stream_raii_t spin_stream_1; + +// std::vector test_instances = {"mip/50v-10-free-bound.mps", +// "mip/neos5-free-bound.mps", +// "mip/neos5.mps", +// "mip/50v-10.mps", +// "mip/gen-ip054.mps", +// "mip/rmatr200-p5.mps"}; +// for (const auto& test_instance : test_instances) { +// std::cout << "Running: " << test_instance << std::endl; +// unsigned long seed = std::random_device{}(); +// std::cerr << "Tested with seed " << seed << "\n"; +// auto path = make_path_absolute(test_instance); +// uint32_t gold_hash = 0; +// for (int i = 0; i < 10; ++i) { +// auto hash = test_probing_cache_determinism(path, seed); +// if (i == 0) { +// gold_hash = hash; +// std::cout << "Gold hash: " << gold_hash << std::endl; +// } else { +// EXPECT_EQ(hash, gold_hash); +// } +// } +// } +// } + +class DiversityTestParams : public testing::TestWithParam> {}; + +TEST_P(DiversityTestParams, initial_population_deterministic) +{ + cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); + + spin_stream_raii_t spin_stream_1; + spin_stream_raii_t spin_stream_2; + + auto test_instance = std::get<0>(GetParam()); + std::cout << "Running: " << test_instance << std::endl; + int seed = + std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); + cuopt::seed_generator::set_seed(seed); + std::cerr << "Tested with seed " << seed << "\n"; + auto path = make_path_absolute(test_instance); + uint32_t gold_hash = 0; + for (int i = 0; i < 2; ++i) { + std::cout << "Running " << test_instance << " " << i << std::endl; + auto hash = test_initial_population_determinism(path, seed); + if (i == 0) { + gold_hash = hash; + std::cout << "Gold hash: " << gold_hash << std::endl; + } else { + ASSERT_EQ(hash, gold_hash); + } + } +} + +INSTANTIATE_TEST_SUITE_P(DiversityTest, + DiversityTestParams, + testing::Values( + // std::make_tuple("mip/sct2.mps"), + // std::make_tuple("mip/thor50dday.mps"), + // std::make_tuple("mip/uccase9.mps"), + // std::make_tuple("mip/neos5-free-bound.mps"), + // std::make_tuple("mip/neos5.mps"), + // std::make_tuple("mip/50v-10.mps"), + // std::make_tuple("mip/rmatr200-p5.mps") + std::make_tuple("mip/gen-ip054.mps"))); + +} // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index 07f625e3ec..186523d27e 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -223,7 +223,8 @@ static bool run_fj_check_feasible(std::string test_instance) static bool run_fj_check_determinism(std::string test_instance, int iter_limit) { - int seed = std::getenv("FJ_SEED") ? std::stoi(std::getenv("FJ_SEED")) : 42; + int seed = + std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); detail::fj_settings_t fj_settings; fj_settings.time_limit = 30.; diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index a3ef1abedd..3fd399699a 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -204,7 +204,8 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) static uint32_t run_fp_check_determinism(std::string test_instance, local_search_mode_t mode) { - int seed = std::getenv("FJ_SEED") ? std::stoi(std::getenv("FJ_SEED")) : 42; + int seed = + std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); cuopt::seed_generator::set_seed(seed); return run_fp(test_instance, mode); diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index a043e2d74a..6ba4edbdfe 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -171,22 +172,91 @@ uint32_t test_probing_cache_determinism(std::string path, return detail::compute_hash(hashes); } -// TEST(presolve, multi_probe) +uint32_t test_scaling_determinism(std::string path, unsigned long seed = std::random_device{}()) +{ + auto memory_resource = make_async(); + rmm::mr::set_current_device_resource(memory_resource.get()); + const raft::handle_t handle_{}; + cuopt::mps_parser::mps_data_model_t mps_problem = + cuopt::mps_parser::parse_mps(path, false); + handle_.sync_stream(); + auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); + problem_checking_t::check_problem_representation(op_problem); + detail::problem_t problem(op_problem); + + pdlp_hyper_params::update_primal_weight_on_initial_solution = false; + pdlp_hyper_params::update_step_size_on_initial_solution = true; + // problem contains unpreprocessed data + detail::problem_t scaled_problem(problem); + + detail::pdhg_solver_t pdhg_solver(scaled_problem.handle_ptr, scaled_problem); + detail::pdlp_initial_scaling_strategy_t scaling( + scaled_problem.handle_ptr, + scaled_problem, + pdlp_hyper_params::default_l_inf_ruiz_iterations, + (double)pdlp_hyper_params::default_alpha_pock_chambolle_rescaling, + pdhg_solver, + scaled_problem.reverse_coefficients, + scaled_problem.reverse_offsets, + scaled_problem.reverse_constraints, + true); + + scaling.scale_problem(); + + // generate a random initial solution in order to ensure scaling of solution vectors is + // deterministic as well as the initial step size + std::vector initial_solution(scaled_problem.n_variables); + std::mt19937 m{seed}; + std::generate(initial_solution.begin(), initial_solution.end(), [&m]() { return m(); }); + auto d_initial_solution = device_copy(initial_solution, handle_.get_stream()); + scaling.scale_primal(d_initial_solution); + + scaled_problem.preprocess_problem(); + + detail::trivial_presolve(scaled_problem); + + std::vector hashes; + hashes.push_back(detail::compute_hash(d_initial_solution, handle_.get_stream())); + hashes.push_back(scaled_problem.get_fingerprint()); + return detail::compute_hash(hashes); +} + +// TEST(presolve, probing_cache_deterministic) // { -// std::vector test_instances = { -// "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; +// spin_stream_raii_t spin_stream_1; + +// std::vector test_instances = {"mip/50v-10-free-bound.mps", +// "mip/neos5-free-bound.mps", +// "mip/neos5.mps", +// "mip/50v-10.mps", +// "mip/gen-ip054.mps", +// "mip/rmatr200-p5.mps"}; // for (const auto& test_instance : test_instances) { // std::cout << "Running: " << test_instance << std::endl; -// auto path = make_path_absolute(test_instance); -// test_multi_probe(path); +// unsigned long seed = std::random_device{}(); +// std::cerr << "Tested with seed " << seed << "\n"; +// auto path = make_path_absolute(test_instance); +// uint32_t gold_hash = 0; +// for (int i = 0; i < 10; ++i) { +// auto hash = test_probing_cache_determinism(path, seed); +// if (i == 0) { +// gold_hash = hash; +// std::cout << "Gold hash: " << gold_hash << std::endl; +// } else { +// EXPECT_EQ(hash, gold_hash); +// } +// } // } // } -TEST(presolve, probing_cache_deterministic) +TEST(presolve, mip_scaling_deterministic) { spin_stream_raii_t spin_stream_1; + spin_stream_raii_t spin_stream_2; - std::vector test_instances = {"mip/50v-10-free-bound.mps", + std::vector test_instances = {"mip/sct2.mps", + "mip/thor50dday.mps", + "mip/uccase9.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps", "mip/50v-10.mps", @@ -199,7 +269,7 @@ TEST(presolve, probing_cache_deterministic) auto path = make_path_absolute(test_instance); uint32_t gold_hash = 0; for (int i = 0; i < 10; ++i) { - auto hash = test_probing_cache_determinism(path, seed); + auto hash = test_scaling_determinism(path, seed); if (i == 0) { gold_hash = hash; std::cout << "Gold hash: " << gold_hash << std::endl; @@ -209,4 +279,5 @@ TEST(presolve, probing_cache_deterministic) } } } + } // namespace cuopt::linear_programming::test From db75bd6a4c716cad4ca0b76a7fe9ac33deaa12a8 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 22 Jul 2025 14:06:36 +0000 Subject: [PATCH 008/225] fixed test bug --- cpp/src/utilities/seed_generator.cuh | 12 ++++++++++++ cpp/tests/mip/diversity_test.cu | 21 ++++++++++----------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/cpp/src/utilities/seed_generator.cuh b/cpp/src/utilities/seed_generator.cuh index b2af26fc98..35e5f3e72e 100644 --- a/cpp/src/utilities/seed_generator.cuh +++ b/cpp/src/utilities/seed_generator.cuh @@ -40,7 +40,19 @@ class seed_generator { set_seed(seed1 + ((seed0 + seed1) * (seed0 + seed1 + 1) / 2), seeds...); } +#if 0 + static int64_t get_seed(const char* caller = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) { + + printf("&&&&&&& SEED CALLED BY %s:%d: %s() ***\n", + file, + line, + caller); + return seed_++; } +#else static int64_t get_seed() { return seed_++; } +#endif public: seed_generator(seed_generator const&) = delete; diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index c749173f6c..7142eb16c7 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -163,11 +163,11 @@ TEST_P(DiversityTestParams, initial_population_deterministic) std::cout << "Running: " << test_instance << std::endl; int seed = std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); - cuopt::seed_generator::set_seed(seed); std::cerr << "Tested with seed " << seed << "\n"; auto path = make_path_absolute(test_instance); uint32_t gold_hash = 0; - for (int i = 0; i < 2; ++i) { + for (int i = 0; i < 10; ++i) { + cuopt::seed_generator::set_seed(seed); std::cout << "Running " << test_instance << " " << i << std::endl; auto hash = test_initial_population_determinism(path, seed); if (i == 0) { @@ -181,14 +181,13 @@ TEST_P(DiversityTestParams, initial_population_deterministic) INSTANTIATE_TEST_SUITE_P(DiversityTest, DiversityTestParams, - testing::Values( - // std::make_tuple("mip/sct2.mps"), - // std::make_tuple("mip/thor50dday.mps"), - // std::make_tuple("mip/uccase9.mps"), - // std::make_tuple("mip/neos5-free-bound.mps"), - // std::make_tuple("mip/neos5.mps"), - // std::make_tuple("mip/50v-10.mps"), - // std::make_tuple("mip/rmatr200-p5.mps") - std::make_tuple("mip/gen-ip054.mps"))); + testing::Values(std::make_tuple("mip/sct2.mps"), + // std::make_tuple("mip/thor50dday.mps"), + // std::make_tuple("mip/uccase9.mps"), + std::make_tuple("mip/neos5-free-bound.mps"), + std::make_tuple("mip/neos5.mps"), + std::make_tuple("mip/50v-10.mps"), + std::make_tuple("mip/rmatr200-p5.mps"), + std::make_tuple("mip/gen-ip054.mps"))); } // namespace cuopt::linear_programming::test From 7315ab81a66747be7cf935c1485bbf378fc3ff8f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 7 Aug 2025 08:27:15 +0000 Subject: [PATCH 009/225] determinism progress w/ recombiners --- .../restart_strategy/pdlp_restart_strategy.cu | 2 + cpp/src/mip/diversity/diversity_config.hpp | 2 +- cpp/src/mip/diversity/diversity_manager.cu | 43 ++++++--- .../recombiners/bound_prop_recombiner.cuh | 23 ++++- .../diversity/recombiners/fp_recombiner.cuh | 11 +++ .../recombiners/line_segment_recombiner.cuh | 1 + .../mip/diversity/recombiners/recombiner.cuh | 11 +++ .../mip/feasibility_jump/feasibility_jump.cu | 2 + .../feasibility_pump/feasibility_pump.cu | 34 +++++-- cpp/src/mip/local_search/local_search.cu | 52 ++++++---- .../local_search/rounding/bounds_repair.cu | 3 +- .../local_search/rounding/constraint_prop.cu | 55 ++++++----- .../local_search/rounding/lb_bounds_repair.cu | 2 +- cpp/src/mip/presolve/bounds_presolve.cu | 4 + cpp/src/mip/problem/problem.cu | 13 ++- cpp/src/mip/relaxed_lp/relaxed_lp.cu | 4 + cpp/tests/mip/diversity_test.cu | 96 +++++++++++-------- cpp/tests/mip/miplib_test.cu | 12 ++- 18 files changed, 264 insertions(+), 106 deletions(-) diff --git a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu index 55b06aecf4..e2fe05a9ab 100644 --- a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu +++ b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu @@ -1490,6 +1490,8 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( // Perform the reduction // Convert raw pointer to thrust::device_ptr to write directly device side through reduce + // CHANGE + // use a deterministic reduce instead of thrust::reduce thrust::device_ptr thrust_hrsp(high_radius_squared_.data()); *thrust_hrsp = thrust::reduce(handle_ptr_->get_thrust_policy(), transformed_begin, diff --git a/cpp/src/mip/diversity/diversity_config.hpp b/cpp/src/mip/diversity/diversity_config.hpp index 30e7d25e47..07eaa33f8e 100644 --- a/cpp/src/mip/diversity/diversity_config.hpp +++ b/cpp/src/mip/diversity/diversity_config.hpp @@ -30,7 +30,7 @@ struct diversity_config_t { static constexpr double initial_infeasibility_weight = 1000.; static constexpr double default_time_limit = 10.; static constexpr int initial_island_size = 3; - static constexpr int maximum_island_size = 8; + static constexpr int maximum_island_size = 4; // CHANGE static constexpr bool use_avg_diversity = false; static constexpr double generation_time_limit_ratio = 0.6; static constexpr double max_island_gen_time = 600; diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 617c89b600..dfcc3ed945 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -74,6 +74,13 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::generate_initial_solutions() diversity_config_t::generation_time_limit_ratio * timer.get_time_limit(); const f_t max_island_gen_time = diversity_config_t::max_island_gen_time; f_t total_island_gen_time = std::min(generation_time_limit, max_island_gen_time); + total_island_gen_time = std::numeric_limits::infinity(); timer_t gen_timer(total_island_gen_time); f_t sol_time_limit = gen_timer.remaining_time(); for (i_t i = 0; i < maximum_island_size && !skip_initial_island_generation; ++i) { @@ -246,8 +254,7 @@ void diversity_manager_t::generate_initial_solutions() } generate_add_solution(initial_sol_vector, sol_time_limit, !is_first_sol); if (is_first_sol && initial_sol_vector.back().get_feasible()) { - CUOPT_LOG_DEBUG("First FP/FJ solution found at %f with objective %f", - timer.elapsed_time(), + CUOPT_LOG_DEBUG("First FP/FJ solution found with objective %f", initial_sol_vector.back().get_user_objective()); } population.run_solution_callbacks(initial_sol_vector.back()); @@ -308,8 +315,11 @@ void diversity_manager_t::generate_quick_feasible_solution() { solution_t solution(*problem_ptr); // min 1 second, max 10 seconds - const f_t generate_fast_solution_time = + f_t generate_fast_solution_time = std::min(diversity_config_t::max_fast_sol_time, std::max(1., timer.remaining_time() / 20.)); + + // CHANGE + generate_fast_solution_time = 10; timer_t sol_timer(generate_fast_solution_time); // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); @@ -324,9 +334,7 @@ void diversity_manager_t::generate_quick_feasible_solution() auto& feas_sol = initial_sol_vector.back().get_feasible() ? initial_sol_vector.back() : initial_sol_vector[initial_sol_vector.size() - 2]; - CUOPT_LOG_INFO("Generated fast solution in %f seconds with objective %f", - timer.elapsed_time(), - feas_sol.get_user_objective()); + CUOPT_LOG_INFO("Generated fast solution with objective %f", feas_sol.get_user_objective()); } problem_ptr->handle_ptr->sync_stream(); } @@ -358,6 +366,8 @@ void diversity_manager_t::run_fj_alone(solution_t& solution) ls.fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_ITERATION_LIMIT; ls.fj.settings.iteration_limit = 10000; ls.fj.settings.time_limit = timer.remaining_time(); + // CHANGE + ls.fj.settings.time_limit = std::numeric_limits::infinity(); // ls.fj.solve(solution); CUOPT_LOG_INFO("FJ alone finished!"); } @@ -366,10 +376,13 @@ void diversity_manager_t::run_fj_alone(solution_t& solution) template solution_t diversity_manager_t::run_solver() { - population.timer = timer; - const f_t time_limit = timer.remaining_time(); - const f_t lp_time_limit = std::min(diversity_config_t::max_time_on_lp, - time_limit * diversity_config_t::time_ratio_on_init_lp); + population.timer = timer; + const f_t time_limit = timer.remaining_time(); + f_t lp_time_limit = std::min(diversity_config_t::max_time_on_lp, + time_limit * diversity_config_t::time_ratio_on_init_lp); + + // CHANGE + lp_time_limit = 600; // to automatically compute the solving time on scope exit auto timer_raii_guard = cuopt::scope_guard([&]() { stats.total_solve_time = timer.elapsed_time(); }); @@ -412,10 +425,12 @@ solution_t diversity_manager_t::run_solver() lp_state.resize(*problem_ptr, problem_ptr->handle_ptr->get_stream()); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = lp_time_limit; + lp_settings.iteration_limit = std::numeric_limits::max(); lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; lp_settings.return_first_feasible = false; lp_settings.save_state = true; - if (!settings.fj_only_run) { + if (!settings.fj_only_run || true) { // CHANGE + CUOPT_LOG_DEBUG("Running root relaxation LP"); auto lp_result = get_relaxed_lp_solution(*problem_ptr, lp_optimal_solution, lp_state, lp_settings); ls.lp_optimal_exists = true; @@ -427,9 +442,10 @@ solution_t diversity_manager_t::run_solver() } else if (lp_result.get_termination_status() == pdlp_termination_status_t::DualInfeasible) { CUOPT_LOG_ERROR("PDLP detected dual infeasibility, continuing anyway!"); ls.lp_optimal_exists = false; - } else if (lp_result.get_termination_status() == pdlp_termination_status_t::TimeLimit) { + } else if (lp_result.get_termination_status() == pdlp_termination_status_t::TimeLimit || + lp_result.get_termination_status() == pdlp_termination_status_t::IterationLimit) { CUOPT_LOG_DEBUG( - "Initial LP run exceeded time limit, continuing solver with partial LP result!"); + "Initial LP run exceeded time/iteration limit, continuing solver with partial LP result!"); // note to developer, in debug mode the LP run might be too slow and it might cause PDLP not // to bring variables within the bounds } @@ -705,6 +721,7 @@ template std::pair, bool> diversity_manager_t::recombine( solution_t& a, solution_t& b) { + CUOPT_LOG_DEBUG("Recombining %d and %d", a.get_hash(), b.get_hash()); i_t recombiner; if (run_only_ls_recombiner) { recombiner = recombiner_enum_t::LINE_SEGMENT; diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index f38cc5759e..9ac6e37054 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -38,6 +38,7 @@ class bound_prop_recombiner_t : public recombiner_t { rng(cuopt::seed_generator::get_seed()), vars_to_fix(n_vars, handle_ptr->get_stream()) { + thrust::fill(handle_ptr->get_thrust_policy(), vars_to_fix.begin(), vars_to_fix.end(), -1); } void get_probing_values_for_infeasible( @@ -203,16 +204,34 @@ class bound_prop_recombiner_t : public recombiner_t { constraint_prop.single_rounding_only = true; constraint_prop.apply_round(offspring, lp_run_time_after_feasible, timer, probing_config); constraint_prop.single_rounding_only = false; + offspring.compute_feasibility(); cuopt_func_call(bool feasible_after_bounds_prop = offspring.get_feasible()); + cuopt_func_call(f_t excess_before = offspring.get_total_excess()); + CUOPT_LOG_ERROR("Excess before: %g, %g, %g, %g, feas %d", + offspring.get_total_excess(), + offspring.compute_max_constraint_violation(), + offspring.compute_max_int_violation(), + offspring.compute_max_variable_violation(), + feasible_after_bounds_prop); offspring.handle_ptr->sync_stream(); offspring.problem_ptr = a.problem_ptr; fixed_assignment = std::move(offspring.assignment); offspring.assignment = std::move(old_assignment); offspring.handle_ptr->sync_stream(); offspring.unfix_variables(fixed_assignment, variable_map); + offspring.compute_feasibility(); cuopt_func_call(bool feasible_after_unfix = offspring.get_feasible()); - cuopt_assert(feasible_after_unfix == feasible_after_bounds_prop, - "Feasible after unfix should be same as feasible after bounds prop!"); + cuopt_func_call(f_t excess_after_unfix = offspring.get_total_excess()); + if (feasible_after_unfix != feasible_after_bounds_prop) { + CUOPT_LOG_WARN("Numerical issue in bounds prop, infeasibility after unfix"); + // might become infeasible after unfixing due to numerical issues. Check that the excess + // remains consistent + // CUOPT_LOG_ERROR("Excess: %g, %g, %g, %g, feas %d", offspring.get_total_excess(), + // offspring.compute_max_constraint_violation(), offspring.compute_max_int_violation(), + // offspring.compute_max_variable_violation(), feasible_after_unfix); + cuopt_assert(fabs(excess_after_unfix - excess_before) < 1e-6, + "Excess after unfix should be same as before unfix!"); + } a.handle_ptr->sync_stream(); } else { timer_t timer(bp_recombiner_config_t::bounds_prop_time_limit); diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index 5597e8e845..c161504e6e 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -57,6 +57,7 @@ class fp_recombiner_t : public recombiner_t { CUOPT_LOG_DEBUG("FP rec: Number of different variables %d MAX_VARS %d", n_different_vars, fp_recombiner_config_t::max_n_of_vars_from_other); + CUOPT_LOG_DEBUG("FP rec: offspring hash 0x%x", offspring.get_hash()); i_t n_vars_from_other = n_different_vars; if (n_vars_from_other > (i_t)fp_recombiner_config_t::max_n_of_vars_from_other) { n_vars_from_other = fp_recombiner_config_t::max_n_of_vars_from_other; @@ -73,10 +74,20 @@ class fp_recombiner_t : public recombiner_t { } CUOPT_LOG_DEBUG( "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); + CUOPT_LOG_DEBUG("FP rec: offspring hash 0x%x, vars to fix 0x%x", + offspring.get_hash(), + detail::compute_hash(vars_to_fix)); this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); + CUOPT_LOG_DEBUG("FP rec post computevarstofix: offspring hash 0x%x, vars to fix 0x%x", + offspring.get_hash(), + detail::compute_hash(vars_to_fix)); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); + CUOPT_LOG_DEBUG("FP rec: fixed_problem hash 0x%x assigned hash 0x%x", + fixed_problem.get_fingerprint(), + detail::compute_hash(fixed_assignment)); fixed_problem.check_problem_representation(true); if (!guiding_solution.get_feasible() && !other_solution.get_feasible()) { + CUOPT_LOG_DEBUG("FP rec: running LP with infeasibility detection"); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; lp_settings.tolerance = fixed_problem.tolerances.absolute_tolerance; diff --git a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh index cfde6d03c4..fd2988d983 100644 --- a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh @@ -80,6 +80,7 @@ class line_segment_recombiner_t : public recombiner_t { const weight_t& weights) { raft::common::nvtx::range fun_scope("line_segment_recombiner"); + CUOPT_LOG_DEBUG("LS rec: a %d b %d", a.get_hash(), b.get_hash()); auto& guiding_solution = a.get_feasible() ? a : b; auto& other_solution = a.get_feasible() ? b : a; // copy the solution from A diff --git a/cpp/src/mip/diversity/recombiners/recombiner.cuh b/cpp/src/mip/diversity/recombiners/recombiner.cuh index e4a86b4914..d6dc4dabf5 100644 --- a/cpp/src/mip/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/recombiner.cuh @@ -99,6 +99,14 @@ class recombiner_t { cuopt::make_span(remaining_indices), n_remaining.data()); i_t remaining_variables = this->n_remaining.value(a.handle_ptr->get_stream()); + // Sort the indices to resolve nondeterministic order due to atomicAdd + thrust::sort(a.handle_ptr->get_thrust_policy(), + this->remaining_indices.data(), + this->remaining_indices.data() + remaining_variables); + + CUOPT_LOG_DEBUG("remaining indices hash 0x%x, size %d", + detail::compute_hash(this->remaining_indices), + remaining_variables); auto vec_remaining_indices = host_copy(this->remaining_indices.data(), remaining_variables, a.handle_ptr->get_stream()); @@ -177,6 +185,9 @@ class recombiner_t { i_t n_vars_from_guiding) { vars_to_fix.resize(n_vars_from_guiding, offspring.handle_ptr->get_stream()); + CUOPT_LOG_DEBUG("remaining indices hash 0x%x", detail::compute_hash(this->remaining_indices)); + CUOPT_LOG_DEBUG("integer_indices hash 0x%x", + detail::compute_hash(offspring.problem_ptr->integer_indices)); // set difference needs two sorted arrays thrust::sort(offspring.handle_ptr->get_thrust_policy(), this->remaining_indices.data(), diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 37567dd7d3..6d1c554437 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -1157,6 +1157,8 @@ i_t fj_t::solve(solution_t& solution) cuopt_assert(false, "Feasibility jump caused feasible solution to become infeasible"); } + cuopt_func_call(solution.test_variable_bounds()); + return is_new_feasible; } diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index d18d29a5a5..7789fcbffa 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -67,6 +67,10 @@ feasibility_pump_t::feasibility_pump_t( rng(cuopt::seed_generator::get_seed()), timer(20.) { + thrust::fill(context.problem_ptr->handle_ptr->get_thrust_policy(), + last_projection.begin(), + last_projection.end(), + (f_t)0); } template @@ -146,7 +150,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tvariable_upper_bounds, solution.handle_ptr->get_stream()); @@ -162,6 +166,13 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tinteger_indices, solution.handle_ptr->get_stream()); f_t obj_offset = 0; + CUOPT_LOG_DEBUG("FP: h_integer_indices hash 0x%x", detail::compute_hash(h_integer_indices)); + CUOPT_LOG_DEBUG("FP: h_assignment hash 0x%x", detail::compute_hash(h_assignment)); + CUOPT_LOG_DEBUG("FP: h_variable_upper_bounds hash 0x%x", + detail::compute_hash(h_variable_upper_bounds)); + CUOPT_LOG_DEBUG("FP: h_variable_lower_bounds hash 0x%x", + detail::compute_hash(h_variable_lower_bounds)); + CUOPT_LOG_DEBUG("FP: h_last_projection hash 0x%x", detail::compute_hash(h_last_projection)); // for each integer add the variable and the distance constraints for (auto i : h_integer_indices) { if (solution.problem_ptr->integer_equal(h_assignment[i], h_variable_upper_bounds[i])) { @@ -198,6 +209,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t 0) { temp_p.insert_variables(h_variables); } @@ -231,12 +243,17 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tn_variables, solution.handle_ptr->get_stream()); raft::copy(last_projection.data(), solution.assignment.data(), @@ -286,6 +303,7 @@ void feasibility_pump_t::perturbate(solution_t& solution) template bool feasibility_pump_t::run_fj_cycle_escape(solution_t& solution) { + CUOPT_LOG_DEBUG("Running FJ cycle escape"); bool is_feasible; fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; fj.settings.update_weights = true; @@ -308,14 +326,16 @@ bool feasibility_pump_t::run_fj_cycle_escape(solution_t& sol template bool feasibility_pump_t::test_fj_feasible(solution_t& solution, f_t time_limit) { - CUOPT_LOG_DEBUG("Running 20%% with %f time limit", time_limit); + CUOPT_LOG_DEBUG("Running 20%% FJ"); bool is_feasible; fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; fj.settings.update_weights = true; fj.settings.feasibility_run = true; fj.settings.n_of_minimums_for_exit = 5000; fj.settings.time_limit = std::min(time_limit, timer.remaining_time()); - fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_TIME_LIMIT; + // CHANGE + fj.settings.time_limit = std::numeric_limits::infinity(); + fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_TIME_LIMIT; cuopt_func_call(solution.test_variable_bounds(true)); is_feasible = fj.solve(solution); cuopt_func_call(solution.test_variable_bounds(true)); @@ -497,6 +517,8 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s solution.assignment.data(), solution.assignment.size(), solution.handle_ptr->get_stream()); + + CUOPT_LOG_DEBUG("FP: starting FP descent, sol hash 0x%x", solution.get_hash()); while (true) { if (timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); @@ -529,7 +551,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s cuopt::default_logger().flush(); f_t remaining_time_end_fp = timer.remaining_time(); total_fp_time_until_cycle = fp_fj_cycle_time_begin - remaining_time_end_fp; - CUOPT_LOG_DEBUG("total_fp_time_until_cycle: %f", total_fp_time_until_cycle); + // CUOPT_LOG_DEBUG("total_fp_time_until_cycle: %f", total_fp_time_until_cycle); return false; } } diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index efda50647b..7a087d0f8d 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -56,6 +56,7 @@ local_search_t::local_search_t(mip_solver_context_t& context template void local_search_t::generate_fast_solution(solution_t& solution, timer_t timer) { + CUOPT_LOG_DEBUG("Running FJ fast sol"); thrust::fill(solution.handle_ptr->get_thrust_policy(), solution.assignment.begin(), solution.assignment.end(), @@ -67,6 +68,8 @@ void local_search_t::generate_fast_solution(solution_t& solu fj.settings.feasibility_run = true; fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_TIME_LIMIT; fj.settings.time_limit = std::min(30., timer.remaining_time()); + // CHANGE + fj.settings.time_limit = std::numeric_limits::infinity(); while (!timer.check_time_limit()) { timer_t constr_prop_timer = timer_t(std::min(timer.remaining_time(), 2.)); // do constraint prop on lp optimal solution @@ -74,6 +77,9 @@ void local_search_t::generate_fast_solution(solution_t& solu if (solution.compute_feasibility()) { return; } if (timer.check_time_limit()) { return; }; fj.settings.time_limit = std::min(3., timer.remaining_time()); + // CHANGE + fj.settings.time_limit = std::numeric_limits::infinity(); + fj.settings.iteration_limit = 5000; // run fj on the solution fj.solve(solution); // TODO check if FJ returns the same solution @@ -91,19 +97,20 @@ bool local_search_t::run_local_search(solution_t& solution, { raft::common::nvtx::range fun_scope("local search"); fj_settings_t fj_settings; - if (timer.check_time_limit()) return false; - // adjust these time limits - if (!solution.get_feasible()) { - if (at_least_one_parent_feasible) { - fj_settings.time_limit = 1.; - timer = timer_t(1.); - } else { - fj_settings.time_limit = 0.5; - timer = timer_t(0.5); - } - } else { - fj_settings.time_limit = timer.remaining_time(); - } + // if (timer.check_time_limit()) return false; + // // adjust these time limits + // if (!solution.get_feasible()) { + // if (at_least_one_parent_feasible) { + // fj_settings.time_limit = 1.; + // timer = timer_t(1.); + // } else { + // fj_settings.time_limit = 0.5; + // timer = timer_t(0.5); + // } + // } else { + // fj_settings.time_limit = timer.remaining_time(); + // } + fj_settings.iteration_limit = 2500; fj_settings.update_weights = false; fj_settings.feasibility_run = false; fj.set_fj_settings(fj_settings); @@ -123,6 +130,7 @@ bool local_search_t::run_fj_until_timer(solution_t& solution const weight_t& weights, timer_t timer) { + CUOPT_LOG_DEBUG("Running FJ until timer"); bool is_feasible; fj.settings.n_of_minimums_for_exit = 1e6; fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; @@ -143,6 +151,8 @@ bool local_search_t::run_fj_annealing(solution_t& solution, timer_t timer, f_t baseline_objective) { + CUOPT_LOG_DEBUG("Running FJ annealing"); + auto prev_settings = fj.settings; // run in FEASIBLE_FIRST to priorize feasibility-improving moves @@ -182,6 +192,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu bool perturb, timer_t timer) { + CUOPT_LOG_DEBUG("Running FJ on LP optimal"); raft::copy(solution.assignment.data(), lp_optimal_solution.data(), solution.assignment.size(), @@ -197,12 +208,17 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu bool is_feasible = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); if (!is_feasible) { - const f_t lp_run_time = 2.; + f_t lp_run_time = 2.; + // CHANGE + lp_run_time = 600; relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_run_time; - lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; + lp_settings.time_limit = lp_run_time; + lp_settings.iteration_limit = 13000; // CHANGE + lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; + CUOPT_LOG_DEBUG("FJ ON LP OPT: lp_time_limit %f", lp_settings.time_limit); run_lp_with_vars_fixed( *solution.problem_ptr, solution, solution.problem_ptr->integer_indices, lp_settings); + CUOPT_LOG_DEBUG("FJ ON LP OPT: exited", lp_settings.time_limit); } else { return is_feasible; } @@ -213,6 +229,9 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu fj.settings.feasibility_run = true; fj.settings.termination = fj_termination_flags_t::FJ_TERMINATION_TIME_LIMIT; fj.settings.time_limit = std::min(30., timer.remaining_time()); + // CHANGE + fj.settings.time_limit = 600; // CHANGE + fj.settings.iteration_limit = 50000; // CHANGE fj.solve(solution); return solution.get_feasible(); } @@ -220,6 +239,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu template bool local_search_t::run_fj_on_zero(solution_t& solution, timer_t timer) { + CUOPT_LOG_DEBUG("Running FJ on zero"); thrust::fill(solution.handle_ptr->get_thrust_policy(), solution.assignment.begin(), solution.assignment.end(), diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index 4b9c0ca504..928250d2d7 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -396,7 +396,8 @@ bool bounds_repair_t::repair_problem(problem_t& problem, curr_violation = best_violation; best_bounds.update_from(problem, handle_ptr); i_t no_candidate_in_a_row = 0; - while (h_n_violated_cstr > 0) { + i_t iter_limit = 20; + while (h_n_violated_cstr > 0 && iter_limit-- > 0) { CUOPT_LOG_TRACE("Bounds repair loop: n_violated %d best_violation %f curr_violation %f", h_n_violated_cstr, best_violation, diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 4661f0906f..ff56cae722 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -754,6 +754,10 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob const raft::handle_t* handle_ptr) { CUOPT_LOG_DEBUG("Running repair procedure"); + + // CHANGE + timer = timer_t(std::numeric_limits::infinity()); + // select the first probing value i_t select = 0; multi_probe.set_updated_bounds(problem, select, handle_ptr); @@ -761,9 +765,10 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob repair_stats.repair_attempts++; f_t repair_start_time = timer.remaining_time(); i_t n_of_repairs_needed_for_feasible = 0; + i_t iter_limit = 100; do { n_of_repairs_needed_for_feasible++; - if (timer.check_time_limit()) { + if (timer.check_time_limit() || iter_limit-- <= 0) { CUOPT_LOG_DEBUG("Time limit is reached in repair loop!"); f_t repair_end_time = timer.remaining_time(); repair_stats.total_time_spent_on_repair += repair_start_time - repair_end_time; @@ -831,6 +836,10 @@ bool constraint_prop_t::find_integer( i_t seed = cuopt::seed_generator::get_seed(); CUOPT_LOG_DEBUG("seed 0x%x", seed); std::mt19937 rng(seed); + + // CHANGE + timer = timer_t(std::numeric_limits::infinity()); + lb_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); ub_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); assignment_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); @@ -881,23 +890,23 @@ bool constraint_prop_t::find_integer( } // do the sort if the problem is not ii. crossing bounds might cause some issues on the sort order else { - CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x, sol 0x%x before sort\n", - detail::compute_hash(unset_integer_vars), - sol.get_hash()); + // CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x, sol 0x%x before sort\n", + // detail::compute_hash(unset_integer_vars), + // sol.get_hash()); // this is a sort to have initial shuffling, so that stable sort within will keep the order and // some randomness will be achieved sort_by_interval_and_frac(sol, make_span(unset_integer_vars), rng); - CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x sol 0x%x after sort\n", - detail::compute_hash(unset_integer_vars), - sol.get_hash()); + // CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x sol 0x%x after sort\n", + // detail::compute_hash(unset_integer_vars), + // sol.get_hash()); } set_host_bounds(sol); size_t set_count = 0; bool timeout_happened = false; i_t n_failed_repair_iterations = 0; while (set_count < unset_integer_vars.size()) { - CUOPT_LOG_DEBUG("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); - CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x\n", detail::compute_hash(unset_integer_vars)); + // CUOPT_LOG_DEBUG("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); + // CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x\n", detail::compute_hash(unset_integer_vars)); update_host_assignment(sol); if (max_timer.check_time_limit()) { CUOPT_LOG_DEBUG("Second time limit is reached returning nearest rounding!"); @@ -933,7 +942,7 @@ bool constraint_prop_t::find_integer( n_vars_to_set, sol.handle_ptr->get_stream()); - printf("host_vars_to_set hash 0x%x\n", detail::compute_hash(host_vars_to_set)); + // printf("host_vars_to_set hash 0x%x\n", detail::compute_hash(host_vars_to_set)); auto var_probe_vals = generate_bulk_rounding_vector(sol, orig_sol, host_vars_to_set, probing_config); @@ -1012,7 +1021,9 @@ bool constraint_prop_t::find_integer( multi_probe.infeas_constraints_count_1 == 0) && !timeout_happened) { relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_run_time_after_feasible; + lp_settings.time_limit = lp_run_time_after_feasible; + // CHANGE + lp_settings.time_limit = 600; lp_settings.tolerance = orig_sol.problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; @@ -1056,17 +1067,17 @@ bool constraint_prop_t::apply_round( f_t bounds_prop_end_time = max_timer.remaining_time(); repair_stats.total_time_spent_on_bounds_prop += bounds_prop_start_time - bounds_prop_end_time; - CUOPT_LOG_DEBUG( - "repair_success %lu repair_attempts %lu intermediate_repair_success %lu total_repair_loops %lu " - "total_time_spent_on_repair %f total_time_spent_bounds_prop_after_repair %f " - "total_time_spent_on_bounds_prop %f", - repair_stats.repair_success, - repair_stats.repair_attempts, - repair_stats.intermediate_repair_success, - repair_stats.total_repair_loops, - repair_stats.total_time_spent_on_repair, - repair_stats.total_time_spent_bounds_prop_after_repair, - repair_stats.total_time_spent_on_bounds_prop); + // CUOPT_LOG_DEBUG( + // "repair_success %lu repair_attempts %lu intermediate_repair_success %lu total_repair_loops + // %lu " "total_time_spent_on_repair %f total_time_spent_bounds_prop_after_repair %f " + // "total_time_spent_on_bounds_prop %f", + // repair_stats.repair_success, + // repair_stats.repair_attempts, + // repair_stats.intermediate_repair_success, + // repair_stats.total_repair_loops, + // repair_stats.total_time_spent_on_repair, + // repair_stats.total_time_spent_bounds_prop_after_repair, + // repair_stats.total_time_spent_on_bounds_prop); if (!sol_found) { sol.compute_feasibility(); return false; diff --git a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu index 50f6cdf006..4844bfaebb 100644 --- a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu @@ -410,7 +410,7 @@ bool lb_bounds_repair_t::repair_problem( timer_t timer_, const raft::handle_t* handle_ptr_) { - CUOPT_LOG_DEBUG("Running bounds repair"); + CUOPT_LOG_DEBUG("LB Running bounds repair"); handle_ptr = handle_ptr_; timer = timer_; resize(*problem); diff --git a/cpp/src/mip/presolve/bounds_presolve.cu b/cpp/src/mip/presolve/bounds_presolve.cu index 45fee622eb..b4d3a9b04b 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cu +++ b/cpp/src/mip/presolve/bounds_presolve.cu @@ -181,6 +181,10 @@ termination_criterion_t bound_presolve_t::bound_update_loop(problem_t< { termination_criterion_t criteria = termination_criterion_t::ITERATION_LIMIT; + // CHANGE + timer = timer_t(std::numeric_limits::infinity()); + settings.iteration_limit = std::min(settings.iteration_limit, 50); + i_t iter; upd.init_changed_constraints(pb.handle_ptr); for (iter = 0; iter < settings.iteration_limit; ++iter) { diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 06ae72596f..1199c1ee7f 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -1169,6 +1169,9 @@ problem_t problem_t::get_problem_after_fixing_vars( RAFT_CHECK_CUDA(handle_ptr->get_stream()); cuopt_assert(result_end - variable_map.data() == variable_map.size(), "Size issue in set_difference"); + CUOPT_LOG_DEBUG("Fixing assignment hash 0x%x, vars to fix 0x%x", + detail::compute_hash(assignment), + detail::compute_hash(variables_to_fix)); problem.fix_given_variables(*this, assignment, variables_to_fix, handle_ptr); RAFT_CHECK_CUDA(handle_ptr->get_stream()); problem.remove_given_variables(*this, assignment, variable_map, handle_ptr); @@ -1193,11 +1196,11 @@ problem_t problem_t::get_problem_after_fixing_vars( static int total_calls = 0; total_time_taken += time_taken; total_calls++; - CUOPT_LOG_DEBUG( - "Time taken to fix variables: %f milliseconds, average: %f milliseconds total time: %f", - time_taken, - total_time_taken / total_calls, - total_time_taken); + // CUOPT_LOG_DEBUG( + // "Time taken to fix variables: %f milliseconds, average: %f milliseconds total time: %f", + // time_taken, + // total_time_taken / total_calls, + // total_time_taken); CUOPT_LOG_DEBUG("Model fingerprint after fixing: 0x%x", problem.get_fingerprint()); return problem; } diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index 9119f924a5..e15cc99385 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -89,7 +89,11 @@ optimization_problem_solution_t get_relaxed_lp_solution( // temporarily add timer auto start_time = std::chrono::high_resolution_clock::now(); lp_solver.set_inside_mip(true); + CUOPT_LOG_DEBUG("prev primal hash 0x%x", detail::compute_hash(assignment)); + CUOPT_LOG_DEBUG("prev dual hash 0x%x", detail::compute_hash(lp_state.prev_dual)); auto solver_response = lp_solver.run_solver(start_time); + CUOPT_LOG_DEBUG("post LP primal hash 0x%x", + detail::compute_hash(solver_response.get_primal_solution())); if (solver_response.get_primal_solution().size() != 0 && solver_response.get_dual_solution().size() != 0 && settings.save_state) { diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index 7142eb16c7..2d31580b2a 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -112,44 +112,59 @@ static uint32_t test_initial_population_determinism(std::string path, // run with FJ only on the initial population, no recombining diversity_manager.run_solver(); + // recombine a few solutions, observe the output + auto pop_vector = diversity_manager.get_population_pointer()->population_to_vector(); + // necessary because .resize() complains about constructors (since it may be used to grow the + // vector as well) + while (pop_vector.size() > 9) { + pop_vector.pop_back(); + } + std::vector hashes; + static std::map, uint32_t> hash_map; + + // diversity_manager.run_only_ls_recombiner = true; + diversity_manager.timer = timer_t(60000); + // diversity_manager.recombine_and_ls_with_all(pop_vector); + for (int i = 1; i < (int)pop_vector.size(); i++) { + for (int j = i + 1; j < (int)pop_vector.size(); j++) { + printf("recombining %d and %d\n", i, j); + auto [offspring, success] = diversity_manager.recombine(pop_vector[i], pop_vector[j]); + auto offspring_hash = offspring.get_hash(); + printf("for %d,%d: offspring hash: 0x%x, parent 1 hash: 0x%x, parent 2 hash: 0x%x\n", + i, + j, + offspring_hash, + pop_vector[i].get_hash(), + pop_vector[j].get_hash()); + if (hash_map.find(std::make_pair(i, j)) == hash_map.end()) { + hash_map[std::make_pair(i, j)] = offspring_hash; + } else { + if (hash_map[std::make_pair(i, j)] != offspring_hash) { + printf("hash mismatch for %d,%d: %d != %d\n", + i, + j, + hash_map[std::make_pair(i, j)], + offspring_hash); + exit(1); + } + } + hashes.push_back(offspring_hash); + } + } + return detail::compute_hash(hashes); + auto pop = diversity_manager.get_population_pointer(); for (const auto& sol : pop->population_to_vector()) { hashes.push_back(sol.get_hash()); } - return detail::compute_hash(hashes); + uint32_t final_hash = detail::compute_hash(hashes); + printf("final hash: 0x%x, pop size %d\n", final_hash, (int)pop->population_to_vector().size()); + return final_hash; } -// TEST(presolve, probing_cache_deterministic) -// { -// spin_stream_raii_t spin_stream_1; - -// std::vector test_instances = {"mip/50v-10-free-bound.mps", -// "mip/neos5-free-bound.mps", -// "mip/neos5.mps", -// "mip/50v-10.mps", -// "mip/gen-ip054.mps", -// "mip/rmatr200-p5.mps"}; -// for (const auto& test_instance : test_instances) { -// std::cout << "Running: " << test_instance << std::endl; -// unsigned long seed = std::random_device{}(); -// std::cerr << "Tested with seed " << seed << "\n"; -// auto path = make_path_absolute(test_instance); -// uint32_t gold_hash = 0; -// for (int i = 0; i < 10; ++i) { -// auto hash = test_probing_cache_determinism(path, seed); -// if (i == 0) { -// gold_hash = hash; -// std::cout << "Gold hash: " << gold_hash << std::endl; -// } else { -// EXPECT_EQ(hash, gold_hash); -// } -// } -// } -// } - class DiversityTestParams : public testing::TestWithParam> {}; TEST_P(DiversityTestParams, initial_population_deterministic) @@ -164,11 +179,14 @@ TEST_P(DiversityTestParams, initial_population_deterministic) int seed = std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); std::cerr << "Tested with seed " << seed << "\n"; - auto path = make_path_absolute(test_instance); + auto path = make_path_absolute(test_instance); + test_instance = std::getenv("CUOPT_INSTANCE") ? std::getenv("CUOPT_INSTANCE") : test_instance; + path = "/home/scratch.yboucher_gpu_1/collection/" + test_instance; uint32_t gold_hash = 0; for (int i = 0; i < 10; ++i) { cuopt::seed_generator::set_seed(seed); std::cout << "Running " << test_instance << " " << i << std::endl; + std::cout << "-------------------------------------------------------------\n"; auto hash = test_initial_population_determinism(path, seed); if (i == 0) { gold_hash = hash; @@ -181,13 +199,15 @@ TEST_P(DiversityTestParams, initial_population_deterministic) INSTANTIATE_TEST_SUITE_P(DiversityTest, DiversityTestParams, - testing::Values(std::make_tuple("mip/sct2.mps"), - // std::make_tuple("mip/thor50dday.mps"), - // std::make_tuple("mip/uccase9.mps"), - std::make_tuple("mip/neos5-free-bound.mps"), - std::make_tuple("mip/neos5.mps"), - std::make_tuple("mip/50v-10.mps"), - std::make_tuple("mip/rmatr200-p5.mps"), - std::make_tuple("mip/gen-ip054.mps"))); + testing::Values(std::make_tuple("fastxgemm-n2r6s0t2.mps") + // std::make_tuple("mip/sct2.mps") + // std::make_tuple("mip/thor50dday.mps") + // std::make_tuple("mip/uccase9.mps") + // std::make_tuple("mip/neos5-free-bound.mps") + // std::make_tuple("mip/neos5.mps") + // std::make_tuple("mip/50v-10.mps") + // std::make_tuple("mip/rmatr200-p5.mps") + )); +// std::make_tuple("mip/gen-ip054.mps"))); } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/miplib_test.cu b/cpp/tests/mip/miplib_test.cu index 603fe60145..1f24f23710 100644 --- a/cpp/tests/mip/miplib_test.cu +++ b/cpp/tests/mip/miplib_test.cu @@ -40,7 +40,7 @@ struct result_map_t { double cost; }; -void test_miplib_file(result_map_t test_instance) +void test_miplib_file(result_map_t test_instance, bool heuristic = false) { const raft::handle_t handle_{}; @@ -57,6 +57,7 @@ void test_miplib_file(result_map_t test_instance) #endif settings.time_limit = test_time_limit; + settings.heuristics_only = heuristic; mip_solution_t solution = solve_mip(&handle_, problem, settings); EXPECT_EQ(solution.get_termination_status(), mip_termination_status_t::FeasibleFound); double obj_val = solution.get_objective_value(); @@ -75,4 +76,13 @@ TEST(mip_solve, run_small_tests) } } +// TEST(mip_solve, run_small_tests_determinism) +// { +// std::vector test_instances = { +// {"mip/50v-10.mps", 11311031.}, {"mip/neos5.mps", 15.}, {"mip/swath1.mps", 1300.}}; +// for (const auto& test_instance : test_instances) { +// test_miplib_file(test_instance, true); +// } +// } + } // namespace cuopt::linear_programming::test From 8cbc37a5da9ad91dd11b487af221f29dd2470742 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 27 Oct 2025 15:52:08 +0000 Subject: [PATCH 010/225] add determinism setting --- cpp/include/cuopt/linear_programming/mip/solver_settings.hpp | 5 +++-- cpp/src/math_optimization/solver_settings.cu | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 1750f2a03e..8e49f80135 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -98,8 +98,9 @@ class mip_solver_settings_t { /** Initial primal solutions */ std::vector>> initial_solutions; - bool mip_scaling = true; - bool presolve = true; + bool mip_scaling = true; + bool presolve = true; + bool deterministic = false; // this is for extracting info from different places of the solver during // benchmarks benchmark_info_t* benchmark_info_ptr = nullptr; diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index 42faaae940..be13894bd1 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -116,6 +116,7 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_PRESOLVE, &pdlp_settings.presolve, false}, {CUOPT_PRESOLVE, &mip_settings.presolve, true}, {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true} + {CUOPT_MIP_DETERMINISTIC, &mip_settings.deterministic, false}, }; // String parameters string_parameters = { From 7ee2c497330cb93fb0ff17ce388102bf79c17ff4 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 28 Oct 2025 12:43:04 +0000 Subject: [PATCH 011/225] restore determinism tests functionality --- .../cuopt/linear_programming/constants.h | 1 + cpp/src/math_optimization/solver_settings.cu | 4 +- cpp/src/mip/diversity/diversity_config.hpp | 2 + cpp/src/mip/diversity/diversity_manager.cu | 8 +- .../diversity/recombiners/fp_recombiner.cuh | 10 +- .../mip/diversity/recombiners/recombiner.cuh | 5 +- .../feasibility_pump/feasibility_pump.cu | 4 + .../line_segment_search.cu | 23 +- .../line_segment_search.cuh | 5 +- cpp/src/mip/local_search/local_search.cu | 88 +++-- .../local_search/rounding/constraint_prop.cu | 7 +- cpp/tests/mip/diversity_test.cu | 206 ++++++++--- cpp/tests/mip/feasibility_jump_tests.cu | 71 +--- cpp/tests/mip/local_search_test.cu | 44 +-- cpp/tests/mip/mip_utils.cuh | 61 ++++ cpp/tests/mip/unit_test.cu | 339 ++++++++++-------- 16 files changed, 531 insertions(+), 347 deletions(-) diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index 7f82e84911..fd44ef658b 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -59,6 +59,7 @@ #define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic" #define CUOPT_PRESOLVE "presolve" #define CUOPT_DUAL_POSTSOLVE "dual_postsolve" +#define CUOPT_MIP_DETERMINISTIC "mip_deterministic" #define CUOPT_MIP_ABSOLUTE_TOLERANCE "mip_absolute_tolerance" #define CUOPT_MIP_RELATIVE_TOLERANCE "mip_relative_tolerance" #define CUOPT_MIP_INTEGRALITY_TOLERANCE "mip_integrality_tolerance" diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index be13894bd1..84b975d9de 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -115,8 +115,8 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_CUDSS_DETERMINISTIC, &pdlp_settings.cudss_deterministic, false}, {CUOPT_PRESOLVE, &pdlp_settings.presolve, false}, {CUOPT_PRESOLVE, &mip_settings.presolve, true}, - {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true} - {CUOPT_MIP_DETERMINISTIC, &mip_settings.deterministic, false}, + {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true}, + {CUOPT_MIP_DETERMINISTIC, &mip_settings.deterministic, false} }; // String parameters string_parameters = { diff --git a/cpp/src/mip/diversity/diversity_config.hpp b/cpp/src/mip/diversity/diversity_config.hpp index 5bfc9f51c9..6149cf8e13 100644 --- a/cpp/src/mip/diversity/diversity_config.hpp +++ b/cpp/src/mip/diversity/diversity_config.hpp @@ -41,6 +41,8 @@ struct diversity_config_t { double lp_run_time_if_infeasible = 1.; bool halve_population = false; bool fj_only_run = false; + bool dry_run = false; + bool initial_solution_only = false; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 78cc432439..1713c1b7b1 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -272,6 +272,10 @@ void diversity_manager_t::run_fj_alone(solution_t& solution) ls.fj.settings.update_weights = true; ls.fj.settings.feasibility_run = false; ls.fj.settings.time_limit = timer.remaining_time(); + if (context.settings.deterministic) { + ls.fj.settings.time_limit = timer.remaining_time(); + ls.fj.settings.iteration_limit = 10000; + } ls.fj.solve(solution); CUOPT_LOG_INFO("FJ alone finished!"); } @@ -313,7 +317,7 @@ solution_t diversity_manager_t::run_solver() problem_ptr->check_problem_representation(true); // have the structure ready for reusing later problem_ptr->compute_integer_fixed_problem(); - recombiner_t::init_enabled_recombiners(*problem_ptr); + recombiner_t::init_enabled_recombiners(context, *problem_ptr); mab_recombiner.resize_mab_arm_stats(recombiner_t::enabled_recombiners.size()); // test problem is not ii cuopt_func_call( @@ -417,12 +421,14 @@ solution_t diversity_manager_t::run_solver() population.best_feasible().get_user_objective(); } + if (diversity_config.dry_run) { return population.best_feasible(); } if (diversity_config.fj_only_run) { solution_t sol(*problem_ptr); run_fj_alone(sol); return sol; } generate_solution(timer.remaining_time(), false); + if (diversity_config.initial_solution_only) { return population.best_feasible(); } if (timer.check_time_limit()) { population.add_external_solutions_to_population(); return population.best_feasible(); diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index 23dab78b2c..f52de0e7be 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -93,7 +93,12 @@ class fp_recombiner_t : public recombiner_t { CUOPT_LOG_DEBUG("FP rec: running LP with infeasibility detection"); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; - lp_settings.tolerance = fixed_problem.tolerances.absolute_tolerance; + if (this->context.settings.deterministic) { + lp_settings.time_limit = + std::numeric_limits::max(); // TODO should be global time limit + lp_settings.iteration_limit = 5000; + } + lp_settings.tolerance = fixed_problem.tolerances.absolute_tolerance; lp_settings.return_first_feasible = true; lp_settings.save_state = true; lp_settings.check_infeasibility = true; @@ -118,6 +123,9 @@ class fp_recombiner_t : public recombiner_t { offspring.assignment = std::move(fixed_assignment); cuopt_func_call(offspring.test_variable_bounds(false)); timer_t timer(fp_recombiner_config_t::fp_time_limit); + if (this->context.settings.deterministic) { + timer = timer_t(std::numeric_limits::max()); // TODO should be global time limit + } fp.timer = timer; fp.cycle_queue.reset(offspring); fp.reset(); diff --git a/cpp/src/mip/diversity/recombiners/recombiner.cuh b/cpp/src/mip/diversity/recombiners/recombiner.cuh index 9725337247..1f134656e8 100644 --- a/cpp/src/mip/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/recombiner.cuh @@ -216,7 +216,8 @@ class recombiner_t { "vars_to_fix should be sorted!"); } - static void init_enabled_recombiners(const problem_t& problem) + static void init_enabled_recombiners(mip_solver_context_t& context, + const problem_t& problem) { std::unordered_set enabled_recombiners; for (auto recombiner : recombiner_types) { @@ -231,6 +232,8 @@ class recombiner_t { (i_t)sub_mip_recombiner_config_t::max_continuous_vars) { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } + // submip not supported in deterministic mode yet + if (context.settings.deterministic) { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } recombiner_t::enabled_recombiners = std::vector(enabled_recombiners.begin(), enabled_recombiners.end()); } diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index 6ab4140dfd..d23bb09c56 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -331,6 +331,10 @@ bool feasibility_pump_t::test_fj_feasible(solution_t& soluti fj.settings.feasibility_run = true; fj.settings.n_of_minimums_for_exit = 5000; fj.settings.time_limit = std::min(time_limit, timer.remaining_time()); + if (context.settings.deterministic) { + fj.settings.time_limit = timer.remaining_time(); + fj.settings.iteration_limit = 10000; + } cuopt_func_call(solution.test_variable_bounds(true)); is_feasible = fj.solve(solution); cuopt_func_call(solution.test_variable_bounds(true)); diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu index 6d0be3c669..18c529fc1f 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu @@ -27,8 +27,10 @@ namespace cuopt::linear_programming::detail { template line_segment_search_t::line_segment_search_t( - fj_t& fj_, constraint_prop_t& constraint_prop_) - : fj(fj_), constraint_prop(constraint_prop_) + mip_solver_context_t& context_, + fj_t& fj_, + constraint_prop_t& constraint_prop_) + : context(context_), fj(fj_), constraint_prop(constraint_prop_) { } @@ -196,7 +198,9 @@ bool line_segment_search_t::search_line_segment( best_feasible_cost, curr_cost); } - if (timer.check_time_limit()) { break; } + if (!context.settings.deterministic) { + if (timer.check_time_limit()) { break; } + } i_t number_of_integer_var_diff = compute_number_of_integer_var_diff( solution.problem_ptr->integer_indices, solution.assignment, @@ -217,7 +221,13 @@ bool line_segment_search_t::search_line_segment( fj.settings.update_weights = false; fj.settings.feasibility_run = is_feasibility_run; fj.settings.time_limit = std::min(1., timer.remaining_time()); - is_feasible = fj.solve(solution); + if (context.settings.deterministic) { + // fj.settings.time_limit = timer.remaining_time(); + fj.settings.time_limit = + std::numeric_limits::max(); // TODO should be global time limit + fj.settings.iteration_limit = 500; + } + is_feasible = fj.solve(solution); if (is_feasibility_run) { if (is_feasible) { CUOPT_LOG_DEBUG("Line segment found feasible"); @@ -234,7 +244,10 @@ bool line_segment_search_t::search_line_segment( best_feasible_cost, curr_cost); } - if (timer.check_time_limit()) { break; } + // cuopt_assert(context.settings.deterministic, ""); + if (!context.settings.deterministic) { + if (timer.check_time_limit()) { break; } + } } // if not recombiner mode but local search mode if (!settings.recombiner_mode) { diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cuh b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cuh index 583cc9fb83..3d89d0e83e 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cuh +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cuh @@ -36,7 +36,9 @@ template class line_segment_search_t { public: line_segment_search_t() = delete; - line_segment_search_t(fj_t& fj, constraint_prop_t& constraint_prop); + line_segment_search_t(mip_solver_context_t& context, + fj_t& fj, + constraint_prop_t& constraint_prop); bool search_line_segment(solution_t& solution, const rmm::device_uvector& point_1, const rmm::device_uvector& point_2, @@ -59,6 +61,7 @@ class line_segment_search_t { f_t& best_feasible_cost, f_t curr_cost); + mip_solver_context_t& context; fj_t& fj; constraint_prop_t& constraint_prop; line_segment_settings_t settings; diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index aa286964e3..5df43aaf5c 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -115,7 +115,7 @@ local_search_t::local_search_t(mip_solver_context_t& context fj(context), // fj_tree(fj), constraint_prop(context), - line_segment_search(fj, constraint_prop), + line_segment_search(context, fj, constraint_prop), fp(context, fj, // fj_tree, @@ -160,19 +160,21 @@ void local_search_t::start_cpufj_scratch_threads(population_t 0); - cpu_fj.fj_cpu->log_prefix = "******* scratch " + std::to_string(counter) + ": "; - cpu_fj.fj_cpu->improvement_callback = [this, &population, &cpu_fj]( - f_t obj, const std::vector& h_vec) { - population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); - if (obj < local_search_best_obj) { - CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", - context.problem_ptr->get_user_obj_from_solver_obj(obj), - context.problem_ptr->get_user_obj_from_solver_obj( - population.is_feasible() ? population.best_feasible().get_objective() - : std::numeric_limits::max())); - local_search_best_obj = obj; - } - }; + cpu_fj.fj_cpu->log_prefix = "******* scratch " + std::to_string(counter) + ": "; + if (!context.settings.deterministic) { + cpu_fj.fj_cpu->improvement_callback = [this, &population, &cpu_fj]( + f_t obj, const std::vector& h_vec) { + population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); + if (obj < local_search_best_obj) { + CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", + context.problem_ptr->get_user_obj_from_solver_obj(obj), + context.problem_ptr->get_user_obj_from_solver_obj( + population.is_feasible() ? population.best_feasible().get_objective() + : std::numeric_limits::max())); + local_search_best_obj = obj; + } + }; + } counter++; }; @@ -195,18 +197,20 @@ void local_search_t::start_cpufj_lptopt_scratch_threads( scratch_cpu_fj_on_lp_opt.fj_cpu = fj.create_cpu_climber(solution_lp, default_weights, default_weights, 0.); scratch_cpu_fj_on_lp_opt.fj_cpu->log_prefix = "******* scratch on LP optimal: "; - scratch_cpu_fj_on_lp_opt.fj_cpu->improvement_callback = - [this, &population](f_t obj, const std::vector& h_vec) { - population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); - if (obj < local_search_best_obj) { - CUOPT_LOG_DEBUG("******* New local search best obj %g, best overall %g", - context.problem_ptr->get_user_obj_from_solver_obj(obj), - context.problem_ptr->get_user_obj_from_solver_obj( - population.is_feasible() ? population.best_feasible().get_objective() - : std::numeric_limits::max())); - local_search_best_obj = obj; - } - }; + if (!context.settings.deterministic) { + scratch_cpu_fj_on_lp_opt.fj_cpu->improvement_callback = + [this, &population](f_t obj, const std::vector& h_vec) { + population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); + if (obj < local_search_best_obj) { + CUOPT_LOG_DEBUG("******* New local search best obj %g, best overall %g", + context.problem_ptr->get_user_obj_from_solver_obj(obj), + context.problem_ptr->get_user_obj_from_solver_obj( + population.is_feasible() ? population.best_feasible().get_objective() + : std::numeric_limits::max())); + local_search_best_obj = obj; + } + }; + } // default weights cudaDeviceSynchronize(); @@ -290,17 +294,21 @@ bool local_search_t::do_fj_solve(solution_t& solution, cpu_better[source]); total_calls[source]++; - if (cpu_feasible && !gpu_feasible || - (cpu_feasible && solution_cpu.get_objective() < solution.get_objective())) { - CUOPT_LOG_DEBUG( - "CPU FJ returns better solution! cpu_obj %g, gpu_obj %g, stats %d/%d, source %s", - solution_cpu.get_user_objective(), - solution.get_user_objective(), - total_calls[source], - cpu_better[source], - source.c_str()); - solution.copy_from(solution_cpu); - cpu_better[source]++; + // ignore the CPUFJ solution if we're in determinism mode + // TODO: use work limits here + if (!context.settings.deterministic) { + if (cpu_feasible && !gpu_feasible || + (cpu_feasible && solution_cpu.get_objective() < solution.get_objective())) { + CUOPT_LOG_DEBUG( + "CPU FJ returns better solution! cpu_obj %g, gpu_obj %g, stats %d/%d, source %s", + solution_cpu.get_user_objective(), + solution.get_user_objective(), + total_calls[source], + cpu_better[source], + source.c_str()); + solution.copy_from(solution_cpu); + cpu_better[source]++; + } } solution.compute_feasibility(); @@ -480,7 +488,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu if (!is_feasible) { f_t lp_run_time = 2.; // CHANGE - lp_run_time = 600; + if (context.settings.deterministic) { lp_run_time = std::numeric_limits::infinity(); } relaxed_lp_settings_t lp_settings; lp_settings.time_limit = std::min(lp_run_time, timer.remaining_time()); lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; @@ -495,7 +503,9 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu fj.settings.n_of_minimums_for_exit = 20000; fj.settings.update_weights = true; fj.settings.feasibility_run = false; - f_t time_limit = std::min(30., timer.remaining_time()); + f_t fj_time_limit = 30.; + if (context.settings.deterministic) { fj_time_limit = std::numeric_limits::infinity(); } + f_t time_limit = std::min(fj_time_limit, timer.remaining_time()); do_fj_solve(solution, fj, time_limit, "on_lp_optimal"); return solution.get_feasible(); } diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 60faca5db7..5bf9764a7f 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -873,7 +873,9 @@ bool constraint_prop_t::find_integer( std::mt19937 rng(seed); // CHANGE - timer = timer_t(std::numeric_limits::infinity()); + if (this->context.settings.deterministic) { + timer = timer_t(std::numeric_limits::infinity()); + } lb_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); ub_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); @@ -1105,6 +1107,9 @@ bool constraint_prop_t::apply_round( { raft::common::nvtx::range fun_scope("constraint prop round"); max_timer = timer_t{max_time_for_bounds_prop}; + if (this->context.settings.deterministic) { + max_timer = timer_t(std::numeric_limits::infinity()); + } if (check_brute_force_rounding(sol)) { return true; } recovery_mode = false; rounding_ii = false; diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index bc6cce33ff..a200858c47 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -72,8 +72,8 @@ static void setup_device_symbols(rmm::cuda_stream_view stream_view) detail::set_pdlp_hyper_parameters(stream_view); } -static uint32_t test_initial_population_determinism(std::string path, - unsigned long seed = std::random_device{}()) +static uint32_t test_initial_solution_determinism(std::string path, + unsigned long seed = std::random_device{}()) { const raft::handle_t handle_{}; @@ -100,57 +100,129 @@ static uint32_t test_initial_population_determinism(std::string path, nullptr, true); - auto settings = mip_solver_settings_t{}; - settings.time_limit = 30.; - auto timer = cuopt::timer_t(30); + auto settings = mip_solver_settings_t{}; + settings.time_limit = 3000.; + settings.deterministic = true; + settings.heuristics_only = true; + auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - diversity_manager.diversity_config.fj_only_run = true; - // run with FJ only on the initial population, no recombining + diversity_manager.timer = timer_t(60000); + diversity_manager.diversity_config.initial_solution_only = true; diversity_manager.run_solver(); + std::vector hashes; + auto pop = diversity_manager.get_population_pointer(); + for (const auto& sol : pop->population_to_vector()) { + hashes.push_back(sol.get_hash()); + } + + uint32_t final_hash = detail::compute_hash(hashes); + printf("%s: final hash: 0x%x, pop size %d\n", + path.c_str(), + final_hash, + (int)pop->population_to_vector().size()); + return final_hash; +} + +static uint32_t test_recombiners_determinism(std::string path, + unsigned long seed = std::random_device{}()) +{ + const raft::handle_t handle_{}; + + cuopt::mps_parser::mps_data_model_t mps_problem = + cuopt::mps_parser::parse_mps(path, false); + handle_.sync_stream(); + auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); + problem_checking_t::check_problem_representation(op_problem); + + init_handler(op_problem.get_handle_ptr()); + // run the problem constructor of MIP, so that we do bounds standardization + detail::problem_t problem(op_problem); + problem.preprocess_problem(); + + setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); + + detail::pdlp_initial_scaling_strategy_t scaling(&handle_, + problem, + 10, + 1.0, + problem.reverse_coefficients, + problem.reverse_offsets, + problem.reverse_constraints, + nullptr, + true); + + auto settings = mip_solver_settings_t{}; + settings.time_limit = 3000.; + settings.deterministic = true; + settings.heuristics_only = true; + auto timer = cuopt::timer_t(3000); + detail::mip_solver_t solver(problem, settings, scaling, timer); + problem.tolerances = settings.get_tolerances(); + + detail::diversity_manager_t diversity_manager(solver.context); + diversity_manager.timer = timer_t(60000); + diversity_manager.diversity_config.dry_run = true; + diversity_manager.run_solver(); + + // Generate a population by running FJ on random starting points // recombine a few solutions, observe the output - auto pop_vector = diversity_manager.get_population_pointer()->population_to_vector(); - // necessary because .resize() complains about constructors (since it may be used to grow the - // vector as well) - while (pop_vector.size() > 9) { - pop_vector.pop_back(); + for (int i = diversity_manager.population.current_size(); i < 3; ++i) { + detail::solution_t random_initial_solution(problem); + random_initial_solution.assign_random_within_bounds(); + detail::fj_settings_t fj_settings; + fj_settings.feasibility_run = false; + fj_settings.iteration_limit = 1000 + i * 100; + fj_settings.seed = seed + i; + auto solution = + run_fj(problem, fj_settings, fj_tweaks_t{}, random_initial_solution.get_host_assignment()) + .solution; + printf("population %d hash: 0x%x\n", i, solution.get_hash()); + diversity_manager.population.add_solution(std::move(solution)); } + auto pop_vector = diversity_manager.get_population_pointer()->population_to_vector(); + std::vector hashes; - static std::map, uint32_t> hash_map; - - // diversity_manager.run_only_ls_recombiner = true; - diversity_manager.timer = timer_t(60000); - // diversity_manager.recombine_and_ls_with_all(pop_vector); - for (int i = 1; i < (int)pop_vector.size(); i++) { - for (int j = i + 1; j < (int)pop_vector.size(); j++) { - printf("recombining %d and %d\n", i, j); - auto [offspring, success] = diversity_manager.recombine( - pop_vector[i], pop_vector[j], detail::recombiner_enum_t::LINE_SEGMENT); - auto offspring_hash = offspring.get_hash(); - printf("for %d,%d: offspring hash: 0x%x, parent 1 hash: 0x%x, parent 2 hash: 0x%x\n", - i, - j, - offspring_hash, - pop_vector[i].get_hash(), - pop_vector[j].get_hash()); - if (hash_map.find(std::make_pair(i, j)) == hash_map.end()) { - hash_map[std::make_pair(i, j)] = offspring_hash; - } else { - if (hash_map[std::make_pair(i, j)] != offspring_hash) { - printf("hash mismatch for %d,%d: %d != %d\n", - i, - j, - hash_map[std::make_pair(i, j)], - offspring_hash); - exit(1); + static std::map, uint32_t> hash_map; + + for (auto recombiner : {detail::recombiner_enum_t::LINE_SEGMENT, + detail::recombiner_enum_t::BOUND_PROP, + detail::recombiner_enum_t::FP}) { + for (int i = 1; i < (int)pop_vector.size(); i++) { + for (int j = i + 1; j < (int)pop_vector.size(); j++) { + printf("recombining %d and %d w/ recombiner %s\n", + i, + j, + detail::all_recombine_stats::recombiner_labels[(int)recombiner]); + auto [offspring, success] = + diversity_manager.recombine(pop_vector[i], pop_vector[j], recombiner); + auto offspring_hash = offspring.get_hash(); + printf("for %d,%d: offspring hash: 0x%x, parent 1 hash: 0x%x, parent 2 hash: 0x%x\n", + i, + j, + offspring_hash, + pop_vector[i].get_hash(), + pop_vector[j].get_hash()); + if (hash_map.find(std::make_tuple(path, i, j, recombiner)) == hash_map.end()) { + hash_map[std::make_tuple(path, i, j, recombiner)] = offspring_hash; + } else { + if (hash_map[std::make_tuple(path, i, j, recombiner)] != offspring_hash) { + printf("%s: hash mismatch for %d,%d: %d != %d\n", + path.c_str(), + i, + j, + hash_map[std::make_tuple(path, i, j, recombiner)], + offspring_hash); + exit(1); + } } + hashes.push_back(offspring_hash); } - hashes.push_back(offspring_hash); } } return detail::compute_hash(hashes); @@ -161,13 +233,46 @@ static uint32_t test_initial_population_determinism(std::string path, } uint32_t final_hash = detail::compute_hash(hashes); - printf("final hash: 0x%x, pop size %d\n", final_hash, (int)pop->population_to_vector().size()); + printf("%s: final hash: 0x%x, pop size %d\n", + path.c_str(), + final_hash, + (int)pop->population_to_vector().size()); return final_hash; } class DiversityTestParams : public testing::TestWithParam> {}; -TEST_P(DiversityTestParams, initial_population_deterministic) +// TEST_P(DiversityTestParams, recombiners_deterministic) +// { +// cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); + +// spin_stream_raii_t spin_stream_1; +// spin_stream_raii_t spin_stream_2; + +// auto test_instance = std::get<0>(GetParam()); +// std::cout << "Running: " << test_instance << std::endl; +// int seed = +// std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); +// std::cerr << "Tested with seed " << seed << "\n"; +// auto path = make_path_absolute(test_instance); +// test_instance = std::getenv("CUOPT_INSTANCE") ? std::getenv("CUOPT_INSTANCE") : test_instance; +// path = "/home/scratch.yboucher_gpu_1/collection/" + test_instance; +// uint32_t gold_hash = 0; +// for (int i = 0; i < 2; ++i) { +// cuopt::seed_generator::set_seed(seed); +// std::cout << "Running " << test_instance << " " << i << std::endl; +// std::cout << "-------------------------------------------------------------\n"; +// auto hash = test_recombiners_determinism(path, seed); +// if (i == 0) { +// gold_hash = hash; +// std::cout << "Gold hash: " << gold_hash << std::endl; +// } else { +// ASSERT_EQ(hash, gold_hash); +// } +// } +// } + +TEST_P(DiversityTestParams, initial_solution_deterministic) { cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); @@ -183,11 +288,11 @@ TEST_P(DiversityTestParams, initial_population_deterministic) test_instance = std::getenv("CUOPT_INSTANCE") ? std::getenv("CUOPT_INSTANCE") : test_instance; path = "/home/scratch.yboucher_gpu_1/collection/" + test_instance; uint32_t gold_hash = 0; - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < 2; ++i) { cuopt::seed_generator::set_seed(seed); std::cout << "Running " << test_instance << " " << i << std::endl; std::cout << "-------------------------------------------------------------\n"; - auto hash = test_initial_population_determinism(path, seed); + auto hash = test_initial_solution_determinism(path, seed); if (i == 0) { gold_hash = hash; std::cout << "Gold hash: " << gold_hash << std::endl; @@ -199,15 +304,14 @@ TEST_P(DiversityTestParams, initial_population_deterministic) INSTANTIATE_TEST_SUITE_P(DiversityTest, DiversityTestParams, - testing::Values(std::make_tuple("fastxgemm-n2r6s0t2.mps") + testing::Values(std::make_tuple("gen-ip054.mps"), + std::make_tuple("pk1.mps"), + std::make_tuple("fastxgemm-n2r6s0t2.mps"), // std::make_tuple("mip/sct2.mps") // std::make_tuple("mip/thor50dday.mps") - // std::make_tuple("mip/uccase9.mps") - // std::make_tuple("mip/neos5-free-bound.mps") + std::make_tuple("uccase9.mps"), // std::make_tuple("mip/neos5.mps") - // std::make_tuple("mip/50v-10.mps") - // std::make_tuple("mip/rmatr200-p5.mps") - )); -// std::make_tuple("mip/gen-ip054.mps"))); + std::make_tuple("50v-10.mps"), + std::make_tuple("rmatr200-p5.mps"))); } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index b6c36a97a5..5efc510f63 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -56,23 +56,11 @@ void init_handler(const raft::handle_t* handle_ptr) handle_ptr->get_cusparse_handle(), CUSPARSE_POINTER_MODE_DEVICE, handle_ptr->get_stream())); } -struct fj_tweaks_t { - double objective_weight = 0; -}; - -struct fj_state_t { - detail::solution_t solution; - std::vector solution_vector; - int minimums; - double incumbent_objective; - double incumbent_violation; -}; - // Helper function to setup MIP solver and run FJ with given settings and initial solution -static fj_state_t run_fj(std::string test_instance, - const detail::fj_settings_t& fj_settings, - fj_tweaks_t tweaks = {}, - std::vector initial_solution = {}) +static fj_state_t run_fj_instance(std::string test_instance, + const detail::fj_settings_t& fj_settings, + fj_tweaks_t tweaks = {}, + std::vector initial_solution = {}) { const raft::handle_t handle_{}; std::cout << "Running: " << test_instance << std::endl; @@ -88,45 +76,8 @@ static fj_state_t run_fj(std::string test_instance, // run the problem constructor of MIP, so that we do bounds standardization detail::problem_t problem(op_problem); problem.preprocess_problem(); - detail::pdlp_initial_scaling_strategy_t scaling(&handle_, - problem, - 10, - 1.0, - problem.reverse_coefficients, - problem.reverse_offsets, - problem.reverse_constraints, - nullptr, - true); - - auto settings = mip_solver_settings_t{}; - settings.time_limit = 30.; - auto timer = cuopt::timer_t(30); - detail::mip_solver_t solver(problem, settings, scaling, timer); - - detail::solution_t solution(*solver.context.problem_ptr); - if (initial_solution.size() > 0) { - expand_device_copy(solution.assignment, initial_solution, solution.handle_ptr->get_stream()); - } else { - thrust::fill(solution.handle_ptr->get_thrust_policy(), - solution.assignment.begin(), - solution.assignment.end(), - 0.0); - } - solution.clamp_within_bounds(); - - detail::fj_t fj(solver.context, fj_settings); - fj.reset_weights(solution.handle_ptr->get_stream(), 1.); - fj.objective_weight.set_value_async(tweaks.objective_weight, solution.handle_ptr->get_stream()); - solution.handle_ptr->sync_stream(); - - fj.solve(solution); - auto solution_vector = host_copy(solution.assignment, solution.handle_ptr->get_stream()); - return {solution, - solution_vector, - fj.climbers[0]->local_minimums_reached.value(solution.handle_ptr->get_stream()), - fj.climbers[0]->incumbent_objective.value(solution.handle_ptr->get_stream()), - fj.climbers[0]->violation_score.value(solution.handle_ptr->get_stream())}; + return run_fj(problem, fj_settings, tweaks, initial_solution); } // FJ had a bug causing objective/violation values to explode in magnitude in certain scenarios. @@ -141,7 +92,7 @@ static bool run_fj_check_no_obj_runoff(std::string test_instance) fj_settings.feasibility_run = false; fj_settings.iteration_limit = 20000; - auto state = run_fj(test_instance, fj_settings); + auto state = run_fj_instance(test_instance, fj_settings); // ensure that the objective and the violation in the FJ state are not too large (<1e60) EXPECT_LE(state.incumbent_violation, 1e60) << "FJ violation too large"; @@ -163,7 +114,7 @@ static bool run_fj_check_objective(std::string test_instance, int iter_limit, do fj_settings.feasibility_run = obj_target == +std::numeric_limits::infinity(); fj_settings.iteration_limit = iter_limit; - auto state = run_fj(test_instance, fj_settings); + auto state = run_fj_instance(test_instance, fj_settings); auto& solution = state.solution; CUOPT_LOG_DEBUG("%s: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", @@ -190,7 +141,7 @@ static bool run_fj_check_feasible(std::string test_instance) fj_settings.feasibility_run = false; fj_settings.iteration_limit = 25000; - auto state = run_fj(test_instance, fj_settings); + auto state = run_fj_instance(test_instance, fj_settings); auto& solution = state.solution; bool previous_feasible = solution.get_feasible(); @@ -201,8 +152,8 @@ static bool run_fj_check_feasible(std::string test_instance) // again but with very large obj weight to force FJ into the infeasible region fj_tweaks_t tweaks; tweaks.objective_weight = 1e6; - auto new_state = run_fj(test_instance, fj_settings, tweaks, state.solution_vector); - auto& new_solution = new_state.solution; + auto new_state = run_fj_instance(test_instance, fj_settings, tweaks, state.solution_vector); + auto& new_solution = new_state.solution; CUOPT_LOG_DEBUG("%s: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", test_instance.c_str(), @@ -233,7 +184,7 @@ static bool run_fj_check_determinism(std::string test_instance, int iter_limit) fj_settings.seed = seed; cuopt::seed_generator::set_seed(fj_settings.seed); - auto state = run_fj(test_instance, fj_settings); + auto state = run_fj_instance(test_instance, fj_settings); auto& solution = state.solution; CUOPT_LOG_DEBUG("%s: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index 73baad7995..ce0ee5216c 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -72,18 +72,6 @@ static void setup_device_symbols(rmm::cuda_stream_view stream_view) detail::set_pdlp_hyper_parameters(stream_view); } -struct fj_tweaks_t { - double objective_weight = 0; -}; - -struct fj_state_t { - detail::solution_t solution; - std::vector solution_vector; - int minimums; - double incumbent_objective; - double incumbent_violation; -}; - enum local_search_mode_t { FP = 0, STAGED_FP, @@ -122,15 +110,19 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) nullptr, true); - auto settings = mip_solver_settings_t{}; - settings.time_limit = 30.; - auto timer = cuopt::timer_t(30); + auto settings = mip_solver_settings_t{}; + settings.time_limit = 30.; + settings.deterministic = true; + auto timer = cuopt::timer_t(30); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); - rmm::device_uvector lp_optimal_solution(0, problem.handle_ptr->get_stream()); - - lp_optimal_solution.resize(problem.n_variables, problem.handle_ptr->get_stream()); + rmm::device_uvector lp_optimal_solution(problem.n_variables, + problem.handle_ptr->get_stream()); + thrust::fill(problem.handle_ptr->get_thrust_policy(), + lp_optimal_solution.begin(), + lp_optimal_solution.end(), + 0.0); detail::lp_state_t& lp_state = problem.lp_state; // resize because some constructor might be called before the presolve lp_state.resize(problem, problem.handle_ptr->get_stream()); @@ -157,7 +149,7 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) printf("LP optimal hash: 0x%x\n", detail::compute_hash(lp_optimal_solution)); printf("running mode: %d\n", mode); - local_search.fp.timer = timer_t{600}; + local_search.fp.timer = timer_t{6000}; detail::ls_config_t ls_config{}; @@ -230,9 +222,9 @@ TEST_P(LocalSearchTestParams, local_search_operator_determinism) for (const auto& instance : { //"thor50dday.mps", "gen-ip054.mps", - //"50v-10.mps", + "50v-10.mps", //"seymour1.mps", - //"rmatr200-p5.mps", + "rmatr200-p5.mps", //"tr12-30.mps", //"sct2.mps", //"uccase9.mps" @@ -245,7 +237,7 @@ TEST_P(LocalSearchTestParams, local_search_operator_determinism) unsigned long seed = std::random_device{}(); std::cerr << "Tested with seed " << seed << "\n"; uint32_t gold_hash = 0; - for (int i = 0; i < 10; ++i) { + for (int i = 0; i < 5; ++i) { uint32_t hash = run_fp_check_determinism(instance, mode); if (i == 0) { gold_hash = hash; @@ -260,9 +252,9 @@ TEST_P(LocalSearchTestParams, local_search_operator_determinism) INSTANTIATE_TEST_SUITE_P(LocalSearchTests, LocalSearchTestParams, - testing::Values(std::make_tuple(local_search_mode_t::FP), - std::make_tuple(local_search_mode_t::FJ_LINE_SEGMENT), - std::make_tuple(local_search_mode_t::FJ_ON_ZERO), - std::make_tuple(local_search_mode_t::FJ_ANNEALING))); + testing::Values( // std::make_tuple(local_search_mode_t::FP), + std::make_tuple(local_search_mode_t::FJ_LINE_SEGMENT), + std::make_tuple(local_search_mode_t::FJ_ON_ZERO), + std::make_tuple(local_search_mode_t::FJ_ANNEALING))); } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/mip_utils.cuh b/cpp/tests/mip/mip_utils.cuh index b53952e127..904f461684 100644 --- a/cpp/tests/mip/mip_utils.cuh +++ b/cpp/tests/mip/mip_utils.cuh @@ -18,7 +18,10 @@ #include #include #include +#include #include +#include +#include #include #include @@ -133,4 +136,62 @@ static std::tuple test_mps_file( solution.get_solution_bound()); } +struct fj_tweaks_t { + double objective_weight = 0; +}; + +struct fj_state_t { + detail::solution_t solution; + std::vector solution_vector; + int minimums; + double incumbent_objective; + double incumbent_violation; +}; + +static fj_state_t run_fj(detail::problem_t& problem, + const detail::fj_settings_t& fj_settings, + fj_tweaks_t tweaks = {}, + std::vector initial_solution = {}) +{ + detail::pdlp_initial_scaling_strategy_t scaling(problem.handle_ptr, + problem, + 10, + 1.0, + problem.reverse_coefficients, + problem.reverse_offsets, + problem.reverse_constraints, + nullptr, + true); + + auto settings = mip_solver_settings_t{}; + settings.time_limit = 30.; + auto timer = cuopt::timer_t(30); + detail::mip_solver_t solver(problem, settings, scaling, timer); + + detail::solution_t solution(*solver.context.problem_ptr); + if (initial_solution.size() > 0) { + expand_device_copy(solution.assignment, initial_solution, solution.handle_ptr->get_stream()); + } else { + thrust::fill(solution.handle_ptr->get_thrust_policy(), + solution.assignment.begin(), + solution.assignment.end(), + 0.0); + } + solution.clamp_within_bounds(); + + detail::fj_t fj(solver.context, fj_settings); + fj.reset_weights(solution.handle_ptr->get_stream(), 1.); + fj.objective_weight.set_value_async(tweaks.objective_weight, solution.handle_ptr->get_stream()); + solution.handle_ptr->sync_stream(); + + fj.solve(solution); + auto solution_vector = host_copy(solution.assignment, solution.handle_ptr->get_stream()); + + return {solution, + solution_vector, + fj.climbers[0]->local_minimums_reached.value(solution.handle_ptr->get_stream()), + fj.climbers[0]->incumbent_objective.value(solution.handle_ptr->get_stream()), + fj.climbers[0]->violation_score.value(solution.handle_ptr->get_stream())}; +} + } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/unit_test.cu b/cpp/tests/mip/unit_test.cu index 9897b2feb0..9258f4dc7a 100644 --- a/cpp/tests/mip/unit_test.cu +++ b/cpp/tests/mip/unit_test.cu @@ -133,164 +133,185 @@ mps_parser::mps_data_model_t create_single_var_milp_problem(bool ma return problem; } -TEST(LPTest, TestSampleLP2) -{ - raft::handle_t handle; - - // Construct a simple LP problem: - // Minimize: x - // Subject to: x <= 1 - // x <= 1 - // x >= 0 - - // One variable, two constraints (both x <= 1) - std::vector A_values = {1.0, 1.0}; - std::vector A_indices = {0, 0}; - std::vector A_offsets = {0, 1, 2}; // CSR: 2 constraints, 1 variable - - std::vector b = {1.0, 1.0}; // RHS for both constraints - std::vector b_lower = {-std::numeric_limits::infinity(), - -std::numeric_limits::infinity()}; - - std::vector c = {1.0}; // Objective: Minimize x - - std::vector row_types = {'L', 'L'}; // Both constraints are <= - - // Build the problem - mps_parser::mps_data_model_t problem; - problem.set_csr_constraint_matrix(A_values.data(), - A_values.size(), - A_indices.data(), - A_indices.size(), - A_offsets.data(), - A_offsets.size()); - problem.set_constraint_upper_bounds(b.data(), b.size()); - problem.set_constraint_lower_bounds(b_lower.data(), b_lower.size()); - - // Set variable bounds (x >= 0) - std::vector var_lower = {0.0}; - std::vector var_upper = {std::numeric_limits::infinity()}; - problem.set_variable_lower_bounds(var_lower.data(), var_lower.size()); - problem.set_variable_upper_bounds(var_upper.data(), var_upper.size()); - - problem.set_objective_coefficients(c.data(), c.size()); - problem.set_maximize(false); - // Set up solver settings - cuopt::linear_programming::pdlp_solver_settings_t settings{}; - settings.set_optimality_tolerance(1e-2); - settings.method = cuopt::linear_programming::method_t::PDLP; - settings.time_limit = 5; - - // Solve - auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); - - // Check results - EXPECT_EQ(result.get_termination_status(), - cuopt::linear_programming::pdlp_termination_status_t::Optimal); - ASSERT_EQ(result.get_primal_solution().size(), 1); - - // Copy solution to host to access values - auto primal_host = cuopt::host_copy(result.get_primal_solution()); - EXPECT_NEAR(primal_host[0], 0.0, 1e-6); - - EXPECT_NEAR(result.get_additional_termination_information().primal_objective, 0.0, 1e-6); - EXPECT_NEAR(result.get_additional_termination_information().dual_objective, 0.0, 1e-6); -} - -TEST(LPTest, TestSampleLP) -{ - raft::handle_t handle; - auto problem = create_std_lp_problem(); - - cuopt::linear_programming::pdlp_solver_settings_t settings{}; - settings.set_optimality_tolerance(1e-4); - settings.time_limit = 5; - settings.presolve = false; - - auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); - - EXPECT_EQ(result.get_termination_status(), - cuopt::linear_programming::pdlp_termination_status_t::Optimal); -} - -TEST(ErrorTest, TestError) -{ - raft::handle_t handle; - auto problem = create_std_milp_problem(false); - - cuopt::linear_programming::mip_solver_settings_t settings{}; - settings.time_limit = 5; - settings.presolve = false; - - // Set constraint bounds - std::vector lower_bounds = {1.0}; - std::vector upper_bounds = {1.0, 1.0}; - problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); - problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); - - auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); - - EXPECT_EQ(result.get_termination_status(), - cuopt::linear_programming::mip_termination_status_t::NoTermination); -} - -class MILPTestParams - : public testing::TestWithParam< - std::tuple> {}; - -TEST_P(MILPTestParams, TestSampleMILP) -{ - bool maximize = std::get<0>(GetParam()); - bool scaling = std::get<1>(GetParam()); - bool heuristics_only = std::get<2>(GetParam()); - auto expected_termination_status = std::get<3>(GetParam()); - - raft::handle_t handle; - auto problem = create_std_milp_problem(maximize); - - cuopt::linear_programming::mip_solver_settings_t settings{}; - settings.time_limit = 5; - settings.mip_scaling = scaling; - settings.heuristics_only = heuristics_only; - settings.presolve = false; - - auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); - - EXPECT_EQ(result.get_termination_status(), expected_termination_status); -} - -TEST_P(MILPTestParams, TestSingleVarMILP) -{ - bool maximize = std::get<0>(GetParam()); - bool scaling = std::get<1>(GetParam()); - bool heuristics_only = std::get<2>(GetParam()); - auto expected_termination_status = std::get<3>(GetParam()); - - raft::handle_t handle; - auto problem = create_single_var_milp_problem(maximize); - - cuopt::linear_programming::mip_solver_settings_t settings{}; - settings.time_limit = 5; - settings.mip_scaling = scaling; - settings.heuristics_only = heuristics_only; - settings.presolve = false; - - auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); - - EXPECT_EQ(result.get_termination_status(), - cuopt::linear_programming::mip_termination_status_t::Optimal); -} - -INSTANTIATE_TEST_SUITE_P( - MILPTests, - MILPTestParams, - testing::Values( - std::make_tuple(true, true, true, cuopt::linear_programming::mip_termination_status_t::Optimal), - std::make_tuple( - false, true, false, cuopt::linear_programming::mip_termination_status_t::Optimal), - std::make_tuple( - true, false, true, cuopt::linear_programming::mip_termination_status_t::Optimal), - std::make_tuple( - false, false, false, cuopt::linear_programming::mip_termination_status_t::Optimal))); +// TEST(LPTest, TestSampleLP2) +// { +// raft::handle_t handle; + +// // Construct a simple LP problem: +// // Minimize: x +// // Subject to: x <= 1 +// // x <= 1 +// // x >= 0 + +// // One variable, two constraints (both x <= 1) +// std::vector A_values = {1.0, 1.0}; +// std::vector A_indices = {0, 0}; +// std::vector A_offsets = {0, 1, 2}; // CSR: 2 constraints, 1 variable + +// std::vector b = {1.0, 1.0}; // RHS for both constraints +// std::vector b_lower = {-std::numeric_limits::infinity(), +// -std::numeric_limits::infinity()}; + +// std::vector c = {1.0}; // Objective: Minimize x + +// std::vector row_types = {'L', 'L'}; // Both constraints are <= + +// // Build the problem +// mps_parser::mps_data_model_t problem; +// problem.set_csr_constraint_matrix(A_values.data(), +// A_values.size(), +// A_indices.data(), +// A_indices.size(), +// A_offsets.data(), +// A_offsets.size()); +// problem.set_constraint_upper_bounds(b.data(), b.size()); +// problem.set_constraint_lower_bounds(b_lower.data(), b_lower.size()); + +// // Set variable bounds (x >= 0) +// std::vector var_lower = {0.0}; +// std::vector var_upper = {std::numeric_limits::infinity()}; +// problem.set_variable_lower_bounds(var_lower.data(), var_lower.size()); +// problem.set_variable_upper_bounds(var_upper.data(), var_upper.size()); + +// problem.set_objective_coefficients(c.data(), c.size()); +// problem.set_maximize(false); +// // Set up solver settings +// cuopt::linear_programming::pdlp_solver_settings_t settings{}; +// settings.set_optimality_tolerance(1e-2); +// settings.method = cuopt::linear_programming::method_t::PDLP; +// settings.time_limit = 5; + +// // Solve +// auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); + +// // Check results +// EXPECT_EQ(result.get_termination_status(), +// cuopt::linear_programming::pdlp_termination_status_t::Optimal); +// ASSERT_EQ(result.get_primal_solution().size(), 1); + +// // Copy solution to host to access values +// auto primal_host = cuopt::host_copy(result.get_primal_solution()); +// EXPECT_NEAR(primal_host[0], 0.0, 1e-6); + +// EXPECT_NEAR(result.get_additional_termination_information().primal_objective, 0.0, 1e-6); +// EXPECT_NEAR(result.get_additional_termination_information().dual_objective, 0.0, 1e-6); +// } + +// TEST(LPTest, TestSampleLP) +// { +// raft::handle_t handle; +// auto problem = create_std_lp_problem(); + +// cuopt::linear_programming::pdlp_solver_settings_t settings{}; +// settings.set_optimality_tolerance(1e-4); +// settings.time_limit = 5; +// settings.presolve = false; + +// auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); + +// EXPECT_EQ(result.get_termination_status(), +// cuopt::linear_programming::pdlp_termination_status_t::Optimal); +// } + +// TEST(ErrorTest, TestError) +// { +// raft::handle_t handle; +// auto problem = create_std_milp_problem(false); + +// cuopt::linear_programming::mip_solver_settings_t settings{}; +// settings.time_limit = 5; +// settings.presolve = false; + +// // Set constraint bounds +// std::vector lower_bounds = {1.0}; +// std::vector upper_bounds = {1.0, 1.0}; +// problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); +// problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); + +// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + +// EXPECT_EQ(result.get_termination_status(), +// cuopt::linear_programming::mip_termination_status_t::NoTermination); +// } + +// class MILPTestParams +// : public testing::TestWithParam< +// std::tuple> {}; + +// TEST_P(MILPTestParams, TestSampleMILP) +// { +// bool maximize = std::get<0>(GetParam()); +// bool scaling = std::get<1>(GetParam()); +// bool heuristics_only = std::get<2>(GetParam()); +// auto expected_termination_status = std::get<3>(GetParam()); + +// raft::handle_t handle; +// auto problem = create_std_milp_problem(maximize); + +// cuopt::linear_programming::mip_solver_settings_t settings{}; +// settings.time_limit = 5; +// settings.mip_scaling = scaling; +// settings.heuristics_only = heuristics_only; +// settings.presolve = false; + +// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + +// EXPECT_EQ(result.get_termination_status(), expected_termination_status); +// } + +// TEST_P(MILPTestParams, TestSingleVarMILP) +// { +// bool maximize = std::get<0>(GetParam()); +// bool scaling = std::get<1>(GetParam()); +// bool heuristics_only = std::get<2>(GetParam()); +// auto expected_termination_status = std::get<3>(GetParam()); + +// raft::handle_t handle; +// auto problem = create_single_var_milp_problem(maximize); + +// cuopt::linear_programming::mip_solver_settings_t settings{}; +// settings.time_limit = 5; +// settings.mip_scaling = scaling; +// settings.heuristics_only = heuristics_only; +// settings.presolve = false; + +// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + +// EXPECT_EQ(result.get_termination_status(), +// cuopt::linear_programming::mip_termination_status_t::Optimal); +// } + +// INSTANTIATE_TEST_SUITE_P( +// MILPTests, +// MILPTestParams, +// testing::Values( +// std::make_tuple(true, true, true, +// cuopt::linear_programming::mip_termination_status_t::Optimal), std::make_tuple( +// false, true, false, cuopt::linear_programming::mip_termination_status_t::Optimal), +// std::make_tuple( +// true, false, true, cuopt::linear_programming::mip_termination_status_t::Optimal), +// std::make_tuple( +// false, false, false, cuopt::linear_programming::mip_termination_status_t::Optimal))); + +// TEST_P(MILPTestParams, TestDeterminism) +// { +// bool maximize = std::get<0>(GetParam()); +// bool scaling = std::get<1>(GetParam()); +// bool heuristics_only = std::get<2>(GetParam()); +// auto expected_termination_status = std::get<3>(GetParam()); + +// raft::handle_t handle; +// auto problem = create_std_milp_problem(maximize); + +// cuopt::linear_programming::mip_solver_settings_t settings{}; +// settings.mip_scaling = true; +// settings.heuristics_only = true; +// settings.presolve = true; +// settings.deterministic = true; + +// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + +// EXPECT_EQ(result.get_termination_status(), expected_termination_status); +// } } // namespace cuopt::linear_programming::test From d5677fe66a5abad1b81293f7b4c47499544abf3f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 28 Oct 2025 16:59:54 +0000 Subject: [PATCH 012/225] adding cupti --- cpp/src/mip/diversity/diversity_config.hpp | 1 + cpp/src/mip/diversity/diversity_manager.cu | 3 +- .../feasibility_pump/feasibility_pump.cu | 17 +- cpp/src/mip/local_search/local_search.cu | 10 +- cpp/src/mip/local_search/local_search.cuh | 3 +- cpp/src/mip/relaxed_lp/relaxed_lp.cu | 18 +- cpp/tests/CMakeLists.txt | 48 +++ cpp/tests/mip/diversity_test.cu | 108 +++++- cpp/tests/utilities/CMakeLists.txt | 1 + cpp/tests/utilities/Eval.h | 274 +++++++++++++ cpp/tests/utilities/Metric.h | 245 ++++++++++++ cpp/tests/utilities/helper_cupti.h | 165 ++++++++ cpp/tests/utilities/test_cupti.cu | 361 ++++++++++++++++++ 13 files changed, 1221 insertions(+), 33 deletions(-) create mode 100644 cpp/tests/utilities/Eval.h create mode 100644 cpp/tests/utilities/Metric.h create mode 100644 cpp/tests/utilities/helper_cupti.h create mode 100644 cpp/tests/utilities/test_cupti.cu diff --git a/cpp/src/mip/diversity/diversity_config.hpp b/cpp/src/mip/diversity/diversity_config.hpp index 6149cf8e13..73d91545b5 100644 --- a/cpp/src/mip/diversity/diversity_config.hpp +++ b/cpp/src/mip/diversity/diversity_config.hpp @@ -43,6 +43,7 @@ struct diversity_config_t { bool fj_only_run = false; bool dry_run = false; bool initial_solution_only = false; + int n_fp_iterations = 1000000; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 1713c1b7b1..d1ebde3b9f 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -286,7 +286,7 @@ void diversity_manager_t::run_fp_alone() { CUOPT_LOG_DEBUG("Running FP alone!"); solution_t sol(population.best_feasible()); - ls.run_fp(sol, timer, &population); + ls.run_fp(sol, timer, &population, diversity_config.n_fp_iterations); CUOPT_LOG_DEBUG("FP alone finished!"); } @@ -428,6 +428,7 @@ solution_t diversity_manager_t::run_solver() return sol; } generate_solution(timer.remaining_time(), false); + printf("=======================================================\n"); if (diversity_config.initial_solution_only) { return population.best_feasible(); } if (timer.check_time_limit()) { population.add_external_solutions_to_population(); diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index d23bb09c56..4f0d3cff76 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -161,10 +161,10 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tinteger_indices, solution.handle_ptr->get_stream()); f_t obj_offset = 0; - CUOPT_LOG_DEBUG("FP: h_integer_indices hash 0x%x", detail::compute_hash(h_integer_indices)); - CUOPT_LOG_DEBUG("FP: h_assignment hash 0x%x", detail::compute_hash(h_assignment)); - CUOPT_LOG_DEBUG("FP: h_variable_bounds hash 0x%x", detail::compute_hash(h_variable_bounds)); - CUOPT_LOG_DEBUG("FP: h_last_projection hash 0x%x", detail::compute_hash(h_last_projection)); + // CUOPT_LOG_DEBUG("FP: h_integer_indices hash 0x%x", detail::compute_hash(h_integer_indices)); + // CUOPT_LOG_DEBUG("FP: h_assignment hash 0x%x", detail::compute_hash(h_assignment)); + // CUOPT_LOG_DEBUG("FP: h_variable_bounds hash 0x%x", detail::compute_hash(h_variable_bounds)); + // CUOPT_LOG_DEBUG("FP: h_last_projection hash 0x%x", detail::compute_hash(h_last_projection)); // for each integer add the variable and the distance constraints for (auto i : h_integer_indices) { auto h_var_bounds = h_variable_bounds[i]; @@ -202,7 +202,8 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t 0) { temp_p.insert_variables(h_variables); } @@ -238,8 +239,8 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t::run_single_fp_descent(solution_t& s solution.assignment.size(), solution.handle_ptr->get_stream()); - CUOPT_LOG_DEBUG("FP: starting FP descent, sol hash 0x%x", solution.get_hash()); + // CUOPT_LOG_DEBUG("FP: starting FP descent, sol hash 0x%x", solution.get_hash()); while (true) { if (timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 5df43aaf5c..f3f8888b24 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -504,7 +504,10 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu fj.settings.update_weights = true; fj.settings.feasibility_run = false; f_t fj_time_limit = 30.; - if (context.settings.deterministic) { fj_time_limit = std::numeric_limits::infinity(); } + if (context.settings.deterministic) { + fj_time_limit = std::numeric_limits::infinity(); + fj.settings.iteration_limit = 100'000; + } f_t time_limit = std::min(fj_time_limit, timer.remaining_time()); do_fj_solve(solution, fj, time_limit, "on_lp_optimal"); return solution.get_feasible(); @@ -724,11 +727,11 @@ void local_search_t::reset_alpha_and_run_recombiners( template bool local_search_t::run_fp(solution_t& solution, timer_t timer, - population_t* population_ptr) + population_t* population_ptr, + i_t n_fp_iterations) { raft::common::nvtx::range fun_scope("run_fp"); cuopt_assert(population_ptr != nullptr, "Population pointer must not be null"); - const i_t n_fp_iterations = 1000000; bool is_feasible = solution.compute_feasibility(); cutting_plane_added_for_active_run = is_feasible; double best_objective = @@ -758,6 +761,7 @@ bool local_search_t::run_fp(solution_t& solution, is_feasible = false; break; } + printf("------------------------------------------------------------------------\n"); CUOPT_LOG_DEBUG("fp_loop it %d last_improved_iteration %d", i, last_improved_iteration); if (population_ptr->preempt_heuristic_solver_.load()) { CUOPT_LOG_DEBUG("Preempting heuristic solver!"); diff --git a/cpp/src/mip/local_search/local_search.cuh b/cpp/src/mip/local_search/local_search.cuh index a8fc6a77ff..ff914a876f 100644 --- a/cpp/src/mip/local_search/local_search.cuh +++ b/cpp/src/mip/local_search/local_search.cuh @@ -110,7 +110,8 @@ class local_search_t { population_t* population_ptr); bool run_fp(solution_t& solution, timer_t timer, - population_t* population_ptr = nullptr); + population_t* population_ptr = nullptr, + i_t n_fp_iterations = 1000000); void resize_vectors(problem_t& problem, const raft::handle_t* handle_ptr); bool do_fj_solve(solution_t& solution, diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index 104d34413c..a705e3c223 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -90,22 +90,22 @@ optimization_problem_solution_t get_relaxed_lp_solution( lp_solver.set_initial_primal_solution(assignment); lp_solver.set_initial_dual_solution(lp_state.prev_dual); } - CUOPT_LOG_DEBUG( - "running LP with n_vars %d n_cstr %d", op_problem.n_variables, op_problem.n_constraints); + // CUOPT_LOG_DEBUG( + // "running LP with n_vars %d n_cstr %d", op_problem.n_variables, op_problem.n_constraints); // before LP flush the logs as it takes quite some time cuopt::default_logger().flush(); // temporarily add timer auto start_time = timer_t(pdlp_settings.time_limit); lp_solver.set_inside_mip(true); - CUOPT_LOG_DEBUG("prev primal hash 0x%x", detail::compute_hash(assignment)); - CUOPT_LOG_DEBUG("prev dual hash 0x%x", detail::compute_hash(lp_state.prev_dual)); + // CUOPT_LOG_DEBUG("prev primal hash 0x%x", detail::compute_hash(assignment)); + // CUOPT_LOG_DEBUG("prev dual hash 0x%x", detail::compute_hash(lp_state.prev_dual)); auto solver_response = lp_solver.run_solver(start_time); - CUOPT_LOG_DEBUG("post LP primal hash 0x%x", - detail::compute_hash(solver_response.get_primal_solution())); + // CUOPT_LOG_DEBUG("post LP primal hash 0x%x", + // detail::compute_hash(solver_response.get_primal_solution())); if (solver_response.get_primal_solution().size() != 0 && solver_response.get_dual_solution().size() != 0 && settings.save_state) { - CUOPT_LOG_DEBUG("saving initial primal solution of size %d", lp_state.prev_primal.size()); + // CUOPT_LOG_DEBUG("saving initial primal solution of size %d", lp_state.prev_primal.size()); lp_state.set_state(solver_response.get_primal_solution(), solver_response.get_dual_solution()); } if (solver_response.get_primal_solution().size() != 0) { @@ -116,8 +116,8 @@ optimization_problem_solution_t get_relaxed_lp_solution( op_problem.handle_ptr->get_stream()); } if (solver_response.get_termination_status() == pdlp_termination_status_t::Optimal) { - CUOPT_LOG_DEBUG("feasible solution found with LP objective %f", - solver_response.get_objective_value()); + // CUOPT_LOG_DEBUG("feasible solution found with LP objective %f", + // solver_response.get_objective_value()); } else { CUOPT_LOG_DEBUG("LP returned with reason %d, %d iterations", solver_response.get_termination_status(), diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index ff6da8894c..0d3ff71160 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -40,6 +40,44 @@ endif() set(CUOPT_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +if (NOT TARGET CUDA::toolkit) + find_package(CUDAToolkit REQUIRED) +endif() + +if (EXISTS "${CUDAToolkit_LIBRARY_ROOT}/extras/CUPTI/lib64") + # NVIDIA installer layout: + set(cuopt_cupti_root "${CUDAToolkit_LIBRARY_ROOT}/extras/CUPTI") +else() + # Ubuntu package layout: + set(cuopt_cupti_root "${CUDAToolkit_LIBRARY_ROOT}") +endif() +message(STATUS "cuopt_cupti_root = ${cuopt_cupti_root}") + +# The CUPTI targets in FindCUDAToolkit are broken: +# - The dll locations are not specified +# - Dependent libraries nvperf_* are not linked. +# So we create our own targets: +function(cuopt_add_cupti_dep dep_name) + string(TOLOWER ${dep_name} dep_name_lower) + string(TOUPPER ${dep_name} dep_name_upper) + + add_library(cuopt::${dep_name_lower} SHARED IMPORTED) + + find_library(CUOPT_${dep_name_upper}_LIBRARY ${dep_name_lower} REQUIRED + DOC "The full path to lib${dep_name_lower}.so from the CUDA Toolkit." + HINTS "${cuopt_cupti_root}/lib64" "${cuopt_cupti_root}/lib" + ) + mark_as_advanced(CUOPT_${dep_name_upper}_LIBRARY) + + set_target_properties(cuopt::${dep_name_lower} PROPERTIES + IMPORTED_LOCATION "${CUOPT_${dep_name_upper}_LIBRARY}" + ) +endfunction() + +cuopt_add_cupti_dep(nvperf_target) +cuopt_add_cupti_dep(nvperf_host) +cuopt_add_cupti_dep(cupti) + # ################################################################ ------------------------------------------------------------------ function(ConfigureTest CMAKE_TEST_NAME) add_executable(${CMAKE_TEST_NAME} ${ARGN}) @@ -63,6 +101,16 @@ function(ConfigureTest CMAKE_TEST_NAME) ${CUOPT_PRIVATE_CUDA_LIBS} ) + target_link_libraries(${CMAKE_TEST_NAME} PRIVATE + #cuopt::nvperf_target + #cuopt::nvperf_host + cuopt::cupti + cuda + ) + target_include_directories(${CMAKE_TEST_NAME} PRIVATE + "${cuopt_cupti_root}/include" + ) + add_test(NAME ${CMAKE_TEST_NAME} COMMAND ${CMAKE_TEST_NAME}) install( diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index a200858c47..b891aa87f5 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -72,6 +72,61 @@ static void setup_device_symbols(rmm::cuda_stream_view stream_view) detail::set_pdlp_hyper_parameters(stream_view); } +static uint32_t test_full_run_determinism(std::string path, + unsigned long seed = std::random_device{}()) +{ + const raft::handle_t handle_{}; + + cuopt::mps_parser::mps_data_model_t mps_problem = + cuopt::mps_parser::parse_mps(path, false); + handle_.sync_stream(); + auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); + problem_checking_t::check_problem_representation(op_problem); + + init_handler(op_problem.get_handle_ptr()); + // run the problem constructor of MIP, so that we do bounds standardization + detail::problem_t problem(op_problem); + problem.preprocess_problem(); + + setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); + + detail::pdlp_initial_scaling_strategy_t scaling(&handle_, + problem, + 10, + 1.0, + problem.reverse_coefficients, + problem.reverse_offsets, + problem.reverse_constraints, + nullptr, + true); + + auto settings = mip_solver_settings_t{}; + settings.time_limit = 3000.; + settings.deterministic = true; + settings.heuristics_only = true; + auto timer = cuopt::timer_t(3000); + detail::mip_solver_t solver(problem, settings, scaling, timer); + problem.tolerances = settings.get_tolerances(); + + detail::diversity_manager_t diversity_manager(solver.context); + diversity_manager.timer = timer_t(60000); + diversity_manager.diversity_config.n_fp_iterations = 3; + diversity_manager.run_solver(); + + std::vector hashes; + auto pop = diversity_manager.get_population_pointer(); + for (const auto& sol : pop->population_to_vector()) { + hashes.push_back(sol.get_hash()); + } + + uint32_t final_hash = detail::compute_hash(hashes); + printf("%s: final hash: 0x%x, pop size %d\n", + path.c_str(), + final_hash, + (int)pop->population_to_vector().size()); + return final_hash; +} + static uint32_t test_initial_solution_determinism(std::string path, unsigned long seed = std::random_device{}()) { @@ -272,7 +327,37 @@ class DiversityTestParams : public testing::TestWithParam(GetParam()); +// std::cout << "Running: " << test_instance << std::endl; +// int seed = +// std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); +// std::cerr << "Tested with seed " << seed << "\n"; +// auto path = make_path_absolute(test_instance); +// test_instance = std::getenv("CUOPT_INSTANCE") ? std::getenv("CUOPT_INSTANCE") : test_instance; +// path = "/home/scratch.yboucher_gpu_1/collection/" + test_instance; +// uint32_t gold_hash = 0; +// for (int i = 0; i < 2; ++i) { +// cuopt::seed_generator::set_seed(seed); +// std::cout << "Running " << test_instance << " " << i << std::endl; +// std::cout << "-------------------------------------------------------------\n"; +// auto hash = test_initial_solution_determinism(path, seed); +// if (i == 0) { +// gold_hash = hash; +// std::cout << "Gold hash: " << gold_hash << std::endl; +// } else { +// ASSERT_EQ(hash, gold_hash); +// } +// } +// } + +TEST_P(DiversityTestParams, full_run_deterministic) { cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); @@ -292,7 +377,7 @@ TEST_P(DiversityTestParams, initial_solution_deterministic) cuopt::seed_generator::set_seed(seed); std::cout << "Running " << test_instance << " " << i << std::endl; std::cout << "-------------------------------------------------------------\n"; - auto hash = test_initial_solution_determinism(path, seed); + auto hash = test_full_run_determinism(path, seed); if (i == 0) { gold_hash = hash; std::cout << "Gold hash: " << gold_hash << std::endl; @@ -304,14 +389,15 @@ TEST_P(DiversityTestParams, initial_solution_deterministic) INSTANTIATE_TEST_SUITE_P(DiversityTest, DiversityTestParams, - testing::Values(std::make_tuple("gen-ip054.mps"), - std::make_tuple("pk1.mps"), - std::make_tuple("fastxgemm-n2r6s0t2.mps"), - // std::make_tuple("mip/sct2.mps") - // std::make_tuple("mip/thor50dday.mps") - std::make_tuple("uccase9.mps"), - // std::make_tuple("mip/neos5.mps") - std::make_tuple("50v-10.mps"), - std::make_tuple("rmatr200-p5.mps"))); + testing::Values( // std::make_tuple("gen-ip054.mps"), + // std::make_tuple("pk1.mps") + std::make_tuple("uccase9.mps"), + // std::make_tuple("mip/sct2.mps") + // std::make_tuple("mip/thor50dday.mps") + // std::make_tuple("uccase9.mps"), + // std::make_tuple("mip/neos5.mps") + std::make_tuple("50v-10.mps") + // std::make_tuple("rmatr200-p5.mps") + )); } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/utilities/CMakeLists.txt b/cpp/tests/utilities/CMakeLists.txt index e893345c01..7a40ee6e77 100644 --- a/cpp/tests/utilities/CMakeLists.txt +++ b/cpp/tests/utilities/CMakeLists.txt @@ -15,3 +15,4 @@ # Add CLI end-to-end test ConfigureTest(CLI_TEST test_cli.cpp) +ConfigureTest(CUPTI_TEST test_cupti.cu) diff --git a/cpp/tests/utilities/Eval.h b/cpp/tests/utilities/Eval.h new file mode 100644 index 0000000000..ccafcd1aea --- /dev/null +++ b/cpp/tests/utilities/Eval.h @@ -0,0 +1,274 @@ +/* + * 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 +#include +#include + +template + +class ScopeExit { + public: + ScopeExit(T t) : t(t) {} + ~ScopeExit() { t(); } + T t; +}; + +template +ScopeExit MoveScopeExit(T t) +{ + return ScopeExit(t); +}; + +#define NV_ANONYMOUS_VARIABLE_DIRECT(name, line) name##line +#define NV_ANONYMOUS_VARIABLE_INDIRECT(name, line) NV_ANONYMOUS_VARIABLE_DIRECT(name, line) + +#define SCOPE_EXIT(func) \ + const auto NV_ANONYMOUS_VARIABLE_INDIRECT(EXIT, __LINE__) = MoveScopeExit([=]() { func; }) + +namespace NV { +namespace Metric { +namespace Eval { +std::string GetHwUnit(const std::string& metricName) +{ + return metricName.substr(0, metricName.find("__", 0)); +} + +bool GetMetricGpuValue(std::string chipName, + const std::vector& counterDataImage, + const std::vector& metricNames, + std::vector& metricNameValueMap, + const uint8_t* pCounterAvailabilityImage) +{ + if (!counterDataImage.size()) { + std::cout << "Counter Data Image is empty!\n"; + return false; + } + + NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params calculateScratchBufferSizeParam = { + NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params_STRUCT_SIZE}; + calculateScratchBufferSizeParam.pChipName = chipName.c_str(); + calculateScratchBufferSizeParam.pCounterAvailabilityImage = pCounterAvailabilityImage; + RETURN_IF_NVPW_ERROR( + false, NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize(&calculateScratchBufferSizeParam)); + + std::vector scratchBuffer(calculateScratchBufferSizeParam.scratchBufferSize); + NVPW_CUDA_MetricsEvaluator_Initialize_Params metricEvaluatorInitializeParams = { + NVPW_CUDA_MetricsEvaluator_Initialize_Params_STRUCT_SIZE}; + metricEvaluatorInitializeParams.scratchBufferSize = scratchBuffer.size(); + metricEvaluatorInitializeParams.pScratchBuffer = scratchBuffer.data(); + metricEvaluatorInitializeParams.pChipName = chipName.c_str(); + metricEvaluatorInitializeParams.pCounterAvailabilityImage = pCounterAvailabilityImage; + RETURN_IF_NVPW_ERROR(false, + NVPW_CUDA_MetricsEvaluator_Initialize(&metricEvaluatorInitializeParams)); + NVPW_MetricsEvaluator* metricEvaluator = metricEvaluatorInitializeParams.pMetricsEvaluator; + + NVPW_CounterData_GetNumRanges_Params getNumRangesParams = { + NVPW_CounterData_GetNumRanges_Params_STRUCT_SIZE}; + getNumRangesParams.pCounterDataImage = counterDataImage.data(); + RETURN_IF_NVPW_ERROR(false, NVPW_CounterData_GetNumRanges(&getNumRangesParams)); + + bool isolated = true; + bool keepInstances = true; + for (size_t metricIndex = 0; metricIndex < metricNames.size(); ++metricIndex) { + std::string reqName; + NV::Metric::Parser::ParseMetricNameString( + metricNames[metricIndex], &reqName, &isolated, &keepInstances); + NVPW_MetricEvalRequest metricEvalRequest; + NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params convertMetricToEvalRequest = { + NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params_STRUCT_SIZE}; + convertMetricToEvalRequest.pMetricsEvaluator = metricEvaluator; + convertMetricToEvalRequest.pMetricName = reqName.c_str(); + convertMetricToEvalRequest.pMetricEvalRequest = &metricEvalRequest; + convertMetricToEvalRequest.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; + RETURN_IF_NVPW_ERROR( + false, + NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest(&convertMetricToEvalRequest)); + + MetricNameValue metricNameValue; + metricNameValue.numRanges = getNumRangesParams.numRanges; + metricNameValue.metricName = metricNames[metricIndex]; + for (size_t rangeIndex = 0; rangeIndex < getNumRangesParams.numRanges; ++rangeIndex) { + NVPW_Profiler_CounterData_GetRangeDescriptions_Params getRangeDescParams = { + NVPW_Profiler_CounterData_GetRangeDescriptions_Params_STRUCT_SIZE}; + getRangeDescParams.pCounterDataImage = counterDataImage.data(); + getRangeDescParams.rangeIndex = rangeIndex; + RETURN_IF_NVPW_ERROR(false, + NVPW_Profiler_CounterData_GetRangeDescriptions(&getRangeDescParams)); + std::vector descriptionPtrs(getRangeDescParams.numDescriptions); + getRangeDescParams.ppDescriptions = descriptionPtrs.data(); + RETURN_IF_NVPW_ERROR(false, + NVPW_Profiler_CounterData_GetRangeDescriptions(&getRangeDescParams)); + + std::string rangeName; + for (size_t descriptionIndex = 0; descriptionIndex < getRangeDescParams.numDescriptions; + ++descriptionIndex) { + if (descriptionIndex) { rangeName += "/"; } + rangeName += descriptionPtrs[descriptionIndex]; + } + + NVPW_MetricsEvaluator_SetDeviceAttributes_Params setDeviceAttribParams = { + NVPW_MetricsEvaluator_SetDeviceAttributes_Params_STRUCT_SIZE}; + setDeviceAttribParams.pMetricsEvaluator = metricEvaluator; + setDeviceAttribParams.pCounterDataImage = counterDataImage.data(); + setDeviceAttribParams.counterDataImageSize = counterDataImage.size(); + RETURN_IF_NVPW_ERROR(false, + NVPW_MetricsEvaluator_SetDeviceAttributes(&setDeviceAttribParams)); + + double metricValue = 0.0; + NVPW_MetricsEvaluator_EvaluateToGpuValues_Params evaluateToGpuValuesParams = { + NVPW_MetricsEvaluator_EvaluateToGpuValues_Params_STRUCT_SIZE}; + evaluateToGpuValuesParams.pMetricsEvaluator = metricEvaluator; + evaluateToGpuValuesParams.pMetricEvalRequests = &metricEvalRequest; + evaluateToGpuValuesParams.numMetricEvalRequests = 1; + evaluateToGpuValuesParams.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; + evaluateToGpuValuesParams.metricEvalRequestStrideSize = sizeof(NVPW_MetricEvalRequest); + evaluateToGpuValuesParams.pCounterDataImage = counterDataImage.data(); + evaluateToGpuValuesParams.counterDataImageSize = counterDataImage.size(); + evaluateToGpuValuesParams.rangeIndex = rangeIndex; + evaluateToGpuValuesParams.isolated = true; + evaluateToGpuValuesParams.pMetricValues = &metricValue; + RETURN_IF_NVPW_ERROR(false, + NVPW_MetricsEvaluator_EvaluateToGpuValues(&evaluateToGpuValuesParams)); + metricNameValue.rangeNameMetricValueMap.push_back(std::make_pair(rangeName, metricValue)); + } + metricNameValueMap.push_back(metricNameValue); + } + + NVPW_MetricsEvaluator_Destroy_Params metricEvaluatorDestroyParams = { + NVPW_MetricsEvaluator_Destroy_Params_STRUCT_SIZE}; + metricEvaluatorDestroyParams.pMetricsEvaluator = metricEvaluator; + RETURN_IF_NVPW_ERROR(false, NVPW_MetricsEvaluator_Destroy(&metricEvaluatorDestroyParams)); + return true; +} + +bool PrintMetricValues(std::string chipName, + const std::vector& counterDataImage, + const std::vector& metricNames, + const uint8_t* pCounterAvailabilityImage) +{ + if (!counterDataImage.size()) { + std::cout << "Counter Data Image is empty!\n"; + return false; + } + + NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params calculateScratchBufferSizeParam = { + NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params_STRUCT_SIZE}; + calculateScratchBufferSizeParam.pChipName = chipName.c_str(); + calculateScratchBufferSizeParam.pCounterAvailabilityImage = pCounterAvailabilityImage; + RETURN_IF_NVPW_ERROR( + false, NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize(&calculateScratchBufferSizeParam)); + + std::vector scratchBuffer(calculateScratchBufferSizeParam.scratchBufferSize); + NVPW_CUDA_MetricsEvaluator_Initialize_Params metricEvaluatorInitializeParams = { + NVPW_CUDA_MetricsEvaluator_Initialize_Params_STRUCT_SIZE}; + metricEvaluatorInitializeParams.scratchBufferSize = scratchBuffer.size(); + metricEvaluatorInitializeParams.pScratchBuffer = scratchBuffer.data(); + metricEvaluatorInitializeParams.pChipName = chipName.c_str(); + metricEvaluatorInitializeParams.pCounterAvailabilityImage = pCounterAvailabilityImage; + metricEvaluatorInitializeParams.pCounterDataImage = counterDataImage.data(); + metricEvaluatorInitializeParams.counterDataImageSize = counterDataImage.size(); + RETURN_IF_NVPW_ERROR(false, + NVPW_CUDA_MetricsEvaluator_Initialize(&metricEvaluatorInitializeParams)); + NVPW_MetricsEvaluator* metricEvaluator = metricEvaluatorInitializeParams.pMetricsEvaluator; + + NVPW_CounterData_GetNumRanges_Params getNumRangesParams = { + NVPW_CounterData_GetNumRanges_Params_STRUCT_SIZE}; + getNumRangesParams.pCounterDataImage = counterDataImage.data(); + RETURN_IF_NVPW_ERROR(false, NVPW_CounterData_GetNumRanges(&getNumRangesParams)); + + std::cout << "\n" + << std::setw(40) << std::left << "Range Name" << std::setw(100) << std::left + << "Metric Name" + << "Metric Value" << std::endl; + std::cout << std::setfill('-') << std::setw(160) << "" << std::setfill(' ') << std::endl; + + std::string reqName; + bool isolated = true; + bool keepInstances = true; + for (std::string metricName : metricNames) { + NV::Metric::Parser::ParseMetricNameString(metricName, &reqName, &isolated, &keepInstances); + NVPW_MetricEvalRequest metricEvalRequest; + NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params convertMetricToEvalRequest = { + NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params_STRUCT_SIZE}; + convertMetricToEvalRequest.pMetricsEvaluator = metricEvaluator; + convertMetricToEvalRequest.pMetricName = reqName.c_str(); + convertMetricToEvalRequest.pMetricEvalRequest = &metricEvalRequest; + convertMetricToEvalRequest.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; + RETURN_IF_NVPW_ERROR( + false, + NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest(&convertMetricToEvalRequest)); + + for (size_t rangeIndex = 0; rangeIndex < getNumRangesParams.numRanges; ++rangeIndex) { + NVPW_Profiler_CounterData_GetRangeDescriptions_Params getRangeDescParams = { + NVPW_Profiler_CounterData_GetRangeDescriptions_Params_STRUCT_SIZE}; + getRangeDescParams.pCounterDataImage = counterDataImage.data(); + getRangeDescParams.rangeIndex = rangeIndex; + RETURN_IF_NVPW_ERROR(false, + NVPW_Profiler_CounterData_GetRangeDescriptions(&getRangeDescParams)); + std::vector descriptionPtrs(getRangeDescParams.numDescriptions); + getRangeDescParams.ppDescriptions = descriptionPtrs.data(); + RETURN_IF_NVPW_ERROR(false, + NVPW_Profiler_CounterData_GetRangeDescriptions(&getRangeDescParams)); + + std::string rangeName; + for (size_t descriptionIndex = 0; descriptionIndex < getRangeDescParams.numDescriptions; + ++descriptionIndex) { + if (descriptionIndex) { rangeName += "/"; } + rangeName += descriptionPtrs[descriptionIndex]; + } + + NVPW_MetricsEvaluator_SetDeviceAttributes_Params setDeviceAttribParams = { + NVPW_MetricsEvaluator_SetDeviceAttributes_Params_STRUCT_SIZE}; + setDeviceAttribParams.pMetricsEvaluator = metricEvaluator; + setDeviceAttribParams.pCounterDataImage = counterDataImage.data(); + setDeviceAttribParams.counterDataImageSize = counterDataImage.size(); + RETURN_IF_NVPW_ERROR(false, + NVPW_MetricsEvaluator_SetDeviceAttributes(&setDeviceAttribParams)); + + double metricValue; + NVPW_MetricsEvaluator_EvaluateToGpuValues_Params evaluateToGpuValuesParams = { + NVPW_MetricsEvaluator_EvaluateToGpuValues_Params_STRUCT_SIZE}; + evaluateToGpuValuesParams.pMetricsEvaluator = metricEvaluator; + evaluateToGpuValuesParams.pMetricEvalRequests = &metricEvalRequest; + evaluateToGpuValuesParams.numMetricEvalRequests = 1; + evaluateToGpuValuesParams.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; + evaluateToGpuValuesParams.metricEvalRequestStrideSize = sizeof(NVPW_MetricEvalRequest); + evaluateToGpuValuesParams.pCounterDataImage = counterDataImage.data(); + evaluateToGpuValuesParams.counterDataImageSize = counterDataImage.size(); + evaluateToGpuValuesParams.rangeIndex = rangeIndex; + evaluateToGpuValuesParams.isolated = true; + evaluateToGpuValuesParams.pMetricValues = &metricValue; + RETURN_IF_NVPW_ERROR(false, + NVPW_MetricsEvaluator_EvaluateToGpuValues(&evaluateToGpuValuesParams)); + + std::cout << std::setw(40) << std::left << rangeName << std::setw(100) << std::left + << metricName << metricValue << std::endl; + } + } + + NVPW_MetricsEvaluator_Destroy_Params metricEvaluatorDestroyParams = { + NVPW_MetricsEvaluator_Destroy_Params_STRUCT_SIZE}; + metricEvaluatorDestroyParams.pMetricsEvaluator = metricEvaluator; + RETURN_IF_NVPW_ERROR(false, NVPW_MetricsEvaluator_Destroy(&metricEvaluatorDestroyParams)); + return true; +} +} // namespace Eval +} // namespace Metric +} // namespace NV diff --git a/cpp/tests/utilities/Metric.h b/cpp/tests/utilities/Metric.h new file mode 100644 index 0000000000..05261c8e63 --- /dev/null +++ b/cpp/tests/utilities/Metric.h @@ -0,0 +1,245 @@ +/* + * 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 +#include +#include + +class ScopeExit { + public: + ScopeExit(T t) : t(t) {} + ~ScopeExit() { t(); } + T t; +}; + +template +ScopeExit MoveScopeExit(T t) +{ + return ScopeExit(t); +}; + +#define NV_ANONYMOUS_VARIABLE_DIRECT(name, line) name##line +#define NV_ANONYMOUS_VARIABLE_INDIRECT(name, line) NV_ANONYMOUS_VARIABLE_DIRECT(name, line) + +#define SCOPE_EXIT(func) \ + const auto NV_ANONYMOUS_VARIABLE_INDIRECT(EXIT, __LINE__) = MoveScopeExit([=]() { func; }) + +namespace NV { +namespace Metric { +namespace Config { + +bool GetRawMetricRequests(std::string chipName, + const std::vector& metricNames, + std::vector& rawMetricRequests, + const uint8_t* pCounterAvailabilityImage) +{ + NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params calculateScratchBufferSizeParam = { + NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params_STRUCT_SIZE}; + calculateScratchBufferSizeParam.pChipName = chipName.c_str(); + calculateScratchBufferSizeParam.pCounterAvailabilityImage = pCounterAvailabilityImage; + RETURN_IF_NVPW_ERROR( + false, NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize(&calculateScratchBufferSizeParam)); + + std::vector scratchBuffer(calculateScratchBufferSizeParam.scratchBufferSize); + NVPW_CUDA_MetricsEvaluator_Initialize_Params metricEvaluatorInitializeParams = { + NVPW_CUDA_MetricsEvaluator_Initialize_Params_STRUCT_SIZE}; + metricEvaluatorInitializeParams.scratchBufferSize = scratchBuffer.size(); + metricEvaluatorInitializeParams.pScratchBuffer = scratchBuffer.data(); + metricEvaluatorInitializeParams.pChipName = chipName.c_str(); + metricEvaluatorInitializeParams.pCounterAvailabilityImage = pCounterAvailabilityImage; + RETURN_IF_NVPW_ERROR(false, + NVPW_CUDA_MetricsEvaluator_Initialize(&metricEvaluatorInitializeParams)); + NVPW_MetricsEvaluator* metricEvaluator = metricEvaluatorInitializeParams.pMetricsEvaluator; + + bool isolated = true; + bool keepInstances = true; + std::vector rawMetricNames; + for (auto& metricName : metricNames) { + std::string reqName; + NV::Metric::Parser::ParseMetricNameString(metricName, &reqName, &isolated, &keepInstances); + keepInstances = true; + NVPW_MetricEvalRequest metricEvalRequest; + NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params convertMetricToEvalRequest = { + NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params_STRUCT_SIZE}; + convertMetricToEvalRequest.pMetricsEvaluator = metricEvaluator; + convertMetricToEvalRequest.pMetricName = reqName.c_str(); + convertMetricToEvalRequest.pMetricEvalRequest = &metricEvalRequest; + convertMetricToEvalRequest.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; + RETURN_IF_NVPW_ERROR( + false, + NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest(&convertMetricToEvalRequest)); + + std::vector rawDependencies; + NVPW_MetricsEvaluator_GetMetricRawDependencies_Params getMetricRawDependenciesParms = { + NVPW_MetricsEvaluator_GetMetricRawDependencies_Params_STRUCT_SIZE}; + getMetricRawDependenciesParms.pMetricsEvaluator = metricEvaluator; + getMetricRawDependenciesParms.pMetricEvalRequests = &metricEvalRequest; + getMetricRawDependenciesParms.numMetricEvalRequests = 1; + getMetricRawDependenciesParms.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; + getMetricRawDependenciesParms.metricEvalRequestStrideSize = sizeof(NVPW_MetricEvalRequest); + RETURN_IF_NVPW_ERROR( + false, NVPW_MetricsEvaluator_GetMetricRawDependencies(&getMetricRawDependenciesParms)); + rawDependencies.resize(getMetricRawDependenciesParms.numRawDependencies); + getMetricRawDependenciesParms.ppRawDependencies = rawDependencies.data(); + RETURN_IF_NVPW_ERROR( + false, NVPW_MetricsEvaluator_GetMetricRawDependencies(&getMetricRawDependenciesParms)); + + for (size_t i = 0; i < rawDependencies.size(); ++i) { + rawMetricNames.push_back(rawDependencies[i]); + } + } + + for (auto& rawMetricName : rawMetricNames) { + NVPA_RawMetricRequest metricRequest = {NVPA_RAW_METRIC_REQUEST_STRUCT_SIZE}; + metricRequest.pMetricName = rawMetricName; + metricRequest.isolated = isolated; + metricRequest.keepInstances = keepInstances; + rawMetricRequests.push_back(metricRequest); + } + + NVPW_MetricsEvaluator_Destroy_Params metricEvaluatorDestroyParams = { + NVPW_MetricsEvaluator_Destroy_Params_STRUCT_SIZE}; + metricEvaluatorDestroyParams.pMetricsEvaluator = metricEvaluator; + RETURN_IF_NVPW_ERROR(false, NVPW_MetricsEvaluator_Destroy(&metricEvaluatorDestroyParams)); + return true; +} + +bool GetConfigImage(std::string chipName, + const std::vector& metricNames, + std::vector& configImage, + const uint8_t* pCounterAvailabilityImage) +{ + std::vector rawMetricRequests; + GetRawMetricRequests(chipName, metricNames, rawMetricRequests, pCounterAvailabilityImage); + + NVPW_CUDA_RawMetricsConfig_Create_V2_Params rawMetricsConfigCreateParams = { + NVPW_CUDA_RawMetricsConfig_Create_V2_Params_STRUCT_SIZE}; + rawMetricsConfigCreateParams.activityKind = NVPA_ACTIVITY_KIND_PROFILER; + rawMetricsConfigCreateParams.pChipName = chipName.c_str(); + rawMetricsConfigCreateParams.pCounterAvailabilityImage = pCounterAvailabilityImage; + RETURN_IF_NVPW_ERROR(false, NVPW_CUDA_RawMetricsConfig_Create_V2(&rawMetricsConfigCreateParams)); + NVPA_RawMetricsConfig* pRawMetricsConfig = rawMetricsConfigCreateParams.pRawMetricsConfig; + + if (pCounterAvailabilityImage) { + NVPW_RawMetricsConfig_SetCounterAvailability_Params setCounterAvailabilityParams = { + NVPW_RawMetricsConfig_SetCounterAvailability_Params_STRUCT_SIZE}; + setCounterAvailabilityParams.pRawMetricsConfig = pRawMetricsConfig; + setCounterAvailabilityParams.pCounterAvailabilityImage = pCounterAvailabilityImage; + RETURN_IF_NVPW_ERROR( + false, NVPW_RawMetricsConfig_SetCounterAvailability(&setCounterAvailabilityParams)); + } + + NVPW_RawMetricsConfig_Destroy_Params rawMetricsConfigDestroyParams = { + NVPW_RawMetricsConfig_Destroy_Params_STRUCT_SIZE}; + rawMetricsConfigDestroyParams.pRawMetricsConfig = pRawMetricsConfig; + SCOPE_EXIT([&]() { + NVPW_RawMetricsConfig_Destroy( + (NVPW_RawMetricsConfig_Destroy_Params*)&rawMetricsConfigDestroyParams); + }); + + NVPW_RawMetricsConfig_BeginPassGroup_Params beginPassGroupParams = { + NVPW_RawMetricsConfig_BeginPassGroup_Params_STRUCT_SIZE}; + beginPassGroupParams.pRawMetricsConfig = pRawMetricsConfig; + RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_BeginPassGroup(&beginPassGroupParams)); + + NVPW_RawMetricsConfig_AddMetrics_Params addMetricsParams = { + NVPW_RawMetricsConfig_AddMetrics_Params_STRUCT_SIZE}; + addMetricsParams.pRawMetricsConfig = pRawMetricsConfig; + addMetricsParams.pRawMetricRequests = rawMetricRequests.data(); + addMetricsParams.numMetricRequests = rawMetricRequests.size(); + RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_AddMetrics(&addMetricsParams)); + + NVPW_RawMetricsConfig_EndPassGroup_Params endPassGroupParams = { + NVPW_RawMetricsConfig_EndPassGroup_Params_STRUCT_SIZE}; + endPassGroupParams.pRawMetricsConfig = pRawMetricsConfig; + RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_EndPassGroup(&endPassGroupParams)); + + NVPW_RawMetricsConfig_GenerateConfigImage_Params generateConfigImageParams = { + NVPW_RawMetricsConfig_GenerateConfigImage_Params_STRUCT_SIZE}; + generateConfigImageParams.pRawMetricsConfig = pRawMetricsConfig; + RETURN_IF_NVPW_ERROR(false, + NVPW_RawMetricsConfig_GenerateConfigImage(&generateConfigImageParams)); + + NVPW_RawMetricsConfig_GetConfigImage_Params getConfigImageParams = { + NVPW_RawMetricsConfig_GetConfigImage_Params_STRUCT_SIZE}; + getConfigImageParams.pRawMetricsConfig = pRawMetricsConfig; + getConfigImageParams.bytesAllocated = 0; + getConfigImageParams.pBuffer = NULL; + RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_GetConfigImage(&getConfigImageParams)); + + configImage.resize(getConfigImageParams.bytesCopied); + getConfigImageParams.bytesAllocated = configImage.size(); + getConfigImageParams.pBuffer = configImage.data(); + RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_GetConfigImage(&getConfigImageParams)); + + return true; +} + +bool GetCounterDataPrefixImage(std::string chipName, + const std::vector& metricNames, + std::vector& counterDataImagePrefix, + const uint8_t* pCounterAvailabilityImage) +{ + std::vector rawMetricRequests; + GetRawMetricRequests(chipName, metricNames, rawMetricRequests, pCounterAvailabilityImage); + + NVPW_CUDA_CounterDataBuilder_Create_Params counterDataBuilderCreateParams = { + NVPW_CUDA_CounterDataBuilder_Create_Params_STRUCT_SIZE}; + counterDataBuilderCreateParams.pChipName = chipName.c_str(); + counterDataBuilderCreateParams.pCounterAvailabilityImage = pCounterAvailabilityImage; + RETURN_IF_NVPW_ERROR(false, NVPW_CUDA_CounterDataBuilder_Create(&counterDataBuilderCreateParams)); + + NVPW_CounterDataBuilder_Destroy_Params counterDataBuilderDestroyParams = { + NVPW_CounterDataBuilder_Destroy_Params_STRUCT_SIZE}; + counterDataBuilderDestroyParams.pCounterDataBuilder = + counterDataBuilderCreateParams.pCounterDataBuilder; + SCOPE_EXIT([&]() { + NVPW_CounterDataBuilder_Destroy( + (NVPW_CounterDataBuilder_Destroy_Params*)&counterDataBuilderDestroyParams); + }); + + NVPW_CounterDataBuilder_AddMetrics_Params addMetricsParams = { + NVPW_CounterDataBuilder_AddMetrics_Params_STRUCT_SIZE}; + addMetricsParams.pCounterDataBuilder = counterDataBuilderCreateParams.pCounterDataBuilder; + addMetricsParams.pRawMetricRequests = rawMetricRequests.data(); + addMetricsParams.numMetricRequests = rawMetricRequests.size(); + RETURN_IF_NVPW_ERROR(false, NVPW_CounterDataBuilder_AddMetrics(&addMetricsParams)); + + size_t counterDataPrefixSize = 0; + NVPW_CounterDataBuilder_GetCounterDataPrefix_Params getCounterDataPrefixParams = { + NVPW_CounterDataBuilder_GetCounterDataPrefix_Params_STRUCT_SIZE}; + getCounterDataPrefixParams.pCounterDataBuilder = + counterDataBuilderCreateParams.pCounterDataBuilder; + getCounterDataPrefixParams.bytesAllocated = 0; + getCounterDataPrefixParams.pBuffer = NULL; + RETURN_IF_NVPW_ERROR(false, + NVPW_CounterDataBuilder_GetCounterDataPrefix(&getCounterDataPrefixParams)); + + counterDataImagePrefix.resize(getCounterDataPrefixParams.bytesCopied); + getCounterDataPrefixParams.bytesAllocated = counterDataImagePrefix.size(); + getCounterDataPrefixParams.pBuffer = counterDataImagePrefix.data(); + RETURN_IF_NVPW_ERROR(false, + NVPW_CounterDataBuilder_GetCounterDataPrefix(&getCounterDataPrefixParams)); + + return true; +} + +} // namespace Config +} // namespace Metric +} // namespace NV diff --git a/cpp/tests/utilities/helper_cupti.h b/cpp/tests/utilities/helper_cupti.h new file mode 100644 index 0000000000..0b5f2c9982 --- /dev/null +++ b/cpp/tests/utilities/helper_cupti.h @@ -0,0 +1,165 @@ +/** + * Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +//////////////////////////////////////////////////////////////////////////////// + +#ifndef HELPER_CUPTI_H_ +#define HELPER_CUPTI_H_ + +#pragma once + +#include + +#ifndef EXIT_WAIVED +#define EXIT_WAIVED 2 +#endif + +#if defined(WIN32) || defined(_WIN32) +#define stricmp _stricmp +#else +#define stricmp strcasecmp +#endif + +#define CUDA_MAX_DEVICES 256 // consider theoretical max devices as 256 + +#ifndef DRIVER_API_CALL +#define DRIVER_API_CALL(apiFunctionCall) \ + do { \ + CUresult _status = apiFunctionCall; \ + if (_status != CUDA_SUCCESS) { \ + const char* pErrorString; \ + cuGetErrorString(_status, &pErrorString); \ + \ + std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ + << #apiFunctionCall << " failed with error(" << _status << "): " << pErrorString \ + << ".\n\n"; \ + \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#ifndef RUNTIME_API_CALL +#define RUNTIME_API_CALL(apiFunctionCall) \ + do { \ + cudaError_t _status = apiFunctionCall; \ + if (_status != cudaSuccess) { \ + std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ + << #apiFunctionCall << " failed with error(" << _status \ + << "): " << cudaGetErrorString(_status) << ".\n\n"; \ + \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#ifndef CUPTI_API_CALL +#define CUPTI_API_CALL(apiFunctionCall) \ + do { \ + CUptiResult _status = apiFunctionCall; \ + if (_status != CUPTI_SUCCESS) { \ + const char* pErrorString; \ + cuptiGetResultString(_status, &pErrorString); \ + \ + std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ + << #apiFunctionCall << " failed with error(" << _status << "): " << pErrorString \ + << ".\n\n"; \ + \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#ifndef CUPTI_API_CALL_VERBOSE +#define CUPTI_API_CALL_VERBOSE(apiFunctionCall) \ + do { \ + std::cout << "Calling CUPTI API: " << #apiFunctionCall << "\n"; \ + \ + CUptiResult _status = apiFunctionCall; \ + if (_status != CUPTI_SUCCESS) { \ + const char* pErrorString; \ + cuptiGetResultString(_status, &pErrorString); \ + \ + std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ + << #apiFunctionCall << " failed with error(" << _status << "): " << pErrorString \ + << ".\n\n"; \ + \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#ifndef CUPTI_UTIL_CALL +#define CUPTI_UTIL_CALL(apiFunctionCall) \ + do { \ + CUptiUtilResult _status = apiFunctionCall; \ + if (_status != CUPTI_UTIL_SUCCESS) { \ + std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ + << #apiFunctionCall << " failed with error: " << _status << "\n\n"; \ + \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#ifndef NVPW_API_CALL +#define NVPW_API_CALL(apiFunctionCall) \ + do { \ + NVPA_Status _status = apiFunctionCall; \ + if (_status != NVPA_STATUS_SUCCESS) { \ + std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ + << #apiFunctionCall << " failed with error: " << _status << "\n\n"; \ + \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#ifndef MEMORY_ALLOCATION_CALL +#define MEMORY_ALLOCATION_CALL(variable) \ + do { \ + if (variable == NULL) { \ + std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ \ + << " Memory allocation failed.\n\n"; \ + \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#ifndef CHECK_CONDITION +#define CHECK_CONDITION(condition) \ + do { \ + if (!(condition)) { \ + std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Condition " << #condition \ + << " failed.\n\n"; \ + \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#ifndef CHECK_INTEGER_CONDITION +#define CHECK_INTEGER_CONDITION(argument1, operator, argument2) \ + do { \ + if (!(argument1 operator argument2)) { \ + std::cerr \ + << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Condition " << #argument1 << " " \ + << # \ + operator<< " " << #argument2 << " fails. " << #argument1 << " = " << argument1 << "," \ + " " << #argument2 << " = " << argument2 << "\n\n"; \ + \ + exit(EXIT_FAILURE); \ + } \ + } while (0) +#endif + +#endif // HELPER_CUPTI_H_ diff --git a/cpp/tests/utilities/test_cupti.cu b/cpp/tests/utilities/test_cupti.cu new file mode 100644 index 0000000000..de1778541a --- /dev/null +++ b/cpp/tests/utilities/test_cupti.cu @@ -0,0 +1,361 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include "../linear_programming/utilities/pdlp_test_utilities.cuh" +#include "../mip/mip_utils.cuh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cuopt::linear_programming::test { + +void init_handler(const raft::handle_t* handle_ptr) +{ + // Init cuBlas / cuSparse context here to avoid having it during solving time + RAFT_CUBLAS_TRY(raft::linalg::detail::cublassetpointermode( + handle_ptr->get_cublas_handle(), CUBLAS_POINTER_MODE_DEVICE, handle_ptr->get_stream())); + RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsesetpointermode( + handle_ptr->get_cusparse_handle(), CUSPARSE_POINTER_MODE_DEVICE, handle_ptr->get_stream())); +} + +__global__ void VectorAdd(const float* A, const float* B, float* C, int N) +{ + int idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < N) C[idx] = A[idx] + B[idx]; +} + +class cupti_profiler_t { + public: + cupti_profiler_t(const std::vector& metrics) + : metricNames_(metrics), pRangeProfilerObject_(nullptr) + { + } + + cupti_profiler_t(const cupti_profiler_t&) = delete; + cupti_profiler_t& operator=(const cupti_profiler_t&) = delete; + + ~cupti_profiler_t() + { + if (!pRangeProfilerObject_) return; + CUpti_RangeProfiler_Disable_Params disableRangeProfiler = { + .structSize = CUpti_RangeProfiler_Disable_Params_STRUCT_SIZE}; + disableRangeProfiler.pRangeProfilerObject = pRangeProfilerObject_; + cuptiRangeProfilerDisable(&disableRangeProfiler); + CUpti_Profiler_DeInitialize_Params profilerDeInitializeParams = { + .structSize = CUpti_Profiler_DeInitialize_Params_STRUCT_SIZE}; + cuptiProfilerDeInitialize(&profilerDeInitializeParams); + } + + void initialize_and_enable(CUcontext cuContext) + { + CUpti_Profiler_Initialize_Params profilerInitializeParams = { + .structSize = CUpti_Profiler_Initialize_Params_STRUCT_SIZE}; + cuptiProfilerInitialize(&profilerInitializeParams); + CUdevice device; + cuCtxGetDevice(&device); + CUpti_Device_GetChipName_Params getChipNameParams = { + .structSize = CUpti_Device_GetChipName_Params_STRUCT_SIZE}; + getChipNameParams.deviceIndex = (size_t)device; + cuptiDeviceGetChipName(&getChipNameParams); + chipName_ = getChipNameParams.pChipName; + CUpti_RangeProfiler_Enable_Params enableRange = { + .structSize = CUpti_RangeProfiler_Enable_Params_STRUCT_SIZE}; + enableRange.ctx = cuContext; + cuptiRangeProfilerEnable(&enableRange); + pRangeProfilerObject_ = enableRange.pRangeProfilerObject; + } + + void configure(CUpti_ProfilerRange range, CUpti_ProfilerReplayMode replayMode, size_t numOfRanges) + { + create_config_image(); + create_counter_data_image(numOfRanges); + + CUpti_RangeProfiler_SetConfig_Params setConfig = { + .structSize = CUpti_RangeProfiler_SetConfig_Params_STRUCT_SIZE}; + setConfig.pRangeProfilerObject = pRangeProfilerObject_; + setConfig.configSize = configImage_.size(); + setConfig.pConfig = configImage_.data(); + setConfig.counterDataImageSize = counterDataImage_.size(); + setConfig.pCounterDataImage = counterDataImage_.data(); + setConfig.range = range; + setConfig.replayMode = replayMode; + setConfig.maxRangesPerPass = numOfRanges; + setConfig.numNestingLevels = 1; + setConfig.minNestingLevel = 1; + setConfig.passIndex = 0; + setConfig.targetNestingLevel = 0; + cuptiRangeProfilerSetConfig(&setConfig); + } + + void start() + { + CUpti_RangeProfiler_Start_Params p = {.structSize = + CUpti_RangeProfiler_Start_Params_STRUCT_SIZE}; + p.pRangeProfilerObject = pRangeProfilerObject_; + cuptiRangeProfilerStart(&p); + } + + void stop() + { + CUpti_RangeProfiler_Stop_Params p = {.structSize = CUpti_RangeProfiler_Stop_Params_STRUCT_SIZE}; + p.pRangeProfilerObject = pRangeProfilerObject_; + cuptiRangeProfilerStop(&p); + } + + std::unordered_map decode() + { + CUpti_RangeProfiler_DecodeData_Params decodeData = { + .structSize = CUpti_RangeProfiler_DecodeData_Params_STRUCT_SIZE}; + decodeData.pRangeProfilerObject = pRangeProfilerObject_; + cuptiRangeProfilerDecodeData(&decodeData); + CUpti_RangeProfiler_GetCounterDataInfo_Params cdiParams = { + .structSize = CUpti_RangeProfiler_GetCounterDataInfo_Params_STRUCT_SIZE}; + cdiParams.pCounterDataImage = counterDataImage_.data(); + cdiParams.counterDataImageSize = counterDataImage_.size(); + cuptiRangeProfilerGetCounterDataInfo(&cdiParams); + evaluate_all_ranges(cdiParams.numTotalRanges > 10 ? 10 : cdiParams.numTotalRanges); + return metric_values; + } + + private: + void create_config_image() + { + CUpti_Profiler_Host_Initialize_Params hostInitializeParams = { + .structSize = CUpti_Profiler_Host_Initialize_Params_STRUCT_SIZE}; + hostInitializeParams.profilerType = CUPTI_PROFILER_TYPE_RANGE_PROFILER; + hostInitializeParams.pChipName = chipName_.c_str(); + hostInitializeParams.pCounterAvailabilityImage = nullptr; + cuptiProfilerHostInitialize(&hostInitializeParams); + CUpti_Profiler_Host_Object* pHostObject = hostInitializeParams.pHostObject; + + CUpti_Profiler_Host_ConfigAddMetrics_Params configAddMetricsParams = { + .structSize = CUpti_Profiler_Host_ConfigAddMetrics_Params_STRUCT_SIZE}; + configAddMetricsParams.pHostObject = pHostObject; + configAddMetricsParams.ppMetricNames = metricNames_.data(); + configAddMetricsParams.numMetrics = metricNames_.size(); + cuptiProfilerHostConfigAddMetrics(&configAddMetricsParams); + + CUpti_Profiler_Host_GetConfigImageSize_Params getConfigImageSizeParams = { + .structSize = CUpti_Profiler_Host_GetConfigImageSize_Params_STRUCT_SIZE}; + getConfigImageSizeParams.pHostObject = pHostObject; + cuptiProfilerHostGetConfigImageSize(&getConfigImageSizeParams); + configImage_.resize(getConfigImageSizeParams.configImageSize); + + CUpti_Profiler_Host_GetConfigImage_Params getConfigImageParams = { + .structSize = CUpti_Profiler_Host_GetConfigImage_Params_STRUCT_SIZE}; + getConfigImageParams.pHostObject = pHostObject; + getConfigImageParams.pConfigImage = configImage_.data(); + getConfigImageParams.configImageSize = configImage_.size(); + cuptiProfilerHostGetConfigImage(&getConfigImageParams); + + CUpti_Profiler_Host_GetNumOfPasses_Params getNumOfPassesParam = { + .structSize = CUpti_Profiler_Host_GetNumOfPasses_Params_STRUCT_SIZE}; + getNumOfPassesParam.pConfigImage = configImage_.data(); + getNumOfPassesParam.configImageSize = configImage_.size(); + cuptiProfilerHostGetNumOfPasses(&getNumOfPassesParam); + printf("Num of Passes: %d\n", (int)getNumOfPassesParam.numOfPasses); + CUpti_Profiler_Host_Deinitialize_Params deinitializeParams = { + .structSize = CUpti_Profiler_Host_Deinitialize_Params_STRUCT_SIZE}; + deinitializeParams.pHostObject = pHostObject; + cuptiProfilerHostDeinitialize(&deinitializeParams); + } + + void create_counter_data_image(size_t maxNumOfRangesInCounterDataImage) + { + CUpti_RangeProfiler_GetCounterDataSize_Params ctDataSize = { + .structSize = CUpti_RangeProfiler_GetCounterDataSize_Params_STRUCT_SIZE}; + ctDataSize.pRangeProfilerObject = pRangeProfilerObject_; + ctDataSize.pMetricNames = metricNames_.data(); + ctDataSize.numMetrics = metricNames_.size(); + ctDataSize.maxNumOfRanges = maxNumOfRangesInCounterDataImage; + ctDataSize.maxNumRangeTreeNodes = maxNumOfRangesInCounterDataImage; + cuptiRangeProfilerGetCounterDataSize(&ctDataSize); + counterDataImage_.resize(ctDataSize.counterDataSize); + CUpti_RangeProfiler_CounterDataImage_Initialize_Params initCtImg = { + .structSize = CUpti_RangeProfiler_CounterDataImage_Initialize_Params_STRUCT_SIZE}; + initCtImg.pRangeProfilerObject = pRangeProfilerObject_; + initCtImg.pCounterData = counterDataImage_.data(); + initCtImg.counterDataSize = counterDataImage_.size(); + cuptiRangeProfilerCounterDataImageInitialize(&initCtImg); + } + + void evaluate_range(size_t rangeIndex, CUpti_Profiler_Host_Object* pHostObject) + { + std::vector metricValues(metricNames_.size()); + CUpti_Profiler_Host_EvaluateToGpuValues_Params p = { + .structSize = CUpti_Profiler_Host_EvaluateToGpuValues_Params_STRUCT_SIZE}; + p.pHostObject = pHostObject; + p.pCounterDataImage = counterDataImage_.data(); + p.counterDataImageSize = counterDataImage_.size(); + p.ppMetricNames = metricNames_.data(); + p.numMetrics = metricNames_.size(); + p.rangeIndex = rangeIndex; + p.pMetricValues = metricValues.data(); + cuptiProfilerHostEvaluateToGpuValues(&p); + for (size_t i = 0; i < metricNames_.size(); ++i) + metric_values[metricNames_[i]] += (size_t)metricValues[i]; + } + + void evaluate_all_ranges(size_t numOfRanges) + { + CUpti_Profiler_Host_Initialize_Params hostInitializeParams = { + .structSize = CUpti_Profiler_Host_Initialize_Params_STRUCT_SIZE}; + hostInitializeParams.profilerType = CUPTI_PROFILER_TYPE_RANGE_PROFILER; + hostInitializeParams.pChipName = chipName_.c_str(); + hostInitializeParams.pCounterAvailabilityImage = nullptr; + cuptiProfilerHostInitialize(&hostInitializeParams); + for (size_t i = 0; i < numOfRanges; ++i) + evaluate_range(i, hostInitializeParams.pHostObject); + CUpti_Profiler_Host_Deinitialize_Params deinitializeParams = { + .structSize = CUpti_Profiler_Host_Deinitialize_Params_STRUCT_SIZE}; + deinitializeParams.pHostObject = hostInitializeParams.pHostObject; + cuptiProfilerHostDeinitialize(&deinitializeParams); + } + + std::unordered_map metric_values; + std::vector metricNames_; + CUpti_RangeProfiler_Object* pRangeProfilerObject_; + std::vector counterDataImage_, configImage_; + std::string chipName_; +}; + +void test_cupti() +{ + rmm::mr::cuda_memory_resource cuda_mr; + rmm::mr::set_current_device_resource(&cuda_mr); + + const raft::handle_t handle_{}; + auto stream = handle_.get_stream(); + std::string test_instance = "pk1.mps"; + + auto path = cuopt::test::get_rapids_dataset_root_dir() + ("/mip/" + test_instance); + cuopt::mps_parser::mps_data_model_t mps_problem = + cuopt::mps_parser::parse_mps(path, false); + handle_.sync_stream(); + auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); + problem_checking_t::check_problem_representation(op_problem); + + init_handler(op_problem.get_handle_ptr()); + // run the problem constructor of MIP, so that we do bounds standardization + detail::problem_t problem(op_problem); + problem.preprocess_problem(); + + const int vectorLen = 100000; + rmm::device_uvector d_A(vectorLen, stream), d_B(vectorLen, stream), d_C(vectorLen, stream); + + auto random_generator = [](unsigned int seed) { + return [=] __device__(int idx) { + thrust::default_random_engine rng(seed); + thrust::uniform_real_distribution dist(0.0f, 1.0f); + rng.discard(idx); + return dist(rng); + }; + }; + + thrust::transform(rmm::exec_policy(stream), + thrust::counting_iterator(0), + thrust::counting_iterator(vectorLen), + d_A.begin(), + random_generator(1234)); + thrust::transform(rmm::exec_policy(stream), + thrust::counting_iterator(0), + thrust::counting_iterator(vectorLen), + d_B.begin(), + random_generator(5678)); + + // Get CUDA context after CUDA operations have initialized it + CUcontext cuContext; + cuCtxGetCurrent(&cuContext); + + // cupti_profiler_t profiler({ + // "sm__warps_launched.sum", "l1tex__t_sectors.sum", "l1tex__data_bank_reads.sum", + // "l1tex__data_bank_writes.sum", "l1tex__m_xbar2l1tex_read_bytes.sum", + // "l1tex__m_l1tex2xbar_write_bytes.sum", "lts__t_sectors_op_read.sum", + // "lts__t_sectors_op_write.sum" + // }); + cupti_profiler_t profiler({"l1tex__t_sectors.sum"}); + profiler.initialize_and_enable(cuContext); + profiler.configure(CUPTI_AutoRange, CUPTI_KernelReplay, 10); + profiler.start(); + + detail::fj_settings_t fj_settings; + fj_settings.feasibility_run = false; + fj_settings.iteration_limit = 10; + fj_settings.seed = 42; + printf("Running FJ\n"); + auto solution = run_fj(problem, fj_settings).solution; + + // VectorAdd<<<(vectorLen + 127) / 128, 128, 0, stream.value()>>>(d_A.data(), d_B.data(), + // d_C.data(), vectorLen); + + profiler.stop(); + + stream.synchronize(); + + auto metric_values = profiler.decode(); + for (const auto& [k, v] : metric_values) + printf("%s: %zu\n", k.c_str(), v); + + const size_t reads = 2 * vectorLen * sizeof(float); + const size_t writes = vectorLen * sizeof(float); + const size_t sector_reads = reads / 32; + const size_t sector_writes = writes / 32; + const size_t total_sectors = sector_reads + sector_writes; + EXPECT_EQ(metric_values["l1tex__t_sectors.sum"], total_sectors); + // EXPECT_EQ(metric_values["l1tex__m_xbar2l1tex_read_bytes.sum"], reads); + // EXPECT_EQ(metric_values["l1tex__m_l1tex2xbar_write_bytes.sum"], writes); +} + +TEST(cupti, test_cupti) { test_cupti(); } + +} // namespace cuopt::linear_programming::test From 429c4929949b6e7302dc6664c4ecb78ee0fd31ac Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 29 Oct 2025 17:24:36 +0000 Subject: [PATCH 013/225] Adding support for work unit inference w/ xgboost --- .gitignore | 1 + cpp/CMakeLists.txt | 2 + cpp/src/CMakeLists.txt | 43 +++- .../recombiners/bound_prop_recombiner.cuh | 4 +- .../mip/feasibility_jump/feasibility_jump.cu | 218 ++++++++++++++++++ .../mip/feasibility_jump/feasibility_jump.cuh | 8 + cpp/src/mip/problem/problem.cu | 53 +++++ cpp/src/mip/problem/problem.cuh | 6 + cpp/src/mip/solver_context.cuh | 7 + cpp/src/utilities/embed_models.sh | 89 +++++++ cpp/src/utilities/models/fj.ubj.gz | Bin 0 -> 12153 bytes cpp/src/utilities/work_unit_predictor.cpp | 184 +++++++++++++++ cpp/src/utilities/work_unit_predictor.hpp | 41 ++++ cpp/tests/mip/feasibility_jump_tests.cu | 29 ++- 14 files changed, 671 insertions(+), 14 deletions(-) create mode 100755 cpp/src/utilities/embed_models.sh create mode 100644 cpp/src/utilities/models/fj.ubj.gz create mode 100644 cpp/src/utilities/work_unit_predictor.cpp create mode 100644 cpp/src/utilities/work_unit_predictor.hpp diff --git a/.gitignore b/.gitignore index 9edc9823c1..3f02aaa413 100644 --- a/.gitignore +++ b/.gitignore @@ -66,5 +66,6 @@ docs/cuopt/build # generated version file cpp/include/cuopt/semantic_version.hpp +cpp/src/utilities/models_ubj.h !datasets/quadratic_programming !datasets/quadratic_programming/** diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 70c6ea0cc7..0b62ec0eb4 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -208,6 +208,7 @@ rapids_cpm_rapids_logger(BUILD_EXPORT_SET cuopt-exports INSTALL_EXPORT_SET cuopt create_logger_macros(CUOPT "cuopt::default_logger()" include/cuopt) find_package(CUDSS REQUIRED) +find_package(xgboost REQUIRED) if(BUILD_TESTS) include(cmake/thirdparty/get_gtest.cmake) @@ -316,6 +317,7 @@ target_link_libraries(cuopt rapids_logger::rapids_logger CCCL::CCCL raft::raft + xgboost::xgboost cuopt::mps_parser ${CUDSS_LIB_FILE} PRIVATE diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index d6868b7d39..837affffe6 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -13,10 +13,51 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Check for required tools +find_program(XXD_EXECUTABLE xxd) +find_program(GZIP_EXECUTABLE gzip) +find_program(MKTEMP_EXECUTABLE mktemp) + +if(NOT XXD_EXECUTABLE) + message(FATAL_ERROR "xxd command not found. Please install xxd (typically provided by vim package).") +endif() + +if(NOT GZIP_EXECUTABLE) + message(FATAL_ERROR "gzip command not found. Please install gzip.") +endif() + +if(NOT MKTEMP_EXECUTABLE) + message(FATAL_ERROR "mktemp command not found. Please install coreutils or equivalent package.") +endif() + +# Directory containing model files +set(XGB_MODEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/utilities/models") +# Find all .ubj.gz model files in models/ +file(GLOB XGB_MODEL_FILES "${XGB_MODEL_DIR}/*.ubj.gz") +message(STATUS "XGB_MODEL_DIR: ${XGB_MODEL_DIR}") +message(STATUS "Found ${XGB_MODEL_FILES} model files") + +# Set output header name +set(XGB_MODELS_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/utilities/models_ubj.h") + +# Path to the embedding script +set(EMBED_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/utilities/embed_models.sh") + +# Custom command to generate header by calling the shell script +add_custom_command( + OUTPUT ${XGB_MODELS_HEADER} + COMMAND sh ${EMBED_SCRIPT} ${XGB_MODELS_HEADER} ${XGB_MODEL_FILES} + DEPENDS ${XGB_MODEL_FILES} ${EMBED_SCRIPT} + COMMENT "Decompressing and embedding all UBJ models into ${XGB_MODELS_HEADER}" +) + +add_custom_target(embed_xgboost_models ALL DEPENDS ${XGB_MODELS_HEADER}) + 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/timestamp_utils.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_predictor.cpp) add_subdirectory(linear_programming) add_subdirectory(math_optimization) diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index 9eb1b8ee3a..9a4df67962 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -208,7 +208,7 @@ class bound_prop_recombiner_t : public recombiner_t { constraint_prop.apply_round(offspring, lp_run_time_after_feasible, timer, probing_config); constraint_prop.single_rounding_only = false; offspring.compute_feasibility(); - cuopt_func_call(bool feasible_after_bounds_prop = offspring.get_feasible()); + bool feasible_after_bounds_prop = offspring.get_feasible(); cuopt_func_call(f_t excess_before = offspring.get_total_excess()); CUOPT_LOG_ERROR("Excess before: %g, %g, %g, %g, feas %d", offspring.get_total_excess(), @@ -223,7 +223,7 @@ class bound_prop_recombiner_t : public recombiner_t { offspring.handle_ptr->sync_stream(); offspring.unfix_variables(fixed_assignment, variable_map); offspring.compute_feasibility(); - cuopt_func_call(bool feasible_after_unfix = offspring.get_feasible()); + bool feasible_after_unfix = offspring.get_feasible(); cuopt_func_call(f_t excess_after_unfix = offspring.get_total_excess()); if (feasible_after_unfix != feasible_after_bounds_prop) { CUOPT_LOG_WARN("Numerical issue in bounds prop, infeasibility after unfix"); diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 42620ba150..264e022cff 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -38,6 +38,9 @@ #include +#include +#include + #define FJ_LOG_PREFIX "FJ: " namespace cuopt::linear_programming::detail { @@ -887,6 +890,181 @@ void fj_t::refresh_lhs_and_violation(const rmm::cuda_stream_view& stre data.violated_constraints.sort(stream); } +template +std::map fj_t::get_feature_vector(i_t climber_idx) const +{ + auto& data = *climbers[climber_idx]; + auto climber_stream = data.stream.view(); + if (climber_idx == 0) climber_stream = handle_ptr->get_stream(); + + std::map features; + + // Basic problem dimensions + features["n_variables"] = (float)pb_ptr->n_variables; + features["n_constraints"] = (float)pb_ptr->n_constraints; + features["nnz"] = (float)pb_ptr->coefficients.size(); + + // Matrix sparsity metrics (already computed) + features["sparsity"] = (float)pb_ptr->sparsity; + features["nnz_stddev"] = (float)pb_ptr->nnz_stddev; + features["unbalancedness"] = (float)pb_ptr->unbalancedness; + + // Algorithm settings + features["target_time"] = (float)settings.work_unit_limit; + features["n_of_minimums_for_exit"] = (float)settings.n_of_minimums_for_exit; + features["feasibility_run"] = (float)settings.feasibility_run; + + // Variable type metrics + features["n_integer_vars"] = (float)pb_ptr->n_integer_vars; + features["n_binary_vars"] = (float)pb_ptr->n_binary_vars; + features["integer_ratio"] = + pb_ptr->n_variables > 0 ? (float)pb_ptr->n_integer_vars / pb_ptr->n_variables : 0.0f; + features["binary_ratio"] = + pb_ptr->n_variables > 0 ? (float)pb_ptr->n_binary_vars / pb_ptr->n_variables : 0.0f; + + // Initial violation metrics (from current state) + features["initial_violation_count"] = + (float)data.violated_constraints.set_size.value(climber_stream); + // features["initial_violation_score"] = (float)data.violation_score.value(climber_stream); + // features["initial_weighted_violation"] = + // (float)data.weighted_violation_score.value(climber_stream); + + // Load balancing decision + bool use_load_balancing = false; + if (settings.load_balancing_mode == fj_load_balancing_mode_t::ALWAYS_OFF) { + use_load_balancing = false; + } else if (settings.load_balancing_mode == fj_load_balancing_mode_t::ALWAYS_ON) { + use_load_balancing = true; + } else if (settings.load_balancing_mode == fj_load_balancing_mode_t::AUTO) { + use_load_balancing = + pb_ptr->n_variables > settings.parameters.load_balancing_codepath_min_varcount; + } + if (settings.mode == fj_mode_t::ROUNDING) { use_load_balancing = false; } + features["uses_load_balancing"] = (float)use_load_balancing; + + // Related variables metrics (if available) + if (pb_ptr->related_variables_offsets.size() > 0) { + auto h_offsets = cuopt::host_copy(pb_ptr->related_variables_offsets, handle_ptr->get_stream()); + i_t total_related = 0; + i_t max_related = 0; + for (i_t i = 0; i < pb_ptr->n_variables; ++i) { + i_t count = h_offsets[i + 1] - h_offsets[i]; + total_related += count; + max_related = std::max(max_related, count); + } + features["avg_related_vars_per_var"] = + pb_ptr->n_variables > 0 ? (float)total_related / pb_ptr->n_variables : 0.0f; + features["max_related_vars"] = (float)max_related; + } else { + features["avg_related_vars_per_var"] = 0.0f; + features["max_related_vars"] = 0.0f; + } + + // Constraint characteristics + auto h_lower = cuopt::host_copy(pb_ptr->constraint_lower_bounds, handle_ptr->get_stream()); + auto h_upper = cuopt::host_copy(pb_ptr->constraint_upper_bounds, handle_ptr->get_stream()); + i_t n_equality = 0; + i_t n_tight = 0; + f_t total_range = 0.0; + i_t n_range_constraints = 0; + + for (i_t i = 0; i < pb_ptr->n_constraints; ++i) { + if (pb_ptr->integer_equal(h_lower[i], h_upper[i])) { + n_equality++; + } else { + f_t range = h_upper[i] - h_lower[i]; + if (std::isfinite(range)) { + total_range += range; + n_range_constraints++; + if (range < 1.0) n_tight++; + } + } + } + features["equality_ratio"] = + pb_ptr->n_constraints > 0 ? (float)n_equality / pb_ptr->n_constraints : 0.0f; + features["avg_constraint_range"] = + n_range_constraints > 0 ? (float)(total_range / n_range_constraints) : 0.0f; + features["tight_constraint_ratio"] = + pb_ptr->n_constraints > 0 ? (float)n_tight / pb_ptr->n_constraints : 0.0f; + + // Variable bound characteristics + auto h_var_bounds = cuopt::host_copy(pb_ptr->variable_bounds, handle_ptr->get_stream()); + i_t n_unbounded = 0; + i_t n_fixed = 0; + f_t total_var_range = 0.0; + i_t n_bounded_vars = 0; + + for (i_t i = 0; i < pb_ptr->n_variables; ++i) { + f_t lower = get_lower(h_var_bounds[i]); + f_t upper = get_upper(h_var_bounds[i]); + + if (!std::isfinite(lower) || !std::isfinite(upper)) { + n_unbounded++; + } else if (pb_ptr->integer_equal(lower, upper)) { + n_fixed++; + } else { + f_t range = upper - lower; + total_var_range += range; + n_bounded_vars++; + } + } + features["unbounded_var_ratio"] = + pb_ptr->n_variables > 0 ? (float)n_unbounded / pb_ptr->n_variables : 0.0f; + features["fixed_var_ratio"] = + pb_ptr->n_variables > 0 ? (float)n_fixed / pb_ptr->n_variables : 0.0f; + features["avg_variable_range"] = + n_bounded_vars > 0 ? (float)(total_var_range / n_bounded_vars) : 0.0f; + + // Objective characteristics + auto h_obj_coeffs = cuopt::host_copy(pb_ptr->objective_coefficients, handle_ptr->get_stream()); + i_t n_obj_vars = 0; + f_t total_obj_magnitude = 0.0; + for (i_t i = 0; i < pb_ptr->n_variables; ++i) { + if (h_obj_coeffs[i] != 0.0) { + n_obj_vars++; + total_obj_magnitude += std::abs(h_obj_coeffs[i]); + } + } + features["obj_var_ratio"] = + pb_ptr->n_variables > 0 ? (float)n_obj_vars / pb_ptr->n_variables : 0.0f; + features["avg_obj_coeff_magnitude"] = + n_obj_vars > 0 ? (float)(total_obj_magnitude / n_obj_vars) : 0.0f; + + // Matrix density patterns + auto h_offsets = cuopt::host_copy(pb_ptr->offsets, handle_ptr->get_stream()); + i_t max_nnz_per_row = 0; + i_t min_nnz_per_row = pb_ptr->n_variables; + f_t sum_sq_dev = 0.0; + f_t mean_nnz = + pb_ptr->n_constraints > 0 ? (f_t)pb_ptr->coefficients.size() / pb_ptr->n_constraints : 0.0f; + + for (i_t i = 0; i < pb_ptr->n_constraints; ++i) { + i_t nnz_row = h_offsets[i + 1] - h_offsets[i]; + max_nnz_per_row = std::max(max_nnz_per_row, nnz_row); + min_nnz_per_row = std::min(min_nnz_per_row, nnz_row); + f_t dev = nnz_row - mean_nnz; + sum_sq_dev += dev * dev; + } + features["max_nnz_per_row"] = (float)max_nnz_per_row; + features["min_nnz_per_row"] = (float)min_nnz_per_row; + features["nnz_variance"] = + pb_ptr->n_constraints > 0 ? (float)(sum_sq_dev / pb_ptr->n_constraints) : 0.0f; + + // Average variable degree (avg constraints per variable) + features["avg_var_degree"] = + pb_ptr->n_variables > 0 ? (float)pb_ptr->coefficients.size() / pb_ptr->n_variables : 0.0f; + + // Derived complexity metrics + features["problem_size_score"] = + (float)(pb_ptr->n_variables * pb_ptr->n_constraints) * (float)pb_ptr->sparsity; + features["structural_complexity"] = + (features["integer_ratio"] + 1.0f) * (float)pb_ptr->unbalancedness; + features["constraint_var_ratio"] = + pb_ptr->n_variables > 0 ? (float)pb_ptr->n_constraints / pb_ptr->n_variables : 0.0f; + + return features; +} + template i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) { @@ -999,6 +1177,9 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) solution.get_feasible(), data.local_minimums_reached.value(climber_stream)); + // compute total time spent + double elapsed_time = timer.elapsed_time(); + CUOPT_LOG_TRACE("best fractional count %d", data.saved_best_fractional_count.value(climber_stream)); @@ -1098,6 +1279,26 @@ i_t fj_t::solve(solution_t& solution) pb_ptr->check_problem_representation(true); resize_vectors(solution.handle_ptr); + // if work_limit is set: compute an estimate of the number of iterations required + if (settings.work_unit_limit != std::numeric_limits::infinity()) { + auto features = std::vector{ + (float)settings.work_unit_limit, + (float)settings.n_of_minimums_for_exit, + (float)pb_ptr->n_variables, + (float)pb_ptr->n_constraints, + (float)pb_ptr->coefficients.size(), + (float)pb_ptr->sparsity, + (float)pb_ptr->nnz_stddev, + (float)pb_ptr->unbalancedness, + }; + float iter_prediction = std::max( + (f_t)0.0, (f_t)ceil(context.work_unit_predictors.fj_predictor.predict_scalar(features))); + CUOPT_LOG_DEBUG("FJ determ: Estimated number of iterations for %f WU: %f", + settings.work_unit_limit, + iter_prediction); + settings.iteration_limit = std::min(settings.iteration_limit, (i_t)iter_prediction); + } + bool is_initial_feasible = solution.compute_feasibility(); auto initial_solution = solution; // if we're in rounding mode, split the time/iteration limit between the first and second stage @@ -1132,6 +1333,9 @@ i_t fj_t::solve(solution_t& solution) RAFT_CHECK_CUDA(handle_ptr->get_stream()); handle_ptr->sync_stream(); + // Compute and store feature vector for later logging + feature_vector = get_feature_vector(0); + i_t iterations = host_loop(solution); RAFT_CHECK_CUDA(handle_ptr->get_stream()); handle_ptr->sync_stream(); @@ -1181,6 +1385,20 @@ i_t fj_t::solve(solution_t& solution) cuopt_func_call(solution.test_variable_bounds()); + // Print compact feature vector summary + char logbuf[4096]; + int offset = 0; + offset += snprintf(logbuf + offset, + sizeof(logbuf) - offset, + "FJ: iter=%d time=%g", + iterations, + timer.elapsed_time()); + for (const auto& [name, value] : feature_vector) { + offset += snprintf(logbuf + offset, sizeof(logbuf) - offset, " %s=%g", name.c_str(), value); + if (offset >= (int)(sizeof(logbuf) - 32)) break; + } + CUOPT_LOG_INFO("%s", logbuf); + return is_new_feasible; } diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh index 314a2f369c..1ee19b39ac 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh @@ -29,6 +29,9 @@ #include +#include +#include + #define FJ_DEBUG_LOAD_BALANCING 0 #define FJ_SINGLE_STEP 0 @@ -109,6 +112,7 @@ struct fj_settings_t { fj_mode_t mode{fj_mode_t::FIRST_FEASIBLE}; fj_candidate_selection_t candidate_selection{fj_candidate_selection_t::WEIGHTED_SCORE}; double time_limit{60.0}; + double work_unit_limit{std::numeric_limits::infinity()}; int iteration_limit{std::numeric_limits::max()}; fj_hyper_parameters_t parameters{}; int n_of_minimums_for_exit = 7000; @@ -637,6 +641,10 @@ class fj_t { std::vector> climbers; rmm::device_uvector climber_views; fj_settings_t settings; + std::map feature_vector; + + private: + std::map get_feature_vector(i_t climber_idx = 0) const; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 736c4b8a67..017245c90b 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -96,6 +96,7 @@ void problem_t::op_problem_cstr_body(const optimization_problem_t(*this, combined_bounds); @@ -724,6 +725,55 @@ void problem_t::recompute_auxilliary_data(bool check_representation) if (check_representation) cuopt_func_call(check_problem_representation(true)); } +template +void problem_t::compute_auxiliary_data() +{ + raft::common::nvtx::range fun_scope("compute_auxiliary_data"); + + // Compute sparsity: nnz / (n_rows * n_cols) + sparsity = (n_constraints > 0 && n_variables > 0) + ? static_cast(nnz) / (static_cast(n_constraints) * n_variables) + : 0.0; + + // Compute stddev of non-zeros per row (on device) + nnz_stddev = 0.0; + unbalancedness = 0.0; + if (offsets.size() == static_cast(n_constraints + 1) && n_constraints > 0) { + // First: compute nnz per row on device + rmm::device_uvector d_nnz_per_row(n_constraints, handle_ptr->get_stream()); + thrust::transform(handle_ptr->get_thrust_policy(), + offsets.begin() + 1, + offsets.begin() + n_constraints + 1, + offsets.begin(), + d_nnz_per_row.begin(), + thrust::minus()); + + // Compute mean + double sum = thrust::reduce(handle_ptr->get_thrust_policy(), + d_nnz_per_row.begin(), + d_nnz_per_row.end(), + 0.0, + thrust::plus()); + double mean = sum / n_constraints; + + // Compute variance + double variance = thrust::transform_reduce( + handle_ptr->get_thrust_policy(), + d_nnz_per_row.begin(), + d_nnz_per_row.end(), + [mean] __device__(i_t x) -> double { + double diff = static_cast(x) - mean; + return diff * diff; + }, + 0.0, + thrust::plus()) / + n_constraints; + + nnz_stddev = std::sqrt(variance); + unbalancedness = nnz_stddev / mean; + } +} + template void problem_t::compute_n_integer_vars() { @@ -1276,6 +1326,7 @@ void problem_t::remove_given_variables(problem_t& original_p coefficients.resize(nnz, handle_ptr->get_stream()); variables.resize(nnz, handle_ptr->get_stream()); compute_transpose_of_problem(); + compute_auxiliary_data(); combine_constraint_bounds(*this, combined_bounds); handle_ptr->sync_stream(); recompute_auxilliary_data(); @@ -1492,6 +1543,7 @@ void problem_t::preprocess_problem() standardize_bounds(variable_constraint_map, *this); compute_csr(variable_constraint_map, *this); compute_transpose_of_problem(); + compute_auxiliary_data(); cuopt_func_call(check_problem_representation(true, false)); presolve_data.initialize_var_mapping(*this, handle_ptr); integer_indices.resize(n_variables, handle_ptr->get_stream()); @@ -1659,6 +1711,7 @@ void problem_t::add_cutting_plane_at_objective(f_t objective) objective); insert_constraints(h_constraints); compute_transpose_of_problem(); + compute_auxiliary_data(); cuopt_func_call(check_problem_representation(true)); } diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index 7b7daac132..984b63e431 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -81,6 +81,7 @@ class problem_t { void check_problem_representation(bool check_transposed = false, bool check_mip_related_data = true); void recompute_auxilliary_data(bool check_representation = true); + void compute_auxiliary_data(); void compute_n_integer_vars(); void compute_binary_var_table(); void compute_related_variables(double time_limit); @@ -223,6 +224,11 @@ class problem_t { bool is_binary_pb{false}; bool empty{false}; + // Auxiliary problem statistics + double sparsity{0.0}; + double nnz_stddev{0.0}; + double unbalancedness{0.0}; + presolve_data_t presolve_data; // original variable ids diff --git a/cpp/src/mip/solver_context.cuh b/cpp/src/mip/solver_context.cuh index 1f7f676231..07ab64964f 100644 --- a/cpp/src/mip/solver_context.cuh +++ b/cpp/src/mip/solver_context.cuh @@ -20,11 +20,16 @@ #include #include #include +#include #pragma once namespace cuopt::linear_programming::detail { +struct mip_solver_work_unit_predictors_t { + work_unit_predictor_t fj_predictor{"fj"}; +}; + // Aggregate structure containing the global context of the solving process for convenience: // The current problem, user settings, raft handle and statistics objects template @@ -45,6 +50,8 @@ struct mip_solver_context_t { const mip_solver_settings_t settings; pdlp_initial_scaling_strategy_t& scaling; solver_stats_t stats; + // TODO: ensure thread local (or use locks...?) + mip_solver_work_unit_predictors_t work_unit_predictors; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/utilities/embed_models.sh b/cpp/src/utilities/embed_models.sh new file mode 100755 index 0000000000..f402c411ce --- /dev/null +++ b/cpp/src/utilities/embed_models.sh @@ -0,0 +1,89 @@ +#!/bin/sh +# 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. + +set -e + +if [ $# -lt 2 ]; then + echo "Usage: $0 [model2.ubj.gz ...]" + exit 1 +fi + +OUTPUT_HEADER="$1" +shift + +# Initialize the header file +echo "// Auto-generated model arrays" > "$OUTPUT_HEADER" +echo "" >> "$OUTPUT_HEADER" + +# Collect model names for the mapping array +MODEL_NAMES="" + +# Process each model file +for MODEL_FILE in "$@"; do + # Extract base name without .ubj.gz extension + MODEL_BASENAME=$(basename "$MODEL_FILE" .ubj.gz) + + echo "Processing: $MODEL_BASENAME from $MODEL_FILE" + + # Create temporary file with proper name so xxd uses it + TEMP_DIR=$(mktemp -d) + TEMP_FILE="$TEMP_DIR/${MODEL_BASENAME}.ubj" + + # Decompress the model + gzip -cd "$MODEL_FILE" > "$TEMP_FILE" + + # Convert to C array with xxd and append to header + # xxd generates variable names like: _tmp_tmp_xyz_model_ubj + # We need to replace the entire variable name, not just add to it + # Add const to place arrays in .rodata section + xxd -i "$TEMP_FILE" | \ + sed "s/unsigned char [_a-zA-Z0-9]*/const unsigned char ${MODEL_BASENAME}_ubj/" | \ + sed "s/unsigned int [_a-zA-Z0-9]*/const unsigned int ${MODEL_BASENAME}_ubj_len/" >> "$OUTPUT_HEADER" + + # Clean up + rm -rf "$TEMP_DIR" + + # Add to list for mapping array + if [ -z "$MODEL_NAMES" ]; then + MODEL_NAMES="$MODEL_BASENAME" + else + MODEL_NAMES="$MODEL_NAMES $MODEL_BASENAME" + fi +done + +# Add model mapping structure +echo "" >> "$OUTPUT_HEADER" +echo "// Model mapping structure" >> "$OUTPUT_HEADER" +echo "struct xgboost_model_info {" >> "$OUTPUT_HEADER" +echo " const char* name;" >> "$OUTPUT_HEADER" +echo " const unsigned char* data;" >> "$OUTPUT_HEADER" +echo " unsigned int length;" >> "$OUTPUT_HEADER" +echo "};" >> "$OUTPUT_HEADER" +echo "" >> "$OUTPUT_HEADER" + +# Generate the mapping array +echo "// Array of all available models" >> "$OUTPUT_HEADER" +echo "static const struct xgboost_model_info xgboost_models[] = {" >> "$OUTPUT_HEADER" + +for MODEL_NAME in $MODEL_NAMES; do + echo " {\"$MODEL_NAME\", ${MODEL_NAME}_ubj, ${MODEL_NAME}_ubj_len}," >> "$OUTPUT_HEADER" +done + +echo "};" >> "$OUTPUT_HEADER" +echo "" >> "$OUTPUT_HEADER" +echo "static const unsigned int xgboost_models_count = sizeof(xgboost_models) / sizeof(xgboost_models[0]);" >> "$OUTPUT_HEADER" + +echo "Successfully embedded models into $OUTPUT_HEADER" diff --git a/cpp/src/utilities/models/fj.ubj.gz b/cpp/src/utilities/models/fj.ubj.gz new file mode 100644 index 0000000000000000000000000000000000000000..08c4d7c1b062e4d73b8afb1e2d83c8d68c6246f2 GIT binary patch literal 12153 zcmV-Wo#~WVrl^God;MHSNHHQy(o&Wu%lu`C5FTTHr$2<8+M~aF_vIQu`BxB znVp4oqtWCu{)5lEJm;BZ=iWPIcg~#OIp?04;14t}vzUm`gxH9L3EB@VLz9vcqQgcf zMI=^!Rx>d?dPHU0J`Ii!O$Z%Xxvoy^=#ktk36 zyOj;kT5LhgZ?~Xlx|pI)4M(MR70Bvb531ZWOeBiwkBko27A4d=N4?HuD&-M{Nx+dcFtaSy(0nJxRF#})AlI;xtKB-=8l%k zwHBHEwo4RuFG{pT>VazB>q+HDcNR4~9!i-XdIC)rc~axQwL`YcoKy}*(^Te$hm_rF z-d8@f%lGk^k*3_b_lj`w+uFp~vNxpNw#H;=pgXZR^;@z@Wn%q6>&wnx(=XQ1Y;F|ek~8q{gZUR68#Hfla^2z+w79~|+d1LaD$Cx1UTj#}4$ z5xRRO8r`qwLY;SLkG5>crL24Ipq89{Nm(9g4yUhOi#l#@jG}tFq9@OLRDMt4p-B;u zaS74*%X5f%&u_VEeQ+%PnZCbOU7c2m5s@SDZyz`G|LfrrqsK?Qzg5kl5yL`9$FSG% zu%sY|epNpi@bA7VFQVw7RX=Sjb}xd%qoQMmCPc*Q9zVM~23U;P3xL&6?3Ke_Tx>5a zwb|>KJv~_JV`+$`5tb%c?65S)(gI5>EUn+-7hbl-(hf_9xBLsg@5C=VW9fpW8x|KV zJ+bt{;*P}=i#HY@ECd$F_o1-(V)4Tw#=>BcV(EjWAK&I1EP+^p`2PpLtp=~kA4^PJ zVj})}LSrK%-qkOC-9`!eq?9N3F$xw1B-UvfFyo+cfNXbEz?98EqHN6*A`cdumB?nw)ul&0Lalv4nXx2}s?qm#}RlBVq;8XZt1omzQvTBpz!sKs9(( z-m>v6Ors5SE}QQ(KQryifEPnvjCe8T#e^4AUd(tg=f#2-OI~X5Qj-@eUTX1Dn-^hNO2OFdrd^U{EqMmm=-D@}Q^!=7;y7JOZM`gHZzl2%|><#&YYR!;8_onDZUtodM8*RKWIuvrBbe<=k|c82(ixD3BX0YO>}9@tCcDD6c%QK8 z~#pm%=O9Rh!Etm!0jDiBmB)5%Kn4PnbVI{aO`NNX1WUU6xaoUzXTfLC%_rDvw| zUbnq=98;b4+Ly7;IJ)Z%bS_|^w>>kRz9#(AkngWmxJFv#t+QX?J>&hS?>v^8uZM-S z{|ghcMj}SDC=8d;p|S5=x}wv41ftWPG6Jh?(Wyx#-#n#jF!Bmqzz1(2~nfLLXd8%h9afYnlZ1|Zhp?c>3T!!kfj)oh;~-v`?}KJmD%CH^c2 zZ&$6^4|cnP49)&mBOr|DDq+u-o_UEr?4vNY!Kh@^<2RjBR! z&T5|gt5&vV3=19;5uOy6pu_T$CVd>0gS_wUSgN#jZ%D~^ zn<~!<$D;28XNWwOL28+7q-y%GrDSo%S~&fPH#*p5plIH=Y1EsvG|Ki!H|prEU8>Fd z+Yt1t8sxSv_2818n^E1QRnV+|8f7uLDOxpF0UtIGqH^<|h-~Toyoo=rtR)4WxF+%n9RI62CNY!*L}I6XF7xZ}}aa^duuqwvoRdYFIb`Fv96Ms;N zQ?8(WV|K%v!E4ZvA@kwBJ{!pS`Msz*-*rZj!7V5klM=KjVSe>QHxaq-VOM zg}s0quQ+oCwV8S>c4L0d>CVc{sLo-^sv%c4`Yq>Fx5ckz3F?_106Kg zj$@#Mv&MQDY^L+Sq0VP!IttWKZ$C|S`c*e<4f;Ps)}X&e))yg^u|pNIviO;fwO9<6 zY`lKs0zmcY1JJz-?;r5qLqf4bc!vMlJjXptUstTIml^ z$L)YJ!?@~zFYg@~U8g7kd@TXwE;)eH(*apm4#<@mRj4ZGQMH9;JA{bG>~;8kDMnR{ zroz=4qf-}EIXpe2K~w>M%sz4+p)Y)KMD6mq5H(oP-pyQbdYd0LcQvJ4QkI7LF5W9L zr*f!)!5dXB?S_yWf{sA1Nq%UKGD6fS(S=#&0UM{+}qjBMSrG2H$`1$n;v9=Oafm&|UFi*61nMe7M~xHZ=YCC&7p zu8l*~%xOl{yt7M1ol{h3a_DF(Yf3p5y@8Z*HvW&_qJ6GVTVYCykk6i4u;;ybsHAlqyng2)d=R!2M!AQ>cAbjQ0GH_hMRPxft%hGXn#&?9 zZ2(G`G4$az8Da+oun*2$=4&ETG*T2Wf0r9qAN%xs+L0Bwe&8 zhk0 zWA6n?r@&PD%-F>o#*PV|M6XYjN$gfdNldBDk_LMK^G&!0TVESxNM2u0WxD1KqfKH2 zbm1dw@tqANpqjj@b2PWE&LyjhwYI$c*P^-lSo@)9?x$kzXN~5v$HJ?dO3c+F?&pr? z^6NOv)s5y>mq_kEh~^HzI>*azp&N^!0_V18xM*%HSioWLlru$I?4>#k2P|58O^aRN zS;H{^b&BGmzbIS|P_1~tnob;k3XeyzrjzT&f^4nn3X4RsWt@q2JocN~-VL|zT_6s%Ucn&@G$ zDgT+_2awrNXCJH8ou@iQb6M?1VAU#8tqno<4ppJ?1q}uYxAM{4tRXULU_*dT58%+4 zIHAEnEgEalki$nT!ik#yg%S-mic%`uKf58QRqbk83V+QSLBenAL%%};lDRw&(H(rzJf}ot zxA<_ulX}yUd}b4L&2BWjMO#slhOMc%pg*Vu-OSO^$!AqveFBuTA7&Bs;PXDGiXM?! z-)4{vi;7|C&It0#*>!Mk|6^o_(Sy;P=3CVDHjh9{A7;VQ^R-a>hUO$W%&4IC9+v`H zt36a_%K<1&wwp??*BRA*FaW(yJV5Os^N~xZ477UWDdo_%law9Y^L-W=eW$cuY%R3A z+?%lPU!-ij;dh_cXO|HvbuW=1wFlw9LZ--TIf(4sGXx6yJP{U{%_RG;90lEN77&f^ zkB51i{h`|eLY3Waw=m725&W*_Zdh337*TMj7(VMio{YF-2tz%h$mMba)%Jak>WuW4 z!pD}8$bQFqv~a~s_`TOFBwk&LZqJ&hJ{I*WoORoQ?6rP5+}CgdTIgv}FfpYU`eXPy zc(5%bXWRN$Pn3@Q!s?hsYNL-Mb>!zpYAsId*vegWY|n1|@}oG-YB#>^2P42NO7m?h zahkv7gkoXIF5+!9eti_VBLl8J@(VB;bwR3X{#_Eu4Q_x$zx>8 zotcdjGQ=Sjw#@M?`^pIN-KV7-dWRvA#8K?)|1wf0?!I_A-Aa%lmOmItr%)mEG~4UU zuKPK3sqH`xxouXf#B0Y0ddn>`m;eD_COx%gcKjp|tBRz457)3n0mLP?a;Dzv3lf`} z{r%56$(Seeb0i_@A#{oefolBvD%=O-Y0 zw+U(9RKZ@%ts`G6>7?EaCoo`c#>sYYO3Bs@p#WL(`P@8{=^pRSm zhr`Q&7U@|$_Rcpf_Y7VC*xTl`kNbw?Viv32PA0x$k=uLy?H+7I+tabf9XMSE(By2u zB0U?$hEvS}o1hKr&U^}OtpV#A(3*HGp0g&N_cHK|MSCv#J<|htp3OD$OkcL2i)^#~ z)`S4>;OBQ)6OTjsoH9V#Vb=Hwf6bR)op&96bq?zmfT|3@nt1M3gOXSmgQ(LI0lS}6 zqalDS$9K^U@zGy{iT7dTkJLeKBR%Xk)kAedou#Q>6x&!YsaiKtTiF zsQ_&f0peHOe?^EUYOa<6A-<*f2y_y@JIR4Co{v^*_eZ1?sXoLfe#dJ*6NPV;Q|VR! zzDWmYttCJ|%K%(^0iaGL0Bt@8psxhLQAGfyF9*;h9uW98PRzyIF~a-NO`vfRC}f;# z#7BD^8Jf%i0$bodC43Tl4(`)X&Lw!WiR@g40N|tO0^W5YsB#rlM*GvNlEv9f0QNoW z^7x{z1EVg^-9DG^AX?C=Os(*j45aF(O;Orh%z-CMT2qU1m!cbaCCZ;??IT;-Zz7cm zA;@B5IkhoZj>g}eK-pgEi~2^rP~LPZ5gt4ekXUrlVTZ}|#U1!zBC9_e>CN%jg{HQYSzOS;`lGTDoSq{pE zkL*?TY#fzM1EJ5UtAWCwv%V!-%SMv@zTc)SP4y+(h<_$6P4b~tjYedx<;AKi<#IS% z-ca~Fq5}GEnGes%*Q+L#$%TPCTae2Zw}Vr2is828ozSaxaX~BSM(+0-UtoHShAqQe z!V#fQQEvCwu#3FtK&zx+GGp5dBx@w6T8(jo2ai8SXA6hIR|`g{4_dc|4g;1IvdU z-h}9YID<0jlr!#9iBXg!qQy1In4{^ES&m~RYkqG^-|OkX%)1!Ipia}6i!NmnxF(+I zF*;T9`o(spmAybb^R>M=v;8tUKL9Z4qvYaSW{&jk>ARWm`58>qEiMmTvU{E*NBmb` zS4iv*lt}~c0&(^Z3DfeL%zyDo8Qn5xoB!eq7sWE;A#~x0Q}nVr0*3S+z!)V><@%|G zHN_XN7WEDwZ!l9=Na&`QN|=e~DwvcG=G^$*lvVleWd;g~Ma(71%;M*gty^D;x4pXP zf7AIc(_qihuhpv%UCq4;wmPKT82opqalbd8{X^aap4eS^Rn_YgybIC)rg!0=RHfHr|daTu`1AUiGGXV5)kK|o1kuZ2fDv0ed>7|$(mkUbFGc~^8xkkHTwv?I`Q$F`@m|ufZ7nu(u)l6B7*dN!~;+fk5BeV5Z}}Fw|M< zDr6JA&w-BeHq`Sr80jSt=;rb3dMnIy+Uh1Vn0&=vhc9nDf5`6;{r{Dcuw|<~<-PpR zeuqU^PgCGeDG6*gKbuX@`Vd&ZfQb16h|ER{V6S*UbvFX!T72Q3TMUS^czY|~<9ERJ zw6G8_@$Z9dE#N-$;Tc{V{;y0|aMke-IB4!4C@l<#Ja@~fHWh%{69Y&;Ie?}zKsD?I z;7ti2+Y10`D+lC$ydQ-t0fBE+gbeS?WV{@q*$$!B90%v)(2mbex__kcJFq?wZq~pB z&18JiUas+vlxX%<`(D8NDmZ@(>x;4H-)HFfE99DE`qKV~&o>iY(6qL>cS z+D=UFxxI+Ac=&`UUThA}gj|LZis=p>YS+riZ9S>yum`Q*4Q1Bj8y9pH$PHpIRm^-=9%O;j0Mb5VoT18~=r zSX6)2ZdGPpHzcWl7S%aEg__%aK4s+G9o4j(M>TsaQQW*(N0H`m(nqjoin77@AmPCD zDMX`=DXI=-t9@S8Y(%U$Q$n6HxKAj$bW=}%+Ftd*qY<$(&Yh^KoUCfUb0u_KVk6vI zXC|y&+Yhe&xw*=ndMs=uN`(V%&w_@hPLk7;1b)3#Kc&CgdjNu@Z&0cf^#57%@)t+u!MXzGfr~Sv=vGT|a!m?j zC|OBg9cWA+HwHS8lraS*$ZX z|H1DZ`OhzDApSnn+4rWP7rpa{c{ui=O%Y8vA5;rQ_1~9GtuA43;4HYqVsCvqg4J+Q}p?^@6fp}ne*!d z^i%Y|3bL-8i4Og&xtZYF_&qn%e7#^HGQ0~|vKF0MA=k$5Su=+vaZk7Umnb3xxRv=J zul2qISqkRj_bntqc+#dmYv#E9cA!zEnS&|YZHT{=o9#|#$k>_hUfHKQx!t+sXxer@;crZA{QT5H@+ZTsfJ&Lv)`Rz&#|>N9~VEQlScrm?@aW;f||K+X(n^^U_x_#imUeQqTQ&U3T z)(U3$*pM?V;}Qh zJrpam9EfR8QW@{Xq;jMlFYT>a@~}ez?HQ zoSI4>*pDQswGnf&#^mY@uY;>8ynZOD{oe{de+*`|5c|32nGgOqAyyk2*HJZpJGA^h zc-2#1f0}FlRlw}vcVKoknfngRE~q({o%7AXtn5lgCCp-jh=o~?!db6-S{mZ4rY0Shz&opbJIQg9i*TawzQQ|fGy`HYURK|D+|P2g8KQ6 zT*#T2iO0w(1A?oEw|cuioxmDI-nBx4bqyjmDc$w@()--J_tcw>d?p@WSzQdG4@2$X zPk>s3k3g+{61i?Nx;|{0=_RKd>9y70zHVi5Go3Mj-k2uf-STVI8E(IfLHHrO9sHMg zdo)*$T-6`O+nlvBD&!X+e1mmfk3&^>>m|TQD*G_rc1i-GZYKad`5aIvkwa7AFL(^Q z$ACPLp-ItJX)uvpN4A!6Gr8HYD6zV#uvsPMYD3BbO+9YpS^`8ZZUSns37}pykr8vS zNqJZV$Y433gpW0Kx-)o0#rp^QNB}$~0Aw#LeRYbb2{ftV+H`lWh#Ff%+*}TXH}U&2 z4c@Y~zKMl0Zgx92ZoE!NS;qIv<7d5pdCdK6nc(5x5A8^K%2+!za{3g7Z}eJN=iXs> zP~8J%<#blOZRJ9Q`^Ceb`kRn9dp{+Anlc1UzR?$L$!vobwr#EyUJv%#d#8qa`#p1F zyi;#jFeIhGc>4o&z;g@O@yINAH05%ED#Ql;I<79v%$opTsymV7{nG%wOzfwp#4{PxmL^~2eG zRkL}fiW#z#O6#6xKI2kK6^Gwcs19cjRo-!)qPl;|R^8!MwlL3%knc*!CbuNT5i9^iLC>j4kX~3S9Nqs26mBsgr!@Qq`rVBu>pd8&c5S~JHi_(A(4ea? zdFM)FwbR?i&}oVr-2UPcnsmBJL3?pu5*$~;jsf4GLA$#X))RsYF4y{zay&j38MWQ3 zUSFP$YD}!AHN(TdaIUx6$00cUb3?Fx{cxZ&bNufB@P}rLYr&Uaz7wt0&-MQ5a=OE% z4Ys^uUYTu@Sm#UVd(S0|O(|f4uGEtF9gv9W^@dp%cw}as9mOiyE_=9TQ{ahUCQ8q_1elDru}S0uTL02PYfg&m(kgbo#jhO2UAyR z-Sck#3)cd_Rc~DV-k6RRN9Nx4TW%E6dvl->kQzO71E7taxx!a!r)8U^RruFNS>*SjMEs*}u>~AeV^UHUl!TLGc zUu7Mv=gehAIO|8h3BvM2++JOc;{Ysw$w+>B{9 z)0Zo%hL<9lslCW#2_P^Uqi0Eg@YFY_W;BenFh`8SaN7ibmy@g?*}nm#$|DnVKNS-xq@k_RgcEvklDFvRx~#=TEJ(0 z4=I#W##TVc7G!&k$3IsSpl&IE%23Hr;$lFo;jx)VbhIuTAnUzAn2)!&8z1jW%{W4T z{!v?OM4WwuR}4Rc8=nJmkgT#cJeM^sfA`T{l@V#cuP>=uYTXN5U3u#-j_kcYTa9$@ zQ%#)Yp0!g@msyO$%5WBWEWsU(+Fk>WyDC%6aY`f>som7eDxQ)RnQu{s`!pnNyA7&W zDGIi`3{jkooh)3qXf7Gvt%7LUa(RJlNF(Um;WR8sbRkmlhDuo#=sWw{%EPa zr((ybT;}gP6B@uX@+5ng!)H0@CrsN%*2exw?30 z2z-*(3(n}}Nq(O*1T|TIy5O{EC?z-VfWEWrMs6B66piZo1ldl0iMj@_Q?$44kv}2Y zOEJpuh_Y4OQ$>d<^;Nz1WefL|)e&TO{7u#H?n-s-q%`8UR_BDZ-po?y{FF~uMgDBj44=HJNzF_u^e9ElGB6#_!Lfttk7{0JuL>8w`QeCLKw7{s< z8F)9l6C4`WMSbq}`NGWS2!_YB>qp$&)LR{P}Qhh=&6-8TxR z=foHEUh}<-g}*>z<2#HtJ#0=-xLrql;Z3zOU_OT5|8|XZ{VCI*J68Rr@cVN=5{$#~ z&s`&(S2O=8ehvPGDbb)B)BLSYZYu$->BOS3puBXh*P(CPllYdv#-DrU&CO&JrCCge1*IHLbIH>fgFWsvCD^BE z3odk-DNOHT#MWPDKWCTT+xyJ)eqg+p8afB~o_m(Zj>VczsK^qyc@~~zYpxTswoj(I zBv#@xERN@xfJ6RjY&!kD8tEo_h-#!4lQz;rZ{5Uc1HHs)BfWSv(Cep*#-=)$t^4RP z-Ra1tdNFR}>XMxO5AgZFStI?jrhr*xR2y4)Y~6AWNiS&dmyOq2!Q`qE4R-d`Ob%voQIr({D9c#@H8KY1S~?)s;dZy|0og_XP|c1&n2g(Y z*F?rE5q&c@B(XXsXWDC?I;}-xk!Dgeho#nh&Go)}0QI~X9y>k*IlvfoFrrjuX=4?0dVeo0|Fv9I z|4un+Ry08U&B;mRsYOc)hODT0z_}t52CMrcpLRpY%NbTEMAeq;>32yv#Cn0M?fNLi zb}u`hn#vifeO_6F?WR=W1ldVdNlzzY>5@^Xt++(_%i%Kh9KwLijh+vGIcw#UX?C&T zN3RwIAkeztw@$az8Rwfrc`}7MQnS%WpA84XyWB?&+e5|mhW8XbFJ>wilJ%4pZ(k@9 z4qTP*Dj6!cWtOWLFh7#E=u8Qm1ZdxKOrx`!(Z11(4+vr8(I zy;YKj&(vQl;Bc=eN>4UlUMZ zz0p9ua@Gs=BWe$EqG>2R{{2pJVY(Z+@KSS_d(E$)VvDWnbxB{iBY-GaU)%V9sQfWh zw)mGGC-#4p&aFjdo$=lq$^ME^*`ok3_4apXLfelOXC7}#U+kYnmzNJ<4g{t8`~P}M z{Bp2}u4_rqH@yt$qU>NgrW5xRYKGpJah&*!Cn?Rw<-otxK_xYsV9>SiPUl+ERxZvtT=i z)w@G2nE6X~{tuP^m)`v$RQ{w$^}qEvvHzAKYpv-du$mF;GkwS=b`CQ;7uqBFzyqRbsggtGe8Y0Vx*uAy{oH0B%8$E4q_wkU;O=hFkTGNRw4n&>>T$M7`bYknFvvZ@Xo9XmvsH~s%ZKRX# zt&JAzBCnYq`kLvr)2+Ov8<{o*e|{tw0Pn8^26V>Ltsf3PfbrF;vRAc2<&)X=P?S>@ zW!^nkPPx7U=&s@a1$%z}2(TuNf{} zsEO*5Y(jP?2@oxxU{8$-0kDP~P_?`PTwMfUSO_4y$~n9wOf-)ZBY%?t;a<(-xU@C7 zI26s%)a7DpkaLOG{8PONjK?Ha)8std_n19EFE0Sm>|+^}2gc}r<{0hqF=7=CCGmhV za0O%&Ie>Ep;C>jH#S%ay;xX>(Jkc8;D~rR#I=mn3*hRd{z=N*?4){7-GEGuA7fI&p zm9xmrM%TH|dEDlr%>P+bo}~T9I5u=-)!?R)VeD>JuC0svC&Wf11dqg4jmBo<(1?VDxP&T$B_cAIv-RIS va_hwK&=~w>#wGGjhWDRaGkmm(_&yp`c_kV}8%2X?lS%&%Cj1$&e4GFPbo<-0 literal 0 HcmV?d00001 diff --git a/cpp/src/utilities/work_unit_predictor.cpp b/cpp/src/utilities/work_unit_predictor.cpp new file mode 100644 index 0000000000..56c04a9a54 --- /dev/null +++ b/cpp/src/utilities/work_unit_predictor.cpp @@ -0,0 +1,184 @@ +/* + * 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 "work_unit_predictor.hpp" + +#include +#include +#include +#include +#include + +#include + +#include "models_ubj.h" + +#define safe_xgboost(call) \ + { \ + int err = (call); \ + if (err != 0) { \ + throw std::runtime_error(std::string(__FILE__) + ":" + std::to_string(__LINE__) + \ + ": error in " + #call + ":" + XGBGetLastError()); \ + } \ + } + +namespace cuopt { + +template +static inline uint32_t compute_hash(std::vector h_contents) +{ + // FNV-1a hash + + uint32_t hash = 2166136261u; // FNV-1a 32-bit offset basis + std::vector byte_contents(h_contents.size() * sizeof(i_t)); + std::memcpy(byte_contents.data(), h_contents.data(), h_contents.size() * sizeof(i_t)); + for (size_t i = 0; i < byte_contents.size(); ++i) { + hash ^= byte_contents[i]; + hash *= 16777619u; + } + return hash; +} + +work_unit_predictor_t::work_unit_predictor_t(const std::string& model_name) : model_name(model_name) +{ + BoosterHandle booster_handle; + int ret = XGBoosterCreate(nullptr, 0, &booster_handle); + safe_xgboost(ret); + assert(ret == 0); + if (ret != 0) return; + raw_handle = reinterpret_cast(booster_handle); + + // load the embedded model from the .rodata section + const unsigned char* model_data = nullptr; + unsigned int model_len = 0; + for (unsigned int i = 0; i < xgboost_models_count; i++) { + if (strcmp(xgboost_models[i].name, model_name.c_str()) == 0) { + model_data = xgboost_models[i].data; + model_len = xgboost_models[i].length; + break; + } + } + + assert(model_data != nullptr); + assert(model_len > 0); + + ret = XGBoosterLoadModelFromBuffer(booster_handle, model_data, model_len); + safe_xgboost(ret); + assert(ret == 0); + if (ret != 0) return; + + XGBoosterSetParam(booster_handle, "predictor", "gpu_predictor"); + + is_valid = true; +} + +work_unit_predictor_t::~work_unit_predictor_t() +{ + if (raw_handle != nullptr) { + BoosterHandle booster_handle = reinterpret_cast(raw_handle); + XGBoosterFree(booster_handle); + raw_handle = nullptr; + } +} + +float work_unit_predictor_t::predict_scalar(const std::vector& features, bool verbose) const +{ + assert(is_valid && raw_handle != nullptr); + if (!is_valid || raw_handle == nullptr) return std::numeric_limits::signaling_NaN(); + + // Check cache first + uint32_t hash = compute_hash(features); + auto it = prediction_cache.find(hash); + if (it != prediction_cache.end()) { return it->second; } + + // Timer: measure elapsed time for prediction + auto t_start = std::chrono::high_resolution_clock::now(); + + // Create DMatrix from feature vector + DMatrixHandle dmatrix; + int ret = XGDMatrixCreateFromMat(features.data(), + 1, // nrow + features.size(), // ncol + std::numeric_limits::quiet_NaN(), // missing value + &dmatrix); + safe_xgboost(ret); + + // Predict from DMatrix + char const config[] = + "{\"type\": 0, \"iteration_begin\": 0, " + "\"iteration_end\": 0, \"strict_shape\": true, \"training\": false}"; + + const bst_ulong* out_shape = nullptr; + bst_ulong out_dim = 0; + const float* out_result = nullptr; + ret = XGBoosterPredictFromDMatrix(reinterpret_cast(raw_handle), + dmatrix, + config, + &out_shape, + &out_dim, + &out_result); + safe_xgboost(ret); + + float prediction = out_result[0]; + + // Free DMatrix + XGDMatrixFree(dmatrix); + + auto t_end = std::chrono::high_resolution_clock::now(); + double elapsed_ms = std::chrono::duration(t_end - t_start).count(); + printf("[work_unit_predictor_t::predict_scalar] Prediction took %.3f ms\n", elapsed_ms); + + // Store in cache + prediction_cache[hash] = prediction; + + return prediction; +} + +float work_unit_predictor_t::predict_scalar(const std::map& feature_map, + bool verbose) const +{ + // Extract features in the expected order for the model + // Order matches training data: [target_time, n_of_minimums_for_exit, n_variables, n_constraints, + // nnz, sparsity, nnz_stddev, unbalancedness] + std::vector features; + features.reserve(feature_map.size()); + + // Add features in the order expected by the model + // This order should match what was used during training + const std::vector feature_order = {"target_time", + "n_of_minimums_for_exit", + "n_variables", + "n_constraints", + "nnz", + "sparsity", + "nnz_stddev", + "unbalancedness"}; + + for (const auto& name : feature_order) { + auto it = feature_map.find(name); + if (it != feature_map.end()) { + features.push_back(it->second); + } else { + // Feature not found - use default value of 0 + features.push_back(0.0f); + } + } + + return predict_scalar(features, verbose); +} + +} // namespace cuopt diff --git a/cpp/src/utilities/work_unit_predictor.hpp b/cpp/src/utilities/work_unit_predictor.hpp new file mode 100644 index 0000000000..420fc9547c --- /dev/null +++ b/cpp/src/utilities/work_unit_predictor.hpp @@ -0,0 +1,41 @@ +/* + * 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 +#include +#include +#include +#include + +namespace cuopt { + +class work_unit_predictor_t { + public: + work_unit_predictor_t(const std::string& model_name); + ~work_unit_predictor_t(); + float predict_scalar(const std::vector& features, bool verbose = false) const; + float predict_scalar(const std::map& features, bool verbose = false) const; + + private: + std::string model_name; + void* raw_handle{nullptr}; // void* to avoid including xgboost in every MIP translation unit + bool is_valid{false}; + mutable std::unordered_map prediction_cache; +}; + +} // namespace cuopt diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index 5efc510f63..ad2bf4ba17 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -66,6 +66,12 @@ static fj_state_t run_fj_instance(std::string test_instance, std::cout << "Running: " << test_instance << std::endl; auto path = cuopt::test::get_rapids_dataset_root_dir() + ("/mip/" + test_instance); + + if (std::getenv("CUOPT_INSTANCE")) { + path = std::string("/home/scratch.yboucher_gpu_1/collection/") + std::getenv("CUOPT_INSTANCE"); + std::cout << "Using instance from CUOPT_INSTANCE: " << path << std::endl; + } + cuopt::mps_parser::mps_data_model_t mps_problem = cuopt::mps_parser::parse_mps(path, false); handle_.sync_stream(); @@ -87,7 +93,7 @@ static bool run_fj_check_no_obj_runoff(std::string test_instance) detail::fj_settings_t fj_settings; fj_settings.time_limit = 30.; fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; - fj_settings.n_of_minimums_for_exit = 20000 * 1000; + fj_settings.n_of_minimums_for_exit = 5000; fj_settings.update_weights = true; fj_settings.feasibility_run = false; fj_settings.iteration_limit = 20000; @@ -109,7 +115,7 @@ static bool run_fj_check_objective(std::string test_instance, int iter_limit, do detail::fj_settings_t fj_settings; fj_settings.time_limit = 30.; fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; - fj_settings.n_of_minimums_for_exit = 20000 * 1000; + fj_settings.n_of_minimums_for_exit = 5000; fj_settings.update_weights = true; fj_settings.feasibility_run = obj_target == +std::numeric_limits::infinity(); fj_settings.iteration_limit = iter_limit; @@ -136,7 +142,7 @@ static bool run_fj_check_feasible(std::string test_instance) detail::fj_settings_t fj_settings; fj_settings.time_limit = 30.; fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; - fj_settings.n_of_minimums_for_exit = 20000 * 1000; + fj_settings.n_of_minimums_for_exit = 5000; fj_settings.update_weights = true; fj_settings.feasibility_run = false; fj_settings.iteration_limit = 25000; @@ -170,19 +176,16 @@ static bool run_fj_check_feasible(std::string test_instance) static bool run_fj_check_determinism(std::string test_instance, int iter_limit) { - int seed = - std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); - detail::fj_settings_t fj_settings; fj_settings.time_limit = std::numeric_limits::max(); fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; - fj_settings.n_of_minimums_for_exit = 20000 * 1000; + fj_settings.n_of_minimums_for_exit = 5000; + fj_settings.work_unit_limit = 0.15; // run for 0.5wu (~0.5s) fj_settings.update_weights = true; fj_settings.feasibility_run = false; - fj_settings.iteration_limit = iter_limit; - fj_settings.load_balancing_mode = detail::fj_load_balancing_mode_t::ALWAYS_ON; - fj_settings.seed = seed; - cuopt::seed_generator::set_seed(fj_settings.seed); + // fj_settings.iteration_limit = iter_limit; + fj_settings.load_balancing_mode = detail::fj_load_balancing_mode_t::ALWAYS_ON; + fj_settings.seed = cuopt::seed_generator::get_seed(); auto state = run_fj_instance(test_instance, fj_settings); auto& solution = state.solution; @@ -265,6 +268,9 @@ static bool run_fj_check_determinism(std::string test_instance, int iter_limit) TEST(mip_solve, feasibility_jump_determinism) { + int seed = + std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); + for (const auto& instance : {"thor50dday.mps", "gen-ip054.mps", "50v-10.mps", @@ -275,6 +281,7 @@ TEST(mip_solve, feasibility_jump_determinism) "uccase9.mps"}) { for (int i = 0; i < 10; i++) { // while (true) { + cuopt::seed_generator::set_seed(seed); run_fj_check_determinism(instance, 1000); } } From f6e1370818e0d220bfeb6740d36501779b094946 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 29 Oct 2025 17:57:21 +0000 Subject: [PATCH 014/225] log more FJ features --- .../linear_programming/cuopt/run_mip.cpp | 3 ++ cpp/src/mip/diversity/diversity_manager.cu | 48 ++++++++++++++----- cpp/src/mip/relaxed_lp/relaxed_lp.cu | 10 ++++ 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 5c57812454..98cf2bd137 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -214,6 +214,9 @@ int run_single_file(std::string file_path, settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; settings.presolve = true; + + settings.heuristics_only = true; + cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index d1ebde3b9f..5e9ef77ebd 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -25,6 +25,8 @@ #include "cuda_profiler_api.h" +#include + namespace cuopt::linear_programming::detail { size_t fp_recombiner_config_t::max_n_of_vars_from_other = @@ -265,19 +267,36 @@ template void diversity_manager_t::run_fj_alone(solution_t& solution) { CUOPT_LOG_INFO("Running FJ alone!"); - solution.round_nearest(); - ls.fj.settings = fj_settings_t{}; - ls.fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; - ls.fj.settings.n_of_minimums_for_exit = 20000 * 1000; - ls.fj.settings.update_weights = true; - ls.fj.settings.feasibility_run = false; - ls.fj.settings.time_limit = timer.remaining_time(); - if (context.settings.deterministic) { - ls.fj.settings.time_limit = timer.remaining_time(); - ls.fj.settings.iteration_limit = 10000; + // Benchmark FJ with 1000 different random starting solutions and varying iteration limits + CUOPT_LOG_INFO("Starting FJ benchmark: 1000 runs with random starting solutions"); + + std::mt19937 rng(cuopt::seed_generator::get_seed()); + std::uniform_int_distribution iter_dist(100, 50000); + + for (i_t run = 0; run < 1000; ++run) { + // Generate random starting solution within bounds + solution.assign_random_within_bounds(1.0, false); + solution.round_nearest(); + + // Configure FJ settings with random iteration limit + ls.fj.settings = fj_settings_t{}; + ls.fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; + ls.fj.settings.n_of_minimums_for_exit = 20000 * 1000; + ls.fj.settings.update_weights = true; + ls.fj.settings.feasibility_run = false; + ls.fj.settings.iteration_limit = iter_dist(rng); + ls.fj.settings.time_limit = std::numeric_limits::infinity(); + + if (context.settings.deterministic) { ls.fj.settings.iteration_limit = iter_dist(rng); } + + CUOPT_LOG_INFO( + "FJ benchmark run %d/%d: iteration_limit=%d", run + 1, 1000, ls.fj.settings.iteration_limit); + + ls.fj.solve(solution); } - ls.fj.solve(solution); - CUOPT_LOG_INFO("FJ alone finished!"); + + CUOPT_LOG_INFO("FJ benchmark finished: 1000 runs completed"); + exit(0); } // returns the best feasible solution @@ -302,6 +321,9 @@ template solution_t diversity_manager_t::run_solver() { raft::common::nvtx::range fun_scope("run_solver"); + + diversity_config.fj_only_run = true; + population.timer = timer; const f_t time_limit = timer.remaining_time(); const f_t lp_time_limit = @@ -351,7 +373,7 @@ solution_t diversity_manager_t::run_solver() bool bb_thread_solution_exists = simplex_solution_exists.load(); if (bb_thread_solution_exists) { ls.lp_optimal_exists = true; - } else if (!diversity_config.fj_only_run) { + } else if (!diversity_config.fj_only_run || true) { relaxed_lp_settings_t lp_settings; lp_settings.time_limit = lp_time_limit; lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index a705e3c223..1f27ede021 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -23,6 +23,8 @@ #include #include +#include + #include #include @@ -50,6 +52,8 @@ optimization_problem_solution_t get_relaxed_lp_solution( const relaxed_lp_settings_t& settings) { raft::common::nvtx::range fun_scope("get_relaxed_lp_solution"); + auto function_start_time = std::chrono::high_resolution_clock::now(); + pdlp_solver_settings_t pdlp_settings{}; pdlp_settings.detect_infeasibility = settings.check_infeasibility; pdlp_settings.set_optimality_tolerance(settings.tolerance); @@ -124,6 +128,12 @@ optimization_problem_solution_t get_relaxed_lp_solution( solver_response.get_additional_termination_information().number_of_steps_taken); } + auto function_end_time = std::chrono::high_resolution_clock::now(); + auto elapsed_ms = + std::chrono::duration_cast(function_end_time - function_start_time) + .count(); + CUOPT_LOG_DEBUG("get_relaxed_lp_solution took %lld ms", elapsed_ms); + return solver_response; } From f02837a2b3d16a26d66bbedbec14dafb35307685 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 29 Oct 2025 11:51:53 -0700 Subject: [PATCH 015/225] remove nvperf dep --- cpp/tests/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 0d3ff71160..ed3c1344f0 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -74,8 +74,8 @@ function(cuopt_add_cupti_dep dep_name) ) endfunction() -cuopt_add_cupti_dep(nvperf_target) -cuopt_add_cupti_dep(nvperf_host) +#cuopt_add_cupti_dep(nvperf_target) +#cuopt_add_cupti_dep(nvperf_host) cuopt_add_cupti_dep(cupti) # ################################################################ ------------------------------------------------------------------ From 773dbd44a85d7f3a34425971db2c0dba3ad4cd48 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 29 Oct 2025 12:03:58 -0700 Subject: [PATCH 016/225] no cupti --- cpp/tests/CMakeLists.txt | 4 ++-- cpp/tests/utilities/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index ed3c1344f0..a63518dcb8 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -76,7 +76,7 @@ endfunction() #cuopt_add_cupti_dep(nvperf_target) #cuopt_add_cupti_dep(nvperf_host) -cuopt_add_cupti_dep(cupti) +#cuopt_add_cupti_dep(cupti) # ################################################################ ------------------------------------------------------------------ function(ConfigureTest CMAKE_TEST_NAME) @@ -104,7 +104,7 @@ function(ConfigureTest CMAKE_TEST_NAME) target_link_libraries(${CMAKE_TEST_NAME} PRIVATE #cuopt::nvperf_target #cuopt::nvperf_host - cuopt::cupti + #cuopt::cupti cuda ) target_include_directories(${CMAKE_TEST_NAME} PRIVATE diff --git a/cpp/tests/utilities/CMakeLists.txt b/cpp/tests/utilities/CMakeLists.txt index 7a40ee6e77..a2bb0eb09a 100644 --- a/cpp/tests/utilities/CMakeLists.txt +++ b/cpp/tests/utilities/CMakeLists.txt @@ -15,4 +15,4 @@ # Add CLI end-to-end test ConfigureTest(CLI_TEST test_cli.cpp) -ConfigureTest(CUPTI_TEST test_cupti.cu) +#ConfigureTest(CUPTI_TEST test_cupti.cu) From 327fcecf674f7bbfb1b7aa7e72a57dd44c211e57 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 30 Oct 2025 12:47:40 +0000 Subject: [PATCH 017/225] added regression scripts --- scripts/README_REGRESSION.md | 279 ++++++++ scripts/determinism_logs_parse.py | 125 ++++ scripts/requirements.txt | 10 + scripts/train_regressor.py | 1021 +++++++++++++++++++++++++++++ 4 files changed, 1435 insertions(+) create mode 100644 scripts/README_REGRESSION.md create mode 100755 scripts/determinism_logs_parse.py create mode 100644 scripts/requirements.txt create mode 100755 scripts/train_regressor.py diff --git a/scripts/README_REGRESSION.md b/scripts/README_REGRESSION.md new file mode 100644 index 0000000000..ab8736aa47 --- /dev/null +++ b/scripts/README_REGRESSION.md @@ -0,0 +1,279 @@ +# Regression Model Training Scripts + +This directory contains scripts for parsing algorithm log files and training regression models to predict iteration counts. + +## Overview + +The workflow consists of two steps: +1. **Parse log files** → Extract key-value pairs into a pickle file +2. **Train models** → Learn to predict iterations from features + +## Installation + +Install required dependencies: + +```bash +pip install -r requirements.txt +``` + +## Usage + +### Step 1: Parse Log Files + +Extract features from log files containing `FJ:` entries: + +```bash +python determinism_logs_parse.py /path/to/logs/directory -o parsed_data.pkl +``` + +**Arguments:** +- `input_dir`: Directory containing `.log` files +- `-o, --output`: Output pickle file (default: `output.pkl`) + +**Output:** +- Pickle file containing list of dictionaries with all key-value pairs +- Each entry includes `file=` field + +### Step 2: Train Regression Model + +Train a model to predict `iter` values from other features: + +```bash +python train_regressor.py parsed_data.pkl --regressor xgboost --seed 42 +``` + +**Arguments:** +- `input_pkl`: Input pickle file from step 1 +- `--regressor, -r`: Type of regressor (required) + - `linear` - Linear Regression + - `poly2`, `poly3`, `poly4` - Polynomial Regression (degree 2, 3, 4) + - `xgboost` - XGBoost Regressor + - `lightgbm` - LightGBM Regressor + - `random_forest` - Random Forest Regressor + - `gradient_boosting` - Gradient Boosting Regressor +- `--output-dir, -o`: Directory to save models (default: `./models`) +- `--seed, -s`: Random seed for reproducibility (optional) +- `--tune`: Enable hyperparameter tuning +- `--cv-folds`: Number of cross-validation folds (default: 5) +- `--test-size`: Test set proportion (default: 0.2) +- `--no-progress`: Disable training progress output +- `--list-features`: List all available features in the dataset and exit +- `--stratify-split`: Stratify train/test split by target distribution +- `--early-stopping N`: Early stopping patience in rounds (default: 20, use 0 to disable) +- `--treelite-compile N`: Export XGBoost/LightGBM as optimized C source code with TL2cgen (N threads, default: 1, includes branch annotation and quantization) + +## Features + +### Data Splitting +- **File-based split**: Ensures entries from the same file go exclusively to train OR test +- **Prevents data leakage**: Improves generalization and reduces overfitting +- **Default**: 20% of files for testing + +### Preprocessing +- **Automatic scaling**: Applied to linear/polynomial models (not tree-based) +- **Polynomial features**: All numeric features expanded for polynomial regression +- **Clean data assumption**: Script expects valid pickle data + +### Feature Selection +- **Manual feature selection**: Edit `FEATURES_TO_EXCLUDE` or `FEATURES_TO_INCLUDE_ONLY` directly in the script +- **Exclude specific features**: Add feature names to `FEATURES_TO_EXCLUDE` list +- **Include only specific features**: Add feature names to `FEATURES_TO_INCLUDE_ONLY` list (overrides exclusion) +- **List available features**: Run with `--list-features` to see all features in your dataset +- **No command-line config**: Intentionally not exposed as CLI args for cleaner configuration file management + +### Model Evaluation +- **Cross-validation**: K-fold CV on training set with progress output +- **Comprehensive metrics**: MSE, RMSE, MAE, R² +- **Feature importance**: All features ranked by importance (top 50 for polynomial models) +- **Sample predictions**: 20 random test predictions with errors + +### Training Progress +- **Progress indicators**: Tree-based models (XGBoost, LightGBM, Random Forest, Gradient Boosting) show real-time training progress +- **Polynomial feature tracking**: Shows number of polynomial features being generated +- **CV progress**: Cross-validation shows progress for each fold +- **Disable option**: Use `--no-progress` flag to suppress all progress output + +### Overfitting Prevention +- **Early stopping**: Enabled by default for XGBoost and LightGBM (20 rounds patience) to prevent overfitting +- **Regularization**: XGBoost and LightGBM include L1/L2 regularization, subsampling, and minimum child weight +- **Stratified splitting**: Use `--stratify-split` to ensure balanced target distributions +- **Disable early stopping**: Use `--early-stopping 0` if you want full training without early stopping + +### Model Persistence +- **XGBoost**: Saved as `.ubj.gz` (UBJ format with gzip compression) +- **LightGBM**: Saved as `.txt` (text format, human-readable) +- **Sklearn models**: Saved as `.joblib` (efficient for numpy arrays) +- **Metadata**: Feature names and preprocessing info saved separately +- **Scaler**: Saved for models requiring normalization + +### TL2cgen Source Export (Optional) +- **C source code export**: Export XGBoost/LightGBM models as optimized C source code using TL2cgen +- **Portable and fast**: Compile the source on any platform for 10-100x faster predictions +- **Enabled by default**: Automatically exports C source with 1 thread (use `--treelite-compile N` for more threads) +- **Requires**: `treelite>=4.0` and `tl2cgen` packages (optional dependencies) +- **Output**: Optimized C source files in dedicated directory +- **Note**: Treelite 4.0+ moved C compilation to TL2cgen ([migration guide](https://tl2cgen.readthedocs.io/en/latest/treelite-migration.html)) + +### Built-in TL2cgen Optimizations +The following optimizations are **automatically applied** when using TL2cgen: + +- **Branch annotation**: + - Analyzes which branches are taken during training + - Optimizes C code with branch prediction hints + - Improves inference speed by 10-30% + - Saves annotation file for inspection +- **Quantization**: + - Reduces model memory footprint by ~75% + - Uses 8-bit integers instead of 32-bit floats where possible + - Minimal accuracy loss (typically <0.1% R²) + - Faster inference on memory-constrained systems + +**Combined effect**: 1.2-1.5x faster inference with 75% less memory + +## Example Workflow + +```bash +# 1. Parse logs +python determinism_logs_parse.py /data/algorithm_logs -o data.pkl + +# 2. List available features +python train_regressor.py data.pkl --regressor xgboost --list-features + +# 3. Train XGBoost model +python train_regressor.py data.pkl --regressor xgboost --seed 42 -o ./models + +# 4. Train polynomial model with tuning +python train_regressor.py data.pkl --regressor poly3 --tune --seed 42 + +# 5. Compare different models +for model in linear poly2 poly3 xgboost lightgbm random_forest gradient_boosting; do + echo "Training $model..." + python train_regressor.py data.pkl --regressor $model --seed 42 +done + +# 6. Train LightGBM model +python train_regressor.py data.pkl --regressor lightgbm --seed 42 + +# 7. Export XGBoost model as C source for production deployment (enabled by default) +python train_regressor.py data.pkl --regressor xgboost --seed 42 + +# Or specify more threads for compilation +python train_regressor.py data.pkl --regressor xgboost --treelite-compile 8 --seed 42 +``` + +## TL2cgen Source Export Example + +For production deployments requiring fast inference, models are **automatically exported as optimized C source code** (if `treelite` and `tl2cgen` are installed): + +```bash +# Install treelite and tl2cgen (optional) +pip install treelite tl2cgen + +# Train model - optimized C source is automatically exported with branch annotation and quantization +python train_regressor.py data.pkl --regressor xgboost -o ./models + +# Use more threads for faster parallel compilation +python train_regressor.py data.pkl --regressor xgboost --treelite-compile 8 -o ./models + +# The C source files will be in: models/xgboost_c_code/ +# Contains optimized C source code with branch annotation and quantization ready for compilation + +# Same process works for LightGBM +python train_regressor.py data.pkl --regressor lightgbm --treelite-compile 8 -o ./models +# Output: models/lightgbm_c_code/ +``` + +### Optimization Impact + +All TL2cgen exports include both branch annotation and quantization automatically: + +| Configuration | Speed | Memory | Accuracy | +|---------------|-------|--------|----------| +| Standard XGBoost/LightGBM | 1x | 100% | 100% | +| **TL2cgen optimized (default)** | **1.2-1.5x** | **25%** | **>99.9%** | + +**Note:** Treelite 4.0+ moved C code generation to TL2cgen. See the [migration guide](https://tl2cgen.readthedocs.io/en/latest/treelite-migration.html) for details. + +### Output Files + +When TL2cgen is enabled, the following files are automatically created: + +``` +models/ +├── xgboost_model.ubj.gz # Standard XGBoost model +├── xgboost_metadata.pkl # Feature names and config +├── xgboost_annotation.json # Branch statistics (automatic) +└── xgboost_c_code/ # TL2cgen generated optimized C++ source + ├── header.h # Header with feature names declaration + ├── main.cpp # Implementation with feature names array + └── *.cpp / *.h # Other C++ source files (quantized + annotated) +``` + +**Namespace Wrapping**: All generated files are automatically wrapped in a C++ namespace with the model name (derived from the input pickle file basename) to avoid naming conflicts when using multiple models in the same project. For example, if the input is `my_dataset.pkl`: +- All functions are in `namespace my_dataset { ... }` +- Access functions as `my_dataset::predict()`, `my_dataset::get_num_features()`, etc. +- All `.c` files are renamed to `.cpp` for C++ compilation + +The generated `header.h` includes: +- `namespace { ... }` wrapping all declarations +- `#define NUM_FEATURES ` - Number of features +- `extern const char* feature_names[]` - Feature names declaration +- Function declarations (e.g., `predict()`, `get_num_features()`) within the namespace + +The generated `main.cpp` includes: +- `namespace { ... }` wrapping all implementations +- `const char* feature_names[]` - Feature names array definition +- Function implementations within the namespace + +## Feature Selection Examples + +To perform feature selection, edit the configuration section at the top of `train_regressor.py`: + +### Example 1: Exclude specific features + +```python +FEATURES_TO_EXCLUDE = [ + 'time', # Exclude time as it may not be available at prediction time + 'avg_constraint_range', + 'binary_ratio', +] + +FEATURES_TO_INCLUDE_ONLY = [] +``` + +### Example 2: Use only specific features + +```python +FEATURES_TO_EXCLUDE = [] + +FEATURES_TO_INCLUDE_ONLY = [ + 'n_variables', + 'n_constraints', + 'sparsity', + 'structural_complexity', +] +``` + +**Note:** If `FEATURES_TO_INCLUDE_ONLY` is non-empty, it overrides `FEATURES_TO_EXCLUDE`. + +## Output Structure + +After training, the output directory contains: + +``` +models/ +├── xgboost_model.ubj.gz # Compressed XGBoost model +├── xgboost_metadata.pkl # Feature names and config +├── linear_model.joblib # Linear regression model +├── linear_scaler.pkl # StandardScaler for linear model +├── linear_metadata.pkl # Metadata +└── ... +``` + +## Notes + +- The train/test split is based on **unique files**, not individual entries +- Models requiring scaling (linear, polynomial) automatically apply `StandardScaler` +- Tree-based models (XGBoost, Random Forest, Gradient Boosting) don't use scaling +- Feature importance shows the most predictive features for iteration count +- Use `--seed` for reproducible results across runs diff --git a/scripts/determinism_logs_parse.py b/scripts/determinism_logs_parse.py new file mode 100755 index 0000000000..ff0ff854be --- /dev/null +++ b/scripts/determinism_logs_parse.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +# 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. + +""" +Parse log files containing key-value pairs and export to pickle format. + +Usage: + python determinism_logs_parse.py [-o output.pkl] +""" + +import argparse +import pickle +import subprocess +import os +import glob + + +def parse_value(value_str): + """Convert string value to appropriate type (int, float, or str).""" + try: + # Try to parse as float if it contains a decimal point or scientific notation + if '.' in value_str or 'e' in value_str.lower(): + return float(value_str) + else: + return int(value_str) + except ValueError: + # Keep as string if conversion fails + return value_str + + +def main(): + parser = argparse.ArgumentParser( + description='Parse log files with key-value pairs and export to pickle' + ) + parser.add_argument( + 'input_dir', + help='Directory containing .log files to parse' + ) + parser.add_argument( + '-o', '--output', + default='output.pkl', + help='Output pickle file path (default: output.pkl)' + ) + args = parser.parse_args() + + # Find all .log files in the input directory + log_files = glob.glob(os.path.join(args.input_dir, '*.log')) + + if not log_files: + print(f"No .log files found in {args.input_dir}") + return + + print(f"Found {len(log_files)} log files") + + # Use grep to efficiently extract all lines containing "FJ:" + # -H flag ensures filename is included in output (even with single file) + # -h suppresses filename (we don't want that) + cmd = ['grep', '-H', 'FJ:'] + log_files + result = subprocess.run(cmd, capture_output=True, text=True) + + if result.returncode != 0 and result.returncode != 1: + # grep returns 1 if no matches found, which is fine + # Other return codes indicate actual errors + print(f"Error running grep: {result.stderr}") + return + + # Parse grep output + entries = [] + for line in result.stdout.strip().split('\n'): + if not line: + continue + + # Grep output format: filename:FJ: key1=value1 key2=value2 ... + # Split on first colon to separate filename from content + colon_idx = line.find(':') + if colon_idx == -1: + continue + + filename = os.path.basename(line[:colon_idx]) + rest = line[colon_idx + 1:] + + # Remove "FJ:" prefix if present + if rest.startswith('FJ:'): + rest = rest[3:].strip() + + # Parse key-value pairs + entry = {'file': filename} + for kv_pair in rest.split(): + if '=' in kv_pair: + key, value = kv_pair.split('=', 1) + entry[key] = parse_value(value) + + # Only add entry if it has more than just the filename + if len(entry) > 1: + entries.append(entry) + + # Calculate statistics + unique_files = set(entry['file'] for entry in entries) + avg_entries_per_file = len(entries) / len(unique_files) if unique_files else 0 + + # Save to pickle file + with open(args.output, 'wb') as f: + pickle.dump(entries, f) + + print(f"Parsed {len(entries)} entries from {len(unique_files)} log files") + print(f"Average entries per file: {avg_entries_per_file:.2f}") + print(f"Saved to {args.output}") + + +if __name__ == '__main__': + main() diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 0000000000..bcce7a7882 --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1,10 @@ +numpy>=1.20.0 +pandas>=1.3.0 +scikit-learn>=1.0.0 +xgboost>=1.5.0 +lightgbm>=3.0.0 +joblib>=1.0.0 + +# Optional: For exporting models to C source code +# treelite>=4.0.0 +# tl2cgen>=0.1.0 diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py new file mode 100755 index 0000000000..84c32ae3d6 --- /dev/null +++ b/scripts/train_regressor.py @@ -0,0 +1,1021 @@ +#!/usr/bin/env python3 +# 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. + +""" +Train regression models to predict algorithm iterations from log features. + +Usage: + python train_regressor.py --regressor [options] +""" + +import argparse +import pickle +import numpy as np +import pandas as pd +from sklearn.model_selection import train_test_split, cross_val_score +from sklearn.preprocessing import StandardScaler, PolynomialFeatures +from sklearn.linear_model import LinearRegression, Ridge +from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor +from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score +from sklearn.pipeline import Pipeline +import joblib +import os +from typing import List, Dict, Any, Tuple +import warnings +warnings.filterwarnings('ignore', category=UserWarning) + + +AVAILABLE_REGRESSORS = [ + 'linear', + 'poly2', 'poly3', 'poly4', + 'xgboost', + 'lightgbm', + 'random_forest', + 'gradient_boosting' +] + +# ============================================================================ +# FEATURE SELECTION CONFIGURATION +# Edit this list to exclude specific features from training +# Leave empty to use all features (except 'file' and 'iter') +# ============================================================================ +FEATURES_TO_EXCLUDE = [ + # Example usage (uncomment to exclude): + # 'time', + # 'avg_constraint_range', + # 'binary_ratio', + 'avg_obj_coeff_magnitude', + 'n_of_minimums_for_exit', + 'feasibility_run', + 'fixed_var_ratio', + 'unbounded_var_ratio', + 'obj_var_ratio', + 'avg_related_vars_per_var', + 'avg_constraint_range', + 'nnz_variance', + 'avg_variable_range', + 'min_nnz_per_row', + 'constraint_var_ratio', + 'avg_var_degree', + 'equality_ratio', + 'integer_ratio', + 'binary_ratio' +] + +# Alternatively, specify ONLY the features you want to use +# If non-empty, only these features will be used (overrides FEATURES_TO_EXCLUDE) +FEATURES_TO_INCLUDE_ONLY = [ + # Example usage (uncomment to use only specific features): + # 'n_variables', + # 'n_constraints', + # 'sparsity', +] +# ============================================================================ + + +def load_data(pkl_path: str) -> pd.DataFrame: + """Load pickle file and convert to DataFrame.""" + with open(pkl_path, 'rb') as f: + data = pickle.load(f) + + if not isinstance(data, list): + raise ValueError(f"Expected list of dictionaries, got {type(data)}") + + if len(data) == 0: + raise ValueError("Empty dataset") + + df = pd.DataFrame(data) + + # Validate required columns + if 'file' not in df.columns: + raise ValueError("Missing required 'file' column in data") + if 'iter' not in df.columns: + raise ValueError("Missing required 'iter' column in data") + + return df + + +def split_by_files(df: pd.DataFrame, test_size: float = 0.2, + random_state: int = None, stratify_by: str = None) -> Tuple[pd.DataFrame, pd.DataFrame]: + """ + Split data into train/test sets based on unique files. + Ensures all entries from a file go to either train or test, not both. + + Args: + ---- + stratify_by: Optional column name to stratify split (e.g., 'iter' for balanced target distribution) + """ + unique_files = df['file'].unique() + + # Optionally stratify by target distribution + if stratify_by: + # Create stratification labels based on quantiles of the specified column + file_stats = df.groupby('file')[stratify_by].median() + stratify_labels = pd.qcut(file_stats, q=min(5, len(unique_files)), labels=False, duplicates='drop') + train_files, test_files = train_test_split( + unique_files, + test_size=test_size, + random_state=random_state, + stratify=stratify_labels + ) + else: + train_files, test_files = train_test_split( + unique_files, + test_size=test_size, + random_state=random_state + ) + + train_df = df[df['file'].isin(train_files)].copy() + test_df = df[df['file'].isin(test_files)].copy() + + print(f"\nData Split:") + print(f" Total entries: {len(df)}") + print(f" Train entries: {len(train_df)} ({len(train_files)} files)") + print(f" Test entries: {len(test_df)} ({len(test_files)} files)") + + # Check distribution similarity + train_target_mean = train_df['iter'].mean() + test_target_mean = test_df['iter'].mean() + train_target_std = train_df['iter'].std() + test_target_std = test_df['iter'].std() + + print(f"\nTarget ('iter') Distribution:") + print(f" Train: mean={train_target_mean:.2f}, std={train_target_std:.2f}") + print(f" Test: mean={test_target_mean:.2f}, std={test_target_std:.2f}") + + mean_diff_pct = abs(train_target_mean - test_target_mean) / train_target_mean * 100 + if mean_diff_pct > 10: + print(f" ⚠️ Warning: Train/test target means differ by {mean_diff_pct:.1f}%") + print(f" Consider using stratified split or different random seed") + + return train_df, test_df + + +def list_available_features(df: pd.DataFrame) -> List[str]: + """ + List all available numeric features in the dataset. + Helper function to see what features can be selected/excluded. + """ + X = df.drop(columns=['iter', 'file'], errors='ignore') + X = X.select_dtypes(include=[np.number]) + return sorted(X.columns.tolist()) + + +def prepare_features(df: pd.DataFrame) -> Tuple[pd.DataFrame, pd.Series, List[str]]: + """ + Prepare features and target from DataFrame. + Excludes 'file' and 'iter' from features. + Applies feature selection based on FEATURES_TO_EXCLUDE and FEATURES_TO_INCLUDE_ONLY. + """ + # Separate target + y = df['iter'].copy() + + # Drop non-feature columns + X = df.drop(columns=['iter', 'file']) + + # Ensure all features are numeric + non_numeric = X.select_dtypes(exclude=[np.number]).columns.tolist() + if non_numeric: + print(f"Warning: Dropping non-numeric columns: {non_numeric}") + X = X.select_dtypes(include=[np.number]) + + # Apply feature selection + original_feature_count = len(X.columns) + + if FEATURES_TO_INCLUDE_ONLY: + # Use only specified features + available_features = [f for f in FEATURES_TO_INCLUDE_ONLY if f in X.columns] + missing_features = [f for f in FEATURES_TO_INCLUDE_ONLY if f not in X.columns] + + if missing_features: + print(f"Warning: Requested features not found in data: {missing_features}") + + X = X[available_features] + print(f"Feature selection: Using only {len(available_features)} specified features") + + elif FEATURES_TO_EXCLUDE: + # Exclude specified features + features_to_drop = [f for f in FEATURES_TO_EXCLUDE if f in X.columns] + if features_to_drop: + X = X.drop(columns=features_to_drop) + print(f"Feature selection: Excluded {len(features_to_drop)} features: {features_to_drop}") + + feature_names = X.columns.tolist() + + if len(feature_names) == 0: + raise ValueError( + "No features remaining after feature selection! " + "Check FEATURES_TO_EXCLUDE and FEATURES_TO_INCLUDE_ONLY settings." + ) + + if len(feature_names) != original_feature_count: + print(f" Using {len(feature_names)} of {original_feature_count} available features") + + return X, y, feature_names + + +def create_regressor(regressor_type: str, random_state: int = None, + tune_hyperparams: bool = False, verbose: bool = True): + """ + Create a regression model with optional preprocessing pipeline. + + Returns: (model, needs_scaling) + """ + if regressor_type == 'linear': + model = LinearRegression() + needs_scaling = True + + elif regressor_type.startswith('poly'): + degree = int(regressor_type[-1]) + model = Pipeline([ + ('poly', PolynomialFeatures(degree=degree, include_bias=False)), + ('regressor', Ridge(alpha=1.0)) # Ridge to handle multicollinearity + ]) + needs_scaling = True + + elif regressor_type == 'xgboost': + try: + import xgboost as xgb + except ImportError: + raise ImportError("XGBoost not installed. Install with: pip install xgboost") + + params = { + 'objective': 'reg:squarederror', + 'random_state': random_state, + 'n_estimators': 100, + 'max_depth': 6, + 'tree_method': 'hist', + 'learning_rate': 0.1, + 'verbosity': 1 if verbose else 0, + # Regularization to prevent overfitting + 'min_child_weight': 3, # Minimum sum of weights in a leaf + 'gamma': 0.1, # Minimum loss reduction for split + 'subsample': 0.8, # Fraction of samples per tree + 'colsample_bytree': 0.8, # Fraction of features per tree + 'reg_alpha': 0.1, # L1 regularization + 'reg_lambda': 1.0, # L2 regularization + } + + if tune_hyperparams: + # Stronger regularization for tuned version + params.update({ + 'n_estimators': 200, + 'max_depth': 5, # Shallower trees + 'learning_rate': 0.05, # Lower learning rate + 'min_child_weight': 5, # Higher minimum weight + 'gamma': 0.2, # More conservative splits + 'subsample': 0.7, # More aggressive subsampling + 'colsample_bytree': 0.7, + 'reg_alpha': 0.5, # Stronger L1 + 'reg_lambda': 2.0, # Stronger L2 + }) + + model = xgb.XGBRegressor(**params) + needs_scaling = False + + elif regressor_type == 'lightgbm': + try: + import lightgbm as lgb + except ImportError: + raise ImportError("LightGBM not installed. Install with: pip install lightgbm") + + params = { + 'objective': 'regression', + 'random_state': random_state, + 'n_estimators': 100, + 'max_depth': 6, + 'learning_rate': 0.1, + 'verbosity': 1 if verbose else -1, + # Regularization to prevent overfitting + 'min_child_weight': 3, + 'min_split_gain': 0.1, + 'subsample': 0.8, + 'colsample_bytree': 0.8, + 'reg_alpha': 0.1, + 'reg_lambda': 1.0, + } + + if tune_hyperparams: + # Stronger regularization for tuned version + params.update({ + 'n_estimators': 200, + 'max_depth': 5, + 'learning_rate': 0.05, + 'min_child_weight': 5, + 'min_split_gain': 0.2, + 'subsample': 0.7, + 'colsample_bytree': 0.7, + 'reg_alpha': 0.5, + 'reg_lambda': 2.0, + }) + + model = lgb.LGBMRegressor(**params) + needs_scaling = False + + elif regressor_type == 'random_forest': + params = { + 'random_state': random_state, + 'n_estimators': 100, + 'max_depth': None, + 'min_samples_split': 2, + 'verbose': 1 if verbose else 0 + } + + if tune_hyperparams: + params.update({ + 'n_estimators': 200, + 'max_depth': 20, + 'min_samples_split': 5 + }) + + model = RandomForestRegressor(**params) + needs_scaling = False + + elif regressor_type == 'gradient_boosting': + params = { + 'random_state': random_state, + 'n_estimators': 100, + 'max_depth': 5, + 'learning_rate': 0.1, + 'verbose': 1 if verbose else 0 + } + + if tune_hyperparams: + params.update({ + 'n_estimators': 200, + 'max_depth': 7, + 'learning_rate': 0.05 + }) + + model = GradientBoostingRegressor(**params) + needs_scaling = False + + else: + raise ValueError(f"Unknown regressor type: {regressor_type}") + + return model, needs_scaling + + +def get_feature_importance(model, feature_names: List[str], + regressor_type: str) -> None: + """Extract and print feature importance if available.""" + print(f"\nFeature Importance:") + + try: + if regressor_type in ['xgboost', 'lightgbm', 'random_forest', 'gradient_boosting']: + # Tree-based models have feature_importances_ + importances = model.feature_importances_ + indices = np.argsort(importances)[::-1] + + for i, idx in enumerate(indices, 1): + print(f" {i:3d}. {feature_names[idx]:40s}: {importances[idx]:.6f}") + + elif regressor_type == 'linear': + # Linear regression coefficients + coefs = np.abs(model.coef_) + indices = np.argsort(coefs)[::-1] + + for i, idx in enumerate(indices, 1): + print(f" {i:3d}. {feature_names[idx]:40s}: {coefs[idx]:.6f}") + + elif regressor_type.startswith('poly'): + # For polynomial, get feature names and coefficients from the Ridge step + poly_features = model.named_steps['poly'].get_feature_names_out(feature_names) + coefs = np.abs(model.named_steps['regressor'].coef_) + indices = np.argsort(coefs)[::-1] + + print(f" (Showing top 50 of {len(indices)} polynomial features)") + for i, idx in enumerate(indices[:50], 1): + feat_name = poly_features[idx] + # Truncate very long polynomial feature names + if len(feat_name) > 60: + feat_name = feat_name[:57] + "..." + print(f" {i:3d}. {feat_name:60s}: {coefs[idx]:.6f}") + else: + print(" Feature importance not available for this model type") + + except Exception as e: + print(f" Could not extract feature importance: {e}") + + +def evaluate_model(model, X_train, y_train, X_test, y_test, + feature_names: List[str], regressor_type: str, + cv_folds: int = 5, verbose: int = 0, + skip_cv: bool = False, X_test_original: pd.DataFrame = None, + test_df: pd.DataFrame = None) -> Tuple[float, float]: + """Evaluate model and print metrics. Returns (train_r2, test_r2). + + Args: + ---- + X_test_original: Unscaled X_test for displaying feature values + test_df: Original test dataframe with 'file' column + """ + # Cross-validation on training set (skip if using early stopping) + if not skip_cv: + print(f"\nCross-Validation on Training Set ({cv_folds}-fold):") + try: + cv_scores = cross_val_score(model, X_train, y_train, + cv=cv_folds, + scoring='neg_mean_squared_error', + n_jobs=-1, + verbose=verbose) + cv_rmse = np.sqrt(-cv_scores) + print(f" CV RMSE: {cv_rmse.mean():.4f} (+/- {cv_rmse.std():.4f})") + except Exception as e: + print(f" CV failed (likely due to early stopping): {str(e)[:100]}") + print(f" Skipping cross-validation...") + else: + print(f"\nSkipping cross-validation (incompatible with early stopping)") + + # Training set metrics + y_train_pred = model.predict(X_train) + train_mse = mean_squared_error(y_train, y_train_pred) + train_rmse = np.sqrt(train_mse) + train_mae = mean_absolute_error(y_train, y_train_pred) + train_r2 = r2_score(y_train, y_train_pred) + + print(f"\nTraining Set Metrics:") + print(f" MSE: {train_mse:.4f}") + print(f" RMSE: {train_rmse:.4f}") + print(f" MAE: {train_mae:.4f}") + print(f" R²: {train_r2:.4f}") + + # Test set metrics + y_test_pred = model.predict(X_test) + test_mse = mean_squared_error(y_test, y_test_pred) + test_rmse = np.sqrt(test_mse) + test_mae = mean_absolute_error(y_test, y_test_pred) + test_r2 = r2_score(y_test, y_test_pred) + + print(f"\nTest Set Metrics:") + print(f" MSE: {test_mse:.4f}") + print(f" RMSE: {test_rmse:.4f}") + print(f" MAE: {test_mae:.4f}") + print(f" R²: {test_r2:.4f}") + + # Feature importance + get_feature_importance(model, feature_names, regressor_type) + + # Sample predictions + print(f"\n20 Sample Predictions from Test Set:") + print(f" {'Actual':>10s} {'Predicted':>10s} {'Error':>10s} {'Error %':>10s}") + print(f" {'-'*10} {'-'*10} {'-'*10} {'-'*10}") + + sample_indices = np.random.choice(len(y_test), min(20, len(y_test)), replace=False) + for idx in sample_indices: + actual = y_test.iloc[idx] + predicted = y_test_pred[idx] + error = actual - predicted + error_pct = (error / actual * 100) if actual != 0 else 0 + print(f" {actual:10.2f} {predicted:10.2f} {error:10.2f} {error_pct:9.2f}%") + + # Show worst predictions with feature values + print(f"\n5 Worst Predictions (Largest Absolute Error):") + abs_errors = np.abs(y_test_pred - y_test.values) + worst_indices = np.argsort(abs_errors)[-5:][::-1] + + # Use original (unscaled) features if available, otherwise use X_test + X_display = X_test_original if X_test_original is not None else X_test + + for rank, idx in enumerate(worst_indices, 1): + actual = y_test.iloc[idx] + predicted = y_test_pred[idx] + error = actual - predicted + error_pct = (error / actual * 100) if actual != 0 else 0 + + # Get filename if available + filename = "" + if test_df is not None and 'file' in test_df.columns: + filename = f" (file: {test_df.iloc[idx]['file']})" + + print(f"\n #{rank} - Actual: {actual:.2f}, Predicted: {predicted:.2f}, " + f"Error: {error:.2f} ({error_pct:.1f}%){filename}") + + # Get feature values (handle both DataFrame and array) + if isinstance(X_display, pd.DataFrame): + feature_values = X_display.iloc[idx].values + elif isinstance(X_display, np.ndarray): + feature_values = X_display[idx] + else: + feature_values = X_display[idx] + + # Display features compactly (5 per line) + print(f" Features:", end="") + for i, (feat_name, feat_val) in enumerate(zip(feature_names, feature_values)): + if i % 5 == 0: + print(f"\n ", end="") + # Format feature value + if isinstance(feat_val, (int, np.integer)): + print(f"{feat_name}={feat_val}", end=" ") + else: + print(f"{feat_name}={feat_val:.3g}", end=" ") + print() # Final newline + + return train_r2, test_r2 + + +def compile_model_treelite(model, regressor_type: str, output_dir: str, + num_threads: int, X_train=None, + annotate: bool = False, quantize: bool = False, + feature_names: List[str] = None, + model_name: str = None) -> None: + """Compile XGBoost/LightGBM model to C source files using TL2cgen. + + Args: + ---- + model: Trained model + regressor_type: Type of regressor + output_dir: Output directory + num_threads: Number of parallel compilation threads + X_train: Training data for branch annotation (optional) + annotate: Whether to annotate branches for optimization + quantize: Whether to use quantization in code generation + feature_names: List of feature names in expected order (optional) + model_name: Name prefix for functions (optional, derived from training file) + """ + if regressor_type not in ['xgboost', 'lightgbm']: + print(f"Warning: TL2cgen compilation only supported for XGBoost and LightGBM, skipping for {regressor_type}") + return + + try: + import treelite + import tl2cgen + except ImportError as e: + missing = [] + try: + import treelite + except ImportError: + missing.append("treelite") + try: + import tl2cgen + except ImportError: + missing.append("tl2cgen") + + print(f"Warning: {', '.join(missing)} not installed. Install with: pip install {' '.join(missing)}") + print("Skipping C code generation.") + return + + optimization_info = [] + if annotate: + optimization_info.append("branch annotation") + if quantize: + optimization_info.append("quantization") + + opt_str = f" with {', '.join(optimization_info)}" if optimization_info else "" + print(f"\nGenerating C source code with TL2cgen (threads={num_threads}){opt_str}...") + + # Convert model to treelite format using frontend API + try: + if regressor_type == 'xgboost': + tl_model = treelite.frontend.from_xgboost(model.get_booster()) + elif regressor_type == 'lightgbm': + tl_model = treelite.frontend.from_lightgbm(model.booster_) + except Exception as e: + print(f"Warning: Failed to convert {regressor_type} model to treelite: {e}") + return + + # Annotate branches if requested and training data is available + annotation_path = None + if annotate and X_train is not None: + try: + print(" Annotating branches with training data...") + # Convert to numpy array if it's a DataFrame + if hasattr(X_train, 'values'): + X_train_array = X_train.values.astype(np.float32) + else: + X_train_array = np.asarray(X_train, dtype=np.float32) + + dmat = tl2cgen.DMatrix(X_train_array, dtype='float32') + annotation_path = os.path.join(output_dir, f'{regressor_type}_annotation.json') + tl2cgen.annotate_branch(tl_model, dmat=dmat, path=annotation_path, verbose=False) + print(f" Branch annotations saved to: {annotation_path}") + except Exception as e: + print(f" Warning: Branch annotation failed: {e}") + print(" Continuing without branch annotation") + annotation_path = None + elif annotate and X_train is None: + print(" Warning: Branch annotation requested but no training data available") + print(" Skipping branch annotation") + + # Generate C source files using TL2cgen + source_dir = os.path.join(output_dir, f'{regressor_type}_c_code') + + try: + #params = {'parallel_comp': num_threads} + params = {} + + # Add quantization parameter if requested + if quantize: + params['quantize'] = 1 # Enable quantization in code generation + + # Add annotation file if available + if annotation_path: + params['annotate_in'] = annotation_path + + tl2cgen.generate_c_code( + tl_model, + dirpath=source_dir, + params=params, + verbose=False + ) + + # Post-process generated files + header_path = os.path.join(source_dir, 'header.h') + main_path = os.path.join(source_dir, 'main.c') + quantize_path = os.path.join(source_dir, 'quantize.c') + recipe_path = os.path.join(source_dir, 'recipe.json') + + # Rename all .c files to .cpp and wrap in namespace + if model_name: + try: + import glob + c_files = glob.glob(os.path.join(source_dir, '*.c')) + + for c_file in c_files: + cpp_file = c_file[:-2] + '.cpp' + + # Read content + with open(c_file, 'r') as f: + content = f.read() + + # Wrap in namespace + namespaced_content = f'namespace {model_name} {{\n\n{content}\n\n}} // namespace {model_name}\n' + + # Write to .cpp file + with open(cpp_file, 'w') as f: + f.write(namespaced_content) + + # Remove original .c file + os.remove(c_file) + + # Update paths for further processing + main_path = main_path[:-2] + '.cpp' + quantize_path = quantize_path[:-2] + '.cpp' + + print(f" Renamed {len(c_files)} .c files to .cpp and wrapped in namespace '{model_name}'") + except Exception as e: + print(f" Warning: Failed to rename .c files: {e}") + + # Wrap header.h content in namespace + if model_name and os.path.exists(header_path): + try: + with open(header_path, 'r') as f: + content = f.read() + + # Wrap in namespace + namespaced_content = f'namespace {model_name} {{\n\n{content}\n\n}} // namespace {model_name}\n' + + with open(header_path, 'w') as f: + f.write(namespaced_content) + + print(f" Wrapped header.h in namespace '{model_name}'") + except Exception as e: + print(f" Warning: Failed to wrap header.h: {e}") + + # Add feature names to header and implementation + if feature_names and os.path.exists(header_path) and os.path.exists(main_path): + try: + # Append to header.h (inside namespace) + with open(header_path, 'r') as f: + content = f.read() + + # Insert before closing namespace + insertion = f'\n// Feature names\n#define NUM_FEATURES {len(feature_names)}\nextern const char* feature_names[NUM_FEATURES];\n' + content = content.replace(f'}} // namespace {model_name}\n', f'{insertion}\n}} // namespace {model_name}\n') + + with open(header_path, 'w') as f: + f.write(content) + + # Append to main.cpp (inside namespace) + with open(main_path, 'r') as f: + content = f.read() + + # Insert before closing namespace + feature_array = f'\n// Feature names array\nconst char* feature_names[NUM_FEATURES] = {{\n' + for i, name in enumerate(feature_names): + comma = ',' if i < len(feature_names) - 1 else '' + feature_array += f' "{name}"{comma}\n' + feature_array += '};\n' + + content = content.replace(f'}} // namespace {model_name}\n', f'{feature_array}\n}} // namespace {model_name}\n') + + with open(main_path, 'w') as f: + f.write(content) + + print(f" Added {len(feature_names)} feature names to header.h and main.cpp") + except Exception as e: + print(f" Warning: Failed to add feature names: {e}") + + # Remove recipe.json if it exists + if os.path.exists(recipe_path): + try: + os.remove(recipe_path) + print(f" Removed recipe.json") + except Exception as e: + print(f" Warning: Failed to remove recipe.json: {e}") + + opt_msg = [] + if annotation_path: + opt_msg.append("branch-annotated") + if quantize: + opt_msg.append("quantized") + opt_suffix = f" ({', '.join(opt_msg)})" if opt_msg else "" + + print(f"C source code generated to: {source_dir}/") + print(f" Contains optimized model source code{opt_suffix} ready for compilation") + except Exception as e: + print(f"Warning: TL2cgen code generation failed: {e}") + print(" Model saved in standard format only.") + + +def save_model(model, scaler, regressor_type: str, output_dir: str, + feature_names: List[str]) -> None: + """Save trained model and preprocessing components to disk.""" + os.makedirs(output_dir, exist_ok=True) + + # Save metadata + metadata = { + 'regressor_type': regressor_type, + 'feature_names': feature_names, + 'has_scaler': scaler is not None + } + + metadata_path = os.path.join(output_dir, f'{regressor_type}_metadata.pkl') + with open(metadata_path, 'wb') as f: + pickle.dump(metadata, f) + print(f"\nSaved metadata to: {metadata_path}") + + # Save scaler if exists + if scaler is not None: + scaler_path = os.path.join(output_dir, f'{regressor_type}_scaler.pkl') + joblib.dump(scaler, scaler_path) + print(f"Saved scaler to: {scaler_path}") + + # Save model + if regressor_type == 'xgboost': + # Save as UBJ with gzip compression + model_path = os.path.join(output_dir, f'{regressor_type}_model.ubj') + model.save_model(model_path) + + # Gzip the file + import gzip + import shutil + with open(model_path, 'rb') as f_in: + with gzip.open(model_path + '.gz', 'wb') as f_out: + shutil.copyfileobj(f_in, f_out) + os.remove(model_path) # Remove uncompressed version + print(f"Saved model to: {model_path}.gz") + elif regressor_type == 'lightgbm': + # Save LightGBM model as text file + model_path = os.path.join(output_dir, f'{regressor_type}_model.txt') + model.booster_.save_model(model_path) + print(f"Saved model to: {model_path}") + else: + # Save sklearn models as joblib (more efficient than pickle for large arrays) + model_path = os.path.join(output_dir, f'{regressor_type}_model.joblib') + joblib.dump(model, model_path) + print(f"Saved model to: {model_path}") + + +def main(): + parser = argparse.ArgumentParser( + description='Train regression models to predict algorithm iterations', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=f""" +Available regressors: + linear - Linear Regression + poly2, poly3, poly4 - Polynomial Regression (degree 2, 3, 4) + xgboost - XGBoost Regressor + lightgbm - LightGBM Regressor + random_forest - Random Forest Regressor + gradient_boosting - Gradient Boosting Regressor + +Example: + python train_regressor.py data.pkl --regressor xgboost --seed 42 + python train_regressor.py data.pkl --regressor lightgbm --seed 42 + """ + ) + + parser.add_argument('input_pkl', help='Input pickle file with log data') + parser.add_argument( + '--regressor', '-r', + required=True, + choices=AVAILABLE_REGRESSORS, + help='Type of regressor to train' + ) + parser.add_argument( + '--output-dir', '-o', + default='./models', + help='Output directory for saved models (default: ./models)' + ) + parser.add_argument( + '--seed', '-s', + type=int, + default=None, + help='Random seed for reproducibility (optional)' + ) + parser.add_argument( + '--tune', + action='store_true', + help='Enable hyperparameter tuning (uses predefined tuned parameters)' + ) + parser.add_argument( + '--cv-folds', + type=int, + default=5, + help='Number of cross-validation folds (default: 5)' + ) + parser.add_argument( + '--test-size', + type=float, + default=0.2, + help='Proportion of files to use for testing (default: 0.2)' + ) + parser.add_argument( + '--no-progress', + action='store_true', + help='Disable training progress output' + ) + parser.add_argument( + '--list-features', + action='store_true', + help='List all available features in the dataset and exit' + ) + parser.add_argument( + '--stratify-split', + action='store_true', + help='Stratify train/test split by target distribution (ensures balanced iter values)' + ) + parser.add_argument( + '--early-stopping', + type=int, + default=20, + metavar='N', + help='Enable early stopping for tree models (default: 20 rounds, use 0 to disable)' + ) + parser.add_argument( + '--treelite-compile', + type=int, + default=1, + metavar='THREADS', + help='Export XGBoost/LightGBM model as optimized C source code with TL2cgen (includes branch annotation and quantization)' + ) + + args = parser.parse_args() + + # Set random seed if provided + if args.seed is not None: + np.random.seed(args.seed) + print(f"Random seed set to: {args.seed}") + + # Load data + print(f"\nLoading data from: {args.input_pkl}") + df = load_data(args.input_pkl) + print(f"Loaded {len(df)} entries with {len(df.columns)} columns") + + # Extract model name from input file (for prefixing generated C functions) + model_name = os.path.splitext(os.path.basename(args.input_pkl))[0] + + # If listing features, do that and exit + if args.list_features: + features = list_available_features(df) + print(f"\n{'='*70}") + print(f"Available features in dataset ({len(features)} total):") + print(f"{'='*70}") + for i, feat in enumerate(features, 1): + print(f" {i:3d}. {feat}") + print(f"\nTo exclude features, edit FEATURES_TO_EXCLUDE in the script:") + print(f" {__file__}") + print(f"\nTo use only specific features, edit FEATURES_TO_INCLUDE_ONLY") + return + + # Split data by files + stratify_by = 'iter' if args.stratify_split else None + train_df, test_df = split_by_files(df, test_size=args.test_size, + random_state=args.seed, + stratify_by=stratify_by) + + # Prepare features + X_train, y_train, feature_names = prepare_features(train_df) + X_test, y_test, _ = prepare_features(test_df) + + print(f"\nFeatures: {len(feature_names)}") + print(f"Target: iter (predicting number of iterations)") + + # Create model + print(f"\nTraining {args.regressor} regressor...") + model, needs_scaling = create_regressor( + args.regressor, + random_state=args.seed, + tune_hyperparams=args.tune, + verbose=not args.no_progress + ) + + # Apply scaling if needed + scaler = None + X_test_original = X_test.copy() # Keep unscaled version for display + if needs_scaling: + print(" Applying StandardScaler to features...") + scaler = StandardScaler() + X_train_scaled = scaler.fit_transform(X_train) + X_test_scaled = scaler.transform(X_test) + else: + X_train_scaled = X_train + X_test_scaled = X_test + + # Train model + if args.regressor.startswith('poly'): + degree = int(args.regressor[-1]) + n_features = X_train_scaled.shape[1] + from math import comb + n_poly_features = sum(comb(n_features + d - 1, d) for d in range(1, degree + 1)) + print(f" Generating {n_poly_features} polynomial features (degree {degree})...") + + # Use early stopping for tree-based models if requested + if args.early_stopping and args.early_stopping > 0 and args.regressor in ['xgboost', 'lightgbm', 'gradient_boosting']: + if args.regressor == 'xgboost': + print(f" Using early stopping (patience={args.early_stopping} rounds)...") + # Set early stopping parameter + model.set_params(early_stopping_rounds=args.early_stopping) + model.fit( + X_train_scaled, y_train, + eval_set=[(X_test_scaled, y_test)], + verbose=False + ) + # Report best iteration + best_iteration = model.best_iteration if hasattr(model, 'best_iteration') else model.n_estimators + print(f" Best iteration: {best_iteration} (out of {model.n_estimators} max)") + elif args.regressor == 'lightgbm': + print(f" Using early stopping (patience={args.early_stopping} rounds)...") + model.fit( + X_train_scaled, y_train, + eval_set=[(X_test_scaled, y_test)], + callbacks=[ + __import__('lightgbm').early_stopping(stopping_rounds=args.early_stopping, verbose=False) + ] + ) + # Report best iteration + best_iteration = model.best_iteration_ if hasattr(model, 'best_iteration_') else model.n_estimators + print(f" Best iteration: {best_iteration} (out of {model.n_estimators} max)") + else: # gradient_boosting + # Gradient Boosting uses n_iter_no_change parameter + print(f" Note: Use --tune with gradient_boosting for early stopping") + model.fit(X_train_scaled, y_train) + else: + model.fit(X_train_scaled, y_train) + + print(" Training complete!") + + # Evaluate model + skip_cv = args.early_stopping is not None and args.early_stopping > 0 + train_r2, test_r2 = evaluate_model(model, X_train_scaled, y_train, X_test_scaled, y_test, + feature_names, args.regressor, cv_folds=args.cv_folds, + verbose=2 if not args.no_progress else 0, + skip_cv=skip_cv, X_test_original=X_test_original, + test_df=test_df) + + # Save model + save_model(model, scaler, args.regressor, args.output_dir, feature_names) + + # Compile with TL2cgen if requested (with optimizations enabled by default) + if args.treelite_compile is not None: + # Use unscaled training data for branch annotation + # Only tree-based models (XGBoost, LightGBM) don't need scaling + X_train_for_annotation = X_train if not needs_scaling else None + + compile_model_treelite( + model, + args.regressor, + args.output_dir, + args.treelite_compile, + X_train=X_train_for_annotation, + annotate=True, # Always enable branch annotation + quantize=True, # Always enable quantization + feature_names=feature_names, + model_name=model_name + ) + + print("\n" + "="*70) + print("Training completed successfully!") + print("="*70) + print(f"\nFinal R² Scores:") + print(f" Train R²: {train_r2:.4f}") + print(f" Test R²: {test_r2:.4f}") + + +if __name__ == '__main__': + main() From c9cae3c546eb37253536a48f99d6d783fd8fae3a Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 30 Oct 2025 14:20:49 +0000 Subject: [PATCH 018/225] switch to using tl2cgen --- .gitignore | 1 - cpp/src/CMakeLists.txt | 44 +- .../mip/feasibility_jump/feasibility_jump.cu | 21 +- cpp/src/mip/solver_context.cuh | 4 +- .../utilities/models/fj_predictor/header.h | 47 + .../utilities/models/fj_predictor/main.cpp | 11828 ++++++++++++++++ .../models/fj_predictor/quantize.cpp | 1180 ++ cpp/src/utilities/work_unit_predictor.cpp | 167 +- cpp/src/utilities/work_unit_predictor.hpp | 9 +- cpp/tests/mip/feasibility_jump_tests.cu | 4 +- scripts/README_REGRESSION.md | 83 +- scripts/train_regressor.py | 188 +- 12 files changed, 13347 insertions(+), 229 deletions(-) create mode 100644 cpp/src/utilities/models/fj_predictor/header.h create mode 100644 cpp/src/utilities/models/fj_predictor/main.cpp create mode 100644 cpp/src/utilities/models/fj_predictor/quantize.cpp diff --git a/.gitignore b/.gitignore index 3f02aaa413..9edc9823c1 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,5 @@ docs/cuopt/build # generated version file cpp/include/cuopt/semantic_version.hpp -cpp/src/utilities/models_ubj.h !datasets/quadratic_programming !datasets/quadratic_programming/** diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 837affffe6..45f884b3f0 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -13,51 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Check for required tools -find_program(XXD_EXECUTABLE xxd) -find_program(GZIP_EXECUTABLE gzip) -find_program(MKTEMP_EXECUTABLE mktemp) - -if(NOT XXD_EXECUTABLE) - message(FATAL_ERROR "xxd command not found. Please install xxd (typically provided by vim package).") -endif() - -if(NOT GZIP_EXECUTABLE) - message(FATAL_ERROR "gzip command not found. Please install gzip.") -endif() - -if(NOT MKTEMP_EXECUTABLE) - message(FATAL_ERROR "mktemp command not found. Please install coreutils or equivalent package.") -endif() - -# Directory containing model files -set(XGB_MODEL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/utilities/models") -# Find all .ubj.gz model files in models/ -file(GLOB XGB_MODEL_FILES "${XGB_MODEL_DIR}/*.ubj.gz") -message(STATUS "XGB_MODEL_DIR: ${XGB_MODEL_DIR}") -message(STATUS "Found ${XGB_MODEL_FILES} model files") - -# Set output header name -set(XGB_MODELS_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/utilities/models_ubj.h") - -# Path to the embedding script -set(EMBED_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/utilities/embed_models.sh") - -# Custom command to generate header by calling the shell script -add_custom_command( - OUTPUT ${XGB_MODELS_HEADER} - COMMAND sh ${EMBED_SCRIPT} ${XGB_MODELS_HEADER} ${XGB_MODEL_FILES} - DEPENDS ${XGB_MODEL_FILES} ${EMBED_SCRIPT} - COMMENT "Decompressing and embedding all UBJ models into ${XGB_MODELS_HEADER}" -) - -add_custom_target(embed_xgboost_models ALL DEPENDS ${XGB_MODELS_HEADER}) - 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/timestamp_utils.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_predictor.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_predictor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/quantize.cpp) add_subdirectory(linear_programming) add_subdirectory(math_optimization) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 264e022cff..fb28a9e136 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -910,7 +910,7 @@ std::map fj_t::get_feature_vector(i_t climber_idx) features["unbalancedness"] = (float)pb_ptr->unbalancedness; // Algorithm settings - features["target_time"] = (float)settings.work_unit_limit; + features["time"] = (float)settings.work_unit_limit; features["n_of_minimums_for_exit"] = (float)settings.n_of_minimums_for_exit; features["feasibility_run"] = (float)settings.feasibility_run; @@ -954,10 +954,10 @@ std::map fj_t::get_feature_vector(i_t climber_idx) } features["avg_related_vars_per_var"] = pb_ptr->n_variables > 0 ? (float)total_related / pb_ptr->n_variables : 0.0f; - features["max_related_vars"] = (float)max_related; + // features["max_related_vars"] = (float)max_related; } else { features["avg_related_vars_per_var"] = 0.0f; - features["max_related_vars"] = 0.0f; + // features["max_related_vars"] = 0.0f; } // Constraint characteristics @@ -1281,18 +1281,9 @@ i_t fj_t::solve(solution_t& solution) // if work_limit is set: compute an estimate of the number of iterations required if (settings.work_unit_limit != std::numeric_limits::infinity()) { - auto features = std::vector{ - (float)settings.work_unit_limit, - (float)settings.n_of_minimums_for_exit, - (float)pb_ptr->n_variables, - (float)pb_ptr->n_constraints, - (float)pb_ptr->coefficients.size(), - (float)pb_ptr->sparsity, - (float)pb_ptr->nnz_stddev, - (float)pb_ptr->unbalancedness, - }; - float iter_prediction = std::max( - (f_t)0.0, (f_t)ceil(context.work_unit_predictors.fj_predictor.predict_scalar(features))); + std::map features_map = get_feature_vector(0); + float iter_prediction = std::max( + (f_t)0.0, (f_t)ceil(context.work_unit_predictors.fj_predictor.predict_scalar(features_map))); CUOPT_LOG_DEBUG("FJ determ: Estimated number of iterations for %f WU: %f", settings.work_unit_limit, iter_prediction); diff --git a/cpp/src/mip/solver_context.cuh b/cpp/src/mip/solver_context.cuh index 07ab64964f..3a1133e1fa 100644 --- a/cpp/src/mip/solver_context.cuh +++ b/cpp/src/mip/solver_context.cuh @@ -20,6 +20,8 @@ #include #include #include + +#include #include #pragma once @@ -27,7 +29,7 @@ namespace cuopt::linear_programming::detail { struct mip_solver_work_unit_predictors_t { - work_unit_predictor_t fj_predictor{"fj"}; + work_unit_predictor_t fj_predictor{}; }; // Aggregate structure containing the global context of the solving process for convenience: diff --git a/cpp/src/utilities/models/fj_predictor/header.h b/cpp/src/utilities/models/fj_predictor/header.h new file mode 100644 index 0000000000..ccae87627f --- /dev/null +++ b/cpp/src/utilities/models/fj_predictor/header.h @@ -0,0 +1,47 @@ +/* + * 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 +#include +#include +#include +#include +#include + +class fj_predictor { + public: + union Entry { + int missing; + double fvalue; + int qvalue; + }; + + static int32_t get_num_target(void); + static void get_num_class(int32_t* out); + static int32_t get_num_feature(void); + static const char* get_threshold_type(void); + static const char* get_leaf_output_type(void); + static void predict(union Entry* data, int pred_margin, double* result); + static void postprocess(double* result); + static int quantize(double val, unsigned fid); + + // Feature names + static constexpr int NUM_FEATURES = 12; + static const char* feature_names[NUM_FEATURES]; +}; // class fj_predictor diff --git a/cpp/src/utilities/models/fj_predictor/main.cpp b/cpp/src/utilities/models/fj_predictor/main.cpp new file mode 100644 index 0000000000..ac5cd1ed11 --- /dev/null +++ b/cpp/src/utilities/models/fj_predictor/main.cpp @@ -0,0 +1,11828 @@ + +/* + * 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 "header.h" + +#if defined(__clang__) || defined(__GNUC__) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif +#define N_TARGET 1 +#define MAX_N_CLASS 1 + +const unsigned char is_categorical[] = { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; +static const int32_t num_class[] = { + 1, +}; + +int32_t fj_predictor::get_num_target(void) { return N_TARGET; } +void fj_predictor::get_num_class(int32_t* out) +{ + for (int i = 0; i < N_TARGET; ++i) { + out[i] = num_class[i]; + } +} +int32_t fj_predictor::get_num_feature(void) { return 12; } +const char* fj_predictor::get_threshold_type(void) { return "float64"; } +const char* fj_predictor::get_leaf_output_type(void) { return "float64"; } + +void fj_predictor::predict(union Entry* data, int pred_margin, double* result) +{ + // Quantize data + for (int i = 0; i < 12; ++i) { + if (data[i].missing != -1 && !is_categorical[i]) { + data[i].qvalue = quantize(data[i].fvalue, i); + } + } + + unsigned int tmp; + if (UNLIKELY(false || (data[0].qvalue <= 186))) { + if (LIKELY(false || (data[0].qvalue <= 98))) { + if (UNLIKELY(false || (data[0].qvalue <= 44))) { + result[0] += 22901.255344838846; + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + result[0] += 23329.74375127941; + } else { + result[0] += 23615.614222033248; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 144))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + result[0] += 24209.472824119093; + } else { + if (LIKELY(false || (data[6].qvalue <= 100))) { + result[0] += 23869.313116934638; + } else { + result[0] += 23405.54395751655; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 88))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 24902.415701614485; + } else { + result[0] += 24520.27915996134; + } + } else { + result[0] += 23891.56119652762; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 262))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[0].qvalue <= 218))) { + result[0] += 25411.420502457517; + } else { + result[0] += 25917.434788937655; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[1].qvalue <= 116))) { + if (LIKELY(false || (data[0].qvalue <= 224))) { + result[0] += 24824.90282590602; + } else { + result[0] += 25260.302972714355; + } + } else { + result[0] += 24241.872897906287; + } + } else { + result[0] += 23668.087700157346; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 112))) { + if (UNLIKELY(false || (data[0].qvalue <= 312))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 26558.857562746103; + } else { + result[0] += 26202.38213380121; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 102))) { + result[0] += 25849.940581446823; + } else { + result[0] += 25230.335505164217; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 92))) { + if (LIKELY(false || (data[8].qvalue <= 126))) { + result[0] += 26911.44811674291; + } else { + result[0] += 26503.327098291113; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + result[0] += 25835.236675109372; + } else { + result[0] += 26394.354620052833; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 430))) { + if (LIKELY(false || (data[6].qvalue <= 152))) { + if (UNLIKELY(false || (data[0].qvalue <= 352))) { + result[0] += 25138.58875113828; + } else { + result[0] += 25959.666864308958; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 406))) { + result[0] += 23920.482800539634; + } else { + result[0] += 24831.920082473396; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 174))) { + if (LIKELY(false || (data[1].qvalue <= 152))) { + result[0] += 26247.05905907499; + } else { + result[0] += 25812.009772798196; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 464))) { + result[0] += 24485.25504118496; + } else { + result[0] += 25971.80731105497; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 188))) { + if (LIKELY(false || (data[0].qvalue <= 104))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + if (LIKELY(false || (data[0].qvalue <= 24))) { + result[0] += -2102.2335038546057; + } else { + result[0] += -1814.143470277424; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 62))) { + result[0] += -1350.4930205440783; + } else { + result[0] += -1691.59714939238; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 62))) { + if (LIKELY(false || (data[0].qvalue <= 156))) { + if (UNLIKELY(false || (data[0].qvalue <= 126))) { + result[0] += -925.0448121271659; + } else { + result[0] += -609.5991225040165; + } + } else { + result[0] += -202.96527072586937; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 116))) { + if (LIKELY(false || (data[0].qvalue <= 152))) { + result[0] += -1181.817220363881; + } else { + result[0] += -759.0207821022941; + } + } else { + result[0] += -1560.8110503642508; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 266))) { + if (LIKELY(false || (data[7].qvalue <= 66))) { + if (LIKELY(false || (data[0].qvalue <= 228))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 578.7799431040581; + } else { + result[0] += 127.52408840507688; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 933.7806696735934; + } else { + result[0] += 471.418662728938; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 142))) { + if (LIKELY(false || (data[7].qvalue <= 128))) { + if (LIKELY(false || (data[6].qvalue <= 100))) { + result[0] += 24.61431879503551; + } else { + result[0] += -514.7441916892188; + } + } else { + result[0] += -840.288058328428; + } + } else { + result[0] += -1369.132803656919; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 88))) { + if (UNLIKELY(false || (data[0].qvalue <= 320))) { + if (LIKELY(false || (data[6].qvalue <= 60))) { + result[0] += 1297.8937605229921; + } else { + if (UNLIKELY(false || (data[9].qvalue <= 48))) { + result[0] += 159.84923661334855; + } else { + result[0] += 806.892953963784; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 92))) { + result[0] += 1631.4555218267105; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + result[0] += 602.1591975811427; + } else { + result[0] += 1225.4498129353617; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 346))) { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[7].qvalue <= 134))) { + result[0] += 477.26815448941943; + } else { + result[0] += -266.49089797874365; + } + } else { + result[0] += -1085.9174116108277; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 136))) { + if (UNLIKELY(false || (data[9].qvalue <= 30))) { + result[0] += 710.5340339475686; + } else { + result[0] += 1160.9702839561826; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 426))) { + result[0] += -336.2082424932214; + } else { + result[0] += 697.2377446648411; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 184))) { + if (UNLIKELY(false || (data[0].qvalue <= 92))) { + if (UNLIKELY(false || (data[0].qvalue <= 38))) { + result[0] += -1827.1103815856493; + } else { + if (LIKELY(false || (data[6].qvalue <= 92))) { + if (LIKELY(false || (data[0].qvalue <= 68))) { + result[0] += -1460.9279929556785; + } else { + result[0] += -1204.5954946215916; + } + } else { + result[0] += -1703.764420874855; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 136))) { + if (LIKELY(false || (data[7].qvalue <= 62))) { + result[0] += -834.0606253314141; + } else { + result[0] += -1268.097736051363; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 62))) { + if (LIKELY(false || (data[0].qvalue <= 168))) { + result[0] += -450.7445362623278; + } else { + result[0] += -151.5065914814049; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + result[0] += -813.8840731408728; + } else { + result[0] += -1552.9886128870096; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 258))) { + if (LIKELY(false || (data[7].qvalue <= 62))) { + if (UNLIKELY(false || (data[0].qvalue <= 212))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 300.18949268400604; + } else { + result[0] += -82.16209082891513; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 821.3155839598878; + } else { + result[0] += 395.4300146794792; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 142))) { + if (LIKELY(false || (data[7].qvalue <= 134))) { + if (LIKELY(false || (data[6].qvalue <= 94))) { + result[0] += -36.622858072571994; + } else { + result[0] += -472.4429272195769; + } + } else { + result[0] += -886.8842878988258; + } + } else { + result[0] += -1274.9255101678516; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 82))) { + if (UNLIKELY(false || (data[0].qvalue <= 302))) { + if (LIKELY(false || (data[6].qvalue <= 50))) { + result[0] += 1102.9306261545728; + } else { + if (UNLIKELY(false || (data[9].qvalue <= 48))) { + result[0] += 4.269252395168786; + } else { + result[0] += 673.9694942587568; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 48))) { + result[0] += 844.0310059878134; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 336))) { + result[0] += 1254.6320984291067; + } else { + result[0] += 1496.534953542473; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 342))) { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[7].qvalue <= 134))) { + result[0] += 369.8856508010498; + } else { + result[0] += -301.745500216277; + } + } else { + result[0] += -971.5869097857602; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 152))) { + if (LIKELY(false || (data[1].qvalue <= 124))) { + result[0] += 1049.0667116337365; + } else { + result[0] += 655.8288897611133; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 430))) { + result[0] += -483.20221095743983; + } else { + result[0] += 574.5979992744856; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 192))) { + if (LIKELY(false || (data[0].qvalue <= 110))) { + if (LIKELY(false || (data[0].qvalue <= 56))) { + if (UNLIKELY(false || (data[0].qvalue <= 20))) { + result[0] += -1731.6694255800344; + } else { + result[0] += -1463.8734271807368; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 62))) { + result[0] += -1013.7125063069707; + } else { + result[0] += -1295.792308734329; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (LIKELY(false || (data[0].qvalue <= 162))) { + result[0] += -512.040169492541; + } else { + result[0] += -62.54166123570826; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 90))) { + if (LIKELY(false || (data[0].qvalue <= 160))) { + result[0] += -807.711585982142; + } else { + result[0] += -447.3244114975565; + } + } else { + result[0] += -1112.4075523200308; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 274))) { + if (LIKELY(false || (data[7].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 236))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 434.1102182190951; + } else { + result[0] += 64.6833316545062; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 24))) { + result[0] += 911.5851039227344; + } else { + result[0] += 509.70074610455003; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 36))) { + result[0] += -922.6420870752214; + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + result[0] += -43.11646969985167; + } else { + result[0] += -711.5443228439684; + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 112))) { + if (LIKELY(false || (data[7].qvalue <= 50))) { + if (UNLIKELY(false || (data[0].qvalue <= 322))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 1217.5857081225881; + } else { + result[0] += 862.7945760545485; + } + } else { + result[0] += 1381.341515141895; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 340))) { + if (LIKELY(false || (data[7].qvalue <= 128))) { + result[0] += 618.2916036455348; + } else { + result[0] += -156.99063121911638; + } + } else { + if (LIKELY(false || (data[10].qvalue <= 78))) { + result[0] += 863.993956946003; + } else { + result[0] += 1217.2914460169281; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 432))) { + if (LIKELY(false || (data[1].qvalue <= 138))) { + if (LIKELY(false || (data[2].qvalue <= 202))) { + result[0] += 554.0458524017479; + } else { + result[0] += -451.2824254081932; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 132))) { + result[0] += -205.44955983215206; + } else { + result[0] += -1114.6634922865608; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 174))) { + if (UNLIKELY(false || (data[8].qvalue <= 44))) { + result[0] += 434.76778433173723; + } else { + result[0] += 874.836013742844; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + result[0] += -415.8493090687832; + } else { + result[0] += 895.0728029685396; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 180))) { + if (UNLIKELY(false || (data[0].qvalue <= 86))) { + if (UNLIKELY(false || (data[0].qvalue <= 32))) { + result[0] += -1510.0101454113396; + } else { + if (LIKELY(false || (data[1].qvalue <= 78))) { + result[0] += -1126.5760081015337; + } else { + result[0] += -1392.4200114552195; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 128))) { + if (LIKELY(false || (data[1].qvalue <= 78))) { + result[0] += -742.4787091372192; + } else { + result[0] += -1128.3169223737384; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 82))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += -217.1747383855503; + } else { + result[0] += -505.7066609860621; + } + } else { + result[0] += -926.1157369421153; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 252))) { + if (LIKELY(false || (data[7].qvalue <= 50))) { + if (UNLIKELY(false || (data[0].qvalue <= 208))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 312.45787313138067; + } else { + result[0] += -10.665507478881368; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 562.9336214602067; + } else { + result[0] += 238.03704733164884; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 116))) { + if (LIKELY(false || (data[7].qvalue <= 126))) { + if (LIKELY(false || (data[0].qvalue <= 212))) { + result[0] += -279.2242547615078; + } else { + result[0] += 42.62552906704628; + } + } else { + result[0] += -677.2319392589053; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 152))) { + result[0] += -619.1669830813266; + } else { + result[0] += -1313.98491170941; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 76))) { + if (UNLIKELY(false || (data[0].qvalue <= 296))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 874.524930910277; + } else { + result[0] += 493.74627459103635; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 48))) { + if (UNLIKELY(false || (data[10].qvalue <= 32))) { + result[0] += 305.25905254947634; + } else { + result[0] += 893.8383559035191; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 330))) { + result[0] += 1017.5358679778622; + } else { + result[0] += 1243.0211544735387; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 330))) { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (UNLIKELY(false || (data[9].qvalue <= 40))) { + result[0] += -310.9808580488324; + } else { + result[0] += 277.1404180803976; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 68))) { + result[0] += -1191.3641199442363; + } else { + result[0] += -376.5469216582162; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[7].qvalue <= 134))) { + result[0] += 906.2480551197423; + } else { + result[0] += 554.4928258443013; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 424))) { + result[0] += -334.1159618884123; + } else { + result[0] += 515.865642037843; + } + } + } + } + } + } + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (UNLIKELY(false || (data[3].qvalue <= 44))) { + if (LIKELY(false || (data[7].qvalue <= 24))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[2].qvalue <= 128))) { + result[0] += 200.2716866984727; + } else { + result[0] += -33.31807134567322; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 34))) { + if (LIKELY(false || (data[2].qvalue <= 124))) { + result[0] += 2.1267132045491657; + } else { + result[0] += -376.52573890556465; + } + } else { + result[0] += 146.5594659396158; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 84))) { + if (UNLIKELY(false || (data[6].qvalue <= 40))) { + if (LIKELY(false || (data[2].qvalue <= 88))) { + result[0] += -78.39211587508055; + } else { + result[0] += -211.1780949469055; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 94))) { + result[0] += -50.54335036012877; + } else { + result[0] += 80.00020842727285; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 40))) { + result[0] += -483.63985186717866; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 56))) { + result[0] += 31.9766310228433; + } else { + result[0] += -151.25515710703033; + } + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 152))) { + if (LIKELY(false || (data[10].qvalue <= 108))) { + if (UNLIKELY(false || (data[8].qvalue <= 20))) { + if (UNLIKELY(false || (data[8].qvalue <= 0))) { + result[0] += 111.60897433537673; + } else { + result[0] += -38.459174422337334; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 152))) { + result[0] += 39.893177773342615; + } else { + result[0] += 117.1746253290821; + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 40))) { + if (UNLIKELY(false || (data[3].qvalue <= 58))) { + result[0] += -269.8313885970149; + } else { + result[0] += -44.81099090001982; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 50))) { + result[0] += 44.36810505858426; + } else { + result[0] += -17.031285003457658; + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 66))) { + result[0] += 29.134840065248483; + } else { + result[0] += -142.57181028298447; + } + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 100))) { + if (LIKELY(false || (data[7].qvalue <= 196))) { + if (UNLIKELY(false || (data[4].qvalue <= 52))) { + result[0] += -122.23715116402353; + } else { + if (UNLIKELY(false || (data[8].qvalue <= 124))) { + result[0] += 97.82653352215696; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 88))) { + result[0] += 19.540553416535854; + } else { + result[0] += -98.26047713895753; + } + } + } + } else { + result[0] += -208.9897177114526; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += -9.013216655506389; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 154))) { + result[0] += -171.41131706373193; + } else { + if (LIKELY(false || (data[7].qvalue <= 188))) { + result[0] += -228.5813161018793; + } else { + result[0] += -325.7003509988099; + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 196))) { + if (LIKELY(false || (data[0].qvalue <= 114))) { + if (LIKELY(false || (data[0].qvalue <= 60))) { + if (UNLIKELY(false || (data[0].qvalue <= 16))) { + result[0] += -1431.9188559535999; + } else { + result[0] += -1190.2844870798897; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 82))) { + result[0] += -829.9317534554965; + } else { + result[0] += -1180.4578431880107; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 94))) { + if (LIKELY(false || (data[0].qvalue <= 166))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += -258.2731590553095; + } else { + result[0] += -523.7930385002326; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 24))) { + result[0] += 148.2458712176789; + } else { + result[0] += -208.97850691995546; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 152))) { + result[0] += -766.4846303120403; + } else { + result[0] += -1387.2597830966474; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 282))) { + if (LIKELY(false || (data[7].qvalue <= 82))) { + if (LIKELY(false || (data[6].qvalue <= 54))) { + if (UNLIKELY(false || (data[0].qvalue <= 234))) { + result[0] += 347.56940876633854; + } else { + result[0] += 659.3973774082955; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 48))) { + result[0] += -299.8000443073724; + } else { + result[0] += 213.67905068511027; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + result[0] += -241.4835022721958; + } else { + result[0] += -964.8334869254049; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 96))) { + if (LIKELY(false || (data[7].qvalue <= 50))) { + if (UNLIKELY(false || (data[0].qvalue <= 326))) { + if (LIKELY(false || (data[6].qvalue <= 54))) { + result[0] += 1024.8241421866958; + } else { + result[0] += 691.4425294017764; + } + } else { + result[0] += 1134.476489243314; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 348))) { + if (LIKELY(false || (data[7].qvalue <= 178))) { + result[0] += 607.890368257065; + } else { + result[0] += -428.4659382893519; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 414))) { + result[0] += 1014.748894801393; + } else { + result[0] += 597.6335980398155; + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 156))) { + if (UNLIKELY(false || (data[0].qvalue <= 366))) { + if (LIKELY(false || (data[7].qvalue <= 102))) { + result[0] += 342.26282113720123; + } else { + result[0] += -230.30740062057927; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 104))) { + result[0] += 813.8543527953774; + } else { + result[0] += 415.6624160363879; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 446))) { + if (LIKELY(false || (data[6].qvalue <= 174))) { + result[0] += -259.7429923675005; + } else { + result[0] += -1426.7951683026927; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 174))) { + result[0] += 762.73962100054; + } else { + result[0] += 231.27954235732147; + } + } + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 152))) { + if (UNLIKELY(false || (data[3].qvalue <= 44))) { + if (LIKELY(false || (data[7].qvalue <= 24))) { + if (UNLIKELY(false || (data[7].qvalue <= 2))) { + if (UNLIKELY(false || (data[2].qvalue <= 10))) { + result[0] += 301.84658146708784; + } else { + result[0] += 144.51444115717683; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 34))) { + if (UNLIKELY(false || (data[3].qvalue <= 0))) { + result[0] += -133.3520185035297; + } else { + result[0] += 7.825198511802189; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 186.55141223016244; + } else { + result[0] += 66.07816401815487; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 84))) { + if (UNLIKELY(false || (data[9].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + result[0] += 190.81711934969553; + } else { + result[0] += -49.06287353594854; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 6))) { + result[0] += -190.1399731591398; + } else { + result[0] += -58.312564377257914; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 40))) { + result[0] += -486.8565320491534; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 122))) { + result[0] += -23.79376937838657; + } else { + result[0] += -193.64446213252586; + } + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (LIKELY(false || (data[10].qvalue <= 108))) { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + if (UNLIKELY(false || (data[10].qvalue <= 0))) { + result[0] += 154.980784796019; + } else { + result[0] += -70.70779749773081; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 166))) { + result[0] += 42.70546399312704; + } else { + result[0] += 231.67165411885554; + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 40))) { + if (UNLIKELY(false || (data[3].qvalue <= 58))) { + result[0] += -276.0507296705326; + } else { + result[0] += -48.24184527320793; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 50))) { + result[0] += 46.29969181016952; + } else { + result[0] += -22.46817209136037; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 196))) { + if (LIKELY(false || (data[2].qvalue <= 184))) { + if (UNLIKELY(false || (data[9].qvalue <= 70))) { + result[0] += 259.7266642867935; + } else { + result[0] += -33.9062832423356; + } + } else { + result[0] += -130.08036962157004; + } + } else { + result[0] += -206.5135454754138; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 188))) { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 21.01496012390576; + } else { + if (LIKELY(false || (data[9].qvalue <= 16))) { + if (LIKELY(false || (data[9].qvalue <= 2))) { + result[0] += -166.20109103415467; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 22))) { + result[0] += 43.76493225425393; + } else { + result[0] += -110.8984323288921; + } + } + } else { + result[0] += -265.77242681150005; + } + } + } else { + result[0] += -316.46151789497696; + } + } + if (LIKELY(false || (data[1].qvalue <= 152))) { + if (UNLIKELY(false || (data[3].qvalue <= 44))) { + if (LIKELY(false || (data[7].qvalue <= 42))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[2].qvalue <= 128))) { + result[0] += 196.29561739518834; + } else { + result[0] += -31.524131824672523; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 154))) { + if (UNLIKELY(false || (data[4].qvalue <= 6))) { + result[0] += 101.91406015487678; + } else { + result[0] += 1.3921745492571436; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 20))) { + result[0] += -150.8055895533361; + } else { + result[0] += 146.0669399096082; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 38))) { + if (LIKELY(false || (data[7].qvalue <= 122))) { + if (LIKELY(false || (data[4].qvalue <= 76))) { + result[0] += -101.79580164134511; + } else { + result[0] += 51.3302149880617; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 108))) { + result[0] += -334.80985020089327; + } else { + result[0] += -145.88767910265673; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 82))) { + result[0] += -560.2505700484534; + } else { + result[0] += -213.96025392317247; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (LIKELY(false || (data[10].qvalue <= 96))) { + if (UNLIKELY(false || (data[8].qvalue <= 20))) { + if (UNLIKELY(false || (data[8].qvalue <= 0))) { + result[0] += 105.68674793264172; + } else { + result[0] += -43.57721621182288; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -147.9863609603396; + } else { + result[0] += 50.11914192647265; + } + } + } else { + if (LIKELY(false || (data[8].qvalue <= 138))) { + if (LIKELY(false || (data[3].qvalue <= 120))) { + result[0] += -21.50623989909495; + } else { + result[0] += 36.087102588326; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 172))) { + result[0] += -401.56511373374224; + } else { + result[0] += -47.54506742735205; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 196))) { + if (LIKELY(false || (data[10].qvalue <= 136))) { + if (LIKELY(false || (data[4].qvalue <= 70))) { + result[0] += -58.788243146060125; + } else { + result[0] += 35.01063889842818; + } + } else { + result[0] += -167.15525202440935; + } + } else { + result[0] += -185.86643283746199; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 188))) { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 18.914144121003567; + } else { + if (LIKELY(false || (data[9].qvalue <= 16))) { + if (LIKELY(false || (data[9].qvalue <= 2))) { + result[0] += -149.58318971768668; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 22))) { + result[0] += 39.39279508530201; + } else { + result[0] += -99.81036974381612; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 28))) { + result[0] += -574.5809531834742; + } else { + result[0] += -223.51903615680277; + } + } + } + } else { + result[0] += -284.8251127560464; + } + } + if (UNLIKELY(false || (data[0].qvalue <= 178))) { + if (UNLIKELY(false || (data[0].qvalue <= 84))) { + if (UNLIKELY(false || (data[0].qvalue <= 34))) { + result[0] += -1224.3672336316197; + } else { + if (LIKELY(false || (data[6].qvalue <= 62))) { + result[0] += -890.0738935144229; + } else { + result[0] += -1083.6868213970283; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 88))) { + if (LIKELY(false || (data[0].qvalue <= 132))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += -449.253175915189; + } else { + result[0] += -666.9058347442864; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 36))) { + result[0] += -171.0454878027209; + } else { + result[0] += -404.9936499215746; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 108))) { + result[0] += -738.8736900979811; + } else { + result[0] += -1156.8338657457468; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 246))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 376.54293691682807; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 204))) { + result[0] += -19.164395129970544; + } else { + result[0] += 229.71203236622483; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 116))) { + if (LIKELY(false || (data[7].qvalue <= 92))) { + result[0] += -93.71602815702187; + } else { + result[0] += -442.3743373003131; + } + } else { + result[0] += -794.9240557679232; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 96))) { + if (UNLIKELY(false || (data[0].qvalue <= 294))) { + if (LIKELY(false || (data[7].qvalue <= 44))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 776.7722682804047; + } else { + result[0] += 551.7601378282178; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 126))) { + result[0] += 359.00317634305014; + } else { + result[0] += -261.0799150747045; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 54))) { + if (UNLIKELY(false || (data[0].qvalue <= 340))) { + result[0] += 868.4127360515845; + } else { + result[0] += 1030.2866556923334; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 174))) { + result[0] += 759.8335007432447; + } else { + result[0] += 375.57847778528725; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 362))) { + if (LIKELY(false || (data[1].qvalue <= 124))) { + if (LIKELY(false || (data[7].qvalue <= 152))) { + result[0] += 192.76419647400402; + } else { + result[0] += -627.210504284817; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 122))) { + result[0] += -248.81615213762672; + } else { + result[0] += -969.1369200758126; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 152))) { + if (LIKELY(false || (data[1].qvalue <= 124))) { + result[0] += 773.3204928636587; + } else { + result[0] += 406.4377394893374; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 448))) { + result[0] += -133.64446813672; + } else { + result[0] += 531.8523379173158; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 198))) { + if (LIKELY(false || (data[0].qvalue <= 116))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + if (UNLIKELY(false || (data[0].qvalue <= 14))) { + result[0] += -1181.6504962663746; + } else { + result[0] += -963.5370227469084; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 70))) { + result[0] += -638.280415142769; + } else { + result[0] += -906.2068637569266; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 78))) { + if (UNLIKELY(false || (data[0].qvalue <= 160))) { + result[0] += -355.3777345047579; + } else { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 38.63569197354808; + } else { + result[0] += -203.4298594886395; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 106))) { + result[0] += -501.25518228442854; + } else { + result[0] += -904.0956495312481; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 284))) { + if (LIKELY(false || (data[6].qvalue <= 74))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + if (LIKELY(false || (data[0].qvalue <= 242))) { + result[0] += 325.0351233983085; + } else { + result[0] += 574.7980032330352; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 240))) { + result[0] += 50.603122386774714; + } else { + result[0] += 307.4265167122256; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 124))) { + if (LIKELY(false || (data[2].qvalue <= 200))) { + result[0] += -75.63749275285782; + } else { + result[0] += -878.8670678802445; + } + } else { + result[0] += -784.825049044781; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 108))) { + if (LIKELY(false || (data[1].qvalue <= 82))) { + if (LIKELY(false || (data[9].qvalue <= 128))) { + if (LIKELY(false || (data[0].qvalue <= 354))) { + result[0] += 768.9030375927771; + } else { + result[0] += 982.7632961026947; + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 26))) { + result[0] += 881.7519427243232; + } else { + result[0] += 460.8225104674566; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 352))) { + result[0] += 336.6542893696312; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 12))) { + result[0] += 232.48634402594894; + } else { + result[0] += 754.2862591795262; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 434))) { + if (LIKELY(false || (data[1].qvalue <= 138))) { + if (LIKELY(false || (data[10].qvalue <= 116))) { + result[0] += 486.1115775722369; + } else { + result[0] += -240.0215539033738; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 152))) { + result[0] += -340.1982716887718; + } else { + result[0] += -1142.2235138961084; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 174))) { + if (UNLIKELY(false || (data[2].qvalue <= 106))) { + result[0] += 356.11751909622217; + } else { + result[0] += 708.5483451506925; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + result[0] += -425.3011321722016; + } else { + result[0] += 737.8486431169086; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 176))) { + if (UNLIKELY(false || (data[0].qvalue <= 82))) { + if (UNLIKELY(false || (data[0].qvalue <= 30))) { + result[0] += -1008.2347845448372; + } else { + if (LIKELY(false || (data[6].qvalue <= 92))) { + result[0] += -759.2605409681978; + } else { + result[0] += -1002.0185264155406; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 90))) { + if (UNLIKELY(false || (data[0].qvalue <= 122))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += -430.8085418741898; + } else { + result[0] += -615.9206678101054; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += -160.1037858692929; + } else { + result[0] += -361.25591627953935; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + result[0] += -677.2268849569755; + } else { + result[0] += -1084.1504346623935; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 256))) { + if (LIKELY(false || (data[7].qvalue <= 48))) { + if (LIKELY(false || (data[0].qvalue <= 216))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 219.18790221063765; + } else { + result[0] += 8.224416225928314; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 473.5370228126004; + } else { + result[0] += 241.42467866161678; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 126))) { + if (LIKELY(false || (data[6].qvalue <= 100))) { + result[0] += -37.498571958273004; + } else { + result[0] += -365.5446484967875; + } + } else { + result[0] += -627.0927668810837; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 88))) { + if (UNLIKELY(false || (data[0].qvalue <= 306))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 703.8669257065242; + } else { + result[0] += 489.86267404805426; + } + } else { + result[0] += 234.87338838535948; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 64))) { + if (LIKELY(false || (data[0].qvalue <= 356))) { + result[0] += 717.6793071720917; + } else { + result[0] += 869.7251021367146; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 6))) { + result[0] += 129.6795962020335; + } else { + result[0] += 601.4460117834096; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 374))) { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[7].qvalue <= 176))) { + result[0] += 214.20152575675183; + } else { + result[0] += -537.860116518967; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 24))) { + result[0] += 39.286767186140054; + } else { + result[0] += -860.9173103201238; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 156))) { + if (UNLIKELY(false || (data[6].qvalue <= 36))) { + result[0] += -207.54940006849526; + } else { + result[0] += 555.8581322313454; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 448))) { + result[0] += -233.03910512550647; + } else { + result[0] += 411.0057503246483; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 200))) { + if (UNLIKELY(false || (data[0].qvalue <= 102))) { + if (UNLIKELY(false || (data[0].qvalue <= 42))) { + result[0] += -879.2505320646146; + } else { + if (LIKELY(false || (data[6].qvalue <= 100))) { + result[0] += -613.8765129117388; + } else { + result[0] += -898.2402256092773; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[0].qvalue <= 152))) { + result[0] += -310.0254085675876; + } else { + result[0] += -23.926126961634566; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (UNLIKELY(false || (data[0].qvalue <= 150))) { + result[0] += -532.7012177870323; + } else { + result[0] += -303.0535459955343; + } + } else { + result[0] += -885.9999951852365; + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 96))) { + if (UNLIKELY(false || (data[0].qvalue <= 288))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[0].qvalue <= 236))) { + result[0] += 215.01429644416353; + } else { + result[0] += 421.53246886972823; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 48))) { + result[0] += -286.0785455584352; + } else { + result[0] += 116.1256792417381; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 120))) { + if (LIKELY(false || (data[8].qvalue <= 118))) { + if (LIKELY(false || (data[0].qvalue <= 414))) { + result[0] += 706.4179843328923; + } else { + result[0] += 374.35058115512874; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 28))) { + result[0] += -23.434409262940505; + } else { + result[0] += 567.1115038047287; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 386))) { + if (LIKELY(false || (data[8].qvalue <= 18))) { + result[0] += 273.6642642670474; + } else { + result[0] += -1046.2285662989975; + } + } else { + result[0] += 487.25359122330957; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 354))) { + if (LIKELY(false || (data[6].qvalue <= 152))) { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (LIKELY(false || (data[4].qvalue <= 98))) { + result[0] += 86.77607178167547; + } else { + result[0] += -327.9227215464309; + } + } else { + result[0] += -702.536120284601; + } + } else { + result[0] += -816.76285821889; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 168))) { + if (LIKELY(false || (data[4].qvalue <= 104))) { + if (LIKELY(false || (data[2].qvalue <= 202))) { + result[0] += 585.7714665368296; + } else { + result[0] += 211.97898615341128; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 446))) { + result[0] += 45.872711470194794; + } else { + result[0] += 596.7727695600357; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 460))) { + if (LIKELY(false || (data[6].qvalue <= 174))) { + result[0] += -110.3734753492252; + } else { + result[0] += -993.643156799906; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 134))) { + result[0] += 513.9132377712247; + } else { + result[0] += -156.94258247387484; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 174))) { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + if (UNLIKELY(false || (data[0].qvalue <= 28))) { + result[0] += -825.7182786124127; + } else { + if (LIKELY(false || (data[1].qvalue <= 78))) { + result[0] += -620.5383438315085; + } else { + result[0] += -812.4844205870289; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 78))) { + if (UNLIKELY(false || (data[0].qvalue <= 124))) { + result[0] += -409.88504033635036; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += -72.60757573397173; + } else { + result[0] += -250.66315233138621; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 32))) { + result[0] += -830.1444150421403; + } else { + result[0] += -512.6141494941938; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 244))) { + if (LIKELY(false || (data[7].qvalue <= 48))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + if (UNLIKELY(false || (data[10].qvalue <= 8))) { + result[0] += 654.5140923867807; + } else { + result[0] += 216.223797321274; + } + } else { + result[0] += 59.127335346599445; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 42))) { + if (LIKELY(false || (data[7].qvalue <= 120))) { + result[0] += -408.9401043985653; + } else { + result[0] += -900.5928970741843; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + result[0] += -88.4273489833162; + } else { + result[0] += -494.5124549516256; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 126))) { + if (UNLIKELY(false || (data[0].qvalue <= 316))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 577.7159751120596; + } else { + result[0] += 390.25605025593114; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 100))) { + result[0] += 225.37808240171967; + } else { + result[0] += -134.1896384710644; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 40))) { + if (UNLIKELY(false || (data[0].qvalue <= 380))) { + result[0] += 26.38536976794332; + } else { + result[0] += 404.9857810684197; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 206))) { + result[0] += 640.5372498970368; + } else { + result[0] += 146.7850504947837; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 398))) { + if (UNLIKELY(false || (data[9].qvalue <= 46))) { + if (UNLIKELY(false || (data[2].qvalue <= 32))) { + result[0] += -69.72606543209433; + } else { + result[0] += -890.4547484128167; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 194))) { + result[0] += 43.29913016336363; + } else { + result[0] += -1135.7449808644187; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 156))) { + if (UNLIKELY(false || (data[4].qvalue <= 72))) { + result[0] += 619.8183215898755; + } else { + result[0] += 285.5489715529262; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 448))) { + result[0] += -358.75757336334124; + } else { + result[0] += 304.16119148341835; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 202))) { + if (LIKELY(false || (data[0].qvalue <= 118))) { + if (LIKELY(false || (data[0].qvalue <= 66))) { + if (UNLIKELY(false || (data[0].qvalue <= 10))) { + result[0] += -817.0480700539837; + } else { + result[0] += -635.8069750267712; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + result[0] += -345.96669659122944; + } else { + result[0] += -534.0482572373161; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 90))) { + if (LIKELY(false || (data[0].qvalue <= 170))) { + result[0] += -211.68858516839794; + } else { + if (LIKELY(false || (data[6].qvalue <= 50))) { + result[0] += 62.76650514311201; + } else { + result[0] += -136.44892442998687; + } + } + } else { + result[0] += -491.2641598575405; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + if (UNLIKELY(false || (data[0].qvalue <= 292))) { + if (LIKELY(false || (data[7].qvalue <= 48))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + if (LIKELY(false || (data[6].qvalue <= 32))) { + result[0] += 331.57071848199166; + } else { + result[0] += 679.3219985689888; + } + } else { + result[0] += 218.00776335345145; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 42))) { + result[0] += -277.030356546739; + } else { + result[0] += 67.28341346905603; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 40))) { + if (LIKELY(false || (data[0].qvalue <= 428))) { + if (LIKELY(false || (data[6].qvalue <= 134))) { + result[0] += 324.7275088735298; + } else { + result[0] += -419.6036244916827; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 90))) { + result[0] += 170.7438608997793; + } else { + result[0] += 713.0065984964028; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (LIKELY(false || (data[0].qvalue <= 356))) { + result[0] += 471.87466419027; + } else { + result[0] += 637.1788630246149; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 408))) { + result[0] += -529.7467726888689; + } else { + result[0] += 412.8057422873779; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 404))) { + if (UNLIKELY(false || (data[9].qvalue <= 46))) { + result[0] += -712.0507049531527; + } else { + if (LIKELY(false || (data[7].qvalue <= 194))) { + if (LIKELY(false || (data[2].qvalue <= 202))) { + result[0] += 36.347398334304295; + } else { + result[0] += -675.8496819191378; + } + } else { + result[0] += -1026.391767021813; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + if (LIKELY(false || (data[6].qvalue <= 174))) { + if (LIKELY(false || (data[2].qvalue <= 218))) { + result[0] += 300.59738999141763; + } else { + result[0] += -768.8823600737215; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 462))) { + result[0] += -815.3971702532364; + } else { + result[0] += -98.98096938280344; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 188))) { + result[0] += 772.6721143780254; + } else { + result[0] += 184.03196143296145; + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 204))) { + if (UNLIKELY(false || (data[0].qvalue <= 106))) { + if (LIKELY(false || (data[0].qvalue <= 52))) { + result[0] += -628.1023483072281; + } else { + if (LIKELY(false || (data[7].qvalue <= 90))) { + result[0] += -417.2006348985446; + } else { + result[0] += -652.72384554584; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 78))) { + if (UNLIKELY(false || (data[0].qvalue <= 150))) { + result[0] += -244.5303558275764; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += 81.86220471198173; + } else { + result[0] += -93.51539141150997; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 42))) { + result[0] += -604.2798075362174; + } else { + result[0] += -312.7206975646505; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 76))) { + if (UNLIKELY(false || (data[0].qvalue <= 280))) { + if (UNLIKELY(false || (data[7].qvalue <= 30))) { + if (UNLIKELY(false || (data[7].qvalue <= 2))) { + result[0] += 807.0167727723248; + } else { + result[0] += 289.0442076002897; + } + } else { + result[0] += 124.5150431079561; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 78))) { + if (UNLIKELY(false || (data[0].qvalue <= 330))) { + if (UNLIKELY(false || (data[7].qvalue <= 30))) { + result[0] += 565.5660231034293; + } else { + result[0] += 342.6451209197193; + } + } else { + result[0] += 592.3830913005597; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 6))) { + if (LIKELY(false || (data[0].qvalue <= 444))) { + result[0] += -430.7701175762352; + } else { + result[0] += 555.2205548100666; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 356))) { + result[0] += 220.55563498286244; + } else { + result[0] += 609.8515804605789; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 364))) { + if (UNLIKELY(false || (data[9].qvalue <= 40))) { + if (LIKELY(false || (data[7].qvalue <= 112))) { + result[0] += -186.288076755465; + } else { + result[0] += -654.6528922882809; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (LIKELY(false || (data[7].qvalue <= 180))) { + result[0] += 134.73234216388812; + } else { + result[0] += -411.6205521150166; + } + } else { + result[0] += -601.6441239663638; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 152))) { + if (LIKELY(false || (data[2].qvalue <= 214))) { + if (UNLIKELY(false || (data[8].qvalue <= 48))) { + result[0] += 159.54229602414563; + } else { + result[0] += 399.7957684439738; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -623.3887244049713; + } else { + result[0] += 412.91921366956416; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 448))) { + if (LIKELY(false || (data[9].qvalue <= 18))) { + result[0] += -725.573342147161; + } else { + result[0] += 232.5245626809719; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 62))) { + result[0] += 781.8973840507092; + } else { + result[0] += 102.27963999730173; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 206))) { + if (LIKELY(false || (data[0].qvalue <= 118))) { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += -541.6072754781043; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 40))) { + result[0] += -266.3645156358689; + } else { + result[0] += -438.26985938213795; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 90))) { + if (LIKELY(false || (data[0].qvalue <= 170))) { + result[0] += -171.34773263938322; + } else { + result[0] += -11.303977068049283; + } + } else { + result[0] += -407.71415355827446; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 126))) { + if (UNLIKELY(false || (data[0].qvalue <= 298))) { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (UNLIKELY(false || (data[7].qvalue <= 16))) { + if (LIKELY(false || (data[5].qvalue <= 46))) { + result[0] += 305.49013378917147; + } else { + result[0] += 766.8660544561868; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 12))) { + result[0] += -212.69432422518977; + } else { + result[0] += 203.45215339752818; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 100))) { + if (UNLIKELY(false || (data[2].qvalue <= 6))) { + result[0] += -1306.979742409561; + } else { + result[0] += 84.56250938982618; + } + } else { + result[0] += -296.8758971391223; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 92))) { + if (LIKELY(false || (data[2].qvalue <= 212))) { + if (UNLIKELY(false || (data[7].qvalue <= 16))) { + result[0] += 634.2960749274997; + } else { + result[0] += 446.27587321034673; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 442))) { + result[0] += -656.3271831376737; + } else { + result[0] += 649.7533136866848; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 346))) { + if (LIKELY(false || (data[6].qvalue <= 100))) { + result[0] += 272.18984613419735; + } else { + result[0] += -183.6208158354411; + } + } else { + result[0] += 335.6468220356205; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 418))) { + if (LIKELY(false || (data[1].qvalue <= 136))) { + if (LIKELY(false || (data[2].qvalue <= 190))) { + if (LIKELY(false || (data[7].qvalue <= 194))) { + result[0] += 155.81121294253342; + } else { + result[0] += -775.8409014979492; + } + } else { + result[0] += -546.6139529208316; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 279.2996966849545; + } else { + result[0] += -746.3491117424761; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 174))) { + if (UNLIKELY(false || (data[0].qvalue <= 444))) { + if (UNLIKELY(false || (data[2].qvalue <= 76))) { + result[0] += -794.9146466173518; + } else { + result[0] += 238.59142556484136; + } + } else { + result[0] += 427.50310495259396; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + if (LIKELY(false || (data[4].qvalue <= 112))) { + result[0] += -91.71744859578774; + } else { + result[0] += -899.7782100028055; + } + } else { + result[0] += 481.37569784189236; + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 182))) { + if (UNLIKELY(false || (data[0].qvalue <= 90))) { + if (UNLIKELY(false || (data[0].qvalue <= 26))) { + result[0] += -559.2465183228969; + } else { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + result[0] += -339.48268673463207; + } else { + result[0] += -477.7038556835741; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 100))) { + if (LIKELY(false || (data[0].qvalue <= 142))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += -173.83777524062293; + } else { + result[0] += -308.64588699940225; + } + } else { + result[0] += -87.70355551216173; + } + } else { + result[0] += -460.0791599316315; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 264))) { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[6].qvalue <= 46))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + if (UNLIKELY(false || (data[4].qvalue <= 2))) { + result[0] += 519.5076559696931; + } else { + result[0] += 36.579262885789255; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 8))) { + result[0] += 850.2335034126149; + } else { + result[0] += 225.76397079413547; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + result[0] += 31.634082363361337; + } else { + result[0] += -491.7231693795383; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 134))) { + result[0] += -143.97621323045203; + } else { + result[0] += -587.7222010882514; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[9].qvalue <= 128))) { + if (LIKELY(false || (data[0].qvalue <= 328))) { + result[0] += 341.70375333102265; + } else { + result[0] += 492.9064526262677; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 32))) { + result[0] += 291.11846627413223; + } else { + result[0] += -67.70442593342548; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 336))) { + if (UNLIKELY(false || (data[9].qvalue <= 42))) { + result[0] += -178.5440203148846; + } else { + result[0] += 126.46282552428471; + } + } else { + if (LIKELY(false || (data[10].qvalue <= 76))) { + result[0] += 222.41058195803035; + } else { + result[0] += 434.73453399120257; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 426))) { + if (LIKELY(false || (data[9].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 416))) { + result[0] += -640.7399976249752; + } else { + result[0] += -167.72938945515875; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 46))) { + result[0] += -1344.5726371900926; + } else { + result[0] += 230.06133289043484; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 168))) { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -84.2627136369062; + } else { + result[0] += 415.7428703783992; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 460))) { + result[0] += -384.29322175155045; + } else { + result[0] += 296.1675101952608; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 214))) { + if (LIKELY(false || (data[0].qvalue <= 122))) { + if (UNLIKELY(false || (data[0].qvalue <= 46))) { + result[0] += -470.73918641723026; + } else { + if (LIKELY(false || (data[7].qvalue <= 90))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += -198.32164705298905; + } else { + result[0] += -321.39079912240294; + } + } else { + result[0] += -498.8080870646863; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 44))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 317.2121545851497; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 172))) { + result[0] += -88.36582704181914; + } else { + result[0] += 46.798653356184104; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 126))) { + if (LIKELY(false || (data[1].qvalue <= 92))) { + result[0] += -119.53396623757338; + } else { + result[0] += -262.3878864323929; + } + } else { + result[0] += -497.48244794654096; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + if (UNLIKELY(false || (data[0].qvalue <= 304))) { + if (LIKELY(false || (data[7].qvalue <= 44))) { + if (UNLIKELY(false || (data[7].qvalue <= 2))) { + result[0] += 746.0045878549049; + } else { + if (LIKELY(false || (data[10].qvalue <= 124))) { + result[0] += 255.0791650107591; + } else { + result[0] += -320.7492571200465; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 90))) { + result[0] += 96.84399754507324; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 18))) { + result[0] += -880.8032147114733; + } else { + result[0] += -73.20637853492896; + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 38))) { + result[0] += 461.5993365359995; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 358))) { + if (LIKELY(false || (data[2].qvalue <= 180))) { + result[0] += 212.73235839758036; + } else { + result[0] += -154.54318766724592; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 124))) { + result[0] += 395.0997024493214; + } else { + result[0] += 210.88997718544783; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 404))) { + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (LIKELY(false || (data[10].qvalue <= 112))) { + if (UNLIKELY(false || (data[5].qvalue <= 50))) { + result[0] += -969.6328168630031; + } else { + result[0] += -86.652192967571; + } + } else { + result[0] += -593.8688924415013; + } + } else { + result[0] += -859.0755121136438; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[5].qvalue <= 120))) { + if (LIKELY(false || (data[2].qvalue <= 222))) { + result[0] += 179.3501661826762; + } else { + result[0] += -755.4195928241176; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -1101.7895944684535; + } else { + result[0] += -169.1089745696061; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 132))) { + result[0] += 735.5941243655279; + } else { + result[0] += -9.689280563857821; + } + } + } + } + } + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + if (LIKELY(false || (data[7].qvalue <= 16))) { + if (UNLIKELY(false || (data[7].qvalue <= 2))) { + result[0] += 241.07465429893853; + } else { + if (UNLIKELY(false || (data[5].qvalue <= 2))) { + result[0] += -167.05234881826266; + } else { + if (LIKELY(false || (data[9].qvalue <= 160))) { + result[0] += 23.635093021555477; + } else { + result[0] += 247.95569232139462; + } + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 24))) { + if (UNLIKELY(false || (data[5].qvalue <= 26))) { + result[0] += -11.35953345193686; + } else { + if (LIKELY(false || (data[8].qvalue <= 46))) { + result[0] += -241.87320597288937; + } else { + result[0] += -558.2222728620737; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 22))) { + result[0] += -717.6843774931726; + } else { + if (UNLIKELY(false || (data[5].qvalue <= 18))) { + result[0] += -119.9905475497901; + } else { + result[0] += 12.781811278483842; + } + } + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 54))) { + if (UNLIKELY(false || (data[5].qvalue <= 78))) { + if (UNLIKELY(false || (data[8].qvalue <= 68))) { + if (UNLIKELY(false || (data[8].qvalue <= 22))) { + result[0] += -383.60019278447663; + } else { + result[0] += 70.39617109267411; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 62))) { + result[0] += 14.894390887322576; + } else { + result[0] += -246.88224072530934; + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 134))) { + if (LIKELY(false || (data[9].qvalue <= 32))) { + result[0] += -26.468777991984183; + } else { + result[0] += 124.62962202584231; + } + } else { + result[0] += -125.32103441709326; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 98))) { + if (UNLIKELY(false || (data[6].qvalue <= 50))) { + if (LIKELY(false || (data[8].qvalue <= 120))) { + result[0] += 79.94063628810217; + } else { + result[0] += -99.91998726218483; + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 54))) { + result[0] += -102.42726831173952; + } else { + result[0] += 19.419260663393466; + } + } + } else { + if (LIKELY(false || (data[8].qvalue <= 120))) { + if (LIKELY(false || (data[10].qvalue <= 90))) { + result[0] += 179.7748013112496; + } else { + result[0] += -7.882776932047753; + } + } else { + result[0] += 448.60994004642566; + } + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 134))) { + if (LIKELY(false || (data[7].qvalue <= 198))) { + if (LIKELY(false || (data[3].qvalue <= 168))) { + if (LIKELY(false || (data[6].qvalue <= 178))) { + result[0] += -30.950902106565053; + } else { + result[0] += 476.7456995983057; + } + } else { + result[0] += -198.82706671026182; + } + } else { + result[0] += -249.8175053383445; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 188))) { + if (UNLIKELY(false || (data[3].qvalue <= 98))) { + result[0] += -378.6732367385603; + } else { + result[0] += -109.64341915005078; + } + } else { + result[0] += -425.2420138470435; + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 220))) { + if (LIKELY(false || (data[0].qvalue <= 134))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + if (UNLIKELY(false || (data[0].qvalue <= 8))) { + result[0] += -532.059203726686; + } else { + result[0] += -372.9090262225109; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 82))) { + result[0] += -209.07044382236805; + } else { + result[0] += -400.00349533828; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[7].qvalue <= 2))) { + result[0] += 412.17374136976844; + } else { + result[0] += -14.036003053192212; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 116))) { + result[0] += -154.44905243467502; + } else { + result[0] += -436.1506043761763; + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 116))) { + if (LIKELY(false || (data[0].qvalue <= 324))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (LIKELY(false || (data[0].qvalue <= 276))) { + result[0] += 180.81716708771435; + } else { + result[0] += 323.80885408996255; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 4))) { + result[0] += -804.687929563034; + } else { + if (LIKELY(false || (data[7].qvalue <= 144))) { + result[0] += 75.45041773548873; + } else { + result[0] += -308.6438664376922; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 198))) { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + if (UNLIKELY(false || (data[9].qvalue <= 60))) { + result[0] += -136.42148889369125; + } else { + result[0] += 347.35402497810156; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 40))) { + result[0] += 519.5328967780671; + } else { + result[0] += 317.09389087410756; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 442))) { + result[0] += -830.9681326018041; + } else { + result[0] += 776.5378702508172; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 392))) { + if (LIKELY(false || (data[1].qvalue <= 120))) { + if (UNLIKELY(false || (data[3].qvalue <= 96))) { + result[0] += -916.9602444683026; + } else { + if (LIKELY(false || (data[2].qvalue <= 190))) { + result[0] += 151.14410198089988; + } else { + result[0] += -534.6138410777346; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 64))) { + result[0] += -20.321297171216408; + } else { + result[0] += -553.6897184690705; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 152))) { + if (LIKELY(false || (data[2].qvalue <= 214))) { + if (UNLIKELY(false || (data[8].qvalue <= 44))) { + result[0] += -94.32615099423269; + } else { + result[0] += 326.8245357585306; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -617.350459951314; + } else { + result[0] += 276.127751759582; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 442))) { + result[0] += -733.0199493164838; + } else { + if (LIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -116.42395706265687; + } else { + result[0] += 335.32040559141655; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 172))) { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + if (UNLIKELY(false || (data[0].qvalue <= 6))) { + result[0] += -493.1717265491526; + } else { + if (LIKELY(false || (data[1].qvalue <= 72))) { + result[0] += -307.1327071737299; + } else { + result[0] += -442.19784021780106; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 116))) { + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[0].qvalue <= 138))) { + result[0] += -167.9842807798525; + } else { + result[0] += -70.31231390063265; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 120))) { + result[0] += -254.86498043371085; + } else { + result[0] += -703.766647232002; + } + } + } else { + result[0] += -458.17666670208604; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 240))) { + if (LIKELY(false || (data[6].qvalue <= 72))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 400.7108318320836; + } else { + if (LIKELY(false || (data[4].qvalue <= 128))) { + if (UNLIKELY(false || (data[4].qvalue <= 6))) { + result[0] += 227.72272060240076; + } else { + result[0] += 16.088229395762443; + } + } else { + result[0] += -679.3987618413844; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 146))) { + if (UNLIKELY(false || (data[3].qvalue <= 112))) { + result[0] += -287.0516773864561; + } else { + result[0] += -82.35735858878537; + } + } else { + result[0] += -473.44254046258266; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (UNLIKELY(false || (data[0].qvalue <= 326))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + result[0] += 167.85776493357912; + } else { + result[0] += 297.3702646687886; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 116))) { + result[0] += 67.52512843170207; + } else { + result[0] += -189.67754872059376; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 40))) { + if (UNLIKELY(false || (data[3].qvalue <= 36))) { + result[0] += 818.1555179158704; + } else { + result[0] += 130.45031708535106; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 454))) { + result[0] += 327.4166938133286; + } else { + result[0] += -120.66748291707741; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 428))) { + if (LIKELY(false || (data[9].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 418))) { + result[0] += -535.7497746001039; + } else { + result[0] += -120.65339683228727; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 46))) { + result[0] += -1201.76536866533; + } else { + result[0] += 160.46676494029293; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 152))) { + if (LIKELY(false || (data[2].qvalue <= 214))) { + result[0] += 379.9797915421976; + } else { + result[0] += -12.579853903701677; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 452))) { + result[0] += -539.1034312262643; + } else { + result[0] += 118.1495781414129; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 210))) { + if (UNLIKELY(false || (data[0].qvalue <= 96))) { + if (UNLIKELY(false || (data[0].qvalue <= 12))) { + result[0] += -414.82218364010805; + } else { + if (LIKELY(false || (data[6].qvalue <= 100))) { + result[0] += -261.7354787568685; + } else { + result[0] += -436.9782510571122; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 126))) { + if (LIKELY(false || (data[6].qvalue <= 50))) { + if (UNLIKELY(false || (data[0].qvalue <= 158))) { + result[0] += -86.99960309582809; + } else { + result[0] += 36.01621466834996; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 54))) { + result[0] += -276.15518055633515; + } else { + result[0] += -117.47322502980383; + } + } + } else { + result[0] += -418.57103843448124; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + if (UNLIKELY(false || (data[0].qvalue <= 300))) { + if (LIKELY(false || (data[6].qvalue <= 100))) { + if (UNLIKELY(false || (data[7].qvalue <= 24))) { + result[0] += 221.38591506553996; + } else { + if (UNLIKELY(false || (data[5].qvalue <= 34))) { + result[0] += -114.10943519348092; + } else { + result[0] += 124.41383507415142; + } + } + } else { + result[0] += -124.776312637069; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 40))) { + if (LIKELY(false || (data[0].qvalue <= 434))) { + if (LIKELY(false || (data[6].qvalue <= 134))) { + result[0] += 173.27180862569062; + } else { + result[0] += -230.0263780267813; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 90))) { + result[0] += 93.55822981381293; + } else { + result[0] += 531.9618157197915; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 138))) { + if (UNLIKELY(false || (data[7].qvalue <= 12))) { + result[0] += 529.342886411045; + } else { + result[0] += 275.38241535187143; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 32))) { + result[0] += 168.94637195832345; + } else { + result[0] += -664.1470891916572; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 442))) { + if (LIKELY(false || (data[6].qvalue <= 174))) { + if (LIKELY(false || (data[7].qvalue <= 198))) { + if (UNLIKELY(false || (data[9].qvalue <= 68))) { + result[0] += -259.076207652103; + } else { + result[0] += 97.97671807149003; + } + } else { + result[0] += -695.1606225313006; + } + } else { + result[0] += -796.0742621530271; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (UNLIKELY(false || (data[2].qvalue <= 104))) { + if (LIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -153.93531708038253; + } else { + result[0] += 448.69780310463796; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 216))) { + result[0] += 477.95650201824304; + } else { + result[0] += -28.61899971217077; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -909.6241555301945; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -188.47445082878266; + } else { + result[0] += 377.35719169796516; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 172))) { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + if (UNLIKELY(false || (data[0].qvalue <= 8))) { + result[0] += -390.84856797995377; + } else { + if (LIKELY(false || (data[1].qvalue <= 64))) { + result[0] += -240.40122514681448; + } else { + result[0] += -347.4550413344793; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + result[0] += -39.25816550113254; + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[0].qvalue <= 142))) { + result[0] += -186.1240171676131; + } else { + result[0] += -98.0111804997198; + } + } else { + result[0] += -436.0524831294065; + } + } + } else { + result[0] += -471.83682540539394; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 238))) { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 348.9273763963965; + } else { + if (LIKELY(false || (data[7].qvalue <= 104))) { + result[0] += 30.53430231649784; + } else { + result[0] += -202.87449384945464; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 120))) { + if (UNLIKELY(false || (data[2].qvalue <= 4))) { + result[0] += -836.9509277269852; + } else { + if (UNLIKELY(false || (data[6].qvalue <= 22))) { + result[0] += -1508.3799402707123; + } else { + result[0] += -94.06253649645048; + } + } + } else { + result[0] += -348.5371075782411; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 126))) { + if (LIKELY(false || (data[0].qvalue <= 346))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[0].qvalue <= 286))) { + result[0] += 162.75459965948465; + } else { + result[0] += 282.56625214886793; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 10))) { + result[0] += -440.06926455976475; + } else { + result[0] += 35.45780394100668; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 212))) { + if (UNLIKELY(false || (data[8].qvalue <= 72))) { + result[0] += 180.74606636101373; + } else { + result[0] += 317.0047465523858; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 438))) { + result[0] += -588.0578066297068; + } else { + result[0] += 433.5588979682989; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 422))) { + if (LIKELY(false || (data[1].qvalue <= 136))) { + if (LIKELY(false || (data[7].qvalue <= 192))) { + result[0] += 45.9325195881808; + } else { + result[0] += -679.3813065404768; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 270.29103457509785; + } else { + result[0] += -517.5531688785617; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 118))) { + if (UNLIKELY(false || (data[10].qvalue <= 56))) { + result[0] += 117.54636858837371; + } else { + result[0] += 647.912058999977; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -41.193603415175254; + } else { + result[0] += 296.9100448809772; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 222))) { + if (LIKELY(false || (data[0].qvalue <= 138))) { + if (UNLIKELY(false || (data[0].qvalue <= 58))) { + result[0] += -275.2115013121635; + } else { + if (LIKELY(false || (data[6].qvalue <= 116))) { + result[0] += -155.46609912259618; + } else { + result[0] += -389.7954080308127; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 100))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + result[0] += 384.7520448135499; + } else { + if (LIKELY(false || (data[9].qvalue <= 154))) { + if (UNLIKELY(false || (data[4].qvalue <= 6))) { + result[0] += 292.1491245847539; + } else { + result[0] += -30.543242192258454; + } + } else { + result[0] += -385.76320075157514; + } + } + } else { + result[0] += -224.14550590009833; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 68))) { + if (UNLIKELY(false || (data[0].qvalue <= 272))) { + result[0] += 106.3555671944755; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + if (UNLIKELY(false || (data[9].qvalue <= 66))) { + if (UNLIKELY(false || (data[0].qvalue <= 410))) { + result[0] += -941.4297952572747; + } else { + result[0] += 56.28649828645251; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 8))) { + result[0] += 507.91991997031147; + } else { + result[0] += 35.20700760987173; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 348))) { + if (UNLIKELY(false || (data[7].qvalue <= 24))) { + result[0] += 324.4895405944488; + } else { + result[0] += 153.59057096574398; + } + } else { + if (LIKELY(false || (data[5].qvalue <= 60))) { + result[0] += 236.3224976398137; + } else { + result[0] += 444.2538183675668; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 372))) { + if (UNLIKELY(false || (data[9].qvalue <= 42))) { + if (UNLIKELY(false || (data[10].qvalue <= 64))) { + result[0] += 22.100327800787746; + } else { + result[0] += -285.1797635732232; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 182))) { + if (UNLIKELY(false || (data[3].qvalue <= 12))) { + result[0] += -916.6873662194294; + } else { + result[0] += 129.80380277726957; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 208))) { + result[0] += -121.90878827484735; + } else { + result[0] += -647.3935446930476; + } + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + if (UNLIKELY(false || (data[0].qvalue <= 444))) { + if (LIKELY(false || (data[3].qvalue <= 168))) { + result[0] += -658.7332388174912; + } else { + result[0] += -100.44944033027124; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 174))) { + result[0] += 286.17922512478475; + } else { + result[0] += -166.4328133167238; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 94))) { + if (LIKELY(false || (data[3].qvalue <= 92))) { + result[0] += 56.97705669543027; + } else { + result[0] += -910.3064928974122; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 108))) { + result[0] += 459.24011774105895; + } else { + result[0] += 187.54878983259766; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 170))) { + if (UNLIKELY(false || (data[0].qvalue <= 80))) { + if (UNLIKELY(false || (data[11].qvalue <= 0))) { + if (UNLIKELY(false || (data[0].qvalue <= 18))) { + result[0] += -272.8015586187466; + } else { + result[0] += -158.01002915185765; + } + } else { + result[0] += -270.0109117909505; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + if (LIKELY(false || (data[5].qvalue <= 44))) { + result[0] += -47.655816285394785; + } else { + result[0] += 148.45176574086; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 48))) { + result[0] += -274.2183049595403; + } else { + result[0] += -120.42739359905393; + } + } + } else { + result[0] += -404.63297777523053; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 250))) { + if (LIKELY(false || (data[7].qvalue <= 86))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 310.3087236621802; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 6))) { + result[0] += -261.86875131184433; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 30))) { + result[0] += 77.33362724018633; + } else { + result[0] += -9.835991514809445; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 178))) { + if (LIKELY(false || (data[10].qvalue <= 94))) { + if (UNLIKELY(false || (data[3].qvalue <= 18))) { + result[0] += -491.7147359309727; + } else { + result[0] += -26.424686375898183; + } + } else { + result[0] += -256.5814333154503; + } + } else { + result[0] += -401.38775248670413; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + if (LIKELY(false || (data[0].qvalue <= 364))) { + if (UNLIKELY(false || (data[9].qvalue <= 54))) { + if (LIKELY(false || (data[8].qvalue <= 78))) { + result[0] += 24.807904435156793; + } else { + result[0] += -255.08075851432937; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 180))) { + result[0] += 171.3936836582958; + } else { + result[0] += -146.48986963094893; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + if (UNLIKELY(false || (data[0].qvalue <= 446))) { + result[0] += -527.9370629462896; + } else { + result[0] += 311.74412615306; + } + } else { + if (UNLIKELY(false || (data[11].qvalue <= 0))) { + result[0] += 61.275444153721516; + } else { + result[0] += 266.8647138392115; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 464))) { + if (LIKELY(false || (data[2].qvalue <= 218))) { + if (UNLIKELY(false || (data[9].qvalue <= 2))) { + result[0] += -553.7238254263838; + } else { + result[0] += 30.964493797225533; + } + } else { + result[0] += -1039.405250832381; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 132))) { + if (LIKELY(false || (data[0].qvalue <= 470))) { + result[0] += 221.3473424703826; + } else { + result[0] += 578.2036295192621; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -1067.1325237122935; + } else { + result[0] += -72.41418684700736; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 222))) { + if (LIKELY(false || (data[0].qvalue <= 120))) { + if (UNLIKELY(false || (data[0].qvalue <= 36))) { + result[0] += -245.63449384858325; + } else { + if (LIKELY(false || (data[1].qvalue <= 102))) { + result[0] += -141.07821937677804; + } else { + result[0] += -311.46095137887164; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 78))) { + result[0] += -18.828476188261668; + } else { + result[0] += -158.60479952768688; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[0].qvalue <= 334))) { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + if (UNLIKELY(false || (data[5].qvalue <= 28))) { + if (LIKELY(false || (data[2].qvalue <= 122))) { + result[0] += 85.17185179060556; + } else { + result[0] += -1488.3371391950336; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 120))) { + result[0] += 251.47188880049714; + } else { + result[0] += -337.59370375551794; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 176))) { + if (LIKELY(false || (data[7].qvalue <= 122))) { + result[0] += 64.67449717812703; + } else { + result[0] += -198.4096965608494; + } + } else { + result[0] += -109.54175072971603; + } + } + } else { + if (LIKELY(false || (data[5].qvalue <= 96))) { + if (UNLIKELY(false || (data[7].qvalue <= 64))) { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + result[0] += 25.027805981227203; + } else { + result[0] += 254.11335581266104; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 22))) { + result[0] += -327.20805261276865; + } else { + result[0] += 158.88433974650883; + } + } + } else { + result[0] += 635.3039299555891; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 422))) { + if (UNLIKELY(false || (data[5].qvalue <= 98))) { + if (LIKELY(false || (data[4].qvalue <= 116))) { + result[0] += -674.4791597614653; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 154))) { + result[0] += -621.8229750354634; + } else { + result[0] += 421.9420604815607; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 102))) { + if (LIKELY(false || (data[0].qvalue <= 390))) { + result[0] += 85.124602127931; + } else { + result[0] += 949.6197614387934; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 144))) { + result[0] += 251.2728036404487; + } else { + result[0] += -309.15702771287084; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 184))) { + if (LIKELY(false || (data[0].qvalue <= 460))) { + if (UNLIKELY(false || (data[10].qvalue <= 38))) { + result[0] += -633.4429247336111; + } else { + result[0] += 160.50792355093574; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 176))) { + result[0] += 479.12605526304554; + } else { + result[0] += 116.33385550515398; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -930.2431709848779; + } else { + if (LIKELY(false || (data[1].qvalue <= 156))) { + result[0] += 214.15884274598116; + } else { + result[0] += -277.81632186374355; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 168))) { + if (UNLIKELY(false || (data[0].qvalue <= 72))) { + if (LIKELY(false || (data[6].qvalue <= 88))) { + if (UNLIKELY(false || (data[0].qvalue <= 4))) { + result[0] += -294.9481294960577; + } else { + result[0] += -165.43274636129723; + } + } else { + result[0] += -283.98106032598395; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 137.94773272215207; + } else { + result[0] += -74.90747793962899; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + result[0] += -146.0253896376931; + } else { + result[0] += -355.419967719613; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 268))) { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 312.51485279750943; + } else { + if (LIKELY(false || (data[2].qvalue <= 208))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + result[0] += -26.67715098965897; + } else { + result[0] += 59.16521507626189; + } + } else { + result[0] += -514.0817805406466; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 22))) { + result[0] += -1382.1700903888081; + } else { + if (LIKELY(false || (data[1].qvalue <= 148))) { + if (UNLIKELY(false || (data[6].qvalue <= 84))) { + result[0] += 2.2238582017569652; + } else { + result[0] += -149.07329069335975; + } + } else { + result[0] += -389.67704456086886; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 134))) { + if (LIKELY(false || (data[2].qvalue <= 212))) { + if (UNLIKELY(false || (data[1].qvalue <= 8))) { + if (LIKELY(false || (data[0].qvalue <= 462))) { + result[0] += -139.5342233564615; + } else { + result[0] += 425.4054517675065; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 370))) { + result[0] += 119.1045005005382; + } else { + result[0] += 225.06622562801806; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 428))) { + if (UNLIKELY(false || (data[10].qvalue <= 116))) { + result[0] += -314.0145429164164; + } else { + result[0] += -791.7715009965185; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 156))) { + result[0] += 299.89845236333366; + } else { + result[0] += -121.70249001324471; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 438))) { + if (LIKELY(false || (data[6].qvalue <= 152))) { + if (UNLIKELY(false || (data[2].qvalue <= 42))) { + result[0] += 308.86651700943054; + } else { + result[0] += -123.15739696081778; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 162))) { + result[0] += -543.1460199892057; + } else { + result[0] += -268.8247484125225; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 170))) { + if (LIKELY(false || (data[5].qvalue <= 114))) { + result[0] += 219.69052341180478; + } else { + result[0] += 768.652154361283; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -330.30502416245446; + } else { + result[0] += 226.83056005764217; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 194))) { + if (UNLIKELY(false || (data[0].qvalue <= 94))) { + result[0] += -164.85997854401103; + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + if (LIKELY(false || (data[6].qvalue <= 32))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + result[0] += 272.37997423411696; + } else { + result[0] += -38.01170141177444; + } + } else { + result[0] += 179.2649038053183; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 40))) { + if (UNLIKELY(false || (data[10].qvalue <= 14))) { + result[0] += -447.1000423177568; + } else { + result[0] += -129.8926570286136; + } + } else { + result[0] += -58.31804328980124; + } + } + } else { + result[0] += -307.58593821488535; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (UNLIKELY(false || (data[0].qvalue <= 308))) { + if (LIKELY(false || (data[6].qvalue <= 116))) { + if (UNLIKELY(false || (data[7].qvalue <= 2))) { + result[0] += 484.75118404823917; + } else { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 101.86133520052235; + } else { + result[0] += 18.262784206337113; + } + } + } else { + result[0] += -171.38940241656513; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 76))) { + if (LIKELY(false || (data[0].qvalue <= 420))) { + if (LIKELY(false || (data[4].qvalue <= 80))) { + result[0] += 164.27136795332518; + } else { + result[0] += 420.19522838999177; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 70))) { + result[0] += 288.60771862284395; + } else { + result[0] += -317.37860129812316; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 380))) { + if (UNLIKELY(false || (data[3].qvalue <= 112))) { + result[0] += -302.83673649889585; + } else { + result[0] += 54.622892990725916; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 98))) { + result[0] += -14.652198735183687; + } else { + result[0] += 188.62633009457414; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 410))) { + if (LIKELY(false || (data[1].qvalue <= 88))) { + if (UNLIKELY(false || (data[10].qvalue <= 68))) { + result[0] += -453.6048808127357; + } else { + result[0] += 39.27686618322581; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 20))) { + result[0] += 0.2890822714437353; + } else { + result[0] += -681.1445664481982; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 114))) { + if (LIKELY(false || (data[7].qvalue <= 198))) { + result[0] += 448.49867267672437; + } else { + if (LIKELY(false || (data[0].qvalue <= 446))) { + result[0] += -510.11854071816606; + } else { + result[0] += 725.828009865931; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + if (UNLIKELY(false || (data[10].qvalue <= 70))) { + result[0] += 82.63133907289594; + } else { + result[0] += -564.6009321848745; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 188))) { + result[0] += 485.04570202598234; + } else { + result[0] += -44.949579597135376; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 226))) { + if (LIKELY(false || (data[0].qvalue <= 140))) { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + result[0] += -89.22639695175398; + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + result[0] += -154.49086546432346; + } else { + result[0] += -329.5903225571293; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 126))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + result[0] += 318.48761171753574; + } else { + result[0] += -18.571568031593554; + } + } else { + result[0] += -220.49206840771905; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 202))) { + if (LIKELY(false || (data[1].qvalue <= 152))) { + if (LIKELY(false || (data[0].qvalue <= 366))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (LIKELY(false || (data[8].qvalue <= 120))) { + result[0] += 150.42799764081784; + } else { + result[0] += -76.38500429830752; + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 56))) { + result[0] += -211.0027314849032; + } else { + result[0] += 24.230230872826798; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 164))) { + if (LIKELY(false || (data[8].qvalue <= 142))) { + result[0] += 132.68242055886515; + } else { + result[0] += -882.8096755983888; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 192))) { + result[0] += 334.11768457333994; + } else { + result[0] += -53.48084930478878; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 450))) { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 233.21157693531032; + } else { + if (LIKELY(false || (data[10].qvalue <= 138))) { + result[0] += -582.6982780907397; + } else { + result[0] += 0.7508374869349193; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 174))) { + if (UNLIKELY(false || (data[2].qvalue <= 66))) { + result[0] += 844.2241348987232; + } else { + result[0] += 166.47618379149745; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -770.8958350747589; + } else { + result[0] += 106.39982734044628; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 424))) { + if (UNLIKELY(false || (data[10].qvalue <= 116))) { + if (LIKELY(false || (data[10].qvalue <= 114))) { + result[0] += -318.826948941992; + } else { + result[0] += 300.5051761631031; + } + } else { + result[0] += -663.2498735346456; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 68))) { + if (LIKELY(false || (data[7].qvalue <= 176))) { + result[0] += 330.2362671942275; + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + result[0] += -261.9859689644222; + } else { + result[0] += 470.337710070637; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + if (UNLIKELY(false || (data[8].qvalue <= 134))) { + result[0] += 52.55390876938948; + } else { + result[0] += -820.466862953795; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 172))) { + result[0] += 678.0504747681679; + } else { + result[0] += -30.65402416986749; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 230))) { + if (LIKELY(false || (data[0].qvalue <= 146))) { + if (LIKELY(false || (data[7].qvalue <= 124))) { + if (UNLIKELY(false || (data[0].qvalue <= 40))) { + result[0] += -163.25748720727836; + } else { + result[0] += -83.1950205740847; + } + } else { + result[0] += -269.08136712131557; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 54))) { + result[0] += -164.01997292662278; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 228.21827507549563; + } else { + result[0] += -9.402288377382439; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 142))) { + if (UNLIKELY(false || (data[7].qvalue <= 16))) { + if (LIKELY(false || (data[6].qvalue <= 30))) { + if (UNLIKELY(false || (data[7].qvalue <= 4))) { + result[0] += 381.64939051951137; + } else { + if (LIKELY(false || (data[0].qvalue <= 302))) { + result[0] += 58.00057357423904; + } else { + result[0] += 223.74792550499748; + } + } + } else { + result[0] += 454.5662709049777; + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 36))) { + if (LIKELY(false || (data[8].qvalue <= 128))) { + if (LIKELY(false || (data[0].qvalue <= 378))) { + result[0] += -93.01200660095587; + } else { + result[0] += 134.32252549902134; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 444))) { + result[0] += -2078.6414691042096; + } else { + result[0] += -159.18640228976722; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 32))) { + if (UNLIKELY(false || (data[0].qvalue <= 396))) { + result[0] += -163.73514121181952; + } else { + result[0] += 100.75922992115221; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 360))) { + result[0] += 84.30972820399808; + } else { + result[0] += 197.85022347944687; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 442))) { + if (LIKELY(false || (data[7].qvalue <= 198))) { + if (UNLIKELY(false || (data[6].qvalue <= 114))) { + if (LIKELY(false || (data[0].qvalue <= 406))) { + result[0] += -124.87742350731226; + } else { + result[0] += 371.4284844619934; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 130))) { + result[0] += -1022.9075285275169; + } else { + result[0] += -154.11889775108097; + } + } + } else { + result[0] += -609.5631862224477; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (UNLIKELY(false || (data[2].qvalue <= 104))) { + if (LIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -172.46788335138166; + } else { + result[0] += 267.92756125694655; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 216))) { + result[0] += 344.21102212490365; + } else { + result[0] += -63.20922723234142; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (UNLIKELY(false || (data[4].qvalue <= 88))) { + result[0] += -98.6686560628389; + } else { + result[0] += -824.7416534209821; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 162))) { + result[0] += 423.0262534565941; + } else { + result[0] += -172.900534785997; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 164))) { + if (UNLIKELY(false || (data[7].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 22))) { + result[0] += -151.00440148400935; + } else { + result[0] += -40.118590528856686; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 134))) { + result[0] += -118.83158145049806; + } else { + result[0] += -265.5488061301905; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (UNLIKELY(false || (data[0].qvalue <= 276))) { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (UNLIKELY(false || (data[7].qvalue <= 2))) { + result[0] += 337.5770961546419; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + result[0] += -45.53149621844132; + } else { + result[0] += 57.69099405123824; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 22))) { + result[0] += -1262.4368580820865; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 4))) { + result[0] += -499.135631046245; + } else { + result[0] += -51.130463115279184; + } + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + if (UNLIKELY(false || (data[0].qvalue <= 446))) { + if (LIKELY(false || (data[10].qvalue <= 132))) { + result[0] += -452.52727046733617; + } else { + result[0] += 207.2426999628595; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 186))) { + result[0] += 147.30212851601848; + } else { + result[0] += -378.60903812177116; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 8))) { + if (LIKELY(false || (data[7].qvalue <= 116))) { + result[0] += 60.647838286510584; + } else { + result[0] += -403.6642612098349; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 144))) { + result[0] += 97.61644906336824; + } else { + result[0] += 243.6057368289787; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 440))) { + if (LIKELY(false || (data[10].qvalue <= 116))) { + if (LIKELY(false || (data[0].qvalue <= 412))) { + if (LIKELY(false || (data[10].qvalue <= 114))) { + result[0] += -372.46208551501405; + } else { + result[0] += 150.12385303560893; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 154))) { + result[0] += 378.2084024000228; + } else { + result[0] += -115.22596011254524; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 104))) { + result[0] += -1013.8954317267925; + } else { + result[0] += -458.7466793813837; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 162))) { + if (LIKELY(false || (data[0].qvalue <= 460))) { + if (UNLIKELY(false || (data[10].qvalue <= 118))) { + result[0] += 573.7495286165021; + } else { + result[0] += -39.64820127548395; + } + } else { + result[0] += 747.9256037479381; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 464))) { + if (UNLIKELY(false || (data[2].qvalue <= 218))) { + result[0] += -322.32729930754635; + } else { + result[0] += -889.5131323414915; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -49.77314604177496; + } else { + result[0] += 452.922151113344; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 230))) { + if (UNLIKELY(false || (data[0].qvalue <= 100))) { + result[0] += -110.27471806521078; + } else { + if (UNLIKELY(false || (data[9].qvalue <= 54))) { + if (UNLIKELY(false || (data[1].qvalue <= 36))) { + result[0] += 405.5242126958143; + } else { + result[0] += -170.43432845409902; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + result[0] += 256.63050409714157; + } else { + if (LIKELY(false || (data[9].qvalue <= 154))) { + if (UNLIKELY(false || (data[4].qvalue <= 6))) { + result[0] += 199.22002184764415; + } else { + result[0] += -21.456223281242632; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 20))) { + result[0] += -394.6713467594481; + } else { + result[0] += 400.4595635278145; + } + } + } + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (UNLIKELY(false || (data[9].qvalue <= 2))) { + if (LIKELY(false || (data[0].qvalue <= 466))) { + if (LIKELY(false || (data[6].qvalue <= 170))) { + if (UNLIKELY(false || (data[1].qvalue <= 146))) { + result[0] += -1171.2407195011317; + } else { + result[0] += 62.25614012625213; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 462))) { + result[0] += -747.9426165897981; + } else { + result[0] += -245.0583143986272; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 184))) { + result[0] += 389.8618473333866; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -714.1912396897699; + } else { + result[0] += 105.56232441101614; + } + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 30))) { + if (LIKELY(false || (data[6].qvalue <= 152))) { + if (LIKELY(false || (data[0].qvalue <= 452))) { + result[0] += 8.267421890159907; + } else { + result[0] += 532.9815498007549; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 450))) { + result[0] += -998.5163824104064; + } else { + result[0] += 67.53749679257139; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 38))) { + if (LIKELY(false || (data[0].qvalue <= 314))) { + result[0] += 128.68056584630503; + } else { + result[0] += 304.65594504617775; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 378))) { + result[0] += 38.864839831291896; + } else { + result[0] += 132.95011491298308; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 422))) { + if (LIKELY(false || (data[10].qvalue <= 116))) { + result[0] += -200.50051859519212; + } else { + result[0] += -570.0695420065967; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 76))) { + if (LIKELY(false || (data[2].qvalue <= 214))) { + result[0] += 284.7667993502012; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 452))) { + result[0] += -439.2896395697744; + } else { + result[0] += 195.2703219505385; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 448))) { + result[0] += -719.4958031369861; + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -48.870417355715304; + } else { + result[0] += 558.0516523176542; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 158))) { + if (LIKELY(false || (data[1].qvalue <= 102))) { + if (UNLIKELY(false || (data[0].qvalue <= 28))) { + result[0] += -132.8644217361974; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 20))) { + if (LIKELY(false || (data[5].qvalue <= 44))) { + if (UNLIKELY(false || (data[6].qvalue <= 0))) { + result[0] += 184.7567664677024; + } else { + result[0] += -31.01544671430151; + } + } else { + result[0] += 93.76937878382813; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 158))) { + result[0] += -70.3541078253051; + } else { + result[0] += -256.18565932246213; + } + } + } + } else { + result[0] += -186.9601821997848; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (UNLIKELY(false || (data[0].qvalue <= 272))) { + if (UNLIKELY(false || (data[7].qvalue <= 22))) { + if (LIKELY(false || (data[6].qvalue <= 32))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + result[0] += 301.99251039980805; + } else { + result[0] += 8.317443045497088; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 285.9447580619998; + } else { + result[0] += -199.02985809976943; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 34))) { + if (UNLIKELY(false || (data[7].qvalue <= 28))) { + result[0] += -757.7155546033136; + } else { + result[0] += -100.8798688536487; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 54))) { + result[0] += -108.18906289397617; + } else { + result[0] += 16.427955171508895; + } + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 32))) { + if (LIKELY(false || (data[0].qvalue <= 450))) { + if (LIKELY(false || (data[6].qvalue <= 168))) { + result[0] += -31.079261573945725; + } else { + result[0] += -439.24160085690585; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 176))) { + result[0] += 196.17828368025368; + } else { + result[0] += -96.92802902335563; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 8))) { + if (LIKELY(false || (data[7].qvalue <= 150))) { + result[0] += 5.472886535824474; + } else { + result[0] += -322.6822453952255; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 104))) { + result[0] += 71.15954913651206; + } else { + result[0] += 170.40770380917564; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 434))) { + if (LIKELY(false || (data[1].qvalue <= 98))) { + result[0] += -431.7182112009705; + } else { + result[0] += -761.9070576282425; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 174))) { + if (LIKELY(false || (data[0].qvalue <= 450))) { + if (LIKELY(false || (data[3].qvalue <= 170))) { + result[0] += 223.6763270971365; + } else { + result[0] += -863.1311602081029; + } + } else { + result[0] += 633.5684765097419; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -822.025766073113; + } else { + if (LIKELY(false || (data[8].qvalue <= 144))) { + result[0] += -357.65137650350306; + } else { + result[0] += 161.10489597769953; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 230))) { + if (UNLIKELY(false || (data[0].qvalue <= 94))) { + result[0] += -92.57565870144303; + } else { + if (LIKELY(false || (data[7].qvalue <= 142))) { + result[0] += -18.109772204939024; + } else { + result[0] += -209.49572955406617; + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 116))) { + if (LIKELY(false || (data[1].qvalue <= 152))) { + if (UNLIKELY(false || (data[10].qvalue <= 30))) { + if (LIKELY(false || (data[0].qvalue <= 462))) { + if (LIKELY(false || (data[5].qvalue <= 102))) { + result[0] += 3.2575921560660412; + } else { + result[0] += -901.3597327991677; + } + } else { + result[0] += 507.2677983999125; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 128))) { + if (UNLIKELY(false || (data[5].qvalue <= 12))) { + result[0] += -59.086766158297294; + } else { + result[0] += 112.28787145525055; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 58))) { + result[0] += 323.6859359964492; + } else { + result[0] += -58.36682033574347; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 460))) { + if (LIKELY(false || (data[2].qvalue <= 84))) { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 300.9633139092185; + } else { + result[0] += -192.01700771623888; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 166))) { + result[0] += -1598.762896930197; + } else { + result[0] += -603.9295642909062; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 16))) { + if (UNLIKELY(false || (data[6].qvalue <= 174))) { + result[0] += 461.56736902180194; + } else { + result[0] += 8.737578542205178; + } + } else { + result[0] += -643.983821829543; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 440))) { + if (LIKELY(false || (data[6].qvalue <= 104))) { + if (UNLIKELY(false || (data[2].qvalue <= 148))) { + if (LIKELY(false || (data[2].qvalue <= 134))) { + result[0] += 179.91548506297573; + } else { + result[0] += -912.5235714991372; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 174))) { + result[0] += 301.8534329221878; + } else { + result[0] += 38.18076419361558; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 94))) { + if (LIKELY(false || (data[0].qvalue <= 406))) { + result[0] += -361.8709774599796; + } else { + result[0] += 270.3263916029786; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 6))) { + result[0] += 104.81627908969739; + } else { + result[0] += -535.2156269218489; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 170))) { + if (LIKELY(false || (data[4].qvalue <= 122))) { + if (UNLIKELY(false || (data[7].qvalue <= 114))) { + result[0] += 594.5833564826081; + } else { + result[0] += 64.11583431949299; + } + } else { + result[0] += 652.8845003559351; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -763.9957389916799; + } else { + if (UNLIKELY(false || (data[4].qvalue <= 102))) { + result[0] += 315.48470650562535; + } else { + result[0] += -203.67379945157032; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 154))) { + if (UNLIKELY(false || (data[4].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 12))) { + result[0] += -126.5718042782856; + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 111.5057760402395; + } else { + result[0] += -28.4538638965687; + } + } else { + result[0] += -313.9852425571987; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 142))) { + if (UNLIKELY(false || (data[9].qvalue <= 48))) { + result[0] += -159.27654309724835; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 54))) { + result[0] += -114.2328146382158; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 18))) { + result[0] += -169.53442010531597; + } else { + result[0] += -51.819614520602755; + } + } + } + } else { + result[0] += -237.49538829330947; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (UNLIKELY(false || (data[7].qvalue <= 16))) { + if (LIKELY(false || (data[5].qvalue <= 46))) { + if (LIKELY(false || (data[0].qvalue <= 290))) { + if (UNLIKELY(false || (data[7].qvalue <= 2))) { + result[0] += 287.9491607426837; + } else { + result[0] += 22.668345828570306; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 0))) { + result[0] += -77.53473409498233; + } else { + result[0] += 204.6386641118168; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 16))) { + result[0] += 593.7874627921306; + } else { + result[0] += 312.8524644986319; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 42))) { + if (UNLIKELY(false || (data[6].qvalue <= 24))) { + if (LIKELY(false || (data[2].qvalue <= 122))) { + result[0] += -142.83280406635228; + } else { + result[0] += -693.2501230727516; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 122))) { + result[0] += 37.74683956549833; + } else { + result[0] += -198.748856011046; + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 108))) { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + result[0] += -89.22275972296083; + } else { + result[0] += 86.32327653910806; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 442))) { + result[0] += -55.02130370736227; + } else { + result[0] += 177.35435049468094; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 432))) { + if (UNLIKELY(false || (data[2].qvalue <= 74))) { + result[0] += -882.5317237443489; + } else { + result[0] += -481.30010328266326; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 132))) { + if (LIKELY(false || (data[0].qvalue <= 450))) { + if (UNLIKELY(false || (data[7].qvalue <= 198))) { + result[0] += 647.320083633933; + } else { + result[0] += 38.11111140878454; + } + } else { + result[0] += 745.2928537341078; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -789.9197396160231; + } else { + if (UNLIKELY(false || (data[8].qvalue <= 144))) { + result[0] += -357.5527732739339; + } else { + result[0] += 102.50578713894222; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 232))) { + if (LIKELY(false || (data[6].qvalue <= 116))) { + if (UNLIKELY(false || (data[0].qvalue <= 62))) { + result[0] += -81.42467173085487; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 16))) { + if (LIKELY(false || (data[5].qvalue <= 48))) { + result[0] += 9.502633625283542; + } else { + result[0] += 279.42600545633803; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 28))) { + if (UNLIKELY(false || (data[6].qvalue <= 18))) { + result[0] += -361.821022373354; + } else { + result[0] += -88.28610792754384; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 120))) { + result[0] += -20.102121037240444; + } else { + result[0] += -420.12674867604005; + } + } + } + } + } else { + result[0] += -159.62279487679336; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 38))) { + if (LIKELY(false || (data[10].qvalue <= 124))) { + if (UNLIKELY(false || (data[6].qvalue <= 10))) { + if (LIKELY(false || (data[7].qvalue <= 18))) { + result[0] += 88.01385578444962; + } else { + if (UNLIKELY(false || (data[6].qvalue <= 4))) { + result[0] += 493.08930447766016; + } else { + result[0] += -285.19813158141295; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 82))) { + result[0] += 106.27335201243972; + } else { + result[0] += 226.93623921451922; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 50))) { + if (LIKELY(false || (data[0].qvalue <= 412))) { + result[0] += -1343.712162589175; + } else { + result[0] += 132.58954443144634; + } + } else { + result[0] += 91.6622701261032; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 44))) { + if (LIKELY(false || (data[3].qvalue <= 38))) { + if (LIKELY(false || (data[4].qvalue <= 84))) { + if (LIKELY(false || (data[0].qvalue <= 368))) { + result[0] += -236.33002335788538; + } else { + result[0] += 20.986096347583267; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 128))) { + result[0] += 344.9152673102923; + } else { + result[0] += -105.45712817788554; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 328.6203717386541; + } else { + if (LIKELY(false || (data[0].qvalue <= 448))) { + result[0] += -650.199452548778; + } else { + result[0] += -47.9905026661036; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 132))) { + if (LIKELY(false || (data[0].qvalue <= 366))) { + if (LIKELY(false || (data[2].qvalue <= 180))) { + result[0] += 42.03195219241939; + } else { + result[0] += -158.93413279533422; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 74))) { + result[0] += -34.071317241183344; + } else { + result[0] += 156.0271506153512; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 440))) { + if (UNLIKELY(false || (data[6].qvalue <= 102))) { + result[0] += 133.87095453891186; + } else { + result[0] += -228.7502100788784; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 108))) { + result[0] += 347.50113932020776; + } else { + result[0] += 14.191860694706207; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 230))) { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + if (LIKELY(false || (data[2].qvalue <= 122))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + result[0] += 177.32409766602134; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 10))) { + result[0] += -222.24918504973667; + } else { + result[0] += -35.30855443997724; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 4))) { + result[0] += -176.9665709227076; + } else { + result[0] += -753.9332796594691; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 116))) { + result[0] += -28.342368225619726; + } else { + if (UNLIKELY(false || (data[4].qvalue <= 8))) { + result[0] += 421.668098564295; + } else { + if (LIKELY(false || (data[3].qvalue <= 70))) { + result[0] += 43.5107313173905; + } else { + result[0] += 227.90844897767985; + } + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + result[0] += -57.17411824360336; + } else { + result[0] += -189.687353931762; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 202))) { + if (UNLIKELY(false || (data[3].qvalue <= 0))) { + if (LIKELY(false || (data[4].qvalue <= 54))) { + if (LIKELY(false || (data[9].qvalue <= 156))) { + result[0] += 143.81130747694493; + } else { + if (LIKELY(false || (data[0].qvalue <= 378))) { + result[0] += -412.9308129291959; + } else { + result[0] += 174.61191720224775; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 422))) { + if (LIKELY(false || (data[9].qvalue <= 88))) { + result[0] += -729.9085027051653; + } else { + result[0] += -1582.3957851064256; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 82))) { + result[0] += 255.17536551772466; + } else { + result[0] += -411.7747098985702; + } + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 0))) { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -687.4964190637061; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 156))) { + result[0] += 579.2665867229997; + } else { + result[0] += -202.15282940470115; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 378))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + result[0] += 87.73491261546084; + } else { + result[0] += -32.02969863953809; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 166))) { + result[0] += 64.28717265542389; + } else { + result[0] += 229.17843828172778; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[6].qvalue <= 176))) { + if (LIKELY(false || (data[0].qvalue <= 442))) { + if (UNLIKELY(false || (data[10].qvalue <= 116))) { + result[0] += -44.67740033036628; + } else { + result[0] += -394.97654476097733; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 222))) { + result[0] += 206.95175345062236; + } else { + result[0] += -422.41503848096585; + } + } + } else { + result[0] += -637.136586228852; + } + } else { + result[0] += 376.9378106237276; + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 162))) { + if (LIKELY(false || (data[1].qvalue <= 64))) { + if (UNLIKELY(false || (data[0].qvalue <= 4))) { + result[0] += -145.63790783710485; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 92.69381886875624; + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + result[0] += -29.52565796333189; + } else { + result[0] += -264.4833581843737; + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 80))) { + result[0] += -65.00862983366554; + } else { + result[0] += -128.3574858335414; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (LIKELY(false || (data[10].qvalue <= 116))) { + if (LIKELY(false || (data[0].qvalue <= 334))) { + if (LIKELY(false || (data[7].qvalue <= 44))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + result[0] += 2.638363742314313; + } else { + result[0] += 90.02482928313995; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 46))) { + result[0] += -159.46662656973402; + } else { + result[0] += -6.479090823017612; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + if (LIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -181.28880117455623; + } else { + result[0] += 348.385125441513; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 8))) { + result[0] += -95.60162541279402; + } else { + result[0] += 108.90915732052065; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 448))) { + if (LIKELY(false || (data[6].qvalue <= 104))) { + if (UNLIKELY(false || (data[8].qvalue <= 92))) { + result[0] += -264.2515788707746; + } else { + result[0] += 101.55470547812884; + } + } else { + if (LIKELY(false || (data[8].qvalue <= 148))) { + result[0] += -178.22322886037145; + } else { + result[0] += -572.0017383471462; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 176))) { + if (LIKELY(false || (data[2].qvalue <= 216))) { + result[0] += 360.0600154694514; + } else { + result[0] += -2.277701777775852; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -592.4446434254904; + } else { + result[0] += 311.89032842128967; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 434))) { + if (LIKELY(false || (data[1].qvalue <= 98))) { + if (LIKELY(false || (data[3].qvalue <= 170))) { + result[0] += -269.5216917245177; + } else { + result[0] += -763.2129798350616; + } + } else { + result[0] += -630.6861669974678; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 132))) { + if (LIKELY(false || (data[0].qvalue <= 452))) { + if (UNLIKELY(false || (data[1].qvalue <= 98))) { + result[0] += 491.16969306572764; + } else { + result[0] += -1.8378346933226348; + } + } else { + result[0] += 735.4661909012816; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -714.8393720262166; + } else { + if (LIKELY(false || (data[9].qvalue <= 124))) { + result[0] += -185.85739524553466; + } else { + result[0] += 534.3912650447504; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 234))) { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + if (LIKELY(false || (data[2].qvalue <= 122))) { + if (LIKELY(false || (data[7].qvalue <= 34))) { + result[0] += -17.516199145518204; + } else { + if (LIKELY(false || (data[5].qvalue <= 40))) { + result[0] += -106.45476753502933; + } else { + result[0] += -903.8252212915366; + } + } + } else { + result[0] += -429.58429904156355; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 108))) { + result[0] += -24.64488868029451; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 6))) { + result[0] += 442.263546116001; + } else { + result[0] += 55.12907189754925; + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + result[0] += -46.866944249531215; + } else { + result[0] += -159.04948180790302; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (UNLIKELY(false || (data[3].qvalue <= 0))) { + if (LIKELY(false || (data[7].qvalue <= 110))) { + result[0] += -7.415599980597751; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 420))) { + if (LIKELY(false || (data[2].qvalue <= 196))) { + result[0] += -1399.7966503370856; + } else { + result[0] += -610.3226946773707; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 96))) { + result[0] += 354.8632074839545; + } else { + result[0] += -377.08280725284146; + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 12))) { + if (LIKELY(false || (data[3].qvalue <= 54))) { + if (LIKELY(false || (data[0].qvalue <= 312))) { + result[0] += 78.41314807466219; + } else { + result[0] += 229.81473981996055; + } + } else { + result[0] += 656.2836431782829; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 370))) { + if (UNLIKELY(false || (data[6].qvalue <= 62))) { + result[0] += 59.2010780966252; + } else { + result[0] += -33.600013356306874; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + result[0] += -40.555513916002155; + } else { + result[0] += 98.08389359507521; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 412))) { + if (UNLIKELY(false || (data[8].qvalue <= 136))) { + if (LIKELY(false || (data[8].qvalue <= 122))) { + result[0] += -319.74524116929115; + } else { + result[0] += -767.7796729203433; + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 82))) { + result[0] += 122.08054350335395; + } else { + result[0] += -301.9722820682188; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 114))) { + if (LIKELY(false || (data[7].qvalue <= 198))) { + result[0] += 313.9124097005065; + } else { + result[0] += -8.79354690498097; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[6].qvalue <= 180))) { + result[0] += -72.7211246346664; + } else { + result[0] += -510.6446546492408; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 188))) { + result[0] += 560.0836128367328; + } else { + result[0] += 17.985013800397187; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 148))) { + if (UNLIKELY(false || (data[4].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 2))) { + result[0] += -141.96709416955443; + } else { + result[0] += -15.705324092558222; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 152))) { + result[0] += -61.382323220403975; + } else { + result[0] += -187.5709723722473; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (LIKELY(false || (data[0].qvalue <= 352))) { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + if (LIKELY(false || (data[7].qvalue <= 16))) { + result[0] += 44.92602753182352; + } else { + result[0] += -211.1546710682142; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 70))) { + result[0] += 84.82973193387151; + } else { + result[0] += 297.5136117027242; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 54))) { + if (LIKELY(false || (data[8].qvalue <= 80))) { + result[0] += 17.890869053801463; + } else { + result[0] += -233.23923140903472; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 42))) { + result[0] += -115.18248063786203; + } else { + result[0] += 19.654733381799502; + } + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 38))) { + if (UNLIKELY(false || (data[8].qvalue <= 48))) { + if (UNLIKELY(false || (data[6].qvalue <= 108))) { + result[0] += 73.48571042663086; + } else { + result[0] += -228.89571182954265; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 412))) { + result[0] += -172.0892135464257; + } else { + result[0] += 115.89684884981358; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 74))) { + if (UNLIKELY(false || (data[2].qvalue <= 2))) { + result[0] += -192.70183326405345; + } else { + result[0] += 228.22523400305982; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 164))) { + result[0] += -14.495336023506457; + } else { + result[0] += 164.58338032467216; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 418))) { + if (UNLIKELY(false || (data[6].qvalue <= 110))) { + result[0] += 92.44304518392212; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -549.5535909490123; + } else { + result[0] += -283.7280102420611; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 170))) { + if (UNLIKELY(false || (data[3].qvalue <= 110))) { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -751.8919251402435; + } else { + result[0] += 185.12085880810656; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 442))) { + result[0] += 106.48103521611722; + } else { + result[0] += 402.22669875787847; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 460))) { + if (UNLIKELY(false || (data[4].qvalue <= 14))) { + result[0] += -36.04581623793381; + } else { + result[0] += -576.7489848172199; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 204))) { + result[0] += 811.3997608577275; + } else { + result[0] += 54.07893988355018; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 190))) { + if (LIKELY(false || (data[1].qvalue <= 118))) { + if (UNLIKELY(false || (data[0].qvalue <= 40))) { + result[0] += -66.6266648878957; + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (UNLIKELY(false || (data[7].qvalue <= 0))) { + result[0] += 218.52501238124137; + } else { + if (LIKELY(false || (data[7].qvalue <= 184))) { + result[0] += -13.543587342327356; + } else { + result[0] += -215.10060994815572; + } + } + } else { + result[0] += -235.9788695560659; + } + } + } else { + result[0] += -124.00456972107527; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (LIKELY(false || (data[10].qvalue <= 122))) { + if (UNLIKELY(false || (data[10].qvalue <= 30))) { + if (LIKELY(false || (data[0].qvalue <= 462))) { + if (LIKELY(false || (data[5].qvalue <= 102))) { + result[0] += -18.111064688282102; + } else { + result[0] += -830.4402494030709; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 114))) { + result[0] += 549.3375303639418; + } else { + result[0] += -471.1944868822796; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 38))) { + if (LIKELY(false || (data[0].qvalue <= 306))) { + result[0] += 58.15499047997176; + } else { + result[0] += 202.68651234520863; + } + } else { + if (LIKELY(false || (data[5].qvalue <= 84))) { + result[0] += 5.292449943571185; + } else { + result[0] += 88.29265314882701; + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 136))) { + if (LIKELY(false || (data[0].qvalue <= 414))) { + if (LIKELY(false || (data[1].qvalue <= 128))) { + result[0] += -1380.2032030523546; + } else { + result[0] += -290.5204067553985; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 428))) { + result[0] += -114.53892152149231; + } else { + result[0] += 514.5293420314608; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 58))) { + if (UNLIKELY(false || (data[4].qvalue <= 48))) { + result[0] += -61.89760896238173; + } else { + result[0] += 238.5724533454146; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 440))) { + result[0] += -129.87454115256398; + } else { + result[0] += 80.61545671612187; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 436))) { + if (UNLIKELY(false || (data[0].qvalue <= 346))) { + result[0] += -205.98214313987833; + } else { + result[0] += -501.7217730494733; + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 94))) { + if (UNLIKELY(false || (data[9].qvalue <= 74))) { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -1647.808151855469; + } else { + result[0] += -111.63345227808891; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 448))) { + result[0] += 135.65529729266765; + } else { + result[0] += 542.5839931646244; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -605.3494305872034; + } else { + if (LIKELY(false || (data[7].qvalue <= 200))) { + result[0] += 23.37492749029042; + } else { + result[0] += -504.33767145881876; + } + } + } + } + } + } + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + if (UNLIKELY(false || (data[8].qvalue <= 6))) { + if (UNLIKELY(false || (data[2].qvalue <= 28))) { + result[0] += 147.6732444791957; + } else { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += -10.796001760756946; + } else { + result[0] += 119.76151703653773; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 16))) { + result[0] += -80.64564777701082; + } else { + result[0] += -137.81918275187488; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 188))) { + result[0] += -301.13288115967265; + } else { + result[0] += -677.8347078414617; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 102))) { + if (LIKELY(false || (data[7].qvalue <= 48))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + if (LIKELY(false || (data[1].qvalue <= 66))) { + result[0] += 177.5420680296681; + } else { + result[0] += -218.59350937699685; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 20))) { + result[0] += -72.74967625197816; + } else { + result[0] += 20.742745610897746; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 84))) { + if (LIKELY(false || (data[4].qvalue <= 78))) { + result[0] += -90.48006952525736; + } else { + result[0] += -828.7521117629706; + } + } else { + if (LIKELY(false || (data[5].qvalue <= 66))) { + result[0] += 120.02128594767765; + } else { + result[0] += -101.64457682331833; + } + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 32))) { + if (UNLIKELY(false || (data[8].qvalue <= 48))) { + if (LIKELY(false || (data[8].qvalue <= 24))) { + result[0] += -29.977235441363906; + } else { + result[0] += -517.2676348797457; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 138))) { + result[0] += 79.82233930374132; + } else { + result[0] += -65.66862801060336; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 176))) { + if (UNLIKELY(false || (data[2].qvalue <= 4))) { + result[0] += -246.17761450724754; + } else { + result[0] += 128.63335245716783; + } + } else { + if (LIKELY(false || (data[10].qvalue <= 134))) { + result[0] += 25.606045866379198; + } else { + result[0] += -125.59366437898994; + } + } + } + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 32))) { + result[0] += -1022.6641412510904; + } else { + if (LIKELY(false || (data[7].qvalue <= 200))) { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 256.4779043789177; + } else { + if (LIKELY(false || (data[1].qvalue <= 126))) { + if (LIKELY(false || (data[4].qvalue <= 68))) { + result[0] += -87.1798391962891; + } else { + result[0] += 43.00407443678074; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 62))) { + result[0] += -130.4952039313895; + } else { + result[0] += -240.59828244124444; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 164))) { + result[0] += -424.70860218767706; + } else { + result[0] += -596.5247694936194; + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 260))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + if (UNLIKELY(false || (data[8].qvalue <= 2))) { + result[0] += 174.72777599410847; + } else { + result[0] += 11.536037972850965; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 6))) { + if (LIKELY(false || (data[9].qvalue <= 96))) { + if (LIKELY(false || (data[7].qvalue <= 72))) { + if (LIKELY(false || (data[0].qvalue <= 132))) { + result[0] += -134.39734952576543; + } else { + result[0] += -367.3809402303026; + } + } else { + result[0] += 85.4518440435695; + } + } else { + result[0] += 30.478639892590373; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 124))) { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + result[0] += -50.893812931197466; + } else { + if (LIKELY(false || (data[2].qvalue <= 200))) { + result[0] += -4.69601116410534; + } else { + result[0] += -265.1217064966412; + } + } + } else { + result[0] += -104.769463503831; + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 64))) { + if (UNLIKELY(false || (data[9].qvalue <= 36))) { + result[0] += 428.87545627753326; + } else { + if (UNLIKELY(false || (data[9].qvalue <= 38))) { + if (UNLIKELY(false || (data[0].qvalue <= 442))) { + result[0] += -1773.848510347332; + } else { + result[0] += 55.659715716371515; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 359.22617617244686; + } else { + if (UNLIKELY(false || (data[8].qvalue <= 20))) { + result[0] += -44.87315510911888; + } else { + result[0] += 74.38569188031644; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 382))) { + if (UNLIKELY(false || (data[2].qvalue <= 140))) { + if (UNLIKELY(false || (data[3].qvalue <= 12))) { + if (UNLIKELY(false || (data[10].qvalue <= 40))) { + result[0] += -1410.981056764837; + } else { + result[0] += -637.2921393197482; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 56))) { + result[0] += -70.36055469147344; + } else { + result[0] += 175.7175261222059; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 146))) { + result[0] += -613.9698554996957; + } else { + if (LIKELY(false || (data[8].qvalue <= 154))) { + result[0] += -92.27993947562106; + } else { + result[0] += -383.51208633267225; + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 88))) { + if (UNLIKELY(false || (data[7].qvalue <= 80))) { + if (UNLIKELY(false || (data[2].qvalue <= 64))) { + result[0] += -11.98521625723796; + } else { + result[0] += -714.7673310356126; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 96))) { + result[0] += 284.6663603250349; + } else { + result[0] += -61.60569809902938; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 40))) { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -1260.7217681991474; + } else { + result[0] += 135.29439352456583; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 114))) { + result[0] += 204.2658499096425; + } else { + result[0] += 26.57998407789056; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 278))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + result[0] += 88.29835564376643; + } else { + result[0] += 433.48530065382903; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 158))) { + if (UNLIKELY(false || (data[4].qvalue <= 6))) { + if (UNLIKELY(false || (data[9].qvalue <= 134))) { + result[0] += 355.11754045683864; + } else { + result[0] += 50.689614734965716; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 10))) { + if (UNLIKELY(false || (data[9].qvalue <= 142))) { + result[0] += -67.45276443587568; + } else { + result[0] += -258.89458735449244; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 150))) { + result[0] += -21.308012427711205; + } else { + result[0] += 244.9050850901425; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 166))) { + result[0] += -159.91554699358346; + } else { + result[0] += -467.3807915108788; + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (LIKELY(false || (data[0].qvalue <= 460))) { + if (LIKELY(false || (data[2].qvalue <= 214))) { + if (UNLIKELY(false || (data[9].qvalue <= 2))) { + if (UNLIKELY(false || (data[3].qvalue <= 98))) { + result[0] += -888.854216500035; + } else { + result[0] += -60.23569984287638; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 10))) { + result[0] += -107.7332013111863; + } else { + result[0] += 44.24648581216298; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 32))) { + if (LIKELY(false || (data[0].qvalue <= 446))) { + result[0] += -226.15311381622098; + } else { + result[0] += 710.1847247221976; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -521.4681372500785; + } else { + result[0] += -121.81009418188125; + } + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 156))) { + if (LIKELY(false || (data[4].qvalue <= 108))) { + if (UNLIKELY(false || (data[9].qvalue <= 36))) { + result[0] += 578.8864769214038; + } else { + result[0] += -65.43432145431503; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -1140.9993698987162; + } else { + result[0] += -277.1047243415079; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 124))) { + if (LIKELY(false || (data[2].qvalue <= 218))) { + result[0] += 625.8667118148287; + } else { + result[0] += 133.56293016701576; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -257.6768725464037; + } else { + result[0] += 235.27089761951143; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (UNLIKELY(false || (data[4].qvalue <= 88))) { + if (LIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -510.7544463506701; + } else { + result[0] += 309.52861873156445; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 186))) { + result[0] += -475.554284048819; + } else { + result[0] += -931.2955241523669; + } + } + } else { + result[0] += 204.12262051329452; + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 148))) { + if (LIKELY(false || (data[1].qvalue <= 60))) { + if (UNLIKELY(false || (data[0].qvalue <= 4))) { + result[0] += -105.41033083691937; + } else { + if (LIKELY(false || (data[7].qvalue <= 118))) { + result[0] += -9.957538169716495; + } else { + result[0] += -116.44279176689716; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 80))) { + if (LIKELY(false || (data[7].qvalue <= 138))) { + result[0] += -36.47666931987333; + } else { + result[0] += -181.90346126434108; + } + } else { + result[0] += -92.3026154859271; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (LIKELY(false || (data[0].qvalue <= 380))) { + if (UNLIKELY(false || (data[9].qvalue <= 54))) { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 635.4266731901135; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 56))) { + result[0] += -956.053733984008; + } else { + result[0] += -104.63367772570996; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 132))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + result[0] += -20.173896617001866; + } else { + result[0] += 65.25040747317426; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 136))) { + result[0] += -570.0499875540565; + } else { + result[0] += -4.395243842020363; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 96))) { + if (LIKELY(false || (data[2].qvalue <= 150))) { + if (UNLIKELY(false || (data[7].qvalue <= 56))) { + result[0] += 82.3347657422682; + } else { + result[0] += -151.85939690566192; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 202))) { + result[0] += 306.7932008675081; + } else { + result[0] += -193.98901427646175; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 30))) { + if (UNLIKELY(false || (data[10].qvalue <= 80))) { + result[0] += -101.31894339788478; + } else { + result[0] += 57.66783954209549; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 160))) { + result[0] += 304.3481193163534; + } else { + result[0] += 101.37427473767308; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 438))) { + if (LIKELY(false || (data[1].qvalue <= 98))) { + if (LIKELY(false || (data[3].qvalue <= 170))) { + result[0] += -173.79619958480487; + } else { + result[0] += -611.7431741530887; + } + } else { + result[0] += -479.5994428796921; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 174))) { + if (LIKELY(false || (data[0].qvalue <= 452))) { + if (LIKELY(false || (data[2].qvalue <= 194))) { + result[0] += 205.09978107028988; + } else { + result[0] += -552.4708829680587; + } + } else { + result[0] += 465.99445615301477; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 466))) { + if (UNLIKELY(false || (data[6].qvalue <= 180))) { + result[0] += -38.14040161366896; + } else { + result[0] += -654.1612824926979; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 164))) { + result[0] += 89.03239010969071; + } else { + result[0] += -334.95950567599243; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 324))) { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + if (LIKELY(false || (data[3].qvalue <= 70))) { + if (LIKELY(false || (data[3].qvalue <= 66))) { + if (LIKELY(false || (data[3].qvalue <= 34))) { + if (LIKELY(false || (data[2].qvalue <= 122))) { + result[0] += -18.433723366725584; + } else { + result[0] += -472.18904133491907; + } + } else { + result[0] += 41.144657963793065; + } + } else { + if (LIKELY(false || (data[10].qvalue <= 126))) { + if (UNLIKELY(false || (data[2].qvalue <= 70))) { + result[0] += -791.0825554146527; + } else { + result[0] += -77.87558566467489; + } + } else { + result[0] += -1273.8300701538087; + } + } + } else { + result[0] += 162.91309292890534; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -374.78034701138165; + } else { + result[0] += -30.23314802748434; + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 116))) { + if (LIKELY(false || (data[1].qvalue <= 152))) { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + if (LIKELY(false || (data[0].qvalue <= 458))) { + if (LIKELY(false || (data[6].qvalue <= 82))) { + result[0] += 19.475576346353392; + } else { + result[0] += -352.9733362293148; + } + } else { + result[0] += 349.1018486741264; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 416))) { + result[0] += 212.94808346197698; + } else { + result[0] += -374.8210809697834; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 46))) { + result[0] += -34.43423741643343; + } else { + result[0] += 75.34901971114162; + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 312.18232536892253; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 454))) { + result[0] += -380.2729414924723; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 74))) { + result[0] += 384.2278192657615; + } else { + result[0] += -48.08512286278479; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 448))) { + if (LIKELY(false || (data[6].qvalue <= 124))) { + if (UNLIKELY(false || (data[2].qvalue <= 148))) { + result[0] += -339.175501983996; + } else { + result[0] += 73.82088972486432; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 122))) { + if (UNLIKELY(false || (data[3].qvalue <= 114))) { + result[0] += -690.6636434137862; + } else { + result[0] += -233.4604631893612; + } + } else { + result[0] += 61.13592985094737; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 176))) { + if (LIKELY(false || (data[2].qvalue <= 216))) { + if (LIKELY(false || (data[0].qvalue <= 466))) { + result[0] += 336.27393994775974; + } else { + result[0] += -1244.2348096296523; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -288.7929275242086; + } else { + result[0] += 303.1087488248426; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -522.5254243038571; + } else { + result[0] += 147.10931954259047; + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 150))) { + if (UNLIKELY(false || (data[0].qvalue <= 2))) { + result[0] += -114.0935731263952; + } else { + if (LIKELY(false || (data[1].qvalue <= 102))) { + if (LIKELY(false || (data[4].qvalue <= 36))) { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (LIKELY(false || (data[5].qvalue <= 42))) { + result[0] += -18.695361121395486; + } else { + result[0] += 10.588982002368144; + } + } else { + result[0] += -172.85563078964458; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 30))) { + if (LIKELY(false || (data[10].qvalue <= 22))) { + result[0] += -51.938265058110325; + } else { + result[0] += -246.80507747694102; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 94))) { + result[0] += -27.959993628946236; + } else { + result[0] += 73.25664987336096; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 154))) { + if (UNLIKELY(false || (data[8].qvalue <= 66))) { + if (UNLIKELY(false || (data[9].qvalue <= 34))) { + result[0] += -101.97494811248283; + } else { + result[0] += 17.881559562839353; + } + } else { + result[0] += -100.47614812809579; + } + } else { + result[0] += -213.33125633625428; + } + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + if (UNLIKELY(false || (data[2].qvalue <= 24))) { + if (LIKELY(false || (data[0].qvalue <= 222))) { + result[0] += 434.3221282795032; + } else { + result[0] += 823.0770567103796; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 258))) { + if (LIKELY(false || (data[2].qvalue <= 128))) { + result[0] += 87.83445346165345; + } else { + result[0] += -212.4695657744424; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 128))) { + result[0] += 464.3977186968871; + } else { + result[0] += 163.9938488166673; + } + } + } + } else { + result[0] += 637.6504024717447; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 154))) { + if (UNLIKELY(false || (data[8].qvalue <= 0))) { + if (LIKELY(false || (data[0].qvalue <= 406))) { + if (LIKELY(false || (data[9].qvalue <= 90))) { + result[0] += 282.83342280184934; + } else { + result[0] += 120.66910156313747; + } + } else { + result[0] += 1028.390014143319; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + if (LIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -292.64750935522983; + } else { + result[0] += 696.0738841280834; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + result[0] += -98.38508109993742; + } else { + result[0] += 20.07618047102507; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 20))) { + if (LIKELY(false || (data[0].qvalue <= 384))) { + if (UNLIKELY(false || (data[1].qvalue <= 14))) { + result[0] += -153.79848410394547; + } else { + result[0] += -491.2192559340425; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 430))) { + result[0] += 170.48551507912254; + } else { + result[0] += 726.1771275054432; + } + } + } else { + result[0] += 582.2067601142297; + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 384))) { + if (LIKELY(false || (data[6].qvalue <= 144))) { + if (LIKELY(false || (data[4].qvalue <= 120))) { + if (LIKELY(false || (data[8].qvalue <= 138))) { + if (LIKELY(false || (data[3].qvalue <= 122))) { + if (LIKELY(false || (data[6].qvalue <= 78))) { + result[0] += 6.611296900045464; + } else { + result[0] += -146.7747758716442; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 106))) { + result[0] += 117.39498296773701; + } else { + result[0] += -52.139056254522906; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 64))) { + result[0] += -786.3744050601753; + } else { + result[0] += -75.75876514356197; + } + } + } else { + result[0] += -251.69019507498334; + } + } else { + result[0] += -159.81350735348502; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + if (UNLIKELY(false || (data[0].qvalue <= 450))) { + if (LIKELY(false || (data[3].qvalue <= 168))) { + if (UNLIKELY(false || (data[8].qvalue <= 78))) { + result[0] += -655.1579692026768; + } else { + result[0] += -234.92840903417084; + } + } else { + result[0] += 129.69681018242764; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 174))) { + if (LIKELY(false || (data[7].qvalue <= 174))) { + if (UNLIKELY(false || (data[8].qvalue <= 54))) { + result[0] += -139.81070363809008; + } else { + result[0] += 236.49651798822757; + } + } else { + result[0] += 997.2638005655676; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 468))) { + result[0] += -440.5340347804863; + } else { + result[0] += 133.34746925104315; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 96))) { + if (LIKELY(false || (data[3].qvalue <= 76))) { + if (UNLIKELY(false || (data[8].qvalue <= 80))) { + if (UNLIKELY(false || (data[9].qvalue <= 16))) { + result[0] += 953.3985019502752; + } else { + result[0] += 115.36154895354196; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 68))) { + result[0] += -75.90121598789128; + } else { + result[0] += 321.1318897275337; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 78))) { + if (LIKELY(false || (data[0].qvalue <= 442))) { + result[0] += -896.7922993531249; + } else { + result[0] += -192.64263220387716; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 82))) { + result[0] += -157.71189744728704; + } else { + result[0] += 388.1757773943014; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 128))) { + if (LIKELY(false || (data[0].qvalue <= 426))) { + if (LIKELY(false || (data[6].qvalue <= 140))) { + result[0] += 239.69457507298395; + } else { + result[0] += -339.18339092719594; + } + } else { + result[0] += 440.89438255994565; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 48))) { + if (LIKELY(false || (data[8].qvalue <= 28))) { + result[0] += 94.78432124431386; + } else { + result[0] += -585.5090672534587; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 118))) { + result[0] += 261.6165504519048; + } else { + result[0] += -0.9669883257810821; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 130))) { + if (UNLIKELY(false || (data[0].qvalue <= 6))) { + result[0] += -85.38452227119578; + } else { + if (LIKELY(false || (data[1].qvalue <= 60))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + result[0] += 71.39641555324053; + } else { + result[0] += -12.679337084314707; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 70))) { + if (UNLIKELY(false || (data[9].qvalue <= 46))) { + result[0] += -328.3515745861993; + } else { + result[0] += -24.65068942389365; + } + } else { + result[0] += -65.54687009952785; + } + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 122))) { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + if (UNLIKELY(false || (data[2].qvalue <= 24))) { + result[0] += 448.8186025793332; + } else { + if (LIKELY(false || (data[0].qvalue <= 262))) { + result[0] += 28.507570304853807; + } else { + result[0] += 334.06403364310586; + } + } + } else { + result[0] += 565.4514138078015; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 154))) { + if (LIKELY(false || (data[9].qvalue <= 148))) { + if (UNLIKELY(false || (data[1].qvalue <= 16))) { + result[0] += -96.69821678256282; + } else { + result[0] += 21.631660184261378; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 204))) { + result[0] += 354.2452862416222; + } else { + result[0] += -51.61557505903728; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 12))) { + if (LIKELY(false || (data[0].qvalue <= 372))) { + result[0] += -328.0629299855443; + } else { + result[0] += 135.76618575654695; + } + } else { + result[0] += 497.32398174001605; + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 136))) { + if (LIKELY(false || (data[0].qvalue <= 416))) { + if (UNLIKELY(false || (data[2].qvalue <= 134))) { + if (UNLIKELY(false || (data[0].qvalue <= 354))) { + result[0] += -15.704885988027627; + } else { + result[0] += -432.2700366601563; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 398))) { + result[0] += -1249.1625247677366; + } else { + result[0] += -669.3611102779328; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 428))) { + result[0] += -58.35712345907448; + } else { + if (LIKELY(false || (data[0].qvalue <= 438))) { + result[0] += 338.03248915141415; + } else { + result[0] += 777.2208330972686; + } + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 84))) { + if (LIKELY(false || (data[0].qvalue <= 426))) { + if (LIKELY(false || (data[1].qvalue <= 154))) { + result[0] += 120.71513738942753; + } else { + result[0] += -308.17490477956534; + } + } else { + result[0] += 833.731541937934; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 438))) { + if (UNLIKELY(false || (data[9].qvalue <= 6))) { + result[0] += 149.7928136109008; + } else { + result[0] += -223.98734671939062; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 152))) { + result[0] += 365.23772234337326; + } else { + result[0] += -24.192037541431123; + } + } + } + } + } + } + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (LIKELY(false || (data[0].qvalue <= 388))) { + if (LIKELY(false || (data[1].qvalue <= 124))) { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (LIKELY(false || (data[3].qvalue <= 122))) { + if (LIKELY(false || (data[8].qvalue <= 116))) { + result[0] += 4.536955058087513; + } else { + result[0] += -72.38105398500589; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -344.4937340104233; + } else { + result[0] += 54.04370832477616; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 352))) { + result[0] += -155.8470275480651; + } else { + result[0] += -517.2572856835848; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 64))) { + if (LIKELY(false || (data[8].qvalue <= 64))) { + if (UNLIKELY(false || (data[3].qvalue <= 62))) { + result[0] += 140.37617439343134; + } else { + result[0] += -55.112467004567236; + } + } else { + result[0] += 774.980876755253; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 346))) { + if (UNLIKELY(false || (data[8].qvalue <= 38))) { + result[0] += -397.1965385921262; + } else { + result[0] += -92.74338066885616; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 36))) { + result[0] += 475.3460832912639; + } else { + result[0] += -489.2890878250337; + } + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 96))) { + if (UNLIKELY(false || (data[8].qvalue <= 10))) { + result[0] += 386.3622072624303; + } else { + if (LIKELY(false || (data[2].qvalue <= 150))) { + if (LIKELY(false || (data[0].qvalue <= 452))) { + result[0] += -138.2046386901946; + } else { + result[0] += 109.19811106405169; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 202))) { + result[0] += 279.484508613883; + } else { + result[0] += -160.50141487078906; + } + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 30))) { + if (UNLIKELY(false || (data[8].qvalue <= 48))) { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 96.50006530829478; + } else { + result[0] += -311.4323024052799; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 114))) { + result[0] += 221.35954689453885; + } else { + result[0] += -11.21222614010631; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 12))) { + if (LIKELY(false || (data[0].qvalue <= 462))) { + result[0] += -250.44082389008545; + } else { + result[0] += 259.77584765088653; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 86))) { + result[0] += 361.4943477960914; + } else { + result[0] += 91.34722785277711; + } + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 436))) { + if (UNLIKELY(false || (data[0].qvalue <= 350))) { + result[0] += -160.4323829658149; + } else { + result[0] += -425.59656420877354; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 146))) { + result[0] += 180.15448233040198; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -433.82026787118116; + } else { + result[0] += -59.26399277285995; + } + } + } + } + if (LIKELY(false || (data[7].qvalue <= 178))) { + if (LIKELY(false || (data[0].qvalue <= 460))) { + if (UNLIKELY(false || (data[9].qvalue <= 2))) { + if (LIKELY(false || (data[7].qvalue <= 152))) { + if (LIKELY(false || (data[0].qvalue <= 442))) { + result[0] += -308.1244882642975; + } else { + if (LIKELY(false || (data[1].qvalue <= 154))) { + result[0] += 509.7187192299381; + } else { + result[0] += -455.2150339355469; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 428))) { + result[0] += -189.49508286587985; + } else { + result[0] += -760.2093864229564; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 212))) { + if (LIKELY(false || (data[7].qvalue <= 166))) { + if (LIKELY(false || (data[7].qvalue <= 164))) { + result[0] += 3.4436391818207746; + } else { + result[0] += -682.0310140822548; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 388))) { + result[0] += 92.34103234198413; + } else { + result[0] += 380.69157714277566; + } + } + } else { + result[0] += -165.7227313266843; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 156))) { + if (LIKELY(false || (data[4].qvalue <= 108))) { + if (UNLIKELY(false || (data[9].qvalue <= 36))) { + result[0] += 549.4816677105838; + } else { + if (LIKELY(false || (data[1].qvalue <= 58))) { + result[0] += 194.29034273702734; + } else { + result[0] += -515.5656251305937; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + if (UNLIKELY(false || (data[9].qvalue <= 6))) { + result[0] += 310.8152990014213; + } else { + result[0] += -1141.971330367939; + } + } else { + result[0] += -60.374245828492555; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 176))) { + result[0] += 351.2745651968375; + } else { + result[0] += 86.38384679612086; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 416))) { + if (UNLIKELY(false || (data[2].qvalue <= 162))) { + if (LIKELY(false || (data[2].qvalue <= 142))) { + result[0] += -197.5086799826019; + } else { + if (LIKELY(false || (data[0].qvalue <= 332))) { + result[0] += -304.0321579015652; + } else { + result[0] += -814.8576146638849; + } + } + } else { + result[0] += -77.31156609799358; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 114))) { + if (LIKELY(false || (data[7].qvalue <= 198))) { + result[0] += 364.03422368739893; + } else { + if (LIKELY(false || (data[0].qvalue <= 448))) { + result[0] += -262.12017291355977; + } else { + result[0] += 355.1467205041031; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (LIKELY(false || (data[2].qvalue <= 222))) { + result[0] += 34.07266539417179; + } else { + result[0] += -461.43650166079533; + } + } else { + result[0] += -392.65138527512556; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 188))) { + result[0] += 424.2608283476747; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -401.74970612209404; + } else { + result[0] += 149.51617217256646; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 318))) { + if (UNLIKELY(false || (data[7].qvalue <= 16))) { + if (LIKELY(false || (data[5].qvalue <= 46))) { + result[0] += 6.274580412893512; + } else { + result[0] += 170.65850290785482; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 28))) { + if (UNLIKELY(false || (data[7].qvalue <= 28))) { + result[0] += -321.26713386171923; + } else { + if (LIKELY(false || (data[10].qvalue <= 24))) { + if (UNLIKELY(false || (data[3].qvalue <= 0))) { + result[0] += -883.3009822319002; + } else { + result[0] += -30.131313419296436; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 44))) { + result[0] += 145.86018225187783; + } else { + result[0] += -533.3653617127321; + } + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + if (LIKELY(false || (data[8].qvalue <= 114))) { + if (UNLIKELY(false || (data[3].qvalue <= 32))) { + result[0] += -64.30753875016195; + } else { + result[0] += 70.480411627325; + } + } else { + if (LIKELY(false || (data[8].qvalue <= 132))) { + result[0] += -282.2392212961099; + } else { + result[0] += 67.65616622116742; + } + } + } else { + result[0] += -24.453451185191113; + } + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 116))) { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + if (LIKELY(false || (data[0].qvalue <= 466))) { + if (UNLIKELY(false || (data[8].qvalue <= 26))) { + result[0] += -607.6977777871718; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 442))) { + result[0] += -273.64198324787975; + } else { + result[0] += 45.47924148212965; + } + } + } else { + result[0] += 215.11954437098967; + } + } else { + result[0] += 45.61441585551685; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 440))) { + if (LIKELY(false || (data[6].qvalue <= 124))) { + if (LIKELY(false || (data[0].qvalue <= 414))) { + if (UNLIKELY(false || (data[6].qvalue <= 42))) { + result[0] += -454.72131809174454; + } else { + result[0] += 0.13988354882495432; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 56))) { + result[0] += 38.582001327207514; + } else { + result[0] += 525.027278044562; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 20))) { + if (UNLIKELY(false || (data[6].qvalue <= 132))) { + result[0] += -698.2304850150304; + } else { + result[0] += -55.05691788452429; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 130))) { + result[0] += -678.96875763753; + } else { + result[0] += -223.46103248730483; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 170))) { + if (LIKELY(false || (data[4].qvalue <= 122))) { + if (LIKELY(false || (data[9].qvalue <= 16))) { + result[0] += -90.61321602369662; + } else { + result[0] += 261.0377505016003; + } + } else { + result[0] += 460.8651737454858; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -518.3029692442528; + } else { + if (UNLIKELY(false || (data[4].qvalue <= 102))) { + result[0] += 251.99443410934916; + } else { + result[0] += -168.5258406883114; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 88))) { + if (UNLIKELY(false || (data[0].qvalue <= 2))) { + result[0] += -93.52752297088361; + } else { + if (LIKELY(false || (data[1].qvalue <= 92))) { + if (UNLIKELY(false || (data[1].qvalue <= 38))) { + if (UNLIKELY(false || (data[0].qvalue <= 22))) { + result[0] += -28.36906776557731; + } else { + if (LIKELY(false || (data[4].qvalue <= 94))) { + result[0] += -3.2423893406338657; + } else { + result[0] += 138.541654434318; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 22))) { + result[0] += -46.28911386105568; + } else { + if (LIKELY(false || (data[3].qvalue <= 150))) { + result[0] += -22.298009717414818; + } else { + result[0] += -81.59335058549901; + } + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 88))) { + if (LIKELY(false || (data[4].qvalue <= 132))) { + result[0] += -66.17939589753632; + } else { + result[0] += -205.33954408242673; + } + } else { + result[0] += 8.493295470465377; + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 2))) { + if (LIKELY(false || (data[5].qvalue <= 6))) { + if (LIKELY(false || (data[0].qvalue <= 264))) { + if (UNLIKELY(false || (data[5].qvalue <= 0))) { + if (LIKELY(false || (data[0].qvalue <= 224))) { + result[0] += 326.1908307836102; + } else { + result[0] += 712.5672110896917; + } + } else { + result[0] += 165.97117149989344; + } + } else { + result[0] += 512.7394095968342; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 290))) { + result[0] += -88.61952314673287; + } else { + result[0] += 257.63671136127255; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 8))) { + if (LIKELY(false || (data[0].qvalue <= 464))) { + if (LIKELY(false || (data[3].qvalue <= 166))) { + if (LIKELY(false || (data[4].qvalue <= 82))) { + result[0] += -64.2972163819839; + } else { + result[0] += -678.5598075508324; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -743.1003478462518; + } else { + result[0] += 22.380609435146425; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (LIKELY(false || (data[1].qvalue <= 2))) { + result[0] += 668.8097667814556; + } else { + result[0] += 172.2438155603644; + } + } else { + result[0] += -103.48953914040013; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 14))) { + if (LIKELY(false || (data[9].qvalue <= 150))) { + if (UNLIKELY(false || (data[9].qvalue <= 112))) { + result[0] += 210.79347178390668; + } else { + result[0] += -66.34475012824036; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 8))) { + result[0] += 79.44162948474975; + } else { + result[0] += 423.58682172057206; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 154))) { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 58.63080216913425; + } else { + result[0] += 0.56660428680394; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 388))) { + result[0] += -354.99641077814067; + } else { + result[0] += 163.82541020213822; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 428))) { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[0].qvalue <= 364))) { + result[0] += -4.43201919585627; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 2))) { + if (LIKELY(false || (data[3].qvalue <= 148))) { + if (LIKELY(false || (data[4].qvalue <= 96))) { + result[0] += -461.34171468334483; + } else { + result[0] += -1974.1613360699155; + } + } else { + result[0] += 464.12272874367665; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 16))) { + if (UNLIKELY(false || (data[2].qvalue <= 116))) { + result[0] += -2425.075299189815; + } else { + result[0] += -330.4655251495208; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 72))) { + result[0] += 166.19696442999253; + } else { + result[0] += 9.303779051447991; + } + } + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 42))) { + result[0] += -481.590488188672; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 84))) { + if (UNLIKELY(false || (data[2].qvalue <= 154))) { + result[0] += -201.8241770516161; + } else { + if (LIKELY(false || (data[0].qvalue <= 400))) { + result[0] += 51.47643177844849; + } else { + result[0] += 557.8587391367337; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 356))) { + result[0] += -68.84035678322192; + } else { + result[0] += -259.4256502962815; + } + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 104))) { + if (LIKELY(false || (data[5].qvalue <= 100))) { + if (UNLIKELY(false || (data[0].qvalue <= 454))) { + if (UNLIKELY(false || (data[4].qvalue <= 78))) { + result[0] += 156.73836309324633; + } else { + if (LIKELY(false || (data[4].qvalue <= 138))) { + result[0] += -607.0488890568329; + } else { + result[0] += 9.03668551193256; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 100))) { + if (UNLIKELY(false || (data[6].qvalue <= 40))) { + result[0] += -1017.2723550166346; + } else { + result[0] += 91.3800484259382; + } + } else { + result[0] += -942.7966219121105; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 26))) { + if (UNLIKELY(false || (data[0].qvalue <= 464))) { + if (UNLIKELY(false || (data[4].qvalue <= 114))) { + result[0] += 346.1853239128133; + } else { + result[0] += -870.2639669615536; + } + } else { + result[0] += 136.37634000604268; + } + } else { + result[0] += 483.8076223319302; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 104))) { + if (UNLIKELY(false || (data[8].qvalue <= 76))) { + result[0] += 632.1932202439316; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 114))) { + result[0] += 859.7961658998064; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 110))) { + result[0] += 6.445434627545658; + } else { + result[0] += 232.20573728889286; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 450))) { + if (UNLIKELY(false || (data[10].qvalue <= 62))) { + result[0] += -766.6301885459153; + } else { + result[0] += -66.31539816252801; + } + } else { + result[0] += 73.09136207866754; + } + } + } + } + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (LIKELY(false || (data[10].qvalue <= 122))) { + if (LIKELY(false || (data[3].qvalue <= 102))) { + if (LIKELY(false || (data[7].qvalue <= 44))) { + if (UNLIKELY(false || (data[6].qvalue <= 24))) { + if (LIKELY(false || (data[7].qvalue <= 8))) { + result[0] += 28.34884333239003; + } else { + result[0] += -46.41782030735656; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 73.71689530827445; + } else { + result[0] += 1.4571574901978375; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 68))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += -319.74328138081097; + } else { + result[0] += 28.72734330489961; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 74))) { + result[0] += -392.30286128897285; + } else { + result[0] += -56.63552344488173; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + if (LIKELY(false || (data[8].qvalue <= 148))) { + if (UNLIKELY(false || (data[3].qvalue <= 134))) { + result[0] += -61.214933692783916; + } else { + result[0] += -207.6113943042064; + } + } else { + result[0] += -632.4056164963666; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 36))) { + if (LIKELY(false || (data[8].qvalue <= 28))) { + result[0] += 0.776759507119416; + } else { + result[0] += -563.4328564341248; + } + } else { + if (LIKELY(false || (data[8].qvalue <= 140))) { + result[0] += 80.66532309064849; + } else { + result[0] += -37.64303198703362; + } + } + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 128))) { + if (LIKELY(false || (data[9].qvalue <= 80))) { + if (UNLIKELY(false || (data[1].qvalue <= 142))) { + result[0] += 3.0907605764638824; + } else { + result[0] += -191.9470886917915; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 42))) { + result[0] += -535.3539427021846; + } else { + result[0] += -747.1267209093315; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 84))) { + if (UNLIKELY(false || (data[4].qvalue <= 48))) { + if (UNLIKELY(false || (data[1].qvalue <= 48))) { + result[0] += 39.65250739364507; + } else { + result[0] += -83.33176284276131; + } + } else { + if (LIKELY(false || (data[10].qvalue <= 144))) { + result[0] += 137.86981971555903; + } else { + result[0] += -47.78103683170895; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 6))) { + if (LIKELY(false || (data[8].qvalue <= 108))) { + result[0] += 265.07465212054825; + } else { + result[0] += -112.5511941043707; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 130))) { + result[0] += -185.10561993544567; + } else { + result[0] += -55.42540507337779; + } + } + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 200))) { + if (LIKELY(false || (data[1].qvalue <= 164))) { + result[0] += -71.05204569728328; + } else { + if (LIKELY(false || (data[3].qvalue <= 174))) { + result[0] += -229.96092256575585; + } else { + result[0] += -663.863689584278; + } + } + } else { + result[0] += -404.1617066176453; + } + } + if (LIKELY(false || (data[0].qvalue <= 434))) { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[0].qvalue <= 374))) { + if (LIKELY(false || (data[4].qvalue <= 120))) { + if (LIKELY(false || (data[2].qvalue <= 176))) { + if (LIKELY(false || (data[5].qvalue <= 80))) { + result[0] += -4.033785114047066; + } else { + result[0] += 51.688746674364644; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 42))) { + result[0] += -308.48199870168713; + } else { + result[0] += -36.130910480232906; + } + } + } else { + result[0] += -195.1197382760659; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 2))) { + if (LIKELY(false || (data[4].qvalue <= 110))) { + if (LIKELY(false || (data[4].qvalue <= 96))) { + result[0] += -408.3567730759215; + } else { + result[0] += -1707.6448987068966; + } + } else { + result[0] += 485.8821080747238; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 16))) { + if (UNLIKELY(false || (data[2].qvalue <= 116))) { + result[0] += -2096.4662113083464; + } else { + result[0] += -276.14738618844734; + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 68))) { + result[0] += -8.171064906722554; + } else { + result[0] += 155.11413889664198; + } + } + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 46))) { + result[0] += -426.0821034776104; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 84))) { + if (UNLIKELY(false || (data[2].qvalue <= 154))) { + if (LIKELY(false || (data[0].qvalue <= 420))) { + result[0] += -251.87299899417505; + } else { + result[0] += 171.7813777604329; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 188))) { + result[0] += 266.03680241951855; + } else { + result[0] += -230.94110204440864; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 98))) { + if (UNLIKELY(false || (data[9].qvalue <= 14))) { + result[0] += -51.4712788511078; + } else { + result[0] += -349.1585791389674; + } + } else { + result[0] += -87.62955894052737; + } + } + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 14))) { + result[0] += -962.914071451823; + } else { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (UNLIKELY(false || (data[2].qvalue <= 104))) { + if (UNLIKELY(false || (data[10].qvalue <= 4))) { + result[0] += 410.01446934535943; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 144))) { + result[0] += -158.79533984060308; + } else { + result[0] += 54.19442054392661; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 64))) { + result[0] += 800.9582024560236; + } else { + if (LIKELY(false || (data[2].qvalue <= 214))) { + result[0] += 152.70184903575273; + } else { + result[0] += -39.258349058638146; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + if (LIKELY(false || (data[10].qvalue <= 106))) { + if (UNLIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -385.9941662617043; + } else { + result[0] += 109.98038694597618; + } + } else { + result[0] += -519.7043617216136; + } + } else { + result[0] += 312.6413190598439; + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 450))) { + if (LIKELY(false || (data[6].qvalue <= 146))) { + if (LIKELY(false || (data[7].qvalue <= 198))) { + if (LIKELY(false || (data[0].qvalue <= 366))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + if (LIKELY(false || (data[1].qvalue <= 74))) { + result[0] += 137.297555227154; + } else { + result[0] += -254.9833262356102; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + result[0] += -123.87983644564534; + } else { + result[0] += -2.4145863679975013; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 6))) { + if (LIKELY(false || (data[6].qvalue <= 82))) { + result[0] += 51.07636765965205; + } else { + result[0] += -390.1200629057833; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 28))) { + result[0] += 215.48035596295546; + } else { + result[0] += 39.12939193887718; + } + } + } + } else { + result[0] += -235.5281315434429; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + result[0] += -534.086589766028; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 156))) { + if (LIKELY(false || (data[7].qvalue <= 166))) { + result[0] += -253.52810060662947; + } else { + if (UNLIKELY(false || (data[6].qvalue <= 164))) { + result[0] += 451.54660129512826; + } else { + result[0] += -6.922248765373611; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 168))) { + if (LIKELY(false || (data[0].qvalue <= 422))) { + result[0] += -87.41127255128306; + } else { + result[0] += 161.7062666527756; + } + } else { + if (LIKELY(false || (data[8].qvalue <= 96))) { + result[0] += -362.4373441748169; + } else { + result[0] += -47.688779690032106; + } + } + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (UNLIKELY(false || (data[6].qvalue <= 70))) { + if (LIKELY(false || (data[3].qvalue <= 118))) { + result[0] += -109.71948701870082; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -3168.276688179348; + } else { + result[0] += -655.8877042161603; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 108))) { + if (UNLIKELY(false || (data[7].qvalue <= 148))) { + if (LIKELY(false || (data[6].qvalue <= 138))) { + result[0] += 211.55392830640332; + } else { + result[0] += 544.2392075566188; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 460))) { + result[0] += -137.766397826111; + } else { + result[0] += 227.29084096946204; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 114))) { + if (UNLIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -1219.702639935603; + } else { + result[0] += 19.073660451518418; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 168))) { + result[0] += 180.8219311362737; + } else { + result[0] += -23.62961539090255; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (UNLIKELY(false || (data[4].qvalue <= 88))) { + result[0] += -24.096509384288314; + } else { + result[0] += -474.0824089535872; + } + } else { + result[0] += 151.94918465665242; + } + } + } + if (LIKELY(false || (data[8].qvalue <= 156))) { + if (LIKELY(false || (data[0].qvalue <= 450))) { + if (LIKELY(false || (data[6].qvalue <= 146))) { + if (LIKELY(false || (data[0].qvalue <= 344))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + if (LIKELY(false || (data[0].qvalue <= 222))) { + result[0] += 39.972789080579226; + } else { + result[0] += 214.53042647495874; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + result[0] += -103.36098764508137; + } else { + result[0] += -3.5862855800631728; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 144))) { + if (LIKELY(false || (data[5].qvalue <= 92))) { + result[0] += 33.48501259384832; + } else { + result[0] += -526.1972342777782; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 188))) { + result[0] += 291.7875686572814; + } else { + result[0] += -154.8400445536322; + } + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 50))) { + if (UNLIKELY(false || (data[6].qvalue <= 152))) { + if (LIKELY(false || (data[0].qvalue <= 436))) { + result[0] += -209.00331042067378; + } else { + result[0] += 476.30810024115374; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 284))) { + result[0] += -174.50986784466022; + } else { + result[0] += -612.8591006144763; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 154))) { + if (LIKELY(false || (data[6].qvalue <= 160))) { + result[0] += -17.361861237499827; + } else { + result[0] += -227.84455664039766; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 170))) { + result[0] += 141.6717472259187; + } else { + result[0] += -152.85677116643248; + } + } + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 16))) { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -2290.8527835669884; + } else { + result[0] += -90.00394883304172; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 0))) { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -670.6849931746262; + } else { + result[0] += -81.08626512407443; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 124))) { + if (UNLIKELY(false || (data[10].qvalue <= 44))) { + result[0] += 252.92553318096964; + } else { + result[0] += -237.02792802064724; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 26))) { + result[0] += -131.25931683115354; + } else { + result[0] += 155.92595301503033; + } + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 416))) { + if (LIKELY(false || (data[0].qvalue <= 350))) { + result[0] += -135.57120230355562; + } else { + result[0] += -444.05965584753494; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 130))) { + if (UNLIKELY(false || (data[0].qvalue <= 454))) { + result[0] += -617.9283577463137; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 168))) { + result[0] += 379.3453933956734; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 468))) { + result[0] += -555.1703256848654; + } else { + result[0] += 183.81435330137998; + } + } + } + } else { + result[0] += 287.9785027492729; + } + } + } + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (LIKELY(false || (data[0].qvalue <= 462))) { + if (LIKELY(false || (data[1].qvalue <= 160))) { + if (LIKELY(false || (data[6].qvalue <= 174))) { + if (LIKELY(false || (data[7].qvalue <= 166))) { + if (LIKELY(false || (data[7].qvalue <= 164))) { + result[0] += 1.9227744347010873; + } else { + result[0] += -372.9806790342511; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 162))) { + result[0] += 38.027655688643975; + } else { + result[0] += 330.84712805066437; + } + } + } else { + result[0] += -254.51641015239204; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 430))) { + result[0] += -29.47274599196618; + } else { + result[0] += -712.7969392811483; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 156))) { + if (LIKELY(false || (data[4].qvalue <= 108))) { + if (UNLIKELY(false || (data[9].qvalue <= 36))) { + result[0] += 557.7390203473873; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 58))) { + result[0] += 232.35239699183904; + } else { + result[0] += -374.7341949643208; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 470))) { + if (UNLIKELY(false || (data[9].qvalue <= 6))) { + result[0] += 438.92836082611086; + } else { + result[0] += -1439.2171157594041; + } + } else { + result[0] += -146.03654903994473; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 176))) { + if (LIKELY(false || (data[4].qvalue <= 130))) { + if (LIKELY(false || (data[5].qvalue <= 108))) { + result[0] += 321.2116732677101; + } else { + result[0] += 754.9708259292751; + } + } else { + result[0] += -22.672436608018547; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 70))) { + result[0] += 795.3453671875; + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -213.941419408782; + } else { + result[0] += 282.16809176472924; + } + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (UNLIKELY(false || (data[5].qvalue <= 32))) { + result[0] += -1600.845136858259; + } else { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (LIKELY(false || (data[0].qvalue <= 440))) { + if (UNLIKELY(false || (data[5].qvalue <= 58))) { + result[0] += -521.5318192486991; + } else { + result[0] += -96.30084920460428; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 218))) { + result[0] += 166.08593687552778; + } else { + result[0] += -341.6197044372843; + } + } + } else { + result[0] += -270.16752261143085; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 188))) { + result[0] += 379.4922879662453; + } else { + if (LIKELY(false || (data[1].qvalue <= 164))) { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + if (LIKELY(false || (data[3].qvalue <= 178))) { + result[0] += 59.33023477478028; + } else { + result[0] += -944.8980152625645; + } + } else { + result[0] += 362.7230938296502; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -1894.5816613281252; + } else { + result[0] += -151.54009237179278; + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 466))) { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (LIKELY(false || (data[2].qvalue <= 218))) { + if (LIKELY(false || (data[0].qvalue <= 428))) { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[10].qvalue <= 146))) { + result[0] += 2.61150493746397; + } else { + result[0] += -294.8989575123201; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 154))) { + result[0] += -144.860709390818; + } else { + result[0] += 49.05217218783569; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 104))) { + if (LIKELY(false || (data[2].qvalue <= 102))) { + result[0] += -69.84846406106546; + } else { + result[0] += -1099.694205050492; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 64))) { + result[0] += 547.2763598289783; + } else { + result[0] += 121.02960542039312; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 430))) { + if (UNLIKELY(false || (data[1].qvalue <= 112))) { + result[0] += -299.9131036348163; + } else { + result[0] += 87.20580911412755; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 162))) { + if (LIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -434.7498970777533; + } else { + result[0] += 375.0325198630567; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 174))) { + result[0] += -761.131324712887; + } else { + result[0] += -387.49766429457327; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 392))) { + result[0] += -94.7425416912909; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 154))) { + result[0] += -190.41458346534543; + } else { + result[0] += -405.4827892391865; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 200))) { + if (UNLIKELY(false || (data[7].qvalue <= 78))) { + if (UNLIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 373.04881527855287; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -1545.4975544084823; + } else { + result[0] += -255.72717774402136; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 98))) { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[1].qvalue <= 116))) { + result[0] += 450.93153398469025; + } else { + result[0] += 1014.2956421685988; + } + } else { + result[0] += 1245.0624796875002; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 148))) { + if (UNLIKELY(false || (data[8].qvalue <= 34))) { + result[0] += -547.0456131590985; + } else { + result[0] += -9.966885090949628; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 104))) { + result[0] += 305.58289721685486; + } else { + result[0] += 51.01148885239249; + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + if (LIKELY(false || (data[1].qvalue <= 162))) { + result[0] += -921.1548865874474; + } else { + result[0] += -1870.7389783653846; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 162))) { + result[0] += 236.7134294836506; + } else { + result[0] += -285.6914099083137; + } + } + } + } + if (LIKELY(false || (data[7].qvalue <= 174))) { + if (UNLIKELY(false || (data[5].qvalue <= 14))) { + if (UNLIKELY(false || (data[4].qvalue <= 2))) { + if (LIKELY(false || (data[2].qvalue <= 58))) { + if (LIKELY(false || (data[2].qvalue <= 52))) { + result[0] += 138.46445954751613; + } else { + result[0] += 30.25739363045126; + } + } else { + result[0] += 308.1558521464535; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 40))) { + if (LIKELY(false || (data[10].qvalue <= 52))) { + if (LIKELY(false || (data[8].qvalue <= 12))) { + result[0] += -8.275799037633549; + } else { + result[0] += -133.54923320626136; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 6))) { + result[0] += 175.8079980773678; + } else { + result[0] += 60.614919146381446; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 42))) { + result[0] += -434.16667085044185; + } else { + if (LIKELY(false || (data[4].qvalue <= 38))) { + result[0] += -41.16427694169654; + } else { + result[0] += -185.9750821120982; + } + } + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 22))) { + if (LIKELY(false || (data[3].qvalue <= 18))) { + if (LIKELY(false || (data[5].qvalue <= 20))) { + if (UNLIKELY(false || (data[4].qvalue <= 12))) { + result[0] += -122.25698184102133; + } else { + result[0] += 80.57705277421712; + } + } else { + result[0] += -153.60843419206896; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 22))) { + result[0] += 408.69803721206245; + } else { + if (UNLIKELY(false || (data[8].qvalue <= 90))) { + result[0] += 136.97672358425646; + } else { + result[0] += 270.9923573487174; + } + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 28))) { + if (LIKELY(false || (data[2].qvalue <= 122))) { + if (UNLIKELY(false || (data[3].qvalue <= 24))) { + result[0] += -159.08908713728795; + } else { + result[0] += -9.923411608506871; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 148))) { + result[0] += -521.1873971145586; + } else { + result[0] += 17.25752566881542; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 2))) { + if (UNLIKELY(false || (data[3].qvalue <= 64))) { + result[0] += 301.7622074237134; + } else { + result[0] += 122.86657003996795; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 4))) { + result[0] += -254.85463978758668; + } else { + result[0] += 5.494987706460857; + } + } + } + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 32))) { + result[0] += -834.7163352102; + } else { + if (LIKELY(false || (data[7].qvalue <= 200))) { + if (UNLIKELY(false || (data[8].qvalue <= 6))) { + result[0] += 172.7220039048895; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 161.15720525096583; + } else { + if (UNLIKELY(false || (data[8].qvalue <= 136))) { + result[0] += -107.08335478914324; + } else { + result[0] += -12.489971356584766; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 180))) { + result[0] += -405.35269670345906; + } else { + result[0] += -207.23569477608535; + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (LIKELY(false || (data[0].qvalue <= 392))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (LIKELY(false || (data[0].qvalue <= 292))) { + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + result[0] += -26.43222222269896; + } else { + result[0] += 7.909229573729924; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 30))) { + result[0] += 8.617729518857145; + } else { + result[0] += 108.90607449231234; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 74))) { + if (UNLIKELY(false || (data[10].qvalue <= 70))) { + result[0] += -1.9284671174533046; + } else { + result[0] += -105.2363711810878; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 106))) { + result[0] += 35.07140832310961; + } else { + result[0] += -52.187791710622804; + } + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 222))) { + if (UNLIKELY(false || (data[2].qvalue <= 86))) { + if (LIKELY(false || (data[2].qvalue <= 64))) { + result[0] += 53.903161702785724; + } else { + result[0] += -171.4159662862328; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 114))) { + result[0] += 123.56275048732999; + } else { + result[0] += 26.26029225107184; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -422.00141694181985; + } else { + result[0] += -107.9116343442738; + } + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 88))) { + if (LIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -288.46097298039064; + } else { + if (LIKELY(false || (data[7].qvalue <= 196))) { + result[0] += 375.28961351398226; + } else { + result[0] += -281.2952544835409; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 446))) { + result[0] += -17.901971338973112; + } else { + if (LIKELY(false || (data[6].qvalue <= 186))) { + result[0] += -312.93686064175296; + } else { + result[0] += -716.0857770302855; + } + } + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 88))) { + result[0] += 451.3160600734765; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 54))) { + if (UNLIKELY(false || (data[8].qvalue <= 16))) { + result[0] += 120.12627461414955; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -1770.8570323768029; + } else { + result[0] += -297.6523306745511; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 184))) { + if (LIKELY(false || (data[7].qvalue <= 196))) { + if (LIKELY(false || (data[0].qvalue <= 472))) { + result[0] += 199.35980767050484; + } else { + result[0] += 503.56640093198286; + } + } else { + result[0] += -518.0686588935853; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + if (LIKELY(false || (data[7].qvalue <= 188))) { + result[0] += -150.74680039702164; + } else { + result[0] += -1055.9052195002068; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 176))) { + result[0] += -46.963809276019184; + } else { + result[0] += 394.9533442330038; + } + } + } + } + } + } + if (LIKELY(false || (data[10].qvalue <= 120))) { + if (LIKELY(false || (data[5].qvalue <= 84))) { + if (LIKELY(false || (data[6].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 96))) { + if (LIKELY(false || (data[1].qvalue <= 92))) { + if (LIKELY(false || (data[8].qvalue <= 116))) { + result[0] += 14.869440540791798; + } else { + result[0] += -36.73557311757522; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 64))) { + result[0] += -538.0900500719082; + } else { + result[0] += -27.241161506715144; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 174))) { + result[0] += 132.0541996118715; + } else { + result[0] += -113.04252572422735; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 24))) { + if (LIKELY(false || (data[8].qvalue <= 68))) { + if (LIKELY(false || (data[1].qvalue <= 160))) { + result[0] += 244.86366814112222; + } else { + result[0] += -95.24641504542937; + } + } else { + result[0] += -76.8715657600925; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 114))) { + if (LIKELY(false || (data[5].qvalue <= 76))) { + result[0] += -120.29792651308429; + } else { + result[0] += 5.15502948111146; + } + } else { + result[0] += -227.26424899945584; + } + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 48))) { + if (LIKELY(false || (data[10].qvalue <= 72))) { + if (LIKELY(false || (data[6].qvalue <= 126))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += -511.47464005858114; + } else { + result[0] += 99.31151638687581; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 2))) { + result[0] += 139.52958863442427; + } else { + result[0] += -222.6339569962306; + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 112))) { + result[0] += -706.5550247771737; + } else { + result[0] += 32.62538379320284; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 152))) { + if (LIKELY(false || (data[8].qvalue <= 150))) { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -225.27583884895458; + } else { + result[0] += 97.88878371179209; + } + } else { + result[0] += -91.48452493663109; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 156))) { + if (UNLIKELY(false || (data[1].qvalue <= 148))) { + result[0] += -972.0166516770522; + } else { + result[0] += -3.8444739361171365; + } + } else { + result[0] += -30.275824314188043; + } + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 136))) { + if (LIKELY(false || (data[1].qvalue <= 128))) { + result[0] += -591.4492984187848; + } else { + result[0] += 52.69617940013822; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 86))) { + if (LIKELY(false || (data[2].qvalue <= 180))) { + result[0] += 84.41514196973255; + } else { + result[0] += -58.25664931323567; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 6))) { + result[0] += 79.8372134768803; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 190))) { + result[0] += -166.72699212355798; + } else { + result[0] += -39.28083649832746; + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 436))) { + if (LIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[0].qvalue <= 384))) { + if (LIKELY(false || (data[1].qvalue <= 126))) { + if (LIKELY(false || (data[4].qvalue <= 102))) { + if (LIKELY(false || (data[1].qvalue <= 102))) { + result[0] += 0.7528430355879543; + } else { + result[0] += -81.79712338566776; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 108))) { + result[0] += 169.93142796462274; + } else { + result[0] += -185.79991240603783; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 126))) { + if (LIKELY(false || (data[2].qvalue <= 120))) { + result[0] += -28.331180770557967; + } else { + result[0] += 672.7509955188011; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 274))) { + result[0] += -114.78306332631581; + } else { + result[0] += -408.53260808925637; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 198))) { + if (LIKELY(false || (data[6].qvalue <= 128))) { + if (UNLIKELY(false || (data[9].qvalue <= 16))) { + result[0] += -1018.0208814686271; + } else { + result[0] += 59.74134293981621; + } + } else { + result[0] += 293.35493295005193; + } + } else { + result[0] += -400.56582852441784; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 98))) { + if (UNLIKELY(false || (data[9].qvalue <= 14))) { + if (LIKELY(false || (data[0].qvalue <= 420))) { + result[0] += -74.3543481633016; + } else { + if (LIKELY(false || (data[1].qvalue <= 150))) { + result[0] += 571.9786783643856; + } else { + result[0] += -219.12600540624658; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 342))) { + result[0] += -101.20421903443618; + } else { + result[0] += -425.1879279279357; + } + } + } else { + result[0] += -38.8853994688072; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 0))) { + result[0] += -129.9772116102948; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 100))) { + if (UNLIKELY(false || (data[7].qvalue <= 56))) { + result[0] += 352.2167561334443; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 452))) { + if (LIKELY(false || (data[4].qvalue <= 104))) { + result[0] += -179.85826237208437; + } else { + result[0] += -1026.3738449938005; + } + } else { + result[0] += 86.91047265123943; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 140))) { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + if (UNLIKELY(false || (data[0].qvalue <= 448))) { + result[0] += -194.66018635911382; + } else { + result[0] += 165.22462004866262; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 112))) { + result[0] += 280.5581946286645; + } else { + result[0] += 803.432975071445; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 464))) { + if (LIKELY(false || (data[2].qvalue <= 218))) { + result[0] += 2.697588396559468; + } else { + result[0] += -478.252898298942; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 154))) { + result[0] += -208.4543974390897; + } else { + result[0] += 185.3089271840308; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 426))) { + if (LIKELY(false || (data[1].qvalue <= 124))) { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (LIKELY(false || (data[5].qvalue <= 86))) { + if (LIKELY(false || (data[6].qvalue <= 76))) { + if (LIKELY(false || (data[4].qvalue <= 80))) { + result[0] += -1.486803701497208; + } else { + result[0] += 82.51456493961574; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 26))) { + result[0] += -1724.8354696321032; + } else { + result[0] += -82.71048025173705; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 22))) { + if (UNLIKELY(false || (data[4].qvalue <= 110))) { + result[0] += -790.0620438667581; + } else { + result[0] += 106.26605184431772; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 378))) { + result[0] += 33.9153156537546; + } else { + result[0] += 173.09121370877705; + } + } + } + } else { + result[0] += -154.93054492116286; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 54))) { + if (LIKELY(false || (data[2].qvalue <= 50))) { + if (LIKELY(false || (data[2].qvalue <= 42))) { + if (LIKELY(false || (data[0].qvalue <= 392))) { + result[0] += -1.9879083035835015; + } else { + result[0] += 398.5109346540218; + } + } else { + result[0] += -786.5885010553891; + } + } else { + result[0] += 430.4466811037737; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 342))) { + result[0] += -47.917740653423564; + } else { + if (LIKELY(false || (data[0].qvalue <= 412))) { + if (LIKELY(false || (data[2].qvalue <= 164))) { + result[0] += -361.3887876613474; + } else { + result[0] += -59.68743934462101; + } + } else { + result[0] += -24.34286763077365; + } + } + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 10))) { + result[0] += 711.5911056007745; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 88))) { + if (LIKELY(false || (data[0].qvalue <= 454))) { + if (LIKELY(false || (data[2].qvalue <= 64))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += -939.5055510805191; + } else { + result[0] += 49.38415531008706; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 128))) { + result[0] += -607.7336297438973; + } else { + result[0] += 231.05599252528052; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 40))) { + result[0] += -676.7676630675595; + } else { + result[0] += 107.56624878796568; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 104))) { + if (UNLIKELY(false || (data[3].qvalue <= 110))) { + if (UNLIKELY(false || (data[7].qvalue <= 52))) { + result[0] += 470.39671526527695; + } else { + result[0] += 19.64177427856826; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 456))) { + result[0] += 257.8561111510292; + } else { + result[0] += -156.72749393540863; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 112))) { + result[0] += 541.0799146065921; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 444))) { + result[0] += -223.96127054701944; + } else { + result[0] += 35.39412132031614; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 458))) { + if (LIKELY(false || (data[2].qvalue <= 200))) { + if (LIKELY(false || (data[3].qvalue <= 172))) { + if (LIKELY(false || (data[2].qvalue <= 198))) { + if (LIKELY(false || (data[0].qvalue <= 392))) { + if (LIKELY(false || (data[2].qvalue <= 180))) { + result[0] += -1.1948610469474203; + } else { + result[0] += -80.88158309915174; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + result[0] += -96.14071101510345; + } else { + result[0] += 63.358216911141255; + } + } + } else { + result[0] += 176.15313222545365; + } + } else { + result[0] += -326.1906626097021; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 116))) { + if (LIKELY(false || (data[0].qvalue <= 430))) { + if (UNLIKELY(false || (data[8].qvalue <= 146))) { + if (LIKELY(false || (data[0].qvalue <= 400))) { + result[0] += 40.65129948483353; + } else { + result[0] += 623.8755186699341; + } + } else { + result[0] += -139.52193586780237; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (LIKELY(false || (data[0].qvalue <= 446))) { + result[0] += 215.51009220017923; + } else { + result[0] += 626.7644565408536; + } + } else { + result[0] += -57.77880667756001; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 78))) { + result[0] += -345.1537827658937; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 206))) { + if (UNLIKELY(false || (data[0].qvalue <= 368))) { + result[0] += 120.97750370682058; + } else { + result[0] += -530.0146207114068; + } + } else { + result[0] += -90.90617312134356; + } + } + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 128))) { + if (LIKELY(false || (data[2].qvalue <= 222))) { + if (UNLIKELY(false || (data[8].qvalue <= 44))) { + if (UNLIKELY(false || (data[2].qvalue <= 30))) { + if (LIKELY(false || (data[4].qvalue <= 114))) { + result[0] += 405.6146661410597; + } else { + result[0] += 22.37476339760467; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 78))) { + result[0] += -499.28634142340854; + } else { + result[0] += 68.9305749234034; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 90))) { + if (LIKELY(false || (data[9].qvalue <= 86))) { + result[0] += 529.6389184175102; + } else { + result[0] += 95.91732717933235; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 158))) { + result[0] += 194.2271286977213; + } else { + result[0] += -22.243321533686604; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -322.8706246634995; + } else { + result[0] += 171.93787845774688; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -280.2205757227194; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 16))) { + result[0] += 805.6007065468135; + } else { + if (LIKELY(false || (data[9].qvalue <= 16))) { + if (LIKELY(false || (data[7].qvalue <= 192))) { + result[0] += 103.74300879356734; + } else { + result[0] += -219.28913112606025; + } + } else { + result[0] += -516.2170829126527; + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 54))) { + if (UNLIKELY(false || (data[0].qvalue <= 0))) { + result[0] += -98.93297408058446; + } else { + if (LIKELY(false || (data[4].qvalue <= 60))) { + if (LIKELY(false || (data[3].qvalue <= 126))) { + if (UNLIKELY(false || (data[0].qvalue <= 18))) { + result[0] += -35.246449455925166; + } else { + result[0] += -11.913885864248398; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 12))) { + result[0] += 244.38629528569243; + } else { + result[0] += -59.63764056448275; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 136))) { + result[0] += -67.53814213480176; + } else { + result[0] += 13.863392069333836; + } + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + if (LIKELY(false || (data[0].qvalue <= 232))) { + if (UNLIKELY(false || (data[2].qvalue <= 24))) { + result[0] += 243.2273344714578; + } else { + if (LIKELY(false || (data[2].qvalue <= 52))) { + result[0] += 56.08540095837141; + } else { + result[0] += -73.3251905895068; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 128))) { + if (UNLIKELY(false || (data[2].qvalue <= 24))) { + result[0] += 643.1260108343162; + } else { + result[0] += 308.232898319556; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 318))) { + result[0] += -85.94227185477129; + } else { + result[0] += 339.32448533355216; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 196))) { + result[0] += 295.57345918020815; + } else { + result[0] += 541.3180556951017; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 156))) { + if (UNLIKELY(false || (data[4].qvalue <= 6))) { + if (LIKELY(false || (data[4].qvalue <= 4))) { + if (UNLIKELY(false || (data[6].qvalue <= 2))) { + result[0] += 176.28854142574565; + } else { + result[0] += -16.26659984192861; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 224))) { + result[0] += 272.5245026026528; + } else { + result[0] += 630.8310847742418; + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 10))) { + if (LIKELY(false || (data[10].qvalue <= 76))) { + result[0] += -99.36810379968507; + } else { + result[0] += -462.5017447936385; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 146))) { + result[0] += 3.9186464796296914; + } else { + result[0] += 225.22750105396563; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 364))) { + if (UNLIKELY(false || (data[0].qvalue <= 178))) { + if (LIKELY(false || (data[1].qvalue <= 22))) { + result[0] += -168.88397604209945; + } else { + result[0] += 38.12118179055833; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 14))) { + result[0] += -208.349456121144; + } else { + result[0] += -434.7176030497106; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 428))) { + if (LIKELY(false || (data[1].qvalue <= 22))) { + result[0] += 147.36975160660015; + } else { + result[0] += -427.1472748332084; + } + } else { + result[0] += 468.6802177857324; + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[5].qvalue <= 118))) { + if (LIKELY(false || (data[0].qvalue <= 438))) { + if (LIKELY(false || (data[7].qvalue <= 194))) { + if (LIKELY(false || (data[1].qvalue <= 130))) { + if (LIKELY(false || (data[4].qvalue <= 106))) { + result[0] += 0.3368638092539733; + } else { + result[0] += 173.775034577441; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 60))) { + result[0] += 48.85885497450373; + } else { + result[0] += -96.87483192360062; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 346))) { + result[0] += -78.03276293917031; + } else { + if (LIKELY(false || (data[0].qvalue <= 428))) { + result[0] += -418.6164374426745; + } else { + result[0] += -98.44042608027418; + } + } + } + } else { + if (LIKELY(false || (data[5].qvalue <= 112))) { + if (UNLIKELY(false || (data[9].qvalue <= 8))) { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -275.91209245174423; + } else { + result[0] += 99.25590915465185; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 82))) { + result[0] += -41.70434165862219; + } else { + result[0] += 130.83735776026924; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 4))) { + if (UNLIKELY(false || (data[0].qvalue <= 444))) { + result[0] += 208.90021667361708; + } else { + result[0] += 614.78246500265; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 452))) { + result[0] += -186.66325247313335; + } else { + result[0] += 252.69463305407146; + } + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 158))) { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -243.87323720092883; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 160))) { + result[0] += 607.3841937390166; + } else { + if (LIKELY(false || (data[2].qvalue <= 224))) { + result[0] += -4.642945887934479; + } else { + result[0] += -425.1676612575311; + } + } + } + } else { + result[0] += -226.78722863564013; + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 88))) { + if (LIKELY(false || (data[2].qvalue <= 226))) { + result[0] += 457.093631103365; + } else { + result[0] += -195.01605350860177; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 54))) { + if (UNLIKELY(false || (data[8].qvalue <= 16))) { + result[0] += 121.71032578695974; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -1535.090860877404; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += -642.94729679988; + } else { + result[0] += -89.58887867028852; + } + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 88))) { + if (UNLIKELY(false || (data[5].qvalue <= 72))) { + result[0] += -178.884099836437; + } else { + result[0] += 349.2407402511536; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 144))) { + result[0] += -1194.8087333496094; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -154.55357593204099; + } else { + result[0] += 257.1888927003237; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 112))) { + if (UNLIKELY(false || (data[0].qvalue <= 0))) { + result[0] += -89.14531598408416; + } else { + result[0] += -18.084829562955424; + } + } else { + if (LIKELY(false || (data[8].qvalue <= 138))) { + if (LIKELY(false || (data[3].qvalue <= 116))) { + if (LIKELY(false || (data[8].qvalue <= 118))) { + if (UNLIKELY(false || (data[4].qvalue <= 38))) { + if (LIKELY(false || (data[1].qvalue <= 44))) { + result[0] += 16.943599637772344; + } else { + result[0] += 106.06836417622998; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 42))) { + result[0] += -528.7742969051144; + } else { + result[0] += -9.291722918418776; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 136))) { + if (LIKELY(false || (data[2].qvalue <= 132))) { + result[0] += -118.09966496394628; + } else { + result[0] += -560.1406224711944; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 140))) { + result[0] += -51.02960535934701; + } else { + result[0] += 338.4129630811006; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 104))) { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + if (LIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -248.21126531471361; + } else { + result[0] += 386.6061213337075; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 376))) { + result[0] += 94.34550988961043; + } else { + result[0] += 304.2723529855085; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 436))) { + if (LIKELY(false || (data[8].qvalue <= 106))) { + result[0] += -8.896943765616806; + } else { + result[0] += -245.20061258455573; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 60))) { + result[0] += -113.87022533457596; + } else { + result[0] += 116.77525773183898; + } + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 152))) { + if (LIKELY(false || (data[0].qvalue <= 428))) { + if (UNLIKELY(false || (data[0].qvalue <= 224))) { + result[0] += -624.1711191522509; + } else { + if (LIKELY(false || (data[0].qvalue <= 414))) { + result[0] += -1347.2302644189722; + } else { + result[0] += -733.7942435464515; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 60))) { + result[0] += 471.0712008104827; + } else { + result[0] += -142.56709666372134; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + if (UNLIKELY(false || (data[9].qvalue <= 104))) { + if (UNLIKELY(false || (data[2].qvalue <= 192))) { + result[0] += -1567.6200324035817; + } else { + result[0] += -98.50162535806925; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 398))) { + result[0] += -50.25901125894962; + } else { + result[0] += 60.40281064144929; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 102))) { + if (LIKELY(false || (data[3].qvalue <= 178))) { + result[0] += 379.87726438502557; + } else { + result[0] += -199.4001040982219; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -287.5563657821928; + } else { + result[0] += 332.3956673905034; + } + } + } + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 232))) { + if (UNLIKELY(false || (data[4].qvalue <= 36))) { + if (LIKELY(false || (data[1].qvalue <= 40))) { + if (LIKELY(false || (data[4].qvalue <= 30))) { + result[0] += -1.565030040146726; + } else { + result[0] += -104.9566604701055; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 138))) { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += -17.037935890389736; + } else { + result[0] += 63.289328847278654; + } + } else { + result[0] += -1324.6271531918176; + } + } + } else { + result[0] += -20.81789198275515; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 34))) { + if (LIKELY(false || (data[8].qvalue <= 100))) { + if (LIKELY(false || (data[8].qvalue <= 98))) { + if (LIKELY(false || (data[2].qvalue <= 134))) { + if (UNLIKELY(false || (data[0].qvalue <= 290))) { + result[0] += 2.015327035742837; + } else { + result[0] += 76.49554789592639; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 402))) { + result[0] += -823.5010456874027; + } else { + result[0] += 182.88330508869717; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 376))) { + result[0] += -684.3245220762328; + } else { + result[0] += 114.75508963752516; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 92))) { + result[0] += 584.9961795342024; + } else { + if (UNLIKELY(false || (data[9].qvalue <= 118))) { + result[0] += 284.6160485461698; + } else { + if (LIKELY(false || (data[0].qvalue <= 342))) { + result[0] += -0.3173057744465385; + } else { + result[0] += 255.28193335274855; + } + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 44))) { + if (UNLIKELY(false || (data[5].qvalue <= 24))) { + if (LIKELY(false || (data[3].qvalue <= 18))) { + if (LIKELY(false || (data[0].qvalue <= 368))) { + result[0] += -109.2669803718638; + } else { + result[0] += 49.7918489437198; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 26))) { + result[0] += -35.33314939837475; + } else { + result[0] += 383.1659807220039; + } + } + } else { + if (UNLIKELY(false || (data[11].qvalue <= 0))) { + if (LIKELY(false || (data[8].qvalue <= 80))) { + result[0] += -119.6036066769413; + } else { + result[0] += -530.947990035426; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + result[0] += -80.63511119863223; + } else { + result[0] += 415.2734672989832; + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 8))) { + if (UNLIKELY(false || (data[3].qvalue <= 68))) { + result[0] += 704.1141519325657; + } else { + if (UNLIKELY(false || (data[8].qvalue <= 2))) { + result[0] += 84.60147882812862; + } else { + result[0] += -187.8254210699395; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 70))) { + if (LIKELY(false || (data[5].qvalue <= 90))) { + result[0] += 121.89790445304102; + } else { + result[0] += -115.0763981218671; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 72))) { + result[0] += -654.9303551006227; + } else { + result[0] += 7.981521248472231; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 420))) { + if (LIKELY(false || (data[6].qvalue <= 144))) { + if (LIKELY(false || (data[10].qvalue <= 146))) { + if (LIKELY(false || (data[2].qvalue <= 210))) { + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (LIKELY(false || (data[0].qvalue <= 358))) { + result[0] += -2.9256693373180407; + } else { + result[0] += 52.86899999050905; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 162))) { + result[0] += -236.3933458780788; + } else { + result[0] += -14.339195301019586; + } + } + } else { + result[0] += -219.14539537485498; + } + } else { + result[0] += -318.42234277715806; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 154))) { + if (UNLIKELY(false || (data[0].qvalue <= 274))) { + result[0] += -22.289702159300546; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 42))) { + result[0] += 180.36521926437015; + } else { + result[0] += -260.09045231531485; + } + } + } else { + result[0] += 44.37903776463972; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 86))) { + if (UNLIKELY(false || (data[0].qvalue <= 440))) { + if (UNLIKELY(false || (data[7].qvalue <= 46))) { + if (LIKELY(false || (data[1].qvalue <= 90))) { + result[0] += -68.81869559889574; + } else { + result[0] += 716.3498421781522; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 106))) { + if (LIKELY(false || (data[4].qvalue <= 78))) { + result[0] += -171.83362174216776; + } else { + result[0] += -984.3537857945884; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 100))) { + result[0] += -338.7075403591485; + } else { + result[0] += 123.96422662235261; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 164))) { + if (LIKELY(false || (data[9].qvalue <= 126))) { + if (UNLIKELY(false || (data[0].qvalue <= 454))) { + result[0] += -204.18509525988378; + } else { + result[0] += 64.71659005621359; + } + } else { + result[0] += 817.3359237308946; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 26))) { + result[0] += -262.7801965080877; + } else { + result[0] += 612.6525648856328; + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 140))) { + if (LIKELY(false || (data[8].qvalue <= 128))) { + if (LIKELY(false || (data[6].qvalue <= 150))) { + if (LIKELY(false || (data[1].qvalue <= 148))) { + result[0] += 369.7289746082688; + } else { + result[0] += -103.32913617071652; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 22))) { + result[0] += -25.94812018229829; + } else { + result[0] += 299.29536905910857; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + result[0] += 18.803068756821325; + } else { + result[0] += 861.4705320103184; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 118))) { + result[0] += 148.92751319365524; + } else { + if (UNLIKELY(false || (data[6].qvalue <= 126))) { + if (UNLIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -1775.7163414874296; + } else { + result[0] += 74.55151140491274; + } + } else { + result[0] += -10.06076847351882; + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 450))) { + if (LIKELY(false || (data[1].qvalue <= 148))) { + if (UNLIKELY(false || (data[10].qvalue <= 30))) { + if (LIKELY(false || (data[6].qvalue <= 152))) { + if (UNLIKELY(false || (data[9].qvalue <= 92))) { + if (LIKELY(false || (data[8].qvalue <= 12))) { + result[0] += 21.302765815704674; + } else { + result[0] += -269.2691797738821; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 14))) { + result[0] += -63.12874563850693; + } else { + result[0] += 62.93227886630939; + } + } + } else { + result[0] += -400.8612725252076; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 424))) { + if (LIKELY(false || (data[0].qvalue <= 386))) { + result[0] += 58.49853608139253; + } else { + result[0] += 379.42843601568325; + } + } else { + result[0] += -1179.5767049009407; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 100))) { + result[0] += 19.778425640043217; + } else { + result[0] += -856.1976997050459; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 424))) { + result[0] += -5.97846202630444; + } else { + result[0] += 86.90154167537219; + } + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 102))) { + result[0] += 410.7033838491514; + } else { + if (UNLIKELY(false || (data[4].qvalue <= 112))) { + result[0] += -278.12154040332797; + } else { + result[0] += -70.26805980080006; + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 180))) { + if (LIKELY(false || (data[4].qvalue <= 136))) { + if (UNLIKELY(false || (data[8].qvalue <= 46))) { + if (LIKELY(false || (data[9].qvalue <= 52))) { + if (UNLIKELY(false || (data[4].qvalue <= 108))) { + result[0] += 202.68141819077738; + } else { + result[0] += -167.2132011812022; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -1384.5104673530457; + } else { + result[0] += -189.4566731860187; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 140))) { + if (LIKELY(false || (data[7].qvalue <= 132))) { + result[0] += 187.09927614728926; + } else { + result[0] += 567.0117546161409; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -93.51709182438407; + } else { + result[0] += 121.54129938504501; + } + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 140))) { + result[0] += 465.57152458676364; + } else { + result[0] += -479.2019554780659; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + if (LIKELY(false || (data[10].qvalue <= 106))) { + if (LIKELY(false || (data[7].qvalue <= 200))) { + if (UNLIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -220.95806736832415; + } else { + result[0] += 116.17247093054512; + } + } else { + result[0] += -729.6717337371826; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 164))) { + result[0] += -312.7238552339285; + } else { + result[0] += -1140.467401624576; + } + } + } else { + result[0] += 195.02478855827175; + } + } + } + if (LIKELY(false || (data[8].qvalue <= 154))) { + if (LIKELY(false || (data[0].qvalue <= 392))) { + if (LIKELY(false || (data[6].qvalue <= 62))) { + if (LIKELY(false || (data[0].qvalue <= 310))) { + if (LIKELY(false || (data[8].qvalue <= 120))) { + if (LIKELY(false || (data[8].qvalue <= 100))) { + result[0] += -9.71572720333726; + } else { + result[0] += 67.59923309810226; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 52))) { + result[0] += -351.55140420047667; + } else { + result[0] += -16.616159911071314; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 72))) { + if (LIKELY(false || (data[4].qvalue <= 66))) { + result[0] += 69.88081147655924; + } else { + result[0] += -969.7781215245079; + } + } else { + result[0] += 228.67764875201647; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 4))) { + if (UNLIKELY(false || (data[10].qvalue <= 2))) { + result[0] += 74.35381121983716; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 138))) { + result[0] += -62.154687912270276; + } else { + result[0] += -796.2456169363136; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 56))) { + if (LIKELY(false || (data[4].qvalue <= 86))) { + result[0] += -156.1506230350774; + } else { + result[0] += 119.82853149552272; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 106))) { + result[0] += 13.900388157788383; + } else { + result[0] += -42.87238835775265; + } + } + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 78))) { + if (UNLIKELY(false || (data[6].qvalue <= 58))) { + if (LIKELY(false || (data[6].qvalue <= 56))) { + if (LIKELY(false || (data[2].qvalue <= 156))) { + result[0] += -25.79300413884613; + } else { + result[0] += 449.6201546856599; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 424))) { + result[0] += -1246.7840846746014; + } else { + result[0] += -60.57121668348967; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 18))) { + if (UNLIKELY(false || (data[0].qvalue <= 416))) { + result[0] += -507.2811346565345; + } else { + result[0] += 1.6959367259683802; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 130))) { + result[0] += 256.5119388340715; + } else { + result[0] += 61.901222326179735; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 4))) { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -712.841039622884; + } else { + result[0] += -50.285859444198785; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 168))) { + if (UNLIKELY(false || (data[7].qvalue <= 80))) { + result[0] += -89.82284215276091; + } else { + result[0] += 57.2312430185315; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 462))) { + result[0] += -196.41216488031796; + } else { + result[0] += 64.87819986422424; + } + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 422))) { + if (LIKELY(false || (data[0].qvalue <= 352))) { + result[0] += -54.40201249787878; + } else { + result[0] += -323.09540305923275; + } + } else { + result[0] += 22.58190522531515; + } + } + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[2].qvalue <= 212))) { + if (LIKELY(false || (data[0].qvalue <= 396))) { + if (UNLIKELY(false || (data[9].qvalue <= 62))) { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 320.8771929320019; + } else { + result[0] += -48.74134204044459; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 64))) { + result[0] += 144.15507685887556; + } else { + result[0] += 2.061937768452418; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 168))) { + if (LIKELY(false || (data[8].qvalue <= 142))) { + result[0] += 14.310048813060831; + } else { + result[0] += -891.4821913653136; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 186))) { + result[0] += 230.68364280672665; + } else { + result[0] += 31.828982825162395; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 460))) { + if (UNLIKELY(false || (data[8].qvalue <= 148))) { + if (LIKELY(false || (data[0].qvalue <= 446))) { + result[0] += 5.3923912609412765; + } else { + result[0] += -296.2815883663324; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 18))) { + result[0] += 0.33503809236265447; + } else { + result[0] += -222.23518809470139; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 162))) { + result[0] += 435.30908950076656; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 220))) { + result[0] += 151.71039930723558; + } else { + result[0] += -220.68651987712957; + } + } + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 158))) { + if (LIKELY(false || (data[5].qvalue <= 106))) { + if (LIKELY(false || (data[8].qvalue <= 34))) { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -761.8192641261176; + } else { + result[0] += -24.696202075383436; + } + } else { + result[0] += 297.8870552382013; + } + } else { + result[0] += -1111.3451507308962; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 180))) { + result[0] += 399.4329665421262; + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -33.631317828261764; + } else { + if (LIKELY(false || (data[6].qvalue <= 186))) { + result[0] += 521.4533408866705; + } else { + result[0] += -47.855811708641056; + } + } + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 438))) { + if (UNLIKELY(false || (data[0].qvalue <= 352))) { + result[0] += -42.87267583810598; + } else { + result[0] += -282.3784003570174; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 132))) { + result[0] += 156.2716050426377; + } else { + if (UNLIKELY(false || (data[8].qvalue <= 144))) { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + if (UNLIKELY(false || (data[2].qvalue <= 154))) { + result[0] += -106.3379635111628; + } else { + result[0] += -799.4368728340669; + } + } else { + result[0] += -30.901543195994442; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -278.9996333283842; + } else { + result[0] += 97.21542436889949; + } + } + } + } + } + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[0].qvalue <= 198))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + if (UNLIKELY(false || (data[2].qvalue <= 24))) { + if (UNLIKELY(false || (data[0].qvalue <= 62))) { + result[0] += -71.54944060418909; + } else { + result[0] += 200.91246808639335; + } + } else { + result[0] += -16.524995964984008; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 56))) { + result[0] += -6.615663904946698; + } else { + result[0] += 277.0038505094405; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 128))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + result[0] += 242.12226853180888; + } else { + result[0] += 495.40205234661437; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 308))) { + result[0] += -144.40748272556144; + } else { + result[0] += 244.60940232429508; + } + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 156))) { + if (UNLIKELY(false || (data[4].qvalue <= 6))) { + if (LIKELY(false || (data[4].qvalue <= 4))) { + if (LIKELY(false || (data[9].qvalue <= 144))) { + if (UNLIKELY(false || (data[10].qvalue <= 12))) { + result[0] += 151.95679595717183; + } else { + result[0] += -75.40462882328015; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 248))) { + result[0] += 59.16238539562008; + } else { + result[0] += 258.58095117618336; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 194))) { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 34.722618898156114; + } else { + result[0] += 229.7578373765491; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 254))) { + result[0] += 416.11542151307907; + } else { + result[0] += 657.1399509168027; + } + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 10))) { + if (LIKELY(false || (data[0].qvalue <= 396))) { + if (LIKELY(false || (data[10].qvalue <= 76))) { + result[0] += -99.76580517101934; + } else { + result[0] += -406.79586814929917; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 410))) { + result[0] += 67.21864653476916; + } else { + result[0] += 318.3788762612092; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 152))) { + if (UNLIKELY(false || (data[1].qvalue <= 8))) { + result[0] += -89.91859493216367; + } else { + result[0] += 2.265520650788892; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 182))) { + result[0] += 86.60159851878495; + } else { + result[0] += 446.4563385244049; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 426))) { + if (UNLIKELY(false || (data[0].qvalue <= 166))) { + result[0] += -85.71042457905949; + } else { + if (LIKELY(false || (data[0].qvalue <= 356))) { + if (LIKELY(false || (data[1].qvalue <= 14))) { + result[0] += -197.6560417052055; + } else { + result[0] += -382.76909364031883; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 22))) { + result[0] += 86.90468637807797; + } else { + result[0] += -409.69153384359265; + } + } + } + } else { + result[0] += 370.0893122985466; + } + } + } + if (LIKELY(false || (data[0].qvalue <= 450))) { + if (LIKELY(false || (data[1].qvalue <= 130))) { + if (LIKELY(false || (data[4].qvalue <= 106))) { + if (UNLIKELY(false || (data[9].qvalue <= 16))) { + result[0] += -778.7936840058063; + } else { + if (UNLIKELY(false || (data[9].qvalue <= 20))) { + result[0] += 194.97101648633375; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 30))) { + result[0] += -35.3509348883947; + } else { + result[0] += 5.30025526653546; + } + } + } + } else { + result[0] += 128.95468067576573; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 106))) { + if (UNLIKELY(false || (data[8].qvalue <= 68))) { + if (LIKELY(false || (data[2].qvalue <= 120))) { + result[0] += -34.18950997644831; + } else { + result[0] += 621.8798392741791; + } + } else { + result[0] += -155.4137360966938; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 108))) { + if (LIKELY(false || (data[0].qvalue <= 430))) { + if (LIKELY(false || (data[7].qvalue <= 108))) { + result[0] += 256.5188225022511; + } else { + result[0] += -99.67316593885579; + } + } else { + result[0] += 620.485300659375; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 100))) { + result[0] += -2286.0077275800704; + } else { + if (LIKELY(false || (data[5].qvalue <= 116))) { + result[0] += -53.309956527903466; + } else { + result[0] += 190.13761434101764; + } + } + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 136))) { + if (UNLIKELY(false || (data[2].qvalue <= 78))) { + if (LIKELY(false || (data[1].qvalue <= 142))) { + if (UNLIKELY(false || (data[7].qvalue <= 76))) { + if (LIKELY(false || (data[2].qvalue <= 68))) { + result[0] += 253.43645195632422; + } else { + result[0] += -1413.0786233422066; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 462))) { + result[0] += -690.7561180054086; + } else { + result[0] += -63.707054709028625; + } + } + } else { + result[0] += 291.7091149857328; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 120))) { + if (UNLIKELY(false || (data[8].qvalue <= 128))) { + result[0] += 478.56900909093986; + } else { + result[0] += 167.99342824787573; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 464))) { + result[0] += 60.9554692627185; + } else { + result[0] += -1073.8503825143669; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 464))) { + if (LIKELY(false || (data[4].qvalue <= 136))) { + if (UNLIKELY(false || (data[8].qvalue <= 52))) { + result[0] += -539.7433428985337; + } else { + if (LIKELY(false || (data[1].qvalue <= 160))) { + result[0] += 20.89762446651438; + } else { + result[0] += -416.20782851777005; + } + } + } else { + result[0] += 361.5994213025975; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 172))) { + if (LIKELY(false || (data[1].qvalue <= 160))) { + if (UNLIKELY(false || (data[10].qvalue <= 34))) { + result[0] += 537.4931809409429; + } else { + result[0] += 93.3123402983442; + } + } else { + result[0] += 509.56219742394296; + } + } else { + result[0] += -30.502598904934388; + } + } + } + } + if (UNLIKELY(false || (data[4].qvalue <= 18))) { + if (UNLIKELY(false || (data[9].qvalue <= 108))) { + if (UNLIKELY(false || (data[0].qvalue <= 170))) { + result[0] += 32.02621609860586; + } else { + result[0] += 195.61137056108709; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 116))) { + result[0] += -56.120014772691675; + } else { + if (UNLIKELY(false || (data[9].qvalue <= 120))) { + if (UNLIKELY(false || (data[8].qvalue <= 38))) { + result[0] += 258.16901404153344; + } else { + result[0] += 53.61413424410968; + } + } else { + result[0] += 3.8083516584014774; + } + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 30))) { + if (LIKELY(false || (data[8].qvalue <= 12))) { + if (UNLIKELY(false || (data[10].qvalue <= 2))) { + result[0] += 95.68803265337215; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 8))) { + if (LIKELY(false || (data[3].qvalue <= 132))) { + result[0] += -103.63143270824092; + } else { + result[0] += -416.841719094304; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 452))) { + result[0] += 29.00180398543003; + } else { + result[0] += 739.6504496232433; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 442))) { + if (UNLIKELY(false || (data[9].qvalue <= 44))) { + result[0] += -1170.886850360489; + } else { + if (UNLIKELY(false || (data[9].qvalue <= 78))) { + result[0] += -23.599978133418663; + } else { + result[0] += -233.86794128469188; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 100))) { + result[0] += 296.8371355378329; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -578.3205363336284; + } else { + result[0] += 28.31820900489086; + } + } + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 110))) { + if (UNLIKELY(false || (data[2].qvalue <= 34))) { + if (LIKELY(false || (data[0].qvalue <= 270))) { + result[0] += 15.084899225242879; + } else { + if (UNLIKELY(false || (data[6].qvalue <= 44))) { + result[0] += 227.37067720434766; + } else { + result[0] += 79.75576158468904; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 66))) { + if (LIKELY(false || (data[2].qvalue <= 64))) { + result[0] += -21.445175019440377; + } else { + result[0] += -736.583490831738; + } + } else { + if (LIKELY(false || (data[10].qvalue <= 122))) { + result[0] += 19.304014117388515; + } else { + result[0] += -31.807962860341632; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 52))) { + if (LIKELY(false || (data[6].qvalue <= 20))) { + if (LIKELY(false || (data[2].qvalue <= 76))) { + result[0] += -2.770553189772293; + } else { + result[0] += -459.67841100976295; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 402))) { + result[0] += -654.9440592987792; + } else { + result[0] += -113.53853189933453; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 52))) { + result[0] += 141.69815316018148; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 74))) { + result[0] += -502.039703472783; + } else { + result[0] += -32.3855029558517; + } + } + } + } + } + } + if (LIKELY(false || (data[7].qvalue <= 176))) { + if (LIKELY(false || (data[3].qvalue <= 122))) { + if (LIKELY(false || (data[6].qvalue <= 68))) { + if (LIKELY(false || (data[7].qvalue <= 96))) { + if (LIKELY(false || (data[7].qvalue <= 92))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += 92.56955997074317; + } else { + result[0] += -0.9922155287745735; + } + } else { + result[0] += -500.10035950605436; + } + } else { + if (LIKELY(false || (data[10].qvalue <= 82))) { + if (UNLIKELY(false || (data[3].qvalue <= 18))) { + result[0] += -349.0415762351967; + } else { + result[0] += 171.85769845927226; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 156))) { + result[0] += -451.9129687933409; + } else { + result[0] += -27.180822178728683; + } + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 24))) { + if (UNLIKELY(false || (data[5].qvalue <= 52))) { + if (LIKELY(false || (data[6].qvalue <= 170))) { + result[0] += 257.80955723579154; + } else { + result[0] += 14.067277977079232; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 78))) { + result[0] += -187.1084046068555; + } else { + result[0] += -16.47644404213514; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 58))) { + if (UNLIKELY(false || (data[1].qvalue <= 68))) { + result[0] += -459.1713788092935; + } else { + result[0] += -121.28232839389386; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 82))) { + result[0] += 45.911193575042404; + } else { + result[0] += -65.09204602729294; + } + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + if (LIKELY(false || (data[8].qvalue <= 148))) { + result[0] += -112.3564328986315; + } else { + result[0] += -581.5931838175455; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 104))) { + if (LIKELY(false || (data[8].qvalue <= 130))) { + if (LIKELY(false || (data[7].qvalue <= 140))) { + result[0] += 99.49014850118782; + } else { + result[0] += 283.8674441857837; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 166))) { + result[0] += -196.33975801441395; + } else { + result[0] += 5.911638064587168; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 128))) { + if (UNLIKELY(false || (data[1].qvalue <= 114))) { + result[0] += 220.39824119047162; + } else { + result[0] += 86.35899353404471; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 136))) { + result[0] += -135.11781706233776; + } else { + result[0] += 1.7254921740277542; + } + } + } + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 32))) { + result[0] += -729.2271141921474; + } else { + if (LIKELY(false || (data[1].qvalue <= 164))) { + if (UNLIKELY(false || (data[8].qvalue <= 6))) { + result[0] += 163.01861591388177; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 182))) { + result[0] += -90.15761066666771; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 184))) { + result[0] += 105.67463769590789; + } else { + result[0] += -40.65848461097406; + } + } + } + } else { + result[0] += -149.36907697589524; + } + } + } + if (LIKELY(false || (data[0].qvalue <= 436))) { + if (UNLIKELY(false || (data[9].qvalue <= 54))) { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (UNLIKELY(false || (data[10].qvalue <= 2))) { + result[0] += 73.61434867220245; + } else { + if (LIKELY(false || (data[4].qvalue <= 92))) { + result[0] += -219.52771218319805; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 182))) { + result[0] += -261.44554618186174; + } else { + result[0] += -1230.408535736753; + } + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 24))) { + if (LIKELY(false || (data[0].qvalue <= 386))) { + if (LIKELY(false || (data[2].qvalue <= 112))) { + result[0] += -5.198946846457009; + } else { + result[0] += 249.1763981732631; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 34))) { + result[0] += 641.3825622819111; + } else { + result[0] += 205.60287406527715; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 32))) { + if (UNLIKELY(false || (data[4].qvalue <= 126))) { + result[0] += -887.6161494419807; + } else { + result[0] += -171.10035394400404; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 408))) { + result[0] += -58.06284362537737; + } else { + result[0] += 25.218332263091998; + } + } + } + } + } else { + if (LIKELY(false || (data[8].qvalue <= 154))) { + if (LIKELY(false || (data[0].qvalue <= 346))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + if (LIKELY(false || (data[0].qvalue <= 232))) { + result[0] += 25.584773238822454; + } else { + result[0] += 219.8658888633042; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 20))) { + result[0] += -51.10525196126827; + } else { + result[0] += 0.46333939293857984; + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 72))) { + if (LIKELY(false || (data[8].qvalue <= 130))) { + result[0] += 164.15191019337982; + } else { + result[0] += -335.2814106754929; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 84))) { + result[0] += -89.39119339130295; + } else { + result[0] += 34.512913855389506; + } + } + } + } else { + result[0] += -108.08090718754343; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 158))) { + if (UNLIKELY(false || (data[8].qvalue <= 46))) { + if (LIKELY(false || (data[2].qvalue <= 118))) { + if (UNLIKELY(false || (data[7].qvalue <= 76))) { + result[0] += 131.26162098672742; + } else { + if (LIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -442.8722988609171; + } else { + result[0] += 73.24052956825756; + } + } + } else { + result[0] += 686.9491978638054; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 54))) { + result[0] += 687.8496107636925; + } else { + if (LIKELY(false || (data[0].qvalue <= 456))) { + if (UNLIKELY(false || (data[9].qvalue <= 12))) { + result[0] += -77.24307302441612; + } else { + result[0] += 147.57566065994; + } + } else { + if (LIKELY(false || (data[5].qvalue <= 110))) { + result[0] += 138.145849125284; + } else { + result[0] += 445.5193884395197; + } + } + } + } + } else { + result[0] += -13.479785177056158; + } + } + if (LIKELY(false || (data[0].qvalue <= 418))) { + if (LIKELY(false || (data[1].qvalue <= 124))) { + if (LIKELY(false || (data[4].qvalue <= 68))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (UNLIKELY(false || (data[9].qvalue <= 50))) { + result[0] += -577.1611446622182; + } else { + if (LIKELY(false || (data[10].qvalue <= 142))) { + result[0] += 3.8314250912323637; + } else { + result[0] += -329.28055580520333; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 26))) { + if (UNLIKELY(false || (data[0].qvalue <= 198))) { + result[0] += -194.40768818753736; + } else { + result[0] += -854.4809931534361; + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 70))) { + result[0] += -160.52174054303293; + } else { + result[0] += 2.741586163284305; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 372))) { + if (UNLIKELY(false || (data[1].qvalue <= 46))) { + if (UNLIKELY(false || (data[0].qvalue <= 168))) { + result[0] += 18.984540004822804; + } else { + result[0] += 327.11800611524313; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 20))) { + result[0] += -129.20406003130722; + } else { + result[0] += 17.92278831555766; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 150))) { + if (UNLIKELY(false || (data[2].qvalue <= 6))) { + result[0] += -221.08946446962568; + } else { + result[0] += 229.63359317462343; + } + } else { + result[0] += -223.7223770166941; + } + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 64))) { + if (LIKELY(false || (data[0].qvalue <= 394))) { + result[0] += 5.729375564939926; + } else { + if (LIKELY(false || (data[4].qvalue <= 136))) { + result[0] += 349.6954434120244; + } else { + result[0] += -447.2340327004826; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 346))) { + result[0] += -18.934394820420263; + } else { + if (LIKELY(false || (data[2].qvalue <= 164))) { + if (LIKELY(false || (data[1].qvalue <= 140))) { + result[0] += -512.2810219610269; + } else { + result[0] += -123.2775600600666; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 176))) { + result[0] += 335.8072972282146; + } else { + result[0] += -135.40134282126365; + } + } + } + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 10))) { + result[0] += 408.30240571312294; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 56))) { + result[0] += 203.85368488394235; + } else { + if (UNLIKELY(false || (data[7].qvalue <= 100))) { + if (LIKELY(false || (data[0].qvalue <= 448))) { + if (LIKELY(false || (data[10].qvalue <= 96))) { + result[0] += -457.3360616844447; + } else { + result[0] += 124.47860255821743; + } + } else { + if (LIKELY(false || (data[4].qvalue <= 108))) { + result[0] += 110.48892123347983; + } else { + result[0] += -391.09864189814425; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 114))) { + result[0] += 211.6228610309497; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 100))) { + result[0] += 56.71696470804632; + } else { + result[0] += -35.66565365297374; + } + } + } + } + } + } + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + if (LIKELY(false || (data[2].qvalue <= 52))) { + if (UNLIKELY(false || (data[2].qvalue <= 24))) { + result[0] += 147.91960717089773; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 43.27359384398152; + } else { + result[0] += 111.57729094083193; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 8))) { + result[0] += 113.34997104546899; + } else { + result[0] += 72.55342688666062; + } + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 128))) { + if (LIKELY(false || (data[1].qvalue <= 8))) { + if (LIKELY(false || (data[1].qvalue <= 6))) { + result[0] += 31.246573605653715; + } else { + result[0] += 46.606138483456206; + } + } else { + result[0] += 25.9373059644741; + } + } else { + result[0] += -31.626364376909578; + } + } + } else { + result[0] += 240.78696773473405; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 156))) { + if (UNLIKELY(false || (data[4].qvalue <= 6))) { + if (LIKELY(false || (data[4].qvalue <= 4))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (UNLIKELY(false || (data[9].qvalue <= 136))) { + result[0] += -48.976438574076354; + } else { + result[0] += 84.65299344607978; + } + } else { + result[0] += -130.88113764489268; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 72))) { + if (UNLIKELY(false || (data[1].qvalue <= 22))) { + result[0] += 338.1827440503927; + } else { + result[0] += 298.08608821916727; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 22))) { + result[0] += 204.95094375285473; + } else { + result[0] += 165.99111228283587; + } + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 10))) { + if (LIKELY(false || (data[10].qvalue <= 76))) { + if (LIKELY(false || (data[2].qvalue <= 92))) { + result[0] += -108.03954054155902; + } else { + result[0] += 14.418807341740987; + } + } else { + result[0] += -321.311952308832; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 146))) { + if (UNLIKELY(false || (data[1].qvalue <= 8))) { + result[0] += -76.25024484457143; + } else { + result[0] += 1.263274912881202; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 6))) { + result[0] += -66.04814325674907; + } else { + result[0] += 205.46869851639258; + } + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 36))) { + if (UNLIKELY(false || (data[1].qvalue <= 14))) { + result[0] += -104.7552531611455; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 18))) { + if (LIKELY(false || (data[1].qvalue <= 16))) { + result[0] += -179.2056109143998; + } else { + result[0] += -196.72295793090106; + } + } else { + result[0] += -228.7848599462564; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -62.70206554713903; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 22))) { + result[0] += -109.57286387525; + } else { + result[0] += -86.90439645392793; + } + } + } + } + } + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + if (LIKELY(false || (data[2].qvalue <= 52))) { + if (UNLIKELY(false || (data[2].qvalue <= 24))) { + result[0] += 133.14242400479026; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 38.96136485201495; + } else { + result[0] += 100.46528939864675; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 8))) { + result[0] += 102.02497851147024; + } else { + result[0] += 65.31936047920384; + } + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 128))) { + if (LIKELY(false || (data[1].qvalue <= 8))) { + if (LIKELY(false || (data[1].qvalue <= 6))) { + result[0] += 28.128267190495155; + } else { + result[0] += 42.006052734820884; + } + } else { + result[0] += 23.349456995785104; + } + } else { + result[0] += -28.466887237596946; + } + } + } else { + result[0] += 216.73249472971176; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 154))) { + if (LIKELY(false || (data[9].qvalue <= 148))) { + if (UNLIKELY(false || (data[10].qvalue <= 2))) { + if (LIKELY(false || (data[2].qvalue <= 156))) { + if (LIKELY(false || (data[9].qvalue <= 114))) { + result[0] += 101.975488812464; + } else { + result[0] += -25.287212545092885; + } + } else { + result[0] += -99.57496398377107; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 30))) { + if (UNLIKELY(false || (data[4].qvalue <= 16))) { + result[0] += 58.55564779565954; + } else { + result[0] += -70.77906117415272; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 18))) { + result[0] += 56.64272064066464; + } else { + result[0] += -1.102421664746258; + } + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 204))) { + if (UNLIKELY(false || (data[4].qvalue <= 4))) { + if (UNLIKELY(false || (data[1].qvalue <= 12))) { + result[0] += 76.95158297864288; + } else { + result[0] += 30.779853089335912; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 12))) { + result[0] += 184.47632299122395; + } else { + result[0] += 240.0561058810638; + } + } + } else { + result[0] += -59.44992973601068; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 20))) { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + if (UNLIKELY(false || (data[2].qvalue <= 36))) { + result[0] += 90.32707990001026; + } else { + result[0] += -56.438123774542795; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 92))) { + if (LIKELY(false || (data[2].qvalue <= 88))) { + result[0] += -97.11633504657749; + } else { + result[0] += -327.052109099788; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 24))) { + result[0] += -88.46877715199902; + } else { + result[0] += 36.38893057075731; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 14))) { + result[0] += 164.4902900819997; + } else { + result[0] += 201.51887436344938; + } + } + } + } + if (LIKELY(false || (data[7].qvalue <= 190))) { + if (LIKELY(false || (data[3].qvalue <= 102))) { + if (LIKELY(false || (data[6].qvalue <= 72))) { + if (LIKELY(false || (data[8].qvalue <= 112))) { + if (LIKELY(false || (data[8].qvalue <= 100))) { + if (LIKELY(false || (data[10].qvalue <= 124))) { + result[0] += 2.677490165277401; + } else { + result[0] += -230.8237998800555; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 106))) { + result[0] += 25.99249929961273; + } else { + result[0] += 143.56253715641475; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 158))) { + if (LIKELY(false || (data[4].qvalue <= 32))) { + result[0] += -56.96864498437293; + } else { + result[0] += -361.08262388656516; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 130))) { + result[0] += -1.2571125993192631; + } else { + result[0] += 139.96081546293442; + } + } + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 24))) { + if (LIKELY(false || (data[4].qvalue <= 126))) { + if (LIKELY(false || (data[8].qvalue <= 68))) { + result[0] += 157.68101550278936; + } else { + result[0] += -24.160628623142866; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 100))) { + result[0] += -87.49349951075376; + } else { + result[0] += -651.2774869908153; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 104))) { + if (UNLIKELY(false || (data[8].qvalue <= 82))) { + result[0] += -72.49802639529806; + } else { + result[0] += -213.66048931924266; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 54))) { + result[0] += 78.72485385773093; + } else { + result[0] += -55.249394110207554; + } + } + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 134))) { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + if (LIKELY(false || (data[8].qvalue <= 148))) { + result[0] += -94.97855070131733; + } else { + result[0] += -527.4611841577268; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 32))) { + if (LIKELY(false || (data[9].qvalue <= 28))) { + result[0] += 0.9950631307094829; + } else { + result[0] += -188.09164012193523; + } + } else { + if (LIKELY(false || (data[8].qvalue <= 140))) { + result[0] += 64.8246177611995; + } else { + result[0] += -21.226393353713657; + } + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 140))) { + if (UNLIKELY(false || (data[10].qvalue <= 138))) { + result[0] += -69.54835276237708; + } else { + if (LIKELY(false || (data[1].qvalue <= 158))) { + result[0] += -236.0745244061219; + } else { + result[0] += -12.63559195083826; + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 52))) { + result[0] += -236.792523303151; + } else { + if (LIKELY(false || (data[10].qvalue <= 144))) { + result[0] += 52.206924149774636; + } else { + result[0] += -39.51021768049611; + } + } + } + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 140))) { + if (LIKELY(false || (data[2].qvalue <= 226))) { + result[0] += -52.42843817107941; + } else { + result[0] += -306.65785355623103; + } + } else { + result[0] += -385.0662454001109; + } + } + if (LIKELY(false || (data[0].qvalue <= 398))) { + if (LIKELY(false || (data[1].qvalue <= 110))) { + if (LIKELY(false || (data[4].qvalue <= 70))) { + if (LIKELY(false || (data[1].qvalue <= 86))) { + if (UNLIKELY(false || (data[5].qvalue <= 42))) { + if (LIKELY(false || (data[5].qvalue <= 40))) { + result[0] += -11.28284578878582; + } else { + result[0] += -558.1753204017739; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 26))) { + result[0] += 133.21891876863342; + } else { + result[0] += 2.7832862656512685; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 26))) { + if (LIKELY(false || (data[0].qvalue <= 246))) { + result[0] += -324.1019695078817; + } else { + result[0] += -1005.9270602557989; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 88))) { + result[0] += -134.86731075700266; + } else { + result[0] += 10.66748293923189; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 336))) { + if (LIKELY(false || (data[6].qvalue <= 98))) { + result[0] += 2.8200100312590166; + } else { + result[0] += 101.03470891273177; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[6].qvalue <= 82))) { + result[0] += 102.04557161945678; + } else { + result[0] += -1199.8811078319284; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 178))) { + result[0] += 209.67226520445524; + } else { + result[0] += -245.61747308533063; + } + } + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 60))) { + result[0] += 42.785881512673626; + } else { + if (LIKELY(false || (data[6].qvalue <= 154))) { + if (LIKELY(false || (data[0].qvalue <= 270))) { + result[0] += -45.95588289959001; + } else { + if (UNLIKELY(false || (data[6].qvalue <= 90))) { + result[0] += -6.815612347789953; + } else { + result[0] += -266.0110871919803; + } + } + } else { + result[0] += 39.02030164716074; + } + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 8))) { + result[0] += 324.9267206663035; + } else { + if (LIKELY(false || (data[2].qvalue <= 214))) { + if (UNLIKELY(false || (data[1].qvalue <= 16))) { + result[0] += -112.18559323452085; + } else { + if (LIKELY(false || (data[1].qvalue <= 132))) { + if (UNLIKELY(false || (data[2].qvalue <= 80))) { + result[0] += -56.2055045870944; + } else { + result[0] += 116.07989618400472; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 118))) { + result[0] += 141.36324262903074; + } else { + result[0] += -22.395839800711574; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 458))) { + if (UNLIKELY(false || (data[10].qvalue <= 118))) { + result[0] += 100.73381070844493; + } else { + if (LIKELY(false || (data[6].qvalue <= 174))) { + result[0] += -365.7173433437338; + } else { + result[0] += 21.744193287907777; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 162))) { + result[0] += 280.70190659426964; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 466))) { + result[0] += -456.7905672944539; + } else { + result[0] += 86.74089207808544; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 438))) { + if (LIKELY(false || (data[6].qvalue <= 146))) { + if (LIKELY(false || (data[7].qvalue <= 198))) { + if (LIKELY(false || (data[0].qvalue <= 388))) { + if (LIKELY(false || (data[1].qvalue <= 126))) { + if (LIKELY(false || (data[4].qvalue <= 102))) { + result[0] += -3.0799897635647393; + } else { + result[0] += 83.18986261549072; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 36))) { + result[0] += 225.15234009730352; + } else { + result[0] += -85.63258649231685; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 82))) { + if (LIKELY(false || (data[3].qvalue <= 80))) { + result[0] += 35.515553203945544; + } else { + result[0] += -529.1932694931496; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 124))) { + result[0] += 183.78815671074506; + } else { + result[0] += 39.81733903217535; + } + } + } + } else { + result[0] += -186.6083734780767; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 50))) { + if (UNLIKELY(false || (data[0].qvalue <= 272))) { + result[0] += 16.882292766847286; + } else { + result[0] += -340.80931681790474; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 154))) { + if (LIKELY(false || (data[3].qvalue <= 170))) { + if (LIKELY(false || (data[10].qvalue <= 102))) { + result[0] += -8.494744851894465; + } else { + result[0] += -138.4317062673716; + } + } else { + result[0] += -365.5767086328917; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 172))) { + if (UNLIKELY(false || (data[0].qvalue <= 304))) { + result[0] += -4.6291599966349635; + } else { + result[0] += 233.27933991789052; + } + } else { + result[0] += -47.89610265065409; + } + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 182))) { + if (LIKELY(false || (data[10].qvalue <= 148))) { + if (UNLIKELY(false || (data[8].qvalue <= 46))) { + if (UNLIKELY(false || (data[7].qvalue <= 56))) { + result[0] += 408.84774771757384; + } else { + if (LIKELY(false || (data[4].qvalue <= 136))) { + result[0] += -81.22211370546637; + } else { + result[0] += 149.79791446348088; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 60))) { + result[0] += 507.31584249730014; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 108))) { + result[0] += 3.6960386503909706; + } else { + result[0] += 132.51686944699037; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + if (LIKELY(false || (data[7].qvalue <= 192))) { + if (UNLIKELY(false || (data[0].qvalue <= 460))) { + result[0] += -289.98766205200593; + } else { + result[0] += -37.620573467202114; + } + } else { + result[0] += -1064.0260380735235; + } + } else { + result[0] += 301.66872530386996; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + if (LIKELY(false || (data[1].qvalue <= 160))) { + if (LIKELY(false || (data[3].qvalue <= 178))) { + result[0] += -66.47823654447284; + } else { + result[0] += -479.9069173542369; + } + } else { + result[0] += -776.3112829887955; + } + } else { + result[0] += 122.73633086275382; + } + } + } + if (LIKELY(false || (data[0].qvalue <= 458))) { + if (LIKELY(false || (data[6].qvalue <= 168))) { + if (LIKELY(false || (data[6].qvalue <= 166))) { + if (LIKELY(false || (data[6].qvalue <= 164))) { + if (LIKELY(false || (data[0].qvalue <= 424))) { + if (LIKELY(false || (data[6].qvalue <= 144))) { + result[0] += -1.16853189584804; + } else { + result[0] += -76.18237147015647; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += -565.1080728858356; + } else { + result[0] += 55.856898322571; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 428))) { + result[0] += -22.36169324873774; + } else { + result[0] += -588.8674501865916; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 444))) { + result[0] += 45.149287996873234; + } else { + result[0] += 472.9437979483076; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 110))) { + if (UNLIKELY(false || (data[0].qvalue <= 426))) { + result[0] += -14.394012939959836; + } else { + result[0] += -486.3581999710685; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 58))) { + result[0] += -223.70340084114315; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 88))) { + if (LIKELY(false || (data[0].qvalue <= 400))) { + result[0] += -62.55465904994526; + } else { + result[0] += 711.6569177288845; + } + } else { + result[0] += -22.12709356395005; + } + } + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 176))) { + if (UNLIKELY(false || (data[6].qvalue <= 136))) { + if (LIKELY(false || (data[4].qvalue <= 108))) { + if (UNLIKELY(false || (data[9].qvalue <= 36))) { + result[0] += 353.08425684031425; + } else { + if (UNLIKELY(false || (data[8].qvalue <= 70))) { + result[0] += -488.1757777871809; + } else { + result[0] += 63.64288315962378; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[1].qvalue <= 164))) { + result[0] += -1340.3345402421837; + } else { + result[0] += 611.7688119589316; + } + } else { + result[0] += -5.555477568010339; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 124))) { + if (LIKELY(false || (data[2].qvalue <= 216))) { + if (UNLIKELY(false || (data[10].qvalue <= 48))) { + result[0] += 101.80136762073124; + } else { + result[0] += 394.0454932921462; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + result[0] += -54.51295272481636; + } else { + result[0] += 369.30662351586926; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 464))) { + if (LIKELY(false || (data[2].qvalue <= 168))) { + result[0] += -280.5682849399643; + } else { + result[0] += 363.49964354819656; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 94))) { + result[0] += 287.00161641580763; + } else { + result[0] += -27.192437609622456; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (UNLIKELY(false || (data[4].qvalue <= 102))) { + result[0] += 4.886466438190986; + } else { + result[0] += -273.6325616457949; + } + } else { + result[0] += 83.09343404390128; + } + } + } + if (LIKELY(false || (data[0].qvalue <= 466))) { + if (LIKELY(false || (data[6].qvalue <= 168))) { + if (LIKELY(false || (data[0].qvalue <= 426))) { + if (UNLIKELY(false || (data[9].qvalue <= 42))) { + if (LIKELY(false || (data[9].qvalue <= 28))) { + if (LIKELY(false || (data[9].qvalue <= 26))) { + result[0] += -12.741637583507675; + } else { + result[0] += 237.9588004111412; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 22))) { + result[0] += -668.1312059622767; + } else { + result[0] += -74.96557902347062; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 96))) { + if (LIKELY(false || (data[1].qvalue <= 92))) { + result[0] += 1.2337343498563946; + } else { + result[0] += -70.38800250372651; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 98))) { + result[0] += 57.91224145436725; + } else { + result[0] += -291.61695102438495; + } + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 46))) { + if (LIKELY(false || (data[3].qvalue <= 138))) { + if (LIKELY(false || (data[2].qvalue <= 78))) { + result[0] += -16.243170204026153; + } else { + result[0] += 316.91551138966975; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 122))) { + result[0] += -653.2681828545267; + } else { + result[0] += -140.38689952692195; + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 116))) { + if (UNLIKELY(false || (data[9].qvalue <= 76))) { + result[0] += 227.09410027903155; + } else { + result[0] += 41.580049580383246; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 112))) { + result[0] += 421.02462529251864; + } else { + result[0] += -29.85412788966988; + } + } + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 84))) { + if (LIKELY(false || (data[0].qvalue <= 462))) { + if (UNLIKELY(false || (data[0].qvalue <= 430))) { + result[0] += -40.88170364268475; + } else { + if (LIKELY(false || (data[0].qvalue <= 458))) { + result[0] += -438.26722679955304; + } else { + result[0] += -153.8759027518579; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 170))) { + result[0] += 596.3699073028564; + } else { + result[0] += -65.1138629905028; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 170))) { + if (LIKELY(false || (data[0].qvalue <= 450))) { + if (UNLIKELY(false || (data[8].qvalue <= 96))) { + result[0] += -174.18902213573074; + } else { + result[0] += 92.92329912293484; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 140))) { + result[0] += 1029.8844129302536; + } else { + result[0] += 173.42569967619715; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 398))) { + result[0] += 117.61714884981923; + } else { + result[0] += -165.11066227116845; + } + } + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 132))) { + if (UNLIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[8].qvalue <= 152))) { + result[0] += 275.79848862506384; + } else { + result[0] += -208.23065829261407; + } + } else { + result[0] += 44.68615918739647; + } + } else { + result[0] += -104.44556106745557; + } + } + if (LIKELY(false || (data[0].qvalue <= 452))) { + if (LIKELY(false || (data[7].qvalue <= 178))) { + if (LIKELY(false || (data[2].qvalue <= 212))) { + if (LIKELY(false || (data[7].qvalue <= 166))) { + if (LIKELY(false || (data[6].qvalue <= 168))) { + if (LIKELY(false || (data[6].qvalue <= 166))) { + result[0] += 0.17111650034346793; + } else { + result[0] += 170.19199658576656; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 114))) { + result[0] += -7.762253214369399; + } else { + result[0] += -229.65839448385577; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 154))) { + if (UNLIKELY(false || (data[7].qvalue <= 168))) { + result[0] += -1527.4651782070064; + } else { + result[0] += -32.341146195408825; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 400))) { + result[0] += 53.78926807880971; + } else { + result[0] += 365.35095312342645; + } + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 40))) { + result[0] += -195.8247595287494; + } else { + if (LIKELY(false || (data[0].qvalue <= 446))) { + result[0] += -6.730419002246698; + } else { + result[0] += -485.7584640261381; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 68))) { + result[0] += -302.41592403705386; + } else { + result[0] += -41.37635294838609; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 146))) { + if (UNLIKELY(false || (data[8].qvalue <= 68))) { + if (LIKELY(false || (data[9].qvalue <= 52))) { + if (LIKELY(false || (data[4].qvalue <= 108))) { + if (LIKELY(false || (data[0].qvalue <= 462))) { + result[0] += 105.6696751827489; + } else { + result[0] += 422.3152713156167; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 154))) { + result[0] += -338.9649649852936; + } else { + result[0] += 94.81444748000506; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 472))) { + if (UNLIKELY(false || (data[2].qvalue <= 32))) { + result[0] += 307.59982556152346; + } else { + result[0] += -2059.814399773849; + } + } else { + result[0] += -114.64047998985878; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 120))) { + if (LIKELY(false || (data[0].qvalue <= 468))) { + if (UNLIKELY(false || (data[6].qvalue <= 56))) { + result[0] += -105.53208465517486; + } else { + result[0] += 242.79092160164484; + } + } else { + result[0] += 794.8344802547236; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 462))) { + result[0] += 211.67574313790456; + } else { + result[0] += -1052.0226856718868; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 470))) { + if (LIKELY(false || (data[2].qvalue <= 220))) { + if (LIKELY(false || (data[5].qvalue <= 122))) { + if (UNLIKELY(false || (data[8].qvalue <= 14))) { + result[0] += 607.7257898100032; + } else { + result[0] += 31.625663758552975; + } + } else { + result[0] += -165.26478050781773; + } + } else { + result[0] += -212.34245839321358; + } + } else { + if (LIKELY(false || (data[7].qvalue <= 188))) { + result[0] += 195.00720541235728; + } else { + result[0] += -16.708118864398536; + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 442))) { + if (LIKELY(false || (data[1].qvalue <= 148))) { + if (LIKELY(false || (data[8].qvalue <= 154))) { + if (UNLIKELY(false || (data[0].qvalue <= 54))) { + result[0] += -25.89138246104899; + } else { + if (LIKELY(false || (data[5].qvalue <= 84))) { + if (LIKELY(false || (data[8].qvalue <= 116))) { + result[0] += 6.395755334851975; + } else { + result[0] += -41.8874025902268; + } + } else { + if (LIKELY(false || (data[10].qvalue <= 136))) { + result[0] += 43.17739670120483; + } else { + result[0] += -113.04299411317503; + } + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 342))) { + result[0] += -9.406748281219956; + } else { + result[0] += -151.8391684094208; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 102))) { + if (LIKELY(false || (data[0].qvalue <= 308))) { + result[0] += 68.15272951691435; + } else { + result[0] += 653.7243028200384; + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 118))) { + if (UNLIKELY(false || (data[0].qvalue <= 360))) { + result[0] += 4.132956299238849; + } else { + if (LIKELY(false || (data[2].qvalue <= 140))) { + result[0] += -391.93226074781654; + } else { + result[0] += -64.14020916590836; + } + } + } else { + result[0] += -42.79033951970415; + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 140))) { + if (UNLIKELY(false || (data[3].qvalue <= 16))) { + result[0] += 673.2372143809691; + } else { + if (LIKELY(false || (data[8].qvalue <= 72))) { + if (UNLIKELY(false || (data[0].qvalue <= 456))) { + if (LIKELY(false || (data[2].qvalue <= 68))) { + result[0] += 72.78571585943284; + } else { + result[0] += -372.52419624016306; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 84))) { + result[0] += -470.8106385006224; + } else { + result[0] += 129.07388124253447; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 448))) { + if (UNLIKELY(false || (data[3].qvalue <= 106))) { + result[0] += -167.83421517433203; + } else { + result[0] += 120.89570583762936; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 144))) { + result[0] += 166.50278950598; + } else { + result[0] += 479.51545503859353; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + if (LIKELY(false || (data[2].qvalue <= 216))) { + if (LIKELY(false || (data[8].qvalue <= 102))) { + if (LIKELY(false || (data[4].qvalue <= 136))) { + result[0] += -85.88265694904688; + } else { + result[0] += 158.08464637061354; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 160))) { + result[0] += 566.823827422359; + } else { + result[0] += 36.90095759323719; + } + } + } else { + result[0] += -221.08459607201044; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 162))) { + result[0] += 279.3051543277019; + } else { + if (UNLIKELY(false || (data[4].qvalue <= 88))) { + result[0] += 225.3999401051908; + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + result[0] += -146.03870534454174; + } else { + result[0] += 135.266458595812; + } + } + } + } + } + } + if (UNLIKELY(false || (data[7].qvalue <= 10))) { + if (LIKELY(false || (data[3].qvalue <= 54))) { + if (LIKELY(false || (data[8].qvalue <= 56))) { + if (LIKELY(false || (data[5].qvalue <= 30))) { + if (UNLIKELY(false || (data[4].qvalue <= 2))) { + result[0] += 95.33481345166494; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 36))) { + result[0] += -51.475241127929905; + } else { + result[0] += 45.54996053721217; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 18))) { + result[0] += 193.47951816282492; + } else { + result[0] += 41.14647330634432; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 8))) { + result[0] += -256.5011877316934; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 44))) { + result[0] += -78.031723920287; + } else { + if (UNLIKELY(false || (data[10].qvalue <= 58))) { + result[0] += -70.18547029244044; + } else { + result[0] += 58.299178728803355; + } + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 56))) { + result[0] += 377.13982577866017; + } else { + result[0] += 125.3708679348752; + } + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 22))) { + if (LIKELY(false || (data[6].qvalue <= 20))) { + if (LIKELY(false || (data[3].qvalue <= 48))) { + if (LIKELY(false || (data[5].qvalue <= 38))) { + if (LIKELY(false || (data[2].qvalue <= 122))) { + result[0] += -27.262907617303707; + } else { + result[0] += -296.1225155241601; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 94))) { + result[0] += -744.6855172554128; + } else { + result[0] += -295.5306383117119; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 74))) { + result[0] += 131.65876610363532; + } else { + result[0] += 275.9221477857152; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 54))) { + result[0] += -266.18327484029714; + } else { + result[0] += -500.5391151498484; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 14))) { + if (LIKELY(false || (data[4].qvalue <= 34))) { + if (LIKELY(false || (data[8].qvalue <= 86))) { + result[0] += -10.58236111787111; + } else { + if (UNLIKELY(false || (data[5].qvalue <= 8))) { + result[0] += -39.932805444944165; + } else { + result[0] += 159.2150451080038; + } + } + } else { + result[0] += 204.44418412478637; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 30))) { + if (LIKELY(false || (data[8].qvalue <= 12))) { + if (UNLIKELY(false || (data[1].qvalue <= 62))) { + result[0] += 125.99859295088723; + } else { + result[0] += -10.15551076665495; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 20))) { + result[0] += -190.22917265383091; + } else { + result[0] += -33.04391029665733; + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 98))) { + if (UNLIKELY(false || (data[7].qvalue <= 32))) { + result[0] += 63.784678651859224; + } else { + result[0] += 4.687841489245975; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 70))) { + result[0] += -193.67925649583466; + } else { + result[0] += -11.97318656822869; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 410))) { + if (UNLIKELY(false || (data[9].qvalue <= 54))) { + if (UNLIKELY(false || (data[4].qvalue <= 74))) { + if (LIKELY(false || (data[0].qvalue <= 298))) { + result[0] += -37.83989902430271; + } else { + result[0] += -246.41304263463073; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 196))) { + result[0] += -24.698954353394658; + } else { + result[0] += 269.8421976574775; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 82))) { + if (LIKELY(false || (data[4].qvalue <= 128))) { + result[0] += 121.42816960444512; + } else { + result[0] += -204.12026096629276; + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 86))) { + result[0] += -150.30284718707338; + } else { + result[0] += -9.526517783514938; + } + } + } + } + } else { + result[0] += 0.6374046148668437; + } + } else { + if (UNLIKELY(false || (data[9].qvalue <= 10))) { + if (LIKELY(false || (data[0].qvalue <= 456))) { + if (UNLIKELY(false || (data[3].qvalue <= 160))) { + if (UNLIKELY(false || (data[2].qvalue <= 56))) { + result[0] += -844.9505630005908; + } else { + result[0] += -266.4489226017028; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 448))) { + result[0] += -59.61765022395716; + } else { + if (UNLIKELY(false || (data[9].qvalue <= 0))) { + result[0] += -422.1597338477869; + } else { + result[0] += 307.5185329018873; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 84))) { + result[0] += 259.874337152467; + } else { + result[0] += -0.14677676347402133; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 76))) { + if (LIKELY(false || (data[2].qvalue <= 60))) { + if (UNLIKELY(false || (data[4].qvalue <= 82))) { + if (LIKELY(false || (data[6].qvalue <= 66))) { + result[0] += 6.901818537027612; + } else { + result[0] += 346.73948874314306; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 0))) { + result[0] += 767.3329148187853; + } else { + result[0] += -55.71428668797978; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 462))) { + if (UNLIKELY(false || (data[8].qvalue <= 62))) { + result[0] += -1186.4827177702393; + } else { + result[0] += -248.10230638115846; + } + } else { + result[0] += 317.2999956644618; + } + } + } else { + if (LIKELY(false || (data[8].qvalue <= 128))) { + if (UNLIKELY(false || (data[10].qvalue <= 46))) { + if (UNLIKELY(false || (data[8].qvalue <= 6))) { + result[0] += 424.73738367278276; + } else { + result[0] += -69.29711136275134; + } + } else { + if (LIKELY(false || (data[9].qvalue <= 38))) { + result[0] += 93.41031827005968; + } else { + result[0] += 256.43713558484785; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 86))) { + if (LIKELY(false || (data[0].qvalue <= 456))) { + result[0] += -194.80499023272242; + } else { + result[0] += 84.16720477646794; + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 130))) { + result[0] += 1117.9205291606104; + } else { + result[0] += 32.52858858984742; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 442))) { + if (LIKELY(false || (data[6].qvalue <= 146))) { + if (LIKELY(false || (data[0].qvalue <= 360))) { + if (UNLIKELY(false || (data[4].qvalue <= 38))) { + if (UNLIKELY(false || (data[6].qvalue <= 24))) { + if (UNLIKELY(false || (data[9].qvalue <= 94))) { + result[0] += -509.2763123794946; + } else { + result[0] += -10.28309254566162; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 46))) { + result[0] += 65.60802974294101; + } else { + result[0] += -11.172650598142237; + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 40))) { + if (UNLIKELY(false || (data[0].qvalue <= 170))) { + result[0] += -379.1246497217576; + } else { + result[0] += -880.7154812437998; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 38))) { + result[0] += 33.59073760216112; + } else { + result[0] += -19.2301464816471; + } + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 198))) { + if (LIKELY(false || (data[2].qvalue <= 186))) { + if (LIKELY(false || (data[3].qvalue <= 144))) { + result[0] += 33.69989668265042; + } else { + result[0] += 208.11777026466976; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 126))) { + result[0] += -4.6241213678677076; + } else { + result[0] += -258.29323587381907; + } + } + } else { + result[0] += -240.03769405637382; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 94))) { + if (UNLIKELY(false || (data[4].qvalue <= 20))) { + if (LIKELY(false || (data[0].qvalue <= 400))) { + result[0] += -1.3019828319392528; + } else { + result[0] += 595.7005340010636; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 258))) { + result[0] += -9.190887637624767; + } else { + result[0] += -285.64672594943085; + } + } + } else { + if (LIKELY(false || (data[7].qvalue <= 154))) { + if (UNLIKELY(false || (data[0].qvalue <= 344))) { + result[0] += 40.545254733724846; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 416))) { + result[0] += -233.93172162375063; + } else { + result[0] += -22.286785557997515; + } + } + } else { + result[0] += 70.10413972244889; + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 140))) { + if (LIKELY(false || (data[4].qvalue <= 114))) { + if (LIKELY(false || (data[4].qvalue <= 112))) { + if (UNLIKELY(false || (data[3].qvalue <= 16))) { + result[0] += 634.2191665019159; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 78))) { + result[0] += 6.665393953447264; + } else { + result[0] += 149.39801863389712; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 154))) { + result[0] += 1226.4539406622023; + } else { + result[0] += 294.9277796445255; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 158))) { + result[0] += 235.1520118587282; + } else { + result[0] += -166.61985769647302; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + if (LIKELY(false || (data[2].qvalue <= 216))) { + result[0] += 5.169623713259927; + } else { + result[0] += -199.41738547469845; + } + } else { + result[0] += 71.9476904143754; + } + } + } + if (UNLIKELY(false || (data[0].qvalue <= 48))) { + if (UNLIKELY(false || (data[0].qvalue <= 0))) { + result[0] += -78.00407393572347; + } else { + if (LIKELY(false || (data[2].qvalue <= 202))) { + if (LIKELY(false || (data[3].qvalue <= 104))) { + if (LIKELY(false || (data[4].qvalue <= 90))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += -70.6888999910638; + } else { + result[0] += -10.627670242516414; + } + } else { + if (LIKELY(false || (data[10].qvalue <= 66))) { + result[0] += -107.69047239009149; + } else { + result[0] += -13.414907874287316; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 138))) { + if (UNLIKELY(false || (data[10].qvalue <= 12))) { + result[0] += 69.98431682634853; + } else { + result[0] += -59.43734140381587; + } + } else { + result[0] += 78.30655993653257; + } + } + } else { + result[0] += 82.01495795861331; + } + } + } else { + if (UNLIKELY(false || (data[4].qvalue <= 0))) { + if (LIKELY(false || (data[2].qvalue <= 128))) { + if (LIKELY(false || (data[0].qvalue <= 226))) { + if (LIKELY(false || (data[2].qvalue <= 58))) { + if (UNLIKELY(false || (data[2].qvalue <= 24))) { + result[0] += 150.52913267303913; + } else { + result[0] += 6.758849638068968; + } + } else { + result[0] += 219.10434569977826; + } + } else { + result[0] += 274.1316917335302; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 332))) { + result[0] += -84.8367008241603; + } else { + result[0] += 317.87177416169243; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 14))) { + if (LIKELY(false || (data[0].qvalue <= 406))) { + if (LIKELY(false || (data[4].qvalue <= 28))) { + if (UNLIKELY(false || (data[9].qvalue <= 108))) { + result[0] += 152.21239710120096; + } else { + result[0] += -49.218505477191925; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 30))) { + result[0] += -369.09166571523195; + } else { + result[0] += -59.62264580017301; + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 122))) { + if (UNLIKELY(false || (data[2].qvalue <= 72))) { + result[0] += 225.48342290509692; + } else { + result[0] += 454.8914585206501; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 420))) { + result[0] += -316.42278833229403; + } else { + result[0] += 89.88278332332641; + } + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 22))) { + if (LIKELY(false || (data[3].qvalue <= 18))) { + if (LIKELY(false || (data[4].qvalue <= 26))) { + result[0] += 83.70108736546864; + } else { + result[0] += -47.03238163468578; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 172))) { + result[0] += 64.00534235112683; + } else { + result[0] += 241.06317309770884; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 28))) { + if (UNLIKELY(false || (data[9].qvalue <= 108))) { + result[0] += 41.85255742051009; + } else { + result[0] += -122.59387579963109; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 12))) { + result[0] += 72.23509636314206; + } else { + result[0] += 2.3792085384878088; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 460))) { + if (LIKELY(false || (data[6].qvalue <= 168))) { + if (LIKELY(false || (data[0].qvalue <= 432))) { + if (LIKELY(false || (data[1].qvalue <= 124))) { + if (LIKELY(false || (data[5].qvalue <= 62))) { + if (LIKELY(false || (data[8].qvalue <= 110))) { + result[0] += -1.818322290568549; + } else { + result[0] += -86.51820960810343; + } + } else { + if (UNLIKELY(false || (data[6].qvalue <= 48))) { + result[0] += 122.64593189161003; + } else { + result[0] += 8.90299476140962; + } + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 64))) { + if (LIKELY(false || (data[8].qvalue <= 64))) { + result[0] += 27.811889151480592; + } else { + result[0] += 564.1322969672179; + } + } else { + if (UNLIKELY(false || (data[10].qvalue <= 66))) { + result[0] += -961.542648801726; + } else { + result[0] += -67.5677271302586; + } + } + } + } else { + if (LIKELY(false || (data[10].qvalue <= 148))) { + if (UNLIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -271.4318221979432; + } else { + if (UNLIKELY(false || (data[5].qvalue <= 16))) { + result[0] += 688.4114417873475; + } else { + result[0] += 58.42035899984279; + } + } + } else { + result[0] += -307.4296972195184; + } + } + } else { + if (UNLIKELY(false || (data[8].qvalue <= 78))) { + if (UNLIKELY(false || (data[0].qvalue <= 434))) { + result[0] += -47.84229037947061; + } else { + result[0] += -341.83457990151675; + } + } else { + result[0] += -24.69473744066359; + } + } + } else { + if (LIKELY(false || (data[6].qvalue <= 176))) { + if (UNLIKELY(false || (data[6].qvalue <= 156))) { + if (LIKELY(false || (data[4].qvalue <= 108))) { + if (LIKELY(false || (data[1].qvalue <= 90))) { + if (LIKELY(false || (data[0].qvalue <= 468))) { + result[0] += -237.2028222385908; + } else { + result[0] += 235.23343926289132; + } + } else { + result[0] += 275.68252452796696; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 470))) { + if (UNLIKELY(false || (data[9].qvalue <= 6))) { + result[0] += 276.655894681045; + } else { + result[0] += -1138.9810647848005; + } + } else { + result[0] += -24.815816631740525; + } + } + } else { + if (LIKELY(false || (data[4].qvalue <= 124))) { + if (UNLIKELY(false || (data[8].qvalue <= 94))) { + result[0] += 468.636846801675; + } else { + result[0] += 173.66365185212408; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 464))) { + result[0] += -156.75158265385107; + } else { + if (UNLIKELY(false || (data[5].qvalue <= 72))) { + result[0] += 418.66415661473707; + } else { + result[0] += 16.60197418193807; + } + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 472))) { + if (UNLIKELY(false || (data[4].qvalue <= 102))) { + if (LIKELY(false || (data[3].qvalue <= 178))) { + result[0] += 112.29216412676556; + } else { + result[0] += -589.6749782826543; + } + } else { + if (LIKELY(false || (data[6].qvalue <= 186))) { + result[0] += -126.14743791646418; + } else { + result[0] += -697.0503549298137; + } + } + } else { + result[0] += 130.01903028897132; + } + } + } + if (LIKELY(false || (data[0].qvalue <= 250))) { + if (LIKELY(false || (data[2].qvalue <= 96))) { + if (LIKELY(false || (data[10].qvalue <= 110))) { + result[0] += -15.209203725866741; + } else { + result[0] += -151.01702601859964; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 116))) { + result[0] += 33.169088504059346; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 136))) { + if (LIKELY(false || (data[2].qvalue <= 134))) { + if (LIKELY(false || (data[9].qvalue <= 132))) { + result[0] += -0.5460734226255192; + } else { + result[0] += -166.18365097405575; + } + } else { + result[0] += -630.4456930959705; + } + } else { + result[0] += 9.610897218995794; + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 12))) { + if (UNLIKELY(false || (data[5].qvalue <= 4))) { + if (UNLIKELY(false || (data[7].qvalue <= 4))) { + result[0] += 292.5131264640451; + } else { + if (LIKELY(false || (data[0].qvalue <= 346))) { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += -90.96452643248735; + } else { + result[0] += -554.2646443755457; + } + } else { + result[0] += 78.62976969599482; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 316))) { + if (LIKELY(false || (data[3].qvalue <= 54))) { + if (UNLIKELY(false || (data[9].qvalue <= 102))) { + result[0] += 178.17593860823325; + } else { + result[0] += 25.683590998151445; + } + } else { + result[0] += 478.77786108066925; + } + } else { + result[0] += 181.52015147560186; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 122))) { + if (UNLIKELY(false || (data[6].qvalue <= 54))) { + if (UNLIKELY(false || (data[3].qvalue <= 10))) { + if (LIKELY(false || (data[4].qvalue <= 46))) { + result[0] += 392.13693925483733; + } else { + result[0] += 93.50921667351855; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 26))) { + result[0] += -171.2455308856315; + } else { + result[0] += 65.00191321740634; + } + } + } else { + if (UNLIKELY(false || (data[5].qvalue <= 54))) { + if (LIKELY(false || (data[3].qvalue <= 60))) { + result[0] += -29.670526192638437; + } else { + result[0] += -250.61118646403057; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 72))) { + result[0] += 139.50112391138077; + } else { + result[0] += 5.183830796614544; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 410))) { + if (UNLIKELY(false || (data[6].qvalue <= 6))) { + if (LIKELY(false || (data[0].qvalue <= 376))) { + result[0] += 72.4511482444548; + } else { + result[0] += 480.9499826432033; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 50))) { + result[0] += -339.82229606896135; + } else { + result[0] += -34.16011839278788; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 52))) { + if (LIKELY(false || (data[4].qvalue <= 22))) { + result[0] += 110.97363027456811; + } else { + result[0] += 409.03280051924963; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 468))) { + result[0] += -25.656918184151955; + } else { + result[0] += 673.645954177365; + } + } + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 338))) { + if (LIKELY(false || (data[2].qvalue <= 96))) { + if (LIKELY(false || (data[10].qvalue <= 110))) { + if (LIKELY(false || (data[2].qvalue <= 86))) { + if (LIKELY(false || (data[2].qvalue <= 82))) { + if (LIKELY(false || (data[10].qvalue <= 74))) { + result[0] += -0.803059421195404; + } else { + result[0] += -41.251005891223926; + } + } else { + result[0] += 143.4559149834235; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 116))) { + if (UNLIKELY(false || (data[7].qvalue <= 26))) { + result[0] += 2.624252580548391; + } else { + result[0] += -227.38952669158417; + } + } else { + result[0] += 45.837014796557646; + } + } + } else { + result[0] += -195.38067959274719; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 116))) { + if (LIKELY(false || (data[0].qvalue <= 176))) { + result[0] += 12.646781663778135; + } else { + if (UNLIKELY(false || (data[8].qvalue <= 50))) { + result[0] += 217.37905842024804; + } else { + if (LIKELY(false || (data[9].qvalue <= 100))) { + result[0] += 18.490879448389602; + } else { + result[0] += 157.2895805126459; + } + } + } + } else { + if (LIKELY(false || (data[5].qvalue <= 88))) { + if (LIKELY(false || (data[10].qvalue <= 130))) { + if (LIKELY(false || (data[10].qvalue <= 124))) { + result[0] += -25.376404193377994; + } else { + result[0] += -234.0117708294; + } + } else { + result[0] += 44.70662468521388; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 130))) { + if (UNLIKELY(false || (data[0].qvalue <= 146))) { + result[0] += 12.86322888112687; + } else { + result[0] += 200.2312601015275; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 162))) { + result[0] += -49.82927755893318; + } else { + result[0] += 34.59858546002059; + } + } + } + } + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 64))) { + if (LIKELY(false || (data[7].qvalue <= 60))) { + result[0] += 44.53611652626999; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 100))) { + result[0] += 378.92912612636616; + } else { + result[0] += 114.52994706164273; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 12))) { + if (UNLIKELY(false || (data[0].qvalue <= 416))) { + if (LIKELY(false || (data[10].qvalue <= 92))) { + result[0] += -628.9086683084666; + } else { + result[0] += 107.65691728510829; + } + } else { + if (UNLIKELY(false || (data[7].qvalue <= 148))) { + if (LIKELY(false || (data[0].qvalue <= 432))) { + result[0] += 30.295081044275346; + } else { + result[0] += 409.9041492892673; + } + } else { + result[0] += -143.73134957553725; + } + } + } else { + if (LIKELY(false || (data[9].qvalue <= 146))) { + if (LIKELY(false || (data[9].qvalue <= 138))) { + if (UNLIKELY(false || (data[6].qvalue <= 54))) { + result[0] += 149.66087572300066; + } else { + result[0] += -2.415057229240939; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 418))) { + result[0] += -377.3930807574788; + } else { + result[0] += 56.193799271376434; + } + } + } else { + result[0] += 545.2850103362496; + } + } + } + } + + // Apply base_scores + result[0] += 0; + + // Apply postprocessor + if (!pred_margin) { postprocess(result); } +} + +void fj_predictor::postprocess(double* result) +{ + // Do nothing +} + +// Feature names array +const char* fj_predictor::feature_names[fj_predictor::NUM_FEATURES] = {"time", + "initial_violation_count", + "max_nnz_per_row", + "n_binary_vars", + "n_constraints", + "n_integer_vars", + "n_variables", + "nnz", + "nnz_stddev", + "sparsity", + "unbalancedness", + "uses_load_balancing"}; diff --git a/cpp/src/utilities/models/fj_predictor/quantize.cpp b/cpp/src/utilities/models/fj_predictor/quantize.cpp new file mode 100644 index 0000000000..4bd50efafc --- /dev/null +++ b/cpp/src/utilities/models/fj_predictor/quantize.cpp @@ -0,0 +1,1180 @@ + +/* + * 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 "header.h" + +static const double threshold[] = { + 0.054247050000000012, + 0.077229550000000022, + 0.099138400000000002, + 0.12088200000000002, + 0.14051650000000002, + 0.15852850000000004, + 0.17679700000000004, + 0.19352650000000002, + 0.21021800000000002, + 0.22558550000000002, + 0.24112700000000004, + 0.25703600000000004, + 0.27504900000000004, + 0.29268150000000009, + 0.31128750000000005, + 0.33090700000000001, + 0.34973950000000004, + 0.36837650000000005, + 0.38755100000000003, + 0.40692750000000005, + 0.42464700000000005, + 0.44212900000000005, + 0.46115050000000007, + 0.47875750000000006, + 0.49512500000000004, + 0.51183000000000012, + 0.52816850000000015, + 0.54488850000000011, + 0.56229750000000012, + 0.57881900000000008, + 0.59550200000000009, + 0.61214950000000012, + 0.62885000000000013, + 0.64696700000000018, + 0.66350950000000009, + 0.67997150000000006, + 0.69847550000000014, + 0.7159675000000002, + 0.73463250000000013, + 0.7525940000000001, + 0.77200500000000016, + 0.79034150000000014, + 0.80839950000000005, + 0.82770850000000007, + 0.8455950000000001, + 0.86400500000000013, + 0.88264050000000005, + 0.9004295000000001, + 0.91857200000000006, + 0.93668050000000014, + 0.95404050000000018, + 0.97391950000000016, + 0.99200750000000004, + 1.0099000000000002, + 1.0289150000000002, + 1.0465600000000002, + 1.0646550000000004, + 1.08342, + 1.1014450000000002, + 1.1397000000000002, + 1.1587800000000004, + 1.1770050000000001, + 1.1970650000000003, + 1.2149600000000003, + 1.2334100000000003, + 1.2509550000000003, + 1.2698400000000001, + 1.2892250000000003, + 1.3068250000000001, + 1.3257700000000001, + 1.3439900000000002, + 1.3627650000000002, + 1.3818950000000003, + 1.3997500000000003, + 1.4181450000000002, + 1.4367350000000003, + 1.4547850000000002, + 1.472415, + 1.4914700000000003, + 1.5101100000000003, + 1.5280200000000004, + 1.5462100000000001, + 1.5645300000000002, + 1.5825350000000002, + 1.6018750000000004, + 1.6398350000000002, + 1.6586050000000003, + 1.6957200000000003, + 1.7142950000000001, + 1.7329100000000002, + 1.7519050000000003, + 1.7889300000000004, + 1.8084400000000003, + 1.84535, + 1.8819950000000003, + 1.9005400000000001, + 1.9191650000000002, + 1.9559750000000002, + 1.9729050000000001, + 1.9908450000000002, + 2.0093400000000003, + 2.0269500000000007, + 2.0634850000000005, + 2.0812900000000005, + 2.0995600000000008, + 2.1174250000000003, + 2.1351400000000003, + 2.1524100000000006, + 2.1703750000000004, + 2.1880300000000004, + 2.2057200000000008, + 2.2244050000000004, + 2.2413600000000007, + 2.2589850000000005, + 2.2762350000000002, + 2.2941550000000004, + 2.3126850000000005, + 2.3300800000000002, + 2.3486300000000004, + 2.3668800000000005, + 2.3849800000000001, + 2.4039000000000006, + 2.4207550000000002, + 2.4380750000000004, + 2.4563950000000006, + 2.4738150000000005, + 2.4917700000000003, + 2.5087150000000005, + 2.5256750000000001, + 2.541865, + 2.5750850000000001, + 2.5926700000000005, + 2.6103800000000006, + 2.6462700000000008, + 2.6646600000000005, + 2.6821750000000004, + 2.6995250000000008, + 2.7161250000000003, + 2.7339100000000003, + 2.7519550000000002, + 2.7684900000000003, + 2.7861950000000006, + 2.8210050000000004, + 2.8391800000000003, + 2.8555100000000002, + 2.8724000000000003, + 2.8905950000000007, + 2.9086600000000007, + 2.9273950000000002, + 2.9445150000000004, + 2.9631150000000006, + 2.9813800000000006, + 3.0161400000000005, + 3.0351250000000003, + 3.0535200000000002, + 3.0715450000000004, + 3.0891000000000006, + 3.1077850000000002, + 3.1259600000000005, + 3.1445250000000002, + 3.1638600000000001, + 3.2029950000000005, + 3.2231000000000005, + 3.2427850000000005, + 3.2622000000000004, + 3.2821500000000001, + 3.3022150000000008, + 3.3210900000000003, + 3.3419400000000006, + 3.3625650000000005, + 3.3821600000000003, + 3.4255600000000004, + 3.4476550000000006, + 3.4694350000000003, + 3.4931250000000005, + 3.5172500000000002, + 3.5405750000000005, + 3.5633350000000004, + 3.5881150000000006, + 3.6123700000000007, + 3.6388050000000005, + 3.6645250000000007, + 3.6929500000000002, + 3.7206100000000002, + 3.7487850000000003, + 3.8103150000000006, + 3.8410400000000005, + 3.8753900000000003, + 3.9096100000000003, + 3.9434900000000002, + 3.9779100000000001, + 4.0157400000000001, + 4.0538750000000006, + 4.0947550000000001, + 4.134875000000001, + 4.1789850000000008, + 4.2277500000000012, + 4.2804200000000003, + 4.3315450000000011, + 4.3857550000000005, + 4.4510850000000008, + 4.5179800000000006, + 4.5945550000000006, + 4.6744550000000009, + 4.7680650000000009, + 4.8691100000000009, + 4.9748750000000008, + 5.1040400000000004, + 5.2357750000000012, + 5.3817050000000011, + 5.5487400000000013, + 5.7226800000000013, + 5.9013650000000011, + 6.0933350000000006, + 6.2959750000000012, + 6.5019050000000016, + 6.7376150000000008, + 6.9850100000000008, + 7.2452850000000009, + 7.5387250000000003, + 7.8497500000000011, + 8.206685000000002, + 8.5881250000000016, + 9.0114350000000005, + 9.5016150000000028, + 10.006050000000002, + 10.534950000000002, + 11.213450000000003, + 12.138300000000003, + 13.343550000000002, + 14.829750000000002, + 16.520350000000004, + 19.149450000000005, + 22.676800000000004, + 27.456400000000002, + 34.351750000000003, + 47.268800000000006, + 1.0000000180025095e-35, + 1.5000000000000002, + 2.5000000000000004, + 3.5000000000000004, + 4.5000000000000009, + 7.5000000000000009, + 9.5000000000000018, + 13.500000000000002, + 20.500000000000004, + 27.500000000000004, + 32.500000000000007, + 35.500000000000007, + 73.500000000000014, + 145.50000000000003, + 210.50000000000003, + 256.50000000000006, + 267.50000000000006, + 271.50000000000006, + 338.50000000000006, + 416.50000000000006, + 627.50000000000011, + 646.50000000000011, + 654.50000000000011, + 728.50000000000011, + 785.50000000000011, + 917.50000000000011, + 943.50000000000011, + 1105.5000000000002, + 1168.5000000000002, + 1414.5000000000002, + 1511.5000000000002, + 1766.5000000000002, + 2142.5000000000005, + 2424.5000000000005, + 2476.5000000000005, + 2824.5000000000005, + 3182.5000000000005, + 3233.5000000000005, + 3334.5000000000005, + 3428.5000000000005, + 3734.5000000000005, + 3862.0000000000005, + 4068.5000000000005, + 4221.5000000000009, + 4296.5000000000009, + 4701.5000000000009, + 4781.5000000000009, + 6990.5000000000009, + 7213.5000000000009, + 7449.5000000000009, + 8540.5000000000018, + 9118.5000000000018, + 9521.5000000000018, + 10539.500000000002, + 11717.500000000002, + 12095.500000000002, + 13029.500000000002, + 14400.000000000002, + 14615.500000000002, + 15096.500000000002, + 15817.500000000002, + 16830.500000000004, + 17346.500000000004, + 20007.000000000004, + 20988.500000000004, + 21532.500000000004, + 23313.500000000004, + 24562.500000000004, + 30402.000000000004, + 35983.500000000007, + 40040.000000000007, + 41987.500000000007, + 43540.000000000007, + 44215.500000000007, + 45601.500000000007, + 47232.500000000007, + 54221.000000000007, + 68471.500000000015, + 74712.500000000015, + 89913.000000000015, + 125519.00000000001, + 149049.00000000003, + 230573.00000000003, + 3.5000000000000004, + 4.5000000000000009, + 5.5000000000000009, + 6.5000000000000009, + 7.5000000000000009, + 8.5000000000000018, + 11.500000000000002, + 13.500000000000002, + 14.500000000000002, + 15.500000000000002, + 17.500000000000004, + 24.500000000000004, + 29.500000000000004, + 30.500000000000004, + 31.500000000000004, + 33.500000000000007, + 35.500000000000007, + 37.500000000000007, + 40.500000000000007, + 41.500000000000007, + 42.500000000000007, + 43.500000000000007, + 45.500000000000007, + 49.500000000000007, + 50.500000000000007, + 55.500000000000007, + 59.500000000000007, + 60.500000000000007, + 65.500000000000014, + 68.000000000000014, + 71.000000000000014, + 72.500000000000014, + 73.500000000000014, + 74.500000000000014, + 80.500000000000014, + 90.000000000000014, + 96.500000000000014, + 98.500000000000014, + 99.500000000000014, + 105.50000000000001, + 108.50000000000001, + 112.50000000000001, + 113.50000000000001, + 116.50000000000001, + 117.50000000000001, + 118.50000000000001, + 121.50000000000001, + 124.50000000000001, + 127.00000000000001, + 156.50000000000003, + 158.50000000000003, + 174.50000000000003, + 182.00000000000003, + 186.50000000000003, + 190.50000000000003, + 193.00000000000003, + 197.50000000000003, + 215.50000000000003, + 230.50000000000003, + 238.50000000000003, + 242.50000000000003, + 274.50000000000006, + 287.50000000000006, + 310.00000000000006, + 317.00000000000006, + 475.50000000000006, + 478.00000000000006, + 501.50000000000006, + 508.00000000000006, + 558.00000000000011, + 586.50000000000011, + 594.50000000000011, + 644.50000000000011, + 665.50000000000011, + 686.00000000000011, + 829.00000000000011, + 933.00000000000011, + 973.00000000000011, + 1000.5000000000001, + 1023.0000000000001, + 1101.5000000000002, + 1594.0000000000002, + 1773.5000000000002, + 1868.0000000000002, + 1935.0000000000002, + 2075.5000000000005, + 2658.5000000000005, + 2842.0000000000005, + 2928.0000000000005, + 3159.0000000000005, + 3844.0000000000005, + 3959.0000000000005, + 5956.0000000000009, + 6744.5000000000009, + 7399.5000000000009, + 8990.0000000000018, + 9523.5000000000018, + 11378.500000000002, + 12174.500000000002, + 15191.500000000002, + 18400.500000000004, + 20392.500000000004, + 21043.500000000004, + 21811.000000000004, + 30397.000000000004, + 30926.500000000004, + 33002.500000000007, + 48554.500000000007, + 56727.500000000007, + 77009.500000000015, + 97457.000000000015, + 117653.00000000001, + 137723.50000000003, + 145615.50000000003, + 1.0000000180025095e-35, + 19.000000000000004, + 31.000000000000004, + 55.500000000000007, + 69.500000000000014, + 70.500000000000014, + 99.000000000000014, + 119.00000000000001, + 176.50000000000003, + 189.50000000000003, + 196.00000000000003, + 201.00000000000003, + 219.00000000000003, + 263.00000000000006, + 290.00000000000006, + 306.00000000000006, + 317.50000000000006, + 361.50000000000006, + 384.00000000000006, + 417.00000000000006, + 448.50000000000006, + 534.50000000000011, + 593.50000000000011, + 622.50000000000011, + 693.50000000000011, + 889.50000000000011, + 927.50000000000011, + 1116.5000000000002, + 1455.5000000000002, + 1498.0000000000002, + 1995.5000000000002, + 2027.5000000000002, + 2105.5000000000005, + 2463.5000000000005, + 2518.5000000000005, + 2595.5000000000005, + 2630.0000000000005, + 3336.0000000000005, + 4222.5000000000009, + 4319.5000000000009, + 4543.5000000000009, + 5196.5000000000009, + 5211.0000000000009, + 5279.0000000000009, + 5647.0000000000009, + 6426.5000000000009, + 6471.5000000000009, + 6781.5000000000009, + 7008.0000000000009, + 7273.0000000000009, + 8219.0000000000018, + 8313.0000000000018, + 8346.0000000000018, + 8890.5000000000018, + 9579.5000000000018, + 10096.000000000002, + 10296.500000000002, + 10412.000000000002, + 10570.000000000002, + 10722.500000000002, + 11284.000000000002, + 11482.000000000002, + 12403.500000000002, + 13743.000000000002, + 16623.500000000004, + 17719.000000000004, + 20045.500000000004, + 22896.000000000004, + 24893.000000000004, + 29297.500000000004, + 32360.500000000004, + 32826.000000000007, + 42917.000000000007, + 45655.500000000007, + 49866.500000000007, + 55546.000000000007, + 57005.000000000007, + 60339.000000000007, + 70968.500000000015, + 77454.500000000015, + 86012.000000000015, + 86932.000000000015, + 88579.000000000015, + 98965.000000000015, + 127333.50000000001, + 169734.00000000003, + 233398.00000000003, + 320960.50000000006, + 341815.50000000006, + 1315285.0000000002, + 2747995.0000000005, + 10.500000000000002, + 16.500000000000004, + 38.000000000000007, + 42.500000000000007, + 61.500000000000007, + 76.500000000000014, + 101.50000000000001, + 300.50000000000006, + 464.00000000000006, + 578.50000000000011, + 640.00000000000011, + 653.50000000000011, + 721.00000000000011, + 849.00000000000011, + 1140.0000000000002, + 1197.0000000000002, + 1359.0000000000002, + 1627.5000000000002, + 1925.0000000000002, + 2298.5000000000005, + 2356.0000000000005, + 2394.5000000000005, + 2529.0000000000005, + 3626.5000000000005, + 4099.0000000000009, + 5157.0000000000009, + 5521.5000000000009, + 7489.0000000000009, + 8797.5000000000018, + 9813.0000000000018, + 11172.500000000002, + 11823.000000000002, + 12490.500000000002, + 12827.000000000002, + 13934.000000000002, + 14718.000000000002, + 14944.500000000002, + 15924.500000000002, + 16641.500000000004, + 17124.000000000004, + 17409.000000000004, + 17805.000000000004, + 18541.500000000004, + 20890.500000000004, + 22641.000000000004, + 23763.500000000004, + 27226.000000000004, + 28849.000000000004, + 34014.000000000007, + 39933.000000000007, + 48730.000000000007, + 55362.000000000007, + 58384.000000000007, + 91637.500000000015, + 97665.000000000015, + 108046.50000000001, + 111482.00000000001, + 127740.00000000001, + 141620.50000000003, + 146171.50000000003, + 164305.50000000003, + 167857.50000000003, + 206722.00000000003, + 215085.50000000003, + 271255.50000000006, + 364916.50000000006, + 376899.50000000006, + 446870.00000000006, + 504658.00000000006, + 534428.50000000012, + 2917540.0000000005, + 8.0000000000000018, + 40.500000000000007, + 69.500000000000014, + 76.000000000000014, + 99.000000000000014, + 102.50000000000001, + 116.50000000000001, + 128.50000000000003, + 147.50000000000003, + 161.00000000000003, + 190.50000000000003, + 201.00000000000003, + 218.00000000000003, + 246.00000000000003, + 301.50000000000006, + 306.50000000000006, + 411.00000000000006, + 448.50000000000006, + 490.50000000000006, + 739.00000000000011, + 786.50000000000011, + 835.00000000000011, + 1435.5000000000002, + 1490.0000000000002, + 1585.0000000000002, + 1947.5000000000002, + 2089.5000000000005, + 2208.0000000000005, + 2262.0000000000005, + 2504.0000000000005, + 2595.5000000000005, + 2694.0000000000005, + 3290.5000000000005, + 3486.0000000000005, + 5199.5000000000009, + 5448.5000000000009, + 7246.0000000000009, + 8305.0000000000018, + 8345.5000000000018, + 8884.0000000000018, + 11484.500000000002, + 12542.000000000002, + 13993.500000000002, + 14321.000000000002, + 14486.500000000002, + 29297.500000000004, + 41026.500000000007, + 45655.500000000007, + 57005.000000000007, + 62415.500000000007, + 69861.000000000015, + 77483.500000000015, + 82860.500000000015, + 86012.000000000015, + 105975.50000000001, + 122661.50000000001, + 135210.50000000003, + 154808.00000000003, + 165244.00000000003, + 195544.50000000003, + 321530.50000000006, + 341815.50000000006, + 14.500000000000002, + 66.500000000000014, + 242.50000000000003, + 265.50000000000006, + 306.50000000000006, + 341.50000000000006, + 518.00000000000011, + 730.50000000000011, + 758.50000000000011, + 896.50000000000011, + 982.50000000000011, + 1007.5000000000001, + 1108.0000000000002, + 1133.5000000000002, + 1178.0000000000002, + 1436.0000000000002, + 1599.5000000000002, + 1916.5000000000002, + 2036.0000000000002, + 2066.5000000000005, + 2456.0000000000005, + 2816.0000000000005, + 2849.0000000000005, + 3190.5000000000005, + 3226.5000000000005, + 3564.0000000000005, + 3726.5000000000005, + 4004.5000000000005, + 4719.5000000000009, + 5175.0000000000009, + 5663.0000000000009, + 5848.0000000000009, + 7347.5000000000009, + 8946.0000000000018, + 10887.500000000002, + 11141.500000000002, + 12386.000000000002, + 12893.000000000002, + 12981.000000000002, + 13236.000000000002, + 13742.500000000002, + 13874.000000000002, + 14396.000000000002, + 14769.500000000002, + 16616.000000000004, + 17772.500000000004, + 18503.000000000004, + 18928.000000000004, + 19963.500000000004, + 22431.000000000004, + 22656.000000000004, + 23089.500000000004, + 25009.500000000004, + 25158.500000000004, + 29801.500000000004, + 30930.500000000004, + 32941.500000000007, + 33925.000000000007, + 35517.000000000007, + 35966.500000000007, + 37351.500000000007, + 38399.000000000007, + 40769.000000000007, + 42352.500000000007, + 43749.000000000007, + 45226.000000000007, + 46621.500000000007, + 57435.500000000007, + 58829.500000000007, + 60692.000000000007, + 62587.500000000007, + 64024.000000000007, + 67166.000000000015, + 72662.000000000015, + 73712.500000000015, + 74556.500000000015, + 78614.500000000015, + 83046.500000000015, + 98968.000000000015, + 102883.00000000001, + 124003.00000000001, + 131103.50000000003, + 135356.50000000003, + 145271.00000000003, + 167192.00000000003, + 172253.00000000003, + 198236.00000000003, + 209702.50000000003, + 298248.50000000006, + 333792.50000000006, + 343878.50000000006, + 404776.00000000006, + 460381.00000000006, + 628991.00000000012, + 83.500000000000014, + 183.50000000000003, + 468.00000000000006, + 1396.0000000000002, + 2433.0000000000005, + 3465.0000000000005, + 3645.5000000000005, + 5270.0000000000009, + 6121.5000000000009, + 6510.5000000000009, + 7307.0000000000009, + 8358.5000000000018, + 8618.5000000000018, + 8828.5000000000018, + 9135.0000000000018, + 13322.500000000002, + 14156.000000000002, + 14773.000000000002, + 18059.000000000004, + 19199.000000000004, + 20030.000000000004, + 21288.500000000004, + 22830.500000000004, + 39487.500000000007, + 51294.500000000007, + 55184.000000000007, + 58766.500000000007, + 78090.000000000015, + 79203.500000000015, + 85545.500000000015, + 86497.500000000015, + 96575.500000000015, + 101879.50000000001, + 104890.50000000001, + 106509.00000000001, + 111473.00000000001, + 133804.50000000003, + 143238.00000000003, + 152266.50000000003, + 188714.00000000003, + 193144.50000000003, + 201859.00000000003, + 204145.00000000003, + 211177.00000000003, + 216282.00000000003, + 227905.50000000003, + 233158.50000000003, + 242732.00000000003, + 247534.50000000003, + 252735.00000000003, + 263001.50000000006, + 267680.00000000006, + 286053.50000000006, + 301277.50000000006, + 316160.00000000006, + 326485.50000000006, + 341223.00000000006, + 382538.50000000006, + 397214.50000000006, + 420753.50000000006, + 427014.00000000006, + 474030.00000000006, + 484374.50000000006, + 490157.50000000006, + 493561.50000000006, + 517275.50000000006, + 525159.50000000012, + 536338.50000000012, + 555454.50000000012, + 577752.00000000012, + 616908.00000000012, + 643671.50000000012, + 665158.00000000012, + 688029.50000000012, + 748092.00000000012, + 791728.50000000012, + 805917.00000000012, + 833331.00000000012, + 846131.50000000012, + 889587.00000000012, + 925745.00000000012, + 960960.50000000012, + 993471.50000000012, + 1000860.0000000001, + 1033940.0000000001, + 1088030.0000000002, + 1104190.0000000002, + 1141090.0000000002, + 1227215.0000000002, + 1302410.0000000002, + 1327790.0000000002, + 1565560.0000000002, + 1713120.0000000002, + 1986515.0000000002, + 2295955.0000000005, + 2459445.0000000005, + 2786970.0000000005, + 4311770.0000000009, + 4897840.0000000009, + 7675510.0000000009, + 12103450.000000002, + 1.0000000180025095e-35, + 0.025444000000000005, + 0.079041650000000005, + 0.47263850000000002, + 0.51316300000000015, + 0.61466950000000009, + 0.70002100000000012, + 0.84333400000000014, + 1.0452850000000002, + 1.2015150000000003, + 1.4797750000000003, + 1.8327900000000001, + 2.2462950000000004, + 2.4300000000000006, + 2.5927000000000002, + 2.6670150000000006, + 2.8069300000000004, + 3.1766550000000007, + 3.2475100000000006, + 3.3747950000000002, + 3.4595750000000005, + 3.6730550000000002, + 3.8561550000000007, + 4.4209650000000007, + 4.6392950000000015, + 4.865940000000001, + 4.9636350000000009, + 5.1358750000000013, + 5.5377350000000005, + 5.745820000000001, + 5.9460500000000005, + 6.3433350000000006, + 6.3778100000000011, + 6.5389250000000008, + 6.6173200000000012, + 7.1363800000000017, + 7.4741750000000016, + 7.6210850000000008, + 8.6734450000000027, + 9.4143250000000016, + 11.746350000000001, + 12.052000000000001, + 12.250050000000002, + 13.267300000000001, + 17.041250000000002, + 17.986650000000001, + 21.122200000000003, + 21.844500000000004, + 23.696150000000006, + 25.750650000000004, + 26.183450000000004, + 27.539350000000002, + 31.875650000000004, + 36.846500000000006, + 38.188850000000009, + 44.081200000000003, + 52.333200000000005, + 54.716150000000006, + 60.769450000000006, + 65.705850000000012, + 85.315750000000023, + 108.21300000000001, + 122.06700000000002, + 150.02500000000001, + 157.58100000000002, + 167.36850000000001, + 180.69350000000003, + 206.27400000000003, + 282.04500000000002, + 301.06900000000002, + 431.21800000000002, + 513.42400000000009, + 780.49650000000008, + 813.8660000000001, + 850.00950000000012, + 1134.5900000000004, + 1346.6950000000002, + 2370.7800000000002, + 2928.5150000000008, + 1.3466250000000002e-05, + 2.2664350000000006e-05, + 2.9555800000000003e-05, + 3.5335850000000012e-05, + 3.6073500000000006e-05, + 3.9458500000000002e-05, + 4.3825250000000005e-05, + 5.8516e-05, + 6.4233050000000024e-05, + 7.0834150000000017e-05, + 8.9559350000000012e-05, + 9.5430800000000015e-05, + 0.00011455550000000001, + 0.00012689600000000001, + 0.00014240450000000004, + 0.00016513700000000001, + 0.00018171050000000003, + 0.00018756350000000003, + 0.00020882050000000003, + 0.00021563800000000001, + 0.00022961900000000003, + 0.00026418050000000004, + 0.00027278350000000008, + 0.00029956750000000005, + 0.00032692350000000002, + 0.00033880400000000006, + 0.00035918650000000006, + 0.00038193300000000004, + 0.00042447950000000008, + 0.00043747650000000002, + 0.00044906850000000007, + 0.00049152850000000004, + 0.00052591450000000011, + 0.00057647100000000017, + 0.00058207650000000012, + 0.00098674500000000003, + 0.0011041950000000003, + 0.0012247250000000001, + 0.0012767100000000001, + 0.0013355900000000002, + 0.001509605, + 0.0017419350000000002, + 0.0018100400000000004, + 0.0018508650000000004, + 0.0021851550000000007, + 0.0024035350000000005, + 0.00336228, + 0.0038408950000000004, + 0.004200225000000001, + 0.0047036750000000009, + 0.0051918100000000007, + 0.0055236550000000014, + 0.0057092000000000002, + 0.0059764100000000006, + 0.0069381250000000007, + 0.0073699550000000009, + 0.007565500000000001, + 0.0084957950000000022, + 0.0089344600000000017, + 0.0099261550000000007, + 0.014203600000000002, + 0.021387700000000006, + 0.023916750000000004, + 0.026089700000000004, + 0.05008255000000001, + 0.055832150000000004, + 0.060545100000000004, + 0.070805450000000006, + 0.087852150000000004, + 0.11160100000000002, + 0.12906450000000003, + 0.18728800000000004, + 0.22001300000000004, + 0.25453400000000004, + 0.26550900000000005, + 0.31291300000000005, + 0.35104150000000006, + 0.41306200000000004, + 0.57042700000000013, + 0.64485350000000008, + 0.96875000000000011, + 1.0000000180025095e-35, + 0.0039979100000000012, + 0.0082457700000000012, + 0.038986650000000005, + 0.054666050000000001, + 0.070373600000000022, + 0.097335900000000017, + 0.19564850000000003, + 0.20630450000000003, + 0.24236000000000002, + 0.25417800000000007, + 0.25770150000000008, + 0.27507600000000004, + 0.28204950000000006, + 0.28742100000000009, + 0.29918200000000006, + 0.30476150000000007, + 0.33851400000000004, + 0.37035050000000008, + 0.38581450000000006, + 0.44627100000000003, + 0.45825600000000005, + 0.4758035000000001, + 0.52170800000000017, + 0.54062450000000017, + 0.54514950000000006, + 0.57222350000000011, + 0.67992200000000003, + 0.70028650000000014, + 0.75992800000000005, + 0.81040200000000018, + 0.85826700000000011, + 0.95305950000000006, + 0.98670650000000015, + 1.0208650000000001, + 1.0782450000000001, + 1.1067700000000003, + 1.1631450000000003, + 1.2699600000000004, + 1.2872250000000001, + 1.2987750000000002, + 1.3993100000000003, + 1.5141650000000002, + 1.5734200000000003, + 1.6142300000000003, + 1.6834550000000001, + 1.8165200000000001, + 1.9725750000000002, + 2.0513250000000007, + 2.0544200000000008, + 2.1066100000000003, + 2.2829500000000005, + 2.3961550000000003, + 2.6080850000000004, + 2.7410500000000004, + 3.0611950000000001, + 3.5134100000000008, + 3.5836800000000006, + 4.7010550000000011, + 4.7679900000000011, + 5.106815000000001, + 5.1937250000000015, + 5.2231500000000013, + 5.349705000000001, + 5.4792200000000006, + 5.6649900000000004, + 5.8800200000000009, + 6.6231050000000007, + 6.9449300000000003, + 7.3326800000000008, + 8.199600000000002, + 11.794000000000002, + 16.289250000000003, + 19.559350000000006, + 24.766300000000005, + 1.0000000180025095e-35, +}; + +static const int th_begin[] = { + 0, + 237, + 320, + 434, + 525, + 596, + 658, + 752, + 853, + 932, + 1013, + 1088, +}; + +static const int th_len[] = { + 237, + 83, + 114, + 91, + 71, + 62, + 94, + 101, + 79, + 81, + 75, + 1, +}; + +/* + * \brief Function to convert a feature value into bin index. + * \param val Feature value, in floating-point + * \param fid Feature identifier + * \return bin Index corresponding to given feature value + */ +int fj_predictor::quantize(double val, unsigned fid) +{ + const size_t offset = th_begin[fid]; + const double* array = &threshold[offset]; + int len = th_len[fid]; + int low = 0; + int high = len; + int mid; + double mval; + // It is possible th_begin[i] == [total_num_threshold]. This means that + // all features i, (i+1), ... are not used for any of the splits in the model. + // So in this case, just return something + if (offset == 1089 || val < array[0]) { return -10; } + while (low + 1 < high) { + mid = (low + high) / 2; + mval = array[mid]; + if (val == mval) { + return mid * 2; + } else if (val < mval) { + high = mid; + } else { + low = mid; + } + } + if (array[low] == val) { + return low * 2; + } else if (high == len) { + return len * 2; + } else { + return low * 2 + 1; + } +} diff --git a/cpp/src/utilities/work_unit_predictor.cpp b/cpp/src/utilities/work_unit_predictor.cpp index 56c04a9a54..8e1d871115 100644 --- a/cpp/src/utilities/work_unit_predictor.cpp +++ b/cpp/src/utilities/work_unit_predictor.cpp @@ -21,20 +21,10 @@ #include #include #include +#include #include -#include - -#include "models_ubj.h" - -#define safe_xgboost(call) \ - { \ - int err = (call); \ - if (err != 0) { \ - throw std::runtime_error(std::string(__FILE__) + ":" + std::to_string(__LINE__) + \ - ": error in " + #call + ":" + XGBGetLastError()); \ - } \ - } +#include "models/fj_predictor/header.h" namespace cuopt { @@ -53,132 +43,43 @@ static inline uint32_t compute_hash(std::vector h_contents) return hash; } -work_unit_predictor_t::work_unit_predictor_t(const std::string& model_name) : model_name(model_name) +template +float work_unit_predictor_t::predict_scalar( + const std::map& features) const { - BoosterHandle booster_handle; - int ret = XGBoosterCreate(nullptr, 0, &booster_handle); - safe_xgboost(ret); - assert(ret == 0); - if (ret != 0) return; - raw_handle = reinterpret_cast(booster_handle); - - // load the embedded model from the .rodata section - const unsigned char* model_data = nullptr; - unsigned int model_len = 0; - for (unsigned int i = 0; i < xgboost_models_count; i++) { - if (strcmp(xgboost_models[i].name, model_name.c_str()) == 0) { - model_data = xgboost_models[i].data; - model_len = xgboost_models[i].length; - break; + typename model_t::Entry data[model_t::NUM_FEATURES]; + for (int i = 0; i < model_t::NUM_FEATURES; ++i) { + if (features.find(std::string(model_t::feature_names[i])) == features.end()) { + data[i].missing = -1; + printf("Feature %s: missing\n", model_t::feature_names[i]); + } else { + data[i].fvalue = features.at(std::string(model_t::feature_names[i])); + printf("Feature %s: %f\n", model_t::feature_names[i], data[i].fvalue); } } - - assert(model_data != nullptr); - assert(model_len > 0); - - ret = XGBoosterLoadModelFromBuffer(booster_handle, model_data, model_len); - safe_xgboost(ret); - assert(ret == 0); - if (ret != 0) return; - - XGBoosterSetParam(booster_handle, "predictor", "gpu_predictor"); - - is_valid = true; -} - -work_unit_predictor_t::~work_unit_predictor_t() -{ - if (raw_handle != nullptr) { - BoosterHandle booster_handle = reinterpret_cast(raw_handle); - XGBoosterFree(booster_handle); - raw_handle = nullptr; + // Compute a hash key for the relevant inputs + std::vector cache_vec; + cache_vec.reserve(model_t::NUM_FEATURES); + for (int i = 0; i < model_t::NUM_FEATURES; ++i) { + cache_vec.push_back(data[i].missing != -1 ? data[i].fvalue + : std::numeric_limits::quiet_NaN()); } + uint32_t key = compute_hash(cache_vec); + + auto cached_it = prediction_cache.find(key); + if (cached_it != prediction_cache.end()) { return cached_it->second; } + + // run predictor + double result = 0.0; + auto start = std::chrono::high_resolution_clock::now(); + model_t::predict(data, 0, &result); + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = end - start; + CUOPT_LOG_DEBUG("Prediction time: %f ms", elapsed.count()); + CUOPT_LOG_DEBUG("Result: %f", result); + return std::abs(result); } -float work_unit_predictor_t::predict_scalar(const std::vector& features, bool verbose) const -{ - assert(is_valid && raw_handle != nullptr); - if (!is_valid || raw_handle == nullptr) return std::numeric_limits::signaling_NaN(); - - // Check cache first - uint32_t hash = compute_hash(features); - auto it = prediction_cache.find(hash); - if (it != prediction_cache.end()) { return it->second; } - - // Timer: measure elapsed time for prediction - auto t_start = std::chrono::high_resolution_clock::now(); - - // Create DMatrix from feature vector - DMatrixHandle dmatrix; - int ret = XGDMatrixCreateFromMat(features.data(), - 1, // nrow - features.size(), // ncol - std::numeric_limits::quiet_NaN(), // missing value - &dmatrix); - safe_xgboost(ret); - - // Predict from DMatrix - char const config[] = - "{\"type\": 0, \"iteration_begin\": 0, " - "\"iteration_end\": 0, \"strict_shape\": true, \"training\": false}"; - - const bst_ulong* out_shape = nullptr; - bst_ulong out_dim = 0; - const float* out_result = nullptr; - ret = XGBoosterPredictFromDMatrix(reinterpret_cast(raw_handle), - dmatrix, - config, - &out_shape, - &out_dim, - &out_result); - safe_xgboost(ret); - - float prediction = out_result[0]; - - // Free DMatrix - XGDMatrixFree(dmatrix); - - auto t_end = std::chrono::high_resolution_clock::now(); - double elapsed_ms = std::chrono::duration(t_end - t_start).count(); - printf("[work_unit_predictor_t::predict_scalar] Prediction took %.3f ms\n", elapsed_ms); - - // Store in cache - prediction_cache[hash] = prediction; - - return prediction; -} - -float work_unit_predictor_t::predict_scalar(const std::map& feature_map, - bool verbose) const -{ - // Extract features in the expected order for the model - // Order matches training data: [target_time, n_of_minimums_for_exit, n_variables, n_constraints, - // nnz, sparsity, nnz_stddev, unbalancedness] - std::vector features; - features.reserve(feature_map.size()); - - // Add features in the order expected by the model - // This order should match what was used during training - const std::vector feature_order = {"target_time", - "n_of_minimums_for_exit", - "n_variables", - "n_constraints", - "nnz", - "sparsity", - "nnz_stddev", - "unbalancedness"}; - - for (const auto& name : feature_order) { - auto it = feature_map.find(name); - if (it != feature_map.end()) { - features.push_back(it->second); - } else { - // Feature not found - use default value of 0 - features.push_back(0.0f); - } - } - - return predict_scalar(features, verbose); -} +template class work_unit_predictor_t; } // namespace cuopt diff --git a/cpp/src/utilities/work_unit_predictor.hpp b/cpp/src/utilities/work_unit_predictor.hpp index 420fc9547c..0a7c63e33d 100644 --- a/cpp/src/utilities/work_unit_predictor.hpp +++ b/cpp/src/utilities/work_unit_predictor.hpp @@ -24,17 +24,12 @@ namespace cuopt { +template class work_unit_predictor_t { public: - work_unit_predictor_t(const std::string& model_name); - ~work_unit_predictor_t(); - float predict_scalar(const std::vector& features, bool verbose = false) const; - float predict_scalar(const std::map& features, bool verbose = false) const; + float predict_scalar(const std::map& features) const; private: - std::string model_name; - void* raw_handle{nullptr}; // void* to avoid including xgboost in every MIP translation unit - bool is_valid{false}; mutable std::unordered_map prediction_cache; }; diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index ad2bf4ba17..c5c695299b 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -179,8 +179,8 @@ static bool run_fj_check_determinism(std::string test_instance, int iter_limit) detail::fj_settings_t fj_settings; fj_settings.time_limit = std::numeric_limits::max(); fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; - fj_settings.n_of_minimums_for_exit = 5000; - fj_settings.work_unit_limit = 0.15; // run for 0.5wu (~0.5s) + fj_settings.n_of_minimums_for_exit = 5000 * 1000; + fj_settings.work_unit_limit = 0.5; // run for 0.5wu (~0.5s) fj_settings.update_weights = true; fj_settings.feasibility_run = false; // fj_settings.iteration_limit = iter_limit; diff --git a/scripts/README_REGRESSION.md b/scripts/README_REGRESSION.md index ab8736aa47..ca6cba0ca1 100644 --- a/scripts/README_REGRESSION.md +++ b/scripts/README_REGRESSION.md @@ -185,7 +185,11 @@ python train_regressor.py data.pkl --regressor lightgbm --treelite-compile 8 -o ### Optimization Impact -All TL2cgen exports include both branch annotation and quantization automatically: +All TL2cgen exports include the following optimizations automatically: + +1. **Branch Annotation**: Uses training data statistics to add branch prediction hints +2. **Quantization**: Reduces memory footprint by converting floating-point to integers +3. **Missing Data Removal**: Removes unnecessary missing data checks (assumes all features provided) | Configuration | Speed | Memory | Accuracy | |---------------|-------|--------|----------| @@ -209,21 +213,80 @@ models/ └── *.cpp / *.h # Other C++ source files (quantized + annotated) ``` -**Namespace Wrapping**: All generated files are automatically wrapped in a C++ namespace with the model name (derived from the input pickle file basename) to avoid naming conflicts when using multiple models in the same project. For example, if the input is `my_dataset.pkl`: -- All functions are in `namespace my_dataset { ... }` +**Class Wrapping**: All generated files are automatically wrapped in a C++ class with the model name (derived from the input pickle file basename) to avoid naming conflicts when using multiple models in the same project. For example, if the input is `my_dataset.pkl`: +- All functions and data are in `class my_dataset { public: ... };` +- All class members are `static` - no instantiation required - Access functions as `my_dataset::predict()`, `my_dataset::get_num_features()`, etc. - All `.c` files are renamed to `.cpp` for C++ compilation +- Header includes `#pragma once` for include guards The generated `header.h` includes: -- `namespace { ... }` wrapping all declarations -- `#define NUM_FEATURES ` - Number of features -- `extern const char* feature_names[]` - Feature names declaration -- Function declarations (e.g., `predict()`, `get_num_features()`) within the namespace +- `#pragma once` at the top +- `#include` statements (outside the class) +- `class { public: ... };` wrapping all declarations +- `static constexpr int NUM_FEATURES` - Number of features +- `static const char* feature_names[]` - Feature names declaration +- Function declarations (e.g., `predict()`, `get_num_features()`) as public static members The generated `main.cpp` includes: -- `namespace { ... }` wrapping all implementations -- `const char* feature_names[]` - Feature names array definition -- Function implementations within the namespace +- `#include` statements at the top +- Macro definitions (`LIKELY`, `UNLIKELY`, `N_TARGET`, `MAX_N_CLASS`) - moved from header for implementation-only use +- Function implementations with `::function_name` qualification +- `const char* ::feature_names[]` - Feature names array definition (at the end of file) + +### Example Generated Code Structure + +**header.h:** + +```cpp +#pragma once + +#include + +class my_dataset { +public: + static float predict(float* data, int pred_margin); + static int get_num_feature(); + // ... other function declarations ... + + static constexpr int NUM_FEATURES = 42; + static const char* feature_names[NUM_FEATURES]; +}; +``` + +**main.cpp:** + +```cpp +#include "header.h" + +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define N_TARGET 1 + +float my_dataset::predict(float* data, int pred_margin) { + // implementation +} + +int my_dataset::get_num_feature() { + return NUM_FEATURES; +} + +// Feature names array +const char* my_dataset::feature_names[my_dataset::NUM_FEATURES] = { + "n_variables", + "n_constraints", + // ... +}; +``` + +**Usage:** + +```cpp +#include "xgboost_c_code/header.h" + +// Call static methods directly - no instantiation needed +float result = my_dataset::predict(features, 0); +int num = my_dataset::get_num_feature(); +``` ## Feature Selection Examples diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index 84c32ae3d6..7c5f1d298e 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -73,7 +73,11 @@ 'avg_var_degree', 'equality_ratio', 'integer_ratio', - 'binary_ratio' + 'binary_ratio', + 'max_related_vars', + 'problem_size_score', + 'structural_complexity', + 'tight_constraint_ratio' ] # Alternatively, specify ONLY the features you want to use @@ -639,7 +643,7 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, quantize_path = os.path.join(source_dir, 'quantize.c') recipe_path = os.path.join(source_dir, 'recipe.json') - # Rename all .c files to .cpp and wrap in namespace + # Rename all .c files to .cpp and wrap in class if model_name: try: import glob @@ -652,12 +656,49 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, with open(c_file, 'r') as f: content = f.read() - # Wrap in namespace - namespaced_content = f'namespace {model_name} {{\n\n{content}\n\n}} // namespace {model_name}\n' + # Split content into includes and rest + lines = content.split('\n') + include_lines = [] + code_lines = [] + in_includes = True + + for line in lines: + if in_includes and (line.strip().startswith('#include') or line.strip().startswith('#') or line.strip() == ''): + include_lines.append(line) + else: + in_includes = False + code_lines.append(line) + + # Prefix function definitions with ClassName:: (for .cpp files, not class wrapping) + import re + processed_lines = [] + for line in code_lines: + # Detect function definitions (return_type function_name(...)) + if line and not line.strip().startswith('//') and not line.strip().startswith('/*'): + # Check if it's a function definition + # Pattern: type name(...) or type* name(...) etc. + func_pattern = r'^(\s*)((?:const\s+)?(?:unsigned\s+)?(?:struct\s+)?[\w_]+(?:\s*\*)*\s+)([\w_]+)(\s*\()' + match = re.match(func_pattern, line) + if match and '::' not in line: # Don't add if already qualified + indent = match.group(1) + return_type = match.group(2) + func_name = match.group(3) + rest = line[match.end(3):] + # Prefix function name with class name + line = f'{indent}{return_type}{model_name}::{func_name}{rest}' + processed_lines.append(line) + code_lines = processed_lines + + # Don't wrap in class for .cpp files - just output the definitions + includes_str = '\n'.join(include_lines) + code_str = '\n'.join(code_lines) + + # For .cpp files, no class wrapper needed + cpp_content = f'{includes_str}\n\n{code_str}\n' # Write to .cpp file with open(cpp_file, 'w') as f: - f.write(namespaced_content) + f.write(cpp_content) # Remove original .c file os.remove(c_file) @@ -666,52 +707,161 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, main_path = main_path[:-2] + '.cpp' quantize_path = quantize_path[:-2] + '.cpp' - print(f" Renamed {len(c_files)} .c files to .cpp and wrapped in namespace '{model_name}'") + print(f" Renamed {len(c_files)} .c files to .cpp") except Exception as e: print(f" Warning: Failed to rename .c files: {e}") - # Wrap header.h content in namespace + # Optimize main.cpp by removing unnecessary missing data checks + # Since all features are always provided, replace !(data[X].missing != -1) with false + if os.path.exists(main_path): + try: + with open(main_path, 'r') as f: + content = f.read() + + # Replace pattern !(data[N].missing != -1) with false + import re + original_content = content + content = re.sub(r'!\(data\[\d+\]\.missing != -1\)', 'false', content) + + if content != original_content: + with open(main_path, 'w') as f: + f.write(content) + print(f" Optimized main.cpp by removing unnecessary missing data checks") + except Exception as e: + print(f" Warning: Failed to optimize main.cpp: {e}") + + # Wrap header.h content in class with #pragma once + defines_to_move = [] if model_name and os.path.exists(header_path): try: with open(header_path, 'r') as f: content = f.read() - # Wrap in namespace - namespaced_content = f'namespace {model_name} {{\n\n{content}\n\n}} // namespace {model_name}\n' + # Split content into includes, defines to move, and rest + lines = content.split('\n') + include_lines = [] + code_lines = [] + in_includes = True + i = 0 + + while i < len(lines): + line = lines[i] + + if in_includes and (line.strip().startswith('#include') or line.strip() == ''): + include_lines.append(line) + i += 1 + # Detect macros to move to main.cpp + elif line.strip().startswith('#if defined(__clang__)') or line.strip().startswith('#define N_TARGET') or line.strip().startswith('#define MAX_N_CLASS'): + in_includes = False + # Capture the entire #if block or single #define + if line.strip().startswith('#if defined(__clang__)'): + # Capture the entire #if...#endif block + macro_block = [] + macro_block.append(line) + i += 1 + while i < len(lines) and not lines[i].strip().startswith('#endif'): + macro_block.append(lines[i]) + i += 1 + if i < len(lines): + macro_block.append(lines[i]) # Include #endif + i += 1 + defines_to_move.append('\n'.join(macro_block)) + else: + # Single #define line + defines_to_move.append(line) + i += 1 + else: + in_includes = False + code_lines.append(line) + i += 1 + + # Add static keyword to function declarations + import re + processed_lines = [] + for line in code_lines: + # Detect function declarations/definitions (return_type function_name(...)) + # Match lines that look like function declarations but don't already have static + if line and not line.strip().startswith('//') and not line.strip().startswith('/*'): + # Check if it's a function declaration/definition + # Pattern: type name(...) or type* name(...) or type name[...](...) etc. + func_pattern = r'^(\s*)((?:const\s+)?(?:unsigned\s+)?(?:struct\s+)?[\w_]+(?:\s*\*)*\s+)([\w_]+)\s*\(' + match = re.match(func_pattern, line) + if match and 'static' not in line: + indent = match.group(1) + return_type = match.group(2) + # Add static keyword + line = f'{indent}static {return_type}{line[len(indent)+len(return_type):]}' + processed_lines.append(line) + code_lines = processed_lines + + # Wrap code in class declaration + includes_str = '\n'.join(include_lines) + code_str = '\n'.join(code_lines) + + wrapped_content = f'#pragma once\n\n{includes_str}\n\nclass {model_name} {{\npublic:\n{code_str}\n}}; // class {model_name}\n' with open(header_path, 'w') as f: - f.write(namespaced_content) + f.write(wrapped_content) - print(f" Wrapped header.h in namespace '{model_name}'") + print(f" Wrapped header.h in class '{model_name}' with #pragma once") except Exception as e: print(f" Warning: Failed to wrap header.h: {e}") + # Add defines to main.cpp (moved from header.h) + if defines_to_move and os.path.exists(main_path): + try: + with open(main_path, 'r') as f: + content = f.read() + + # Insert defines after includes (look for where code starts - typically after blank line after includes) + defines_str = '\n'.join(defines_to_move) + + # Find the first non-include, non-blank line to insert before + lines = content.split('\n') + insert_pos = 0 + for i, line in enumerate(lines): + if line.strip() and not line.strip().startswith('#include'): + insert_pos = i + break + + # Insert defines at the position + lines.insert(insert_pos, defines_str) + lines.insert(insert_pos + 1, '') # Add blank line after defines + + content = '\n'.join(lines) + with open(main_path, 'w') as f: + f.write(content) + print(f" Moved {len(defines_to_move)} macro definition(s) from header.h to main.cpp") + except Exception as e: + print(f" Warning: Failed to add defines to main.cpp: {e}") + # Add feature names to header and implementation if feature_names and os.path.exists(header_path) and os.path.exists(main_path): try: - # Append to header.h (inside namespace) + # Append to header.h (inside class) with open(header_path, 'r') as f: content = f.read() - # Insert before closing namespace - insertion = f'\n// Feature names\n#define NUM_FEATURES {len(feature_names)}\nextern const char* feature_names[NUM_FEATURES];\n' - content = content.replace(f'}} // namespace {model_name}\n', f'{insertion}\n}} // namespace {model_name}\n') + # Insert before closing class + insertion = f'\n // Feature names\n static constexpr int NUM_FEATURES = {len(feature_names)};\n static const char* feature_names[NUM_FEATURES];\n' + content = content.replace(f'}}; // class {model_name}\n', f'{insertion}}}; // class {model_name}\n') with open(header_path, 'w') as f: f.write(content) - # Append to main.cpp (inside namespace) + # Append to main.cpp (at the end of the file, outside any class) with open(main_path, 'r') as f: content = f.read() - # Insert before closing namespace - feature_array = f'\n// Feature names array\nconst char* feature_names[NUM_FEATURES] = {{\n' + # Append feature array definition at the end of the file + feature_array = f'\n// Feature names array\nconst char* {model_name}::feature_names[{model_name}::NUM_FEATURES] = {{\n' for i, name in enumerate(feature_names): comma = ',' if i < len(feature_names) - 1 else '' feature_array += f' "{name}"{comma}\n' feature_array += '};\n' - content = content.replace(f'}} // namespace {model_name}\n', f'{feature_array}\n}} // namespace {model_name}\n') + # Append to end of file + content = content.rstrip() + '\n' + feature_array with open(main_path, 'w') as f: f.write(content) From 017c4d5ea7e7fee67a438228d28778543aed9882 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 30 Oct 2025 14:33:29 +0000 Subject: [PATCH 019/225] merge w/ main, fix non-deterministic codepath --- .../restart_strategy/pdlp_restart_strategy.cu | 4 ++-- cpp/src/mip/diversity/diversity_manager.cu | 2 +- .../mip/local_search/rounding/constraint_prop.cu | 15 +++++++++------ cpp/src/mip/presolve/bounds_presolve.cu | 6 ++++-- cpp/src/mip/relaxed_lp/relaxed_lp.cu | 4 +++- cpp/tests/mip/CMakeLists.txt | 4 ---- cpp/tests/mip/presolve_test.cu | 12 +++++++++--- 7 files changed, 28 insertions(+), 19 deletions(-) diff --git a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu index 5f7facb8d5..e4714bcdc8 100644 --- a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu +++ b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu @@ -1719,8 +1719,8 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( // Perform the reduction // Convert raw pointer to thrust::device_ptr to write directly device side through reduce - // CHANGE - // use a deterministic reduce instead of thrust::reduce + // TODO + // use a guaranteed-deterministic reduce instead of thrust::reduce thrust::device_ptr thrust_hrsp(high_radius_squared_.data()); *thrust_hrsp = thrust::reduce(handle_ptr_->get_thrust_policy(), transformed_begin, diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 5e9ef77ebd..d4f21c1800 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -322,7 +322,7 @@ solution_t diversity_manager_t::run_solver() { raft::common::nvtx::range fun_scope("run_solver"); - diversity_config.fj_only_run = true; + diversity_config.fj_only_run = false; population.timer = timer; const f_t time_limit = timer.remaining_time(); diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 5bf9764a7f..d512b760cf 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -775,9 +775,6 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob { CUOPT_LOG_DEBUG("Running repair procedure"); - // CHANGE - timer = timer_t(std::numeric_limits::infinity()); - // select the first probing value i_t select = 0; multi_probe.set_updated_bounds(problem, select, handle_ptr); @@ -785,7 +782,11 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob repair_stats.repair_attempts++; f_t repair_start_time = timer.remaining_time(); i_t n_of_repairs_needed_for_feasible = 0; - i_t iter_limit = 100; + i_t iter_limit = std::numeric_limits::max(); + if (this->context.settings.deterministic) { + timer = timer_t(std::numeric_limits::infinity()); + iter_limit = 100; + } do { n_of_repairs_needed_for_feasible++; if (timer.check_time_limit() || iter_limit-- <= 0) { @@ -1082,11 +1083,13 @@ bool constraint_prop_t::find_integer( relaxed_lp_settings_t lp_settings; lp_settings.time_limit = lp_run_time_after_feasible; // CHANGE - lp_settings.time_limit = 600; lp_settings.tolerance = orig_sol.problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; - lp_settings.iteration_limit = 13000; + if (this->context.settings.deterministic) { + lp_settings.iteration_limit = 13000; + lp_settings.time_limit = std::numeric_limits::infinity(); + } run_lp_with_vars_fixed(*orig_sol.problem_ptr, orig_sol, orig_sol.problem_ptr->integer_indices, diff --git a/cpp/src/mip/presolve/bounds_presolve.cu b/cpp/src/mip/presolve/bounds_presolve.cu index df1d9223ef..167cf4e8f1 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cu +++ b/cpp/src/mip/presolve/bounds_presolve.cu @@ -182,8 +182,10 @@ termination_criterion_t bound_presolve_t::bound_update_loop(problem_t< termination_criterion_t criteria = termination_criterion_t::ITERATION_LIMIT; // CHANGE - timer = timer_t(std::numeric_limits::infinity()); - settings.iteration_limit = std::min(settings.iteration_limit, 50); + if (context.settings.deterministic) { + timer = timer_t(std::numeric_limits::infinity()); + settings.iteration_limit = std::min(settings.iteration_limit, 50); + } i_t iter; upd.init_changed_constraints(pb.handle_ptr); diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index 1f27ede021..b492f749f9 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -132,7 +132,9 @@ optimization_problem_solution_t get_relaxed_lp_solution( auto elapsed_ms = std::chrono::duration_cast(function_end_time - function_start_time) .count(); - CUOPT_LOG_DEBUG("get_relaxed_lp_solution took %lld ms", elapsed_ms); + CUOPT_LOG_DEBUG("get_relaxed_lp_solution took %lld ms for %d iterations", + elapsed_ms, + solver_response.get_additional_termination_information().number_of_steps_taken); return solver_response; } diff --git a/cpp/tests/mip/CMakeLists.txt b/cpp/tests/mip/CMakeLists.txt index d5556d4f75..0452ae3ef8 100644 --- a/cpp/tests/mip/CMakeLists.txt +++ b/cpp/tests/mip/CMakeLists.txt @@ -53,10 +53,6 @@ ConfigureTest(FEASIBILITY_JUMP_TEST ConfigureTest(MIP_TERMINATION_STATUS_TEST ${CMAKE_CURRENT_SOURCE_DIR}/termination_test.cu ) - -ConfigureTest(PRESOLVE_TEST - ${CMAKE_CURRENT_SOURCE_DIR}/presolve_test.cu -) ConfigureTest(LOCAL_SEARCH_TEST ${CMAKE_CURRENT_SOURCE_DIR}/local_search_test.cu ) diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index d7d861de8d..e08f283cb0 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -20,18 +20,24 @@ #include "mip_utils.cuh" #include +#include #include #include +#include #include #include +#include #include +#include #include +#include #include -#include -#include #include +#include #include -#include + +#include +#include #include From 8cb04ec42391b1f919ec553cdc4e35c3672fe842 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 30 Oct 2025 15:05:17 +0000 Subject: [PATCH 020/225] data collection --- PREDICTOR_DATA_COLLECTION.md | 292 ++++++++++++++++++ .../feasibility_pump/feasibility_pump.cu | 82 +++++ .../local_search/rounding/constraint_prop.cu | 51 ++- cpp/src/mip/relaxed_lp/relaxed_lp.cu | 39 +++ 4 files changed, 462 insertions(+), 2 deletions(-) create mode 100644 PREDICTOR_DATA_COLLECTION.md diff --git a/PREDICTOR_DATA_COLLECTION.md b/PREDICTOR_DATA_COLLECTION.md new file mode 100644 index 0000000000..658ac4d352 --- /dev/null +++ b/PREDICTOR_DATA_COLLECTION.md @@ -0,0 +1,292 @@ +# Predictor Data Collection: Feature Logging + +This document describes the instrumentation added to collect training data for work unit predictors. + +## Overview + +Three algorithms have been instrumented to log features before execution and performance metrics after completion: + +1. **Feasibility Pump (FP)** - Main heuristic for finding feasible solutions +2. **PDLP** - First-order LP solver used for polytope projection +3. **Constraint Propagation (CP)** - Variable rounding with bounds propagation + +Note: **Feasibility Jump (FJ)** already has a working predictor and doesn't need additional instrumentation. + +## Log Format + +All logs use `CUOPT_LOG_INFO` level with structured prefixes for easy parsing: + +### Feasibility Pump (FP) + +**Features logged before execution:** +``` +FP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d n_binary_vars=%d +FP_FEATURES: nnz=%lu sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f +FP_FEATURES: initial_feasibility=%d initial_excess=%.6f initial_objective=%.6f +FP_FEATURES: initial_ratio_of_integers=%.6f initial_n_integers=%d +FP_FEATURES: alpha=%.6f check_distance_cycle=%d cycle_detection_length=%d +FP_FEATURES: has_cutting_plane=%d time_budget=%.6f +``` + +**Results logged after execution:** +``` +FP_RESULT: iterations=%d time_taken=%.6f termination= +``` + +**Termination reasons:** +- `TIME_LIMIT` - Time budget exhausted +- `TIME_LIMIT_AFTER_ROUND` - Time limit during rounding phase +- `FEASIBLE_LP_PROJECTION` - Found feasible via LP projection +- `FEASIBLE_LP_VERIFIED` - Found feasible via high-precision LP +- `FEASIBLE_AFTER_ROUND` - Found feasible after rounding +- `FEASIBLE_DISTANCE_CYCLE` - Found feasible during distance cycle handling +- `INFEASIBLE_DISTANCE_CYCLE` - Distance cycle detected, no feasible found +- `ASSIGNMENT_CYCLE` - Assignment cycle detected + +**Location:** `cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu::run_single_fp_descent` + +--- + +### PDLP (LP Solver) + +**Features logged before execution:** +``` +PDLP_FEATURES: n_variables=%d n_constraints=%d nnz=%lu +PDLP_FEATURES: sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f +PDLP_FEATURES: has_warm_start=%d time_limit=%.6f iteration_limit=%d +PDLP_FEATURES: tolerance=%.10f check_infeasibility=%d return_first_feasible=%d +``` + +**Results logged after execution:** +``` +PDLP_RESULT: iterations=%d time_ms=%lld termination=%d +PDLP_RESULT: primal_objective=%.10f dual_objective=%.10f gap=%.10f +PDLP_RESULT: l2_primal_residual=%.10f l2_dual_residual=%.10f +``` + +**Termination status codes:** +- `0` - NoTermination +- `1` - NumericalError +- `2` - Optimal +- `3` - PrimalInfeasible +- `4` - DualInfeasible +- `5` - IterationLimit +- `6` - TimeLimit +- `7` - PrimalFeasible +- `8` - ConcurrentLimit + +**Location:** `cpp/src/mip/relaxed_lp/relaxed_lp.cu::get_relaxed_lp_solution` + +--- + +### Constraint Propagation (CP) + +**Features logged before execution:** +``` +CP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d +CP_FEATURES: nnz=%lu sparsity=%.6f +CP_FEATURES: n_unset_vars=%d initial_excess=%.6f time_budget=%.6f +CP_FEATURES: round_all_vars=%d lp_run_time_after_feasible=%.6f +``` + +**Results logged after execution:** +``` +CP_RESULT: time_ms=%lld termination= iterations=%d +``` + +**Termination status:** +- `BRUTE_FORCE_SUCCESS` - Succeeded via simple rounding +- `SUCCESS` - Found feasible solution +- `FAILED` - Did not find feasible solution + +**Location:** `cpp/src/mip/local_search/rounding/constraint_prop.cu::apply_round` + +--- + +## Data Collection Workflow + +### 1. Run Solver with Logging Enabled + +Ensure the log level is set to `INFO` or higher to capture the feature logs: + +```bash +export CUOPT_LOG_LEVEL=INFO +# or +export CUOPT_LOG_LEVEL=DEBUG +``` + +### 2. Parse Logs + +Use the following pattern to extract training data: + +```python +import re +import json + +def parse_fp_features(log_lines): + """Parse FP features from log lines""" + features = {} + + # Match feature lines + for line in log_lines: + if "FP_FEATURES:" in line: + # Extract key=value pairs + matches = re.findall(r'(\w+)=([\d.e+-]+)', line) + features.update({k: float(v) if '.' in v else int(v) + for k, v in matches}) + + return features + +def parse_fp_result(log_lines): + """Parse FP results from log lines""" + for line in log_lines: + if "FP_RESULT:" in line: + match = re.search(r'iterations=(\d+) time_taken=([\d.]+) termination=(\w+)', line) + if match: + return { + 'iterations': int(match.group(1)), + 'time_taken': float(match.group(2)), + 'termination': match.group(3) + } + return None + +# Similar functions for PDLP and CP... +``` + +### 3. Create Training Dataset + +Combine features and results into training examples: + +```python +training_data = [] + +for problem_run in log_files: + features = parse_fp_features(problem_run) + result = parse_fp_result(problem_run) + + if features and result: + training_data.append({ + 'features': features, + 'target': result['iterations'], # or result['time_taken'] + 'metadata': { + 'termination': result['termination'] + } + }) +``` + +### 4. Train Predictor Model + +Use the same approach as the existing FJ predictor: + +```python +# Example using XGBoost (like FJ predictor) +import xgboost as xgb + +# Prepare data +X = [sample['features'] for sample in training_data] +y = [sample['target'] for sample in training_data] + +# Train model +model = xgb.XGBRegressor( + n_estimators=100, + max_depth=6, + learning_rate=0.1 +) +model.fit(X, y) + +# Save model for C++ code generation +# (Similar to cpp/src/utilities/models/fj_predictor/) +``` + +--- + +## Feature Descriptions + +### Problem Structure Features + +- **n_variables** - Total number of decision variables +- **n_constraints** - Total number of constraints +- **n_integer_vars** - Number of integer/binary variables +- **n_binary_vars** - Number of binary (0/1) variables +- **nnz** - Non-zero coefficients in constraint matrix +- **sparsity** - Matrix sparsity: nnz / (n_constraints × n_variables) +- **nnz_stddev** - Standard deviation of non-zeros per constraint row +- **unbalancedness** - Load balancing metric for constraint matrix + +### Solution State Features (FP) + +- **initial_feasibility** - Whether starting solution is feasible (0/1) +- **initial_excess** - Sum of constraint violations +- **initial_objective** - Objective value of initial solution +- **initial_ratio_of_integers** - Fraction of integer vars already integral +- **initial_n_integers** - Count of integer vars at integral values + +### Algorithm Configuration Features (FP) + +- **alpha** - Weight between original objective and distance objective +- **check_distance_cycle** - Whether distance-based cycle detection is enabled +- **cycle_detection_length** - Number of recent solutions tracked +- **has_cutting_plane** - Whether objective cutting plane was added +- **time_budget** - Allocated time in seconds + +### Solver Configuration Features (PDLP) + +- **has_warm_start** - Whether initial primal/dual solution provided +- **time_limit** - Time budget in seconds +- **iteration_limit** - Maximum iterations allowed +- **tolerance** - Optimality tolerance +- **check_infeasibility** - Whether to detect infeasibility +- **return_first_feasible** - Whether to return on first primal feasible + +### Rounding Configuration Features (CP) + +- **n_unset_vars** - Integer variables not yet set +- **round_all_vars** - Whether to round all variables or selective +- **lp_run_time_after_feasible** - Time budget for post-feasibility LP + +--- + +## Integration with Existing Predictor + +The FJ predictor already exists at `cpp/src/utilities/models/fj_predictor/`. The same workflow can be used: + +1. Collect training data as described above +2. Train XGBoost model +3. Export to C++ using TreeLite (as done for FJ) +4. Integrate into solver with work unit → iteration conversion + +Example from FJ predictor: +```cpp +// cpp/src/mip/feasibility_jump/feasibility_jump.cu:1283-1291 +if (settings.work_unit_limit != std::numeric_limits::infinity()) { + std::map features_map = get_feature_vector(0); + float iter_prediction = std::max( + (f_t)0.0, + (f_t)ceil(context.work_unit_predictors.fj_predictor.predict_scalar(features_map)) + ); + CUOPT_LOG_DEBUG("FJ determ: Estimated number of iterations for %f WU: %f", + settings.work_unit_limit, + iter_prediction); + settings.iteration_limit = std::min(settings.iteration_limit, (i_t)iter_prediction); +} +``` + +--- + +## Next Steps + +1. **Collect Data**: Run solver on diverse problem sets with logging enabled +2. **Analyze**: Examine feature importance and correlation with execution time +3. **Train Models**: Build iteration predictors for FP, PDLP, and CP +4. **Validate**: Test predictors maintain solution quality while achieving determinism +5. **Deploy**: Integrate trained models into solver (similar to FJ predictor) +6. **Hierarchical Allocation**: Implement work unit budget allocation across nested algorithms + +--- + +## Notes + +- Line Segment Search was excluded as it can be predicted from FJ predictor (it runs FJ internally) +- CP iteration tracking needs enhancement (currently logs 0 iterations) +- Consider adding more dynamic features during execution for better predictions +- The termination reasons can help understand when algorithms succeed/fail diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index 4f0d3cff76..dfd153476d 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -500,6 +500,51 @@ template bool feasibility_pump_t::run_single_fp_descent(solution_t& solution) { raft::common::nvtx::range fun_scope("run_single_fp_descent"); + + // === FP PREDICTOR FEATURES - START === + f_t start_time = timer.remaining_time(); + i_t fp_iterations = 0; + + // Problem structure features + CUOPT_LOG_INFO("FP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d n_binary_vars=%d", + solution.problem_ptr->n_variables, + solution.problem_ptr->n_constraints, + solution.problem_ptr->n_integer_vars, + solution.problem_ptr->n_binary_vars); + + CUOPT_LOG_INFO("FP_FEATURES: nnz=%lu sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f", + solution.problem_ptr->coefficients.size(), + solution.problem_ptr->sparsity, + solution.problem_ptr->nnz_stddev, + solution.problem_ptr->unbalancedness); + + // Initial solution features + solution.compute_feasibility(); + i_t initial_n_integers = solution.compute_number_of_integers(); + f_t initial_ratio_of_integers = solution.problem_ptr->n_integer_vars > 0 + ? (f_t)initial_n_integers / solution.problem_ptr->n_integer_vars + : 0.0; + + CUOPT_LOG_INFO("FP_FEATURES: initial_feasibility=%d initial_excess=%.6f initial_objective=%.6f", + solution.get_feasible(), + solution.get_total_excess(), + solution.get_objective()); + + CUOPT_LOG_INFO("FP_FEATURES: initial_ratio_of_integers=%.6f initial_n_integers=%d", + initial_ratio_of_integers, + initial_n_integers); + + // Algorithm configuration features + CUOPT_LOG_INFO("FP_FEATURES: alpha=%.6f check_distance_cycle=%d cycle_detection_length=%d", + config.alpha, + config.check_distance_cycle, + cycle_queue.cycle_detection_length); + + CUOPT_LOG_INFO("FP_FEATURES: has_cutting_plane=%d time_budget=%.6f", + solution.problem_ptr->cutting_plane_added, + timer.remaining_time()); + // === FP PREDICTOR FEATURES - END === + // start by doing nearest rounding solution.round_nearest(); raft::copy(last_rounding.data(), @@ -509,8 +554,13 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s // CUOPT_LOG_DEBUG("FP: starting FP descent, sol hash 0x%x", solution.get_hash()); while (true) { + fp_iterations++; if (timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); + f_t time_taken = start_time - timer.remaining_time(); + CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=TIME_LIMIT", + fp_iterations, + time_taken); round(solution); return false; } @@ -534,12 +584,22 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (is_feasible) { bool res = solution.compute_feasibility(); cuopt_assert(res, "Feasibility issue"); + f_t time_taken = start_time - timer.remaining_time(); + CUOPT_LOG_INFO( + "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_DISTANCE_CYCLE", + fp_iterations, + time_taken); return true; } cuopt::default_logger().flush(); f_t remaining_time_end_fp = timer.remaining_time(); total_fp_time_until_cycle = fp_fj_cycle_time_begin - remaining_time_end_fp; // CUOPT_LOG_DEBUG("total_fp_time_until_cycle: %f", total_fp_time_until_cycle); + f_t time_taken = start_time - timer.remaining_time(); + CUOPT_LOG_INFO( + "FP_RESULT: iterations=%d time_taken=%.6f termination=INFEASIBLE_DISTANCE_CYCLE", + fp_iterations, + time_taken); return false; } } @@ -547,6 +607,11 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (n_integers == solution.problem_ptr->n_integer_vars) { if (is_feasible) { CUOPT_LOG_DEBUG("Feasible solution found after LP with relative tolerance"); + f_t time_taken = start_time - timer.remaining_time(); + CUOPT_LOG_INFO( + "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_LP_PROJECTION", + fp_iterations, + time_taken); return true; } // if the solution is almost on polytope @@ -567,6 +632,11 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s n_integers = solution.compute_number_of_integers(); if (is_feasible && n_integers == solution.problem_ptr->n_integer_vars) { CUOPT_LOG_DEBUG("Feasible solution verified with LP!"); + f_t time_taken = start_time - timer.remaining_time(); + CUOPT_LOG_INFO( + "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_LP_VERIFIED", + fp_iterations, + time_taken); return true; } } @@ -581,11 +651,19 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s } if (timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); + f_t time_taken = start_time - timer.remaining_time(); + CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=TIME_LIMIT_AFTER_ROUND", + fp_iterations, + time_taken); return false; } if (is_feasible) { bool res = solution.compute_feasibility(); cuopt_assert(res, "Feasibility issue"); + f_t time_taken = start_time - timer.remaining_time(); + CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_AFTER_ROUND", + fp_iterations, + time_taken); return true; } // do the cycle check if alpha diff is small enough @@ -603,6 +681,10 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s remaining_time_end_fp, fp_fj_cycle_time_begin, total_fp_time_until_cycle); + f_t time_taken = start_time - timer.remaining_time(); + CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=ASSIGNMENT_CYCLE", + fp_iterations, + time_taken); return false; } cycle_queue.n_iterations_without_cycle++; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index d512b760cf..26aa923ebb 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -1109,11 +1109,44 @@ bool constraint_prop_t::apply_round( std::optional>> probing_config) { raft::common::nvtx::range fun_scope("constraint prop round"); + + // === CONSTRAINT PROP PREDICTOR FEATURES - START === + auto cp_start_time = std::chrono::high_resolution_clock::now(); + + CUOPT_LOG_INFO("CP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d", + sol.problem_ptr->n_variables, + sol.problem_ptr->n_constraints, + sol.problem_ptr->n_integer_vars); + + CUOPT_LOG_INFO("CP_FEATURES: nnz=%lu sparsity=%.6f", + sol.problem_ptr->coefficients.size(), + sol.problem_ptr->sparsity); + + sol.compute_feasibility(); + i_t n_unset_integers = sol.problem_ptr->n_integer_vars - sol.compute_number_of_integers(); + + CUOPT_LOG_INFO("CP_FEATURES: n_unset_vars=%d initial_excess=%.6f time_budget=%.6f", + n_unset_integers, + sol.get_total_excess(), + max_time_for_bounds_prop); + + CUOPT_LOG_INFO("CP_FEATURES: round_all_vars=%d lp_run_time_after_feasible=%.6f", + round_all_vars, + lp_run_time_after_feasible); + // === CONSTRAINT PROP PREDICTOR FEATURES - END === + max_timer = timer_t{max_time_for_bounds_prop}; if (this->context.settings.deterministic) { max_timer = timer_t(std::numeric_limits::infinity()); } - if (check_brute_force_rounding(sol)) { return true; } + if (check_brute_force_rounding(sol)) { + auto cp_end_time = std::chrono::high_resolution_clock::now(); + auto cp_elapsed_ms = + std::chrono::duration_cast(cp_end_time - cp_start_time).count(); + CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=BRUTE_FORCE_SUCCESS iterations=0", + cp_elapsed_ms); + return true; + } recovery_mode = false; rounding_ii = false; n_iter_in_recovery = 0; @@ -1138,11 +1171,25 @@ bool constraint_prop_t::apply_round( // repair_stats.total_time_spent_on_repair, // repair_stats.total_time_spent_bounds_prop_after_repair, // repair_stats.total_time_spent_on_bounds_prop); + // === CONSTRAINT PROP PREDICTOR RESULTS - START === + auto cp_end_time = std::chrono::high_resolution_clock::now(); + auto cp_elapsed_ms = + std::chrono::duration_cast(cp_end_time - cp_start_time).count(); + // === CONSTRAINT PROP PREDICTOR RESULTS - END === + if (!sol_found) { sol.compute_feasibility(); + CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=FAILED iterations=%d", + cp_elapsed_ms, + 0); // TODO: track actual iterations return false; } - return sol.compute_feasibility(); + bool result = sol.compute_feasibility(); + CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=%s iterations=%d", + cp_elapsed_ms, + result ? "SUCCESS" : "FAILED", + 0); // TODO: track actual iterations + return result; } template diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index b492f749f9..d8afa0da6d 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -54,6 +54,28 @@ optimization_problem_solution_t get_relaxed_lp_solution( raft::common::nvtx::range fun_scope("get_relaxed_lp_solution"); auto function_start_time = std::chrono::high_resolution_clock::now(); + // === PDLP PREDICTOR FEATURES - START === + CUOPT_LOG_INFO("PDLP_FEATURES: n_variables=%d n_constraints=%d nnz=%lu", + op_problem.n_variables, + op_problem.n_constraints, + op_problem.coefficients.size()); + + CUOPT_LOG_INFO("PDLP_FEATURES: sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f", + op_problem.sparsity, + op_problem.nnz_stddev, + op_problem.unbalancedness); + + CUOPT_LOG_INFO("PDLP_FEATURES: has_warm_start=%d time_limit=%.6f iteration_limit=%d", + settings.has_initial_primal, + settings.time_limit, + settings.iteration_limit); + + CUOPT_LOG_INFO("PDLP_FEATURES: tolerance=%.10f check_infeasibility=%d return_first_feasible=%d", + settings.tolerance, + settings.check_infeasibility, + settings.return_first_feasible); + // === PDLP PREDICTOR FEATURES - END === + pdlp_solver_settings_t pdlp_settings{}; pdlp_settings.detect_infeasibility = settings.check_infeasibility; pdlp_settings.set_optimality_tolerance(settings.tolerance); @@ -136,6 +158,23 @@ optimization_problem_solution_t get_relaxed_lp_solution( elapsed_ms, solver_response.get_additional_termination_information().number_of_steps_taken); + // === PDLP PREDICTOR RESULTS - START === + auto term_info = solver_response.get_additional_termination_information(); + CUOPT_LOG_INFO("PDLP_RESULT: iterations=%d time_ms=%lld termination=%d", + term_info.number_of_steps_taken, + elapsed_ms, + (int)solver_response.get_termination_status()); + + CUOPT_LOG_INFO("PDLP_RESULT: primal_objective=%.10f dual_objective=%.10f gap=%.10f", + term_info.primal_objective, + term_info.dual_objective, + term_info.gap); + + CUOPT_LOG_INFO("PDLP_RESULT: l2_primal_residual=%.10f l2_dual_residual=%.10f", + term_info.l2_primal_residual, + term_info.l2_dual_residual); + // === PDLP PREDICTOR RESULTS - END === + return solver_response; } From f5f27268f5b6dd87a49177c15a93a6d00e1fd756 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 31 Oct 2025 12:48:55 +0000 Subject: [PATCH 021/225] script changes --- PREDICTOR_DATA_COLLECTION.md | 268 ++++++++++++----- scripts/README_PREDICTOR_WORKFLOW.md | 214 ++++++++++++++ scripts/determinism_logs_parse.py | 423 ++++++++++++++++++++++++--- 3 files changed, 792 insertions(+), 113 deletions(-) create mode 100644 scripts/README_PREDICTOR_WORKFLOW.md diff --git a/PREDICTOR_DATA_COLLECTION.md b/PREDICTOR_DATA_COLLECTION.md index 658ac4d352..cc81eb9c33 100644 --- a/PREDICTOR_DATA_COLLECTION.md +++ b/PREDICTOR_DATA_COLLECTION.md @@ -117,87 +117,97 @@ export CUOPT_LOG_LEVEL=DEBUG ### 2. Parse Logs -Use the following pattern to extract training data: - -```python -import re -import json - -def parse_fp_features(log_lines): - """Parse FP features from log lines""" - features = {} - - # Match feature lines - for line in log_lines: - if "FP_FEATURES:" in line: - # Extract key=value pairs - matches = re.findall(r'(\w+)=([\d.e+-]+)', line) - features.update({k: float(v) if '.' in v else int(v) - for k, v in matches}) - - return features - -def parse_fp_result(log_lines): - """Parse FP results from log lines""" - for line in log_lines: - if "FP_RESULT:" in line: - match = re.search(r'iterations=(\d+) time_taken=([\d.]+) termination=(\w+)', line) - if match: - return { - 'iterations': int(match.group(1)), - 'time_taken': float(match.group(2)), - 'termination': match.group(3) - } - return None - -# Similar functions for PDLP and CP... -``` +Use the provided `determinism_logs_parse.py` script to automatically extract training data: -### 3. Create Training Dataset +```bash +# Parse FP (Feasibility Pump) logs +python scripts/determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl -Combine features and results into training examples: +# Parse PDLP (LP Solver) logs +python scripts/determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.pkl -```python -training_data = [] +# Parse CP (Constraint Propagation) logs +python scripts/determinism_logs_parse.py logs/ --algorithm CP -o cp_data.pkl -for problem_run in log_files: - features = parse_fp_features(problem_run) - result = parse_fp_result(problem_run) +# Parse FJ (Feasibility Jump) legacy logs +python scripts/determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.pkl +``` - if features and result: - training_data.append({ - 'features': features, - 'target': result['iterations'], # or result['time_taken'] - 'metadata': { - 'termination': result['termination'] - } - }) +The script will: +- Find all `.log` files in the specified directory +- Extract all `_FEATURES` and `_RESULT` log lines using grep +- Pair features with results in order using line numbers +- Export to pickle format compatible with `train_regressor.py` + +**Performance optimizations for large logs:** +- **Exact pattern matching**: Grep uses `FP_FEATURES:` / `FP_RESULT:` (with colon) to match ONLY predictor lines + - Example: Log with 100K lines and 10K "FP" references → grep extracts only ~200 predictor lines + - Filters before Python processing, so noisy logs don't slow down parsing +- Single grep call per algorithm (combines features + results) +- Uses grep's `-n` flag for line-number-based pairing +- Minimal Python string processing (simple split operations) +- Single-pass parsing with efficient dictionary accumulation +- Handles millions of log lines efficiently + +**Script output (with progress indicators):** +``` +Scanning logs/ for .log files... +Found 42 log files + +Parsing FP (Feasibility Pump) logs... + Running grep on 42 files... + Processing 3046 matching lines... + Progress: 10000/3046 lines, 42 files + Progress: 20000/3046 lines, 42 files + Processed 3046 lines from 42 files + Pairing features with results... + Pairing: 10/42 files, 362 entries found + Pairing: 20/42 files, 724 entries found + Pairing: 30/42 files, 1086 entries found + Pairing: 40/42 files, 1448 entries found + Found 1523 complete entries from 42 files + + Total entries: 1523 + Unique files: 42 + Avg entries per file: 36.26 + Iterations (target): min=1, max=847, avg=142.35 + +Saving 1523 entries to fp_data.pkl... + +====================================================================== +✓ Success! Saved 1523 entries to fp_data.pkl + File size: 2.34 MB +====================================================================== ``` -### 4. Train Predictor Model +**Progress updates:** +- Line processing: Every 10,000 lines +- Pairing: Every 10 files +- Uses carriage return (`\r`) for in-place updates -Use the same approach as the existing FJ predictor: +### 3. Train Predictor Model -```python -# Example using XGBoost (like FJ predictor) -import xgboost as xgb +Use the `train_regressor.py` script with the parsed data: -# Prepare data -X = [sample['features'] for sample in training_data] -y = [sample['target'] for sample in training_data] +```bash +# Train XGBoost model for FP +python scripts/train_regressor.py fp_data.pkl --regressor xgboost --seed 42 -# Train model -model = xgb.XGBRegressor( - n_estimators=100, - max_depth=6, - learning_rate=0.1 -) -model.fit(X, y) +# Train LightGBM model for PDLP +python scripts/train_regressor.py pdlp_data.pkl --regressor lightgbm --seed 42 -# Save model for C++ code generation -# (Similar to cpp/src/utilities/models/fj_predictor/) +# View available features before training +python scripts/train_regressor.py cp_data.pkl --regressor xgboost --list-features ``` +The training script will: +- Load the pickle file +- Split data by files (train/test) +- Train the specified model +- Evaluate performance (R², RMSE, MAE) +- Export to C++ code using TL2cgen (for XGBoost/LightGBM) +- Save model and metadata + --- ## Feature Descriptions @@ -284,9 +294,133 @@ if (settings.work_unit_limit != std::numeric_limits::infinity()) { --- +## Complete Workflow Example + +Here's a complete end-to-end example: + +### Step 1: Run Solver and Collect Logs + +```bash +# Set log level to capture feature logs +export CUOPT_LOG_LEVEL=INFO + +# Run your solver on test problems +./my_solver problem1.mps > logs/problem1.log 2>&1 +./my_solver problem2.mps > logs/problem2.log 2>&1 +# ... run on many problems +``` + +### Step 2: Parse Logs for Each Algorithm + +```bash +# Parse FP logs +python scripts/determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl + +# Parse PDLP logs +python scripts/determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.pkl + +# Parse CP logs +python scripts/determinism_logs_parse.py logs/ --algorithm CP -o cp_data.pkl +``` + +### Step 3: Inspect Features + +```bash +# See what features are available for FP +python scripts/train_regressor.py fp_data.pkl --regressor xgboost --list-features +``` + +Output: +``` +====================================================================== +Available features in dataset (28 total): +====================================================================== + 1. alpha + 2. check_distance_cycle + 3. cycle_detection_length + 4. has_cutting_plane + 5. initial_excess + 6. initial_feasibility + 7. initial_n_integers + 8. initial_objective + 9. initial_ratio_of_integers + 10. n_binary_vars + 11. n_constraints + 12. n_integer_vars + 13. n_variables + 14. nnz + 15. nnz_stddev + 16. sparsity + 17. time_budget + 18. unbalancedness + ... +``` + +### Step 4: Train Models + +```bash +# Train FP predictor with XGBoost +python scripts/train_regressor.py fp_data.pkl \ + --regressor xgboost \ + --seed 42 \ + --early-stopping 20 \ + --treelite-compile 8 + +# Train PDLP predictor with LightGBM +python scripts/train_regressor.py pdlp_data.pkl \ + --regressor lightgbm \ + --seed 42 \ + --early-stopping 20 \ + --treelite-compile 8 +``` + +### Step 5: Review Results + +The training script will output: +- Cross-validation scores +- Train/test metrics (R², RMSE, MAE) +- Feature importance ranking +- Sample predictions +- Worst predictions with feature values + +Example output: +``` +Training complete! + +Test Set Metrics: + MSE: 1234.5678 + RMSE: 35.14 + MAE: 22.67 + R²: 0.8542 + +Feature Importance: + 1. n_variables : 0.245123 + 2. n_constraints : 0.187456 + 3. initial_ratio_of_integers : 0.156234 + 4. sparsity : 0.098765 + ... + +C source code generated to: ./models/fp_data_c_code/ + Contains optimized model source code (branch-annotated, quantized) +``` + +### Step 6: Integrate into Solver + +The generated C++ code will be in `./models/_data_c_code/`: +- `header.h` - Class declaration with predict functions +- `main.cpp` - Implementation +- `quantize.cpp` - Quantization helpers (if enabled) + +Copy these files to `cpp/src/utilities/models/_predictor/` and integrate similar to the existing FJ predictor. + +--- + ## Notes - Line Segment Search was excluded as it can be predicted from FJ predictor (it runs FJ internally) - CP iteration tracking needs enhancement (currently logs 0 iterations) - Consider adding more dynamic features during execution for better predictions - The termination reasons can help understand when algorithms succeed/fail +- Use `--stratify-split` when training to ensure balanced train/test distribution +- The `--early-stopping` parameter helps prevent overfitting on tree models +- TL2cgen compilation with `--treelite-compile` generates optimized C++ code with branch annotation and quantization enabled by default diff --git a/scripts/README_PREDICTOR_WORKFLOW.md b/scripts/README_PREDICTOR_WORKFLOW.md new file mode 100644 index 0000000000..7cc1fbc8a3 --- /dev/null +++ b/scripts/README_PREDICTOR_WORKFLOW.md @@ -0,0 +1,214 @@ +# Predictor Training Workflow + +Quick reference for training iteration predictors from solver logs. + +## Prerequisites + +```bash +pip install pandas scikit-learn xgboost lightgbm treelite tl2cgen joblib +``` + +## Workflow + +### 1. Collect Logs + +Run solver with `INFO` log level to capture feature logs: + +```bash +export CUOPT_LOG_LEVEL=INFO +./solver problem.mps > logs/problem.log 2>&1 +``` + +### 2. Parse Logs + +Extract training data for specific algorithm: + +```bash +# Parse Feasibility Pump logs +python scripts/determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl + +# Parse PDLP (LP solver) logs +python scripts/determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.pkl + +# Parse Constraint Propagation logs +python scripts/determinism_logs_parse.py logs/ --algorithm CP -o cp_data.pkl + +# Parse Feasibility Jump legacy logs +python scripts/determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.pkl +``` + +**The parser shows real-time progress:** +- File scanning progress +- Grep execution on N files +- Line processing progress (updates every 10,000 lines) +- Pairing progress (updates every 10 files) +- Final statistics and file size + +**IMPORTANT - Log Filtering:** +The parser uses grep with EXACT pattern matching to be highly efficient: +- Pattern: `FP_FEATURES:` and `FP_RESULT:` (with colon suffix) +- Only matches predictor log lines, ignores all other FP-related logs +- Example: A log with 100,000 lines might have 10,000 lines with "FP" but only 200 predictor lines +- Grep filters these down BEFORE Python processing, making it extremely fast even with noisy logs + +### 3. Inspect Features (Optional) + +See what features are available: + +```bash +python scripts/train_regressor.py fp_data.pkl --regressor xgboost --list-features +``` + +### 4. Train Model + +Train with XGBoost or LightGBM: + +```bash +# XGBoost with early stopping and C++ code generation +python scripts/train_regressor.py fp_data.pkl \ + --regressor xgboost \ + --seed 42 \ + --early-stopping 20 \ + --treelite-compile 8 + +# LightGBM with stratified split +python scripts/train_regressor.py pdlp_data.pkl \ + --regressor lightgbm \ + --seed 42 \ + --stratify-split \ + --early-stopping 20 \ + --treelite-compile 8 +``` + +### 5. Use Generated Code + +The trained model will be exported to C++ code in `./models/_c_code/`: + +- `header.h` - Class declaration +- `main.cpp` - Implementation +- `quantize.cpp` - Quantization helpers + +Copy to `cpp/src/utilities/models/_predictor/` and integrate into solver. + +## Log Format Reference + +### FP (Feasibility Pump) +``` +FP_FEATURES: n_variables=100 n_constraints=50 n_integer_vars=80 ... +FP_RESULT: iterations=142 time_taken=5.234 termination=FEASIBLE_LP_PROJECTION +``` + +### PDLP (LP Solver) +``` +PDLP_FEATURES: n_variables=100 n_constraints=50 nnz=450 ... +PDLP_RESULT: iterations=237 time_ms=1234 termination=2 +``` + +### CP (Constraint Propagation) +``` +CP_FEATURES: n_variables=100 n_constraints=50 n_unset_vars=25 ... +CP_RESULT: time_ms=567 termination=SUCCESS iterations=0 +``` + +## Common Options + +### Training Script + +- `--regressor {xgboost,lightgbm,linear,poly2,...}` - Model type +- `--seed N` - Random seed for reproducibility +- `--test-size 0.2` - Test set proportion (default 20%) +- `--stratify-split` - Balance train/test by target distribution +- `--early-stopping N` - Early stopping patience (prevents overfitting) +- `--treelite-compile N` - Generate C++ code with N threads +- `--list-features` - Show available features and exit +- `--tune` - Use tuned hyperparameters + +### Parsing Script + +- `--algorithm {FP,PDLP,CP,FJ}` - Which algorithm to parse +- `-o FILE` - Output pickle file (default: `_data.pkl`) +- `--verbose` - Show warnings and detailed output + +## Tips + +1. **Collect diverse problems**: Train on variety of problem types/sizes +2. **Check train/test split**: Use `--stratify-split` if targets are imbalanced +3. **Prevent overfitting**: Use `--early-stopping` with tree models +4. **Feature selection**: Edit `FEATURES_TO_EXCLUDE` in `train_regressor.py` +5. **Reproducibility**: Always set `--seed` for consistent results + +## Troubleshooting + +**No entries found for algorithm X** +- Check log level is set to INFO or DEBUG +- Verify solver is executing the algorithm +- Look for `X_FEATURES` and `X_RESULT` lines in logs + +**Poor model performance** +- Collect more training data +- Try different regressor types +- Use `--list-features` to identify important features +- Enable `--stratify-split` for balanced splits + +**C++ code generation fails** +- Install: `pip install treelite tl2cgen` +- Only works with XGBoost and LightGBM +- Check model trained successfully first + +## Example Output + +```bash +$ python scripts/determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl + +Scanning logs/ for .log files... +Found 42 log files + +Parsing FP (Feasibility Pump) logs... + Running grep on 42 files... + Processing 3046 matching lines... + Processed 3046 lines from 42 files + Pairing features with results... + Found 1523 complete entries from 42 files + + Total entries: 1523 + Unique files: 42 + Avg entries per file: 36.26 + Iterations (target): min=1, max=847, avg=142.35 + +Saving 1523 entries to fp_data.pkl... + +====================================================================== +✓ Success! Saved 1523 entries to fp_data.pkl + File size: 2.34 MB +====================================================================== + +$ python scripts/train_regressor.py fp_data.pkl --regressor xgboost --seed 42 + +Loading data from: fp_data.pkl +Loaded 1523 entries with 29 columns + +Data Split: + Total entries: 1523 + Train entries: 1218 (34 files) + Test entries: 305 (8 files) + +Training xgboost regressor... + Training complete! + +Test Set Metrics: + MSE: 1234.56 + RMSE: 35.14 + MAE: 22.67 + R²: 0.8542 + +Feature Importance: + 1. n_variables : 0.245123 + 2. n_constraints : 0.187456 + 3. initial_ratio_of_integers : 0.156234 + ... + +C source code generated to: ./models/fp_data_c_code/ + Contains optimized model source code (branch-annotated, quantized) + +Success! Saved 1523 entries to fp_data.pkl +``` diff --git a/scripts/determinism_logs_parse.py b/scripts/determinism_logs_parse.py index ff0ff854be..687f0c6516 100755 --- a/scripts/determinism_logs_parse.py +++ b/scripts/determinism_logs_parse.py @@ -16,10 +16,36 @@ # limitations under the License. """ -Parse log files containing key-value pairs and export to pickle format. +Parse log files containing algorithm feature logs and export to pickle format for training. + +Supports parsing of: +- FP (Feasibility Pump): FP_FEATURES and FP_RESULT logs +- PDLP (LP Solver): PDLP_FEATURES and PDLP_RESULT logs +- CP (Constraint Propagation): CP_FEATURES and CP_RESULT logs +- FJ (Feasibility Jump): Legacy FJ: format + +IMPORTANT - Grep Specificity: +The parser uses EXACT pattern matching with grep to filter logs efficiently. +For example, when parsing FP logs: +- Grep pattern: 'FP_FEATURES:' and 'FP_RESULT:' (with colon) +- Matches ONLY predictor log lines, NOT general FP debug/info lines +- A log with 10,000 lines containing "FP" might have only 100 predictor lines +- Grep filters down to only the relevant lines before Python processing + +Performance optimizations for very large log files: +- Single grep call per algorithm (instead of separate calls for features/results) +- Uses grep's -n flag to get line numbers for efficient pairing +- Minimal Python string processing (split instead of regex) +- Single-pass parsing with dictionary accumulation +- Avoids redundant string operations +- DRY refactoring: Generic parser eliminates duplicate code (~105 lines removed) +- Real-time progress indicators (every 10K lines, every 10 files) Usage: - python determinism_logs_parse.py [-o output.pkl] + python determinism_logs_parse.py --algorithm FP [-o output.pkl] + python determinism_logs_parse.py --algorithm PDLP [-o output.pkl] + python determinism_logs_parse.py --algorithm CP [-o output.pkl] + python determinism_logs_parse.py --algorithm FJ [-o output.pkl] """ import argparse @@ -27,9 +53,14 @@ import subprocess import os import glob +import re +from typing import List, Dict, Any, Optional, Tuple + +SUPPORTED_ALGORITHMS = ['FP', 'PDLP', 'CP', 'FJ'] -def parse_value(value_str): + +def parse_value(value_str: str) -> Any: """Convert string value to appropriate type (int, float, or str).""" try: # Try to parse as float if it contains a decimal point or scientific notation @@ -42,64 +73,234 @@ def parse_value(value_str): return value_str -def main(): - parser = argparse.ArgumentParser( - description='Parse log files with key-value pairs and export to pickle' - ) - parser.add_argument( - 'input_dir', - help='Directory containing .log files to parse' - ) - parser.add_argument( - '-o', '--output', - default='output.pkl', - help='Output pickle file path (default: output.pkl)' - ) - args = parser.parse_args() +def parse_key_value_line(line: str, prefix: str) -> Dict[str, Any]: + """ + Parse a line containing key=value pairs after removing prefix. - # Find all .log files in the input directory - log_files = glob.glob(os.path.join(args.input_dir, '*.log')) + Example + ------- + "FP_FEATURES: n_variables=100 n_constraints=50" + -> {'n_variables': 100, 'n_constraints': 50} + """ + entry = {} - if not log_files: - print(f"No .log files found in {args.input_dir}") - return + # Remove prefix + if prefix in line: + line = line.split(prefix, 1)[1].strip() + + # Parse key=value pairs + # Handle both space-separated and comma-separated + for kv_pair in line.split(): + if '=' in kv_pair: + key, value = kv_pair.split('=', 1) + # Remove trailing commas + value = value.rstrip(',') + entry[key] = parse_value(value) + + return entry + + +def parse_generic_algorithm_logs(log_files: List[str], + algorithm: str, + algorithm_name: str) -> List[Dict[str, Any]]: + """ + Generic parser for algorithm feature and result logs. + + Matches _FEATURES lines with subsequent _RESULT lines. + Uses grep efficiently to minimize Python-side processing. + + Args: + log_files: List of log file paths to parse + algorithm: Algorithm prefix (e.g., 'FP', 'PDLP', 'CP') + algorithm_name: Full name for display (e.g., 'Feasibility Pump') + + Returns + ------- + List of dictionaries with combined features and results + """ + print(f"\nParsing {algorithm} ({algorithm_name}) logs...") + print(f" Running grep on {len(log_files)} files...") + + # Construct grep patterns with EXACT match requirements + # The colon at the end ensures we ONLY match the feature/result log lines + # and ignore all other lines containing the algorithm name + features_pattern = f'{algorithm}_FEATURES:' + result_pattern = f'{algorithm}_RESULT:' + + # Use grep with: + # -H: Always print filename (even with single file) + # -n: Print line numbers for correct pairing + # -e: Multiple patterns to match + # This ensures we ONLY get the specific predictor log lines, not debug/info lines + cmd = ['grep', '-Hn', '-e', features_pattern, '-e', result_pattern] + log_files + + result = subprocess.run(cmd, capture_output=True, text=True) + + if not result.stdout: + print(f" No {algorithm} logs found") + return [] + + # Count lines for progress indication + total_lines = result.stdout.count('\n') + print(f" Processing {total_lines} matching lines...") + + # Process grep output efficiently + # Format: filename:linenum:_FEATURES: key1=value1 ... + entries_by_file = {} + lines_processed = 0 + files_seen = set() + + for line in result.stdout.split('\n'): + if not line: + continue + + lines_processed += 1 + + # Progress update every 10000 lines + if lines_processed % 10000 == 0: + print(f" Progress: {lines_processed}/{total_lines} lines, {len(files_seen)} files", end='\r') + + # Split on first two colons to get filename, linenum, and content + # The rest of the line after the second colon is the log content + parts = line.split(':', 2) + if len(parts) < 3: + continue + + filename = os.path.basename(parts[0]) + linenum = int(parts[1]) + content = parts[2] # This includes everything after linenum + + if filename not in entries_by_file: + entries_by_file[filename] = {'features': [], 'results': []} + files_seen.add(filename) + + # Double-check pattern match (grep already filtered, but be extra safe) + # This ensures we ONLY process lines with the exact patterns we want + if features_pattern in content: + # Parse features - only if pattern is present + features = parse_key_value_line(content, features_pattern) + if features: # Only add if parsing succeeded + entries_by_file[filename]['features'].append((linenum, features)) + elif result_pattern in content: + # Parse results - only if pattern is present + results = parse_key_value_line(content, result_pattern) + if results: # Only add if parsing succeeded + entries_by_file[filename]['results'].append((linenum, results)) + + # Clear progress line + if lines_processed > 0: + print(f" Processed {lines_processed} lines from {len(files_seen)} files ") + + # Match features with results (pair them in order by line number) + print(f" Pairing features with results...") + entries = [] + files_processed = 0 + total_files = len(entries_by_file) + + for filename, data in entries_by_file.items(): + files_processed += 1 + + # Progress update every 10 files + if files_processed % 10 == 0 or files_processed == total_files: + print(f" Pairing: {files_processed}/{total_files} files, {len(entries)} entries found", end='\r') + + features_list = sorted(data['features']) # Sort by line number + results_list = sorted(data['results']) + + # Pair up features and results in order + for i, (_, features) in enumerate(features_list): + if i < len(results_list): + _, results = results_list[i] + + # Create combined entry + entry = {'file': filename} + entry.update(features) + entry.update(results) + + # Rename 'iterations' to 'iter' for consistency with train_regressor.py + if 'iterations' in entry: + entry['iter'] = entry.pop('iterations') + + entries.append(entry) + + # Clear progress line and show final count + if total_files > 0: + print(f" Found {len(entries)} complete entries from {total_files} files ") + + return entries - print(f"Found {len(log_files)} log files") - # Use grep to efficiently extract all lines containing "FJ:" - # -H flag ensures filename is included in output (even with single file) - # -h suppresses filename (we don't want that) +# Algorithm-specific wrappers for the generic parser +# These provide a clean API and eliminate code duplication + +def parse_fp_logs(log_files: List[str]) -> List[Dict[str, Any]]: + """Parse Feasibility Pump feature and result logs.""" + return parse_generic_algorithm_logs(log_files, 'FP', 'Feasibility Pump') + + +def parse_pdlp_logs(log_files: List[str]) -> List[Dict[str, Any]]: + """Parse PDLP (LP Solver) feature and result logs.""" + return parse_generic_algorithm_logs(log_files, 'PDLP', 'LP Solver') + + +def parse_cp_logs(log_files: List[str]) -> List[Dict[str, Any]]: + """Parse Constraint Propagation feature and result logs.""" + return parse_generic_algorithm_logs(log_files, 'CP', 'Constraint Propagation') + + +def parse_fj_logs(log_files: List[str]) -> List[Dict[str, Any]]: + """ + Parse legacy Feasibility Jump logs (original format). + + Parses lines containing "FJ:" with key=value pairs. + Uses grep efficiently to minimize Python-side processing. + """ + print("\nParsing FJ (Feasibility Jump) legacy logs...") + print(f" Running grep on {len(log_files)} files...") + + # Use grep to efficiently extract ONLY lines with the exact "FJ:" pattern + # Note: FJ uses legacy format with just "FJ:" prefix (not FJ_FEATURES/FJ_RESULT) + # The colon ensures we don't match other FJ-related debug lines cmd = ['grep', '-H', 'FJ:'] + log_files result = subprocess.run(cmd, capture_output=True, text=True) - if result.returncode != 0 and result.returncode != 1: - # grep returns 1 if no matches found, which is fine - # Other return codes indicate actual errors - print(f"Error running grep: {result.stderr}") - return + if not result.stdout: + print(f" No FJ logs found") + return [] + + # Count lines for progress indication + total_lines = result.stdout.count('\n') + print(f" Processing {total_lines} matching lines...") - # Parse grep output + # Parse grep output efficiently entries = [] - for line in result.stdout.strip().split('\n'): + lines_processed = 0 + + for line in result.stdout.split('\n'): if not line: continue + lines_processed += 1 + + # Progress update every 10000 lines + if lines_processed % 10000 == 0: + print(f" Progress: {lines_processed}/{total_lines} lines, {len(entries)} entries", end='\r') + # Grep output format: filename:FJ: key1=value1 key2=value2 ... - # Split on first colon to separate filename from content - colon_idx = line.find(':') - if colon_idx == -1: + parts = line.split(':', 2) + if len(parts) < 3: continue - filename = os.path.basename(line[:colon_idx]) - rest = line[colon_idx + 1:] + filename = os.path.basename(parts[0]) + content = parts[2] # Remove "FJ:" prefix if present - if rest.startswith('FJ:'): - rest = rest[3:].strip() + if content.startswith('FJ:'): + content = content[3:].strip() # Parse key-value pairs entry = {'file': filename} - for kv_pair in rest.split(): + for kv_pair in content.split(): if '=' in kv_pair: key, value = kv_pair.split('=', 1) entry[key] = parse_value(value) @@ -108,18 +309,148 @@ def main(): if len(entry) > 1: entries.append(entry) - # Calculate statistics + # Clear progress line + if lines_processed > 0: + print(f" Found {len(entries)} entries from {total_lines} lines ") + + return entries + + +def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: + """Print statistics about parsed entries.""" + if not entries: + print(f"\n No entries found for {algorithm}") + return + unique_files = set(entry['file'] for entry in entries) avg_entries_per_file = len(entries) / len(unique_files) if unique_files else 0 + # Check if 'iter' field exists + has_iter = all('iter' in entry for entry in entries) + + if has_iter: + iter_values = [entry['iter'] for entry in entries] + min_iter = min(iter_values) + max_iter = max(iter_values) + avg_iter = sum(iter_values) / len(iter_values) + + print(f"\n Total entries: {len(entries)}") + print(f" Unique files: {len(unique_files)}") + print(f" Avg entries per file: {avg_entries_per_file:.2f}") + print(f" Iterations (target): min={min_iter}, max={max_iter}, avg={avg_iter:.2f}") + else: + print(f"\n Total entries: {len(entries)}") + print(f" Unique files: {len(unique_files)}") + print(f" Avg entries per file: {avg_entries_per_file:.2f}") + + # Show sample entry + if entries: + print(f"\n Sample entry (first):") + sample = entries[0] + for key, value in sorted(sample.items()): + print(f" {key}: {value}") + + +def main(): + parser = argparse.ArgumentParser( + description='Parse algorithm feature logs and export to pickle for training', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=f""" +Supported Algorithms: + FP - Feasibility Pump (parses FP_FEATURES and FP_RESULT logs) + PDLP - LP Solver (parses PDLP_FEATURES and PDLP_RESULT logs) + CP - Constraint Propagation (parses CP_FEATURES and CP_RESULT logs) + FJ - Feasibility Jump (parses legacy FJ: format) + +Examples: + python determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl + python determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.pkl + python determinism_logs_parse.py logs/ --algorithm CP -o cp_data.pkl + python determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.pkl + """ + ) + + parser.add_argument( + 'input_dir', + help='Directory containing .log files to parse' + ) + parser.add_argument( + '--algorithm', '-a', + required=True, + choices=SUPPORTED_ALGORITHMS, + help='Algorithm to parse logs for' + ) + parser.add_argument( + '-o', '--output', + default=None, + help='Output pickle file path (default: _data.pkl)' + ) + parser.add_argument( + '--verbose', '-v', + action='store_true', + help='Print verbose output including warnings' + ) + + args = parser.parse_args() + + # Set default output filename based on algorithm + if args.output is None: + args.output = f"{args.algorithm.lower()}_data.pkl" + + # Find all .log files in the input directory + print(f"\nScanning {args.input_dir} for .log files...") + log_files = glob.glob(os.path.join(args.input_dir, '*.log')) + + if not log_files: + print(f"Error: No .log files found in {args.input_dir}") + return 1 + + print(f"Found {len(log_files)} log files") + + # Parse logs based on algorithm + if args.algorithm == 'FP': + entries = parse_fp_logs(log_files) + elif args.algorithm == 'PDLP': + entries = parse_pdlp_logs(log_files) + elif args.algorithm == 'CP': + entries = parse_cp_logs(log_files) + elif args.algorithm == 'FJ': + entries = parse_fj_logs(log_files) + else: + print(f"Error: Unsupported algorithm: {args.algorithm}") + return 1 + + if not entries: + print(f"\nError: No entries found for {args.algorithm}") + print(f"Make sure your logs contain {args.algorithm}_FEATURES and {args.algorithm}_RESULT lines") + return 1 + + # Print statistics + print_statistics(entries, args.algorithm) + # Save to pickle file + print(f"\nSaving {len(entries)} entries to {args.output}...") with open(args.output, 'wb') as f: pickle.dump(entries, f) - print(f"Parsed {len(entries)} entries from {len(unique_files)} log files") - print(f"Average entries per file: {avg_entries_per_file:.2f}") - print(f"Saved to {args.output}") + # Get file size + file_size_bytes = os.path.getsize(args.output) + file_size_mb = file_size_bytes / (1024 * 1024) + + print(f"\n{'='*70}") + print(f"✓ Success! Saved {len(entries)} entries to {args.output}") + print(f" File size: {file_size_mb:.2f} MB") + print(f"{'='*70}") + print(f"\nNext steps:") + print(f" 1. View available features:") + print(f" python scripts/train_regressor.py {args.output} --regressor xgboost --list-features") + print(f" 2. Train a model:") + print(f" python scripts/train_regressor.py {args.output} --regressor xgboost --seed 42") + print(f" 3. Train with early stopping and C++ export:") + print(f" python scripts/train_regressor.py {args.output} --regressor xgboost --seed 42 --early-stopping 20 --treelite-compile 8") + + return 0 if __name__ == '__main__': - main() + exit(main()) From c4d3fde547a6c31aab5e74d4406a94a0176284f3 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 31 Oct 2025 15:59:47 +0000 Subject: [PATCH 022/225] bump --- cpp/src/linear_programming/solve.cu | 2 +- cpp/src/mip/diversity/diversity_manager.cu | 17 ++ cpp/src/mip/relaxed_lp/relaxed_lp.cu | 9 - scripts/determinism_logs_parse.py | 268 +++++++++++++++--- scripts/train_regressor.py | 313 ++++++++++++++++++--- 5 files changed, 518 insertions(+), 91 deletions(-) diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index bd3080102b..5699895b1e 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -123,7 +123,7 @@ static void set_Stable2() pdlp_hyper_params::major_iteration = 40; pdlp_hyper_params::min_iteration_restart = 10; pdlp_hyper_params::restart_strategy = 1; - pdlp_hyper_params::never_restart_to_average = false; + pdlp_hyper_params::never_restart_to_average = true; pdlp_hyper_params::host_default_reduction_exponent = 0.3; pdlp_hyper_params::host_default_growth_exponent = 0.6; pdlp_hyper_params::host_default_primal_weight_update_smoothing = 0.5; diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index d4f21c1800..f45132819f 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -425,6 +425,23 @@ solution_t diversity_manager_t::run_solver() clamp_within_var_bounds(lp_optimal_solution, problem_ptr, problem_ptr->handle_ptr); } + // Run this 100 times with varying iteration limits + for (int i = 0; i < 100; i++) { + relaxed_lp_settings_t lp_settings; + lp_settings.time_limit = lp_time_limit; + lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; + lp_settings.return_first_feasible = false; + lp_settings.save_state = true; + lp_settings.concurrent_halt = &global_concurrent_halt; + lp_settings.has_initial_primal = false; + lp_settings.iteration_limit = 100000 + i * 10000; + rmm::device_uvector lp_optimal_solution_copy(lp_optimal_solution.size(), + problem_ptr->handle_ptr->get_stream()); + auto lp_result = + get_relaxed_lp_solution(*problem_ptr, lp_optimal_solution_copy, lp_state, lp_settings); + } + exit(0); + if (ls.lp_optimal_exists) { solution_t lp_rounded_sol(*problem_ptr); lp_rounded_sol.copy_new_assignment(lp_optimal_solution); diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index d8afa0da6d..ad649feb26 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -164,15 +164,6 @@ optimization_problem_solution_t get_relaxed_lp_solution( term_info.number_of_steps_taken, elapsed_ms, (int)solver_response.get_termination_status()); - - CUOPT_LOG_INFO("PDLP_RESULT: primal_objective=%.10f dual_objective=%.10f gap=%.10f", - term_info.primal_objective, - term_info.dual_objective, - term_info.gap); - - CUOPT_LOG_INFO("PDLP_RESULT: l2_primal_residual=%.10f l2_dual_residual=%.10f", - term_info.l2_primal_residual, - term_info.l2_dual_residual); // === PDLP PREDICTOR RESULTS - END === return solver_response; diff --git a/scripts/determinism_logs_parse.py b/scripts/determinism_logs_parse.py index 687f0c6516..0e0b9b5b1b 100755 --- a/scripts/determinism_logs_parse.py +++ b/scripts/determinism_logs_parse.py @@ -42,18 +42,19 @@ - Real-time progress indicators (every 10K lines, every 10 files) Usage: - python determinism_logs_parse.py --algorithm FP [-o output.pkl] - python determinism_logs_parse.py --algorithm PDLP [-o output.pkl] - python determinism_logs_parse.py --algorithm CP [-o output.pkl] - python determinism_logs_parse.py --algorithm FJ [-o output.pkl] + python determinism_logs_parse.py --algorithm FP [-o output.feather] + python determinism_logs_parse.py --algorithm PDLP [-o output.feather] + python determinism_logs_parse.py --algorithm CP [-o output.feather] + python determinism_logs_parse.py --algorithm FJ [-o output.feather] """ import argparse -import pickle import subprocess import os import glob import re +import numpy as np +import pandas as pd from typing import List, Dict, Any, Optional, Tuple @@ -63,6 +64,11 @@ def parse_value(value_str: str) -> Any: """Convert string value to appropriate type (int, float, or str).""" try: + # Handle special float values first (nan, inf, -inf) + value_lower = value_str.lower() + if value_lower in ('nan', 'inf', '-inf', '+inf'): + return float(value_str) + # Try to parse as float if it contains a decimal point or scientific notation if '.' in value_str or 'e' in value_str.lower(): return float(value_str) @@ -158,7 +164,8 @@ def parse_generic_algorithm_logs(log_files: List[str], # Progress update every 10000 lines if lines_processed % 10000 == 0: - print(f" Progress: {lines_processed}/{total_lines} lines, {len(files_seen)} files", end='\r') + pct = (lines_processed / total_lines * 100) if total_lines > 0 else 0 + print(f" Progress: {pct:.1f}% ({lines_processed}/{total_lines} lines, {len(files_seen)} files)", end='\r') # Split on first two colons to get filename, linenum, and content # The rest of the line after the second colon is the log content @@ -191,8 +198,10 @@ def parse_generic_algorithm_logs(log_files: List[str], if lines_processed > 0: print(f" Processed {lines_processed} lines from {len(files_seen)} files ") - # Match features with results (pair them in order by line number) - print(f" Pairing features with results...") + # Match features with results + # IMPORTANT: Multiple FEATURES lines followed by multiple RESULT lines form ONE complete entry + # We need to merge consecutive lines of the same type, then combine them + print(f" Merging features and results...") entries = [] files_processed = 0 total_files = len(entries_by_file) @@ -202,26 +211,62 @@ def parse_generic_algorithm_logs(log_files: List[str], # Progress update every 10 files if files_processed % 10 == 0 or files_processed == total_files: - print(f" Pairing: {files_processed}/{total_files} files, {len(entries)} entries found", end='\r') - - features_list = sorted(data['features']) # Sort by line number - results_list = sorted(data['results']) - - # Pair up features and results in order - for i, (_, features) in enumerate(features_list): - if i < len(results_list): - _, results = results_list[i] + print(f" Merging: {files_processed}/{total_files} files, {len(entries)} entries found", end='\r') + + # Combine features and results by line number + # Group consecutive FEATURES lines and consecutive RESULT lines + all_items = [] + for linenum, features in data['features']: + all_items.append((linenum, 'features', features)) + for linenum, results in data['results']: + all_items.append((linenum, 'results', results)) + + # Sort by line number + all_items.sort(key=lambda x: x[0]) + + # Merge consecutive items of the same type + current_features = {} + current_results = {} + last_type = None + + for linenum, item_type, content in all_items: + # If we transition from RESULT back to FEATURES, save the previous entry + if item_type == 'features' and last_type == 'results': + if current_features and current_results: + # Create combined entry + entry = {'file': filename} + entry.update(current_features) + entry.update(current_results) + + # Rename 'iterations' to 'iter' for consistency + if 'iterations' in entry: + entry['iter'] = entry.pop('iterations') + + entries.append(entry) + + # Reset for next entry + current_features = {} + current_results = {} + + if item_type == 'features': + # Accumulate features + current_features.update(content) + else: # results + # Accumulate results + current_results.update(content) + + last_type = item_type + + # Don't forget the last entry in the file + if current_features and current_results: + entry = {'file': filename} + entry.update(current_features) + entry.update(current_results) + + if 'iterations' in entry: + entry['iter'] = entry.pop('iterations') - # Create combined entry - entry = {'file': filename} - entry.update(features) - entry.update(results) - - # Rename 'iterations' to 'iter' for consistency with train_regressor.py - if 'iterations' in entry: - entry['iter'] = entry.pop('iterations') - - entries.append(entry) + entries.append(entry) # Clear progress line and show final count if total_files > 0: @@ -284,7 +329,8 @@ def parse_fj_logs(log_files: List[str]) -> List[Dict[str, Any]]: # Progress update every 10000 lines if lines_processed % 10000 == 0: - print(f" Progress: {lines_processed}/{total_lines} lines, {len(entries)} entries", end='\r') + pct = (lines_processed / total_lines * 100) if total_lines > 0 else 0 + print(f" Progress: {pct:.1f}% ({lines_processed}/{total_lines} lines, {len(entries)} entries)", end='\r') # Grep output format: filename:FJ: key1=value1 key2=value2 ... parts = line.split(':', 2) @@ -353,7 +399,7 @@ def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: def main(): parser = argparse.ArgumentParser( - description='Parse algorithm feature logs and export to pickle for training', + description='Parse algorithm feature logs and export to Feather format for training', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=f""" Supported Algorithms: @@ -363,10 +409,13 @@ def main(): FJ - Feasibility Jump (parses legacy FJ: format) Examples: - python determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl - python determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.pkl - python determinism_logs_parse.py logs/ --algorithm CP -o cp_data.pkl - python determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.pkl + python determinism_logs_parse.py logs/ --algorithm FP -o fp_data.feather + python determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.feather + python determinism_logs_parse.py logs/ --algorithm CP -o cp_data.feather + python determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.feather + + # Limit to first 10 files for testing + python determinism_logs_parse.py logs/ --algorithm FP --max-files 10 """ ) @@ -383,19 +432,25 @@ def main(): parser.add_argument( '-o', '--output', default=None, - help='Output pickle file path (default: _data.pkl)' + help='Output Feather file path (default: _data.feather)' ) parser.add_argument( '--verbose', '-v', action='store_true', help='Print verbose output including warnings' ) + parser.add_argument( + '--max-files', + type=int, + default=None, + help='Limit number of log files to process (useful for testing)' + ) args = parser.parse_args() # Set default output filename based on algorithm if args.output is None: - args.output = f"{args.algorithm.lower()}_data.pkl" + args.output = f"{args.algorithm.lower()}_data.feather" # Find all .log files in the input directory print(f"\nScanning {args.input_dir} for .log files...") @@ -407,6 +462,14 @@ def main(): print(f"Found {len(log_files)} log files") + # Apply max-files limit if specified + if args.max_files is not None and args.max_files > 0: + if args.max_files < len(log_files): + log_files = log_files[:args.max_files] + print(f"Limiting to first {args.max_files} files (--max-files)") + else: + print(f"Note: --max-files={args.max_files} is >= total files, using all files") + # Parse logs based on algorithm if args.algorithm == 'FP': entries = parse_fp_logs(log_files) @@ -428,17 +491,142 @@ def main(): # Print statistics print_statistics(entries, args.algorithm) - # Save to pickle file - print(f"\nSaving {len(entries)} entries to {args.output}...") - with open(args.output, 'wb') as f: - pickle.dump(entries, f) + # Convert to DataFrame + df = pd.DataFrame(entries) + + # Validate: Check for NaN and infinite values + print(f"\nValidating data integrity...") + + # Check for NaN + nan_counts = df.isna().sum() + columns_with_nan = nan_counts[nan_counts > 0] + + # Check for infinite values in numeric columns + numeric_cols = df.select_dtypes(include=[np.number]).columns + inf_counts = {} + for col in numeric_cols: + inf_count = np.isinf(df[col]).sum() + if inf_count > 0: + inf_counts[col] = inf_count + + has_issues = len(columns_with_nan) > 0 or len(inf_counts) > 0 + + if has_issues: + print(f"\n{'='*70}") + print(f"ERROR: Invalid data detected (NaN/inf values)!") + print(f"{'='*70}") + print(f"\nColumns with invalid values:") + for col, count in columns_with_nan.items(): + pct = (count / len(df)) * 100 + print(f" {col}: {count} NaN ({pct:.1f}%)") + for col, count in inf_counts.items(): + pct = (count / len(df)) * 100 + print(f" {col}: {count} infinite ({pct:.1f}%)") + + # Show which log files have the issues + rows_with_issues_mask = df.isna().any(axis=1) + for col in inf_counts.keys(): + if col in df.columns: + rows_with_issues_mask |= np.isinf(df[col]) + rows_with_nan = df[rows_with_issues_mask] + problematic_files = rows_with_nan['file'].unique() + print(f"\nProblematic log files ({len(problematic_files)} total):") + for i, filename in enumerate(sorted(problematic_files)[:20], 1): + count_in_file = len(rows_with_nan[rows_with_nan['file'] == filename]) + print(f" {i}. {filename}: {count_in_file} invalid entries") + if len(problematic_files) > 20: + print(f" ... and {len(problematic_files) - 20} more files") + + print(f"\nSample rows with invalid data:") + cols_to_show = ['file'] + cols_to_show.extend([col for col in columns_with_nan.index]) + cols_to_show.extend([col for col in inf_counts.keys() if col not in columns_with_nan.index]) + cols_to_show = [col for col in cols_to_show if col in df.columns] + print(rows_with_nan[cols_to_show].head(10)) + + print(f"\nPossible causes:") + print(f" 1. Algorithm failed/crashed during execution (produced 'nan' in logs)") + print(f" 2. Incomplete log entries (missing FEATURES or RESULT lines)") + print(f" 3. Log format doesn't match expected pattern") + print(f"\nAction required:") + print(f" - Review the problematic log files listed above") + print(f" - Fix the underlying issues causing NaN values") + print(f" - Re-run the algorithm on those problem instances") + print(f"{'='*70}\n") + return 1 + + print(f" ✅ No missing values detected") + + # Filter out negative iterations (invalid data) + if 'iter' in df.columns: + negative_mask = df['iter'] < 0 + negative_count = negative_mask.sum() + + if negative_count > 0: + print(f"\n⚠️ Found {negative_count} entries with negative iterations ({negative_count/len(df)*100:.2f}%)") + + # Show which files have negative iterations + negative_entries = df[negative_mask] + problematic_files = negative_entries['file'].unique() + if len(problematic_files) <= 10: + print(f" Affected files: {', '.join(sorted(problematic_files))}") + else: + print(f" Affected files: {len(problematic_files)} files") + + # Drop negative iterations + df = df[~negative_mask].reset_index(drop=True) + print(f" → Dropped negative entries, remaining: {len(df)} entries") + + # Check if we have any data left + if len(df) == 0: + print(f"\n❌ Error: No valid entries remaining after filtering!") + print(f" All entries had negative iterations.") + return 1 + + # Print obtained features (all columns) + print(f"\nObtained Features ({len(df.columns)} total):") + print(f"{'='*70}") + + # Separate metadata from actual features + metadata_cols = ['file'] + target_cols = ['iter', 'iterations'] # Target variable (if present) + + feature_cols = [col for col in df.columns if col not in metadata_cols and col not in target_cols] + + # Print in categories + if metadata_cols: + meta_present = [col for col in metadata_cols if col in df.columns] + if meta_present: + print(f" Metadata: {', '.join(meta_present)}") + + target_present = [col for col in target_cols if col in df.columns] + if target_present: + print(f" Target: {', '.join(target_present)}") + + if feature_cols: + print(f" Features ({len(feature_cols)}):") + for i, col in enumerate(sorted(feature_cols), 1): + # Print 3 features per line for readability + if i % 3 == 1: + print(f" ", end="") + print(f"{col:30s}", end="") + if i % 3 == 0: + print() # Newline after 3 features + if len(feature_cols) % 3 != 0: + print() # Final newline if needed + + print(f"{'='*70}") + + # Save to Feather file (Apache Arrow format for fast I/O) + print(f"\nSaving {len(df)} entries to {args.output}...") + df.to_feather(args.output, compression='lz4') # Get file size file_size_bytes = os.path.getsize(args.output) file_size_mb = file_size_bytes / (1024 * 1024) print(f"\n{'='*70}") - print(f"✓ Success! Saved {len(entries)} entries to {args.output}") + print(f"✓ Success! Saved {len(df)} entries to {args.output}") print(f" File size: {file_size_mb:.2f} MB") print(f"{'='*70}") print(f"\nNext steps:") diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index 7c5f1d298e..f48208c4b6 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -19,7 +19,7 @@ Train regression models to predict algorithm iterations from log features. Usage: - python train_regressor.py --regressor [options] + python train_regressor.py --regressor [options] """ import argparse @@ -77,7 +77,19 @@ 'max_related_vars', 'problem_size_score', 'structural_complexity', - 'tight_constraint_ratio' + 'tight_constraint_ratio', + 'tolerance', + 'time_limit', + 'tolerance', + 'primal_objective', + 'dual_objective', + 'gap', + 'l2_primal_residual', + 'l2_dual_residual', + 'detect_infeasibility', + 'iteration_limit', + 'termination', + 'check_infeasibility' ] # Alternatively, specify ONLY the features you want to use @@ -91,24 +103,33 @@ # ============================================================================ -def load_data(pkl_path: str) -> pd.DataFrame: - """Load pickle file and convert to DataFrame.""" - with open(pkl_path, 'rb') as f: - data = pickle.load(f) +def load_data(data_path: str, target_col: str = 'iter') -> pd.DataFrame: + """Load data file (supports .feather and legacy .pkl formats).""" + ext = os.path.splitext(data_path)[1].lower() - if not isinstance(data, list): - raise ValueError(f"Expected list of dictionaries, got {type(data)}") + if ext == '.feather': + # Fast Apache Arrow format + df = pd.read_feather(data_path) + elif ext == '.pkl': + # Legacy pickle support + with open(data_path, 'rb') as f: + data = pickle.load(f) - if len(data) == 0: - raise ValueError("Empty dataset") + if not isinstance(data, list): + raise ValueError(f"Expected list of dictionaries, got {type(data)}") - df = pd.DataFrame(data) + if len(data) == 0: + raise ValueError("Empty dataset") + + df = pd.DataFrame(data) + else: + raise ValueError(f"Unsupported file format: {ext}. Use .feather (recommended) or .pkl") # Validate required columns if 'file' not in df.columns: raise ValueError("Missing required 'file' column in data") - if 'iter' not in df.columns: - raise ValueError("Missing required 'iter' column in data") + if target_col not in df.columns: + raise ValueError(f"Missing target column '{target_col}' in data. Available columns: {list(df.columns)}") return df @@ -151,45 +172,170 @@ def split_by_files(df: pd.DataFrame, test_size: float = 0.2, print(f" Train entries: {len(train_df)} ({len(train_files)} files)") print(f" Test entries: {len(test_df)} ({len(test_files)} files)") - # Check distribution similarity - train_target_mean = train_df['iter'].mean() - test_target_mean = test_df['iter'].mean() - train_target_std = train_df['iter'].std() - test_target_std = test_df['iter'].std() + # Check distribution similarity (use stratify_by column if provided, otherwise first numeric column) + target_col = stratify_by if stratify_by else df.select_dtypes(include=[np.number]).columns[0] + if target_col in train_df.columns and target_col in test_df.columns: + train_target_mean = train_df[target_col].mean() + test_target_mean = test_df[target_col].mean() + train_target_std = train_df[target_col].std() + test_target_std = test_df[target_col].std() - print(f"\nTarget ('iter') Distribution:") - print(f" Train: mean={train_target_mean:.2f}, std={train_target_std:.2f}") - print(f" Test: mean={test_target_mean:.2f}, std={test_target_std:.2f}") + print(f"\nTarget ('{target_col}') Distribution:") + print(f" Train: mean={train_target_mean:.2f}, std={train_target_std:.2f}") + print(f" Test: mean={test_target_mean:.2f}, std={test_target_std:.2f}") - mean_diff_pct = abs(train_target_mean - test_target_mean) / train_target_mean * 100 - if mean_diff_pct > 10: - print(f" ⚠️ Warning: Train/test target means differ by {mean_diff_pct:.1f}%") - print(f" Consider using stratified split or different random seed") + mean_diff_pct = abs(train_target_mean - test_target_mean) / train_target_mean * 100 if train_target_mean != 0 else 0 + if mean_diff_pct > 10: + print(f" ⚠️ Warning: Train/test target means differ by {mean_diff_pct:.1f}%") + print(f" Consider using stratified split or different random seed") return train_df, test_df -def list_available_features(df: pd.DataFrame) -> List[str]: +def list_available_features(df: pd.DataFrame, target_col: str = 'iter') -> List[str]: """ List all available numeric features in the dataset. Helper function to see what features can be selected/excluded. """ - X = df.drop(columns=['iter', 'file'], errors='ignore') + # Drop target and metadata columns + cols_to_drop = [target_col, 'file', 'iter', 'iterations'] # Drop common target column names + X = df.drop(columns=[c for c in cols_to_drop if c in df.columns], errors='ignore') X = X.select_dtypes(include=[np.number]) return sorted(X.columns.tolist()) -def prepare_features(df: pd.DataFrame) -> Tuple[pd.DataFrame, pd.Series, List[str]]: +def validate_data_quality(df: pd.DataFrame, target_col: str = 'iter', verbose: bool = True) -> Tuple[bool, Dict[str, Any]]: + """ + Validate data quality and report issues. + + Returns + ------- + (is_valid, report_dict) + """ + report = { + 'target_issues': [], + 'feature_issues': [], + 'rows_with_issues': [], + 'is_valid': True + } + + # Check target variable + if target_col not in df.columns: + report['is_valid'] = False + report['target_issues'].append(f"Missing '{target_col}' column") + return False, report + + y = df[target_col] + + # Check for NaN in target + nan_count = y.isna().sum() + if nan_count > 0: + report['is_valid'] = False + report['target_issues'].append(f"Target has {nan_count} NaN values ({nan_count/len(y)*100:.1f}%)") + report['rows_with_issues'].extend(df[y.isna()].index.tolist()) + + # Check for inf in target + inf_count = np.isinf(y).sum() + if inf_count > 0: + report['is_valid'] = False + report['target_issues'].append(f"Target has {inf_count} infinite values ({inf_count/len(y)*100:.1f}%)") + report['rows_with_issues'].extend(df[np.isinf(y)].index.tolist()) + + # Check for extreme values in target + if not y.isna().all() and not np.isinf(y).all(): + y_clean = y[~(y.isna() | np.isinf(y))] + if len(y_clean) > 0: + y_max = y_clean.max() + y_min = y_clean.min() + if y_max > 1e10: + report['target_issues'].append(f"Target has very large values (max={y_max:.2e})") + if y_min < -1e10: + report['target_issues'].append(f"Target has very large negative values (min={y_min:.2e})") + + # Check features + X = df.drop(columns=[target_col, 'file'], errors='ignore') + X_numeric = X.select_dtypes(include=[np.number]) + + for col in X_numeric.columns: + col_data = X_numeric[col] + + # Check for NaN + nan_count = col_data.isna().sum() + if nan_count > 0: + pct = nan_count / len(col_data) * 100 + if pct > 10: # Only report if > 10% are NaN + report['feature_issues'].append(f"{col}: {nan_count} NaN ({pct:.1f}%)") + + # Check for inf + inf_count = np.isinf(col_data).sum() + if inf_count > 0: + report['is_valid'] = False + pct = inf_count / len(col_data) * 100 + report['feature_issues'].append(f"{col}: {inf_count} infinite ({pct:.1f}%)") + report['rows_with_issues'].extend(df[np.isinf(col_data)].index.tolist()) + + # Deduplicate row indices + report['rows_with_issues'] = sorted(list(set(report['rows_with_issues']))) + + # Print report if verbose + if verbose and (report['target_issues'] or report['feature_issues']): + print("\n" + "="*70) + print("DATA QUALITY ISSUES DETECTED") + print("="*70) + + if report['target_issues']: + print("\nTarget Variable Issues:") + for issue in report['target_issues']: + print(f" ❌ {issue}") + + if report['feature_issues']: + print(f"\nFeature Issues ({len(report['feature_issues'])} features affected):") + for issue in report['feature_issues'][:10]: # Show first 10 + print(f" ⚠️ {issue}") + if len(report['feature_issues']) > 10: + print(f" ... and {len(report['feature_issues']) - 10} more features") + + if report['rows_with_issues']: + print(f"\nAffected Rows: {len(report['rows_with_issues'])} total") + if len(report['rows_with_issues']) <= 10: + print(f" Row indices: {report['rows_with_issues']}") + else: + print(f" First 10: {report['rows_with_issues'][:10]}") + + # Show sample problematic rows with filenames + if 'file' in df.columns: + print(f"\n Sample problematic entries:") + sample_indices = report['rows_with_issues'][:5] + for idx in sample_indices: + if idx < len(df): + filename = df.iloc[idx].get('file', 'unknown') + iter_val = df.iloc[idx].get('iter', 'N/A') + print(f" Row {idx}: file={filename}, iter={iter_val}") + + print("\nSuggested Actions:") + if not report['is_valid']: + print(" 1. Remove rows with invalid data: --drop-invalid-rows") + print(" 2. Check your log files for data collection issues") + print(" 3. Verify algorithm didn't produce invalid results") + else: + print(" Data is valid but has some NaN values in features (will be handled)") + + print("="*70 + "\n") + + return report['is_valid'], report + + +def prepare_features(df: pd.DataFrame, target_col: str = 'iter') -> Tuple[pd.DataFrame, pd.Series, List[str]]: """ Prepare features and target from DataFrame. - Excludes 'file' and 'iter' from features. + Excludes 'file' and target column from features. Applies feature selection based on FEATURES_TO_EXCLUDE and FEATURES_TO_INCLUDE_ONLY. """ # Separate target - y = df['iter'].copy() + y = df[target_col].copy() # Drop non-feature columns - X = df.drop(columns=['iter', 'file']) + X = df.drop(columns=[target_col, 'file']) # Ensure all features are numeric non_numeric = X.select_dtypes(exclude=[np.number]).columns.tolist() @@ -954,13 +1100,26 @@ def main(): random_forest - Random Forest Regressor gradient_boosting - Gradient Boosting Regressor -Example: +Examples: + # Train a model (default target: iter) + python train_regressor.py data.feather --regressor xgboost --seed 42 + python train_regressor.py data.feather --regressor lightgbm --seed 42 + + # Train to predict time instead of iterations + python train_regressor.py data.feather --regressor xgboost --target time_ms --seed 42 + + # Check data quality before training + python train_regressor.py data.feather --regressor xgboost --check-data + + # Train with automatic removal of invalid rows + python train_regressor.py data.feather --regressor xgboost --drop-invalid-rows --seed 42 + + # Legacy pickle format python train_regressor.py data.pkl --regressor xgboost --seed 42 - python train_regressor.py data.pkl --regressor lightgbm --seed 42 """ ) - parser.add_argument('input_pkl', help='Input pickle file with log data') + parser.add_argument('input_pkl', help='Input data file (.feather or .pkl) with log data') parser.add_argument( '--regressor', '-r', required=True, @@ -1013,7 +1172,7 @@ def main(): parser.add_argument( '--early-stopping', type=int, - default=20, + default=0, metavar='N', help='Enable early stopping for tree models (default: 20 rounds, use 0 to disable)' ) @@ -1024,6 +1183,22 @@ def main(): metavar='THREADS', help='Export XGBoost/LightGBM model as optimized C source code with TL2cgen (includes branch annotation and quantization)' ) + parser.add_argument( + '--drop-invalid-rows', + action='store_true', + help='Drop rows with NaN or infinite values in target variable (instead of failing)' + ) + parser.add_argument( + '--check-data', + action='store_true', + help='Run data quality checks and exit (no training)' + ) + parser.add_argument( + '--target', + type=str, + default='iter', + help='Target column to predict (default: iter). Examples: iter, time_ms, iterations' + ) args = parser.parse_args() @@ -1034,37 +1209,93 @@ def main(): # Load data print(f"\nLoading data from: {args.input_pkl}") - df = load_data(args.input_pkl) + print(f"Target column: '{args.target}'") + df = load_data(args.input_pkl, target_col=args.target) print(f"Loaded {len(df)} entries with {len(df.columns)} columns") + # Report file format + ext = os.path.splitext(args.input_pkl)[1].lower() + if ext == '.feather': + print(f" Format: Apache Arrow/Feather (fast I/O)") + elif ext == '.pkl': + print(f" Format: Pickle (legacy)") + # Extract model name from input file (for prefixing generated C functions) model_name = os.path.splitext(os.path.basename(args.input_pkl))[0] + # Validate data quality + print("\nValidating data quality...") + is_valid, report = validate_data_quality(df, target_col=args.target, verbose=True) + + # If just checking data, exit here + if args.check_data: + if is_valid: + print("\n✅ Data quality check passed! Ready for training.") + return 0 + else: + print("\n❌ Data quality check failed! Fix issues before training.") + return 1 + + # Handle invalid data + if not is_valid: + if args.drop_invalid_rows: + print(f"\nDropping {len(report['rows_with_issues'])} rows with invalid data...") + df_clean = df.drop(index=report['rows_with_issues']) + df = df_clean.reset_index(drop=True) + print(f" Remaining: {len(df)} entries") + + # Re-validate + is_valid_after, _ = validate_data_quality(df, verbose=False) + if not is_valid_after: + print("❌ Error: Data still invalid after dropping rows. Check your data.") + return 1 + print(" ✅ Data is now valid") + else: + print("\n❌ Training aborted due to invalid data.") + print(" Use --drop-invalid-rows to automatically remove invalid rows, or") + print(" Use --check-data to just run validation without training") + return 1 + # If listing features, do that and exit if args.list_features: - features = list_available_features(df) + features = list_available_features(df, target_col=args.target) + + # Also list potential target columns + numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist() + potential_targets = [col for col in numeric_cols if col not in features] + print(f"\n{'='*70}") print(f"Available features in dataset ({len(features)} total):") print(f"{'='*70}") for i, feat in enumerate(features, 1): print(f" {i:3d}. {feat}") + + if potential_targets: + print(f"\n{'='*70}") + print(f"Potential target columns ({len(potential_targets)} total):") + print(f"{'='*70}") + for i, col in enumerate(potential_targets, 1): + marker = " (current)" if col == args.target else "" + print(f" {i:3d}. {col}{marker}") + print(f"\nTo exclude features, edit FEATURES_TO_EXCLUDE in the script:") print(f" {__file__}") print(f"\nTo use only specific features, edit FEATURES_TO_INCLUDE_ONLY") + print(f"\nTo change target column, use: --target ") return # Split data by files - stratify_by = 'iter' if args.stratify_split else None + stratify_by = args.target if args.stratify_split else None train_df, test_df = split_by_files(df, test_size=args.test_size, random_state=args.seed, stratify_by=stratify_by) # Prepare features - X_train, y_train, feature_names = prepare_features(train_df) - X_test, y_test, _ = prepare_features(test_df) + X_train, y_train, feature_names = prepare_features(train_df, target_col=args.target) + X_test, y_test, _ = prepare_features(test_df, target_col=args.target) print(f"\nFeatures: {len(feature_names)}") - print(f"Target: iter (predicting number of iterations)") + print(f"Target: {args.target} (prediction target)") # Create model print(f"\nTraining {args.regressor} regressor...") From 67808934c7b8e37fca9618d9a26e037241dcc514 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 31 Oct 2025 16:07:24 +0000 Subject: [PATCH 023/225] bump --- cpp/src/mip/diversity/diversity_manager.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index f45132819f..1b56bb6e52 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -434,7 +434,7 @@ solution_t diversity_manager_t::run_solver() lp_settings.save_state = true; lp_settings.concurrent_halt = &global_concurrent_halt; lp_settings.has_initial_primal = false; - lp_settings.iteration_limit = 100000 + i * 10000; + lp_settings.iteration_limit = 100 + i * 100; rmm::device_uvector lp_optimal_solution_copy(lp_optimal_solution.size(), problem_ptr->handle_ptr->get_stream()); auto lp_result = From 7474ff0a8fcb14e1dae3b4348cec1e039f4501fa Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 4 Nov 2025 14:14:42 +0000 Subject: [PATCH 024/225] work unit timers --- .../linear_programming/cuopt/run_mip.cpp | 21 ++- .../mip/solver_settings.hpp | 1 + cpp/src/mip/diversity/diversity_manager.cu | 34 +++-- cpp/src/mip/diversity/diversity_manager.cuh | 8 +- cpp/src/mip/diversity/population.cu | 4 +- cpp/src/mip/diversity/population.cuh | 4 +- .../recombiners/bound_prop_recombiner.cuh | 6 +- .../diversity/recombiners/fp_recombiner.cuh | 7 +- .../recombiners/line_segment_recombiner.cuh | 3 +- .../feasibility_pump/feasibility_pump.cu | 5 +- .../feasibility_pump/feasibility_pump.cuh | 4 +- .../line_segment_search.cu | 4 +- .../line_segment_search.cuh | 4 +- cpp/src/mip/local_search/local_search.cu | 42 +++--- cpp/src/mip/local_search/local_search.cuh | 22 +-- .../local_search/rounding/bounds_repair.cu | 2 +- .../local_search/rounding/bounds_repair.cuh | 4 +- .../local_search/rounding/constraint_prop.cu | 19 +-- .../local_search/rounding/constraint_prop.cuh | 8 +- .../rounding/lb_bounds_repair.cuh | 4 +- .../rounding/lb_constraint_prop.cu | 4 +- .../rounding/lb_constraint_prop.cuh | 8 +- cpp/src/mip/presolve/lb_probing_cache.cu | 4 +- cpp/src/mip/presolve/probing_cache.cu | 4 +- cpp/src/mip/presolve/probing_cache.cuh | 3 +- cpp/src/mip/solve.cu | 6 +- cpp/src/mip/solver.cu | 2 +- cpp/src/mip/solver.cuh | 6 +- cpp/src/utilities/work_limit_timer.hpp | 136 ++++++++++++++++++ cpp/tests/mip/diversity_test.cu | 14 +- cpp/tests/mip/local_search_test.cu | 4 +- cpp/tests/mip/mip_utils.cuh | 2 +- cpp/tests/mip/multi_probe_test.cu | 6 +- cpp/tests/mip/presolve_test.cu | 11 +- 34 files changed, 305 insertions(+), 111 deletions(-) create mode 100644 cpp/src/utilities/work_limit_timer.hpp diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 936a320813..da3168a569 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -157,7 +157,8 @@ int run_single_file(std::string file_path, int num_cpu_threads, bool write_log_file, bool log_to_console, - double time_limit) + double time_limit, + bool deterministic) { const raft::handle_t handle_{}; cuopt::linear_programming::mip_solver_settings_t settings; @@ -211,6 +212,7 @@ int run_single_file(std::string file_path, settings.heuristics_only = heuristics_only; settings.num_cpu_threads = num_cpu_threads; settings.log_to_console = log_to_console; + settings.deterministic = deterministic; settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; settings.presolve = true; @@ -263,7 +265,8 @@ void run_single_file_mp(std::string file_path, int num_cpu_threads, bool write_log_file, bool log_to_console, - double time_limit) + double time_limit, + bool deterministic) { std::cout << "running file " << file_path << " on gpu : " << device << std::endl; auto memory_resource = make_async(); @@ -278,7 +281,8 @@ void run_single_file_mp(std::string file_path, num_cpu_threads, write_log_file, log_to_console, - time_limit); + time_limit, + deterministic); // this is a bad design to communicate the result but better than adding complexity of IPC or // pipes exit(sol_found); @@ -361,6 +365,10 @@ int main(int argc, char* argv[]) .help("track allocations (t/f)") .default_value(std::string("f")); + program.add_argument("-d", "--determinism") + .help("enable deterministic mode (t/f)") + .default_value(std::string("f")); + // Parse arguments try { program.parse_args(argc, argv); @@ -389,6 +397,7 @@ int main(int argc, char* argv[]) bool log_to_console = program.get("--log-to-console")[0] == 't'; double memory_limit = program.get("--memory-limit"); bool track_allocations = program.get("--track-allocations")[0] == 't'; + bool deterministic = program.get("--determinism")[0] == 't'; if (num_cpu_threads < 0) { num_cpu_threads = omp_get_max_threads() / n_gpus; } @@ -476,7 +485,8 @@ int main(int argc, char* argv[]) num_cpu_threads, write_log_file, log_to_console, - time_limit); + time_limit, + deterministic); } else if (sys_pid < 0) { std::cerr << "Fork failed!" << std::endl; exit(1); @@ -516,7 +526,8 @@ int main(int argc, char* argv[]) num_cpu_threads, write_log_file, log_to_console, - time_limit); + time_limit, + deterministic); } return 0; diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 8e49f80135..07cc108b57 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -89,6 +89,7 @@ class mip_solver_settings_t { tolerances_t tolerances; f_t time_limit = std::numeric_limits::infinity(); + f_t work_limit = std::numeric_limits::infinity(); bool heuristics_only = false; i_t num_cpu_threads = -1; // -1 means use default number of threads in branch and bound bool log_to_console = true; diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 1b56bb6e52..328757e114 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -57,7 +57,7 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_tn_constraints, context.problem_ptr->handle_ptr->get_stream()), ls(context, lp_optimal_solution), - timer(diversity_config.default_time_limit), + timer(context.settings.deterministic, diversity_config.default_time_limit), bound_prop_recombiner(context, context.problem_ptr->n_variables, ls.constraint_prop, @@ -118,7 +118,7 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t bool diversity_manager_t::run_local_search(solution_t& solution, const weight_t& weights, - timer_t& timer, + work_limit_timer_t& timer, ls_config_t& ls_config) { raft::common::nvtx::range fun_scope("run_local_search"); @@ -189,8 +189,9 @@ bool diversity_manager_t::run_presolve(f_t time_limit) { raft::common::nvtx::range fun_scope("run_presolve"); CUOPT_LOG_INFO("Running presolve!"); - timer_t presolve_timer(time_limit); + work_limit_timer_t presolve_timer(context.settings.deterministic, time_limit); auto term_crit = ls.constraint_prop.bounds_update.solve(*problem_ptr); + presolve_timer.record_work(213762); if (ls.constraint_prop.bounds_update.infeas_constraints_count > 0) { stats.presolve_time = timer.elapsed_time(); return false; @@ -231,9 +232,10 @@ void diversity_manager_t::generate_quick_feasible_solution() // min 1 second, max 10 seconds const f_t generate_fast_solution_time = std::min(diversity_config.max_fast_sol_time, std::max(1., timer.remaining_time() / 20.)); - timer_t sol_timer(generate_fast_solution_time); + work_limit_timer_t sol_timer(context.settings.deterministic, generate_fast_solution_time); // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); + sol_timer.record_work(213762); if (solution.get_feasible()) { population.run_solution_callbacks(solution); initial_sol_vector.emplace_back(std::move(solution)); @@ -323,6 +325,7 @@ solution_t diversity_manager_t::run_solver() raft::common::nvtx::range fun_scope("run_solver"); diversity_config.fj_only_run = false; + if (context.settings.deterministic) { remaining_work_limit = context.settings.work_limit; } population.timer = timer; const f_t time_limit = timer.remaining_time(); @@ -360,10 +363,11 @@ solution_t diversity_manager_t::run_solver() const f_t max_time_on_probing = diversity_config.max_time_on_probing; f_t time_for_probing_cache = std::min(max_time_on_probing, time_limit * time_ratio_of_probing_cache); - timer_t probing_timer{time_for_probing_cache}; + work_limit_timer_t probing_timer{context.settings.deterministic, time_for_probing_cache}; if (check_b_b_preemption()) { return population.best_feasible(); } if (!diversity_config.fj_only_run) { compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); + probing_timer.record_work(213762); } if (check_b_b_preemption()) { return population.best_feasible(); } @@ -469,7 +473,7 @@ solution_t diversity_manager_t::run_solver() generate_solution(timer.remaining_time(), false); printf("=======================================================\n"); if (diversity_config.initial_solution_only) { return population.best_feasible(); } - if (timer.check_time_limit()) { + if (work_limit_reached()) { population.add_external_solutions_to_population(); return population.best_feasible(); } @@ -495,7 +499,7 @@ void diversity_manager_t::diversity_step(i_t max_iterations_without_im CUOPT_LOG_DEBUG("Population degenerated in diversity step"); return; } - if (timer.check_time_limit()) return; + if (work_limit_reached()) return; constexpr bool tournament = true; auto [sol1, sol2] = population.get_two_random(tournament); cuopt_assert(population.test_invariant(), ""); @@ -548,7 +552,7 @@ void diversity_manager_t::recombine_and_ls_with_all(solution_t::recombine_and_ls_with_all( population.add_solution(std::move(solution_t(sol))); } for (auto& sol : solutions) { - if (timer.check_time_limit()) { return; } + if (work_limit_reached()) { return; } solution_t ls_solution(sol); ls_config_t ls_config; run_local_search(ls_solution, population.weights, timer, ls_config); - if (timer.check_time_limit()) { return; } + if (work_limit_reached()) { return; } // TODO try if running LP with integers fixed makes it feasible if (ls_solution.get_feasible()) { CUOPT_LOG_DEBUG("LS searched solution feasible, running recombiners!"); @@ -806,6 +810,16 @@ void diversity_manager_t::set_simplex_solution(const std::vector& context.handle_ptr->sync_stream(); } +template +bool diversity_manager_t::work_limit_reached() +{ + if (context.settings.deterministic) { + return remaining_work_limit <= 0; + } else { + return work_limit_reached(); + } +} + #if MIP_INSTANTIATE_FLOAT template class diversity_manager_t; #endif diff --git a/cpp/src/mip/diversity/diversity_manager.cuh b/cpp/src/mip/diversity/diversity_manager.cuh index 5e94b1eccc..a1fbd64388 100644 --- a/cpp/src/mip/diversity/diversity_manager.cuh +++ b/cpp/src/mip/diversity/diversity_manager.cuh @@ -34,7 +34,7 @@ #include #include #include -#include +#include namespace cuopt::linear_programming::detail { @@ -72,8 +72,9 @@ class diversity_manager_t { solution_t& sol2); bool run_local_search(solution_t& solution, const weight_t& weights, - timer_t& timer, + work_limit_timer_t& timer, ls_config_t& ls_config); + bool work_limit_reached(); void set_simplex_solution(const std::vector& solution, const std::vector& dual_solution, @@ -87,7 +88,8 @@ class diversity_manager_t { rmm::device_uvector lp_dual_optimal_solution; std::atomic simplex_solution_exists{false}; local_search_t ls; - cuopt::timer_t timer; + cuopt::work_limit_timer_t timer; + f_t remaining_work_limit{std::numeric_limits::infinity()}; bound_prop_recombiner_t bound_prop_recombiner; fp_recombiner_t fp_recombiner; line_segment_recombiner_t line_segment_recombiner; diff --git a/cpp/src/mip/diversity/population.cu b/cpp/src/mip/diversity/population.cu index 03f658a0bf..56eedaf4ad 100644 --- a/cpp/src/mip/diversity/population.cu +++ b/cpp/src/mip/diversity/population.cu @@ -54,7 +54,7 @@ population_t::population_t(std::string const& name_, rng(cuopt::seed_generator::get_seed()), early_exit_primal_generation(false), population_hash_map(*problem_ptr), - timer(0) + timer(context.settings.deterministic, 0) { best_feasible_objective = std::numeric_limits::max(); } @@ -720,7 +720,7 @@ void population_t::start_threshold_adjustment() } template -void population_t::adjust_threshold(cuopt::timer_t timer) +void population_t::adjust_threshold(cuopt::work_limit_timer_t timer) { double time_ratio = (timer.elapsed_time() - population_start_time) / (timer.get_time_limit() - population_start_time); diff --git a/cpp/src/mip/diversity/population.cuh b/cpp/src/mip/diversity/population.cuh index deeca2f949..167e6690f3 100644 --- a/cpp/src/mip/diversity/population.cuh +++ b/cpp/src/mip/diversity/population.cuh @@ -131,7 +131,7 @@ class population_t { // updates qualities of each solution void update_qualities(); // adjusts the threshold of the population - void adjust_threshold(cuopt::timer_t timer); + void adjust_threshold(cuopt::work_limit_timer_t timer); /*! \param sol { Input solution } * \return { Index of the best solution similar to sol. If no similar is found we return * max_solutions. }*/ @@ -210,7 +210,7 @@ class population_t { std::atomic preempt_heuristic_solver_ = false; f_t best_feasible_objective = std::numeric_limits::max(); assignment_hash_map_t population_hash_map; - cuopt::timer_t timer; + cuopt::work_limit_timer_t timer; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index 9a4df67962..756942819c 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -187,7 +187,8 @@ class bound_prop_recombiner_t : public recombiner_t { if (guiding_solution.get_feasible() && !a.problem_ptr->expensive_to_fix_vars) { this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); - timer_t timer(bp_recombiner_config_t::bounds_prop_time_limit); + work_limit_timer_t timer(this->context.settings.deterministic, + bp_recombiner_config_t::bounds_prop_time_limit); rmm::device_uvector old_assignment(offspring.assignment, offspring.handle_ptr->get_stream()); offspring.handle_ptr->sync_stream(); @@ -237,7 +238,8 @@ class bound_prop_recombiner_t : public recombiner_t { } a.handle_ptr->sync_stream(); } else { - timer_t timer(bp_recombiner_config_t::bounds_prop_time_limit); + work_limit_timer_t timer(this->context.settings.deterministic, + bp_recombiner_config_t::bounds_prop_time_limit); get_probing_values_for_infeasible( guiding_solution, other_solution, offspring, probing_values, n_vars_from_other); probing_config.probing_values = host_copy(probing_values); diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index f52de0e7be..f4ca9d5c4f 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -122,9 +122,12 @@ class fp_recombiner_t : public recombiner_t { offspring.handle_ptr->sync_stream(); offspring.assignment = std::move(fixed_assignment); cuopt_func_call(offspring.test_variable_bounds(false)); - timer_t timer(fp_recombiner_config_t::fp_time_limit); + work_limit_timer_t timer(this->context.settings.deterministic, + fp_recombiner_config_t::fp_time_limit); if (this->context.settings.deterministic) { - timer = timer_t(std::numeric_limits::max()); // TODO should be global time limit + timer = work_limit_timer_t( + this->context.settings.deterministic, + std::numeric_limits::max()); // TODO should be global time limit } fp.timer = timer; fp.cycle_queue.reset(offspring); diff --git a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh index e8cf0d30e8..dce1f05cfa 100644 --- a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh @@ -86,7 +86,8 @@ class line_segment_recombiner_t : public recombiner_t { auto& other_solution = a.get_feasible() ? b : a; // copy the solution from A solution_t offspring(guiding_solution); - timer_t line_segment_timer{ls_recombiner_config_t::time_limit}; + work_limit_timer_t line_segment_timer{this->context.settings.deterministic, + ls_recombiner_config_t::time_limit}; // TODO after we have the conic combination, detect the lambda change // (i.e. the integral variables flip on line segment) i_t n_points_to_search = ls_recombiner_config_t::n_points_to_search; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index dfd153476d..d8472dae04 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -61,7 +61,7 @@ feasibility_pump_t::feasibility_pump_t( context.problem_ptr->handle_ptr->get_stream()), lp_optimal_solution(lp_optimal_solution_), rng(cuopt::seed_generator::get_seed()), - timer(20.) + timer(context.settings.deterministic, 20.) { thrust::fill(context.problem_ptr->handle_ptr->get_thrust_policy(), last_projection.begin(), @@ -271,7 +271,8 @@ bool feasibility_pump_t::round(solution_t& solution) { bool result; CUOPT_LOG_DEBUG("Rounding the point"); - timer_t bounds_prop_timer(std::max(0.05, std::min(0.5, timer.remaining_time() / 10.))); + work_limit_timer_t bounds_prop_timer(context.settings.deterministic, + std::max(0.05, std::min(0.5, timer.remaining_time() / 10.))); const f_t lp_run_time_after_feasible = 0.; bool old_var = constraint_prop.round_all_vars; f_t old_time = constraint_prop.max_time_for_bounds_prop; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh index f4f12808b1..9888f5a2ce 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cuh @@ -136,7 +136,7 @@ class feasibility_pump_t { bool check_distance_cycle(solution_t& solution); void reset(); void resize_vectors(problem_t& problem, const raft::handle_t* handle_ptr); - bool random_round_with_fj(solution_t& solution, timer_t& round_timer); + bool random_round_with_fj(solution_t& solution, work_limit_timer_t& round_timer); bool round_multiple_points(solution_t& solution); void relax_general_integers(solution_t& solution); void revert_relaxation(solution_t& solution); @@ -163,7 +163,7 @@ class feasibility_pump_t { f_t proj_begin; i_t n_fj_single_descents; i_t max_n_of_integers = 0; - cuopt::timer_t timer; + cuopt::work_limit_timer_t timer; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu index 18c529fc1f..6371b7bd5e 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu @@ -140,7 +140,7 @@ bool line_segment_search_t::search_line_segment( const rmm::device_uvector& point_2, const rmm::device_uvector& delta_vector, bool is_feasibility_run, - cuopt::timer_t& timer) + cuopt::work_limit_timer_t& timer) { CUOPT_LOG_DEBUG("Running line segment search with a given delta vector"); cuopt_assert(point_1.size() == point_2.size(), "size mismatch"); @@ -286,7 +286,7 @@ bool line_segment_search_t::search_line_segment(solution_t& const rmm::device_uvector& point_1, const rmm::device_uvector& point_2, bool is_feasibility_run, - cuopt::timer_t& timer) + cuopt::work_limit_timer_t& timer) { CUOPT_LOG_DEBUG("Running line segment search"); cuopt_assert(point_1.size() == point_2.size(), "size mismatch"); diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cuh b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cuh index 3d89d0e83e..f3cc2c65c2 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cuh +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cuh @@ -43,14 +43,14 @@ class line_segment_search_t { const rmm::device_uvector& point_1, const rmm::device_uvector& point_2, bool is_feasibility_run, - cuopt::timer_t& timer); + cuopt::work_limit_timer_t& timer); bool search_line_segment(solution_t& solution, const rmm::device_uvector& point_1, const rmm::device_uvector& point_2, const rmm::device_uvector& delta_vector, bool is_feasibility_run, - cuopt::timer_t& timer); + cuopt::work_limit_timer_t& timer); void save_solution_if_better(solution_t& solution, const rmm::device_uvector& point_1, diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index f3f8888b24..0f2be589aa 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include @@ -234,7 +234,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, { if (time_limit == 0.) return solution.get_feasible(); - timer_t timer(time_limit); + work_limit_timer_t timer(context.settings.deterministic, time_limit); auto h_weights = cuopt::host_copy(in_fj.cstr_weights, solution.handle_ptr->get_stream()); auto h_objective_weight = in_fj.objective_weight.value(solution.handle_ptr->get_stream()); @@ -316,7 +316,8 @@ bool local_search_t::do_fj_solve(solution_t& solution, } template -void local_search_t::generate_fast_solution(solution_t& solution, timer_t timer) +void local_search_t::generate_fast_solution(solution_t& solution, + work_limit_timer_t timer) { CUOPT_LOG_DEBUG("Running FJ fast sol"); thrust::fill(solution.handle_ptr->get_thrust_policy(), @@ -332,9 +333,11 @@ void local_search_t::generate_fast_solution(solution_t& solu // CHANGE fj.settings.time_limit = std::numeric_limits::infinity(); while (!timer.check_time_limit()) { - timer_t constr_prop_timer = timer_t(std::min(timer.remaining_time(), 2.)); + work_limit_timer_t constr_prop_timer = + work_limit_timer_t(context.settings.deterministic, std::min(timer.remaining_time(), 2.)); // do constraint prop on lp optimal solution constraint_prop.apply_round(solution, 1., constr_prop_timer); + constr_prop_timer.record_work(213762); if (solution.compute_feasibility()) { return; } if (timer.check_time_limit()) { return; }; f_t time_limit = std::min(3., timer.remaining_time()); @@ -349,7 +352,7 @@ void local_search_t::generate_fast_solution(solution_t& solu template bool local_search_t::run_local_search(solution_t& solution, const weight_t& weights, - timer_t timer, + work_limit_timer_t timer, const ls_config_t& ls_config) { raft::common::nvtx::range fun_scope("local search"); @@ -359,10 +362,10 @@ bool local_search_t::run_local_search(solution_t& solution, if (!solution.get_feasible()) { if (ls_config.at_least_one_parent_feasible) { fj_settings.time_limit = 0.5; - timer = timer_t(fj_settings.time_limit); + timer = work_limit_timer_t(context.settings.deterministic, fj_settings.time_limit); } else { fj_settings.time_limit = 0.25; - timer = timer_t(fj_settings.time_limit); + timer = work_limit_timer_t(context.settings.deterministic, fj_settings.time_limit); } } else { fj_settings.time_limit = std::min(1., timer.remaining_time()); @@ -393,7 +396,7 @@ bool local_search_t::run_local_search(solution_t& solution, template bool local_search_t::run_fj_until_timer(solution_t& solution, const weight_t& weights, - timer_t timer) + work_limit_timer_t timer) { CUOPT_LOG_DEBUG("Running FJ until timer"); bool is_feasible; @@ -412,7 +415,7 @@ bool local_search_t::run_fj_until_timer(solution_t& solution template bool local_search_t::run_fj_annealing(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, const ls_config_t& ls_config) { raft::common::nvtx::range fun_scope("run_fj_annealing"); @@ -442,7 +445,7 @@ bool local_search_t::run_fj_annealing(solution_t& solution, template bool local_search_t::run_fj_line_segment(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, const ls_config_t& ls_config) { raft::common::nvtx::range fun_scope("run_fj_line_segment"); @@ -465,7 +468,7 @@ bool local_search_t::run_fj_line_segment(solution_t& solutio template bool local_search_t::check_fj_on_lp_optimal(solution_t& solution, bool perturb, - timer_t timer) + work_limit_timer_t timer) { raft::common::nvtx::range fun_scope("check_fj_on_lp_optimal"); if (lp_optimal_exists) { @@ -482,9 +485,11 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu } cuopt_func_call(solution.test_variable_bounds(false)); f_t lp_run_time_after_feasible = std::min(1., timer.remaining_time()); - timer_t bounds_prop_timer = timer_t(std::min(timer.remaining_time(), 10.)); + work_limit_timer_t bounds_prop_timer = + work_limit_timer_t(context.settings.deterministic, std::min(timer.remaining_time(), 10.)); bool is_feasible = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); + bounds_prop_timer.record_work(213762); if (!is_feasible) { f_t lp_run_time = 2.; // CHANGE @@ -514,7 +519,8 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu } template -bool local_search_t::run_fj_on_zero(solution_t& solution, timer_t timer) +bool local_search_t::run_fj_on_zero(solution_t& solution, + work_limit_timer_t timer) { raft::common::nvtx::range fun_scope("run_fj_on_zero"); thrust::fill(solution.handle_ptr->get_thrust_policy(), @@ -533,7 +539,7 @@ bool local_search_t::run_fj_on_zero(solution_t& solution, ti template bool local_search_t::run_staged_fp(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, population_t* population_ptr) { raft::common::nvtx::range fun_scope("run_staged_fp"); @@ -560,7 +566,7 @@ bool local_search_t::run_staged_fp(solution_t& solution, } CUOPT_LOG_DEBUG("Running staged FP from beginning it %d", i); fp.relax_general_integers(solution); - timer_t binary_timer(timer.remaining_time() / 3); + work_limit_timer_t binary_timer(context.settings.deterministic, timer.remaining_time() / 3); i_t binary_it_counter = 0; for (; binary_it_counter < 100; ++binary_it_counter) { if (population_ptr->preempt_heuristic_solver_.load()) { @@ -726,7 +732,7 @@ void local_search_t::reset_alpha_and_run_recombiners( template bool local_search_t::run_fp(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, population_t* population_ptr, i_t n_fp_iterations) { @@ -738,7 +744,7 @@ bool local_search_t::run_fp(solution_t& solution, is_feasible ? solution.get_objective() : std::numeric_limits::max(); rmm::device_uvector best_solution(solution.assignment, solution.handle_ptr->get_stream()); problem_t* old_problem_ptr = solution.problem_ptr; - fp.timer = timer_t(timer.remaining_time()); + fp.timer = work_limit_timer_t(context.settings.deterministic, timer.remaining_time()); // if it has not been initialized yet, create a new problem and move it to the cut problem if (!problem_with_objective_cut.cutting_plane_added) { problem_with_objective_cut = std::move(problem_t(*old_problem_ptr)); @@ -839,7 +845,7 @@ bool local_search_t::generate_solution(solution_t& solution, { raft::common::nvtx::range fun_scope("generate_solution"); cuopt_assert(population_ptr != nullptr, "Population pointer must not be null"); - timer_t timer(time_limit); + work_limit_timer_t timer(context.settings.deterministic, time_limit); auto n_vars = solution.problem_ptr->n_variables; auto n_binary_vars = solution.problem_ptr->get_n_binary_variables(); auto n_integer_vars = solution.problem_ptr->n_integer_vars; diff --git a/cpp/src/mip/local_search/local_search.cuh b/cpp/src/mip/local_search/local_search.cuh index ff914a876f..4325a52990 100644 --- a/cpp/src/mip/local_search/local_search.cuh +++ b/cpp/src/mip/local_search/local_search.cuh @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include @@ -85,31 +85,33 @@ class local_search_t { void start_cpufj_scratch_threads(population_t& population); void start_cpufj_lptopt_scratch_threads(population_t& population); void stop_cpufj_scratch_threads(); - void generate_fast_solution(solution_t& solution, timer_t timer); + void generate_fast_solution(solution_t& solution, work_limit_timer_t timer); bool generate_solution(solution_t& solution, bool perturb, population_t* population_ptr, f_t time_limit = 300.); bool run_fj_until_timer(solution_t& solution, const weight_t& weights, - timer_t timer); + work_limit_timer_t timer); bool run_local_search(solution_t& solution, const weight_t& weights, - timer_t timer, + work_limit_timer_t timer, const ls_config_t& ls_config); bool run_fj_annealing(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, const ls_config_t& ls_config); bool run_fj_line_segment(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, const ls_config_t& ls_config); - bool run_fj_on_zero(solution_t& solution, timer_t timer); - bool check_fj_on_lp_optimal(solution_t& solution, bool perturb, timer_t timer); + bool run_fj_on_zero(solution_t& solution, work_limit_timer_t timer); + bool check_fj_on_lp_optimal(solution_t& solution, + bool perturb, + work_limit_timer_t timer); bool run_staged_fp(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, population_t* population_ptr); bool run_fp(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, population_t* population_ptr = nullptr, i_t n_fp_iterations = 1000000); void resize_vectors(problem_t& problem, const raft::handle_t* handle_ptr); diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index 664fd6d4e0..b6587641bb 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -387,7 +387,7 @@ void bounds_repair_t::apply_move(problem_t& problem, template bool bounds_repair_t::repair_problem(problem_t& problem, problem_t& original_problem, - timer_t timer_, + work_limit_timer_t timer_, const raft::handle_t* handle_ptr_) { CUOPT_LOG_DEBUG("Running bounds repair"); diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cuh b/cpp/src/mip/local_search/rounding/bounds_repair.cuh index 8cc392c0b9..b86f41d17f 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cuh +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cuh @@ -130,7 +130,7 @@ class bounds_repair_t { void compute_damages(problem_t& problem, i_t n_candidates); bool repair_problem(problem_t& problem, problem_t& original_problem, - timer_t timer_, + work_limit_timer_t timer_, const raft::handle_t* handle_ptr_); void apply_move(problem_t& problem, problem_t& original_problem, @@ -154,7 +154,7 @@ class bounds_repair_t { i_t h_n_violated_cstr; const raft::handle_t* handle_ptr; std::mt19937 gen; - timer_t timer{0.}; + work_limit_timer_t timer; std::vector cycle_vector; i_t cycle_write_pos = 0; }; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 26aa923ebb..3c118becb7 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -770,7 +770,7 @@ void constraint_prop_t::restore_original_bounds_on_unfixed( template bool constraint_prop_t::run_repair_procedure(problem_t& problem, problem_t& original_problem, - timer_t& timer, + work_limit_timer_t& timer, const raft::handle_t* handle_ptr) { CUOPT_LOG_DEBUG("Running repair procedure"); @@ -784,7 +784,8 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob i_t n_of_repairs_needed_for_feasible = 0; i_t iter_limit = std::numeric_limits::max(); if (this->context.settings.deterministic) { - timer = timer_t(std::numeric_limits::infinity()); + timer = + work_limit_timer_t(context.settings.deterministic, std::numeric_limits::infinity()); iter_limit = 100; } do { @@ -864,7 +865,7 @@ bool constraint_prop_t::find_integer( solution_t& sol, solution_t& orig_sol, f_t lp_run_time_after_feasible, - timer_t& timer, + work_limit_timer_t& timer, std::optional>> probing_config) { using crit_t = termination_criterion_t; @@ -875,7 +876,8 @@ bool constraint_prop_t::find_integer( // CHANGE if (this->context.settings.deterministic) { - timer = timer_t(std::numeric_limits::infinity()); + timer = + work_limit_timer_t(context.settings.deterministic, std::numeric_limits::infinity()); } lb_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); @@ -1014,7 +1016,7 @@ bool constraint_prop_t::find_integer( if (!(n_failed_repair_iterations >= max_n_failed_repair_iterations) && rounding_ii && !timeout_happened) { // timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; - timer_t repair_timer{timer.remaining_time() / 5}; + work_limit_timer_t repair_timer(context.settings.deterministic, timer.remaining_time() / 5); save_bounds(sol); // update bounds and run repair procedure bool bounds_repaired = @@ -1105,7 +1107,7 @@ template bool constraint_prop_t::apply_round( solution_t& sol, f_t lp_run_time_after_feasible, - timer_t& timer, + work_limit_timer_t& timer, std::optional>> probing_config) { raft::common::nvtx::range fun_scope("constraint prop round"); @@ -1135,9 +1137,10 @@ bool constraint_prop_t::apply_round( lp_run_time_after_feasible); // === CONSTRAINT PROP PREDICTOR FEATURES - END === - max_timer = timer_t{max_time_for_bounds_prop}; + max_timer = work_limit_timer_t{context.settings.deterministic, max_time_for_bounds_prop}; if (this->context.settings.deterministic) { - max_timer = timer_t(std::numeric_limits::infinity()); + max_timer = + work_limit_timer_t(context.settings.deterministic, std::numeric_limits::infinity()); } if (check_brute_force_rounding(sol)) { auto cp_end_time = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cuh b/cpp/src/mip/local_search/rounding/constraint_prop.cuh index 591f25f36b..a867b2aa93 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cuh +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cuh @@ -52,7 +52,7 @@ struct constraint_prop_t { constraint_prop_t(mip_solver_context_t& context); bool apply_round(solution_t& sol, f_t lp_run_time_after_feasible, - timer_t& timer, + work_limit_timer_t& timer, std::optional>> probing_config = std::nullopt); void sort_by_implied_slack_consumption(solution_t& sol, @@ -65,7 +65,7 @@ struct constraint_prop_t { bool find_integer(solution_t& sol, solution_t& orig_sol, f_t lp_run_time_after_feasible, - timer_t& timer, + work_limit_timer_t& timer, std::optional>> probing_config = std::nullopt); void find_set_integer_vars(solution_t& sol, rmm::device_uvector& set_vars); @@ -130,7 +130,7 @@ struct constraint_prop_t { const raft::handle_t* handle_ptr); bool run_repair_procedure(problem_t& problem, problem_t& original_problem, - timer_t& timer, + work_limit_timer_t& timer, const raft::handle_t* handle_ptr); bool handle_fixed_vars( solution_t& sol, @@ -158,7 +158,7 @@ struct constraint_prop_t { i_t bounds_prop_interval = 1; i_t n_iter_in_recovery = 0; i_t max_n_failed_repair_iterations = 1; - timer_t max_timer{0.}; + work_limit_timer_t max_timer; bool use_probing_cache = true; static repair_stats_t repair_stats; bool single_rounding_only = false; diff --git a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cuh b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cuh index 5a59fae7e5..9be1ddee38 100644 --- a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cuh +++ b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cuh @@ -68,7 +68,7 @@ class lb_bounds_repair_t { bool repair_problem(load_balanced_problem_t* problem, load_balanced_bounds_presolve_t& lb_bound_presolve, problem_t& original_problem, - timer_t timer_, + work_limit_timer_t timer_, const raft::handle_t* handle_ptr_); void apply_move(load_balanced_problem_t* problem, problem_t& original_problem, @@ -92,7 +92,7 @@ class lb_bounds_repair_t { i_t h_n_violated_cstr; const raft::handle_t* handle_ptr; std::mt19937 gen; - timer_t timer{0.}; + work_limit_timer_t timer; std::vector cycle_vector; i_t cycle_write_pos = 0; }; diff --git a/cpp/src/mip/local_search/rounding/lb_constraint_prop.cu b/cpp/src/mip/local_search/rounding/lb_constraint_prop.cu index be74611063..acf28b961d 100644 --- a/cpp/src/mip/local_search/rounding/lb_constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/lb_constraint_prop.cu @@ -711,14 +711,14 @@ template bool lb_constraint_prop_t::apply_round( solution_t& sol, f_t lp_run_time_after_feasible, - timer_t& timer, + work_limit_timer_t& timer, std::optional>> probing_candidates) { raft::common::nvtx::range fun_scope("constraint prop round"); // this is second timer that can continue but without recovery mode const f_t max_time_for_bounds_prop = 5.; - max_timer = timer_t{max_time_for_bounds_prop}; + max_timer = work_limit_timer_t{context.settings.deterministic, max_time_for_bounds_prop}; if (check_brute_force_rounding(sol)) { return true; } recovery_mode = false; rounding_ii = false; diff --git a/cpp/src/mip/local_search/rounding/lb_constraint_prop.cuh b/cpp/src/mip/local_search/rounding/lb_constraint_prop.cuh index e820cf3fe9..e01afad714 100644 --- a/cpp/src/mip/local_search/rounding/lb_constraint_prop.cuh +++ b/cpp/src/mip/local_search/rounding/lb_constraint_prop.cuh @@ -33,7 +33,7 @@ struct lb_constraint_prop_t { bool apply_round( solution_t& sol, f_t lp_run_time_after_feasible, - timer_t& timer, + work_limit_timer_t& timer, std::optional>> probing_candidates = std::nullopt); void sort_by_implied_slack_consumption( problem_t& original_problem, @@ -50,7 +50,7 @@ struct lb_constraint_prop_t { load_balanced_bounds_presolve_t& lb_bounds_update, solution_t& orig_sol, f_t lp_run_time_after_feasible, - timer_t& timer, + work_limit_timer_t& timer, std::optional>> probing_candidates); std::tuple probing_values( load_balanced_bounds_presolve_t& lb_bounds_update, @@ -93,7 +93,7 @@ struct lb_constraint_prop_t { bool run_repair_procedure(load_balanced_problem_t* problem, load_balanced_bounds_presolve_t& lb_bounds_update, problem_t& original_problem, - timer_t& timer, + work_limit_timer_t& timer, const raft::handle_t* handle_ptr); mip_solver_context_t& context; @@ -110,7 +110,7 @@ struct lb_constraint_prop_t { bool rounding_ii = false; i_t bounds_prop_interval = 1; i_t n_iter_in_recovery = 0; - timer_t max_timer{0.}; + work_limit_timer_t max_timer; bool use_probing_cache = true; size_t repair_attempts = 0; diff --git a/cpp/src/mip/presolve/lb_probing_cache.cu b/cpp/src/mip/presolve/lb_probing_cache.cu index 00c28d3450..e26e03284c 100644 --- a/cpp/src/mip/presolve/lb_probing_cache.cu +++ b/cpp/src/mip/presolve/lb_probing_cache.cu @@ -319,7 +319,7 @@ inline std::vector compute_prioritized_integer_indices( template void compute_probing_cache(load_balanced_bounds_presolve_t& bound_presolve, load_balanced_problem_t& problem, - timer_t timer) + work_limit_timer_t timer) { // we dont want to compute the probing cache for all variables for time and computation resources auto priority_indices = compute_prioritized_integer_indices(bound_presolve, problem); @@ -409,7 +409,7 @@ void compute_probing_cache(load_balanced_bounds_presolve_t& bound_pres template void compute_probing_cache( \ load_balanced_bounds_presolve_t & bound_presolve, \ load_balanced_problem_t & problem, \ - timer_t timer); \ + work_limit_timer_t timer); \ template class lb_probing_cache_t; #if MIP_INSTANTIATE_FLOAT diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index 044b141559..fc7bed0e8b 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -465,7 +465,7 @@ void compute_cache_for_var(i_t var_idx, template void compute_probing_cache(bound_presolve_t& bound_presolve, problem_t& problem, - timer_t timer) + work_limit_timer_t timer) { raft::common::nvtx::range fun_scope("compute_probing_cache"); // we dont want to compute the probing cache for all variables for time and computation resources @@ -530,7 +530,7 @@ void compute_probing_cache(bound_presolve_t& bound_presolve, #define INSTANTIATE(F_TYPE) \ template void compute_probing_cache(bound_presolve_t & bound_presolve, \ problem_t & problem, \ - timer_t timer); \ + work_limit_timer_t timer); \ template class probing_cache_t; #if MIP_INSTANTIATE_FLOAT diff --git a/cpp/src/mip/presolve/probing_cache.cuh b/cpp/src/mip/presolve/probing_cache.cuh index 755c18b0bb..f8c5e49512 100644 --- a/cpp/src/mip/presolve/probing_cache.cuh +++ b/cpp/src/mip/presolve/probing_cache.cuh @@ -22,6 +22,7 @@ #include #include +#include namespace cuopt::linear_programming::detail { @@ -127,6 +128,6 @@ class lb_probing_cache_t { template void compute_probing_cache(bound_presolve_t& bound_presolve, problem_t& problem, - timer_t timer); + work_limit_timer_t timer); } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 8f979e683f..0827068a26 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -31,8 +31,8 @@ #include #include #include -#include #include +#include #include #include @@ -71,7 +71,7 @@ static void setup_device_symbols(rmm::cuda_stream_view stream_view) template mip_solution_t run_mip(detail::problem_t& problem, mip_solver_settings_t const& settings, - cuopt::timer_t& timer) + cuopt::work_limit_timer_t& timer) { raft::common::nvtx::range fun_scope("run_mip"); auto constexpr const running_mip = true; @@ -194,7 +194,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, op_problem.get_handle_ptr()->get_stream()); } - auto timer = cuopt::timer_t(time_limit); + auto timer = cuopt::work_limit_timer_t(settings.deterministic, time_limit); double presolve_time = 0.0; std::unique_ptr> presolver; diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index 0114882b03..607fe6c9f6 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -53,7 +53,7 @@ template mip_solver_t::mip_solver_t(const problem_t& op_problem, const mip_solver_settings_t& solver_settings, pdlp_initial_scaling_strategy_t& scaling, - timer_t timer) + work_limit_timer_t timer) : op_problem_(op_problem), solver_settings_(solver_settings), context(op_problem.handle_ptr, diff --git a/cpp/src/mip/solver.cuh b/cpp/src/mip/solver.cuh index beff11d9b4..012c5fdfa6 100644 --- a/cpp/src/mip/solver.cuh +++ b/cpp/src/mip/solver.cuh @@ -20,7 +20,7 @@ #include #include #include -#include +#include #pragma once namespace cuopt::linear_programming::detail { @@ -31,7 +31,7 @@ class mip_solver_t { explicit mip_solver_t(const problem_t& op_problem, const mip_solver_settings_t& solver_settings, pdlp_initial_scaling_strategy_t& scaling, - timer_t timer); + work_limit_timer_t timer); solution_t run_solver(); solver_stats_t& get_solver_stats() { return context.stats; } @@ -40,7 +40,7 @@ class mip_solver_t { // reference to the original problem const problem_t& op_problem_; const mip_solver_settings_t& solver_settings_; - timer_t timer_; + work_limit_timer_t timer_; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp new file mode 100644 index 0000000000..dad1446aa9 --- /dev/null +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -0,0 +1,136 @@ +/* + * 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 +#include +#include "timer.hpp" + +namespace cuopt { + +// In determinism mode, relies on a work limit accumulator; otherwise rely on a timer +// in non-determinism mode: 1s = 1wu +class work_limit_timer_t { + public: + work_limit_timer_t() : deterministic(false), work_limit(0), timer(0) {} + work_limit_timer_t(bool deterministic, double work_limit_) + : deterministic(deterministic), work_limit(work_limit_), timer(work_limit_) + { + } + + bool check_limit(const char* caller = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) const noexcept + { + if (deterministic) { + bool finished_now = work_total >= work_limit; + if (finished_now && !finished) { + finished = true; + double actual_elapsed_time = timer.elapsed_time(); + // 10% timing error + if (abs(actual_elapsed_time - work_limit) / work_limit > 0.10) { + CUOPT_LOG_ERROR( + "%s:%d: %s(): Work limit timer finished with a large discrepancy: %fs for %fwu", + file, + line, + caller, + actual_elapsed_time, + work_limit); + } + } + return finished; + } else { + return timer.check_time_limit(); + } + } + + // in determinism mode, add the work units to the work limit accumulator + void record_work(double work_units) + { + if (deterministic) { work_total += work_units; } + } + + double remaining_units() const noexcept + { + if (deterministic) { + return work_limit - work_total; + } else { + return timer.remaining_time(); + } + } + + double remaining_time() const noexcept { return remaining_units(); } + + double elapsed_time() const noexcept + { + if (deterministic) { + return work_total; + } else { + return timer.elapsed_time(); + } + } + + bool check_time_limit(const char* caller = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) const noexcept + { + return check_limit(caller, file, line); + } + + bool check_half_time() const noexcept + { + if (deterministic) { + return work_total >= work_limit / 2; + } else { + return timer.check_half_time(); + } + } + + double clamp_remaining_time(double desired_time) const noexcept + { + return std::min(desired_time, remaining_time()); + } + + double get_time_limit() const noexcept + { + if (deterministic) { + return work_limit; + } else { + return timer.get_time_limit(); + } + } + + void print_debug(std::string msg) const + { + if (deterministic) { + printf("%s work_limit: %f remaining_work: %f elapsed_work: %f \n", + msg.c_str(), + work_limit, + remaining_time(), + elapsed_time()); + } else { + timer.print_debug(msg); + } + } + + timer_t timer; + double work_total{}; + double work_limit{}; + mutable bool finished{false}; + bool deterministic{false}; +}; +} // namespace cuopt diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index b891aa87f5..15f2877748 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -102,15 +102,15 @@ static uint32_t test_full_run_determinism(std::string path, auto settings = mip_solver_settings_t{}; settings.time_limit = 3000.; + settings.work_limit = 10; // about 10 seconds of runtime settings.deterministic = true; settings.heuristics_only = true; - auto timer = cuopt::timer_t(3000); + auto timer = cuopt::work_limit_timer_t(settings.deterministic, 3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - diversity_manager.timer = timer_t(60000); - diversity_manager.diversity_config.n_fp_iterations = 3; + diversity_manager.timer = work_limit_timer_t(settings.deterministic, 60000); diversity_manager.run_solver(); std::vector hashes; @@ -159,12 +159,12 @@ static uint32_t test_initial_solution_determinism(std::string path, settings.time_limit = 3000.; settings.deterministic = true; settings.heuristics_only = true; - auto timer = cuopt::timer_t(3000); + auto timer = cuopt::work_limit_timer_t(settings.deterministic, 3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - diversity_manager.timer = timer_t(60000); + diversity_manager.timer = work_limit_timer_t(settings.deterministic, 60000); diversity_manager.diversity_config.initial_solution_only = true; diversity_manager.run_solver(); @@ -214,12 +214,12 @@ static uint32_t test_recombiners_determinism(std::string path, settings.time_limit = 3000.; settings.deterministic = true; settings.heuristics_only = true; - auto timer = cuopt::timer_t(3000); + auto timer = cuopt::work_limit_timer_t(settings.deterministic, 3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - diversity_manager.timer = timer_t(60000); + diversity_manager.timer = work_limit_timer_t(settings.deterministic, 60000); diversity_manager.diversity_config.dry_run = true; diversity_manager.run_solver(); diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index ce0ee5216c..36e71481ce 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -113,7 +113,7 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) auto settings = mip_solver_settings_t{}; settings.time_limit = 30.; settings.deterministic = true; - auto timer = cuopt::timer_t(30); + auto timer = cuopt::work_limit_timer_t(settings.deterministic, 30); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); @@ -149,7 +149,7 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) printf("LP optimal hash: 0x%x\n", detail::compute_hash(lp_optimal_solution)); printf("running mode: %d\n", mode); - local_search.fp.timer = timer_t{6000}; + local_search.fp.timer = work_limit_timer_t(settings.deterministic, 6000); detail::ls_config_t ls_config{}; diff --git a/cpp/tests/mip/mip_utils.cuh b/cpp/tests/mip/mip_utils.cuh index 904f461684..3a8d6e48b5 100644 --- a/cpp/tests/mip/mip_utils.cuh +++ b/cpp/tests/mip/mip_utils.cuh @@ -165,7 +165,7 @@ static fj_state_t run_fj(detail::problem_t& problem, auto settings = mip_solver_settings_t{}; settings.time_limit = 30.; - auto timer = cuopt::timer_t(30); + auto timer = cuopt::work_limit_timer_t(settings.deterministic, 30); detail::mip_solver_t solver(problem, settings, scaling, timer); detail::solution_t solution(*solver.context.problem_ptr); diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index dc7449dc1d..e15f23ffd1 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -170,7 +170,11 @@ uint32_t test_multi_probe(std::string path, unsigned long seed = std::random_dev problem.reverse_constraints, nullptr, true); - detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); + detail::mip_solver_t solver( + problem, + default_settings, + scaling, + cuopt::work_limit_timer_t(default_settings.deterministic, 0)); detail::bound_presolve_t bnd_prb_0(solver.context); detail::bound_presolve_t bnd_prb_1(solver.context); detail::multi_probe_t multi_probe_prs(solver.context); diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index e08f283cb0..55ce8851cc 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -131,11 +131,18 @@ uint32_t test_probing_cache_determinism(std::string path, problem.reverse_constraints, nullptr, true); - detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); + detail::mip_solver_t solver( + problem, + default_settings, + scaling, + cuopt::work_limit_timer_t(default_settings.deterministic, 0)); detail::bound_presolve_t bnd_prb(solver.context); // rely on the iteration limit - compute_probing_cache(bnd_prb, problem, timer_t(std::numeric_limits::max())); + compute_probing_cache( + bnd_prb, + problem, + work_limit_timer_t(default_settings.deterministic, std::numeric_limits::max())); std::vector, 2>>> cached_values( bnd_prb.probing_cache.probing_cache.begin(), bnd_prb.probing_cache.probing_cache.end()); std::sort(cached_values.begin(), cached_values.end(), [](const auto& a, const auto& b) { From c6e9d1573460ec63b90bb76832f256e0076f7c87 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 4 Nov 2025 14:15:51 +0000 Subject: [PATCH 025/225] linear estimation of PDLP work units --- cpp/src/mip/diversity/lns/rins.cu | 342 ++++++++++++++++++++++++++ cpp/src/mip/relaxed_lp/relaxed_lp.cu | 73 +++--- cpp/src/mip/relaxed_lp/relaxed_lp.cuh | 1 + scripts/train_regressor.py | 18 +- 4 files changed, 400 insertions(+), 34 deletions(-) create mode 100644 cpp/src/mip/diversity/lns/rins.cu diff --git a/cpp/src/mip/diversity/lns/rins.cu b/cpp/src/mip/diversity/lns/rins.cu new file mode 100644 index 0000000000..df149c38b6 --- /dev/null +++ b/cpp/src/mip/diversity/lns/rins.cu @@ -0,0 +1,342 @@ +/* + * 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 + +namespace cuopt::linear_programming::detail { + +template +rins_t::rins_t(mip_solver_context_t& context_, + diversity_manager_t& dm_, + rins_settings_t settings_) + : context(context_), problem_ptr(context.problem_ptr), dm(dm_), settings(settings_) +{ + fixrate = settings.default_fixrate; + time_limit = settings.default_time_limit; +} + +template +rins_thread_t::~rins_thread_t() +{ + this->request_termination(); +} + +template +void rins_thread_t::run_worker() +{ + raft::common::nvtx::range fun_scope("Running RINS"); + rins_ptr->run_rins(); +} + +template +void rins_t::new_best_incumbent_callback(const std::vector& solution) +{ + node_count_at_last_improvement = node_count.load(); +} + +template +void rins_t::node_callback(const std::vector& solution, f_t objective) +{ + if (!enabled) return; + + node_count++; + + if (node_count - node_count_at_last_improvement < settings.nodes_after_later_improvement) return; + + if (node_count - node_count_at_last_rins > settings.node_freq) { + // opportunistic early test w/ atomic to avoid having to take the lock + if (!rins_thread->cpu_thread_done) return; + std::lock_guard lock(rins_mutex); + if (rins_thread->cpu_thread_done && dm.population.current_size() > 0 && + dm.population.is_feasible()) { + lp_optimal_solution = solution; + rins_thread->start_cpu_solver(); + } + } +} + +template +void rins_t::enable() +{ + rins_thread = std::make_unique>(); + rins_thread->rins_ptr = this; + seed = cuopt::seed_generator::get_seed(); + problem_copy = std::make_unique>(*problem_ptr); + problem_copy->handle_ptr = &rins_handle; + enabled = true; +} + +template +void rins_t::run_rins() +{ + if (total_calls == 0) RAFT_CUDA_TRY(cudaSetDevice(context.handle_ptr->get_device())); + + auto external_solution_size = dm.population.get_external_solution_size(); + if (external_solution_size > 0) dm.population.add_external_solutions_to_population(); + + if (!dm.population.is_feasible()) return; + + cuopt_assert(lp_optimal_solution.size() == problem_ptr->n_variables, "Assignment size mismatch"); + cuopt_assert(problem_copy->handle_ptr == &rins_handle, "Handle mismatch"); + cuopt_assert(problem_copy->n_variables == problem_ptr->n_variables, "Problem size mismatch"); + cuopt_assert(problem_copy->n_constraints == problem_ptr->n_constraints, "Problem size mismatch"); + cuopt_assert(problem_copy->n_integer_vars == problem_ptr->n_integer_vars, + "Problem size mismatch"); + cuopt_assert(problem_copy->n_binary_vars == problem_ptr->n_binary_vars, "Problem size mismatch"); + cuopt_assert(dm.population.current_size() > 0, "No solutions in population"); + + solution_t best_sol(*problem_copy); + // copy the best from the population into a solution_t in the RINS stream + { + std::lock_guard lock(dm.population.write_mutex); + auto& best_feasible_ref = dm.population.best_feasible(); + cuopt_assert(best_feasible_ref.assignment.size() == best_sol.assignment.size(), + "Assignment size mismatch"); + cuopt_assert(best_feasible_ref.get_feasible(), "Best feasible is not feasible"); + expand_device_copy(best_sol.assignment, best_feasible_ref.assignment, rins_handle.get_stream()); + best_sol.handle_ptr = &rins_handle; + best_sol.problem_ptr = problem_copy.get(); + best_sol.compute_feasibility(); + } + cuopt_assert(best_sol.handle_ptr == &rins_handle, "Handle mismatch"); + + cuopt_assert(best_sol.get_feasible(), "Best solution is not feasible"); + if (!best_sol.get_feasible()) { return; } + + i_t sol_size_before_rins = best_sol.assignment.size(); + auto lp_opt_device = cuopt::device_copy(this->lp_optimal_solution, rins_handle.get_stream()); + cuopt_assert(lp_opt_device.size() == problem_ptr->n_variables, "Assignment size mismatch"); + cuopt_assert(best_sol.assignment.size() == problem_ptr->n_variables, "Assignment size mismatch"); + + rmm::device_uvector vars_to_fix(problem_ptr->n_integer_vars, rins_handle.get_stream()); + auto end = thrust::copy_if(rins_handle.get_thrust_policy(), + problem_ptr->integer_indices.begin(), + problem_ptr->integer_indices.end(), + vars_to_fix.begin(), + [lpopt = lp_opt_device.data(), + pb = problem_ptr->view(), + incumbent = best_sol.assignment.data()] __device__(i_t var_idx) { + return pb.integer_equal(lpopt[var_idx], incumbent[var_idx]); + }); + vars_to_fix.resize(end - vars_to_fix.begin(), rins_handle.get_stream()); + f_t fractional_ratio = (f_t)(vars_to_fix.size()) / (f_t)problem_ptr->n_integer_vars; + + // abort if the fractional ratio is too low + if (fractional_ratio < settings.min_fractional_ratio) { + CUOPT_LOG_TRACE("RINS fractional ratio too low, aborting"); + return; + } + + thrust::default_random_engine g(seed + node_count); + + // shuffle fixing order + thrust::shuffle(rins_handle.get_thrust_policy(), vars_to_fix.begin(), vars_to_fix.end(), g); + + // fix n first according to fractional ratio + f_t rins_ratio = fixrate; + i_t n_to_fix = std::max((int)(vars_to_fix.size() * rins_ratio), 0); + vars_to_fix.resize(n_to_fix, rins_handle.get_stream()); + thrust::sort(rins_handle.get_thrust_policy(), vars_to_fix.begin(), vars_to_fix.end()); + + cuopt_assert(thrust::all_of(rins_handle.get_thrust_policy(), + vars_to_fix.begin(), + vars_to_fix.end(), + [pb = problem_ptr->view()] __device__(i_t var_idx) { + return pb.is_integer_var(var_idx); + }), + "All variables to fix must be integer variables"); + + if (n_to_fix == 0) { + CUOPT_LOG_DEBUG("RINS no variables to fix"); + return; + } + + total_calls++; + node_count_at_last_rins = node_count.load(); + time_limit = std::min(time_limit, dm.timer.remaining_time()); + CUOPT_LOG_DEBUG("Running RINS on solution with objective %g, fixing %d/%d", + best_sol.get_user_objective(), + vars_to_fix.size(), + problem_ptr->n_integer_vars); + CUOPT_LOG_DEBUG("RINS fixrate %g time limit %g", fixrate, time_limit); + CUOPT_LOG_DEBUG("RINS fractional ratio %g%%", fractional_ratio * 100); + + f_t prev_obj = best_sol.get_user_objective(); + + auto [fixed_problem, fixed_assignment, variable_map] = best_sol.fix_variables(vars_to_fix); + CUOPT_LOG_DEBUG( + "new var count %d var_count %d", fixed_problem.n_variables, problem_ptr->n_integer_vars); + + // should probably just do an spmv to get the objective instead. ugly mess of copies + solution_t best_sol_fixed_space(fixed_problem); + cuopt_assert(best_sol_fixed_space.handle_ptr == &rins_handle, "Handle mismatch"); + best_sol_fixed_space.copy_new_assignment( + cuopt::host_copy(fixed_assignment, rins_handle.get_stream())); + best_sol_fixed_space.compute_feasibility(); + CUOPT_LOG_DEBUG("RINS best sol fixed space objective %g", + best_sol_fixed_space.get_user_objective()); + + if (settings.objective_cut) { + f_t objective_cut = + best_sol_fixed_space.get_objective() - + std::max(std::abs(0.001 * best_sol_fixed_space.get_objective()), OBJECTIVE_EPSILON); + fixed_problem.add_cutting_plane_at_objective(objective_cut); + } + + fixed_problem.presolve_data.reset_additional_vars(fixed_problem, &rins_handle); + fixed_problem.presolve_data.initialize_var_mapping(fixed_problem, &rins_handle); + trivial_presolve(fixed_problem); + fixed_problem.check_problem_representation(true); + + std::vector> rins_solution_queue; + + mip_solver_context_t fj_context( + &rins_handle, &fixed_problem, context.settings, context.scaling); + fj_t fj(fj_context); + solution_t fj_solution(fixed_problem); + fj_solution.copy_new_assignment(cuopt::host_copy(fixed_assignment)); + std::vector default_weights(fixed_problem.n_constraints, 1.); + cpu_fj_thread_t cpu_fj_thread; + cpu_fj_thread.fj_cpu = + fj.create_cpu_climber(fj_solution, default_weights, default_weights, 0., fj_settings_t{}, true); + cpu_fj_thread.fj_ptr = &fj; + cpu_fj_thread.fj_cpu->log_prefix = "[RINS] "; + cpu_fj_thread.time_limit = time_limit; + cpu_fj_thread.start_cpu_solver(); + + f_t lower_bound = context.branch_and_bound_ptr ? context.branch_and_bound_ptr->get_lower_bound() + : -std::numeric_limits::infinity(); + f_t current_mip_gap = compute_rel_mip_gap(prev_obj, lower_bound); + + // run sub-mip + namespace dual_simplex = cuopt::linear_programming::dual_simplex; + dual_simplex::user_problem_t branch_and_bound_problem(&rins_handle); + dual_simplex::simplex_solver_settings_t branch_and_bound_settings; + dual_simplex::mip_solution_t branch_and_bound_solution(1); + dual_simplex::mip_status_t branch_and_bound_status = dual_simplex::mip_status_t::UNSET; + fixed_problem.get_host_user_problem(branch_and_bound_problem); + branch_and_bound_solution.resize(branch_and_bound_problem.num_cols); + // Fill in the settings for branch and bound + branch_and_bound_settings.time_limit = time_limit; + // branch_and_bound_settings.node_limit = 5000 + node_count / 100; // try harder as time goes + // on + branch_and_bound_settings.print_presolve_stats = false; + branch_and_bound_settings.absolute_mip_gap_tol = context.settings.tolerances.absolute_mip_gap; + branch_and_bound_settings.relative_mip_gap_tol = + std::min(current_mip_gap, (f_t)settings.target_mip_gap); + branch_and_bound_settings.integer_tol = context.settings.tolerances.integrality_tolerance; + branch_and_bound_settings.num_threads = 2; + branch_and_bound_settings.num_bfs_threads = 1; + branch_and_bound_settings.num_diving_threads = 1; + branch_and_bound_settings.log.log_prefix = "[RINS] "; + branch_and_bound_settings.solution_callback = [this, &rins_solution_queue]( + std::vector& solution, f_t objective) { + rins_solution_queue.push_back(solution); + }; + dual_simplex::branch_and_bound_t branch_and_bound(branch_and_bound_problem, + branch_and_bound_settings); + branch_and_bound.set_initial_guess(cuopt::host_copy(fixed_assignment, rins_handle.get_stream())); + branch_and_bound_status = branch_and_bound.solve(branch_and_bound_solution); + + if (!std::isnan(branch_and_bound_solution.objective)) { + CUOPT_LOG_DEBUG("RINS submip solution found. Objective %.16e. Status %d", + branch_and_bound_solution.objective, + int(branch_and_bound_status)); + cuopt_assert(rins_solution_queue.size() > 0, "RINS solution queue is unexpectedly empty"); + } + if (branch_and_bound_status == dual_simplex::mip_status_t::OPTIMAL) { + CUOPT_LOG_DEBUG("RINS submip optimal"); + // do goldilocks update + fixrate = std::max(fixrate - 0.05, settings.min_fixrate); + time_limit = std::max(time_limit - 2, settings.min_time_limit); + } else if (branch_and_bound_status == dual_simplex::mip_status_t::TIME_LIMIT) { + CUOPT_LOG_DEBUG("RINS submip time limit"); + // do goldilocks update + fixrate = std::min(fixrate + 0.05, settings.max_fixrate); + time_limit = std::min(time_limit + 2, settings.max_time_limit); + } else if (branch_and_bound_status == dual_simplex::mip_status_t::INFEASIBLE) { + CUOPT_LOG_DEBUG("RINS submip infeasible"); + // do goldilocks update, decreasing fixrate + fixrate = std::max(fixrate - 0.05, settings.min_fixrate); + } else { + CUOPT_LOG_DEBUG("RINS solution not found"); + // do goldilocks update + fixrate = std::min(fixrate + 0.05, settings.max_fixrate); + time_limit = std::min(time_limit + 2, settings.max_time_limit); + } + + cpu_fj_thread.stop_cpu_solver(); + bool fj_solution_found = cpu_fj_thread.wait_for_cpu_solver(); + CUOPT_LOG_DEBUG("RINS FJ ran for %d iterations", cpu_fj_thread.fj_cpu->iterations); + if (fj_solution_found) { + CUOPT_LOG_DEBUG("RINS FJ solution found. Objective %.16e", + cpu_fj_thread.fj_cpu->h_best_objective); + rins_solution_queue.push_back(cpu_fj_thread.fj_cpu->h_best_assignment); + } + // Thread will be automatically terminated and joined by destructor + + bool improvement_found = false; + for (auto& fixed_sol : rins_solution_queue) { + cuopt_assert(fixed_assignment.size() == fixed_sol.size(), "Assignment size mismatch"); + rmm::device_uvector post_processed_solution(fixed_sol.size(), rins_handle.get_stream()); + raft::copy( + post_processed_solution.data(), fixed_sol.data(), fixed_sol.size(), rins_handle.get_stream()); + fixed_problem.post_process_assignment(post_processed_solution, false); + cuopt_assert(post_processed_solution.size() == fixed_assignment.size(), + "Assignment size mismatch"); + rins_handle.sync_stream(); + + rmm::device_uvector unfixed_assignment(post_processed_solution.size(), + rins_handle.get_stream()); + raft::copy(unfixed_assignment.data(), + post_processed_solution.data(), + post_processed_solution.size(), + rins_handle.get_stream()); + best_sol.unfix_variables(unfixed_assignment, variable_map); + best_sol.compute_feasibility(); + + if (best_sol.get_feasible()) { + cuopt_assert(best_sol.test_number_all_integer(), "All must be integers after RINS"); + if (best_sol.get_user_objective() < prev_obj) { improvement_found = true; } + cuopt_assert(best_sol.assignment.size() == sol_size_before_rins, "Assignment size mismatch"); + cuopt_assert(best_sol.assignment.size() == problem_ptr->n_variables, + "Assignment size mismatch"); + dm.population.add_external_solution( + best_sol.get_host_assignment(), best_sol.get_objective(), solution_origin_t::RINS); + } + } + + if (improvement_found) total_success++; + CUOPT_LOG_DEBUG("RINS calls/successes %d/%d", total_calls, total_success); +} + +#if MIP_INSTANTIATE_FLOAT +template class rins_thread_t; +template class rins_t; +#endif + +#if MIP_INSTANTIATE_DOUBLE +template class rins_thread_t; +template class rins_t; +#endif + +} // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index ad649feb26..a615d84507 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -54,27 +54,28 @@ optimization_problem_solution_t get_relaxed_lp_solution( raft::common::nvtx::range fun_scope("get_relaxed_lp_solution"); auto function_start_time = std::chrono::high_resolution_clock::now(); - // === PDLP PREDICTOR FEATURES - START === - CUOPT_LOG_INFO("PDLP_FEATURES: n_variables=%d n_constraints=%d nnz=%lu", - op_problem.n_variables, - op_problem.n_constraints, - op_problem.coefficients.size()); + // // === PDLP PREDICTOR FEATURES - START === + // CUOPT_LOG_INFO("PDLP_FEATURES: n_variables=%d n_constraints=%d nnz=%lu", + // op_problem.n_variables, + // op_problem.n_constraints, + // op_problem.coefficients.size()); - CUOPT_LOG_INFO("PDLP_FEATURES: sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f", - op_problem.sparsity, - op_problem.nnz_stddev, - op_problem.unbalancedness); + // CUOPT_LOG_INFO("PDLP_FEATURES: sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f", + // op_problem.sparsity, + // op_problem.nnz_stddev, + // op_problem.unbalancedness); - CUOPT_LOG_INFO("PDLP_FEATURES: has_warm_start=%d time_limit=%.6f iteration_limit=%d", - settings.has_initial_primal, - settings.time_limit, - settings.iteration_limit); + // CUOPT_LOG_INFO("PDLP_FEATURES: has_warm_start=%d time_limit=%.6f iteration_limit=%d", + // settings.has_initial_primal, + // settings.time_limit, + // settings.iteration_limit); - CUOPT_LOG_INFO("PDLP_FEATURES: tolerance=%.10f check_infeasibility=%d return_first_feasible=%d", - settings.tolerance, - settings.check_infeasibility, - settings.return_first_feasible); - // === PDLP PREDICTOR FEATURES - END === + // CUOPT_LOG_INFO("PDLP_FEATURES: tolerance=%.10f check_infeasibility=%d + // return_first_feasible=%d", + // settings.tolerance, + // settings.check_infeasibility, + // settings.return_first_feasible); + // // === PDLP PREDICTOR FEATURES - END === pdlp_solver_settings_t pdlp_settings{}; pdlp_settings.detect_infeasibility = settings.check_infeasibility; @@ -86,10 +87,24 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.tolerances.relative_dual_tolerance = settings.tolerance / tolerance_divisor; pdlp_settings.time_limit = settings.time_limit; pdlp_settings.iteration_limit = settings.iteration_limit; - pdlp_settings.concurrent_halt = settings.concurrent_halt; - pdlp_settings.per_constraint_residual = settings.per_constraint_residual; - pdlp_settings.first_primal_feasible = settings.return_first_feasible; - pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; + if (settings.work_limit != std::numeric_limits::infinity()) { + // try to estimate the iteration count based on the requested work limit + int estim_iters = 0; + do { + // TODO: use an actual predictor model here + double estim_ms = 313 + 200 * op_problem.n_variables - 400 * op_problem.n_constraints + + 600 * op_problem.coefficients.size() + 7100 * estim_iters; + estim_ms = std::max(0.0, estim_ms); + if (estim_ms > settings.work_limit) { break; } + estim_iters += 100; + } while (true); + CUOPT_LOG_DEBUG("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); + pdlp_settings.iteration_limit = estim_iters; + } + pdlp_settings.concurrent_halt = settings.concurrent_halt; + pdlp_settings.per_constraint_residual = settings.per_constraint_residual; + pdlp_settings.first_primal_feasible = settings.return_first_feasible; + pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; set_pdlp_solver_mode(pdlp_settings); // TODO: set Stable3 here? pdlp_solver_t lp_solver(op_problem, pdlp_settings); @@ -158,13 +173,13 @@ optimization_problem_solution_t get_relaxed_lp_solution( elapsed_ms, solver_response.get_additional_termination_information().number_of_steps_taken); - // === PDLP PREDICTOR RESULTS - START === - auto term_info = solver_response.get_additional_termination_information(); - CUOPT_LOG_INFO("PDLP_RESULT: iterations=%d time_ms=%lld termination=%d", - term_info.number_of_steps_taken, - elapsed_ms, - (int)solver_response.get_termination_status()); - // === PDLP PREDICTOR RESULTS - END === + // // === PDLP PREDICTOR RESULTS - START === + // auto term_info = solver_response.get_additional_termination_information(); + // CUOPT_LOG_INFO("PDLP_RESULT: iterations=%d time_ms=%lld termination=%d", + // term_info.number_of_steps_taken, + // elapsed_ms, + // (int)solver_response.get_termination_status()); + // // === PDLP PREDICTOR RESULTS - END === return solver_response; } diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cuh b/cpp/src/mip/relaxed_lp/relaxed_lp.cuh index d23921fc5b..8e1e8c0816 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cuh +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cuh @@ -30,6 +30,7 @@ struct relaxed_lp_settings_t { double tolerance = 1e-4; double time_limit = 1.0; int iteration_limit = std::numeric_limits::max(); + double work_limit = std::numeric_limits::infinity(); bool check_infeasibility = true; bool return_first_feasible = false; bool save_state = true; diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index f48208c4b6..dc12fa3844 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -535,12 +535,20 @@ def get_feature_importance(model, feature_names: List[str], print(f" {i:3d}. {feature_names[idx]:40s}: {importances[idx]:.6f}") elif regressor_type == 'linear': - # Linear regression coefficients - coefs = np.abs(model.coef_) - indices = np.argsort(coefs)[::-1] - + # Linear regression coefficients and intercept + print(f"\nIntercept: {model.intercept_:.6f}\n") + print(f"Coefficients:") + + # Print each feature with its coefficient + for i, (feature_name, coef) in enumerate(zip(feature_names, model.coef_), 1): + print(f" {i:3d}. {feature_name:40s}: {coef:.6f}") + + # Also show sorted by absolute value for importance ranking + print(f"\nRanked by absolute magnitude:") + coefs_abs = np.abs(model.coef_) + indices = np.argsort(coefs_abs)[::-1] for i, idx in enumerate(indices, 1): - print(f" {i:3d}. {feature_names[idx]:40s}: {coefs[idx]:.6f}") + print(f" {i:3d}. {feature_names[idx]:40s}: {model.coef_[idx]:.6f} (|{coefs_abs[idx]:.6f}|)") elif regressor_type.startswith('poly'): # For polynomial, get feature names and coefficients from the Ridge step From c24f6ef5f8b768d11fb610b5254a59a1b2c4cced Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 4 Nov 2025 14:57:34 +0000 Subject: [PATCH 026/225] more work unit timer stuff --- PREDICTOR_DATA_COLLECTION.md | 4 +- .../linear_programming/cuopt/run_mip.cpp | 7 +- cpp/src/mip/diversity/diversity_manager.cu | 45 ++++--- .../diversity/recombiners/fp_recombiner.cuh | 2 +- .../mip/feasibility_jump/feasibility_jump.cu | 6 +- .../mip/feasibility_jump/feasibility_jump.cuh | 2 +- .../feasibility_pump/feasibility_pump.cu | 126 ++++++++++-------- cpp/src/mip/local_search/local_search.cu | 4 +- .../local_search/rounding/constraint_prop.cu | 61 +++++---- cpp/src/mip/solver.cu | 6 +- cpp/src/utilities/work_unit_predictor.cpp | 2 +- cpp/tests/mip/feasibility_jump_tests.cu | 2 +- 12 files changed, 142 insertions(+), 125 deletions(-) diff --git a/PREDICTOR_DATA_COLLECTION.md b/PREDICTOR_DATA_COLLECTION.md index cc81eb9c33..d026a11d70 100644 --- a/PREDICTOR_DATA_COLLECTION.md +++ b/PREDICTOR_DATA_COLLECTION.md @@ -268,14 +268,14 @@ The FJ predictor already exists at `cpp/src/utilities/models/fj_predictor/`. The Example from FJ predictor: ```cpp // cpp/src/mip/feasibility_jump/feasibility_jump.cu:1283-1291 -if (settings.work_unit_limit != std::numeric_limits::infinity()) { +if (settings.work_limit != std::numeric_limits::infinity()) { std::map features_map = get_feature_vector(0); float iter_prediction = std::max( (f_t)0.0, (f_t)ceil(context.work_unit_predictors.fj_predictor.predict_scalar(features_map)) ); CUOPT_LOG_DEBUG("FJ determ: Estimated number of iterations for %f WU: %f", - settings.work_unit_limit, + settings.work_limit, iter_prediction); settings.iteration_limit = std::min(settings.iteration_limit, (i_t)iter_prediction); } diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index da3168a569..56a1be629e 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -366,8 +366,9 @@ int main(int argc, char* argv[]) .default_value(std::string("f")); program.add_argument("-d", "--determinism") - .help("enable deterministic mode (t/f)") - .default_value(std::string("f")); + .help("enable deterministic mode") + .default_value(false) + .implicit_value(true); // Parse arguments try { @@ -397,7 +398,7 @@ int main(int argc, char* argv[]) bool log_to_console = program.get("--log-to-console")[0] == 't'; double memory_limit = program.get("--memory-limit"); bool track_allocations = program.get("--track-allocations")[0] == 't'; - bool deterministic = program.get("--determinism")[0] == 't'; + bool deterministic = program.get("--determinism"); if (num_cpu_threads < 0) { num_cpu_threads = omp_get_max_threads() / n_gpus; } diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 328757e114..49552bef5c 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -191,7 +191,7 @@ bool diversity_manager_t::run_presolve(f_t time_limit) CUOPT_LOG_INFO("Running presolve!"); work_limit_timer_t presolve_timer(context.settings.deterministic, time_limit); auto term_crit = ls.constraint_prop.bounds_update.solve(*problem_ptr); - presolve_timer.record_work(213762); + presolve_timer.record_work(0); if (ls.constraint_prop.bounds_update.infeas_constraints_count > 0) { stats.presolve_time = timer.elapsed_time(); return false; @@ -235,7 +235,7 @@ void diversity_manager_t::generate_quick_feasible_solution() work_limit_timer_t sol_timer(context.settings.deterministic, generate_fast_solution_time); // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); - sol_timer.record_work(213762); + sol_timer.record_work(0); if (solution.get_feasible()) { population.run_solution_callbacks(solution); initial_sol_vector.emplace_back(std::move(solution)); @@ -325,7 +325,10 @@ solution_t diversity_manager_t::run_solver() raft::common::nvtx::range fun_scope("run_solver"); diversity_config.fj_only_run = false; - if (context.settings.deterministic) { remaining_work_limit = context.settings.work_limit; } + if (context.settings.deterministic) { + remaining_work_limit = context.settings.work_limit; + CUOPT_LOG_INFO("Deterministic mode, remaining work limit: %f", remaining_work_limit); + } population.timer = timer; const f_t time_limit = timer.remaining_time(); @@ -356,7 +359,7 @@ solution_t diversity_manager_t::run_solver() // Run CPUFJ early to find quick initial solutions population.allocate_solutions(); ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop - ls.start_cpufj_scratch_threads(population); + if (!context.settings.deterministic) { ls.start_cpufj_scratch_threads(population); } // before probing cache or LP, run FJ to generate initial primal feasible solution const f_t time_ratio_of_probing_cache = diversity_config.time_ratio_of_probing_cache; @@ -367,7 +370,7 @@ solution_t diversity_manager_t::run_solver() if (check_b_b_preemption()) { return population.best_feasible(); } if (!diversity_config.fj_only_run) { compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); - probing_timer.record_work(213762); + probing_timer.record_work(0); } if (check_b_b_preemption()) { return population.best_feasible(); } @@ -430,21 +433,21 @@ solution_t diversity_manager_t::run_solver() } // Run this 100 times with varying iteration limits - for (int i = 0; i < 100; i++) { - relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_time_limit; - lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; - lp_settings.return_first_feasible = false; - lp_settings.save_state = true; - lp_settings.concurrent_halt = &global_concurrent_halt; - lp_settings.has_initial_primal = false; - lp_settings.iteration_limit = 100 + i * 100; - rmm::device_uvector lp_optimal_solution_copy(lp_optimal_solution.size(), - problem_ptr->handle_ptr->get_stream()); - auto lp_result = - get_relaxed_lp_solution(*problem_ptr, lp_optimal_solution_copy, lp_state, lp_settings); - } - exit(0); + // for (int i = 0; i < 100; i++) { + // relaxed_lp_settings_t lp_settings; + // lp_settings.time_limit = lp_time_limit; + // lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; + // lp_settings.return_first_feasible = false; + // lp_settings.save_state = true; + // lp_settings.concurrent_halt = &global_concurrent_halt; + // lp_settings.has_initial_primal = false; + // lp_settings.iteration_limit = 100 + i * 100; + // rmm::device_uvector lp_optimal_solution_copy(lp_optimal_solution.size(), + // problem_ptr->handle_ptr->get_stream()); + // auto lp_result = + // get_relaxed_lp_solution(*problem_ptr, lp_optimal_solution_copy, lp_state, lp_settings); + // } + // exit(0); if (ls.lp_optimal_exists) { solution_t lp_rounded_sol(*problem_ptr); @@ -452,7 +455,7 @@ solution_t diversity_manager_t::run_solver() lp_rounded_sol.round_nearest(); lp_rounded_sol.compute_feasibility(); population.add_solution(std::move(lp_rounded_sol)); - ls.start_cpufj_lptopt_scratch_threads(population); + if (!context.settings.deterministic) { ls.start_cpufj_lptopt_scratch_threads(population); } } population.add_solutions_from_vec(std::move(initial_sol_vector)); diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index f4ca9d5c4f..17c8c89e62 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -96,7 +96,7 @@ class fp_recombiner_t : public recombiner_t { if (this->context.settings.deterministic) { lp_settings.time_limit = std::numeric_limits::max(); // TODO should be global time limit - lp_settings.iteration_limit = 5000; + lp_settings.work_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; } lp_settings.tolerance = fixed_problem.tolerances.absolute_tolerance; lp_settings.return_first_feasible = true; diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index fb28a9e136..58d0af50c5 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -910,7 +910,7 @@ std::map fj_t::get_feature_vector(i_t climber_idx) features["unbalancedness"] = (float)pb_ptr->unbalancedness; // Algorithm settings - features["time"] = (float)settings.work_unit_limit; + features["time"] = (float)settings.work_limit; features["n_of_minimums_for_exit"] = (float)settings.n_of_minimums_for_exit; features["feasibility_run"] = (float)settings.feasibility_run; @@ -1280,12 +1280,12 @@ i_t fj_t::solve(solution_t& solution) resize_vectors(solution.handle_ptr); // if work_limit is set: compute an estimate of the number of iterations required - if (settings.work_unit_limit != std::numeric_limits::infinity()) { + if (settings.work_limit != std::numeric_limits::infinity()) { std::map features_map = get_feature_vector(0); float iter_prediction = std::max( (f_t)0.0, (f_t)ceil(context.work_unit_predictors.fj_predictor.predict_scalar(features_map))); CUOPT_LOG_DEBUG("FJ determ: Estimated number of iterations for %f WU: %f", - settings.work_unit_limit, + settings.work_limit, iter_prediction); settings.iteration_limit = std::min(settings.iteration_limit, (i_t)iter_prediction); } diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh index 1ee19b39ac..2ebeab30eb 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh @@ -112,7 +112,7 @@ struct fj_settings_t { fj_mode_t mode{fj_mode_t::FIRST_FEASIBLE}; fj_candidate_selection_t candidate_selection{fj_candidate_selection_t::WEIGHTED_SCORE}; double time_limit{60.0}; - double work_unit_limit{std::numeric_limits::infinity()}; + double work_limit{std::numeric_limits::infinity()}; int iteration_limit{std::numeric_limits::max()}; fj_hyper_parameters_t parameters{}; int n_of_minimums_for_exit = 7000; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index d8472dae04..75624e1fbc 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -241,8 +241,11 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t::infinity(); + lp_settings.work_limit = time_limit; + } + auto solver_response = get_relaxed_lp_solution(temp_p, solution, lp_settings); cuopt_func_call(solution.test_variable_bounds(false)); last_lp_time = old_remaining - timer.remaining_time(); lp_time += last_lp_time; @@ -270,7 +273,7 @@ template bool feasibility_pump_t::round(solution_t& solution) { bool result; - CUOPT_LOG_DEBUG("Rounding the point"); + // CUOPT_LOG_DEBUG("Rounding the point"); work_limit_timer_t bounds_prop_timer(context.settings.deterministic, std::max(0.05, std::min(0.5, timer.remaining_time() / 10.))); const f_t lp_run_time_after_feasible = 0.; @@ -326,8 +329,9 @@ bool feasibility_pump_t::run_fj_cycle_escape(solution_t& sol template bool feasibility_pump_t::test_fj_feasible(solution_t& solution, f_t time_limit) { - CUOPT_LOG_DEBUG("Running 20%% FJ"); + CUOPT_LOG_DEBUG("Running 20%% FJ, remaining %fwu", timer.remaining_time()); bool is_feasible; + fj.settings = fj_settings_t{}; fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; fj.settings.update_weights = true; fj.settings.feasibility_run = true; @@ -335,6 +339,7 @@ bool feasibility_pump_t::test_fj_feasible(solution_t& soluti fj.settings.time_limit = std::min(time_limit, timer.remaining_time()); if (context.settings.deterministic) { fj.settings.time_limit = timer.remaining_time(); + fj.settings.work_limit = fj.settings.time_limit; fj.settings.iteration_limit = 10000; } cuopt_func_call(solution.test_variable_bounds(true)); @@ -350,6 +355,10 @@ bool feasibility_pump_t::test_fj_feasible(solution_t& soluti } else { CUOPT_LOG_DEBUG("20%% FJ run found feasible!"); } + timer.record_work(fj.settings.time_limit); + CUOPT_LOG_DEBUG("20%% FJ run finished, elapsed %fs remaining %fwu", + fj.settings.time_limit, + timer.remaining_time()); return is_feasible; } @@ -506,18 +515,19 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s f_t start_time = timer.remaining_time(); i_t fp_iterations = 0; - // Problem structure features - CUOPT_LOG_INFO("FP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d n_binary_vars=%d", - solution.problem_ptr->n_variables, - solution.problem_ptr->n_constraints, - solution.problem_ptr->n_integer_vars, - solution.problem_ptr->n_binary_vars); + // // Problem structure features + // CUOPT_LOG_INFO("FP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d + // n_binary_vars=%d", + // solution.problem_ptr->n_variables, + // solution.problem_ptr->n_constraints, + // solution.problem_ptr->n_integer_vars, + // solution.problem_ptr->n_binary_vars); - CUOPT_LOG_INFO("FP_FEATURES: nnz=%lu sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f", - solution.problem_ptr->coefficients.size(), - solution.problem_ptr->sparsity, - solution.problem_ptr->nnz_stddev, - solution.problem_ptr->unbalancedness); + // CUOPT_LOG_INFO("FP_FEATURES: nnz=%lu sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f", + // solution.problem_ptr->coefficients.size(), + // solution.problem_ptr->sparsity, + // solution.problem_ptr->nnz_stddev, + // solution.problem_ptr->unbalancedness); // Initial solution features solution.compute_feasibility(); @@ -526,24 +536,25 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s ? (f_t)initial_n_integers / solution.problem_ptr->n_integer_vars : 0.0; - CUOPT_LOG_INFO("FP_FEATURES: initial_feasibility=%d initial_excess=%.6f initial_objective=%.6f", - solution.get_feasible(), - solution.get_total_excess(), - solution.get_objective()); - - CUOPT_LOG_INFO("FP_FEATURES: initial_ratio_of_integers=%.6f initial_n_integers=%d", - initial_ratio_of_integers, - initial_n_integers); - - // Algorithm configuration features - CUOPT_LOG_INFO("FP_FEATURES: alpha=%.6f check_distance_cycle=%d cycle_detection_length=%d", - config.alpha, - config.check_distance_cycle, - cycle_queue.cycle_detection_length); - - CUOPT_LOG_INFO("FP_FEATURES: has_cutting_plane=%d time_budget=%.6f", - solution.problem_ptr->cutting_plane_added, - timer.remaining_time()); + // CUOPT_LOG_INFO("FP_FEATURES: initial_feasibility=%d initial_excess=%.6f + // initial_objective=%.6f", + // solution.get_feasible(), + // solution.get_total_excess(), + // solution.get_objective()); + + // CUOPT_LOG_INFO("FP_FEATURES: initial_ratio_of_integers=%.6f initial_n_integers=%d", + // initial_ratio_of_integers, + // initial_n_integers); + + // // Algorithm configuration features + // CUOPT_LOG_INFO("FP_FEATURES: alpha=%.6f check_distance_cycle=%d cycle_detection_length=%d", + // config.alpha, + // config.check_distance_cycle, + // cycle_queue.cycle_detection_length); + + // CUOPT_LOG_INFO("FP_FEATURES: has_cutting_plane=%d time_budget=%.6f", + // solution.problem_ptr->cutting_plane_added, + // timer.remaining_time()); // === FP PREDICTOR FEATURES - END === // start by doing nearest rounding @@ -559,9 +570,9 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); f_t time_taken = start_time - timer.remaining_time(); - CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=TIME_LIMIT", - fp_iterations, - time_taken); + // CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=TIME_LIMIT", + // fp_iterations, + // time_taken); round(solution); return false; } @@ -597,10 +608,10 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s total_fp_time_until_cycle = fp_fj_cycle_time_begin - remaining_time_end_fp; // CUOPT_LOG_DEBUG("total_fp_time_until_cycle: %f", total_fp_time_until_cycle); f_t time_taken = start_time - timer.remaining_time(); - CUOPT_LOG_INFO( - "FP_RESULT: iterations=%d time_taken=%.6f termination=INFEASIBLE_DISTANCE_CYCLE", - fp_iterations, - time_taken); + // CUOPT_LOG_INFO( + // "FP_RESULT: iterations=%d time_taken=%.6f termination=INFEASIBLE_DISTANCE_CYCLE", + // fp_iterations, + // time_taken); return false; } } @@ -609,10 +620,10 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (is_feasible) { CUOPT_LOG_DEBUG("Feasible solution found after LP with relative tolerance"); f_t time_taken = start_time - timer.remaining_time(); - CUOPT_LOG_INFO( - "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_LP_PROJECTION", - fp_iterations, - time_taken); + // CUOPT_LOG_INFO( + // "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_LP_PROJECTION", + // fp_iterations, + // time_taken); return true; } // if the solution is almost on polytope @@ -634,10 +645,10 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (is_feasible && n_integers == solution.problem_ptr->n_integer_vars) { CUOPT_LOG_DEBUG("Feasible solution verified with LP!"); f_t time_taken = start_time - timer.remaining_time(); - CUOPT_LOG_INFO( - "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_LP_VERIFIED", - fp_iterations, - time_taken); + // CUOPT_LOG_INFO( + // "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_LP_VERIFIED", + // fp_iterations, + // time_taken); return true; } } @@ -653,18 +664,19 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); f_t time_taken = start_time - timer.remaining_time(); - CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=TIME_LIMIT_AFTER_ROUND", - fp_iterations, - time_taken); + // CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f + // termination=TIME_LIMIT_AFTER_ROUND", + // fp_iterations, + // time_taken); return false; } if (is_feasible) { bool res = solution.compute_feasibility(); cuopt_assert(res, "Feasibility issue"); f_t time_taken = start_time - timer.remaining_time(); - CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_AFTER_ROUND", - fp_iterations, - time_taken); + // CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_AFTER_ROUND", + // fp_iterations, + // time_taken); return true; } // do the cycle check if alpha diff is small enough @@ -683,9 +695,9 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s fp_fj_cycle_time_begin, total_fp_time_until_cycle); f_t time_taken = start_time - timer.remaining_time(); - CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=ASSIGNMENT_CYCLE", - fp_iterations, - time_taken); + // CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=ASSIGNMENT_CYCLE", + // fp_iterations, + // time_taken); return false; } cycle_queue.n_iterations_without_cycle++; diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 0f2be589aa..859b7aab08 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -337,7 +337,7 @@ void local_search_t::generate_fast_solution(solution_t& solu work_limit_timer_t(context.settings.deterministic, std::min(timer.remaining_time(), 2.)); // do constraint prop on lp optimal solution constraint_prop.apply_round(solution, 1., constr_prop_timer); - constr_prop_timer.record_work(213762); + constr_prop_timer.record_work(0); if (solution.compute_feasibility()) { return; } if (timer.check_time_limit()) { return; }; f_t time_limit = std::min(3., timer.remaining_time()); @@ -489,7 +489,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu work_limit_timer_t(context.settings.deterministic, std::min(timer.remaining_time(), 10.)); bool is_feasible = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); - bounds_prop_timer.record_work(213762); + bounds_prop_timer.record_work(0); if (!is_feasible) { f_t lp_run_time = 2.; // CHANGE diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 3c118becb7..87d2a778b6 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -229,11 +229,11 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t } }); - CUOPT_LOG_DEBUG("hash vars 0x%x", detail::compute_hash(vars)); - // now do the suffling, for that we need to assign some random values to rnd array - // we will sort this rnd array and the vars in subsections, so that each subsection will be - // shuffled in total we will have 3(binary, ternary and rest) x 7 intervals = 21 subsections. - // first extract these subsections from the data + // CUOPT_LOG_DEBUG("hash vars 0x%x", detail::compute_hash(vars)); + // now do the suffling, for that we need to assign some random values to rnd array + // we will sort this rnd array and the vars in subsections, so that each subsection will be + // shuffled in total we will have 3(binary, ternary and rest) x 7 intervals = 21 subsections. + // first extract these subsections from the data rmm::device_uvector subsection_offsets(size_of_subsections, sol.handle_ptr->get_stream()); thrust::fill( sol.handle_ptr->get_thrust_policy(), subsection_offsets.begin(), subsection_offsets.end(), -1); @@ -284,9 +284,9 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t } }); - CUOPT_LOG_DEBUG("hash subsection_offsets 0x%x", detail::compute_hash(subsection_offsets)); + // CUOPT_LOG_DEBUG("hash subsection_offsets 0x%x", detail::compute_hash(subsection_offsets)); auto random_vector = get_random_uniform_vector((i_t)vars.size(), rng); - CUOPT_LOG_DEBUG("hash random_vector 0x%x", detail::compute_hash(random_vector)); + // CUOPT_LOG_DEBUG("hash random_vector 0x%x", detail::compute_hash(random_vector)); rmm::device_uvector device_random_vector(random_vector.size(), sol.handle_ptr->get_stream()); raft::copy(device_random_vector.data(), random_vector.data(), @@ -871,7 +871,6 @@ bool constraint_prop_t::find_integer( using crit_t = termination_criterion_t; auto& unset_integer_vars = unset_vars; i_t seed = cuopt::seed_generator::get_seed(); - CUOPT_LOG_DEBUG("seed 0x%x", seed); std::mt19937 rng(seed); // CHANGE @@ -938,8 +937,8 @@ bool constraint_prop_t::find_integer( } // this is needed for the sort inside of the loop bool problem_ii = is_problem_ii(*sol.problem_ptr); - CUOPT_LOG_DEBUG("is problem ii %d\n", problem_ii); - // if the problem is ii, run the bounds prop in the beginning + // CUOPT_LOG_DEBUG("is problem ii %d", problem_ii); + // if the problem is ii, run the bounds prop in the beginning if (problem_ii) { bool bounds_repaired = bounds_repair.repair_problem(*sol.problem_ptr, *orig_sol.problem_ptr, timer, sol.handle_ptr); @@ -1115,26 +1114,26 @@ bool constraint_prop_t::apply_round( // === CONSTRAINT PROP PREDICTOR FEATURES - START === auto cp_start_time = std::chrono::high_resolution_clock::now(); - CUOPT_LOG_INFO("CP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d", - sol.problem_ptr->n_variables, - sol.problem_ptr->n_constraints, - sol.problem_ptr->n_integer_vars); + // CUOPT_LOG_INFO("CP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d", + // sol.problem_ptr->n_variables, + // sol.problem_ptr->n_constraints, + // sol.problem_ptr->n_integer_vars); - CUOPT_LOG_INFO("CP_FEATURES: nnz=%lu sparsity=%.6f", - sol.problem_ptr->coefficients.size(), - sol.problem_ptr->sparsity); + // CUOPT_LOG_INFO("CP_FEATURES: nnz=%lu sparsity=%.6f", + // sol.problem_ptr->coefficients.size(), + // sol.problem_ptr->sparsity); sol.compute_feasibility(); i_t n_unset_integers = sol.problem_ptr->n_integer_vars - sol.compute_number_of_integers(); - CUOPT_LOG_INFO("CP_FEATURES: n_unset_vars=%d initial_excess=%.6f time_budget=%.6f", - n_unset_integers, - sol.get_total_excess(), - max_time_for_bounds_prop); + // CUOPT_LOG_INFO("CP_FEATURES: n_unset_vars=%d initial_excess=%.6f time_budget=%.6f", + // n_unset_integers, + // sol.get_total_excess(), + // max_time_for_bounds_prop); - CUOPT_LOG_INFO("CP_FEATURES: round_all_vars=%d lp_run_time_after_feasible=%.6f", - round_all_vars, - lp_run_time_after_feasible); + // CUOPT_LOG_INFO("CP_FEATURES: round_all_vars=%d lp_run_time_after_feasible=%.6f", + // round_all_vars, + // lp_run_time_after_feasible); // === CONSTRAINT PROP PREDICTOR FEATURES - END === max_timer = work_limit_timer_t{context.settings.deterministic, max_time_for_bounds_prop}; @@ -1182,16 +1181,16 @@ bool constraint_prop_t::apply_round( if (!sol_found) { sol.compute_feasibility(); - CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=FAILED iterations=%d", - cp_elapsed_ms, - 0); // TODO: track actual iterations + // CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=FAILED iterations=%d", + // cp_elapsed_ms, + // 0); // TODO: track actual iterations return false; } bool result = sol.compute_feasibility(); - CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=%s iterations=%d", - cp_elapsed_ms, - result ? "SUCCESS" : "FAILED", - 0); // TODO: track actual iterations + // CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=%s iterations=%d", + // cp_elapsed_ms, + // result ? "SUCCESS" : "FAILED", + // 0); // TODO: track actual iterations return result; } diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index 607fe6c9f6..bcce85439c 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -159,7 +159,9 @@ solution_t mip_solver_t::run_solver() branch_and_bound_solution_helper_t solution_helper(&dm, branch_and_bound_settings); dual_simplex::mip_solution_t branch_and_bound_solution(1); - if (!context.settings.heuristics_only) { + // for now, disable B&B in deterministic mode + bool run_bb = !context.settings.deterministic && !context.settings.heuristics_only; + if (run_bb) { // Convert the presolved problem to dual_simplex::user_problem_t op_problem_.get_host_user_problem(branch_and_bound_problem); // Resize the solution now that we know the number of columns/variables @@ -223,7 +225,7 @@ solution_t mip_solver_t::run_solver() // Start the primal heuristics auto sol = dm.run_solver(); - if (!context.settings.heuristics_only) { + if (run_bb) { // Wait for the branch and bound to finish auto bb_status = branch_and_bound_status_future.get(); if (branch_and_bound_solution.lower_bound > -std::numeric_limits::infinity()) { diff --git a/cpp/src/utilities/work_unit_predictor.cpp b/cpp/src/utilities/work_unit_predictor.cpp index 8e1d871115..4d273d3b60 100644 --- a/cpp/src/utilities/work_unit_predictor.cpp +++ b/cpp/src/utilities/work_unit_predictor.cpp @@ -54,7 +54,7 @@ float work_unit_predictor_t::predict_scalar( printf("Feature %s: missing\n", model_t::feature_names[i]); } else { data[i].fvalue = features.at(std::string(model_t::feature_names[i])); - printf("Feature %s: %f\n", model_t::feature_names[i], data[i].fvalue); + // printf("Feature %s: %f\n", model_t::feature_names[i], data[i].fvalue); } } // Compute a hash key for the relevant inputs diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index c5c695299b..76e6aed4c8 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -180,7 +180,7 @@ static bool run_fj_check_determinism(std::string test_instance, int iter_limit) fj_settings.time_limit = std::numeric_limits::max(); fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; fj_settings.n_of_minimums_for_exit = 5000 * 1000; - fj_settings.work_unit_limit = 0.5; // run for 0.5wu (~0.5s) + fj_settings.work_limit = 0.5; // run for 0.5wu (~0.5s) fj_settings.update_weights = true; fj_settings.feasibility_run = false; // fj_settings.iteration_limit = iter_limit; From c70bc1b8ed36e2439aec092549a298455c7a5070 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 4 Nov 2025 17:36:57 +0000 Subject: [PATCH 027/225] further work unit timer progress for gpu heuristics --- PREDICTOR_DATA_COLLECTION.md | 426 ------------------ cpp/src/mip/diversity/diversity_manager.cu | 12 +- cpp/src/mip/diversity/population.cu | 2 +- .../recombiners/bound_prop_recombiner.cuh | 4 +- .../diversity/recombiners/fp_recombiner.cuh | 8 +- .../recombiners/line_segment_recombiner.cuh | 2 +- .../mip/feasibility_jump/feasibility_jump.cu | 65 ++- .../feasibility_pump/feasibility_pump.cu | 32 +- .../line_segment_search.cu | 2 +- cpp/src/mip/local_search/local_search.cu | 16 +- .../local_search/rounding/constraint_prop.cu | 25 +- .../rounding/lb_constraint_prop.cu | 2 +- cpp/src/mip/relaxed_lp/relaxed_lp.cu | 16 +- cpp/src/mip/solve.cu | 4 +- cpp/src/mip/solver.cu | 4 +- cpp/src/mip/solver.cuh | 4 +- cpp/src/mip/solver_context.cuh | 9 +- cpp/src/utilities/work_limit_timer.hpp | 77 +++- cpp/tests/mip/diversity_test.cu | 15 +- cpp/tests/mip/local_search_test.cu | 13 +- cpp/tests/mip/mip_utils.cuh | 2 +- cpp/tests/mip/multi_probe_test.cu | 6 +- cpp/tests/mip/presolve_test.cu | 11 +- 23 files changed, 204 insertions(+), 553 deletions(-) delete mode 100644 PREDICTOR_DATA_COLLECTION.md diff --git a/PREDICTOR_DATA_COLLECTION.md b/PREDICTOR_DATA_COLLECTION.md deleted file mode 100644 index d026a11d70..0000000000 --- a/PREDICTOR_DATA_COLLECTION.md +++ /dev/null @@ -1,426 +0,0 @@ -# Predictor Data Collection: Feature Logging - -This document describes the instrumentation added to collect training data for work unit predictors. - -## Overview - -Three algorithms have been instrumented to log features before execution and performance metrics after completion: - -1. **Feasibility Pump (FP)** - Main heuristic for finding feasible solutions -2. **PDLP** - First-order LP solver used for polytope projection -3. **Constraint Propagation (CP)** - Variable rounding with bounds propagation - -Note: **Feasibility Jump (FJ)** already has a working predictor and doesn't need additional instrumentation. - -## Log Format - -All logs use `CUOPT_LOG_INFO` level with structured prefixes for easy parsing: - -### Feasibility Pump (FP) - -**Features logged before execution:** -``` -FP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d n_binary_vars=%d -FP_FEATURES: nnz=%lu sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f -FP_FEATURES: initial_feasibility=%d initial_excess=%.6f initial_objective=%.6f -FP_FEATURES: initial_ratio_of_integers=%.6f initial_n_integers=%d -FP_FEATURES: alpha=%.6f check_distance_cycle=%d cycle_detection_length=%d -FP_FEATURES: has_cutting_plane=%d time_budget=%.6f -``` - -**Results logged after execution:** -``` -FP_RESULT: iterations=%d time_taken=%.6f termination= -``` - -**Termination reasons:** -- `TIME_LIMIT` - Time budget exhausted -- `TIME_LIMIT_AFTER_ROUND` - Time limit during rounding phase -- `FEASIBLE_LP_PROJECTION` - Found feasible via LP projection -- `FEASIBLE_LP_VERIFIED` - Found feasible via high-precision LP -- `FEASIBLE_AFTER_ROUND` - Found feasible after rounding -- `FEASIBLE_DISTANCE_CYCLE` - Found feasible during distance cycle handling -- `INFEASIBLE_DISTANCE_CYCLE` - Distance cycle detected, no feasible found -- `ASSIGNMENT_CYCLE` - Assignment cycle detected - -**Location:** `cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu::run_single_fp_descent` - ---- - -### PDLP (LP Solver) - -**Features logged before execution:** -``` -PDLP_FEATURES: n_variables=%d n_constraints=%d nnz=%lu -PDLP_FEATURES: sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f -PDLP_FEATURES: has_warm_start=%d time_limit=%.6f iteration_limit=%d -PDLP_FEATURES: tolerance=%.10f check_infeasibility=%d return_first_feasible=%d -``` - -**Results logged after execution:** -``` -PDLP_RESULT: iterations=%d time_ms=%lld termination=%d -PDLP_RESULT: primal_objective=%.10f dual_objective=%.10f gap=%.10f -PDLP_RESULT: l2_primal_residual=%.10f l2_dual_residual=%.10f -``` - -**Termination status codes:** -- `0` - NoTermination -- `1` - NumericalError -- `2` - Optimal -- `3` - PrimalInfeasible -- `4` - DualInfeasible -- `5` - IterationLimit -- `6` - TimeLimit -- `7` - PrimalFeasible -- `8` - ConcurrentLimit - -**Location:** `cpp/src/mip/relaxed_lp/relaxed_lp.cu::get_relaxed_lp_solution` - ---- - -### Constraint Propagation (CP) - -**Features logged before execution:** -``` -CP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d -CP_FEATURES: nnz=%lu sparsity=%.6f -CP_FEATURES: n_unset_vars=%d initial_excess=%.6f time_budget=%.6f -CP_FEATURES: round_all_vars=%d lp_run_time_after_feasible=%.6f -``` - -**Results logged after execution:** -``` -CP_RESULT: time_ms=%lld termination= iterations=%d -``` - -**Termination status:** -- `BRUTE_FORCE_SUCCESS` - Succeeded via simple rounding -- `SUCCESS` - Found feasible solution -- `FAILED` - Did not find feasible solution - -**Location:** `cpp/src/mip/local_search/rounding/constraint_prop.cu::apply_round` - ---- - -## Data Collection Workflow - -### 1. Run Solver with Logging Enabled - -Ensure the log level is set to `INFO` or higher to capture the feature logs: - -```bash -export CUOPT_LOG_LEVEL=INFO -# or -export CUOPT_LOG_LEVEL=DEBUG -``` - -### 2. Parse Logs - -Use the provided `determinism_logs_parse.py` script to automatically extract training data: - -```bash -# Parse FP (Feasibility Pump) logs -python scripts/determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl - -# Parse PDLP (LP Solver) logs -python scripts/determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.pkl - -# Parse CP (Constraint Propagation) logs -python scripts/determinism_logs_parse.py logs/ --algorithm CP -o cp_data.pkl - -# Parse FJ (Feasibility Jump) legacy logs -python scripts/determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.pkl -``` - -The script will: -- Find all `.log` files in the specified directory -- Extract all `_FEATURES` and `_RESULT` log lines using grep -- Pair features with results in order using line numbers -- Export to pickle format compatible with `train_regressor.py` - -**Performance optimizations for large logs:** -- **Exact pattern matching**: Grep uses `FP_FEATURES:` / `FP_RESULT:` (with colon) to match ONLY predictor lines - - Example: Log with 100K lines and 10K "FP" references → grep extracts only ~200 predictor lines - - Filters before Python processing, so noisy logs don't slow down parsing -- Single grep call per algorithm (combines features + results) -- Uses grep's `-n` flag for line-number-based pairing -- Minimal Python string processing (simple split operations) -- Single-pass parsing with efficient dictionary accumulation -- Handles millions of log lines efficiently - -**Script output (with progress indicators):** -``` -Scanning logs/ for .log files... -Found 42 log files - -Parsing FP (Feasibility Pump) logs... - Running grep on 42 files... - Processing 3046 matching lines... - Progress: 10000/3046 lines, 42 files - Progress: 20000/3046 lines, 42 files - Processed 3046 lines from 42 files - Pairing features with results... - Pairing: 10/42 files, 362 entries found - Pairing: 20/42 files, 724 entries found - Pairing: 30/42 files, 1086 entries found - Pairing: 40/42 files, 1448 entries found - Found 1523 complete entries from 42 files - - Total entries: 1523 - Unique files: 42 - Avg entries per file: 36.26 - Iterations (target): min=1, max=847, avg=142.35 - -Saving 1523 entries to fp_data.pkl... - -====================================================================== -✓ Success! Saved 1523 entries to fp_data.pkl - File size: 2.34 MB -====================================================================== -``` - -**Progress updates:** -- Line processing: Every 10,000 lines -- Pairing: Every 10 files -- Uses carriage return (`\r`) for in-place updates - -### 3. Train Predictor Model - -Use the `train_regressor.py` script with the parsed data: - -```bash -# Train XGBoost model for FP -python scripts/train_regressor.py fp_data.pkl --regressor xgboost --seed 42 - -# Train LightGBM model for PDLP -python scripts/train_regressor.py pdlp_data.pkl --regressor lightgbm --seed 42 - -# View available features before training -python scripts/train_regressor.py cp_data.pkl --regressor xgboost --list-features -``` - -The training script will: -- Load the pickle file -- Split data by files (train/test) -- Train the specified model -- Evaluate performance (R², RMSE, MAE) -- Export to C++ code using TL2cgen (for XGBoost/LightGBM) -- Save model and metadata - ---- - -## Feature Descriptions - -### Problem Structure Features - -- **n_variables** - Total number of decision variables -- **n_constraints** - Total number of constraints -- **n_integer_vars** - Number of integer/binary variables -- **n_binary_vars** - Number of binary (0/1) variables -- **nnz** - Non-zero coefficients in constraint matrix -- **sparsity** - Matrix sparsity: nnz / (n_constraints × n_variables) -- **nnz_stddev** - Standard deviation of non-zeros per constraint row -- **unbalancedness** - Load balancing metric for constraint matrix - -### Solution State Features (FP) - -- **initial_feasibility** - Whether starting solution is feasible (0/1) -- **initial_excess** - Sum of constraint violations -- **initial_objective** - Objective value of initial solution -- **initial_ratio_of_integers** - Fraction of integer vars already integral -- **initial_n_integers** - Count of integer vars at integral values - -### Algorithm Configuration Features (FP) - -- **alpha** - Weight between original objective and distance objective -- **check_distance_cycle** - Whether distance-based cycle detection is enabled -- **cycle_detection_length** - Number of recent solutions tracked -- **has_cutting_plane** - Whether objective cutting plane was added -- **time_budget** - Allocated time in seconds - -### Solver Configuration Features (PDLP) - -- **has_warm_start** - Whether initial primal/dual solution provided -- **time_limit** - Time budget in seconds -- **iteration_limit** - Maximum iterations allowed -- **tolerance** - Optimality tolerance -- **check_infeasibility** - Whether to detect infeasibility -- **return_first_feasible** - Whether to return on first primal feasible - -### Rounding Configuration Features (CP) - -- **n_unset_vars** - Integer variables not yet set -- **round_all_vars** - Whether to round all variables or selective -- **lp_run_time_after_feasible** - Time budget for post-feasibility LP - ---- - -## Integration with Existing Predictor - -The FJ predictor already exists at `cpp/src/utilities/models/fj_predictor/`. The same workflow can be used: - -1. Collect training data as described above -2. Train XGBoost model -3. Export to C++ using TreeLite (as done for FJ) -4. Integrate into solver with work unit → iteration conversion - -Example from FJ predictor: -```cpp -// cpp/src/mip/feasibility_jump/feasibility_jump.cu:1283-1291 -if (settings.work_limit != std::numeric_limits::infinity()) { - std::map features_map = get_feature_vector(0); - float iter_prediction = std::max( - (f_t)0.0, - (f_t)ceil(context.work_unit_predictors.fj_predictor.predict_scalar(features_map)) - ); - CUOPT_LOG_DEBUG("FJ determ: Estimated number of iterations for %f WU: %f", - settings.work_limit, - iter_prediction); - settings.iteration_limit = std::min(settings.iteration_limit, (i_t)iter_prediction); -} -``` - ---- - -## Next Steps - -1. **Collect Data**: Run solver on diverse problem sets with logging enabled -2. **Analyze**: Examine feature importance and correlation with execution time -3. **Train Models**: Build iteration predictors for FP, PDLP, and CP -4. **Validate**: Test predictors maintain solution quality while achieving determinism -5. **Deploy**: Integrate trained models into solver (similar to FJ predictor) -6. **Hierarchical Allocation**: Implement work unit budget allocation across nested algorithms - ---- - -## Complete Workflow Example - -Here's a complete end-to-end example: - -### Step 1: Run Solver and Collect Logs - -```bash -# Set log level to capture feature logs -export CUOPT_LOG_LEVEL=INFO - -# Run your solver on test problems -./my_solver problem1.mps > logs/problem1.log 2>&1 -./my_solver problem2.mps > logs/problem2.log 2>&1 -# ... run on many problems -``` - -### Step 2: Parse Logs for Each Algorithm - -```bash -# Parse FP logs -python scripts/determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl - -# Parse PDLP logs -python scripts/determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.pkl - -# Parse CP logs -python scripts/determinism_logs_parse.py logs/ --algorithm CP -o cp_data.pkl -``` - -### Step 3: Inspect Features - -```bash -# See what features are available for FP -python scripts/train_regressor.py fp_data.pkl --regressor xgboost --list-features -``` - -Output: -``` -====================================================================== -Available features in dataset (28 total): -====================================================================== - 1. alpha - 2. check_distance_cycle - 3. cycle_detection_length - 4. has_cutting_plane - 5. initial_excess - 6. initial_feasibility - 7. initial_n_integers - 8. initial_objective - 9. initial_ratio_of_integers - 10. n_binary_vars - 11. n_constraints - 12. n_integer_vars - 13. n_variables - 14. nnz - 15. nnz_stddev - 16. sparsity - 17. time_budget - 18. unbalancedness - ... -``` - -### Step 4: Train Models - -```bash -# Train FP predictor with XGBoost -python scripts/train_regressor.py fp_data.pkl \ - --regressor xgboost \ - --seed 42 \ - --early-stopping 20 \ - --treelite-compile 8 - -# Train PDLP predictor with LightGBM -python scripts/train_regressor.py pdlp_data.pkl \ - --regressor lightgbm \ - --seed 42 \ - --early-stopping 20 \ - --treelite-compile 8 -``` - -### Step 5: Review Results - -The training script will output: -- Cross-validation scores -- Train/test metrics (R², RMSE, MAE) -- Feature importance ranking -- Sample predictions -- Worst predictions with feature values - -Example output: -``` -Training complete! - -Test Set Metrics: - MSE: 1234.5678 - RMSE: 35.14 - MAE: 22.67 - R²: 0.8542 - -Feature Importance: - 1. n_variables : 0.245123 - 2. n_constraints : 0.187456 - 3. initial_ratio_of_integers : 0.156234 - 4. sparsity : 0.098765 - ... - -C source code generated to: ./models/fp_data_c_code/ - Contains optimized model source code (branch-annotated, quantized) -``` - -### Step 6: Integrate into Solver - -The generated C++ code will be in `./models/_data_c_code/`: -- `header.h` - Class declaration with predict functions -- `main.cpp` - Implementation -- `quantize.cpp` - Quantization helpers (if enabled) - -Copy these files to `cpp/src/utilities/models/_predictor/` and integrate similar to the existing FJ predictor. - ---- - -## Notes - -- Line Segment Search was excluded as it can be predicted from FJ predictor (it runs FJ internally) -- CP iteration tracking needs enhancement (currently logs 0 iterations) -- Consider adding more dynamic features during execution for better predictions -- The termination reasons can help understand when algorithms succeed/fail -- Use `--stratify-split` when training to ensure balanced train/test distribution -- The `--early-stopping` parameter helps prevent overfitting on tree models -- TL2cgen compilation with `--treelite-compile` generates optimized C++ code with branch annotation and quantization enabled by default diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 49552bef5c..33f228b492 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -57,7 +57,7 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_tn_constraints, context.problem_ptr->handle_ptr->get_stream()), ls(context, lp_optimal_solution), - timer(context.settings.deterministic, diversity_config.default_time_limit), + timer(context.gpu_heur_loop, diversity_config.default_time_limit), bound_prop_recombiner(context, context.problem_ptr->n_variables, ls.constraint_prop, @@ -112,6 +112,8 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::run_presolve(f_t time_limit) { raft::common::nvtx::range fun_scope("run_presolve"); CUOPT_LOG_INFO("Running presolve!"); - work_limit_timer_t presolve_timer(context.settings.deterministic, time_limit); + work_limit_timer_t presolve_timer(context.gpu_heur_loop, time_limit); auto term_crit = ls.constraint_prop.bounds_update.solve(*problem_ptr); presolve_timer.record_work(0); if (ls.constraint_prop.bounds_update.infeas_constraints_count > 0) { @@ -232,7 +234,7 @@ void diversity_manager_t::generate_quick_feasible_solution() // min 1 second, max 10 seconds const f_t generate_fast_solution_time = std::min(diversity_config.max_fast_sol_time, std::max(1., timer.remaining_time() / 20.)); - work_limit_timer_t sol_timer(context.settings.deterministic, generate_fast_solution_time); + work_limit_timer_t sol_timer(context.gpu_heur_loop, generate_fast_solution_time); // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); sol_timer.record_work(0); @@ -336,7 +338,7 @@ solution_t diversity_manager_t::run_solver() std::min(diversity_config.max_time_on_lp, time_limit * diversity_config.time_ratio_on_init_lp); // to automatically compute the solving time on scope exit auto timer_raii_guard = - cuopt::scope_guard([&]() { stats.total_solve_time = timer.elapsed_time(); }); + cuopt::scope_guard([&]() { stats.total_solve_time = timer.timer.elapsed_time(); }); // after every change to the problem, we should resize all the relevant vars // we need to encapsulate that to prevent repetitions recombine_stats.reset(); @@ -366,7 +368,7 @@ solution_t diversity_manager_t::run_solver() const f_t max_time_on_probing = diversity_config.max_time_on_probing; f_t time_for_probing_cache = std::min(max_time_on_probing, time_limit * time_ratio_of_probing_cache); - work_limit_timer_t probing_timer{context.settings.deterministic, time_for_probing_cache}; + work_limit_timer_t probing_timer{context.gpu_heur_loop, time_for_probing_cache}; if (check_b_b_preemption()) { return population.best_feasible(); } if (!diversity_config.fj_only_run) { compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); diff --git a/cpp/src/mip/diversity/population.cu b/cpp/src/mip/diversity/population.cu index 56eedaf4ad..d2f9200d15 100644 --- a/cpp/src/mip/diversity/population.cu +++ b/cpp/src/mip/diversity/population.cu @@ -54,7 +54,7 @@ population_t::population_t(std::string const& name_, rng(cuopt::seed_generator::get_seed()), early_exit_primal_generation(false), population_hash_map(*problem_ptr), - timer(context.settings.deterministic, 0) + timer(context.gpu_heur_loop, 0) { best_feasible_objective = std::numeric_limits::max(); } diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index 756942819c..4ca19436c1 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -187,7 +187,7 @@ class bound_prop_recombiner_t : public recombiner_t { if (guiding_solution.get_feasible() && !a.problem_ptr->expensive_to_fix_vars) { this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); - work_limit_timer_t timer(this->context.settings.deterministic, + work_limit_timer_t timer(this->context.gpu_heur_loop, bp_recombiner_config_t::bounds_prop_time_limit); rmm::device_uvector old_assignment(offspring.assignment, offspring.handle_ptr->get_stream()); @@ -238,7 +238,7 @@ class bound_prop_recombiner_t : public recombiner_t { } a.handle_ptr->sync_stream(); } else { - work_limit_timer_t timer(this->context.settings.deterministic, + work_limit_timer_t timer(this->context.gpu_heur_loop, bp_recombiner_config_t::bounds_prop_time_limit); get_probing_values_for_infeasible( guiding_solution, other_solution, offspring, probing_values, n_vars_from_other); diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index 17c8c89e62..ade72a23b6 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -122,13 +122,7 @@ class fp_recombiner_t : public recombiner_t { offspring.handle_ptr->sync_stream(); offspring.assignment = std::move(fixed_assignment); cuopt_func_call(offspring.test_variable_bounds(false)); - work_limit_timer_t timer(this->context.settings.deterministic, - fp_recombiner_config_t::fp_time_limit); - if (this->context.settings.deterministic) { - timer = work_limit_timer_t( - this->context.settings.deterministic, - std::numeric_limits::max()); // TODO should be global time limit - } + work_limit_timer_t timer(this->context.gpu_heur_loop, fp_recombiner_config_t::fp_time_limit); fp.timer = timer; fp.cycle_queue.reset(offspring); fp.reset(); diff --git a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh index dce1f05cfa..6a28827315 100644 --- a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh @@ -86,7 +86,7 @@ class line_segment_recombiner_t : public recombiner_t { auto& other_solution = a.get_feasible() ? b : a; // copy the solution from A solution_t offspring(guiding_solution); - work_limit_timer_t line_segment_timer{this->context.settings.deterministic, + work_limit_timer_t line_segment_timer{this->context.gpu_heur_loop, ls_recombiner_config_t::time_limit}; // TODO after we have the conic combination, detect the lambda change // (i.e. the integral variables flip on line segment) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 58d0af50c5..bd11f3aae8 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -1084,7 +1084,7 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) data.incumbent_quality.set_value_async(obj, handle_ptr->get_stream()); - timer_t timer(settings.time_limit); + work_limit_timer_t timer(context.gpu_heur_loop, settings.time_limit); i_t steps; bool limit_reached = false; for (steps = 0; steps < std::numeric_limits::max(); steps += iterations_per_graph) { @@ -1269,7 +1269,7 @@ template i_t fj_t::solve(solution_t& solution) { raft::common::nvtx::range scope("fj_solve"); - timer_t timer(settings.time_limit); + work_limit_timer_t timer(context.gpu_heur_loop, settings.time_limit); handle_ptr = const_cast(solution.handle_ptr); pb_ptr = solution.problem_ptr; if (settings.mode != fj_mode_t::ROUNDING) { @@ -1279,6 +1279,7 @@ i_t fj_t::solve(solution_t& solution) pb_ptr->check_problem_representation(true); resize_vectors(solution.handle_ptr); + if (context.settings.deterministic) { settings.work_limit = settings.time_limit; } // if work_limit is set: compute an estimate of the number of iterations required if (settings.work_limit != std::numeric_limits::infinity()) { std::map features_map = get_feature_vector(0); @@ -1287,6 +1288,7 @@ i_t fj_t::solve(solution_t& solution) CUOPT_LOG_DEBUG("FJ determ: Estimated number of iterations for %f WU: %f", settings.work_limit, iter_prediction); + if (settings.work_limit == 0) iter_prediction = 0; settings.iteration_limit = std::min(settings.iteration_limit, (i_t)iter_prediction); } @@ -1376,19 +1378,54 @@ i_t fj_t::solve(solution_t& solution) cuopt_func_call(solution.test_variable_bounds()); - // Print compact feature vector summary - char logbuf[4096]; - int offset = 0; - offset += snprintf(logbuf + offset, - sizeof(logbuf) - offset, - "FJ: iter=%d time=%g", - iterations, - timer.elapsed_time()); - for (const auto& [name, value] : feature_vector) { - offset += snprintf(logbuf + offset, sizeof(logbuf) - offset, " %s=%g", name.c_str(), value); - if (offset >= (int)(sizeof(logbuf) - 32)) break; + double work_to_record = settings.work_limit; + + if (iterations < settings.iteration_limit) { + CUOPT_LOG_DEBUG( + "FJ early exit at %d iterations (limit: %d)", iterations, settings.iteration_limit); + // Compute the work unit corresponding to the number of iterations elapsed + // by incrementally guessing work units until the model predicts >= actual iterations + if (context.settings.deterministic && iterations > 0) { + double guessed_work = 0.0; + const double work_increment = 0.1; + const double max_work = settings.work_limit * 2.0; // Safety limit + float predicted_iters = 0.0f; + + // Make a copy of the feature vector and modify the time/work_limit field + std::map features_for_prediction = feature_vector; + + while (guessed_work <= max_work) { + features_for_prediction["time"] = (float)guessed_work; + predicted_iters = std::max( + 0.0f, + (float)ceil( + context.work_unit_predictors.fj_predictor.predict_scalar(features_for_prediction))); + + if (predicted_iters >= (float)iterations) { + work_to_record = guessed_work; + break; + } + + guessed_work += work_increment; + } + } } - CUOPT_LOG_INFO("%s", logbuf); + + timer.record_work(work_to_record); + + // // Print compact feature vector summary + // char logbuf[4096]; + // int offset = 0; + // offset += snprintf(logbuf + offset, + // sizeof(logbuf) - offset, + // "FJ: iter=%d time=%g", + // iterations, + // timer.elapsed_time()); + // for (const auto& [name, value] : feature_vector) { + // offset += snprintf(logbuf + offset, sizeof(logbuf) - offset, " %s=%g", name.c_str(), value); + // if (offset >= (int)(sizeof(logbuf) - 32)) break; + // } + // CUOPT_LOG_INFO("%s", logbuf); return is_new_feasible; } diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index 75624e1fbc..403600c449 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -61,7 +61,7 @@ feasibility_pump_t::feasibility_pump_t( context.problem_ptr->handle_ptr->get_stream()), lp_optimal_solution(lp_optimal_solution_), rng(cuopt::seed_generator::get_seed()), - timer(context.settings.deterministic, 20.) + timer(context.gpu_heur_loop, 20.) { thrust::fill(context.problem_ptr->handle_ptr->get_thrust_policy(), last_projection.begin(), @@ -147,7 +147,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tvariable_bounds, solution.handle_ptr->get_stream()); @@ -274,7 +274,7 @@ bool feasibility_pump_t::round(solution_t& solution) { bool result; // CUOPT_LOG_DEBUG("Rounding the point"); - work_limit_timer_t bounds_prop_timer(context.settings.deterministic, + work_limit_timer_t bounds_prop_timer(context.gpu_heur_loop, std::max(0.05, std::min(0.5, timer.remaining_time() / 10.))); const f_t lp_run_time_after_feasible = 0.; bool old_var = constraint_prop.round_all_vars; @@ -337,11 +337,7 @@ bool feasibility_pump_t::test_fj_feasible(solution_t& soluti fj.settings.feasibility_run = true; fj.settings.n_of_minimums_for_exit = 5000; fj.settings.time_limit = std::min(time_limit, timer.remaining_time()); - if (context.settings.deterministic) { - fj.settings.time_limit = timer.remaining_time(); - fj.settings.work_limit = fj.settings.time_limit; - fj.settings.iteration_limit = 10000; - } + if (context.settings.deterministic) { fj.settings.work_limit = fj.settings.time_limit; } cuopt_func_call(solution.test_variable_bounds(true)); is_feasible = fj.solve(solution); cuopt_func_call(solution.test_variable_bounds(true)); @@ -366,7 +362,7 @@ template bool feasibility_pump_t::handle_cycle(solution_t& solution) { raft::common::nvtx::range fun_scope("handle_cycle"); - CUOPT_LOG_DEBUG("running handle cycle"); + // CUOPT_LOG_DEBUG("running handle cycle"); bool is_feasible = false; fp_fj_cycle_time_begin = timer.remaining_time(); CUOPT_LOG_DEBUG("Running longer FJ on last rounding"); @@ -443,15 +439,15 @@ bool feasibility_pump_t::check_distance_cycle(solution_t& so std::accumulate(last_distances.begin(), last_distances.end(), 0.0) / last_distances.size(); if (avg_distance - distance_to_last_rounding < config.cycle_distance_reduction_ration * avg_distance) { - CUOPT_LOG_DEBUG("Distance cycle detected curr %f avg %f for last %d iter", - distance_to_last_rounding, - avg_distance, - last_distances.size()); + // CUOPT_LOG_DEBUG("Distance cycle detected curr %f avg %f for last %d iter", + // distance_to_last_rounding, + // avg_distance, + // last_distances.size()); is_cycle = true; } last_distances.pop_back(); } else { - CUOPT_LOG_DEBUG("Distance of projection: %f", distance_to_last_rounding); + // CUOPT_LOG_DEBUG("Distance of projection: %f", distance_to_last_rounding); } last_distances.push_front(distance_to_last_rounding); return is_cycle; @@ -582,9 +578,9 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s f_t(solution.n_assigned_integers) / solution.problem_ptr->n_integer_vars; bool is_feasible = linear_project_onto_polytope(solution, ratio_of_assigned_integers); i_t n_integers = solution.compute_number_of_integers(); - CUOPT_LOG_DEBUG("after fp projection n_integers %d total n_integes %d", - n_integers, - solution.problem_ptr->n_integer_vars); + // CUOPT_LOG_DEBUG("after fp projection n_integers %d total n_integes %d", + // n_integers, + // solution.problem_ptr->n_integer_vars); bool is_cycle = true; // temp comment for presolve run if (config.check_distance_cycle) { @@ -657,7 +653,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s is_feasible = round(solution); cuopt_func_call(solution.test_variable_bounds(true)); proj_and_round_time = proj_begin - timer.remaining_time(); - if (!is_feasible) { + if (!is_feasible && proj_and_round_time > 0) { const f_t time_ratio = 0.2; is_feasible = test_fj_feasible(solution, time_ratio * proj_and_round_time); } diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu index 6371b7bd5e..475e8d9914 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu @@ -244,7 +244,7 @@ bool line_segment_search_t::search_line_segment( best_feasible_cost, curr_cost); } - // cuopt_assert(context.settings.deterministic, ""); + // cuopt_assert(context.gpu_heur_loop, ""); if (!context.settings.deterministic) { if (timer.check_time_limit()) { break; } } diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 859b7aab08..8fd8ba503b 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -234,7 +234,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, { if (time_limit == 0.) return solution.get_feasible(); - work_limit_timer_t timer(context.settings.deterministic, time_limit); + work_limit_timer_t timer(context.gpu_heur_loop, time_limit); auto h_weights = cuopt::host_copy(in_fj.cstr_weights, solution.handle_ptr->get_stream()); auto h_objective_weight = in_fj.objective_weight.value(solution.handle_ptr->get_stream()); @@ -334,7 +334,7 @@ void local_search_t::generate_fast_solution(solution_t& solu fj.settings.time_limit = std::numeric_limits::infinity(); while (!timer.check_time_limit()) { work_limit_timer_t constr_prop_timer = - work_limit_timer_t(context.settings.deterministic, std::min(timer.remaining_time(), 2.)); + work_limit_timer_t(context.gpu_heur_loop, std::min(timer.remaining_time(), 2.)); // do constraint prop on lp optimal solution constraint_prop.apply_round(solution, 1., constr_prop_timer); constr_prop_timer.record_work(0); @@ -362,10 +362,10 @@ bool local_search_t::run_local_search(solution_t& solution, if (!solution.get_feasible()) { if (ls_config.at_least_one_parent_feasible) { fj_settings.time_limit = 0.5; - timer = work_limit_timer_t(context.settings.deterministic, fj_settings.time_limit); + timer = work_limit_timer_t(context.gpu_heur_loop, fj_settings.time_limit); } else { fj_settings.time_limit = 0.25; - timer = work_limit_timer_t(context.settings.deterministic, fj_settings.time_limit); + timer = work_limit_timer_t(context.gpu_heur_loop, fj_settings.time_limit); } } else { fj_settings.time_limit = std::min(1., timer.remaining_time()); @@ -486,7 +486,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu cuopt_func_call(solution.test_variable_bounds(false)); f_t lp_run_time_after_feasible = std::min(1., timer.remaining_time()); work_limit_timer_t bounds_prop_timer = - work_limit_timer_t(context.settings.deterministic, std::min(timer.remaining_time(), 10.)); + work_limit_timer_t(context.gpu_heur_loop, std::min(timer.remaining_time(), 10.)); bool is_feasible = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); bounds_prop_timer.record_work(0); @@ -566,7 +566,7 @@ bool local_search_t::run_staged_fp(solution_t& solution, } CUOPT_LOG_DEBUG("Running staged FP from beginning it %d", i); fp.relax_general_integers(solution); - work_limit_timer_t binary_timer(context.settings.deterministic, timer.remaining_time() / 3); + work_limit_timer_t binary_timer(context.gpu_heur_loop, timer.remaining_time() / 3); i_t binary_it_counter = 0; for (; binary_it_counter < 100; ++binary_it_counter) { if (population_ptr->preempt_heuristic_solver_.load()) { @@ -744,7 +744,7 @@ bool local_search_t::run_fp(solution_t& solution, is_feasible ? solution.get_objective() : std::numeric_limits::max(); rmm::device_uvector best_solution(solution.assignment, solution.handle_ptr->get_stream()); problem_t* old_problem_ptr = solution.problem_ptr; - fp.timer = work_limit_timer_t(context.settings.deterministic, timer.remaining_time()); + fp.timer = work_limit_timer_t(context.gpu_heur_loop, timer.remaining_time()); // if it has not been initialized yet, create a new problem and move it to the cut problem if (!problem_with_objective_cut.cutting_plane_added) { problem_with_objective_cut = std::move(problem_t(*old_problem_ptr)); @@ -845,7 +845,7 @@ bool local_search_t::generate_solution(solution_t& solution, { raft::common::nvtx::range fun_scope("generate_solution"); cuopt_assert(population_ptr != nullptr, "Population pointer must not be null"); - work_limit_timer_t timer(context.settings.deterministic, time_limit); + work_limit_timer_t timer(context.gpu_heur_loop, time_limit); auto n_vars = solution.problem_ptr->n_variables; auto n_binary_vars = solution.problem_ptr->get_n_binary_variables(); auto n_integer_vars = solution.problem_ptr->n_integer_vars; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 87d2a778b6..016aa03b38 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -784,8 +784,7 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob i_t n_of_repairs_needed_for_feasible = 0; i_t iter_limit = std::numeric_limits::max(); if (this->context.settings.deterministic) { - timer = - work_limit_timer_t(context.settings.deterministic, std::numeric_limits::infinity()); + timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); iter_limit = 100; } do { @@ -875,8 +874,7 @@ bool constraint_prop_t::find_integer( // CHANGE if (this->context.settings.deterministic) { - timer = - work_limit_timer_t(context.settings.deterministic, std::numeric_limits::infinity()); + timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); } lb_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); @@ -928,9 +926,9 @@ bool constraint_prop_t::find_integer( set_bounds_on_fixed_vars(sol); } - CUOPT_LOG_DEBUG("Bounds propagation rounding: unset vars %lu", unset_integer_vars.size()); + // CUOPT_LOG_DEBUG("Bounds propagation rounding: unset vars %lu", unset_integer_vars.size()); if (unset_integer_vars.size() == 0) { - CUOPT_LOG_DEBUG("No integer variables provided in the bounds prop rounding"); + // CUOPT_LOG_DEBUG("No integer variables provided in the bounds prop rounding"); expand_device_copy(orig_sol.assignment, sol.assignment, sol.handle_ptr->get_stream()); cuopt_func_call(orig_sol.test_variable_bounds()); return orig_sol.compute_feasibility(); @@ -1015,7 +1013,7 @@ bool constraint_prop_t::find_integer( if (!(n_failed_repair_iterations >= max_n_failed_repair_iterations) && rounding_ii && !timeout_happened) { // timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; - work_limit_timer_t repair_timer(context.settings.deterministic, timer.remaining_time() / 5); + work_limit_timer_t repair_timer(context.gpu_heur_loop, timer.remaining_time() / 5); save_bounds(sol); // update bounds and run repair procedure bool bounds_repaired = @@ -1070,10 +1068,10 @@ bool constraint_prop_t::find_integer( // which is the unchanged problem bounds multi_probe.update_host_bounds(sol.handle_ptr, make_span(sol.problem_ptr->variable_bounds)); } - CUOPT_LOG_DEBUG( - "Bounds propagation rounding end: ii constraint count first buffer %d, second buffer %d", - multi_probe.infeas_constraints_count_0, - multi_probe.infeas_constraints_count_1); + // CUOPT_LOG_DEBUG( + // "Bounds propagation rounding end: ii constraint count first buffer %d, second buffer %d", + // multi_probe.infeas_constraints_count_0, + // multi_probe.infeas_constraints_count_1); cuopt_assert(sol.test_number_all_integer(), "All integers must be rounded"); expand_device_copy(orig_sol.assignment, sol.assignment, sol.handle_ptr->get_stream()); cuopt_func_call(orig_sol.test_variable_bounds()); @@ -1136,10 +1134,9 @@ bool constraint_prop_t::apply_round( // lp_run_time_after_feasible); // === CONSTRAINT PROP PREDICTOR FEATURES - END === - max_timer = work_limit_timer_t{context.settings.deterministic, max_time_for_bounds_prop}; + max_timer = work_limit_timer_t{context.gpu_heur_loop, max_time_for_bounds_prop}; if (this->context.settings.deterministic) { - max_timer = - work_limit_timer_t(context.settings.deterministic, std::numeric_limits::infinity()); + max_timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); } if (check_brute_force_rounding(sol)) { auto cp_end_time = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/mip/local_search/rounding/lb_constraint_prop.cu b/cpp/src/mip/local_search/rounding/lb_constraint_prop.cu index acf28b961d..475d4a3574 100644 --- a/cpp/src/mip/local_search/rounding/lb_constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/lb_constraint_prop.cu @@ -718,7 +718,7 @@ bool lb_constraint_prop_t::apply_round( // this is second timer that can continue but without recovery mode const f_t max_time_for_bounds_prop = 5.; - max_timer = work_limit_timer_t{context.settings.deterministic, max_time_for_bounds_prop}; + max_timer = work_limit_timer_t{context.gpu_heur_loop, max_time_for_bounds_prop}; if (check_brute_force_rounding(sol)) { return true; } recovery_mode = false; rounding_ii = false; diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index a615d84507..ae8742866b 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -89,13 +89,13 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.iteration_limit = settings.iteration_limit; if (settings.work_limit != std::numeric_limits::infinity()) { // try to estimate the iteration count based on the requested work limit - int estim_iters = 0; + int estim_iters = 100; do { // TODO: use an actual predictor model here double estim_ms = 313 + 200 * op_problem.n_variables - 400 * op_problem.n_constraints + 600 * op_problem.coefficients.size() + 7100 * estim_iters; estim_ms = std::max(0.0, estim_ms); - if (estim_ms > settings.work_limit) { break; } + if (estim_ms > settings.work_limit * 1000) { break; } estim_iters += 100; } while (true); CUOPT_LOG_DEBUG("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); @@ -110,12 +110,12 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_solver_t lp_solver(op_problem, pdlp_settings); if (settings.has_initial_primal) { i_t prev_size = lp_state.prev_dual.size(); - CUOPT_LOG_DEBUG( - "setting initial primal solution of size %d dual size %d problem vars %d cstrs %d", - assignment.size(), - lp_state.prev_dual.size(), - op_problem.n_variables, - op_problem.n_constraints); + // CUOPT_LOG_DEBUG( + // "setting initial primal solution of size %d dual size %d problem vars %d cstrs %d", + // assignment.size(), + // lp_state.prev_dual.size(), + // op_problem.n_variables, + // op_problem.n_constraints); lp_state.resize(op_problem, op_problem.handle_ptr->get_stream()); clamp_within_var_bounds(assignment, &op_problem, op_problem.handle_ptr); // The previous dual sometimes contain invalid values w.r.t current problem diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 0827068a26..71d02f531f 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -71,7 +71,7 @@ static void setup_device_symbols(rmm::cuda_stream_view stream_view) template mip_solution_t run_mip(detail::problem_t& problem, mip_solver_settings_t const& settings, - cuopt::work_limit_timer_t& timer) + timer_t& timer) { raft::common::nvtx::range fun_scope("run_mip"); auto constexpr const running_mip = true; @@ -194,7 +194,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, op_problem.get_handle_ptr()->get_stream()); } - auto timer = cuopt::work_limit_timer_t(settings.deterministic, time_limit); + auto timer = timer_t(time_limit); double presolve_time = 0.0; std::unique_ptr> presolver; diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index bcce85439c..a27b370887 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -53,7 +53,7 @@ template mip_solver_t::mip_solver_t(const problem_t& op_problem, const mip_solver_settings_t& solver_settings, pdlp_initial_scaling_strategy_t& scaling, - work_limit_timer_t timer) + timer_t timer) : op_problem_(op_problem), solver_settings_(solver_settings), context(op_problem.handle_ptr, @@ -112,7 +112,7 @@ solution_t mip_solver_t::run_solver() } diversity_manager_t dm(context); - dm.timer = timer_; + dm.timer = work_limit_timer_t(context.gpu_heur_loop, timer_.remaining_time()); bool presolve_success = dm.run_presolve(timer_.remaining_time()); if (!presolve_success) { CUOPT_LOG_INFO("Problem proven infeasible in presolve"); diff --git a/cpp/src/mip/solver.cuh b/cpp/src/mip/solver.cuh index 012c5fdfa6..261e32c7b4 100644 --- a/cpp/src/mip/solver.cuh +++ b/cpp/src/mip/solver.cuh @@ -31,7 +31,7 @@ class mip_solver_t { explicit mip_solver_t(const problem_t& op_problem, const mip_solver_settings_t& solver_settings, pdlp_initial_scaling_strategy_t& scaling, - work_limit_timer_t timer); + timer_t timer); solution_t run_solver(); solver_stats_t& get_solver_stats() { return context.stats; } @@ -40,7 +40,7 @@ class mip_solver_t { // reference to the original problem const problem_t& op_problem_; const mip_solver_settings_t& solver_settings_; - work_limit_timer_t timer_; + timer_t timer_; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/solver_context.cuh b/cpp/src/mip/solver_context.cuh index 3a1133e1fa..211cb83abe 100644 --- a/cpp/src/mip/solver_context.cuh +++ b/cpp/src/mip/solver_context.cuh @@ -22,6 +22,7 @@ #include #include +#include #include #pragma once @@ -43,8 +44,9 @@ struct mip_solver_context_t { : handle_ptr(handle_ptr_), problem_ptr(problem_ptr_), settings(settings_), scaling(scaling) { cuopt_assert(problem_ptr != nullptr, "problem_ptr is nullptr"); - stats.solution_bound = problem_ptr->maximize ? std::numeric_limits::infinity() - : -std::numeric_limits::infinity(); + stats.solution_bound = problem_ptr->maximize ? std::numeric_limits::infinity() + : -std::numeric_limits::infinity(); + gpu_heur_loop.deterministic = settings.deterministic; } raft::handle_t const* const handle_ptr; @@ -54,6 +56,9 @@ struct mip_solver_context_t { solver_stats_t stats; // TODO: ensure thread local (or use locks...?) mip_solver_work_unit_predictors_t work_unit_predictors; + // Work limit context for tracking work units in deterministic mode (shared across all timers in + // GPU heuristic loop) + cuopt::work_limit_context_t gpu_heur_loop; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp index dad1446aa9..2cd644786a 100644 --- a/cpp/src/utilities/work_limit_timer.hpp +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -22,13 +22,37 @@ namespace cuopt { +// Context for tracking global work units across multiple timers in deterministic mode +// This allows different subsystems to have independent work unit tracking +struct work_limit_context_t { + double global_work_units_elapsed{0.0}; + bool deterministic{false}; + + void record_work(double work) + { + if (deterministic) global_work_units_elapsed += work; + } +}; + // In determinism mode, relies on a work limit accumulator; otherwise rely on a timer // in non-determinism mode: 1s = 1wu +// In deterministic mode, all timers share a global work units counter (via work_limit_context_t), +// and each timer records the snapshot at construction time to determine when its own limit is +// reached. class work_limit_timer_t { public: - work_limit_timer_t() : deterministic(false), work_limit(0), timer(0) {} - work_limit_timer_t(bool deterministic, double work_limit_) - : deterministic(deterministic), work_limit(work_limit_), timer(work_limit_) + work_limit_timer_t() + : deterministic(false), work_limit(0), timer(0), work_context(nullptr), work_units_at_start(0) + { + } + + // Constructor taking work limit context reference (for deterministic mode) + work_limit_timer_t(work_limit_context_t& context, double work_limit_) + : deterministic(context.deterministic), + work_limit(work_limit_), + timer(work_limit_), + work_context(&context), + work_units_at_start(context.deterministic ? context.global_work_units_elapsed : 0) { } @@ -37,19 +61,25 @@ class work_limit_timer_t { int line = __builtin_LINE()) const noexcept { if (deterministic) { - bool finished_now = work_total >= work_limit; + if (!work_context) { return false; } + // Check if global work has exceeded our budget (snapshot + limit) + double elapsed_since_start = work_context->global_work_units_elapsed - work_units_at_start; + bool finished_now = elapsed_since_start >= work_limit; if (finished_now && !finished) { finished = true; double actual_elapsed_time = timer.elapsed_time(); // 10% timing error - if (abs(actual_elapsed_time - work_limit) / work_limit > 0.10) { + if (work_limit > 0 && abs(actual_elapsed_time - work_limit) / work_limit > 0.10) { CUOPT_LOG_ERROR( - "%s:%d: %s(): Work limit timer finished with a large discrepancy: %fs for %fwu", + "%s:%d: %s(): Work limit timer finished with a large discrepancy: %fs for %fwu " + "(global: %f, start: %f)", file, line, caller, actual_elapsed_time, - work_limit); + work_limit, + work_context->global_work_units_elapsed, + work_units_at_start); } } return finished; @@ -58,16 +88,30 @@ class work_limit_timer_t { } } - // in determinism mode, add the work units to the work limit accumulator - void record_work(double work_units) + // in determinism mode, add the work units to the global work limit accumulator + void record_work(double work_units, + const char* caller = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) { - if (deterministic) { work_total += work_units; } + if (deterministic && work_context) { + work_context->global_work_units_elapsed += work_units; + CUOPT_LOG_DEBUG("%s:%d: %s(): Recorded %f work units in %fs, total %f", + file, + line, + caller, + work_units, + timer.elapsed_time(), + work_context->global_work_units_elapsed); + } } double remaining_units() const noexcept { if (deterministic) { - return work_limit - work_total; + if (!work_context) { return work_limit; } + double elapsed_since_start = work_context->global_work_units_elapsed - work_units_at_start; + return work_limit - elapsed_since_start; } else { return timer.remaining_time(); } @@ -78,7 +122,7 @@ class work_limit_timer_t { double elapsed_time() const noexcept { if (deterministic) { - return work_total; + return work_context->global_work_units_elapsed - work_units_at_start; } else { return timer.elapsed_time(); } @@ -94,7 +138,9 @@ class work_limit_timer_t { bool check_half_time() const noexcept { if (deterministic) { - return work_total >= work_limit / 2; + if (!work_context) { return false; } + double elapsed_since_start = work_context->global_work_units_elapsed - work_units_at_start; + return elapsed_since_start >= work_limit / 2; } else { return timer.check_half_time(); } @@ -128,9 +174,12 @@ class work_limit_timer_t { } timer_t timer; - double work_total{}; double work_limit{}; mutable bool finished{false}; bool deterministic{false}; + // Pointer to work limit context (shared across all timers in deterministic mode) + work_limit_context_t* work_context{nullptr}; + // Snapshot of global work units when this timer was created + double work_units_at_start{0}; }; } // namespace cuopt diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index 15f2877748..1f96348a26 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -105,12 +105,13 @@ static uint32_t test_full_run_determinism(std::string path, settings.work_limit = 10; // about 10 seconds of runtime settings.deterministic = true; settings.heuristics_only = true; - auto timer = cuopt::work_limit_timer_t(settings.deterministic, 3000); + auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - diversity_manager.timer = work_limit_timer_t(settings.deterministic, 60000); + work_limit_context_t work_limit_context; + diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); diversity_manager.run_solver(); std::vector hashes; @@ -159,12 +160,13 @@ static uint32_t test_initial_solution_determinism(std::string path, settings.time_limit = 3000.; settings.deterministic = true; settings.heuristics_only = true; - auto timer = cuopt::work_limit_timer_t(settings.deterministic, 3000); + auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - diversity_manager.timer = work_limit_timer_t(settings.deterministic, 60000); + work_limit_context_t work_limit_context; + diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); diversity_manager.diversity_config.initial_solution_only = true; diversity_manager.run_solver(); @@ -214,12 +216,13 @@ static uint32_t test_recombiners_determinism(std::string path, settings.time_limit = 3000.; settings.deterministic = true; settings.heuristics_only = true; - auto timer = cuopt::work_limit_timer_t(settings.deterministic, 3000); + auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - diversity_manager.timer = work_limit_timer_t(settings.deterministic, 60000); + work_limit_context_t work_limit_context; + diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); diversity_manager.diversity_config.dry_run = true; diversity_manager.run_solver(); diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index 36e71481ce..0393c2def6 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -113,7 +113,7 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) auto settings = mip_solver_settings_t{}; settings.time_limit = 30.; settings.deterministic = true; - auto timer = cuopt::work_limit_timer_t(settings.deterministic, 30); + auto timer = cuopt::timer_t(30); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); @@ -149,7 +149,8 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) printf("LP optimal hash: 0x%x\n", detail::compute_hash(lp_optimal_solution)); printf("running mode: %d\n", mode); - local_search.fp.timer = work_limit_timer_t(settings.deterministic, 6000); + work_limit_context_t work_limit_context; + local_search.fp.timer = work_limit_timer_t(work_limit_context, 6000); detail::ls_config_t ls_config{}; @@ -171,11 +172,13 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) iterations++; } } else if (mode == local_search_mode_t::FJ_LINE_SEGMENT) { - local_search.run_fj_line_segment(solution, timer, ls_config); + local_search.run_fj_line_segment( + solution, work_limit_timer_t(work_limit_context, 6000), ls_config); } else if (mode == local_search_mode_t::FJ_ON_ZERO) { - local_search.run_fj_on_zero(solution, timer); + local_search.run_fj_on_zero(solution, work_limit_timer_t(work_limit_context, 6000)); } else if (mode == local_search_mode_t::FJ_ANNEALING) { - local_search.run_fj_annealing(solution, timer, ls_config); + local_search.run_fj_annealing( + solution, work_limit_timer_t(work_limit_context, 6000), ls_config); } std::vector hashes; diff --git a/cpp/tests/mip/mip_utils.cuh b/cpp/tests/mip/mip_utils.cuh index 3a8d6e48b5..5dcbe03718 100644 --- a/cpp/tests/mip/mip_utils.cuh +++ b/cpp/tests/mip/mip_utils.cuh @@ -165,7 +165,7 @@ static fj_state_t run_fj(detail::problem_t& problem, auto settings = mip_solver_settings_t{}; settings.time_limit = 30.; - auto timer = cuopt::work_limit_timer_t(settings.deterministic, 30); + auto timer = timer_t(30); detail::mip_solver_t solver(problem, settings, scaling, timer); detail::solution_t solution(*solver.context.problem_ptr); diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index e15f23ffd1..b35c30ae6b 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -170,11 +170,7 @@ uint32_t test_multi_probe(std::string path, unsigned long seed = std::random_dev problem.reverse_constraints, nullptr, true); - detail::mip_solver_t solver( - problem, - default_settings, - scaling, - cuopt::work_limit_timer_t(default_settings.deterministic, 0)); + detail::mip_solver_t solver(problem, default_settings, scaling, timer_t(0)); detail::bound_presolve_t bnd_prb_0(solver.context); detail::bound_presolve_t bnd_prb_1(solver.context); detail::multi_probe_t multi_probe_prs(solver.context); diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index 55ce8851cc..dc5977404a 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -131,18 +131,13 @@ uint32_t test_probing_cache_determinism(std::string path, problem.reverse_constraints, nullptr, true); - detail::mip_solver_t solver( - problem, - default_settings, - scaling, - cuopt::work_limit_timer_t(default_settings.deterministic, 0)); + detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); detail::bound_presolve_t bnd_prb(solver.context); + work_limit_context_t work_limit_context; // rely on the iteration limit compute_probing_cache( - bnd_prb, - problem, - work_limit_timer_t(default_settings.deterministic, std::numeric_limits::max())); + bnd_prb, problem, work_limit_timer_t(work_limit_context, std::numeric_limits::max())); std::vector, 2>>> cached_values( bnd_prb.probing_cache.probing_cache.begin(), bnd_prb.probing_cache.probing_cache.end()); std::sort(cached_values.begin(), cached_values.end(), [](const auto& a, const auto& b) { From 4e0f79a8d23e9a9abae7bbff635634fbcd71252f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 6 Nov 2025 18:55:55 +0000 Subject: [PATCH 028/225] fix determinism issue in bounds repair, further work limit progress --- .../linear_programming/cuopt/run_mip.cpp | 1 - cpp/src/mip/diversity/diversity_manager.cu | 13 ++- cpp/src/mip/diversity/population.cu | 19 +++- .../mip/feasibility_jump/feasibility_jump.cu | 32 +++++- .../feasibility_jump_kernels.cu | 107 +++++++++++++----- .../mip/feasibility_jump/load_balancing.cuh | 3 +- .../feasibility_pump/feasibility_pump.cu | 8 +- .../line_segment_search.cu | 9 +- cpp/src/mip/local_search/local_search.cu | 1 + .../local_search/rounding/bounds_repair.cu | 10 +- .../local_search/rounding/constraint_prop.cu | 33 ++++-- .../local_search/rounding/lb_bounds_repair.cu | 1 + cpp/src/mip/problem/problem.cu | 2 + cpp/src/mip/relaxed_lp/relaxed_lp.cu | 22 ++-- cpp/src/mip/solution/solution.cu | 5 +- cpp/src/mip/solve.cu | 2 +- cpp/src/mip/solver.cu | 4 +- cpp/src/mip/utils.cuh | 82 ++++++++------ cpp/src/utilities/logger.cpp | 4 +- 19 files changed, 249 insertions(+), 109 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 56a1be629e..950acb4232 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -417,7 +417,6 @@ int main(int argc, char* argv[]) if (program.is_used("--initial-solution-path")) { initial_solution_file = program.get("--initial-solution-path"); } - if (run_dir) { std::queue task_queue; std::queue gpu_queue; diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 33f228b492..075a483ec4 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -155,6 +155,7 @@ void diversity_manager_t::add_user_given_solutions( if (problem_ptr->pre_process_assignment(init_sol_assignment)) { relaxed_lp_settings_t lp_settings; lp_settings.time_limit = std::min(60., timer.remaining_time() / 2); + lp_settings.work_limit = lp_settings.time_limit; lp_settings.tolerance = problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; @@ -327,15 +328,17 @@ solution_t diversity_manager_t::run_solver() raft::common::nvtx::range fun_scope("run_solver"); diversity_config.fj_only_run = false; - if (context.settings.deterministic) { - remaining_work_limit = context.settings.work_limit; - CUOPT_LOG_INFO("Deterministic mode, remaining work limit: %f", remaining_work_limit); - } population.timer = timer; const f_t time_limit = timer.remaining_time(); const f_t lp_time_limit = std::min(diversity_config.max_time_on_lp, time_limit * diversity_config.time_ratio_on_init_lp); + + if (context.settings.deterministic) { + remaining_work_limit = context.settings.work_limit; + CUOPT_LOG_INFO("Deterministic mode, remaining work limit: %f", time_limit); + } + // to automatically compute the solving time on scope exit auto timer_raii_guard = cuopt::scope_guard([&]() { stats.total_solve_time = timer.timer.elapsed_time(); }); @@ -385,6 +388,7 @@ solution_t diversity_manager_t::run_solver() } else if (!diversity_config.fj_only_run || true) { relaxed_lp_settings_t lp_settings; lp_settings.time_limit = lp_time_limit; + lp_settings.work_limit = lp_time_limit; lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; lp_settings.return_first_feasible = false; lp_settings.save_state = true; @@ -676,6 +680,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& lp_run_time = std::min(lp_run_time, timer.remaining_time()); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = lp_run_time; + lp_settings.work_limit = lp_settings.time_limit; lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; lp_settings.return_first_feasible = false; lp_settings.save_state = true; diff --git a/cpp/src/mip/diversity/population.cu b/cpp/src/mip/diversity/population.cu index d2f9200d15..d1969659ac 100644 --- a/cpp/src/mip/diversity/population.cu +++ b/cpp/src/mip/diversity/population.cu @@ -395,10 +395,11 @@ std::pair population_t::add_solution(solution_t&& population_hash_map.insert(sol); double sol_cost = sol.get_quality(weights); bool best_updated = false; - CUOPT_LOG_DEBUG("Adding solution with quality %f and objective %f n_integers %d!", + CUOPT_LOG_DEBUG("Adding solution with quality %f and objective %f n_integers %d, hash %x!", sol_cost, sol.get_user_objective(), - sol.n_assigned_integers); + sol.n_assigned_integers, + sol.get_hash()); // We store the best feasible found so far at index 0. if (sol.get_feasible() && (solutions[0].first == false || sol_cost + OBJECTIVE_EPSILON < indices[0].second)) { @@ -809,23 +810,29 @@ bool population_t::test_invariant() template void population_t::print() { + std::vector hashes; + for (auto& index : indices) + hashes.push_back(solutions[index.first].second.get_hash()); + uint32_t final_hash = compute_hash(hashes); CUOPT_LOG_DEBUG(" -------------- "); - CUOPT_LOG_DEBUG("%s infeas weight %f threshold %d/%d:", + CUOPT_LOG_DEBUG("%s infeas weight %f threshold %d/%d (hash %x):", name.c_str(), infeasibility_importance, var_threshold, - problem_ptr->n_integer_vars); + problem_ptr->n_integer_vars, + final_hash); i_t i = 0; for (auto& index : indices) { if (index.first == 0 && solutions[0].first) { CUOPT_LOG_DEBUG(" Best feasible: %f", solutions[index.first].second.get_user_objective()); } - CUOPT_LOG_DEBUG("%d : %f\t%f\t%f\t%d", + CUOPT_LOG_DEBUG("%d : %f\t%f\t%f\t%d (hash %x)", i, index.second, solutions[index.first].second.get_total_excess(), solutions[index.first].second.get_user_objective(), - solutions[index.first].second.get_feasible()); + solutions[index.first].second.get_feasible(), + solutions[index.first].second.get_hash()); i++; } CUOPT_LOG_DEBUG(" -------------- "); diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index bd11f3aae8..2877c827a9 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -669,10 +669,14 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; + // use_graph = false; + auto& data = *climbers[climber_idx]; auto v = data.view(); settings.seed = cuopt::seed_generator::get_seed(); - // ensure an updated copy of the settings is used device-side + // settings.iteration_limit = 10000; + // CUOPT_LOG_DEBUG("FJ: settings seed %d", settings.seed); + // ensure an updated copy of the settings is used device-side raft::copy(v.settings, &settings, 1, climber_stream); bool is_binary_pb = pb_ptr->n_variables == thrust::count(handle_ptr->get_thrust_policy(), @@ -804,6 +808,7 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream 0, climber_stream); + (void)grid_update_weights; cudaLaunchCooperativeKernel((void*)handle_local_minimum_kernel, grid_update_weights, blocks_update_weights, @@ -837,7 +842,12 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream 0, climber_stream); + // TODO: figure out why the ordering of the violated constraints is ruined // data.violated_constraints.sort(climber_stream); + // printf("[%d] iter: Violated constraints hash: %x\n", data.iterations.value(climber_stream), + // compute_hash( + // make_span(data.violated_constraints.contents, 0, + // data.violated_constraints.set_size.value(climber_stream)), climber_stream)); // { // printf("Violated constraints hash: %x, size: %d\n", compute_hash( @@ -1099,7 +1109,7 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) CUOPT_LOG_TRACE( "FJ " "step %d viol %.2g [%d], obj %.8g, best %.8g, mins %d, maxw %g, " - "objw %g", + "objw %g, sol hash %x, delta hash %x", steps, data.violation_score.value(climber_stream), data.violated_constraints.set_size.value(climber_stream), @@ -1107,7 +1117,9 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) data.best_objective.value(climber_stream), data.local_minimums_reached.value(climber_stream), max_cstr_weight.value(climber_stream), - objective_weight.value(climber_stream)); + objective_weight.value(climber_stream), + solution.get_hash(), + detail::compute_hash(data.jump_move_delta, climber_stream)); } if (!limit_reached) { run_step_device(climber_stream, climber_idx); } @@ -1279,6 +1291,18 @@ i_t fj_t::solve(solution_t& solution) pb_ptr->check_problem_representation(true); resize_vectors(solution.handle_ptr); + CUOPT_LOG_DEBUG("FJ: work_limit %f time_limit %f sol hash %x pb hash %x", + settings.work_limit, + settings.time_limit, + solution.get_hash(), + pb_ptr->get_fingerprint()); + CUOPT_LOG_DEBUG("FJ: weights hash %x, left weights hash %x, right weights hash %x", + detail::compute_hash(cstr_weights), + detail::compute_hash(cstr_left_weights), + detail::compute_hash(cstr_right_weights)); + + settings.load_balancing_mode = fj_load_balancing_mode_t::ALWAYS_OFF; + if (context.settings.deterministic) { settings.work_limit = settings.time_limit; } // if work_limit is set: compute an estimate of the number of iterations required if (settings.work_limit != std::numeric_limits::infinity()) { @@ -1413,6 +1437,8 @@ i_t fj_t::solve(solution_t& solution) timer.record_work(work_to_record); + CUOPT_LOG_DEBUG("FJ sol hash %x", solution.get_hash()); + // // Print compact feature vector summary // char logbuf[4096]; // int offset = 0; diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 0d5e873fdc..cd83803eac 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -25,6 +25,7 @@ #include #include +#include #include @@ -37,6 +38,22 @@ namespace cg = cooperative_groups; namespace cuopt::linear_programming::detail { +template +struct score_with_tiebreaker_comparator { + DI auto operator()(const thrust::pair& a, + const thrust::pair& b) const + { + auto a_score = a.first; + auto a_idx = a.second; + auto b_score = b.first; + auto b_idx = b.second; + + if (a_score > b_score) return a; + if (a_score == b_score && a_idx > b_idx) return a; + return b; + } +}; + template DI thrust::pair move_objective_score( const typename fj_t::climber_data_t::view_t& fj, i_t var_idx, f_t delta) @@ -360,7 +377,7 @@ DI std::pair::move_score_info_t> compute_best_mtm( return std::make_pair(best_val, best_score_info); } -template +template DI void update_jump_value(typename fj_t::climber_data_t::view_t fj, i_t var_idx) { cuopt_assert(var_idx >= 0 && var_idx < fj.pb.n_variables, "invalid variable index"); @@ -387,12 +404,11 @@ DI void update_jump_value(typename fj_t::climber_data_t::view_t fj, i_ fj.pb.check_variable_within_bounds(var_idx, fj.incumbent_assignment[var_idx] + delta), "Var not within bounds!"); } - best_score_info = compute_new_score(fj, var_idx, delta); + best_score_info = compute_new_score(fj, var_idx, delta); } else { - auto [best_val, score_info] = - compute_best_mtm(fj, var_idx); - delta = best_val - fj.incumbent_assignment[var_idx]; - best_score_info = score_info; + auto [best_val, score_info] = compute_best_mtm(fj, var_idx); + delta = best_val - fj.incumbent_assignment[var_idx]; + best_score_info = score_info; } } else { delta = round(1.0 - 2 * fj.incumbent_assignment[var_idx]); @@ -637,8 +653,8 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t #if FJ_SINGLE_STEP DEVICE_LOG_DEBUG( - "=---- FJ[%d]: updated %d [%g/%g] :%.4g+{%.4g}=%.4g score {%g,%g}, d_obj %.2g+%.2g=%.2g, " - "err_range %.2g%%, infeas %.2g, total viol %d\n", + "=---- FJ[%d]: updated %d [%g/%g] :%.4g+{%.4g}=%.4g score {%d,%d}, d_obj %.2g+%.2g=%.2g, " + "err_range %.2g%%, infeas %.2g, total viol %d, obj %x, delta %x, coef %x\n", *fj.iterations, var_idx, get_lower(fj.pb.variable_bounds[var_idx]), @@ -653,7 +669,10 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t *fj.incumbent_objective + fj.jump_move_delta[var_idx] * fj.pb.objective_coefficients[var_idx], delta_rel_err, fj.jump_move_infeasibility[var_idx], - fj.violated_constraints.size()); + fj.violated_constraints.size(), + detail::compute_hash(*fj.incumbent_objective), + detail::compute_hash(fj.jump_move_delta[var_idx]), + detail::compute_hash(fj.pb.objective_coefficients[var_idx])); #endif // reset the score fj.jump_move_scores[var_idx] = fj_t::move_score_t::invalid(); @@ -970,7 +989,7 @@ __global__ void compute_iteration_related_variables_kernel( compute_iteration_related_variables(fj); } -template +template __device__ void compute_mtm_moves(typename fj_t::climber_data_t::view_t fj, bool ForceRefresh) { @@ -1033,7 +1052,12 @@ __device__ void compute_mtm_moves(typename fj_t::climber_data_t::view_ } cuopt_assert(var_idx >= 0 && var_idx < fj.pb.n_variables, ""); - update_jump_value(fj, var_idx); + update_jump_value(fj, var_idx); + // if (move_type == MTMMoveType::FJ_MTM_SATISFIED && threadIdx.x == 0) { + // printf("iter[%d] block[%d] var %d score {%d,%d}, delta %g\n", *fj.iterations, blockIdx.x, + // var_idx, fj.jump_move_scores[var_idx].base, fj.jump_move_scores[var_idx].bonus, + // fj.jump_move_delta[var_idx]); + // } } } @@ -1041,7 +1065,7 @@ template __global__ void compute_mtm_moves_kernel(typename fj_t::climber_data_t::view_t fj, bool ForceRefresh) { - compute_mtm_moves(fj, ForceRefresh); + compute_mtm_moves(fj, ForceRefresh); } template @@ -1053,8 +1077,9 @@ __global__ void select_variable_kernel(typename fj_t::climber_data_t:: fj.settings->seed, *fj.iterations * fj.settings->parameters.max_sampled_moves, 0); using move_score_t = typename fj_t::move_score_t; - __shared__ alignas(move_score_t) char shmem_storage[2 * raft::WarpSize * sizeof(move_score_t)]; - auto* const shmem = (move_score_t*)shmem_storage; + __shared__ alignas(thrust::pair) char + shmem_storage[raft::WarpSize * sizeof(thrust::pair)]; + auto* const shmem = (thrust::pair*)shmem_storage; auto th_best_score = fj_t::move_score_t::invalid(); i_t th_selected_var = std::numeric_limits::max(); @@ -1091,8 +1116,11 @@ __global__ void select_variable_kernel(typename fj_t::climber_data_t:: } } // Block level reduction to get the best variable from the sample + // Use deterministic tie-breaking comparator based on var_idx auto [best_score, reduced_selected_var] = - raft::blockRankedReduce(th_best_score, shmem, th_selected_var, raft::max_op{}); + raft::blockReduce(thrust::make_pair(th_best_score, th_selected_var), + (char*)shmem, + score_with_tiebreaker_comparator{}); if (FIRST_THREAD) { // assign it to print the value outside th_best_score = best_score; @@ -1127,9 +1155,9 @@ __global__ void select_variable_kernel(typename fj_t::climber_data_t:: i_t var_range = get_upper(bounds) - get_lower(bounds); double delta_rel_err = fabs(fj.jump_move_delta[selected_var]) / var_range * 100; DEVICE_LOG_INFO( - "=---- FJ: selected %d [%g/%g] %c :%.4g+{%.4g}=%.4g score {%g,%g}, d_obj %.2g+%.2g->%.2g, " + "=---- FJ: selected %d [%g/%g] %c :%.4g+{%.4g}=%.4g score {%d,%d}, d_obj %.2g+%.2g->%.2g, " "delta_rel_err %.2g%%, " - "infeas %.2g, total viol %d, out of %d\n", + "infeas %.2g, total viol %d, out of %d, obj %x\n", selected_var, get_lower(bounds), get_upper(bounds), @@ -1146,7 +1174,8 @@ __global__ void select_variable_kernel(typename fj_t::climber_data_t:: delta_rel_err, fj.jump_move_infeasibility[selected_var], fj.violated_constraints.size(), - good_var_count); + good_var_count, + detail::compute_hash(*fj.incumbent_objective)); #endif cuopt_assert(fj.jump_move_scores[selected_var].valid(), ""); } else { @@ -1183,6 +1212,12 @@ DI thrust::tuple::move_score_t> gridwide_reduc // affected by tabu f_t delta = get_move(var_idx); + + // if (!WeakTabu && !recompute_score) { + // printf("iter[%d] block[%d] considered var %d delta %g\n", *fj.iterations, blockIdx.x, + // var_idx, delta); + // } + if constexpr (WeakTabu) { if ((delta < 0 && *fj.iterations == fj.tabu_lastinc[var_idx] + 1) || (delta > 0 && *fj.iterations == fj.tabu_lastdec[var_idx] + 1)) @@ -1203,6 +1238,12 @@ DI thrust::tuple::move_score_t> gridwide_reduc loc_best_score_info = compute_new_score(fj, var_idx, delta); } + // if (!WeakTabu && !recompute_score) { + // printf("iter[%d] block[%d] found score (%d,%d) for var %d delta %g\n", *fj.iterations, + // blockIdx.x, loc_best_score_info.score.base, loc_best_score_info.score.bonus, var_idx, + // delta); + // } + if (threadIdx.x == 0) { if (loc_best_score_info.score > best_score || (loc_best_score_info.score == best_score && var_idx > best_var)) { @@ -1214,6 +1255,11 @@ DI thrust::tuple::move_score_t> gridwide_reduc } if (threadIdx.x == 0) { + // if (!WeakTabu && !recompute_score) { + // printf("iter[%d] block[%d] var_idx %d score {%d,%d}, delta %g\n", *fj.iterations, + // blockIdx.x, best_var, best_score.base, best_score.bonus, best_delta); + // } + fj.grid_score_buf[blockIdx.x] = best_score; fj.grid_var_buf[blockIdx.x] = best_var; fj.grid_delta_buf[blockIdx.x] = best_delta; @@ -1225,27 +1271,32 @@ DI thrust::tuple::move_score_t> gridwide_reduc if (blockIdx.x == 0) { using move_score_t = typename fj_t::move_score_t; - __shared__ alignas(move_score_t) char shmem_storage[2 * raft::WarpSize * sizeof(move_score_t)]; - auto* const shmem = (move_score_t*)shmem_storage; + __shared__ alignas(thrust::pair) char + shmem_storage[2 * raft::WarpSize * sizeof(thrust::pair)]; + auto* const shmem = (thrust::pair*)shmem_storage; auto th_best_score = fj_t::move_score_t::invalid(); i_t th_best_block = 0; + i_t th_best_var = -1; for (i_t i = threadIdx.x; i < gridDim.x; i += blockDim.x) { auto var_idx = fj.grid_var_buf[i]; auto move_score = fj.grid_score_buf[i]; - if (move_score > th_best_score || - (move_score == th_best_score && var_idx > fj.grid_var_buf[th_best_block])) { + if (move_score > th_best_score || (move_score == th_best_score && var_idx > th_best_var)) { th_best_score = move_score; th_best_block = i; + th_best_var = var_idx; } } // Block level reduction to get the best variable from all blocks - auto [reduced_best_score, reduced_best_block] = - raft::blockRankedReduce(th_best_score, shmem, th_best_block, raft::max_op{}); - - if (reduced_best_score.valid() && threadIdx.x == 0) { - cuopt_assert(th_best_block < gridDim.x, ""); + auto [reduced_best_score_pair, reduced_best_block] = + raft::blockRankedReduce(thrust::make_pair(th_best_score, th_best_var), + shmem, + th_best_block, + score_with_tiebreaker_comparator{}); + + if (reduced_best_score_pair.first.valid() && threadIdx.x == 0) { + cuopt_assert(reduced_best_block < gridDim.x, ""); best_var = fj.grid_var_buf[reduced_best_block]; best_delta = fj.grid_delta_buf[reduced_best_block]; best_score = fj.grid_score_buf[reduced_best_block]; @@ -1284,7 +1335,7 @@ DI thrust::tuple::move_score_t> best_sat_cstr_ typename fj_t::climber_data_t::view_t fj) { // compute all MTM moves within satisfied constraints - compute_mtm_moves(fj, true); + compute_mtm_moves(fj, true); return gridwide_reduce_best_move( fj, fj.objective_vars.begin(), fj.objective_vars.end(), [fj] __device__(i_t var_idx) { return fj.jump_move_delta[var_idx]; diff --git a/cpp/src/mip/feasibility_jump/load_balancing.cuh b/cpp/src/mip/feasibility_jump/load_balancing.cuh index 38b3d192fb..2009f47560 100644 --- a/cpp/src/mip/feasibility_jump/load_balancing.cuh +++ b/cpp/src/mip/feasibility_jump/load_balancing.cuh @@ -140,7 +140,8 @@ __global__ void load_balancing_prepare_iteration(const __grid_constant__ for (i_t i = blockIdx.x + range.first; i < range.second; i += gridDim.x) { i_t var_idx = fj.pb.related_variables[i]; - update_jump_value(fj, var_idx); + update_jump_value(fj, + var_idx); } if (FIRST_THREAD) *fj.load_balancing_skip = true; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index 403600c449..2521206d8b 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -560,7 +560,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s solution.assignment.size(), solution.handle_ptr->get_stream()); - // CUOPT_LOG_DEBUG("FP: starting FP descent, sol hash 0x%x", solution.get_hash()); + CUOPT_LOG_DEBUG("FP: starting FP descent, sol hash 0x%x", solution.get_hash()); while (true) { fp_iterations++; if (timer.check_time_limit()) { @@ -577,7 +577,9 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s f_t ratio_of_assigned_integers = f_t(solution.n_assigned_integers) / solution.problem_ptr->n_integer_vars; bool is_feasible = linear_project_onto_polytope(solution, ratio_of_assigned_integers); - i_t n_integers = solution.compute_number_of_integers(); + CUOPT_LOG_DEBUG( + "FP: after fp projection, iter %d sol hash 0x%x", fp_iterations, solution.get_hash()); + i_t n_integers = solution.compute_number_of_integers(); // CUOPT_LOG_DEBUG("after fp projection n_integers %d total n_integes %d", // n_integers, // solution.problem_ptr->n_integer_vars); @@ -628,9 +630,11 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s const f_t lp_verify_time_limit = 5.; relaxed_lp_settings_t lp_settings; lp_settings.time_limit = lp_verify_time_limit; + lp_settings.work_limit = lp_settings.time_limit; lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; lp_settings.return_first_feasible = true; lp_settings.save_state = true; + CUOPT_LOG_DEBUG("FP LP verify, sol hash 0x%x", solution.get_hash()); run_lp_with_vars_fixed(*solution.problem_ptr, solution, solution.problem_ptr->integer_indices, diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu index 475e8d9914..c28d24dce4 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu @@ -179,6 +179,7 @@ bool line_segment_search_t::search_line_segment( fj.settings.update_weights = false; fj.settings.feasibility_run = is_feasibility_run; fj.settings.time_limit = std::min(0.1, timer.remaining_time()); + fj.settings.work_limit = fj.settings.time_limit; is_feasible = fj.solve(solution); } cuopt_func_call(solution.test_number_all_integer()); @@ -221,13 +222,7 @@ bool line_segment_search_t::search_line_segment( fj.settings.update_weights = false; fj.settings.feasibility_run = is_feasibility_run; fj.settings.time_limit = std::min(1., timer.remaining_time()); - if (context.settings.deterministic) { - // fj.settings.time_limit = timer.remaining_time(); - fj.settings.time_limit = - std::numeric_limits::max(); // TODO should be global time limit - fj.settings.iteration_limit = 500; - } - is_feasible = fj.solve(solution); + is_feasible = fj.solve(solution); if (is_feasibility_run) { if (is_feasible) { CUOPT_LOG_DEBUG("Line segment found feasible"); diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 8fd8ba503b..e8f125625f 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -496,6 +496,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu if (context.settings.deterministic) { lp_run_time = std::numeric_limits::infinity(); } relaxed_lp_settings_t lp_settings; lp_settings.time_limit = std::min(lp_run_time, timer.remaining_time()); + lp_settings.work_limit = lp_settings.time_limit; lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; run_lp_with_vars_fixed( *solution.problem_ptr, solution, solution.problem_ptr->integer_indices, lp_settings); diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index b6587641bb..1354ab18ed 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -200,7 +200,15 @@ i_t bounds_repair_t::compute_best_shift(problem_t& problem, } }); handle_ptr->sync_stream(); - return candidates.n_candidates.value(handle_ptr->get_stream()); + i_t n_candidates = candidates.n_candidates.value(handle_ptr->get_stream()); + + // Sort by variable index to ensure deterministic ordering + thrust::sort_by_key(handle_ptr->get_thrust_policy(), + candidates.variable_index.begin(), + candidates.variable_index.begin() + n_candidates, + candidates.bound_shift.begin()); + + return n_candidates; } template diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 016aa03b38..266a79e371 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -432,6 +432,7 @@ void constraint_prop_t::collapse_crossing_bounds(problem_t& template void constraint_prop_t::set_bounds_on_fixed_vars(solution_t& sol) { + CUOPT_LOG_DEBUG("Bound hash before 0x%x", detail::compute_hash(sol.problem_ptr->variable_bounds)); auto assgn = make_span(sol.assignment); auto var_bounds = make_span(sol.problem_ptr->variable_bounds); thrust::for_each(sol.handle_ptr->get_thrust_policy(), @@ -443,6 +444,7 @@ void constraint_prop_t::set_bounds_on_fixed_vars(solution_t& var_bounds[idx] = typename type_2::type{var_val, var_val}; } }); + CUOPT_LOG_DEBUG("Bound hash after 0x%x", detail::compute_hash(sol.problem_ptr->variable_bounds)); } template @@ -967,8 +969,8 @@ bool constraint_prop_t::find_integer( bool timeout_happened = false; i_t n_failed_repair_iterations = 0; while (set_count < unset_integer_vars.size()) { - // CUOPT_LOG_DEBUG("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); - // CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x\n", detail::compute_hash(unset_integer_vars)); + CUOPT_LOG_DEBUG("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); + CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x", detail::compute_hash(unset_integer_vars)); update_host_assignment(sol); if (max_timer.check_time_limit()) { CUOPT_LOG_DEBUG("Second time limit is reached returning nearest rounding!"); @@ -1004,12 +1006,21 @@ bool constraint_prop_t::find_integer( n_vars_to_set, sol.handle_ptr->get_stream()); - // printf("host_vars_to_set hash 0x%x\n", detail::compute_hash(host_vars_to_set)); + CUOPT_LOG_DEBUG("host_vars_to_set hash 0x%x", detail::compute_hash(host_vars_to_set)); auto var_probe_vals = generate_bulk_rounding_vector(sol, orig_sol, host_vars_to_set, probing_config); + + CUOPT_LOG_DEBUG("var_probe_vals hash 1 0x%x, hash 2 0x%x, hash 3 0x%x", + detail::compute_hash(std::get<0>(var_probe_vals)), + detail::compute_hash(std::get<1>(var_probe_vals)), + detail::compute_hash(std::get<2>(var_probe_vals))); probe( sol, orig_sol.problem_ptr, var_probe_vals, &set_count, unset_integer_vars, probing_config); + CUOPT_LOG_DEBUG("post probe, set count %d, unset var hash 0x%x, size %lu", + (int)set_count, + detail::compute_hash(unset_integer_vars), + unset_integer_vars.size()); if (!(n_failed_repair_iterations >= max_n_failed_repair_iterations) && rounding_ii && !timeout_happened) { // timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; @@ -1085,10 +1096,7 @@ bool constraint_prop_t::find_integer( lp_settings.tolerance = orig_sol.problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; - if (this->context.settings.deterministic) { - lp_settings.iteration_limit = 13000; - lp_settings.time_limit = std::numeric_limits::infinity(); - } + CUOPT_LOG_DEBUG("bounds repair LP, sol hash 0x%x", orig_sol.get_hash()); run_lp_with_vars_fixed(*orig_sol.problem_ptr, orig_sol, orig_sol.problem_ptr->integer_indices, @@ -1270,6 +1278,15 @@ bool constraint_prop_t::handle_fixed_vars( auto set_count = *set_count_ptr; const f_t int_tol = sol.problem_ptr->tolerances.integrality_tolerance; // which other variables were affected? + CUOPT_LOG_DEBUG("handle_fixed_vars, unset vars hash 0x%x, sol.assignment hash 0x%x", + detail::compute_hash(unset_vars), + detail::compute_hash(sol.assignment)); + CUOPT_LOG_DEBUG( + "handle_fixed_vars, original_problem->variable_bounds hash 0x%x, " + "sol.problem_ptr->variable_bounds hash 0x%x", + detail::compute_hash(original_problem->variable_bounds), + detail::compute_hash(sol.problem_ptr->variable_bounds)); + auto iter = thrust::stable_partition(sol.handle_ptr->get_thrust_policy(), unset_vars.begin() + set_count, unset_vars.end(), @@ -1282,7 +1299,7 @@ bool constraint_prop_t::handle_fixed_vars( cuopt_assert(n_fixed_vars >= std::get<0>(var_probe_vals).size(), "Error in number of vars fixed!"); set_count += n_fixed_vars; - CUOPT_LOG_TRACE("Set var count increased from %d to %d", *set_count_ptr, set_count); + CUOPT_LOG_DEBUG("Set var count increased from %d to %d", *set_count_ptr, set_count); *set_count_ptr = set_count; return multi_probe.infeas_constraints_count_0 == 0 || multi_probe.infeas_constraints_count_1 == 0; } diff --git a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu index 4844bfaebb..7c0d2dcc56 100644 --- a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu @@ -410,6 +410,7 @@ bool lb_bounds_repair_t::repair_problem( timer_t timer_, const raft::handle_t* handle_ptr_) { + nvtx::range fun_scope("LB repair_problem"); CUOPT_LOG_DEBUG("LB Running bounds repair"); handle_ptr = handle_ptr_; timer = timer_; diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index d04442e7a1..775491c679 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -1714,6 +1714,8 @@ uint32_t problem_t::get_fingerprint() const detail::compute_hash(constraint_lower_bounds, handle_ptr->get_stream()), detail::compute_hash(constraint_upper_bounds, handle_ptr->get_stream()), detail::compute_hash(variable_types, handle_ptr->get_stream()), + detail::compute_hash(presolve_data.objective_offset), + detail::compute_hash(presolve_data.objective_scaling_factor), }; return detail::compute_hash(hashes); } diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index ae8742866b..451cdf0d8c 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -87,7 +87,11 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.tolerances.relative_dual_tolerance = settings.tolerance / tolerance_divisor; pdlp_settings.time_limit = settings.time_limit; pdlp_settings.iteration_limit = settings.iteration_limit; - if (settings.work_limit != std::numeric_limits::infinity()) { + + // CHANGE + i_t work_limit = pdlp_settings.time_limit; // settings.work_limit + pdlp_settings.time_limit = std::numeric_limits::infinity(); + if (work_limit != std::numeric_limits::infinity()) { // try to estimate the iteration count based on the requested work limit int estim_iters = 100; do { @@ -95,11 +99,12 @@ optimization_problem_solution_t get_relaxed_lp_solution( double estim_ms = 313 + 200 * op_problem.n_variables - 400 * op_problem.n_constraints + 600 * op_problem.coefficients.size() + 7100 * estim_iters; estim_ms = std::max(0.0, estim_ms); - if (estim_ms > settings.work_limit * 1000) { break; } + if (estim_ms > work_limit * 1000) { break; } estim_iters += 100; } while (true); CUOPT_LOG_DEBUG("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); pdlp_settings.iteration_limit = estim_iters; + pdlp_settings.time_limit = std::numeric_limits::infinity(); } pdlp_settings.concurrent_halt = settings.concurrent_halt; pdlp_settings.per_constraint_residual = settings.per_constraint_residual; @@ -138,11 +143,11 @@ optimization_problem_solution_t get_relaxed_lp_solution( // temporarily add timer auto start_time = timer_t(pdlp_settings.time_limit); lp_solver.set_inside_mip(true); - // CUOPT_LOG_DEBUG("prev primal hash 0x%x", detail::compute_hash(assignment)); - // CUOPT_LOG_DEBUG("prev dual hash 0x%x", detail::compute_hash(lp_state.prev_dual)); + CUOPT_LOG_DEBUG("prev primal hash 0x%x", detail::compute_hash(assignment)); + CUOPT_LOG_DEBUG("prev dual hash 0x%x", detail::compute_hash(lp_state.prev_dual)); auto solver_response = lp_solver.run_solver(start_time); - // CUOPT_LOG_DEBUG("post LP primal hash 0x%x", - // detail::compute_hash(solver_response.get_primal_solution())); + CUOPT_LOG_DEBUG("post LP primal hash 0x%x", + detail::compute_hash(solver_response.get_primal_solution())); if (solver_response.get_primal_solution().size() != 0 && solver_response.get_dual_solution().size() != 0 && settings.save_state) { @@ -160,9 +165,10 @@ optimization_problem_solution_t get_relaxed_lp_solution( // CUOPT_LOG_DEBUG("feasible solution found with LP objective %f", // solver_response.get_objective_value()); } else { - CUOPT_LOG_DEBUG("LP returned with reason %d, %d iterations", + CUOPT_LOG_DEBUG("LP returned with reason %d, %d iterations, sol hash 0x%x", solver_response.get_termination_status(), - solver_response.get_additional_termination_information().number_of_steps_taken); + solver_response.get_additional_termination_information().number_of_steps_taken, + compute_hash(assignment)); } auto function_end_time = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/mip/solution/solution.cu b/cpp/src/mip/solution/solution.cu index 0ce3b1e764..5b22360517 100644 --- a/cpp/src/mip/solution/solution.cu +++ b/cpp/src/mip/solution/solution.cu @@ -622,7 +622,7 @@ mip_solution_t solution_t::get_solution(bool output_feasible "Solution objective: %f , relative_mip_gap %f solution_bound %f presolve_time %f " "total_solve_time %f " "max constraint violation %f max int violation %f max var bounds violation %f " - "nodes %d simplex_iterations %d", + "nodes %d simplex_iterations %d hash %x", h_user_obj, rel_mip_gap, solution_bound, @@ -632,7 +632,8 @@ mip_solution_t solution_t::get_solution(bool output_feasible max_int_violation, max_variable_bound_violation, num_nodes, - num_simplex_iterations); + num_simplex_iterations, + get_hash()); } const bool not_optimal = rel_mip_gap > problem_ptr->tolerances.relative_mip_gap && abs_mip_gap > problem_ptr->tolerances.absolute_mip_gap; diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 71d02f531f..25d53f9c24 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -149,7 +149,7 @@ mip_solution_t run_mip(detail::problem_t& problem, auto sol = scaled_sol.get_solution( is_feasible_before_scaling || is_feasible_after_unscaling, solver.get_solver_stats(), false); - detail::print_solution(scaled_problem.handle_ptr, sol.get_solution()); + // detail::print_solution(scaled_problem.handle_ptr, sol.get_solution()); return sol; } diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index a27b370887..9911b0ee8b 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -112,8 +112,8 @@ solution_t mip_solver_t::run_solver() } diversity_manager_t dm(context); - dm.timer = work_limit_timer_t(context.gpu_heur_loop, timer_.remaining_time()); - bool presolve_success = dm.run_presolve(timer_.remaining_time()); + dm.timer = work_limit_timer_t(context.gpu_heur_loop, timer_.get_time_limit()); + bool presolve_success = dm.run_presolve(timer_.get_time_limit()); if (!presolve_success) { CUOPT_LOG_INFO("Problem proven infeasible in presolve"); solution_t sol(*context.problem_ptr); diff --git a/cpp/src/mip/utils.cuh b/cpp/src/mip/utils.cuh index a5d7c5dde6..d398b65249 100644 --- a/cpp/src/mip/utils.cuh +++ b/cpp/src/mip/utils.cuh @@ -39,6 +39,52 @@ constexpr int default_int_upper = std::numeric_limits::max(); constexpr int default_int_lower = std::numeric_limits::min(); constexpr double zero_bound = 0.; +template +inline uint32_t compute_hash(std::vector h_contents) +{ + // FNV-1a hash + + uint32_t hash = 2166136261u; // FNV-1a 32-bit offset basis + std::vector byte_contents(h_contents.size() * sizeof(i_t)); + std::memcpy(byte_contents.data(), h_contents.data(), h_contents.size() * sizeof(i_t)); + for (size_t i = 0; i < byte_contents.size(); ++i) { + hash ^= byte_contents[i]; + hash *= 16777619u; + } + return hash; +} + +template +HDI uint32_t compute_hash(const i_t val) +{ + uint32_t hash = 2166136261u; + uint8_t byte_contents[sizeof(i_t)]; + std::memcpy(byte_contents, &val, sizeof(i_t)); + for (size_t i = 0; i < sizeof(i_t); ++i) { + hash ^= byte_contents[i]; + hash *= 16777619u; + } + return hash; +} + +template +inline uint32_t compute_hash(raft::device_span values, + rmm::cuda_stream_view stream = rmm::cuda_stream_default) +{ + auto h_contents = cuopt::host_copy(values, stream); + RAFT_CHECK_CUDA(stream); + return compute_hash(h_contents); +} + +template +inline uint32_t compute_hash(const rmm::device_uvector& values, + rmm::cuda_stream_view stream = rmm::cuda_stream_default) +{ + auto h_contents = cuopt::host_copy(values, stream); + RAFT_CHECK_CUDA(stream); + return compute_hash(h_contents); +} + template HDI f_t get_cstr_tolerance(f_t combined_bound, f_t abs_tol, f_t rel_tol) { @@ -229,6 +275,9 @@ f_t compute_objective_from_vec(const rmm::device_uvector& assignment, assignment.end(), objective_coefficients.begin(), 0.); + // CUOPT_LOG_DEBUG("compute_objective_from_vec: input %x coefs %x obj %x", + // detail::compute_hash(assignment), detail::compute_hash(objective_coefficients), + // detail::compute_hash(computed_obj)); return computed_obj; } @@ -369,37 +418,4 @@ bool has_variable_bounds_violation(const raft::handle_t* handle_ptr, }); } -template -inline uint32_t compute_hash(std::vector h_contents) -{ - // FNV-1a hash - - uint32_t hash = 2166136261u; // FNV-1a 32-bit offset basis - std::vector byte_contents(h_contents.size() * sizeof(i_t)); - std::memcpy(byte_contents.data(), h_contents.data(), h_contents.size() * sizeof(i_t)); - for (size_t i = 0; i < byte_contents.size(); ++i) { - hash ^= byte_contents[i]; - hash *= 16777619u; - } - return hash; -} - -template -inline uint32_t compute_hash(raft::device_span values, - rmm::cuda_stream_view stream = rmm::cuda_stream_default) -{ - auto h_contents = cuopt::host_copy(values, stream); - RAFT_CHECK_CUDA(stream); - return compute_hash(h_contents); -} - -template -inline uint32_t compute_hash(const rmm::device_uvector& values, - rmm::cuda_stream_view stream = rmm::cuda_stream_default) -{ - auto h_contents = cuopt::host_copy(values, stream); - RAFT_CHECK_CUDA(stream); - return compute_hash(h_contents); -} - } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/utilities/logger.cpp b/cpp/src/utilities/logger.cpp index c4752d9ff4..20f146d46d 100644 --- a/cpp/src/utilities/logger.cpp +++ b/cpp/src/utilities/logger.cpp @@ -90,8 +90,8 @@ rapids_logger::sink_ptr default_sink() * * @return std::string The default log pattern. */ -inline std::string default_pattern() { return "[%Y-%m-%d %H:%M:%S:%f] [%n] [%-6l] %v"; } - +// inline std::string default_pattern() { return "[%Y-%m-%d %H:%M:%S:%f] [%n] [%-6l] %v"; } +inline std::string default_pattern() { return "[%n] [%-6l] %v"; } /** * @brief Returns the default log level for the global logger. * From bcadb92adeb232a8295ef94b96c77e3031774dfc Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 6 Nov 2025 18:57:05 +0000 Subject: [PATCH 029/225] bump 1 --- cpp/src/mip/feasibility_jump/feasibility_jump.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 2877c827a9..47ea0edfb5 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -1407,7 +1407,7 @@ i_t fj_t::solve(solution_t& solution) if (iterations < settings.iteration_limit) { CUOPT_LOG_DEBUG( "FJ early exit at %d iterations (limit: %d)", iterations, settings.iteration_limit); - // Compute the work unit corresponding to the number of iterations elapsed + // 1Compute the work unit corresponding to the number of iterations elapsed // by incrementally guessing work units until the model predicts >= actual iterations if (context.settings.deterministic && iterations > 0) { double guessed_work = 0.0; From 66288f8d57d91e90fe0f9f0627dcae8cf63b1b99 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 6 Nov 2025 18:57:22 +0000 Subject: [PATCH 030/225] bump2 --- cpp/src/mip/feasibility_jump/feasibility_jump.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 47ea0edfb5..2877c827a9 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -1407,7 +1407,7 @@ i_t fj_t::solve(solution_t& solution) if (iterations < settings.iteration_limit) { CUOPT_LOG_DEBUG( "FJ early exit at %d iterations (limit: %d)", iterations, settings.iteration_limit); - // 1Compute the work unit corresponding to the number of iterations elapsed + // Compute the work unit corresponding to the number of iterations elapsed // by incrementally guessing work units until the model predicts >= actual iterations if (context.settings.deterministic && iterations > 0) { double guessed_work = 0.0; From 8e19de8febea2b7e897905e135bd1953cd03d6ff Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 7 Nov 2025 15:10:12 +0000 Subject: [PATCH 031/225] fix FJ indeterminism --- .../mip/feasibility_jump/feasibility_jump.cu | 51 +++++++++++++++---- .../feasibility_jump_kernels.cu | 23 ++++----- cpp/src/mip/feasibility_jump/utils.cuh | 4 ++ cpp/src/mip/problem/problem.cu | 3 ++ cpp/src/mip/solve.cu | 11 ++-- 5 files changed, 66 insertions(+), 26 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 2877c827a9..cf55dc9834 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -441,10 +441,7 @@ void fj_t::climber_init(i_t climber_idx, const rmm::cuda_stream_view& f_t inf = std::numeric_limits::infinity(); climber->best_objective.set_value_async(inf, climber_stream); climber->saved_solution_objective.set_value_async(inf, climber_stream); - climber->violation_score.set_value_to_zero_async(climber_stream); - climber->weighted_violation_score.set_value_to_zero_async(climber_stream); - init_lhs_and_violation<<<256, 256, 0, climber_stream.value()>>>(view); - climber->violated_constraints.sort(climber_stream); + refresh_lhs_and_violation(climber_stream); // printf("init: Violated constraints hash: %x\n", compute_hash( // make_span(climber->violated_constraints.contents, 0, @@ -894,10 +891,38 @@ void fj_t::refresh_lhs_and_violation(const rmm::cuda_stream_view& stre auto v = data.view(); data.violated_constraints.clear(stream); - data.violation_score.set_value_to_zero_async(stream); - data.weighted_violation_score.set_value_to_zero_async(stream); init_lhs_and_violation<<<4096, 256, 0, stream>>>(v); + // both transformreduce could be fused; but oh well hardly a bottleneck + auto violation = + thrust::transform_reduce(rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(pb_ptr->n_constraints), + cuda::proclaim_return_type([v] __device__(i_t cstr_idx) { + return v.excess_score(cstr_idx, v.incumbent_lhs[cstr_idx]); + }), + (f_t)0, + thrust::plus()); + auto weighted_violation = thrust::transform_reduce( + rmm::exec_policy(stream), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(pb_ptr->n_constraints), + cuda::proclaim_return_type([v] __device__(i_t cstr_idx) { + return v.excess_score(cstr_idx, v.incumbent_lhs[cstr_idx]) * v.cstr_weights[cstr_idx]; + }), + (f_t)0, + thrust::plus()); + data.violation_score.set_value_async(violation, stream); + data.weighted_violation_score.set_value_async(weighted_violation, stream); data.violated_constraints.sort(stream); +#if FJ_SINGLE_STEP + CUOPT_LOG_DEBUG("hash assignment %x, hash lhs %x, hash lhscomp %x", + detail::compute_hash(data.incumbent_assignment, stream), + detail::compute_hash(data.incumbent_lhs, stream), + detail::compute_hash(data.incumbent_lhs_sumcomp, stream)); + CUOPT_LOG_DEBUG("Violated constraints hash post sort: %x, index map %x", + detail::compute_hash(data.violated_constraints.contents, stream), + detail::compute_hash(data.violated_constraints.index_map, stream)); +#endif } template @@ -1103,13 +1128,14 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) if (timer.check_time_limit() || steps >= settings.iteration_limit) { limit_reached = true; } #if !FJ_SINGLE_STEP - if (steps % 500 == 0) + // if (steps % 500 == 0) + if (false) #endif { - CUOPT_LOG_TRACE( + CUOPT_LOG_DEBUG( "FJ " "step %d viol %.2g [%d], obj %.8g, best %.8g, mins %d, maxw %g, " - "objw %g, sol hash %x, delta hash %x", + "objw %g, sol %x, delta %x, inc %x, lhs %x, lhscomp %x, viol %x, weights %x", steps, data.violation_score.value(climber_stream), data.violated_constraints.set_size.value(climber_stream), @@ -1119,7 +1145,12 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) max_cstr_weight.value(climber_stream), objective_weight.value(climber_stream), solution.get_hash(), - detail::compute_hash(data.jump_move_delta, climber_stream)); + detail::compute_hash(data.jump_move_delta, climber_stream), + detail::compute_hash(data.incumbent_assignment, climber_stream), + detail::compute_hash(data.incumbent_lhs, climber_stream), + detail::compute_hash(data.incumbent_lhs_sumcomp, climber_stream), + detail::compute_hash(data.violated_constraints.contents, climber_stream), + detail::compute_hash(cstr_left_weights, climber_stream)); } if (!limit_reached) { run_step_device(climber_stream, climber_idx); } diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index cd83803eac..21a5bf5998 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -180,10 +180,7 @@ __global__ void init_lhs_and_violation(typename fj_t::climber_data_t:: fj_kahan_babushka_neumaier_sum(delta_it + offset_begin, delta_it + offset_end); fj.incumbent_lhs_sumcomp[cstr_idx] = 0; - f_t th_violation = fj.excess_score(cstr_idx, fj.incumbent_lhs[cstr_idx]); - f_t weighted_violation = th_violation * fj.cstr_weights[cstr_idx]; - atomicAdd(fj.violation_score, th_violation); - atomicAdd(fj.weighted_violation_score, weighted_violation); + f_t th_violation = fj.excess_score(cstr_idx, fj.incumbent_lhs[cstr_idx]); f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx); if (th_violation < -cstr_tolerance) { fj.violated_constraints.insert(cstr_idx); } } @@ -604,14 +601,16 @@ __global__ void update_assignment_kernel(typename fj_t::climber_data_t __syncthreads(); - cuopt_assert(isfinite(fj.jump_move_delta[var_idx]), "delta should be finite"); - // Kahan compensated summation - // fj.incumbent_lhs[cstr_idx] = old_lhs + cstr_coeff * fj.jump_move_delta[var_idx]; - f_t y = cstr_coeff * fj.jump_move_delta[var_idx] - fj.incumbent_lhs_sumcomp[cstr_idx]; - f_t t = old_lhs + y; - fj.incumbent_lhs_sumcomp[cstr_idx] = (t - old_lhs) - y; - fj.incumbent_lhs[cstr_idx] = t; - cuopt_assert(isfinite(fj.incumbent_lhs[cstr_idx]), "assignment should be finite"); + if (threadIdx.x == 0) { + cuopt_assert(isfinite(fj.jump_move_delta[var_idx]), "delta should be finite"); + // Kahan compensated summation + // fj.incumbent_lhs[cstr_idx] = old_lhs + cstr_coeff * fj.jump_move_delta[var_idx]; + f_t y = cstr_coeff * fj.jump_move_delta[var_idx] - fj.incumbent_lhs_sumcomp[cstr_idx]; + f_t t = old_lhs + y; + fj.incumbent_lhs_sumcomp[cstr_idx] = (t - old_lhs) - y; + fj.incumbent_lhs[cstr_idx] = t; + cuopt_assert(isfinite(fj.incumbent_lhs[cstr_idx]), "assignment should be finite"); + } } // update the assignment and objective proper diff --git a/cpp/src/mip/feasibility_jump/utils.cuh b/cpp/src/mip/feasibility_jump/utils.cuh index bd1dd4b495..1b82ae8b60 100644 --- a/cpp/src/mip/feasibility_jump/utils.cuh +++ b/cpp/src/mip/feasibility_jump/utils.cuh @@ -152,6 +152,10 @@ struct contiguous_set_t { thrust::make_counting_iterator(0), thrust::make_counting_iterator(set_size.value(stream)), [v = view()] __device__(i_t idx) { v.index_map[v.contents[idx]] = idx; }); + + // TODO: remove, only useful for debugging and ensuring the same hashes + thrust::fill( + rmm::exec_policy(stream), contents.begin() + set_size.value(stream), contents.end(), 0); } struct view_t { diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 775491c679..a6d942e35b 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -1709,6 +1709,9 @@ uint32_t problem_t::get_fingerprint() const detail::compute_hash(coefficients, handle_ptr->get_stream()), detail::compute_hash(variables, handle_ptr->get_stream()), detail::compute_hash(offsets, handle_ptr->get_stream()), + detail::compute_hash(reverse_coefficients, handle_ptr->get_stream()), + detail::compute_hash(reverse_offsets, handle_ptr->get_stream()), + detail::compute_hash(reverse_constraints, handle_ptr->get_stream()), detail::compute_hash(objective_coefficients, handle_ptr->get_stream()), detail::compute_hash(variable_bounds, handle_ptr->get_stream()), detail::compute_hash(constraint_lower_bounds, handle_ptr->get_stream()), diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 25d53f9c24..5e330eac1f 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -210,10 +210,13 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, detail::sort_csr(op_problem); // allocate not more than 10% of the time limit to presolve. // Note that this is not the presolve time, but the time limit for presolve. - const double presolve_time_limit = std::min(0.1 * time_limit, 60.0); - const bool dual_postsolve = false; - presolver = std::make_unique>(); - auto result = presolver->apply(op_problem, + double presolve_time_limit = std::min(0.1 * time_limit, 60.0); + + // TODO FIX + presolve_time_limit = std::numeric_limits::infinity(); + const bool dual_postsolve = false; + presolver = std::make_unique>(); + auto result = presolver->apply(op_problem, cuopt::linear_programming::problem_category_t::MIP, dual_postsolve, settings.tolerances.absolute_tolerance, From 5eacbb89d03a49fb3d6a2179635c72946c3a53b0 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 7 Nov 2025 15:10:42 +0000 Subject: [PATCH 032/225] bump 1 --- cpp/src/mip/feasibility_jump/utils.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/utils.cuh b/cpp/src/mip/feasibility_jump/utils.cuh index 1b82ae8b60..4afd30b15e 100644 --- a/cpp/src/mip/feasibility_jump/utils.cuh +++ b/cpp/src/mip/feasibility_jump/utils.cuh @@ -153,7 +153,7 @@ struct contiguous_set_t { thrust::make_counting_iterator(set_size.value(stream)), [v = view()] __device__(i_t idx) { v.index_map[v.contents[idx]] = idx; }); - // TODO: remove, only useful for debugging and ensuring the same hashes + // TODO: r1emove, only useful for debugging and ensuring the same hashes thrust::fill( rmm::exec_policy(stream), contents.begin() + set_size.value(stream), contents.end(), 0); } From b96a7844ff64a462f875379256190877d595b173 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 7 Nov 2025 15:10:59 +0000 Subject: [PATCH 033/225] bump 2 --- cpp/src/mip/feasibility_jump/utils.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/utils.cuh b/cpp/src/mip/feasibility_jump/utils.cuh index 4afd30b15e..1b82ae8b60 100644 --- a/cpp/src/mip/feasibility_jump/utils.cuh +++ b/cpp/src/mip/feasibility_jump/utils.cuh @@ -153,7 +153,7 @@ struct contiguous_set_t { thrust::make_counting_iterator(set_size.value(stream)), [v = view()] __device__(i_t idx) { v.index_map[v.contents[idx]] = idx; }); - // TODO: r1emove, only useful for debugging and ensuring the same hashes + // TODO: remove, only useful for debugging and ensuring the same hashes thrust::fill( rmm::exec_policy(stream), contents.begin() + set_size.value(stream), contents.end(), 0); } From ccc64a22acd17dce0d0c5ef5d1a579dc5775ec96 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 7 Nov 2025 18:47:21 +0000 Subject: [PATCH 034/225] fix bounds repair indeterminism --- .../mip/feasibility_jump/feasibility_jump.cu | 26 +++---- .../feasibility_jump_kernels.cu | 69 ++++++++++--------- .../local_search/rounding/bounds_repair.cu | 20 ++++-- .../local_search/rounding/lb_bounds_repair.cu | 16 +++-- cpp/src/utilities/timer.hpp | 1 + 5 files changed, 76 insertions(+), 56 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index cf55dc9834..7b75c9823c 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -664,7 +664,7 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; - auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; + // auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; // use_graph = false; @@ -774,18 +774,18 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream } #endif - cudaLaunchKernel((void*)update_lift_moves_kernel, - grid_lift_move, - blocks_lift_move, - kernel_args, - 0, - climber_stream); - cudaLaunchKernel((void*)update_breakthrough_moves_kernel, - grid_lift_move, - blocks_lift_move, - kernel_args, - 0, - climber_stream); + // cudaLaunchKernel((void*)update_lift_moves_kernel, + // grid_lift_move, + // blocks_lift_move, + // kernel_args, + // 0, + // climber_stream); + // cudaLaunchKernel((void*)update_breakthrough_moves_kernel, + // grid_lift_move, + // blocks_lift_move, + // kernel_args, + // 0, + // climber_stream); } // compaction kernel diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 21a5bf5998..f62d15abf4 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1454,44 +1454,45 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat } // also consider breakthrough moves - if (*fj.best_objective < std::numeric_limits::infinity() && - *fj.incumbent_objective > *fj.best_objective) { - cg::this_grid().sync(); - auto [bm_best_var, bm_best_delta, bm_best_score] = - best_breakthrough_move_at_local_min(fj); - if (bm_best_score > best_score) { - best_score = bm_best_score; - best_var = bm_best_var; - best_delta = bm_best_delta; - best_movetype = 'B'; - cuopt_assert(fj.pb.check_variable_within_bounds( - best_var, fj.incumbent_assignment[best_var] + best_delta), - "assignment not within bounds"); - } - } + // if (*fj.best_objective < std::numeric_limits::infinity() && + // *fj.incumbent_objective > *fj.best_objective) { + // cg::this_grid().sync(); + // auto [bm_best_var, bm_best_delta, bm_best_score] = + // best_breakthrough_move_at_local_min(fj); + // if (bm_best_score > best_score) { + // best_score = bm_best_score; + // best_var = bm_best_var; + // best_delta = bm_best_delta; + // best_movetype = 'B'; + // cuopt_assert(fj.pb.check_variable_within_bounds( + // best_var, fj.incumbent_assignment[best_var] + best_delta), + // "assignment not within bounds"); + // } + // } if (FIRST_THREAD) *fj.selected_var = best_var; cg::this_grid().sync(); // still nothing? try sat MTM moves if we are in the feasible region // Attempt to find a valid move by going over MTM moves in valid constraints - if (*fj.selected_var == std::numeric_limits::max() && - *fj.incumbent_objective < std::numeric_limits::infinity()) { - auto [sat_best_var, sat_best_delta, sat_best_score] = - best_sat_cstr_mtm_move(fj); - - if (FIRST_THREAD && sat_best_score.valid()) - cuopt_assert(fj.pb.check_variable_within_bounds( - sat_best_var, fj.incumbent_assignment[sat_best_var] + sat_best_delta), - "assignment not within bounds"); - - if (sat_best_score.base > 0 && sat_best_score > best_score) { - if (FIRST_THREAD) { - best_score = sat_best_score; - best_var = sat_best_var; - best_delta = sat_best_delta; - } - } - } + // if (*fj.selected_var == std::numeric_limits::max() && + // *fj.incumbent_objective < std::numeric_limits::infinity()) { + // auto [sat_best_var, sat_best_delta, sat_best_score] = + // best_sat_cstr_mtm_move(fj); + + // if (FIRST_THREAD && sat_best_score.valid()) + // cuopt_assert(fj.pb.check_variable_within_bounds( + // sat_best_var, fj.incumbent_assignment[sat_best_var] + sat_best_delta), + // "assignment not within bounds"); + + // if (sat_best_score.base > 0 && sat_best_score > best_score) { + // if (FIRST_THREAD) { + // best_score = sat_best_score; + // best_var = sat_best_var; + // best_delta = sat_best_delta; + // best_movetype = 'S'; + // } + // } + // } if (FIRST_THREAD) { *fj.selected_var = best_var; @@ -1500,6 +1501,8 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat best_var, fj.incumbent_assignment[best_var] + best_delta), "assignment not within bounds"); fj.jump_move_delta[best_var] = best_delta; + // DEVICE_LOG_DEBUG("FJ[%d] selected_var: %d, delta %g, score {%d %d}, type %c\n", + // *fj.iterations, best_var, best_delta, best_score.base, best_score.bonus, best_movetype); } } } diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index 1354ab18ed..0013fc2e07 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -78,8 +78,7 @@ f_t bounds_repair_t::get_ii_violation(problem_t& problem) min_act = bound_presolve.upd.min_activity.data(), max_act = bound_presolve.upd.max_activity.data(), cstr_violations_up = cstr_violations_up.data(), - cstr_violations_down = cstr_violations_down.data(), - total_vio = total_vio.data()] __device__(i_t cstr_idx) { + cstr_violations_down = cstr_violations_down.data()] __device__(i_t cstr_idx) -> f_t { f_t cnst_lb = pb_v.constraint_lower_bounds[cstr_idx]; f_t cnst_ub = pb_v.constraint_upper_bounds[cstr_idx]; f_t eps = get_cstr_tolerance( @@ -89,21 +88,30 @@ f_t bounds_repair_t::get_ii_violation(problem_t& problem) f_t violation = max(curr_cstr_violation_up, curr_cstr_violation_down); if (violation >= ROUNDOFF_TOLERANCE) { violated_cstr_map[cstr_idx] = 1; - atomicAdd(total_vio, violation); } else { violated_cstr_map[cstr_idx] = 0; } cstr_violations_up[cstr_idx] = curr_cstr_violation_up; cstr_violations_down[cstr_idx] = curr_cstr_violation_down; }); - auto iter = thrust::copy_if(handle_ptr->get_thrust_policy(), + auto iter = thrust::copy_if(handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), thrust::make_counting_iterator(0) + problem.n_constraints, violated_cstr_map.data(), violated_constraints.data(), cuda::std::identity{}); - h_n_violated_cstr = iter - violated_constraints.data(); - f_t total_violation = total_vio.value(handle_ptr->get_stream()); + h_n_violated_cstr = iter - violated_constraints.data(); + // Use deterministic reduction instead of non-deterministic atomicAdd + f_t total_violation = thrust::transform_reduce( + handle_ptr->get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(0) + problem.n_constraints, + [cstr_violations_up = cstr_violations_up.data(), + cstr_violations_down = cstr_violations_down.data()] __device__(i_t cstr_idx) -> f_t { + return max(cstr_violations_up[cstr_idx], cstr_violations_down[cstr_idx]); + }, + (f_t)0, + thrust::plus()); CUOPT_LOG_TRACE( "Repair: n_violated_cstr %d total_violation %f", h_n_violated_cstr, total_violation); return total_violation; diff --git a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu index 7c0d2dcc56..6fa9510334 100644 --- a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu @@ -78,8 +78,7 @@ std::tuple lb_bounds_repair_t::get_ii_violation( constraint_upper_bounds = problem.constraint_upper_bounds, cnst_slack = make_span_2(lb_bound_presolve.cnst_slack), cstr_violations_up = cstr_violations_up.data(), - cstr_violations_down = cstr_violations_down.data(), - total_vio = total_vio.data()] __device__(i_t cstr_idx) { + cstr_violations_down = cstr_violations_down.data()] __device__(i_t cstr_idx) { f_t cnst_lb = constraint_lower_bounds[cstr_idx]; f_t cnst_ub = constraint_upper_bounds[cstr_idx]; f_t2 slack = cnst_slack[cstr_idx]; @@ -90,7 +89,6 @@ std::tuple lb_bounds_repair_t::get_ii_violation( f_t violation = max(curr_cstr_violation_up, curr_cstr_violation_down); if (violation >= ROUNDOFF_TOLERANCE) { violated_cstr_map[cstr_idx] = 1; - atomicAdd(total_vio, violation); } else { violated_cstr_map[cstr_idx] = 0; } @@ -104,7 +102,17 @@ std::tuple lb_bounds_repair_t::get_ii_violation( violated_constraints.data(), cuda::std::identity{}); i_t n_violated_cstr = iter - violated_constraints.data(); - f_t total_violation = total_vio.value(handle_ptr->get_stream()); + // Use deterministic reduction instead of non-deterministic atomicAdd + f_t total_violation = thrust::transform_reduce( + handle_ptr->get_thrust_policy(), + thrust::make_counting_iterator(0), + thrust::make_counting_iterator(0) + problem.n_constraints, + [cstr_violations_up = cstr_violations_up.data(), + cstr_violations_down = cstr_violations_down.data()] __device__(i_t cstr_idx) -> f_t { + return max(cstr_violations_up[cstr_idx], cstr_violations_down[cstr_idx]); + }, + (f_t)0, + thrust::plus()); CUOPT_LOG_TRACE( "Repair: n_violated_cstr %d total_violation %f", n_violated_cstr, total_violation); return std::make_tuple(total_violation, n_violated_cstr); diff --git a/cpp/src/utilities/timer.hpp b/cpp/src/utilities/timer.hpp index d250e15cd4..b3ea94f55f 100644 --- a/cpp/src/utilities/timer.hpp +++ b/cpp/src/utilities/timer.hpp @@ -56,6 +56,7 @@ class timer_t { line, caller); assert(false && "unexpected timer"); + __builtin_trap(); } return elapsed; } From 01afc9d107d52cbbea0ebf162ce703ec8a2976b2 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 7 Nov 2025 18:47:40 +0000 Subject: [PATCH 035/225] bump1 --- cpp/src/mip/local_search/rounding/bounds_repair.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index 0013fc2e07..d7ab819d1b 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -101,7 +101,7 @@ f_t bounds_repair_t::get_ii_violation(problem_t& problem) violated_constraints.data(), cuda::std::identity{}); h_n_violated_cstr = iter - violated_constraints.data(); - // Use deterministic reduction instead of non-deterministic atomicAdd + // Use deterministic reduction instead of non-deterministic atomicAdd1 f_t total_violation = thrust::transform_reduce( handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), From 6ba4905b521a79821805a6d170463573e576af57 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 7 Nov 2025 18:47:52 +0000 Subject: [PATCH 036/225] bump2 --- cpp/src/mip/local_search/rounding/bounds_repair.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index d7ab819d1b..0013fc2e07 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -101,7 +101,7 @@ f_t bounds_repair_t::get_ii_violation(problem_t& problem) violated_constraints.data(), cuda::std::identity{}); h_n_violated_cstr = iter - violated_constraints.data(); - // Use deterministic reduction instead of non-deterministic atomicAdd1 + // Use deterministic reduction instead of non-deterministic atomicAdd f_t total_violation = thrust::transform_reduce( handle_ptr->get_thrust_policy(), thrust::make_counting_iterator(0), From 3e6d081be749c09181febd5f3087bf156ebc589e Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 7 Nov 2025 18:50:08 +0000 Subject: [PATCH 037/225] restore FJ moves --- .../mip/feasibility_jump/feasibility_jump.cu | 26 +++---- .../feasibility_jump_kernels.cu | 68 +++++++++---------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 7b75c9823c..cf55dc9834 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -664,7 +664,7 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; - // auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; + auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; // use_graph = false; @@ -774,18 +774,18 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream } #endif - // cudaLaunchKernel((void*)update_lift_moves_kernel, - // grid_lift_move, - // blocks_lift_move, - // kernel_args, - // 0, - // climber_stream); - // cudaLaunchKernel((void*)update_breakthrough_moves_kernel, - // grid_lift_move, - // blocks_lift_move, - // kernel_args, - // 0, - // climber_stream); + cudaLaunchKernel((void*)update_lift_moves_kernel, + grid_lift_move, + blocks_lift_move, + kernel_args, + 0, + climber_stream); + cudaLaunchKernel((void*)update_breakthrough_moves_kernel, + grid_lift_move, + blocks_lift_move, + kernel_args, + 0, + climber_stream); } // compaction kernel diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index f62d15abf4..8d92d898ff 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1454,45 +1454,45 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat } // also consider breakthrough moves - // if (*fj.best_objective < std::numeric_limits::infinity() && - // *fj.incumbent_objective > *fj.best_objective) { - // cg::this_grid().sync(); - // auto [bm_best_var, bm_best_delta, bm_best_score] = - // best_breakthrough_move_at_local_min(fj); - // if (bm_best_score > best_score) { - // best_score = bm_best_score; - // best_var = bm_best_var; - // best_delta = bm_best_delta; - // best_movetype = 'B'; - // cuopt_assert(fj.pb.check_variable_within_bounds( - // best_var, fj.incumbent_assignment[best_var] + best_delta), - // "assignment not within bounds"); - // } - // } + if (*fj.best_objective < std::numeric_limits::infinity() && + *fj.incumbent_objective > *fj.best_objective) { + cg::this_grid().sync(); + auto [bm_best_var, bm_best_delta, bm_best_score] = + best_breakthrough_move_at_local_min(fj); + if (bm_best_score > best_score) { + best_score = bm_best_score; + best_var = bm_best_var; + best_delta = bm_best_delta; + best_movetype = 'B'; + cuopt_assert(fj.pb.check_variable_within_bounds( + best_var, fj.incumbent_assignment[best_var] + best_delta), + "assignment not within bounds"); + } + } if (FIRST_THREAD) *fj.selected_var = best_var; cg::this_grid().sync(); // still nothing? try sat MTM moves if we are in the feasible region // Attempt to find a valid move by going over MTM moves in valid constraints - // if (*fj.selected_var == std::numeric_limits::max() && - // *fj.incumbent_objective < std::numeric_limits::infinity()) { - // auto [sat_best_var, sat_best_delta, sat_best_score] = - // best_sat_cstr_mtm_move(fj); - - // if (FIRST_THREAD && sat_best_score.valid()) - // cuopt_assert(fj.pb.check_variable_within_bounds( - // sat_best_var, fj.incumbent_assignment[sat_best_var] + sat_best_delta), - // "assignment not within bounds"); - - // if (sat_best_score.base > 0 && sat_best_score > best_score) { - // if (FIRST_THREAD) { - // best_score = sat_best_score; - // best_var = sat_best_var; - // best_delta = sat_best_delta; - // best_movetype = 'S'; - // } - // } - // } + if (*fj.selected_var == std::numeric_limits::max() && + *fj.incumbent_objective < std::numeric_limits::infinity()) { + auto [sat_best_var, sat_best_delta, sat_best_score] = + best_sat_cstr_mtm_move(fj); + + if (FIRST_THREAD && sat_best_score.valid()) + cuopt_assert(fj.pb.check_variable_within_bounds( + sat_best_var, fj.incumbent_assignment[sat_best_var] + sat_best_delta), + "assignment not within bounds"); + + if (sat_best_score.base > 0 && sat_best_score > best_score) { + if (FIRST_THREAD) { + best_score = sat_best_score; + best_var = sat_best_var; + best_delta = sat_best_delta; + best_movetype = 'S'; + } + } + } if (FIRST_THREAD) { *fj.selected_var = best_var; From 38df1ece95f19964de2fe72af5f9ce2af9e4c232 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 7 Nov 2025 18:50:21 +0000 Subject: [PATCH 038/225] bump1 --- cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 8d92d898ff..5357e7261d 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1501,7 +1501,7 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat best_var, fj.incumbent_assignment[best_var] + best_delta), "assignment not within bounds"); fj.jump_move_delta[best_var] = best_delta; - // DEVICE_LOG_DEBUG("FJ[%d] selected_var: %d, delta %g, score {%d %d}, type %c\n", + // 1DEVICE_LOG_DEBUG("FJ[%d] selected_var: %d, delta %g, score {%d %d}, type %c\n", // *fj.iterations, best_var, best_delta, best_score.base, best_score.bonus, best_movetype); } } From c713dbca3df79dc89057d9f28467e8530bb00231 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 7 Nov 2025 18:51:54 +0000 Subject: [PATCH 039/225] bump2 --- cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 5357e7261d..8d92d898ff 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1501,7 +1501,7 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat best_var, fj.incumbent_assignment[best_var] + best_delta), "assignment not within bounds"); fj.jump_move_delta[best_var] = best_delta; - // 1DEVICE_LOG_DEBUG("FJ[%d] selected_var: %d, delta %g, score {%d %d}, type %c\n", + // DEVICE_LOG_DEBUG("FJ[%d] selected_var: %d, delta %g, score {%d %d}, type %c\n", // *fj.iterations, best_var, best_delta, best_score.base, best_score.bonus, best_movetype); } } From 3b0d116710b73e991d936008a0adaa5390622536 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 08:21:13 +0000 Subject: [PATCH 040/225] recombiners record work --- cpp/src/mip/diversity/diversity_manager.cu | 19 +++++++++---------- cpp/src/mip/diversity/multi_armed_bandit.cuh | 11 ++++------- .../recombiners/bound_prop_recombiner.cuh | 13 +++++++------ .../diversity/recombiners/fp_recombiner.cuh | 14 ++++++++------ .../recombiners/line_segment_recombiner.cuh | 12 +++++++----- .../recombiners/recombiner_stats.hpp | 16 +++------------- cpp/src/mip/diversity/recombiners/sub_mip.cuh | 14 ++++++++------ 7 files changed, 46 insertions(+), 53 deletions(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 075a483ec4..e7304ece95 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -706,7 +706,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& best_quality_of_parents, population.best().get_quality(population.weights), offspring_qual, - recombiner_work_normalized_reward_t(recombine_stats.get_last_recombiner_time())); + recombiner_work_normalized_reward_t(recombine_stats.get_last_recombiner_work())); mab_ls.add_mab_reward(mab_ls_config_t::last_ls_mab_option, best_quality_of_parents, population.best_feasible().get_quality(population.weights), @@ -753,30 +753,29 @@ std::pair, bool> diversity_manager_t::recombine( } mab_recombiner.set_last_chosen_option(selected_index); recombine_stats.add_attempt((recombiner_enum_t)recombiner); - recombine_stats.start_recombiner_time(); // Refactored code using a switch statement switch (recombiner) { case recombiner_enum_t::BOUND_PROP: { - auto [sol, success] = bound_prop_recombiner.recombine(a, b, population.weights); - recombine_stats.stop_recombiner_time(); + auto [sol, success, work] = bound_prop_recombiner.recombine(a, b, population.weights); + recombine_stats.set_recombiner_work(work); if (success) { recombine_stats.add_success(); } return std::make_pair(sol, success); } case recombiner_enum_t::FP: { - auto [sol, success] = fp_recombiner.recombine(a, b, population.weights); - recombine_stats.stop_recombiner_time(); + auto [sol, success, work] = fp_recombiner.recombine(a, b, population.weights); + recombine_stats.set_recombiner_work(work); if (success) { recombine_stats.add_success(); } return std::make_pair(sol, success); } case recombiner_enum_t::LINE_SEGMENT: { - auto [sol, success] = line_segment_recombiner.recombine(a, b, population.weights); - recombine_stats.stop_recombiner_time(); + auto [sol, success, work] = line_segment_recombiner.recombine(a, b, population.weights); + recombine_stats.set_recombiner_work(work); if (success) { recombine_stats.add_success(); } return std::make_pair(sol, success); } case recombiner_enum_t::SUB_MIP: { - auto [sol, success] = sub_mip_recombiner.recombine(a, b, population.weights); - recombine_stats.stop_recombiner_time(); + auto [sol, success, work] = sub_mip_recombiner.recombine(a, b, population.weights); + recombine_stats.set_recombiner_work(work); if (success) { recombine_stats.add_success(); } return std::make_pair(sol, success); } diff --git a/cpp/src/mip/diversity/multi_armed_bandit.cuh b/cpp/src/mip/diversity/multi_armed_bandit.cuh index fe969ad8d7..61dbf05474 100644 --- a/cpp/src/mip/diversity/multi_armed_bandit.cuh +++ b/cpp/src/mip/diversity/multi_armed_bandit.cuh @@ -55,16 +55,13 @@ struct ls_work_normalized_reward_t { }; struct recombiner_work_normalized_reward_t { - double time_in_miliseconds; - recombiner_work_normalized_reward_t(double time_in_miliseconds) - : time_in_miliseconds(time_in_miliseconds) - { - } + double work; + recombiner_work_normalized_reward_t(double work) : work(work) {} double operator()(double factor) const { - // normal recombiners take 2000 ms - return factor * (std::max(0.1, 4.0 - (time_in_miliseconds / 2000))); + // normal recombiners process 200 variables + return factor * (std::max(0.1, 4.0 - (work / 200))); } }; diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index 4ca19436c1..daf981ea71 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -141,9 +141,9 @@ class bound_prop_recombiner_t : public recombiner_t { }); } - std::pair, bool> recombine(solution_t& a, - solution_t& b, - const weight_t& weights) + std::tuple, bool, double> recombine(solution_t& a, + solution_t& b, + const weight_t& weights) { raft::common::nvtx::range fun_scope("bound_prop_recombiner"); auto& guiding_solution = a.get_feasible() ? a : b; @@ -174,8 +174,9 @@ class bound_prop_recombiner_t : public recombiner_t { // all are different), return if (n_vars_from_guiding == 0 || n_vars_from_other == 0) { CUOPT_LOG_DEBUG("Returning false because all vars are common or different"); - return std::make_pair(offspring, false); + return std::make_tuple(offspring, false, 0.0); } + double work = static_cast(n_vars_from_other); cuopt_assert(a.problem_ptr == b.problem_ptr, "The two solutions should not refer to different problems"); @@ -266,9 +267,9 @@ class bound_prop_recombiner_t : public recombiner_t { } if (better_cost_than_parents || better_feasibility_than_parents) { CUOPT_LOG_DEBUG("Offspring is feasible or better than both parents"); - return std::make_pair(offspring, true); + return std::make_tuple(offspring, true, work); } - return std::make_pair(offspring, !same_as_parents); + return std::make_tuple(offspring, !same_as_parents, work); } rmm::device_uvector vars_to_fix; diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index ade72a23b6..e67ff7c1b4 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -45,9 +45,9 @@ class fp_recombiner_t : public recombiner_t { { } - std::pair, bool> recombine(solution_t& a, - solution_t& b, - const weight_t& weights) + std::tuple, bool, double> recombine(solution_t& a, + solution_t& b, + const weight_t& weights) { raft::common::nvtx::range fun_scope("FP recombiner"); auto& guiding_solution = a.get_feasible() ? a : b; @@ -73,8 +73,10 @@ class fp_recombiner_t : public recombiner_t { i_t n_vars_from_guiding = a.problem_ptr->n_integer_vars - n_vars_from_other; if (n_vars_from_other == 0 || n_vars_from_guiding == 0) { CUOPT_LOG_DEBUG("Returning false because all vars are common or different"); - return std::make_pair(offspring, false); + return std::make_tuple(offspring, false, 0.0); } + // TODO: CHANGE + double work = static_cast(n_vars_from_other); CUOPT_LOG_DEBUG( "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); CUOPT_LOG_DEBUG("FP rec: offspring hash 0x%x, vars to fix 0x%x", @@ -160,9 +162,9 @@ class fp_recombiner_t : public recombiner_t { !guiding_solution.get_feasible(); if (better_cost_than_parents || better_feasibility_than_parents) { CUOPT_LOG_DEBUG("Offspring is feasible or better than both parents"); - return std::make_pair(offspring, true); + return std::make_tuple(offspring, true, work); } - return std::make_pair(offspring, !same_as_parents); + return std::make_tuple(offspring, !same_as_parents, work); } rmm::device_uvector vars_to_fix; // keep a copy of FP to prevent interference with generation FP diff --git a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh index 6a28827315..b7bb6f904d 100644 --- a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh @@ -76,9 +76,9 @@ class line_segment_recombiner_t : public recombiner_t { return delta_vector; } - std::pair, bool> recombine(solution_t& a, - solution_t& b, - const weight_t& weights) + std::tuple, bool, double> recombine(solution_t& a, + solution_t& b, + const weight_t& weights) { raft::common::nvtx::range fun_scope("line_segment_recombiner"); CUOPT_LOG_DEBUG("LS rec: a %d b %d", a.get_hash(), b.get_hash()); @@ -94,6 +94,8 @@ class line_segment_recombiner_t : public recombiner_t { const bool is_feasibility_run = false; i_t n_different_vars = this->assign_same_integer_values(guiding_solution, other_solution, offspring); + // TODO: CHANGE + double work = static_cast(n_different_vars); rmm::device_uvector delta_vector = generate_delta_vector( guiding_solution, other_solution, offspring, n_points_to_search, n_different_vars); line_segment_search.fj.copy_weights(weights, offspring.handle_ptr); @@ -129,9 +131,9 @@ class line_segment_recombiner_t : public recombiner_t { } if (better_cost_than_parents || better_feasibility_than_parents) { CUOPT_LOG_DEBUG("Offspring is feasible or better than both parents"); - return std::make_pair(offspring, true); + return std::make_tuple(offspring, true, work); } - return std::make_pair(offspring, !same_as_parents); + return std::make_tuple(offspring, !same_as_parents, work); } line_segment_search_t& line_segment_search; diff --git a/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp b/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp index ca9e1fcb1b..c4003202ef 100644 --- a/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp +++ b/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp @@ -85,21 +85,11 @@ struct all_recombine_stats { // enum of the last attempted recombiner std::optional last_attempt; - double last_recombiner_time; - std::chrono::high_resolution_clock::time_point last_recombiner_start_time; + double last_recombiner_work; - void start_recombiner_time() - { - last_recombiner_start_time = std::chrono::high_resolution_clock::now(); - } - void stop_recombiner_time() - { - last_recombiner_time = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - last_recombiner_start_time) - .count(); - } + void set_recombiner_work(double work) { last_recombiner_work = work; } - double get_last_recombiner_time() { return last_recombiner_time; } + double get_last_recombiner_work() { return last_recombiner_work; } void reset() { diff --git a/cpp/src/mip/diversity/recombiners/sub_mip.cuh b/cpp/src/mip/diversity/recombiners/sub_mip.cuh index 2f465b39dc..956ab46f71 100644 --- a/cpp/src/mip/diversity/recombiners/sub_mip.cuh +++ b/cpp/src/mip/diversity/recombiners/sub_mip.cuh @@ -46,9 +46,9 @@ class sub_mip_recombiner_t : public recombiner_t { solution_vector.push_back(solution); } - std::pair, bool> recombine(solution_t& a, - solution_t& b, - const weight_t& weights) + std::tuple, bool, double> recombine(solution_t& a, + solution_t& b, + const weight_t& weights) { raft::common::nvtx::range fun_scope("Sub-MIP recombiner"); solution_vector.clear(); @@ -74,8 +74,10 @@ class sub_mip_recombiner_t : public recombiner_t { i_t n_vars_from_guiding = a.problem_ptr->n_integer_vars - n_vars_from_other; if (n_vars_from_other == 0 || n_vars_from_guiding == 0) { CUOPT_LOG_DEBUG("Returning false because all vars are common or different"); - return std::make_pair(offspring, false); + return std::make_tuple(offspring, false, 0.0); } + // TODO: CHANGE + double work = static_cast(n_vars_from_other); CUOPT_LOG_DEBUG( "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); @@ -193,9 +195,9 @@ class sub_mip_recombiner_t : public recombiner_t { !guiding_solution.get_feasible(); if (better_cost_than_parents || better_feasibility_than_parents) { CUOPT_LOG_DEBUG("Offspring is feasible or better than both parents"); - return std::make_pair(offspring, true); + return std::make_tuple(offspring, true, work); } - return std::make_pair(offspring, !std::isnan(branch_and_bound_solution.objective)); + return std::make_tuple(offspring, !std::isnan(branch_and_bound_solution.objective), work); } rmm::device_uvector vars_to_fix; mip_solver_context_t& context; From 7f81da1fc10364083553e95871e034854224fb80 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 08:40:53 +0000 Subject: [PATCH 041/225] bump1 --- .../diversity/recombiners/fp_recombiner.cuh | 2 +- .../mip/feasibility_jump/feasibility_jump.cu | 26 +++---- .../feasibility_jump_kernels.cu | 70 +++++++++---------- .../local_search/rounding/constraint_prop.cu | 4 +- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index d0eb26c895..d84e24254f 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -101,7 +101,7 @@ class fp_recombiner_t : public recombiner_t { lp_response.get_termination_status() == pdlp_termination_status_t::DualInfeasible || lp_response.get_termination_status() == pdlp_termination_status_t::TimeLimit) { CUOPT_LOG_DEBUG("FP recombiner failed because LP found infeasible!"); - return std::make_pair(offspring, false); + return std::make_tuple(offspring, false, 0.0); } } // brute force rounding threshold is 8 diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 7b1417e782..6785be21ae 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -654,7 +654,7 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; - auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; + // auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; // use_graph = false; @@ -764,18 +764,18 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream } #endif - cudaLaunchKernel((void*)update_lift_moves_kernel, - grid_lift_move, - blocks_lift_move, - kernel_args, - 0, - climber_stream); - cudaLaunchKernel((void*)update_breakthrough_moves_kernel, - grid_lift_move, - blocks_lift_move, - kernel_args, - 0, - climber_stream); + // cudaLaunchKernel((void*)update_lift_moves_kernel, + // grid_lift_move, + // blocks_lift_move, + // kernel_args, + // 0, + // climber_stream); + // cudaLaunchKernel((void*)update_breakthrough_moves_kernel, + // grid_lift_move, + // blocks_lift_move, + // kernel_args, + // 0, + // climber_stream); } // compaction kernel diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 3210b55da1..69def8b5ce 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1443,46 +1443,46 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat "assignment not within bounds"); } - // also consider breakthrough moves - if (*fj.best_objective < std::numeric_limits::infinity() && - *fj.incumbent_objective > *fj.best_objective) { - cg::this_grid().sync(); - auto [bm_best_var, bm_best_delta, bm_best_score] = - best_breakthrough_move_at_local_min(fj); - if (bm_best_score > best_score) { - best_score = bm_best_score; - best_var = bm_best_var; - best_delta = bm_best_delta; - best_movetype = 'B'; - cuopt_assert(fj.pb.check_variable_within_bounds( - best_var, fj.incumbent_assignment[best_var] + best_delta), - "assignment not within bounds"); - } - } + // // also consider breakthrough moves + // if (*fj.best_objective < std::numeric_limits::infinity() && + // *fj.incumbent_objective > *fj.best_objective) { + // cg::this_grid().sync(); + // auto [bm_best_var, bm_best_delta, bm_best_score] = + // best_breakthrough_move_at_local_min(fj); + // if (bm_best_score > best_score) { + // best_score = bm_best_score; + // best_var = bm_best_var; + // best_delta = bm_best_delta; + // best_movetype = 'B'; + // cuopt_assert(fj.pb.check_variable_within_bounds( + // best_var, fj.incumbent_assignment[best_var] + best_delta), + // "assignment not within bounds"); + // } + // } if (FIRST_THREAD) *fj.selected_var = best_var; cg::this_grid().sync(); // still nothing? try sat MTM moves if we are in the feasible region // Attempt to find a valid move by going over MTM moves in valid constraints - if (*fj.selected_var == std::numeric_limits::max() && - *fj.incumbent_objective < std::numeric_limits::infinity()) { - auto [sat_best_var, sat_best_delta, sat_best_score] = - best_sat_cstr_mtm_move(fj); - - if (FIRST_THREAD && sat_best_score.valid()) - cuopt_assert(fj.pb.check_variable_within_bounds( - sat_best_var, fj.incumbent_assignment[sat_best_var] + sat_best_delta), - "assignment not within bounds"); - - if (sat_best_score.base > 0 && sat_best_score > best_score) { - if (FIRST_THREAD) { - best_score = sat_best_score; - best_var = sat_best_var; - best_delta = sat_best_delta; - best_movetype = 'S'; - } - } - } + // if (*fj.selected_var == std::numeric_limits::max() && + // *fj.incumbent_objective < std::numeric_limits::infinity()) { + // auto [sat_best_var, sat_best_delta, sat_best_score] = + // best_sat_cstr_mtm_move(fj); + + // if (FIRST_THREAD && sat_best_score.valid()) + // cuopt_assert(fj.pb.check_variable_within_bounds( + // sat_best_var, fj.incumbent_assignment[sat_best_var] + sat_best_delta), + // "assignment not within bounds"); + + // if (sat_best_score.base > 0 && sat_best_score > best_score) { + // if (FIRST_THREAD) { + // best_score = sat_best_score; + // best_var = sat_best_var; + // best_delta = sat_best_delta; + // best_movetype = 'S';1 + // } + // } + // } if (FIRST_THREAD) { *fj.selected_var = best_var; diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index f4b60ad7aa..dd9221dbf2 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -1140,8 +1140,8 @@ bool constraint_prop_t::apply_round( auto cp_end_time = std::chrono::high_resolution_clock::now(); auto cp_elapsed_ms = std::chrono::duration_cast(cp_end_time - cp_start_time).count(); - CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=BRUTE_FORCE_SUCCESS iterations=0", - cp_elapsed_ms); + // CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=BRUTE_FORCE_SUCCESS iterations=0", + // cp_elapsed_ms); return true; } recovery_mode = false; From 2726d23a482a2d89f438679e0c518d94cffad557 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 08:41:15 +0000 Subject: [PATCH 042/225] bump2 --- cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 69def8b5ce..49583aec37 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1479,7 +1479,7 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat // best_score = sat_best_score; // best_var = sat_best_var; // best_delta = sat_best_delta; - // best_movetype = 'S';1 + // best_movetype = 'S';2 // } // } // } From 7ea0f1d7ba97f9764a3e8166ce7ba67fc8d98edc Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 11:17:34 +0000 Subject: [PATCH 043/225] fix brute force rounding indeterminism --- cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh b/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh index ce1f4879ee..d0f86863df 100644 --- a/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh +++ b/cpp/src/mip/local_search/rounding/simple_rounding_kernels.cuh @@ -125,7 +125,7 @@ __global__ void brute_force_check_kernel(typename solution_t::view_t s __shared__ i_t shbuf[raft::WarpSize]; i_t total_feasible = raft::blockReduce(th_feasible_count, (char*)shbuf); if (threadIdx.x == 0) { - if (total_feasible == solution.problem.n_constraints) { atomicExch(best_config, config); } + if (total_feasible == solution.problem.n_constraints) { atomicMin(best_config, config); } } } From 4e74f15c7330ff5c7fdb5c41695dd8c52fe9fb0e Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 11:18:02 +0000 Subject: [PATCH 044/225] bump --- cpp/src/mip/local_search/rounding/bounds_repair.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index d6cd62d2ee..e7484ea3ab 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -111,7 +111,7 @@ template i_t bounds_repair_t::get_random_cstr() { std::uniform_int_distribution<> dist(0, h_n_violated_cstr - 1); - // Generate random number + // Generate random number1 i_t random_number = dist(gen); i_t cstr_idx = violated_constraints.element(random_number, handle_ptr->get_stream()); CUOPT_LOG_TRACE("Repair: selected random cstr %d", cstr_idx); From 709f281c2a0c758738cf732123d5d7860fea879f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 11:22:06 +0000 Subject: [PATCH 045/225] restore fj --- .../mip/feasibility_jump/feasibility_jump.cu | 26 +++---- .../feasibility_jump_kernels.cu | 68 +++++++++---------- .../local_search/rounding/bounds_repair.cu | 2 +- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 6785be21ae..7b1417e782 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -654,7 +654,7 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; - // auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; + auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; // use_graph = false; @@ -764,18 +764,18 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream } #endif - // cudaLaunchKernel((void*)update_lift_moves_kernel, - // grid_lift_move, - // blocks_lift_move, - // kernel_args, - // 0, - // climber_stream); - // cudaLaunchKernel((void*)update_breakthrough_moves_kernel, - // grid_lift_move, - // blocks_lift_move, - // kernel_args, - // 0, - // climber_stream); + cudaLaunchKernel((void*)update_lift_moves_kernel, + grid_lift_move, + blocks_lift_move, + kernel_args, + 0, + climber_stream); + cudaLaunchKernel((void*)update_breakthrough_moves_kernel, + grid_lift_move, + blocks_lift_move, + kernel_args, + 0, + climber_stream); } // compaction kernel diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 49583aec37..cff2a1c569 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1444,45 +1444,45 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat } // // also consider breakthrough moves - // if (*fj.best_objective < std::numeric_limits::infinity() && - // *fj.incumbent_objective > *fj.best_objective) { - // cg::this_grid().sync(); - // auto [bm_best_var, bm_best_delta, bm_best_score] = - // best_breakthrough_move_at_local_min(fj); - // if (bm_best_score > best_score) { - // best_score = bm_best_score; - // best_var = bm_best_var; - // best_delta = bm_best_delta; - // best_movetype = 'B'; - // cuopt_assert(fj.pb.check_variable_within_bounds( - // best_var, fj.incumbent_assignment[best_var] + best_delta), - // "assignment not within bounds"); - // } - // } + if (*fj.best_objective < std::numeric_limits::infinity() && + *fj.incumbent_objective > *fj.best_objective) { + cg::this_grid().sync(); + auto [bm_best_var, bm_best_delta, bm_best_score] = + best_breakthrough_move_at_local_min(fj); + if (bm_best_score > best_score) { + best_score = bm_best_score; + best_var = bm_best_var; + best_delta = bm_best_delta; + best_movetype = 'B'; + cuopt_assert(fj.pb.check_variable_within_bounds( + best_var, fj.incumbent_assignment[best_var] + best_delta), + "assignment not within bounds"); + } + } if (FIRST_THREAD) *fj.selected_var = best_var; cg::this_grid().sync(); // still nothing? try sat MTM moves if we are in the feasible region // Attempt to find a valid move by going over MTM moves in valid constraints - // if (*fj.selected_var == std::numeric_limits::max() && - // *fj.incumbent_objective < std::numeric_limits::infinity()) { - // auto [sat_best_var, sat_best_delta, sat_best_score] = - // best_sat_cstr_mtm_move(fj); - - // if (FIRST_THREAD && sat_best_score.valid()) - // cuopt_assert(fj.pb.check_variable_within_bounds( - // sat_best_var, fj.incumbent_assignment[sat_best_var] + sat_best_delta), - // "assignment not within bounds"); - - // if (sat_best_score.base > 0 && sat_best_score > best_score) { - // if (FIRST_THREAD) { - // best_score = sat_best_score; - // best_var = sat_best_var; - // best_delta = sat_best_delta; - // best_movetype = 'S';2 - // } - // } - // } + if (*fj.selected_var == std::numeric_limits::max() && + *fj.incumbent_objective < std::numeric_limits::infinity()) { + auto [sat_best_var, sat_best_delta, sat_best_score] = + best_sat_cstr_mtm_move(fj); + + if (FIRST_THREAD && sat_best_score.valid()) + cuopt_assert(fj.pb.check_variable_within_bounds( + sat_best_var, fj.incumbent_assignment[sat_best_var] + sat_best_delta), + "assignment not within bounds"); + + if (sat_best_score.base > 0 && sat_best_score > best_score) { + if (FIRST_THREAD) { + best_score = sat_best_score; + best_var = sat_best_var; + best_delta = sat_best_delta; + best_movetype = 'S'; + } + } + } if (FIRST_THREAD) { *fj.selected_var = best_var; diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index e7484ea3ab..d6cd62d2ee 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -111,7 +111,7 @@ template i_t bounds_repair_t::get_random_cstr() { std::uniform_int_distribution<> dist(0, h_n_violated_cstr - 1); - // Generate random number1 + // Generate random number i_t random_number = dist(gen); i_t cstr_idx = violated_constraints.element(random_number, handle_ptr->get_stream()); CUOPT_LOG_TRACE("Repair: selected random cstr %d", cstr_idx); From 41198e25c339130a57e2250f4f2ebc23cbc72000 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 11:22:26 +0000 Subject: [PATCH 046/225] bump1 --- cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index cff2a1c569..3210b55da1 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1443,7 +1443,7 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat "assignment not within bounds"); } - // // also consider breakthrough moves + // also consider breakthrough moves if (*fj.best_objective < std::numeric_limits::infinity() && *fj.incumbent_objective > *fj.best_objective) { cg::this_grid().sync(); From d98e083c29413e542061ad8f877e9b715087eab2 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 13:32:03 +0000 Subject: [PATCH 047/225] more logging --- cpp/src/mip/diversity/diversity_manager.cu | 5 +++ .../recombiners/bound_prop_recombiner.cuh | 40 ++++++++++++++++++- cpp/src/mip/diversity/weights.cuh | 7 ++++ .../feasibility_pump/feasibility_pump.cu | 2 +- cpp/src/mip/problem/problem.cu | 21 ++++++---- 5 files changed, 65 insertions(+), 10 deletions(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 244f2223e7..6e3d5c78f2 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -743,6 +743,11 @@ std::pair, bool> diversity_manager_t::recombine( } mab_recombiner.set_last_chosen_option(selected_index); recombine_stats.add_attempt((recombiner_enum_t)recombiner); + CUOPT_LOG_DEBUG("Recombining sol %x and %x with recombiner %d, weights %x", + a.get_hash(), + b.get_hash(), + recombiner, + population.weights.get_hash()); // Refactored code using a switch statement switch (recombiner) { case recombiner_enum_t::BOUND_PROP: { diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index 286f7d7b29..2c85520f0e 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -148,10 +148,11 @@ class bound_prop_recombiner_t : public recombiner_t { i_t n_vars_from_other = n_different_vars; i_t fixed_from_guiding = 0; i_t fixed_from_other = 0; + i_t seed = cuopt::seed_generator::get_seed(); if (n_different_vars > (i_t)bp_recombiner_config_t::max_n_of_vars_from_other) { fixed_from_guiding = n_vars_from_other - bp_recombiner_config_t::max_n_of_vars_from_other; n_vars_from_other = bp_recombiner_config_t::max_n_of_vars_from_other; - thrust::default_random_engine g{(unsigned int)cuopt::seed_generator::get_seed()}; + thrust::default_random_engine g{(unsigned int)seed}; thrust::shuffle(a.handle_ptr->get_thrust_policy(), this->remaining_indices.data(), this->remaining_indices.data() + n_different_vars, @@ -160,6 +161,27 @@ class bound_prop_recombiner_t : public recombiner_t { i_t n_vars_from_guiding = a.problem_ptr->n_integer_vars - n_vars_from_other; CUOPT_LOG_DEBUG( "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); + + // DETERMINISM DEBUG: Log everything that could affect divergence + CUOPT_LOG_DEBUG("BP_DET: sol_a_hash=0x%x sol_b_hash=0x%x offspring_hash=0x%x, seed %x", + a.get_hash(), + b.get_hash(), + offspring.get_hash(), + seed); + CUOPT_LOG_DEBUG("BP_DET: n_different_vars=%d n_vars_from_other=%d n_vars_from_guiding=%d", + n_different_vars, + n_vars_from_other, + n_vars_from_guiding); + CUOPT_LOG_DEBUG("BP_DET: remaining_indices_hash=0x%x (first %d elements)", + detail::compute_hash(this->remaining_indices), + std::min((i_t)10, n_vars_from_other)); + CUOPT_LOG_DEBUG("BP_DET: guiding_feasible=%d other_feasible=%d expensive_to_fix=%d", + guiding_solution.get_feasible(), + other_solution.get_feasible(), + a.problem_ptr->expensive_to_fix_vars); + CUOPT_LOG_DEBUG( + "BP_DET: fixed_from_guiding=%d fixed_from_other=%d", fixed_from_guiding, fixed_from_other); + // if either all integers are from A(meaning all are common) or all integers are from B(meaning // all are different), return if (n_vars_from_guiding == 0 || n_vars_from_other == 0) { @@ -176,8 +198,13 @@ class bound_prop_recombiner_t : public recombiner_t { a.handle_ptr->get_stream()); probing_config_t probing_config(a.problem_ptr->n_variables, a.handle_ptr); if (guiding_solution.get_feasible() && !a.problem_ptr->expensive_to_fix_vars) { + CUOPT_LOG_DEBUG("BP_DET: Taking FEASIBLE path (with variable fixing)"); this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); + CUOPT_LOG_DEBUG("BP_DET: vars_to_fix_hash=0x%x", detail::compute_hash(vars_to_fix)); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); + CUOPT_LOG_DEBUG("BP_DET: fixed_problem_fingerprint=0x%x variable_map_size=%lu", + fixed_problem.get_fingerprint(), + variable_map.size()); work_limit_timer_t timer(this->context.gpu_heur_loop, bp_recombiner_config_t::bounds_prop_time_limit); rmm::device_uvector old_assignment(offspring.assignment, @@ -229,13 +256,18 @@ class bound_prop_recombiner_t : public recombiner_t { } a.handle_ptr->sync_stream(); } else { + CUOPT_LOG_DEBUG("BP_DET: Taking INFEASIBLE path (no variable fixing)"); work_limit_timer_t timer(this->context.gpu_heur_loop, bp_recombiner_config_t::bounds_prop_time_limit); get_probing_values_for_infeasible( guiding_solution, other_solution, offspring, probing_values, n_vars_from_other); probing_config.probing_values = host_copy(probing_values); + CUOPT_LOG_DEBUG("BP_DET: probing_values_hash=0x%x", detail::compute_hash(probing_values)); constraint_prop.apply_round(offspring, lp_run_time_after_feasible, timer, probing_config); } + CUOPT_LOG_DEBUG("BP_DET: After apply_round: offspring_hash=0x%x feasible=%d", + offspring.get_hash(), + offspring.get_feasible()); constraint_prop.max_n_failed_repair_iterations = 1; cuopt_func_call(offspring.test_number_all_integer()); bool better_cost_than_parents = @@ -255,6 +287,12 @@ class bound_prop_recombiner_t : public recombiner_t { bp_recombiner_config_t::decrease_max_n_of_vars_from_other(); } } + CUOPT_LOG_DEBUG( + "BP_DET: Final offspring_hash=0x%x same_as_parents=%d better_cost=%d better_feas=%d", + offspring.get_hash(), + same_as_parents, + better_cost_than_parents, + better_feasibility_than_parents); if (better_cost_than_parents || better_feasibility_than_parents) { CUOPT_LOG_DEBUG("Offspring is feasible or better than both parents"); return std::make_tuple(offspring, true, work); diff --git a/cpp/src/mip/diversity/weights.cuh b/cpp/src/mip/diversity/weights.cuh index 7502ae9210..38f3975ed5 100644 --- a/cpp/src/mip/diversity/weights.cuh +++ b/cpp/src/mip/diversity/weights.cuh @@ -12,6 +12,8 @@ #include #include +#include + namespace cuopt::linear_programming::detail { template @@ -25,6 +27,11 @@ struct weight_t { objective_weight.set_value_async(one, handle_ptr->get_stream()); } + uint32_t get_hash(rmm::cuda_stream_view stream = rmm::cuda_stream_default) const + { + return compute_hash(cstr_weights, stream) ^ compute_hash(objective_weight.value(stream)); + } + rmm::device_uvector cstr_weights; rmm::device_scalar objective_weight; }; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index c2110609da..205292f7f0 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -646,7 +646,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s cuopt_func_call(solution.test_variable_bounds(false)); is_feasible = round(solution); cuopt_func_call(solution.test_variable_bounds(true)); - proj_and_round_time = proj_begin - timer.remaining_time(); + proj_and_round_time = timer.remaining_time(); if (!is_feasible && proj_and_round_time > 0) { const f_t time_ratio = 0.2; is_feasible = test_fj_feasible(solution, time_ratio * proj_and_round_time); diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 07c14c6c01..03ff403302 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -885,7 +885,7 @@ void problem_t::compute_related_variables(double time_limit) i_t output_offset = 0; i_t related_var_offset = 0; - auto start_time = std::chrono::high_resolution_clock::now(); + // auto start_time = std::chrono::high_resolution_clock::now(); for (i_t i = 0;; ++i) { i_t slice_size = std::min(max_slice_size, n_variables - i * max_slice_size); if (slice_size <= 0) break; @@ -914,12 +914,13 @@ void problem_t::compute_related_variables(double time_limit) i_t related_var_base = related_variables.size(); related_variables.resize(related_variables.size() + array_size, handle_ptr->get_stream()); - auto current_time = std::chrono::high_resolution_clock::now(); + // auto current_time = std::chrono::high_resolution_clock::now(); // if the related variable array would wind up being too large for available memory, abort // TODO this used to be 1e9 - if (related_variables.size() > 1e9 * size_factor || - std::chrono::duration_cast(current_time - start_time).count() > - time_limit) { + if (related_variables.size() > 1e9 * size_factor) { + // || + // std::chrono::duration_cast(current_time - start_time).count() > + // time_limit) { CUOPT_LOG_DEBUG( "Computing the related variable array would use too much memory or time, aborting\n"); related_variables.resize(0, handle_ptr->get_stream()); @@ -1287,9 +1288,13 @@ problem_t problem_t::get_problem_after_fixing_vars( // total_time_taken); // if the fixing is greater than 150, mark this as expensive. // this way we can avoid frequent fixings for this problem - constexpr double expensive_time_threshold = 150; - if (time_taken > expensive_time_threshold) { expensive_to_fix_vars = true; } - CUOPT_LOG_DEBUG("Model fingerprint after fixing: 0x%x", problem.get_fingerprint()); + + // TODO: CHANGE + // constexpr double expensive_time_threshold = 150; + // if (time_taken > expensive_time_threshold) { expensive_to_fix_vars = true; } + CUOPT_LOG_DEBUG("Model fingerprint after fixing: 0x%x, expensive? %d", + problem.get_fingerprint(), + expensive_to_fix_vars); return problem; } From 786ae8a887837a8b7dae81105360ab9ba0739d21 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 13:32:23 +0000 Subject: [PATCH 048/225] bump1 --- cpp/src/mip/diversity/weights.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/diversity/weights.cuh b/cpp/src/mip/diversity/weights.cuh index 38f3975ed5..f7f35fdbe5 100644 --- a/cpp/src/mip/diversity/weights.cuh +++ b/cpp/src/mip/diversity/weights.cuh @@ -22,7 +22,7 @@ struct weight_t { : cstr_weights(cstr_size, handle_ptr->get_stream()), objective_weight(handle_ptr->get_stream()) { thrust::fill(handle_ptr->get_thrust_policy(), cstr_weights.begin(), cstr_weights.end(), 1.0); - // objective_weight.set_value_to_zero_async(handle_ptr->get_stream()); + // objective_weight.set_value_to_zero_async(handle_ptr->get_stream());1 const f_t one = 1.; objective_weight.set_value_async(one, handle_ptr->get_stream()); } From 2fade1d6418f41a88547963e37607db188582bbe Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 13:32:40 +0000 Subject: [PATCH 049/225] bump2 --- cpp/src/mip/diversity/weights.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/diversity/weights.cuh b/cpp/src/mip/diversity/weights.cuh index f7f35fdbe5..38f3975ed5 100644 --- a/cpp/src/mip/diversity/weights.cuh +++ b/cpp/src/mip/diversity/weights.cuh @@ -22,7 +22,7 @@ struct weight_t { : cstr_weights(cstr_size, handle_ptr->get_stream()), objective_weight(handle_ptr->get_stream()) { thrust::fill(handle_ptr->get_thrust_policy(), cstr_weights.begin(), cstr_weights.end(), 1.0); - // objective_weight.set_value_to_zero_async(handle_ptr->get_stream());1 + // objective_weight.set_value_to_zero_async(handle_ptr->get_stream()); const f_t one = 1.; objective_weight.set_value_async(one, handle_ptr->get_stream()); } From c89e2c9da59ae4108eab5cb532da4141cc29c53f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 15:57:37 +0000 Subject: [PATCH 050/225] fix missing grid sync --- .../feasibility_jump/feasibility_jump_kernels.cu | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 3210b55da1..441d4dedae 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1324,7 +1324,11 @@ DI thrust::tuple::move_score_t> best_sat_cstr_ typename fj_t::climber_data_t::view_t fj) { // compute all MTM moves within satisfied constraints + // TODO: only compute such moves over the objective variables. that way, the grid sync can be + // eliminated compute_mtm_moves(fj, true); + // TODO: could probably be eliminated + cg::this_grid().sync(); return gridwide_reduce_best_move( fj, fj.objective_vars.begin(), fj.objective_vars.end(), [fj] __device__(i_t var_idx) { return fj.jump_move_delta[var_idx]; @@ -1491,8 +1495,15 @@ __global__ void handle_local_minimum_kernel(typename fj_t::climber_dat best_var, fj.incumbent_assignment[best_var] + best_delta), "assignment not within bounds"); fj.jump_move_delta[best_var] = best_delta; - // DEVICE_LOG_DEBUG("FJ[%d] selected_var: %d, delta %g, score {%d %d}, type %c\n", - // *fj.iterations, best_var, best_delta, best_score.base, best_score.bonus, best_movetype); +#if FJ_SINGLE_STEP + DEVICE_LOG_DEBUG("FJ[%d] selected_var: %d, delta %g, score {%d %d}, type %c\n", + *fj.iterations, + best_var, + best_delta, + best_score.base, + best_score.bonus, + best_movetype); +#endif } } } From cbfd34be53b5783f543df671078deef9e9934ce8 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 15:57:56 +0000 Subject: [PATCH 051/225] bump1 --- cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 441d4dedae..24c01cc705 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1327,7 +1327,7 @@ DI thrust::tuple::move_score_t> best_sat_cstr_ // TODO: only compute such moves over the objective variables. that way, the grid sync can be // eliminated compute_mtm_moves(fj, true); - // TODO: could probably be eliminated + // TODO: could probably be eliminated1 cg::this_grid().sync(); return gridwide_reduce_best_move( fj, fj.objective_vars.begin(), fj.objective_vars.end(), [fj] __device__(i_t var_idx) { From ea5e1bd61d12ff9246133691024f431ced754080 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 15:58:08 +0000 Subject: [PATCH 052/225] bump2 --- cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 24c01cc705..441d4dedae 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1327,7 +1327,7 @@ DI thrust::tuple::move_score_t> best_sat_cstr_ // TODO: only compute such moves over the objective variables. that way, the grid sync can be // eliminated compute_mtm_moves(fj, true); - // TODO: could probably be eliminated1 + // TODO: could probably be eliminated cg::this_grid().sync(); return gridwide_reduce_best_move( fj, fj.objective_vars.begin(), fj.objective_vars.end(), [fj] __device__(i_t var_idx) { From 8c53c755b7ac52870f2cf34cf5ea79fb29b8184c Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 15:59:29 +0000 Subject: [PATCH 053/225] restore load balancing --- cpp/src/mip/feasibility_jump/feasibility_jump.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 7b1417e782..1c6587f3b5 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -1322,7 +1322,7 @@ i_t fj_t::solve(solution_t& solution) detail::compute_hash(cstr_left_weights), detail::compute_hash(cstr_right_weights)); - settings.load_balancing_mode = fj_load_balancing_mode_t::ALWAYS_OFF; + // 1settings.load_balancing_mode = fj_load_balancing_mode_t::ALWAYS_OFF; if (context.settings.deterministic) { settings.work_limit = settings.time_limit; } // if work_limit is set: compute an estimate of the number of iterations required From c6a103713daaa3410702b0f5d3e21ae5759dd422 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 15:59:42 +0000 Subject: [PATCH 054/225] bump 1 --- cpp/src/mip/feasibility_jump/feasibility_jump.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 1c6587f3b5..2817c7883a 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -1322,7 +1322,7 @@ i_t fj_t::solve(solution_t& solution) detail::compute_hash(cstr_left_weights), detail::compute_hash(cstr_right_weights)); - // 1settings.load_balancing_mode = fj_load_balancing_mode_t::ALWAYS_OFF; + // settings.load_balancing_mode = fj_load_balancing_mode_t::ALWAYS_OFF; if (context.settings.deterministic) { settings.work_limit = settings.time_limit; } // if work_limit is set: compute an estimate of the number of iterations required From 09a484a7ab4560c3b464237001f2d02598c13b77 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 17:51:30 +0000 Subject: [PATCH 055/225] remove unecessary grid sync --- .../feasibility_jump_kernels.cu | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 441d4dedae..a7195fabea 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -990,11 +990,14 @@ __device__ void compute_mtm_moves(typename fj_t::climber_data_t::view_ if (*fj.selected_var == std::numeric_limits::max()) full_refresh = true; // always do a full sweep when looking for satisfied mtm moves - if constexpr (move_type == MTMMoveType::FJ_MTM_SATISFIED) full_refresh = true; - - // only update related variables i_t split_begin, split_end; - if (full_refresh) { + if constexpr (move_type == MTMMoveType::FJ_MTM_SATISFIED) { + full_refresh = true; + split_begin = 0; + split_end = fj.objective_vars.size(); + } + // only update related variables + else if (full_refresh) { split_begin = 0; split_end = fj.pb.n_variables; } @@ -1016,9 +1019,15 @@ __device__ void compute_mtm_moves(typename fj_t::climber_data_t::view_ if (FIRST_THREAD) *fj.relvar_count_last_update = split_end - split_begin; for (i_t i = blockIdx.x + split_begin; i < split_end; i += gridDim.x) { - i_t var_idx = full_refresh ? i - : fj.pb.related_variables.size() == 0 ? i - : fj.pb.related_variables[i]; + // if sat MTM mode, go over objective variables only + i_t var_idx; + if constexpr (move_type == MTMMoveType::FJ_MTM_SATISFIED) { + var_idx = fj.objective_vars[i]; + } else { + var_idx = full_refresh ? i + : fj.pb.related_variables.size() == 0 ? i + : fj.pb.related_variables[i]; + } // skip if we couldnt precompute a related var table and // this variable isnt in the dynamic related variable table @@ -1042,11 +1051,6 @@ __device__ void compute_mtm_moves(typename fj_t::climber_data_t::view_ cuopt_assert(var_idx >= 0 && var_idx < fj.pb.n_variables, ""); update_jump_value(fj, var_idx); - // if (move_type == MTMMoveType::FJ_MTM_SATISFIED && threadIdx.x == 0) { - // printf("iter[%d] block[%d] var %d score {%d,%d}, delta %g\n", *fj.iterations, blockIdx.x, - // var_idx, fj.jump_move_scores[var_idx].base, fj.jump_move_scores[var_idx].bonus, - // fj.jump_move_delta[var_idx]); - // } } } @@ -1324,11 +1328,9 @@ DI thrust::tuple::move_score_t> best_sat_cstr_ typename fj_t::climber_data_t::view_t fj) { // compute all MTM moves within satisfied constraints - // TODO: only compute such moves over the objective variables. that way, the grid sync can be - // eliminated compute_mtm_moves(fj, true); - // TODO: could probably be eliminated - cg::this_grid().sync(); + // NOTE: grid sync not required since each block only reduces over variables that it updated in + // compute_mtm_moves return gridwide_reduce_best_move( fj, fj.objective_vars.begin(), fj.objective_vars.end(), [fj] __device__(i_t var_idx) { return fj.jump_move_delta[var_idx]; From f55e64898e499c1208e2a3f66d14b4b3ca4531b6 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 17:54:01 +0000 Subject: [PATCH 056/225] bump1 --- cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index a7195fabea..21b6a0a7ee 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -989,7 +989,7 @@ __device__ void compute_mtm_moves(typename fj_t::climber_data_t::view_ if (*fj.full_refresh_iteration == *fj.iterations) full_refresh = true; if (*fj.selected_var == std::numeric_limits::max()) full_refresh = true; - // always do a full sweep when looking for satisfied mtm moves + // always do a full sweep when looking for satisfied mtm moves1 i_t split_begin, split_end; if constexpr (move_type == MTMMoveType::FJ_MTM_SATISFIED) { full_refresh = true; From 554d24c837b8efaaa4f9596899552eb3a7e2f493 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 8 Nov 2025 09:56:00 -0800 Subject: [PATCH 057/225] bump2 --- cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 21b6a0a7ee..a7195fabea 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -989,7 +989,7 @@ __device__ void compute_mtm_moves(typename fj_t::climber_data_t::view_ if (*fj.full_refresh_iteration == *fj.iterations) full_refresh = true; if (*fj.selected_var == std::numeric_limits::max()) full_refresh = true; - // always do a full sweep when looking for satisfied mtm moves1 + // always do a full sweep when looking for satisfied mtm moves i_t split_begin, split_end; if constexpr (move_type == MTMMoveType::FJ_MTM_SATISFIED) { full_refresh = true; From db73e00aebedf12e06aec1c3eb74f865d59428c2 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 02:44:00 -0800 Subject: [PATCH 058/225] FJCPU in deterministic mode --- cpp/src/mip/local_search/local_search.cu | 32 ++++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 0e2e1702d0..08f2c92f53 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -88,9 +88,13 @@ void cpu_fj_thread_t::stop_cpu_solver() template bool cpu_fj_thread_t::wait_for_cpu_solver() { + auto wait_start = std::chrono::high_resolution_clock::now(); while (!cpu_thread_done && !cpu_thread_terminate) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } + auto wait_end = std::chrono::high_resolution_clock::now(); + double wait_time = std::chrono::duration(wait_end - wait_start).count(); + if (wait_time > 1.0) { CUOPT_LOG_DEBUG("CPU FJ thread wait time: %.2f seconds", wait_time); } return cpu_fj_solution_found; } @@ -134,6 +138,9 @@ void local_search_t::start_cpufj_scratch_threads(population_t default_weights(context.problem_ptr->n_constraints, 1.); + fj_settings_t cpu_fj_settings{}; + cpu_fj_settings.time_limit = std::numeric_limits::infinity(); + solution_t solution(*context.problem_ptr); thrust::fill(solution.handle_ptr->get_thrust_policy(), solution.assignment.begin(), @@ -147,7 +154,7 @@ void local_search_t::start_cpufj_scratch_threads(population_t 0); cpu_fj.fj_cpu->log_prefix = "******* scratch " + std::to_string(counter) + ": "; @@ -181,11 +188,14 @@ void local_search_t::start_cpufj_lptopt_scratch_threads( std::vector default_weights(context.problem_ptr->n_constraints, 1.); + fj_settings_t cpu_fj_settings{}; + cpu_fj_settings.time_limit = std::numeric_limits::infinity(); + solution_t solution_lp(*context.problem_ptr); solution_lp.copy_new_assignment(host_copy(lp_optimal_solution)); solution_lp.round_random_nearest(500); scratch_cpu_fj_on_lp_opt.fj_cpu = - fj.create_cpu_climber(solution_lp, default_weights, default_weights, 0.); + fj.create_cpu_climber(solution_lp, default_weights, default_weights, 0., cpu_fj_settings, true); scratch_cpu_fj_on_lp_opt.fj_cpu->log_prefix = "******* scratch on LP optimal: "; if (!context.settings.deterministic) { scratch_cpu_fj_on_lp_opt.fj_cpu->improvement_callback = @@ -228,9 +238,19 @@ bool local_search_t::do_fj_solve(solution_t& solution, auto h_weights = cuopt::host_copy(in_fj.cstr_weights, solution.handle_ptr->get_stream()); auto h_objective_weight = in_fj.objective_weight.value(solution.handle_ptr->get_stream()); + + // for now: always assign the CPUFJs to perform 1000 iters per s + fj_settings_t cpu_fj_settings{}; + if (context.settings.deterministic) { + cpu_fj_settings.iteration_limit = std::numeric_limits::max(); + } else { + // TODO: CHANGE + cpu_fj_settings.iteration_limit = 1000 * time_limit; + } + for (auto& cpu_fj : ls_cpu_fj) { cpu_fj.fj_cpu = cpu_fj.fj_ptr->create_cpu_climber( - solution, h_weights, h_weights, h_objective_weight, fj_settings_t{}, true); + solution, h_weights, h_weights, h_objective_weight, cpu_fj_settings, true); } auto solution_copy = solution; @@ -246,8 +266,10 @@ bool local_search_t::do_fj_solve(solution_t& solution, in_fj.solve(solution); // Stop CPU solver - for (auto& cpu_fj : ls_cpu_fj) { - cpu_fj.stop_cpu_solver(); + if (!context.settings.deterministic) { + for (auto& cpu_fj : ls_cpu_fj) { + cpu_fj.stop_cpu_solver(); + } } auto gpu_fj_end = std::chrono::high_resolution_clock::now(); From 0b8b6e156fc2c2e4b316dca9ed1c9adaae38eec1 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 02:44:28 -0800 Subject: [PATCH 059/225] bump1 --- cpp/src/mip/local_search/local_search.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 08f2c92f53..1379a0397d 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -107,7 +107,7 @@ local_search_t::local_search_t(mip_solver_context_t& context fj_sol_on_lp_opt(context.problem_ptr->n_variables, context.problem_ptr->handle_ptr->get_stream()), fj(context), - // fj_tree(fj), + // fj_tree(fj),1 constraint_prop(context), line_segment_search(context, fj, constraint_prop), fp(context, From 122bfbce18233e69594965db5c503140bea4a753 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 02:44:42 -0800 Subject: [PATCH 060/225] bump2 --- cpp/src/mip/local_search/local_search.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 1379a0397d..08f2c92f53 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -107,7 +107,7 @@ local_search_t::local_search_t(mip_solver_context_t& context fj_sol_on_lp_opt(context.problem_ptr->n_variables, context.problem_ptr->handle_ptr->get_stream()), fj(context), - // fj_tree(fj),1 + // fj_tree(fj), constraint_prop(context), line_segment_search(context, fj, constraint_prop), fp(context, From 45ebd575b905e12c17abd60a697b482d0f19cfca Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 02:46:11 -0800 Subject: [PATCH 061/225] fix oversight --- cpp/src/mip/local_search/local_search.cu | 26 ++++++++++-------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 08f2c92f53..82175012b5 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -306,21 +306,17 @@ bool local_search_t::do_fj_solve(solution_t& solution, cpu_better[source]); total_calls[source]++; - // ignore the CPUFJ solution if we're in determinism mode - // TODO: use work limits here - if (!context.settings.deterministic) { - if (cpu_feasible && !gpu_feasible || - (cpu_feasible && solution_cpu.get_objective() < solution.get_objective())) { - CUOPT_LOG_DEBUG( - "CPU FJ returns better solution! cpu_obj %g, gpu_obj %g, stats %d/%d, source %s", - solution_cpu.get_user_objective(), - solution.get_user_objective(), - total_calls[source], - cpu_better[source], - source.c_str()); - solution.copy_from(solution_cpu); - cpu_better[source]++; - } + if (cpu_feasible && !gpu_feasible || + (cpu_feasible && solution_cpu.get_objective() < solution.get_objective())) { + CUOPT_LOG_DEBUG( + "CPU FJ returns better solution! cpu_obj %g, gpu_obj %g, stats %d/%d, source %s", + solution_cpu.get_user_objective(), + solution.get_user_objective(), + total_calls[source], + cpu_better[source], + source.c_str()); + solution.copy_from(solution_cpu); + cpu_better[source]++; } solution.compute_feasibility(); From a209096bdda55dc28d09441a2227b6acdc3667a5 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 02:46:28 -0800 Subject: [PATCH 062/225] bump1 --- cpp/src/mip/local_search/local_search.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 82175012b5..62e2b98910 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -352,7 +352,7 @@ void local_search_t::generate_fast_solution(solution_t& solu // run fj on the solution do_fj_solve(solution, fj, time_limit, "fast"); // TODO check if FJ returns the same solution - // check if the solution is feasible + // check if the solution is feasible1 if (solution.compute_feasibility()) { return; } } } From 718f3e75c11f63db3aa65581f812f7f5270e2dad Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 02:46:37 -0800 Subject: [PATCH 063/225] bump2 --- cpp/src/mip/local_search/local_search.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 62e2b98910..82175012b5 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -352,7 +352,7 @@ void local_search_t::generate_fast_solution(solution_t& solu // run fj on the solution do_fj_solve(solution, fj, time_limit, "fast"); // TODO check if FJ returns the same solution - // check if the solution is feasible1 + // check if the solution is feasible if (solution.compute_feasibility()) { return; } } } From 1df9313bee9c59bc110e8b0a115e263c6989f83c Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 14:45:52 +0000 Subject: [PATCH 064/225] logger --- cpp/src/utilities/logger.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cpp/src/utilities/logger.cpp b/cpp/src/utilities/logger.cpp index fd053c94e0..6a99eb7bc2 100644 --- a/cpp/src/utilities/logger.cpp +++ b/cpp/src/utilities/logger.cpp @@ -80,8 +80,7 @@ rapids_logger::sink_ptr default_sink() * * @return std::string The default log pattern. */ -// inline std::string default_pattern() { return "[%Y-%m-%d %H:%M:%S:%f] [%n] [%-6l] %v"; } -inline std::string default_pattern() { return "[%n] [%-6l] %v"; } +inline std::string default_pattern() { return "[%Y-%m-%d %H:%M:%S:%f] [%n] [%-6l] %v"; } /** * @brief Returns the default log level for the global logger. * From b45982ef81226a6585bc620f3432263fceb39026 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 14:57:40 +0000 Subject: [PATCH 065/225] fix cpufj --- cpp/src/mip/feasibility_jump/fj_cpu.cu | 13 ++++++++++++- cpp/src/mip/local_search/local_search.cu | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index f59bc39977..cbcf30dc48 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -943,6 +943,13 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l fj_cpu.iterations); break; } + if (fj_cpu.iterations >= fj_cpu.settings.iteration_limit) { + CUOPT_LOG_TRACE("%sIteration limit of %d reached, breaking loop at iteration %d\n", + fj_cpu.log_prefix.c_str(), + fj_cpu.settings.iteration_limit, + fj_cpu.iterations); + break; + } // periodically recompute the LHS and violation scores // to correct any accumulated numerical errors @@ -1001,10 +1008,14 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l } if (fj_cpu.iterations % fj_cpu.log_interval == 0) { CUOPT_LOG_TRACE( - "%sCPUFJ iteration: %d, local mins: %d, best_objective: %g, viol: %zu, obj weight %g, maxw " + "%sCPUFJ iteration: %d/%d, local mins: %d, best_objective: %g, viol: %zu, obj weight %g, " + "maxw " "%g\n", fj_cpu.log_prefix.c_str(), fj_cpu.iterations, + fj_cpu.settings.iteration_limit != std::numeric_limits::max() + ? fj_cpu.settings.iteration_limit + : -1, local_mins, fj_cpu.pb_ptr->get_user_obj_from_solver_obj(fj_cpu.h_best_objective), fj_cpu.violated_constraints.size(), diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 82175012b5..3d3d636154 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -241,7 +241,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, // for now: always assign the CPUFJs to perform 1000 iters per s fj_settings_t cpu_fj_settings{}; - if (context.settings.deterministic) { + if (!context.settings.deterministic) { cpu_fj_settings.iteration_limit = std::numeric_limits::max(); } else { // TODO: CHANGE From d27218099a6457ad0ba7b02e9d6ed141d744acb0 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 14:58:34 +0000 Subject: [PATCH 066/225] bump1 --- cpp/src/mip/feasibility_jump/fj_cpu.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index cbcf30dc48..f3df302453 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -1047,7 +1047,7 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l avg_time_per_iter * 1000.0); #if CPUFJ_TIMING_TRACE - // Print final timing statistics + // Print final timing statistics1 CUOPT_LOG_TRACE("\n=== Final Timing Statistics ===\n"); print_timing_stats(fj_cpu); #endif From 0987b5c24ba801f2895a27990002e702f0b39c13 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 9 Nov 2025 14:58:54 +0000 Subject: [PATCH 067/225] bump2 --- cpp/src/mip/feasibility_jump/fj_cpu.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index f3df302453..cbcf30dc48 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -1047,7 +1047,7 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l avg_time_per_iter * 1000.0); #if CPUFJ_TIMING_TRACE - // Print final timing statistics1 + // Print final timing statistics CUOPT_LOG_TRACE("\n=== Final Timing Statistics ===\n"); print_timing_stats(fj_cpu); #endif From f378246d4fc4b01ecd41a2cd6027343e316ce378 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 10 Nov 2025 10:30:59 +0000 Subject: [PATCH 068/225] tentative LB determinism fix --- .../mip/feasibility_jump/load_balancing.cuh | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/load_balancing.cuh b/cpp/src/mip/feasibility_jump/load_balancing.cuh index 361162d6f7..b6c9ea1f9d 100644 --- a/cpp/src/mip/feasibility_jump/load_balancing.cuh +++ b/cpp/src/mip/feasibility_jump/load_balancing.cuh @@ -567,24 +567,29 @@ __launch_bounds__(TPB_loadbalance, 16) __global__ best_score_ref{fj.jump_move_scores[var_idx]}; auto best_score = best_score_ref.load(cuda::memory_order_relaxed); + cuda::atomic_ref best_delta_ref{ + fj.jump_move_delta[var_idx]}; + auto best_delta = best_delta_ref.load(cuda::memory_order_relaxed); + if (best_score < candidate.score || - (best_score == candidate.score && candidate.delta < fj.jump_move_delta[var_idx])) { + (best_score == candidate.score && candidate.delta < best_delta)) { // update the best move delta acquire_lock(&fj.jump_locks[var_idx]); // reject this move if it would increase the target variable to a numerically unstable // value - if (!fj.move_numerically_stable(fj.incumbent_assignment[var_idx], - fj.incumbent_assignment[var_idx] + delta, - base_feas, - *fj.violation_score)) { - fj.jump_move_scores[var_idx] = fj_t::move_score_t::invalid(); - } else if (fj.jump_move_scores[var_idx] < candidate.score - // determinism for ease of debugging - || (fj.jump_move_scores[var_idx] == candidate.score && - candidate.delta < fj.jump_move_delta[var_idx])) { - fj.jump_move_delta[var_idx] = candidate.delta; - fj.jump_move_scores[var_idx] = candidate.score; + // only skip updating, don't invalidate existing valid moves + if (fj.move_numerically_stable(fj.incumbent_assignment[var_idx], + fj.incumbent_assignment[var_idx] + delta, + base_feas, + *fj.violation_score)) { + if (fj.jump_move_scores[var_idx] < candidate.score + // determinism for ease of debugging + || (fj.jump_move_scores[var_idx] == candidate.score && + candidate.delta < fj.jump_move_delta[var_idx])) { + fj.jump_move_delta[var_idx] = candidate.delta; + fj.jump_move_scores[var_idx] = candidate.score; + } } release_lock(&fj.jump_locks[var_idx]); } From 4b77a5958a3c14bcbdea59208c8967379f757c6f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 10 Nov 2025 13:26:37 +0000 Subject: [PATCH 069/225] extra logging CPUFJ, PAPI --- cpp/CMakeLists.txt | 11 + cpp/src/mip/diversity/diversity_manager.cu | 7 +- cpp/src/mip/feasibility_jump/fj_cpu.cu | 428 +++++++++++++++++++-- cpp/src/mip/feasibility_jump/fj_cpu.cuh | 27 ++ cpp/src/mip/solve.cu | 10 + 5 files changed, 460 insertions(+), 23 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 0131690422..7178c61ff7 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -323,6 +323,17 @@ target_link_libraries(cuopt ${CUOPT_PRIVATE_CUDA_LIBS} ) +find_path(PAPI_INCLUDE_DIR papi.h) +find_library(PAPI_LIBRARY papi) + +if (PAPI_INCLUDE_DIR AND PAPI_LIBRARY) + message(STATUS "Found PAPI in ${PAPI_INCLUDE_DIR}") + target_include_directories(cuopt PRIVATE ${PAPI_INCLUDE_DIR}) + target_link_libraries(cuopt PRIVATE ${PAPI_LIBRARY}) +else() + message(FATAL_ERROR "Could not find PAPI") +endif() + # ################################################################################################## # - generate tests -------------------------------------------------------------------------------- diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 6e3d5c78f2..392145ee53 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -354,7 +354,12 @@ solution_t diversity_manager_t::run_solver() // Run CPUFJ early to find quick initial solutions population.allocate_solutions(); ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop - if (!context.settings.deterministic) { ls.start_cpufj_scratch_threads(population); } + if (!context.settings.deterministic) { + ls.start_cpufj_scratch_threads(population); + std::this_thread::sleep_for(std::chrono::seconds(10)); + ls.stop_cpufj_scratch_threads(); + exit(0); + } // before probing cache or LP, run FJ to generate initial primal feasible solution const f_t time_ratio_of_probing_cache = diversity_config.time_ratio_of_probing_cache; diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index cbcf30dc48..58850ab2cf 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -14,16 +14,29 @@ #include #include +#include #include +#include #include #include +#ifdef __linux__ +#include +#include +#include +#endif + #define CPUFJ_TIMING_TRACE 0 namespace cuopt::linear_programming::detail { static constexpr double BIGVAL_THRESHOLD = 1e20; +#ifdef __linux__ +// Global mutex to protect PAPI metric printing across multiple threads +static std::mutex papi_print_mutex; +#endif + template class timing_raii_t { public: @@ -62,43 +75,354 @@ static void print_timing_stats(fj_cpu_climber_t& fj_cpu) auto [apply_avg, apply_total] = compute_avg_and_total(fj_cpu.apply_move_times); auto [weights_avg, weights_total] = compute_avg_and_total(fj_cpu.update_weights_times); auto [compute_score_avg, compute_score_total] = compute_avg_and_total(fj_cpu.compute_score_times); - CUOPT_LOG_TRACE("=== Timing Statistics (Iteration %d) ===\n", fj_cpu.iterations); - CUOPT_LOG_TRACE("find_lift_move: avg=%.6f ms, total=%.6f ms, calls=%zu\n", + CUOPT_LOG_TRACE("=== Timing Statistics (Iteration %d) ===", fj_cpu.iterations); + CUOPT_LOG_TRACE("find_lift_move: avg=%.6f ms, total=%.6f ms, calls=%zu", lift_avg * 1000.0, lift_total * 1000.0, fj_cpu.find_lift_move_times.size()); - CUOPT_LOG_TRACE("find_mtm_move_viol: avg=%.6f ms, total=%.6f ms, calls=%zu\n", + CUOPT_LOG_TRACE("find_mtm_move_viol: avg=%.6f ms, total=%.6f ms, calls=%zu", viol_avg * 1000.0, viol_total * 1000.0, fj_cpu.find_mtm_move_viol_times.size()); - CUOPT_LOG_TRACE("find_mtm_move_sat: avg=%.6f ms, total=%.6f ms, calls=%zu\n", + CUOPT_LOG_TRACE("find_mtm_move_sat: avg=%.6f ms, total=%.6f ms, calls=%zu", sat_avg * 1000.0, sat_total * 1000.0, fj_cpu.find_mtm_move_sat_times.size()); - CUOPT_LOG_TRACE("apply_move: avg=%.6f ms, total=%.6f ms, calls=%zu\n", + CUOPT_LOG_TRACE("apply_move: avg=%.6f ms, total=%.6f ms, calls=%zu", apply_avg * 1000.0, apply_total * 1000.0, fj_cpu.apply_move_times.size()); - CUOPT_LOG_TRACE("update_weights: avg=%.6f ms, total=%.6f ms, calls=%zu\n", + CUOPT_LOG_TRACE("update_weights: avg=%.6f ms, total=%.6f ms, calls=%zu", weights_avg * 1000.0, weights_total * 1000.0, fj_cpu.update_weights_times.size()); - CUOPT_LOG_TRACE("compute_score: avg=%.6f ms, total=%.6f ms, calls=%zu\n", + CUOPT_LOG_TRACE("compute_score: avg=%.6f ms, total=%.6f ms, calls=%zu", compute_score_avg * 1000.0, compute_score_total * 1000.0, fj_cpu.compute_score_times.size()); - CUOPT_LOG_TRACE("cache hit percentage: %.2f%%\n", + CUOPT_LOG_TRACE("cache hit percentage: %.2f%%", (double)fj_cpu.hit_count / (fj_cpu.hit_count + fj_cpu.miss_count) * 100.0); - CUOPT_LOG_TRACE("bin candidate move hit percentage: %.2f%%\n", + CUOPT_LOG_TRACE("bin candidate move hit percentage: %.2f%%", (double)fj_cpu.candidate_move_hits[0] / (fj_cpu.candidate_move_hits[0] + fj_cpu.candidate_move_misses[0]) * 100.0); - CUOPT_LOG_TRACE("int candidate move hit percentage: %.2f%%\n", + CUOPT_LOG_TRACE("int candidate move hit percentage: %.2f%%", (double)fj_cpu.candidate_move_hits[1] / (fj_cpu.candidate_move_hits[1] + fj_cpu.candidate_move_misses[1]) * 100.0); - CUOPT_LOG_TRACE("cont candidate move hit percentage: %.2f%%\n", + CUOPT_LOG_TRACE("cont candidate move hit percentage: %.2f%%", (double)fj_cpu.candidate_move_hits[2] / (fj_cpu.candidate_move_hits[2] + fj_cpu.candidate_move_misses[2]) * 100.0); - CUOPT_LOG_TRACE("========================================\n"); + CUOPT_LOG_TRACE("========================================"); +} + +#ifdef __linux__ +template +static void initialize_papi(fj_cpu_climber_t& fj_cpu) +{ + int retval = PAPI_library_init(PAPI_VER_CURRENT); + if (retval != PAPI_VER_CURRENT && retval > 0) { + CUOPT_LOG_TRACE("%sPAPI library version mismatch", fj_cpu.log_prefix.c_str()); + return; + } + if (retval < 0) { + CUOPT_LOG_TRACE("%sPAPI library initialization failed", fj_cpu.log_prefix.c_str()); + return; + } + + fj_cpu.papi_event_set = PAPI_NULL; + retval = PAPI_create_eventset(&fj_cpu.papi_event_set); + if (retval != PAPI_OK) { + CUOPT_LOG_TRACE("%sPAPI eventset creation failed", fj_cpu.log_prefix.c_str()); + return; + } + + // Define the events we want to track + int candidate_events[] = { + PAPI_L1_DCA, // L1 data cache accesses + PAPI_L1_DCM, // L1 data cache misses + PAPI_L2_DCA, // L2 data cache accesses + PAPI_L2_DCM, // L2 data cache misses + PAPI_L3_TCA, // L3 total cache accesses + PAPI_L3_TCM, // L3 total cache misses + PAPI_LD_INS, // Load instructions + PAPI_SR_INS // Store instructions + }; + + const char* event_names[] = {"PAPI_L1_DCA", + "PAPI_L1_DCM", + "PAPI_L2_DCA", + "PAPI_L2_DCM", + "PAPI_L3_TCA", + "PAPI_L3_TCM", + "PAPI_LD_INS", + "PAPI_SR_INS"}; + + int num_candidate_events = sizeof(candidate_events) / sizeof(candidate_events[0]); + + // Try to add each event, store -1 for unavailable ones + for (int i = 0; i < num_candidate_events; i++) { + retval = PAPI_add_event(fj_cpu.papi_event_set, candidate_events[i]); + if (retval == PAPI_OK) { + fj_cpu.papi_events.push_back(candidate_events[i]); + } else { + fj_cpu.papi_events.push_back(-1); + CUOPT_LOG_TRACE( + "%sPAPI event %s not available on this system", fj_cpu.log_prefix.c_str(), event_names[i]); + } + } + (void)event_names; // Suppress unused warning when logging is disabled + + // Start counting + retval = PAPI_start(fj_cpu.papi_event_set); + if (retval != PAPI_OK) { + CUOPT_LOG_TRACE("%sPAPI start failed", fj_cpu.log_prefix.c_str()); + return; + } + + fj_cpu.papi_initialized = true; + CUOPT_LOG_TRACE("%sPAPI initialized successfully", fj_cpu.log_prefix.c_str()); +} + +template +static void collect_and_print_papi_metrics(fj_cpu_climber_t& fj_cpu) +{ + if (!fj_cpu.papi_initialized) return; + + std::vector values(fj_cpu.papi_events.size(), 0); + int retval = PAPI_read(fj_cpu.papi_event_set, values.data()); + if (retval != PAPI_OK) { + CUOPT_LOG_TRACE("%sPAPI read failed", fj_cpu.log_prefix.c_str()); + return; + } + + // Get thread ID + pid_t tid = syscall(SYS_gettid); + + // Build map of actual values indexed by event position + std::vector all_values(8, -1); + int value_idx = 0; + for (size_t i = 0; i < fj_cpu.papi_events.size(); i++) { + if (fj_cpu.papi_events[i] != -1) { all_values[i] = values[value_idx++]; } + } + + // Compute derived metrics + double l1_miss_rate = -1.0; + if (all_values[0] > 0 && all_values[1] != -1) { + l1_miss_rate = (double)all_values[1] / all_values[0] * 100.0; + } + + double l2_miss_rate = -1.0; + if (all_values[2] > 0 && all_values[3] != -1) { + l2_miss_rate = (double)all_values[3] / all_values[2] * 100.0; + } + + // Lock to ensure thread-safe printing + std::lock_guard lock(papi_print_mutex); + + // Print everything on a single compact line + CUOPT_LOG_DEBUG( + "%sPAPI iter=%d tid=%d L1_DCA=%lld L1_DCM=%lld L2_DCA=%lld L2_DCM=%lld L3_TCA=%lld " + "L3_TCM=%lld LD_INS=%lld SR_INS=%lld L1_miss=%.2f%% L2_miss=%.2f%%", + fj_cpu.log_prefix.c_str(), + fj_cpu.iterations, + tid, + all_values[0], + all_values[1], + all_values[2], + all_values[3], + all_values[4], + all_values[5], + all_values[6], + all_values[7], + l1_miss_rate, + l2_miss_rate); + + // Reset counters for the next 1000 iterations + retval = PAPI_reset(fj_cpu.papi_event_set); + if (retval != PAPI_OK) { CUOPT_LOG_TRACE("%sPAPI reset failed", fj_cpu.log_prefix.c_str()); } +} + +template +static void cleanup_papi(fj_cpu_climber_t& fj_cpu) +{ + if (fj_cpu.papi_initialized) { + PAPI_stop(fj_cpu.papi_event_set, nullptr); + PAPI_cleanup_eventset(fj_cpu.papi_event_set); + PAPI_destroy_eventset(&fj_cpu.papi_event_set); + } +} +#endif + +template +static void precompute_problem_features(fj_cpu_climber_t& fj_cpu) +{ + // Count variable types - use host vectors + fj_cpu.n_binary_vars = 0; + fj_cpu.n_integer_vars = 0; + fj_cpu.n_continuous_vars = 0; + for (i_t i = 0; i < (i_t)fj_cpu.h_is_binary_variable.size(); i++) { + if (fj_cpu.h_is_binary_variable[i]) { + fj_cpu.n_binary_vars++; + } else if (fj_cpu.h_var_types[i] == var_t::INTEGER) { + fj_cpu.n_integer_vars++; + } else { + fj_cpu.n_continuous_vars++; + } + } + + i_t total_nnz = fj_cpu.h_reverse_offsets.back(); + i_t n_vars = fj_cpu.h_reverse_offsets.size() - 1; + i_t n_cstrs = fj_cpu.h_offsets.size() - 1; + + fj_cpu.avg_var_degree = (double)total_nnz / n_vars; + + // Compute max variable degree + fj_cpu.max_var_degree = 0; + for (i_t i = 0; i < n_vars; i++) { + i_t degree = fj_cpu.h_reverse_offsets[i + 1] - fj_cpu.h_reverse_offsets[i]; + fj_cpu.max_var_degree = std::max(fj_cpu.max_var_degree, degree); + } + + fj_cpu.avg_cstr_degree = (double)total_nnz / n_cstrs; + + // Compute max constraint degree + fj_cpu.max_cstr_degree = 0; + for (i_t i = 0; i < n_cstrs; i++) { + i_t degree = fj_cpu.h_offsets[i + 1] - fj_cpu.h_offsets[i]; + fj_cpu.max_cstr_degree = std::max(fj_cpu.max_cstr_degree, degree); + } + + fj_cpu.problem_density = (double)total_nnz / ((double)n_vars * n_cstrs); +} + +template +static void log_regression_features(fj_cpu_climber_t& fj_cpu, + double time_window_ms, + double total_time_ms) +{ +#ifdef __linux__ + pid_t tid = syscall(SYS_gettid); +#else + int tid = 0; +#endif + + i_t total_nnz = fj_cpu.h_reverse_offsets.back(); + i_t n_vars = fj_cpu.h_reverse_offsets.size() - 1; + i_t n_cstrs = fj_cpu.h_offsets.size() - 1; + + // Dynamic runtime features + i_t n_violated = fj_cpu.violated_constraints.size(); + double violated_ratio = (double)n_violated / n_cstrs; + + // Compute improvement velocity + double improvement_velocity = 0.0; + if (fj_cpu.iterations > 0) { + improvement_velocity = (fj_cpu.prev_best_objective - fj_cpu.h_best_objective) / 1000.0; + } + + // Compute per-iteration metrics + double nnz_per_move = 0.0; + i_t total_moves = + fj_cpu.n_lift_moves_window + fj_cpu.n_mtm_viol_moves_window + fj_cpu.n_mtm_sat_moves_window; + if (total_moves > 0) { nnz_per_move = (double)fj_cpu.nnz_processed_window / total_moves; } + + double eval_intensity = (double)fj_cpu.n_constraint_evals_window / n_cstrs; + + double loads_per_iter = 0.0; + double stores_per_iter = 0.0; + double l1_miss = -1.0; + double l2_miss = -1.0; + double l3_miss = -1.0; + +#ifdef __linux__ + // Get PAPI metrics if available + if (fj_cpu.papi_initialized) { + std::vector values(fj_cpu.papi_events.size(), 0); + int retval = PAPI_read(fj_cpu.papi_event_set, values.data()); + if (retval == PAPI_OK) { + std::vector all_values(8, -1); + int value_idx = 0; + for (size_t i = 0; i < fj_cpu.papi_events.size(); i++) { + if (fj_cpu.papi_events[i] != -1) { all_values[i] = values[value_idx++]; } + } + + // Compute cache miss rates + if (all_values[0] > 0 && all_values[1] != -1) { + l1_miss = (double)all_values[1] / all_values[0] * 100.0; + } + if (all_values[2] > 0 && all_values[3] != -1) { + l2_miss = (double)all_values[3] / all_values[2] * 100.0; + } + if (all_values[4] > 0 && all_values[5] != -1) { + l3_miss = (double)all_values[5] / all_values[4] * 100.0; + } + + // Loads and stores per iteration + if (all_values[6] != -1) { loads_per_iter = (double)all_values[6] / 1000.0; } + if (all_values[7] != -1) { stores_per_iter = (double)all_values[7] / 1000.0; } + } + } +#endif + + // Print everything on a single line using precomputed features + CUOPT_LOG_DEBUG( + "%sCPUFJ_FEATURES iter=%d tid=%d time_window=%.2f " + "n_vars=%d n_cstrs=%d n_bin=%d n_int=%d n_cont=%d total_nnz=%d " + "avg_var_deg=%.2f max_var_deg=%d avg_cstr_deg=%.2f max_cstr_deg=%d density=%.6f " + "n_viol=%d total_viol=%.4f curr_obj=%.4f best_obj=%.4f obj_weight=%.4f max_weight=%.4f " + "n_locmin=%d is_feas=%d iter_since_best=%d feas_found=%d " + "nnz_proc=%d n_lift=%d n_mtm_viol=%d n_mtm_sat=%d n_cstr_eval=%d n_var_updates=%d " + "L1_miss=%.2f L2_miss=%.2f L3_miss=%.2f loads_per_iter=%.0f stores_per_iter=%.0f " + "viol_ratio=%.4f improv_vel=%.6f nnz_per_move=%.2f eval_intensity=%.2f", + fj_cpu.log_prefix.c_str(), + fj_cpu.iterations, + tid, + time_window_ms, + n_vars, + n_cstrs, + fj_cpu.n_binary_vars, + fj_cpu.n_integer_vars, + fj_cpu.n_continuous_vars, + total_nnz, + fj_cpu.avg_var_degree, + fj_cpu.max_var_degree, + fj_cpu.avg_cstr_degree, + fj_cpu.max_cstr_degree, + fj_cpu.problem_density, + n_violated, + fj_cpu.total_violations, + fj_cpu.h_incumbent_objective, + fj_cpu.h_best_objective, + fj_cpu.h_objective_weight, + fj_cpu.max_weight, + fj_cpu.n_local_minima_window, + fj_cpu.feasible_found ? 1 : 0, + fj_cpu.iterations_since_best, + fj_cpu.feasible_found ? 1 : 0, + fj_cpu.nnz_processed_window, + fj_cpu.n_lift_moves_window, + fj_cpu.n_mtm_viol_moves_window, + fj_cpu.n_mtm_sat_moves_window, + fj_cpu.n_constraint_evals_window, + fj_cpu.n_variable_updates_window, + l1_miss, + l2_miss, + l3_miss, + loads_per_iter, + stores_per_iter, + violated_ratio, + improvement_velocity, + nnz_per_move, + eval_intensity); + + // Reset window counters + fj_cpu.nnz_processed_window = 0; + fj_cpu.n_lift_moves_window = 0; + fj_cpu.n_mtm_viol_moves_window = 0; + fj_cpu.n_mtm_sat_moves_window = 0; + fj_cpu.n_constraint_evals_window = 0; + fj_cpu.n_variable_updates_window = 0; + fj_cpu.n_local_minima_window = 0; + fj_cpu.prev_best_objective = fj_cpu.h_best_objective; } template @@ -149,6 +473,8 @@ static inline std::pair compute_score(fj_cpu_climber_t& fj_cpu, // Update the LHSs of all involved constraints. auto [offset_begin, offset_end] = fj_cpu.view.pb.reverse_range_for_var(var_idx); + // Track work metrics for regression model + fj_cpu.nnz_processed_window += (offset_end - offset_begin); + fj_cpu.n_variable_updates_window++; + i_t previous_viol = fj_cpu.violated_constraints.size(); for (auto i = offset_begin; i < offset_end; i++) { @@ -343,8 +673,9 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, cuopt_assert(fj_cpu.satisfied_constraints.size() == fj_cpu.view.pb.n_constraints, ""); fj_cpu.h_best_objective = fj_cpu.h_incumbent_objective - fj_cpu.settings.parameters.breakthrough_move_epsilon; - fj_cpu.h_best_assignment = fj_cpu.h_assignment; - CUOPT_LOG_TRACE("%sCPUFJ: new best objective: %g\n", + fj_cpu.h_best_assignment = fj_cpu.h_assignment; + fj_cpu.iterations_since_best = 0; // Reset counter on improvement + CUOPT_LOG_TRACE("%sCPUFJ: new best objective: %g", fj_cpu.log_prefix.c_str(), fj_cpu.pb_ptr->get_user_obj_from_solver_obj(fj_cpu.h_best_objective)); if (fj_cpu.improvement_callback) { @@ -361,12 +692,12 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, fj_cpu.h_tabu_lastinc[var_idx] = fj_cpu.iterations; fj_cpu.h_tabu_nodec_until[var_idx] = fj_cpu.iterations + tabu_tenure; fj_cpu.h_tabu_noinc_until[var_idx] = fj_cpu.iterations + tabu_tenure / 2; - // CUOPT_LOG_TRACE("CPU: tabu nodec_until: %d\n", fj_cpu.h_tabu_nodec_until[var_idx]); + // CUOPT_LOG_TRACE("CPU: tabu nodec_until: %d", fj_cpu.h_tabu_nodec_until[var_idx]); } else { fj_cpu.h_tabu_lastdec[var_idx] = fj_cpu.iterations; fj_cpu.h_tabu_noinc_until[var_idx] = fj_cpu.iterations + tabu_tenure; fj_cpu.h_tabu_nodec_until[var_idx] = fj_cpu.iterations + tabu_tenure / 2; - // CUOPT_LOG_TRACE("CPU: tabu noinc_until: %d\n", fj_cpu.h_tabu_noinc_until[var_idx]); + // CUOPT_LOG_TRACE("CPU: tabu noinc_until: %d", fj_cpu.h_tabu_noinc_until[var_idx]); } std::fill(fj_cpu.flip_move_computed.begin(), fj_cpu.flip_move_computed.end(), false); @@ -855,6 +1186,9 @@ static void init_fj_cpu(fj_cpu_climber_t& fj_cpu, fj_cpu.iter_mtm_vars.reserve(fj_cpu.view.pb.n_variables); recompute_lhs(fj_cpu); + + // Precompute static problem features for regression model + precompute_problem_features(fj_cpu); } template @@ -932,19 +1266,30 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l auto loop_start = std::chrono::high_resolution_clock::now(); auto time_limit = std::chrono::milliseconds((int)(in_time_limit * 1000)); auto loop_time_start = std::chrono::high_resolution_clock::now(); + +#ifdef __linux__ + // Initialize PAPI for performance monitoring + initialize_papi(fj_cpu); +#endif + + // Initialize feature tracking + fj_cpu.last_feature_log_time = loop_start; + fj_cpu.prev_best_objective = fj_cpu.h_best_objective; + fj_cpu.iterations_since_best = 0; + while (!fj_cpu.halted) { // Check if 5 seconds have passed auto now = std::chrono::high_resolution_clock::now(); if (in_time_limit < std::numeric_limits::infinity() && now - loop_time_start > time_limit) { - CUOPT_LOG_TRACE("%sTime limit of %.4f seconds reached, breaking loop at iteration %d\n", + CUOPT_LOG_TRACE("%sTime limit of %.4f seconds reached, breaking loop at iteration %d", fj_cpu.log_prefix.c_str(), time_limit.count() / 1000.f, fj_cpu.iterations); break; } if (fj_cpu.iterations >= fj_cpu.settings.iteration_limit) { - CUOPT_LOG_TRACE("%sIteration limit of %d reached, breaking loop at iteration %d\n", + CUOPT_LOG_TRACE("%sIteration limit of %d reached, breaking loop at iteration %d", fj_cpu.log_prefix.c_str(), fj_cpu.settings.iteration_limit, fj_cpu.iterations); @@ -963,15 +1308,24 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l fj_move_t move = fj_move_t{-1, 0}; fj_staged_score_t score = fj_staged_score_t::invalid(); + bool is_lift = false; + bool is_mtm_viol = false; + bool is_mtm_sat = false; + // Perform lift moves - if (fj_cpu.violated_constraints.empty()) { thrust::tie(move, score) = find_lift_move(fj_cpu); } + if (fj_cpu.violated_constraints.empty()) { + thrust::tie(move, score) = find_lift_move(fj_cpu); + if (score > fj_staged_score_t::zero()) is_lift = true; + } // Regular MTM if (!(score > fj_staged_score_t::zero())) { thrust::tie(move, score) = find_mtm_move_viol(fj_cpu, fj_cpu.mtm_viol_samples); + if (score > fj_staged_score_t::zero()) is_mtm_viol = true; } // try with MTM in satisfied constraints if (fj_cpu.feasible_found && !(score > fj_staged_score_t::zero())) { thrust::tie(move, score) = find_mtm_move_sat(fj_cpu, fj_cpu.mtm_sat_samples); + if (score > fj_staged_score_t::zero()) is_mtm_sat = true; } // if we're in the feasible region but haven't found improvements in the last n iterations, // perturb @@ -984,6 +1338,10 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l if (score > fj_staged_score_t::zero() && !should_perturb) { apply_move(fj_cpu, move.var_idx, move.value, false); + // Track move types + if (is_lift) fj_cpu.n_lift_moves_window++; + if (is_mtm_viol) fj_cpu.n_mtm_viol_moves_window++; + if (is_mtm_sat) fj_cpu.n_mtm_sat_moves_window++; } else { // Local Min update_weights(fj_cpu); @@ -998,6 +1356,7 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l f_t delta = move.var_idx >= 0 ? move.value : 0; apply_move(fj_cpu, var_idx, delta, true); ++local_mins; + ++fj_cpu.n_local_minima_window; } // number of violated constraints is usually small (<100). recomputing from all LHSs is cheap @@ -1010,7 +1369,7 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l CUOPT_LOG_TRACE( "%sCPUFJ iteration: %d/%d, local mins: %d, best_objective: %g, viol: %zu, obj weight %g, " "maxw " - "%g\n", + "%g", fj_cpu.log_prefix.c_str(), fj_cpu.iterations, fj_cpu.settings.iteration_limit != std::numeric_limits::max() @@ -1035,23 +1394,48 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l print_timing_stats(fj_cpu); } #endif + + // Collect and print PAPI metrics and regression features every 1000 iterations + if (fj_cpu.iterations % 1000 == 0 && fj_cpu.iterations > 0) { + auto now = std::chrono::high_resolution_clock::now(); + double time_window_ms = std::chrono::duration_cast>( + now - fj_cpu.last_feature_log_time) + .count() * + 1000.0; + double total_time_ms = + std::chrono::duration_cast>(now - loop_start).count() * + 1000.0; + +#ifdef __linux__ + collect_and_print_papi_metrics(fj_cpu); +#endif + log_regression_features(fj_cpu, time_window_ms, total_time_ms); + fj_cpu.last_feature_log_time = now; + } + cuopt_func_call(sanity_checks(fj_cpu)); fj_cpu.iterations++; + fj_cpu.iterations_since_best++; } auto loop_end = std::chrono::high_resolution_clock::now(); double total_time = std::chrono::duration_cast>(loop_end - loop_start).count(); double avg_time_per_iter = total_time / fj_cpu.iterations; - CUOPT_LOG_TRACE("%sCPUFJ Average time per iteration: %.8fms\n", + CUOPT_LOG_TRACE("%sCPUFJ Average time per iteration: %.8fms", fj_cpu.log_prefix.c_str(), avg_time_per_iter * 1000.0); #if CPUFJ_TIMING_TRACE // Print final timing statistics - CUOPT_LOG_TRACE("\n=== Final Timing Statistics ===\n"); + CUOPT_LOG_TRACE("=== Final Timing Statistics ==="); print_timing_stats(fj_cpu); #endif +#ifdef __linux__ + // Cleanup PAPI + cleanup_papi(fj_cpu); +#endif + return fj_cpu.feasible_found; } diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cuh b/cpp/src/mip/feasibility_jump/fj_cpu.cuh index 04cc514f45..1728d0e079 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cuh +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cuh @@ -109,6 +109,33 @@ struct fj_cpu_climber_t { std::string log_prefix{""}; std::atomic halted{false}; + + // PAPI performance counters + int papi_event_set{-1}; + bool papi_initialized{false}; + std::vector papi_events; + + // Feature tracking for regression model (last 1000 iterations) + i_t nnz_processed_window{0}; + i_t n_lift_moves_window{0}; + i_t n_mtm_viol_moves_window{0}; + i_t n_mtm_sat_moves_window{0}; + i_t n_constraint_evals_window{0}; + i_t n_variable_updates_window{0}; + i_t n_local_minima_window{0}; + std::chrono::high_resolution_clock::time_point last_feature_log_time; + f_t prev_best_objective{std::numeric_limits::infinity()}; + i_t iterations_since_best{0}; + + // Precomputed static problem features + i_t n_binary_vars{0}; + i_t n_integer_vars{0}; + i_t n_continuous_vars{0}; + i_t max_var_degree{0}; + i_t max_cstr_degree{0}; + double avg_var_degree{0.0}; + double avg_cstr_degree{0.0}; + double problem_density{0.0}; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index e87f346876..37628cd8a6 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -37,6 +37,7 @@ #include #include +#include namespace cuopt::linear_programming { @@ -161,6 +162,15 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, // This needs to be called before pdlp is initialized init_handler(op_problem.get_handle_ptr()); + auto retval = PAPI_library_init(PAPI_VER_CURRENT); + if (retval != PAPI_VER_CURRENT && retval > 0) { + fprintf(stderr, "PAPI library version mismatch!\n"); + exit(1); + } else if (retval < 0) { + fprintf(stderr, "PAPI initialization error!\n"); + exit(1); + } + print_version_info(); raft::common::nvtx::range fun_scope("Running solver"); From bcea60d1bbf9f59a58f68fe3ee275eb88effc49f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 10 Nov 2025 13:59:45 +0000 Subject: [PATCH 070/225] branch n bound nvtx ranges --- cpp/src/dual_simplex/basis_solves.cpp | 3 + cpp/src/dual_simplex/basis_updates.cpp | 5 + cpp/src/dual_simplex/branch_and_bound.cpp | 75 +++++--- cpp/src/dual_simplex/crossover.cpp | 3 + cpp/src/dual_simplex/initial_basis.cpp | 3 + cpp/src/dual_simplex/phase2.cpp | 220 ++++++++++++---------- cpp/src/dual_simplex/presolve.cpp | 3 + cpp/src/dual_simplex/primal.cpp | 3 + cpp/src/dual_simplex/pseudo_costs.cpp | 3 + cpp/src/dual_simplex/right_looking_lu.cpp | 3 + cpp/src/dual_simplex/solve.cpp | 29 ++- 11 files changed, 221 insertions(+), 129 deletions(-) diff --git a/cpp/src/dual_simplex/basis_solves.cpp b/cpp/src/dual_simplex/basis_solves.cpp index d0f82967d4..adaf8fdf6a 100644 --- a/cpp/src/dual_simplex/basis_solves.cpp +++ b/cpp/src/dual_simplex/basis_solves.cpp @@ -13,6 +13,8 @@ #include #include +#include + namespace cuopt::linear_programming::dual_simplex { template @@ -165,6 +167,7 @@ i_t factorize_basis(const csc_matrix_t& A, std::vector& deficient, std::vector& slacks_needed) { + raft::common::nvtx::range scope("LU::factorize_basis"); const i_t m = basic_list.size(); constexpr f_t medium_tol = 1e-12; diff --git a/cpp/src/dual_simplex/basis_updates.cpp b/cpp/src/dual_simplex/basis_updates.cpp index 3e16411f47..0fdcb9d5ce 100644 --- a/cpp/src/dual_simplex/basis_updates.cpp +++ b/cpp/src/dual_simplex/basis_updates.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include #include @@ -35,6 +37,7 @@ i_t basis_update_t::b_solve(const std::vector& rhs, std::vector& solution, std::vector& Lsol) const { + raft::common::nvtx::range scope("LU::b_solve"); const i_t m = L0_.m; assert(row_permutation_.size() == m); assert(rhs.size() == m); @@ -86,6 +89,7 @@ template i_t basis_update_t::b_transpose_solve(const std::vector& rhs, std::vector& solution) const { + raft::common::nvtx::range scope("LU::b_transpose_solve"); // Observe that // P*B = L*U // B'*P' = U'*L' @@ -2046,6 +2050,7 @@ int basis_update_mpf_t::refactor_basis( std::vector& nonbasic_list, std::vector& vstatus) { + raft::common::nvtx::range scope("LU::refactor_basis"); std::vector deficient; std::vector slacks_needed; diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index a43ee12c56..a0de45642b 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -19,6 +19,8 @@ #include #include +#include + #include #include #include @@ -376,6 +378,7 @@ bool branch_and_bound_t::repair_solution(const std::vector& edge_ template void branch_and_bound_t::repair_heuristic_solutions() { + raft::common::nvtx::range scope("BB::repair_heuristics"); // Check if there are any solutions to repair std::vector> to_repair; mutex_repair_.lock(); @@ -550,6 +553,7 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& logger_t& log, char thread_type) { + raft::common::nvtx::range scope("BB::solve_node"); f_t abs_fathom_tol = settings_.absolute_mip_gap_tol / 10; lp_solution_t leaf_solution(leaf_problem.num_rows, leaf_problem.num_cols); @@ -570,8 +574,12 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& // in B&B we only have equality constraints, leave it empty for default std::vector row_sense; - bool feasible = - bound_strengthening(row_sense, lp_settings, leaf_problem, Arow, var_types_, bounds_changed); + bool feasible; + { + raft::common::nvtx::range scope_bs("BB::bound_strengthening"); + feasible = + bound_strengthening(row_sense, lp_settings, leaf_problem, Arow, var_types_, bounds_changed); + } dual::status_t lp_status = dual::status_t::DUAL_UNBOUNDED; @@ -580,15 +588,18 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& f_t lp_start_time = tic(); std::vector leaf_edge_norms = edge_norms_; // = node.steepest_edge_norms; - lp_status = dual_phase2(2, - 0, - lp_start_time, - leaf_problem, - lp_settings, - leaf_vstatus, - leaf_solution, - node_iter, - leaf_edge_norms); + { + raft::common::nvtx::range scope_lp("BB::node_lp_solve"); + lp_status = dual_phase2(2, + 0, + lp_start_time, + leaf_problem, + lp_settings, + leaf_vstatus, + leaf_solution, + node_iter, + leaf_edge_norms); + } if (lp_status == dual::status_t::NUMERICAL) { log.printf("Numerical issue node %d. Resolving from scratch.\n", node_ptr->node_id); @@ -774,6 +785,7 @@ void branch_and_bound_t::explore_subtree(i_t task_id, lp_problem_t& leaf_problem, const csc_matrix_t& Arow) { + raft::common::nvtx::range scope("BB::explore_subtree"); std::deque*> stack; stack.push_front(start_node); @@ -888,6 +900,7 @@ void branch_and_bound_t::best_first_thread(i_t id, lp_problem_t& leaf_problem, const csc_matrix_t& Arow) { + raft::common::nvtx::range scope("BB::best_first_thread"); f_t lower_bound = -inf; f_t upper_bound = inf; f_t abs_gap = inf; @@ -943,6 +956,7 @@ template void branch_and_bound_t::diving_thread(lp_problem_t& leaf_problem, const csc_matrix_t& Arow) { + raft::common::nvtx::range scope("BB::diving_thread"); logger_t log; log.log = false; @@ -1010,6 +1024,8 @@ void branch_and_bound_t::diving_thread(lp_problem_t& leaf_pr template mip_status_t branch_and_bound_t::solve(mip_solution_t& solution) { + raft::common::nvtx::range scope("BB::solve"); + logger_t log; log.log = false; status_ = mip_exploration_status_t::UNSET; @@ -1017,6 +1033,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut stats_.nodes_explored = 0; if (guess_.size() != 0) { + raft::common::nvtx::range scope_guess("BB::check_initial_guess"); std::vector crushed_guess; crush_primal_solution(original_problem_, original_lp_, guess_, new_slacks_, crushed_guess); f_t primal_err; @@ -1036,10 +1053,14 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_relax_soln_.resize(original_lp_.num_rows, original_lp_.num_cols); settings_.log.printf("Solving LP root relaxation\n"); - simplex_solver_settings_t lp_settings = settings_; - lp_settings.inside_mip = 1; - lp_status_t root_status = solve_linear_program_advanced( - original_lp_, stats_.start_time, lp_settings, root_relax_soln_, root_vstatus_, edge_norms_); + lp_status_t root_status; + { + raft::common::nvtx::range scope_root("BB::root_relaxation"); + simplex_solver_settings_t lp_settings = settings_; + lp_settings.inside_mip = 1; + root_status = solve_linear_program_advanced( + original_lp_, stats_.start_time, lp_settings, root_relax_soln_, root_vstatus_, edge_norms_); + } stats_.total_lp_iters = root_relax_soln_.iterations; stats_.total_lp_solve_time = toc(stats_.start_time); if (root_status == lp_status_t::INFEASIBLE) { @@ -1115,16 +1136,19 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } pc_.resize(original_lp_.num_cols); - strong_branching(original_lp_, - settings_, - stats_.start_time, - var_types_, - root_relax_soln_.x, - fractional, - root_objective_, - root_vstatus_, - edge_norms_, - pc_); + { + raft::common::nvtx::range scope_sb("BB::strong_branching"); + strong_branching(original_lp_, + settings_, + stats_.start_time, + var_types_, + root_relax_soln_.x, + fractional, + root_objective_, + root_vstatus_, + edge_norms_, + pc_); + } if (toc(stats_.start_time) > settings_.time_limit) { status_ = mip_exploration_status_t::TIME_LIMIT; @@ -1167,6 +1191,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut #pragma omp parallel num_threads(settings_.num_threads) { + raft::common::nvtx::range scope_tree("BB::tree_exploration"); // Make a copy of the original LP. We will modify its bounds at each leaf lp_problem_t leaf_problem = original_lp_; diff --git a/cpp/src/dual_simplex/crossover.cpp b/cpp/src/dual_simplex/crossover.cpp index 2e7bea111a..7332b1f6ac 100644 --- a/cpp/src/dual_simplex/crossover.cpp +++ b/cpp/src/dual_simplex/crossover.cpp @@ -15,6 +15,8 @@ #include #include +#include + #include namespace cuopt::linear_programming::dual_simplex { @@ -1045,6 +1047,7 @@ crossover_status_t crossover(const lp_problem_t& lp, lp_solution_t& solution, std::vector& vstatus) { + raft::common::nvtx::range scope("Barrier::crossover"); const i_t m = lp.num_rows; const i_t n = lp.num_cols; f_t crossover_start = tic(); diff --git a/cpp/src/dual_simplex/initial_basis.cpp b/cpp/src/dual_simplex/initial_basis.cpp index 9dbe4052bf..7d2ec9288f 100644 --- a/cpp/src/dual_simplex/initial_basis.cpp +++ b/cpp/src/dual_simplex/initial_basis.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include #include @@ -24,6 +26,7 @@ i_t initial_basis_selection(const lp_problem_t& problem, std::vector& vstatus, std::vector& dependent_rows) { + raft::common::nvtx::range scope("DualSimplex::initial_basis"); i_t m = problem.num_rows; i_t n = problem.num_cols; i_t nz = problem.A.col_start[n]; diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 39ea9b4657..0e6fc57836 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -16,6 +16,8 @@ #include #include +#include + #include #include #include @@ -2171,6 +2173,7 @@ dual::status_t dual_phase2(i_t phase, i_t& iter, std::vector& delta_y_steepest_edge) { + raft::common::nvtx::range scope("DualSimplex::phase2"); const i_t m = lp.num_rows; const i_t n = lp.num_cols; std::vector basic_list(m); @@ -2208,6 +2211,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, i_t& iter, std::vector& delta_y_steepest_edge) { + raft::common::nvtx::range scope("DualSimplex::phase2_advanced"); const i_t m = lp.num_rows; const i_t n = lp.num_cols; assert(m <= n); @@ -2379,21 +2383,24 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, i_t leaving_index = -1; f_t max_val; timers.start_timer(); - if (settings.use_steepest_edge_pricing) { - leaving_index = phase2::steepest_edge_pricing_with_infeasibilities(lp, - settings, - x, - delta_y_steepest_edge, - basic_mark, - squared_infeasibilities, - infeasibility_indices, - direction, - basic_leaving_index, - max_val); - } else { - // Max infeasibility pricing - leaving_index = phase2::phase2_pricing( - lp, settings, x, basic_list, direction, basic_leaving_index, primal_infeasibility); + { + raft::common::nvtx::range scope_pricing("DualSimplex::pricing"); + if (settings.use_steepest_edge_pricing) { + leaving_index = phase2::steepest_edge_pricing_with_infeasibilities(lp, + settings, + x, + delta_y_steepest_edge, + basic_mark, + squared_infeasibilities, + infeasibility_indices, + direction, + basic_leaving_index, + max_val); + } else { + // Max infeasibility pricing + leaving_index = phase2::phase2_pricing( + lp, settings, x, basic_list, direction, basic_leaving_index, primal_infeasibility); + } } timers.pricing_time += timers.stop_timer(); if (leaving_index == -1) { @@ -2421,7 +2428,10 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, timers.start_timer(); sparse_vector_t delta_y_sparse(m, 0); sparse_vector_t UTsol_sparse(m, 0); - phase2::compute_delta_y(ft, basic_leaving_index, direction, delta_y_sparse, UTsol_sparse); + { + raft::common::nvtx::range scope_btran("DualSimplex::btran"); + phase2::compute_delta_y(ft, basic_leaving_index, direction, delta_y_sparse, UTsol_sparse); + } timers.btran_time += timers.stop_timer(); const f_t steepest_edge_norm_check = delta_y_sparse.norm2_squared(); @@ -2490,43 +2500,46 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, i_t nonbasic_entering_index = -1; const bool harris_ratio = settings.use_harris_ratio; const bool bound_flip_ratio = settings.use_bound_flip_ratio; - if (harris_ratio) { - f_t max_step_length = phase2::first_stage_harris(lp, vstatus, nonbasic_list, z, delta_z); - entering_index = phase2::second_stage_harris(lp, + { + raft::common::nvtx::range scope_ratio("DualSimplex::ratio_test"); + if (harris_ratio) { + f_t max_step_length = phase2::first_stage_harris(lp, vstatus, nonbasic_list, z, delta_z); + entering_index = phase2::second_stage_harris(lp, + vstatus, + nonbasic_list, + z, + delta_z, + max_step_length, + step_length, + nonbasic_entering_index); + } else if (bound_flip_ratio) { + timers.start_timer(); + f_t slope = direction == 1 ? (lp.lower[leaving_index] - x[leaving_index]) + : (x[leaving_index] - lp.upper[leaving_index]); + bound_flipping_ratio_test_t bfrt(settings, + start_time, + m, + n, + slope, + lp.lower, + lp.upper, + bounded_variables, vstatus, nonbasic_list, z, delta_z, - max_step_length, - step_length, - nonbasic_entering_index); - } else if (bound_flip_ratio) { - timers.start_timer(); - f_t slope = direction == 1 ? (lp.lower[leaving_index] - x[leaving_index]) - : (x[leaving_index] - lp.upper[leaving_index]); - bound_flipping_ratio_test_t bfrt(settings, - start_time, - m, - n, - slope, - lp.lower, - lp.upper, - bounded_variables, - vstatus, - nonbasic_list, - z, - delta_z, - delta_z_indices, - nonbasic_mark); - entering_index = bfrt.compute_step_length(step_length, nonbasic_entering_index); - if (entering_index == -4) { - settings.log.printf("Numerical issues encountered in ratio test.\n"); - return dual::status_t::NUMERICAL; + delta_z_indices, + nonbasic_mark); + entering_index = bfrt.compute_step_length(step_length, nonbasic_entering_index); + if (entering_index == -4) { + settings.log.printf("Numerical issues encountered in ratio test.\n"); + return dual::status_t::NUMERICAL; + } + timers.bfrt_time += timers.stop_timer(); + } else { + entering_index = phase2::phase2_ratio_test( + lp, settings, vstatus, nonbasic_list, z, delta_z, step_length, nonbasic_entering_index); } - timers.bfrt_time += timers.stop_timer(); - } else { - entering_index = phase2::phase2_ratio_test( - lp, settings, vstatus, nonbasic_list, z, delta_z, step_length, nonbasic_entering_index); } if (entering_index == -2) { return dual::status_t::TIME_LIMIT; } if (entering_index == -3) { return dual::status_t::CONCURRENT_LIMIT; } @@ -2725,21 +2738,24 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, sparse_vector_t utilde_sparse(m, 0); sparse_vector_t scaled_delta_xB_sparse(m, 0); sparse_vector_t rhs_sparse(lp.A, entering_index); - if (phase2::compute_delta_x(lp, - ft, - entering_index, - leaving_index, - basic_leaving_index, - direction, - basic_list, - delta_x_flip, - rhs_sparse, - x, - utilde_sparse, - scaled_delta_xB_sparse, - delta_x) == -1) { - settings.log.printf("Failed to compute delta_x. Iter %d\n", iter); - return dual::status_t::NUMERICAL; + { + raft::common::nvtx::range scope_ftran("DualSimplex::ftran"); + if (phase2::compute_delta_x(lp, + ft, + entering_index, + leaving_index, + basic_leaving_index, + direction, + basic_list, + delta_x_flip, + rhs_sparse, + x, + utilde_sparse, + scaled_delta_xB_sparse, + delta_x) == -1) { + settings.log.printf("Failed to compute delta_x. Iter %d\n", iter); + return dual::status_t::NUMERICAL; + } } timers.ftran_time += timers.stop_timer(); @@ -2867,55 +2883,59 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, timers.start_timer(); // Refactor or update the basis factorization - bool should_refactor = ft.num_updates() > settings.refactor_frequency; - if (!should_refactor) { - i_t recommend_refactor = ft.update(utilde_sparse, UTsol_sparse, basic_leaving_index); + { + raft::common::nvtx::range scope_update("DualSimplex::basis_update"); + bool should_refactor = ft.num_updates() > settings.refactor_frequency; + if (!should_refactor) { + i_t recommend_refactor = ft.update(utilde_sparse, UTsol_sparse, basic_leaving_index); #ifdef CHECK_UPDATE - phase2::check_update(lp, settings, ft, basic_list, basic_leaving_index); + phase2::check_update(lp, settings, ft, basic_list, basic_leaving_index); #endif - should_refactor = recommend_refactor == 1; - } + should_refactor = recommend_refactor == 1; + } #ifdef CHECK_BASIC_INFEASIBILITIES - phase2::check_basic_infeasibilities(basic_list, basic_mark, infeasibility_indices, 6); + phase2::check_basic_infeasibilities(basic_list, basic_mark, infeasibility_indices, 6); #endif - if (should_refactor) { - bool should_recompute_x = false; - if (ft.refactor_basis(lp.A, settings, basic_list, nonbasic_list, vstatus) > 0) { - should_recompute_x = true; - settings.log.printf("Failed to factorize basis. Iteration %d\n", iter); - if (toc(start_time) > settings.time_limit) { return dual::status_t::TIME_LIMIT; } - i_t count = 0; - i_t deficient_size; - while ((deficient_size = - ft.refactor_basis(lp.A, settings, basic_list, nonbasic_list, vstatus)) > 0) { - settings.log.printf("Failed to repair basis. Iteration %d. %d deficient columns.\n", - iter, - static_cast(deficient_size)); - + if (should_refactor) { + raft::common::nvtx::range scope_refactor("DualSimplex::refactorization"); + bool should_recompute_x = false; + if (ft.refactor_basis(lp.A, settings, basic_list, nonbasic_list, vstatus) > 0) { + should_recompute_x = true; + settings.log.printf("Failed to factorize basis. Iteration %d\n", iter); if (toc(start_time) > settings.time_limit) { return dual::status_t::TIME_LIMIT; } - settings.threshold_partial_pivoting_tol = 1.0; + i_t count = 0; + i_t deficient_size; + while ((deficient_size = + ft.refactor_basis(lp.A, settings, basic_list, nonbasic_list, vstatus)) > 0) { + settings.log.printf("Failed to repair basis. Iteration %d. %d deficient columns.\n", + iter, + static_cast(deficient_size)); + + if (toc(start_time) > settings.time_limit) { return dual::status_t::TIME_LIMIT; } + settings.threshold_partial_pivoting_tol = 1.0; + + count++; + if (count > 10) { return dual::status_t::NUMERICAL; } + } - count++; - if (count > 10) { return dual::status_t::NUMERICAL; } + settings.log.printf("Successfully repaired basis. Iteration %d\n", iter); } - settings.log.printf("Successfully repaired basis. Iteration %d\n", iter); - } - - phase2::reset_basis_mark(basic_list, nonbasic_list, basic_mark, nonbasic_mark); - if (should_recompute_x) { - std::vector unperturbed_x(n); - phase2::compute_primal_solution_from_basis( - lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); - x = unperturbed_x; + phase2::reset_basis_mark(basic_list, nonbasic_list, basic_mark, nonbasic_mark); + if (should_recompute_x) { + std::vector unperturbed_x(n); + phase2::compute_primal_solution_from_basis( + lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); + x = unperturbed_x; + } + phase2::compute_initial_primal_infeasibilities( + lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); } - phase2::compute_initial_primal_infeasibilities( - lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); - } #ifdef CHECK_BASIC_INFEASIBILITIES - phase2::check_basic_infeasibilities(basic_list, basic_mark, infeasibility_indices, 7); + phase2::check_basic_infeasibilities(basic_list, basic_mark, infeasibility_indices, 7); #endif + } timers.lu_update_time += timers.stop_timer(); timers.start_timer(); diff --git a/cpp/src/dual_simplex/presolve.cpp b/cpp/src/dual_simplex/presolve.cpp index b36f7a1de8..f81be32c94 100644 --- a/cpp/src/dual_simplex/presolve.cpp +++ b/cpp/src/dual_simplex/presolve.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include #include @@ -67,6 +69,7 @@ bool bound_strengthening(const std::vector& row_sense, const std::vector& var_types, const std::vector& bounds_changed) { + raft::common::nvtx::range scope("BB::bound_strengthening_full"); const i_t m = problem.num_rows; const i_t n = problem.num_cols; diff --git a/cpp/src/dual_simplex/primal.cpp b/cpp/src/dual_simplex/primal.cpp index 80406dcf0b..818d632ab6 100644 --- a/cpp/src/dual_simplex/primal.cpp +++ b/cpp/src/dual_simplex/primal.cpp @@ -14,6 +14,8 @@ #include #include +#include + namespace cuopt::linear_programming::dual_simplex { namespace { @@ -254,6 +256,7 @@ primal::status_t primal_phase2(i_t phase, lp_solution_t& sol, i_t& iter) { + raft::common::nvtx::range scope("PrimalSimplex::phase2"); const i_t m = lp.num_rows; const i_t n = lp.num_cols; assert(m <= n); diff --git a/cpp/src/dual_simplex/pseudo_costs.cpp b/cpp/src/dual_simplex/pseudo_costs.cpp index ca3e58041a..f1888a4aba 100644 --- a/cpp/src/dual_simplex/pseudo_costs.cpp +++ b/cpp/src/dual_simplex/pseudo_costs.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include namespace cuopt::linear_programming::dual_simplex { @@ -31,6 +33,7 @@ void strong_branch_helper(i_t start, const std::vector& edge_norms, pseudo_costs_t& pc) { + raft::common::nvtx::range scope("BB::strong_branch_helper"); lp_problem_t child_problem = original_lp; constexpr bool verbose = false; diff --git a/cpp/src/dual_simplex/right_looking_lu.cpp b/cpp/src/dual_simplex/right_looking_lu.cpp index 59dac3ac90..7ab3454a50 100644 --- a/cpp/src/dual_simplex/right_looking_lu.cpp +++ b/cpp/src/dual_simplex/right_looking_lu.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include #include #include @@ -577,6 +579,7 @@ i_t right_looking_lu(const csc_matrix_t& A, csc_matrix_t& U, std::vector& pinv) { + raft::common::nvtx::range scope("LU::right_looking_lu"); const i_t n = column_list.size(); const i_t m = A.m; diff --git a/cpp/src/dual_simplex/solve.cpp b/cpp/src/dual_simplex/solve.cpp index 5c5f9e1656..3431ca345c 100644 --- a/cpp/src/dual_simplex/solve.cpp +++ b/cpp/src/dual_simplex/solve.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include #include #include @@ -107,6 +109,7 @@ lp_status_t solve_linear_program_advanced(const lp_problem_t& original std::vector& vstatus, std::vector& edge_norms) { + raft::common::nvtx::range scope("DualSimplex::solve_lp"); const i_t m = original_lp.num_rows; const i_t n = original_lp.num_cols; assert(m <= n); @@ -139,7 +142,11 @@ lp_status_t solve_linear_program_with_advanced_basis( lp_status_t lp_status = lp_status_t::UNSET; lp_problem_t presolved_lp(original_lp.handle_ptr, 1, 1, 1); presolve_info_t presolve_info; - const i_t ok = presolve(original_lp, settings, presolved_lp, presolve_info); + i_t ok; + { + raft::common::nvtx::range scope_presolve("DualSimplex::presolve"); + ok = presolve(original_lp, settings, presolved_lp, presolve_info); + } if (ok == -1) { return lp_status_t::INFEASIBLE; } constexpr bool write_out_matlab = false; @@ -154,7 +161,10 @@ lp_status_t solve_linear_program_with_advanced_basis( presolved_lp.num_cols, presolved_lp.A.col_start[presolved_lp.num_cols]); std::vector column_scales; - column_scaling(presolved_lp, settings, lp, column_scales); + { + raft::common::nvtx::range scope_scaling("DualSimplex::scaling"); + column_scaling(presolved_lp, settings, lp, column_scales); + } assert(presolved_lp.num_cols == lp.num_cols); lp_problem_t phase1_problem(original_lp.handle_ptr, 1, 1, 1); std::vector phase1_vstatus; @@ -180,8 +190,19 @@ lp_status_t solve_linear_program_with_advanced_basis( i_t iter = 0; lp_solution_t phase1_solution(phase1_problem.num_rows, phase1_problem.num_cols); edge_norms.clear(); - dual::status_t phase1_status = dual_phase2( - 1, 1, start_time, phase1_problem, settings, phase1_vstatus, phase1_solution, iter, edge_norms); + dual::status_t phase1_status; + { + raft::common::nvtx::range scope_phase1("DualSimplex::phase1"); + phase1_status = dual_phase2(1, + 1, + start_time, + phase1_problem, + settings, + phase1_vstatus, + phase1_solution, + iter, + edge_norms); + } if (phase1_status == dual::status_t::NUMERICAL || phase1_status == dual::status_t::DUAL_UNBOUNDED) { settings.log.printf("Failed in Phase 1\n"); From d7ff462aa999f778a41e6445be0af27b93411b5e Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 10 Nov 2025 16:03:48 +0000 Subject: [PATCH 071/225] more cpufj logging --- cpp/src/mip/diversity/diversity_manager.cu | 4 +- cpp/src/mip/feasibility_jump/fj_cpu.cu | 139 ++- cpp/src/mip/feasibility_jump/fj_cpu.cuh | 10 +- scripts/determinism_logs_parse.py | 538 ++++++--- scripts/train_regressor.py | 1174 ++++++++++++-------- 5 files changed, 1210 insertions(+), 655 deletions(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 392145ee53..39e79a1bb6 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -355,10 +355,12 @@ solution_t diversity_manager_t::run_solver() population.allocate_solutions(); ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop if (!context.settings.deterministic) { +#if 1 ls.start_cpufj_scratch_threads(population); - std::this_thread::sleep_for(std::chrono::seconds(10)); + std::this_thread::sleep_for(std::chrono::seconds(30)); ls.stop_cpufj_scratch_threads(); exit(0); +#endif } // before probing cache or LP, run FJ to generate initial primal feasible solution diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index 58850ab2cf..262bd2f35f 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -256,16 +256,13 @@ template static void precompute_problem_features(fj_cpu_climber_t& fj_cpu) { // Count variable types - use host vectors - fj_cpu.n_binary_vars = 0; - fj_cpu.n_integer_vars = 0; - fj_cpu.n_continuous_vars = 0; + fj_cpu.n_binary_vars = 0; + fj_cpu.n_integer_vars = 0; for (i_t i = 0; i < (i_t)fj_cpu.h_is_binary_variable.size(); i++) { if (fj_cpu.h_is_binary_variable[i]) { fj_cpu.n_binary_vars++; } else if (fj_cpu.h_var_types[i] == var_t::INTEGER) { fj_cpu.n_integer_vars++; - } else { - fj_cpu.n_continuous_vars++; } } @@ -275,22 +272,47 @@ static void precompute_problem_features(fj_cpu_climber_t& fj_cpu) fj_cpu.avg_var_degree = (double)total_nnz / n_vars; - // Compute max variable degree + // Compute variable degree statistics (max, cv) fj_cpu.max_var_degree = 0; + std::vector var_degrees(n_vars); for (i_t i = 0; i < n_vars; i++) { i_t degree = fj_cpu.h_reverse_offsets[i + 1] - fj_cpu.h_reverse_offsets[i]; + var_degrees[i] = degree; fj_cpu.max_var_degree = std::max(fj_cpu.max_var_degree, degree); } + // Compute variable degree coefficient of variation + double var_deg_variance = 0.0; + for (i_t i = 0; i < n_vars; i++) { + double diff = var_degrees[i] - fj_cpu.avg_var_degree; + var_deg_variance += diff * diff; + } + var_deg_variance /= n_vars; + double var_degree_std = std::sqrt(var_deg_variance); + fj_cpu.var_degree_cv = fj_cpu.avg_var_degree > 0 ? var_degree_std / fj_cpu.avg_var_degree : 0.0; + fj_cpu.avg_cstr_degree = (double)total_nnz / n_cstrs; - // Compute max constraint degree + // Compute constraint degree statistics (max, cv) fj_cpu.max_cstr_degree = 0; + std::vector cstr_degrees(n_cstrs); for (i_t i = 0; i < n_cstrs; i++) { i_t degree = fj_cpu.h_offsets[i + 1] - fj_cpu.h_offsets[i]; + cstr_degrees[i] = degree; fj_cpu.max_cstr_degree = std::max(fj_cpu.max_cstr_degree, degree); } + // Compute constraint degree coefficient of variation + double cstr_deg_variance = 0.0; + for (i_t i = 0; i < n_cstrs; i++) { + double diff = cstr_degrees[i] - fj_cpu.avg_cstr_degree; + cstr_deg_variance += diff * diff; + } + cstr_deg_variance /= n_cstrs; + double cstr_degree_std = std::sqrt(cstr_deg_variance); + fj_cpu.cstr_degree_cv = + fj_cpu.avg_cstr_degree > 0 ? cstr_degree_std / fj_cpu.avg_cstr_degree : 0.0; + fj_cpu.problem_density = (double)total_nnz / ((double)n_vars * n_cstrs); } @@ -299,25 +321,12 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, double time_window_ms, double total_time_ms) { -#ifdef __linux__ - pid_t tid = syscall(SYS_gettid); -#else - int tid = 0; -#endif - i_t total_nnz = fj_cpu.h_reverse_offsets.back(); i_t n_vars = fj_cpu.h_reverse_offsets.size() - 1; i_t n_cstrs = fj_cpu.h_offsets.size() - 1; // Dynamic runtime features - i_t n_violated = fj_cpu.violated_constraints.size(); - double violated_ratio = (double)n_violated / n_cstrs; - - // Compute improvement velocity - double improvement_velocity = 0.0; - if (fj_cpu.iterations > 0) { - improvement_velocity = (fj_cpu.prev_best_objective - fj_cpu.h_best_objective) / 1000.0; - } + double violated_ratio = (double)fj_cpu.violated_constraints.size() / n_cstrs; // Compute per-iteration metrics double nnz_per_move = 0.0; @@ -325,12 +334,37 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, fj_cpu.n_lift_moves_window + fj_cpu.n_mtm_viol_moves_window + fj_cpu.n_mtm_sat_moves_window; if (total_moves > 0) { nnz_per_move = (double)fj_cpu.nnz_processed_window / total_moves; } - double eval_intensity = (double)fj_cpu.n_constraint_evals_window / n_cstrs; + double eval_intensity = (double)fj_cpu.nnz_processed_window / 1000.0; + + // Cache and locality metrics + i_t cache_hits_window = fj_cpu.hit_count - fj_cpu.hit_count_window_start; + i_t cache_misses_window = fj_cpu.miss_count - fj_cpu.miss_count_window_start; + i_t total_cache_accesses = cache_hits_window + cache_misses_window; + double cache_hit_rate = + total_cache_accesses > 0 ? (double)cache_hits_window / total_cache_accesses : 0.0; + + i_t unique_cstrs = fj_cpu.unique_cstrs_accessed_window.size(); + i_t unique_vars = fj_cpu.unique_vars_accessed_window.size(); + + // Reuse ratios: how many times each constraint/variable was accessed on average + double cstr_reuse_ratio = + unique_cstrs > 0 ? (double)fj_cpu.nnz_processed_window / unique_cstrs : 0.0; + double var_reuse_ratio = + unique_vars > 0 ? (double)fj_cpu.n_variable_updates_window / unique_vars : 0.0; + + // Working set size estimation (KB) + // Each constraint: lhs (f_t) + 2 bounds (f_t) + sumcomp (f_t) = 4 * sizeof(f_t) + // Each variable: assignment (f_t) = 1 * sizeof(f_t) + i_t working_set_bytes = unique_cstrs * 4 * sizeof(f_t) + unique_vars * sizeof(f_t); + double working_set_kb = working_set_bytes / 1024.0; + + // Coverage: what fraction of problem is actively touched + double cstr_coverage = (double)unique_cstrs / n_cstrs; + double var_coverage = (double)unique_vars / n_vars; double loads_per_iter = 0.0; double stores_per_iter = 0.0; double l1_miss = -1.0; - double l2_miss = -1.0; double l3_miss = -1.0; #ifdef __linux__ @@ -349,9 +383,6 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, if (all_values[0] > 0 && all_values[1] != -1) { l1_miss = (double)all_values[1] / all_values[0] * 100.0; } - if (all_values[2] > 0 && all_values[3] != -1) { - l2_miss = (double)all_values[3] / all_values[2] * 100.0; - } if (all_values[4] > 0 && all_values[5] != -1) { l3_miss = (double)all_values[5] / all_values[4] * 100.0; } @@ -365,52 +396,58 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, // Print everything on a single line using precomputed features CUOPT_LOG_DEBUG( - "%sCPUFJ_FEATURES iter=%d tid=%d time_window=%.2f " - "n_vars=%d n_cstrs=%d n_bin=%d n_int=%d n_cont=%d total_nnz=%d " - "avg_var_deg=%.2f max_var_deg=%d avg_cstr_deg=%.2f max_cstr_deg=%d density=%.6f " - "n_viol=%d total_viol=%.4f curr_obj=%.4f best_obj=%.4f obj_weight=%.4f max_weight=%.4f " - "n_locmin=%d is_feas=%d iter_since_best=%d feas_found=%d " - "nnz_proc=%d n_lift=%d n_mtm_viol=%d n_mtm_sat=%d n_cstr_eval=%d n_var_updates=%d " - "L1_miss=%.2f L2_miss=%.2f L3_miss=%.2f loads_per_iter=%.0f stores_per_iter=%.0f " - "viol_ratio=%.4f improv_vel=%.6f nnz_per_move=%.2f eval_intensity=%.2f", + "%sCPUFJ_FEATURES iter=%d time_window=%.2f " + "n_vars=%d n_cstrs=%d n_bin=%d n_int=%d total_nnz=%d " + "avg_var_deg=%.2f max_var_deg=%d var_deg_cv=%.4f " + "avg_cstr_deg=%.2f max_cstr_deg=%d cstr_deg_cv=%.4f " + "density=%.6f " + "total_viol=%.4f obj_weight=%.4f max_weight=%.4f " + "n_locmin=%d iter_since_best=%d feas_found=%d " + "nnz_proc=%d n_lift=%d n_mtm_viol=%d n_mtm_sat=%d n_var_updates=%d " + "cache_hit_rate=%.4f unique_cstrs=%d unique_vars=%d " + "cstr_reuse=%.2f var_reuse=%.2f working_set_kb=%.1f " + "cstr_coverage=%.4f var_coverage=%.4f " + "L1_miss=%.2f L3_miss=%.2f loads_per_iter=%.0f stores_per_iter=%.0f " + "viol_ratio=%.4f nnz_per_move=%.2f eval_intensity=%.2f", fj_cpu.log_prefix.c_str(), fj_cpu.iterations, - tid, time_window_ms, n_vars, n_cstrs, fj_cpu.n_binary_vars, fj_cpu.n_integer_vars, - fj_cpu.n_continuous_vars, total_nnz, fj_cpu.avg_var_degree, fj_cpu.max_var_degree, + fj_cpu.var_degree_cv, fj_cpu.avg_cstr_degree, fj_cpu.max_cstr_degree, + fj_cpu.cstr_degree_cv, fj_cpu.problem_density, - n_violated, fj_cpu.total_violations, - fj_cpu.h_incumbent_objective, - fj_cpu.h_best_objective, fj_cpu.h_objective_weight, fj_cpu.max_weight, fj_cpu.n_local_minima_window, - fj_cpu.feasible_found ? 1 : 0, fj_cpu.iterations_since_best, fj_cpu.feasible_found ? 1 : 0, fj_cpu.nnz_processed_window, fj_cpu.n_lift_moves_window, fj_cpu.n_mtm_viol_moves_window, fj_cpu.n_mtm_sat_moves_window, - fj_cpu.n_constraint_evals_window, fj_cpu.n_variable_updates_window, + cache_hit_rate, + unique_cstrs, + unique_vars, + cstr_reuse_ratio, + var_reuse_ratio, + working_set_kb, + cstr_coverage, + var_coverage, l1_miss, - l2_miss, l3_miss, loads_per_iter, stores_per_iter, violated_ratio, - improvement_velocity, nnz_per_move, eval_intensity); @@ -419,10 +456,15 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, fj_cpu.n_lift_moves_window = 0; fj_cpu.n_mtm_viol_moves_window = 0; fj_cpu.n_mtm_sat_moves_window = 0; - fj_cpu.n_constraint_evals_window = 0; fj_cpu.n_variable_updates_window = 0; fj_cpu.n_local_minima_window = 0; fj_cpu.prev_best_objective = fj_cpu.h_best_objective; + + // Reset cache and locality tracking + fj_cpu.hit_count_window_start = fj_cpu.hit_count; + fj_cpu.miss_count_window_start = fj_cpu.miss_count; + fj_cpu.unique_cstrs_accessed_window.clear(); + fj_cpu.unique_vars_accessed_window.clear(); } template @@ -473,10 +515,11 @@ static inline std::pair compute_score(fj_cpu_climber_t& fj_cpu, // Track work metrics for regression model fj_cpu.nnz_processed_window += (offset_end - offset_begin); fj_cpu.n_variable_updates_window++; + fj_cpu.unique_vars_accessed_window.insert(var_idx); i_t previous_viol = fj_cpu.violated_constraints.size(); @@ -608,7 +652,8 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, cuopt_assert(i < (i_t)fj_cpu.h_reverse_constraints.size(), ""); auto [c_lb, c_ub] = fj_cpu.cached_cstr_bounds[i]; - auto cstr_idx = fj_cpu.h_reverse_constraints[i]; + auto cstr_idx = fj_cpu.h_reverse_constraints[i]; + fj_cpu.unique_cstrs_accessed_window.insert(cstr_idx); auto cstr_coeff = fj_cpu.h_reverse_coefficients[i]; f_t old_lhs = fj_cpu.h_lhs[cstr_idx]; diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cuh b/cpp/src/mip/feasibility_jump/fj_cpu.cuh index 1728d0e079..c1f5316f8a 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cuh +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cuh @@ -120,21 +120,27 @@ struct fj_cpu_climber_t { i_t n_lift_moves_window{0}; i_t n_mtm_viol_moves_window{0}; i_t n_mtm_sat_moves_window{0}; - i_t n_constraint_evals_window{0}; i_t n_variable_updates_window{0}; i_t n_local_minima_window{0}; std::chrono::high_resolution_clock::time_point last_feature_log_time; f_t prev_best_objective{std::numeric_limits::infinity()}; i_t iterations_since_best{0}; + // Cache and locality tracking + i_t hit_count_window_start{0}; + i_t miss_count_window_start{0}; + std::unordered_set unique_cstrs_accessed_window; + std::unordered_set unique_vars_accessed_window; + // Precomputed static problem features i_t n_binary_vars{0}; i_t n_integer_vars{0}; - i_t n_continuous_vars{0}; i_t max_var_degree{0}; i_t max_cstr_degree{0}; double avg_var_degree{0.0}; double avg_cstr_degree{0.0}; + double var_degree_cv{0.0}; + double cstr_degree_cv{0.0}; double problem_density{0.0}; }; diff --git a/scripts/determinism_logs_parse.py b/scripts/determinism_logs_parse.py index 0e0b9b5b1b..09ea08627d 100755 --- a/scripts/determinism_logs_parse.py +++ b/scripts/determinism_logs_parse.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +# SPDX-License-Identifier: Apache-2.0 # All rights reserved. # SPDX-License-Identifier: Apache-2.0 # @@ -23,6 +24,7 @@ - PDLP (LP Solver): PDLP_FEATURES and PDLP_RESULT logs - CP (Constraint Propagation): CP_FEATURES and CP_RESULT logs - FJ (Feasibility Jump): Legacy FJ: format +- CPUFJ (CPU Feasibility Jump): CPUFJ_FEATURES single-line logs IMPORTANT - Grep Specificity: The parser uses EXACT pattern matching with grep to filter logs efficiently. @@ -46,19 +48,19 @@ python determinism_logs_parse.py --algorithm PDLP [-o output.feather] python determinism_logs_parse.py --algorithm CP [-o output.feather] python determinism_logs_parse.py --algorithm FJ [-o output.feather] + python determinism_logs_parse.py --algorithm CPUFJ [-o output.feather] """ import argparse import subprocess import os import glob -import re import numpy as np import pandas as pd -from typing import List, Dict, Any, Optional, Tuple +from typing import List, Dict, Any, Optional -SUPPORTED_ALGORITHMS = ['FP', 'PDLP', 'CP', 'FJ'] +SUPPORTED_ALGORITHMS = ["FP", "PDLP", "CP", "FJ", "CPUFJ"] def parse_value(value_str: str) -> Any: @@ -66,11 +68,11 @@ def parse_value(value_str: str) -> Any: try: # Handle special float values first (nan, inf, -inf) value_lower = value_str.lower() - if value_lower in ('nan', 'inf', '-inf', '+inf'): + if value_lower in ("nan", "inf", "-inf", "+inf"): return float(value_str) # Try to parse as float if it contains a decimal point or scientific notation - if '.' in value_str or 'e' in value_str.lower(): + if "." in value_str or "e" in value_str.lower(): return float(value_str) else: return int(value_str) @@ -97,18 +99,18 @@ def parse_key_value_line(line: str, prefix: str) -> Dict[str, Any]: # Parse key=value pairs # Handle both space-separated and comma-separated for kv_pair in line.split(): - if '=' in kv_pair: - key, value = kv_pair.split('=', 1) + if "=" in kv_pair: + key, value = kv_pair.split("=", 1) # Remove trailing commas - value = value.rstrip(',') + value = value.rstrip(",") entry[key] = parse_value(value) return entry -def parse_generic_algorithm_logs(log_files: List[str], - algorithm: str, - algorithm_name: str) -> List[Dict[str, Any]]: +def parse_generic_algorithm_logs( + log_files: List[str], algorithm: str, algorithm_name: str +) -> List[Dict[str, Any]]: """ Generic parser for algorithm feature and result logs. @@ -130,15 +132,22 @@ def parse_generic_algorithm_logs(log_files: List[str], # Construct grep patterns with EXACT match requirements # The colon at the end ensures we ONLY match the feature/result log lines # and ignore all other lines containing the algorithm name - features_pattern = f'{algorithm}_FEATURES:' - result_pattern = f'{algorithm}_RESULT:' + features_pattern = f"{algorithm}_FEATURES:" + result_pattern = f"{algorithm}_RESULT:" # Use grep with: # -H: Always print filename (even with single file) # -n: Print line numbers for correct pairing # -e: Multiple patterns to match # This ensures we ONLY get the specific predictor log lines, not debug/info lines - cmd = ['grep', '-Hn', '-e', features_pattern, '-e', result_pattern] + log_files + cmd = [ + "grep", + "-Hn", + "-e", + features_pattern, + "-e", + result_pattern, + ] + log_files result = subprocess.run(cmd, capture_output=True, text=True) @@ -147,7 +156,7 @@ def parse_generic_algorithm_logs(log_files: List[str], return [] # Count lines for progress indication - total_lines = result.stdout.count('\n') + total_lines = result.stdout.count("\n") print(f" Processing {total_lines} matching lines...") # Process grep output efficiently @@ -156,7 +165,7 @@ def parse_generic_algorithm_logs(log_files: List[str], lines_processed = 0 files_seen = set() - for line in result.stdout.split('\n'): + for line in result.stdout.split("\n"): if not line: continue @@ -164,12 +173,17 @@ def parse_generic_algorithm_logs(log_files: List[str], # Progress update every 10000 lines if lines_processed % 10000 == 0: - pct = (lines_processed / total_lines * 100) if total_lines > 0 else 0 - print(f" Progress: {pct:.1f}% ({lines_processed}/{total_lines} lines, {len(files_seen)} files)", end='\r') + pct = ( + (lines_processed / total_lines * 100) if total_lines > 0 else 0 + ) + print( + f" Progress: {pct:.1f}% ({lines_processed}/{total_lines} lines, {len(files_seen)} files)", + end="\r", + ) # Split on first two colons to get filename, linenum, and content # The rest of the line after the second colon is the log content - parts = line.split(':', 2) + parts = line.split(":", 2) if len(parts) < 3: continue @@ -178,7 +192,7 @@ def parse_generic_algorithm_logs(log_files: List[str], content = parts[2] # This includes everything after linenum if filename not in entries_by_file: - entries_by_file[filename] = {'features': [], 'results': []} + entries_by_file[filename] = {"features": [], "results": []} files_seen.add(filename) # Double-check pattern match (grep already filtered, but be extra safe) @@ -187,21 +201,25 @@ def parse_generic_algorithm_logs(log_files: List[str], # Parse features - only if pattern is present features = parse_key_value_line(content, features_pattern) if features: # Only add if parsing succeeded - entries_by_file[filename]['features'].append((linenum, features)) + entries_by_file[filename]["features"].append( + (linenum, features) + ) elif result_pattern in content: # Parse results - only if pattern is present results = parse_key_value_line(content, result_pattern) if results: # Only add if parsing succeeded - entries_by_file[filename]['results'].append((linenum, results)) + entries_by_file[filename]["results"].append((linenum, results)) # Clear progress line if lines_processed > 0: - print(f" Processed {lines_processed} lines from {len(files_seen)} files ") + print( + f" Processed {lines_processed} lines from {len(files_seen)} files " + ) # Match features with results # IMPORTANT: Multiple FEATURES lines followed by multiple RESULT lines form ONE complete entry # We need to merge consecutive lines of the same type, then combine them - print(f" Merging features and results...") + print(" Merging features and results...") entries = [] files_processed = 0 total_files = len(entries_by_file) @@ -211,15 +229,18 @@ def parse_generic_algorithm_logs(log_files: List[str], # Progress update every 10 files if files_processed % 10 == 0 or files_processed == total_files: - print(f" Merging: {files_processed}/{total_files} files, {len(entries)} entries found", end='\r') + print( + f" Merging: {files_processed}/{total_files} files, {len(entries)} entries found", + end="\r", + ) # Combine features and results by line number # Group consecutive FEATURES lines and consecutive RESULT lines all_items = [] - for linenum, features in data['features']: - all_items.append((linenum, 'features', features)) - for linenum, results in data['results']: - all_items.append((linenum, 'results', results)) + for linenum, features in data["features"]: + all_items.append((linenum, "features", features)) + for linenum, results in data["results"]: + all_items.append((linenum, "results", results)) # Sort by line number all_items.sort(key=lambda x: x[0]) @@ -231,16 +252,16 @@ def parse_generic_algorithm_logs(log_files: List[str], for linenum, item_type, content in all_items: # If we transition from RESULT back to FEATURES, save the previous entry - if item_type == 'features' and last_type == 'results': + if item_type == "features" and last_type == "results": if current_features and current_results: # Create combined entry - entry = {'file': filename} + entry = {"file": filename} entry.update(current_features) entry.update(current_results) # Rename 'iterations' to 'iter' for consistency - if 'iterations' in entry: - entry['iter'] = entry.pop('iterations') + if "iterations" in entry: + entry["iter"] = entry.pop("iterations") entries.append(entry) @@ -248,7 +269,7 @@ def parse_generic_algorithm_logs(log_files: List[str], current_features = {} current_results = {} - if item_type == 'features': + if item_type == "features": # Accumulate features current_features.update(content) else: # results @@ -259,18 +280,20 @@ def parse_generic_algorithm_logs(log_files: List[str], # Don't forget the last entry in the file if current_features and current_results: - entry = {'file': filename} + entry = {"file": filename} entry.update(current_features) entry.update(current_results) - if 'iterations' in entry: - entry['iter'] = entry.pop('iterations') + if "iterations" in entry: + entry["iter"] = entry.pop("iterations") entries.append(entry) # Clear progress line and show final count if total_files > 0: - print(f" Found {len(entries)} complete entries from {total_files} files ") + print( + f" Found {len(entries)} complete entries from {total_files} files " + ) return entries @@ -278,50 +301,65 @@ def parse_generic_algorithm_logs(log_files: List[str], # Algorithm-specific wrappers for the generic parser # These provide a clean API and eliminate code duplication + def parse_fp_logs(log_files: List[str]) -> List[Dict[str, Any]]: """Parse Feasibility Pump feature and result logs.""" - return parse_generic_algorithm_logs(log_files, 'FP', 'Feasibility Pump') + return parse_generic_algorithm_logs(log_files, "FP", "Feasibility Pump") def parse_pdlp_logs(log_files: List[str]) -> List[Dict[str, Any]]: """Parse PDLP (LP Solver) feature and result logs.""" - return parse_generic_algorithm_logs(log_files, 'PDLP', 'LP Solver') + return parse_generic_algorithm_logs(log_files, "PDLP", "LP Solver") def parse_cp_logs(log_files: List[str]) -> List[Dict[str, Any]]: """Parse Constraint Propagation feature and result logs.""" - return parse_generic_algorithm_logs(log_files, 'CP', 'Constraint Propagation') + return parse_generic_algorithm_logs( + log_files, "CP", "Constraint Propagation" + ) -def parse_fj_logs(log_files: List[str]) -> List[Dict[str, Any]]: +def parse_single_line_logs( + log_files: List[str], + pattern: str, + algorithm_name: str, + prefix_to_remove: Optional[str] = None, +) -> List[Dict[str, Any]]: """ - Parse legacy Feasibility Jump logs (original format). + Generic parser for single-line logs with key=value pairs. - Parses lines containing "FJ:" with key=value pairs. - Uses grep efficiently to minimize Python-side processing. + Used for legacy formats that don't have separate FEATURES/RESULT lines. + + Args: + log_files: List of log file paths to parse + pattern: Grep pattern to match (e.g., 'FJ:', 'CPUFJ_FEATURES') + algorithm_name: Full name for display + prefix_to_remove: Optional prefix to strip from content (e.g., 'FJ:') + + Returns + ------- + List of dictionaries with parsed key-value pairs """ - print("\nParsing FJ (Feasibility Jump) legacy logs...") + print(f"\nParsing {algorithm_name} logs...") print(f" Running grep on {len(log_files)} files...") - # Use grep to efficiently extract ONLY lines with the exact "FJ:" pattern - # Note: FJ uses legacy format with just "FJ:" prefix (not FJ_FEATURES/FJ_RESULT) - # The colon ensures we don't match other FJ-related debug lines - cmd = ['grep', '-H', 'FJ:'] + log_files + # Use grep to efficiently extract ONLY lines with the exact pattern + cmd = ["grep", "-H", pattern] + log_files result = subprocess.run(cmd, capture_output=True, text=True) if not result.stdout: - print(f" No FJ logs found") + print(f" No {algorithm_name} logs found") return [] # Count lines for progress indication - total_lines = result.stdout.count('\n') + total_lines = result.stdout.count("\n") print(f" Processing {total_lines} matching lines...") # Parse grep output efficiently entries = [] lines_processed = 0 - for line in result.stdout.split('\n'): + for line in result.stdout.split("\n"): if not line: continue @@ -329,26 +367,33 @@ def parse_fj_logs(log_files: List[str]) -> List[Dict[str, Any]]: # Progress update every 10000 lines if lines_processed % 10000 == 0: - pct = (lines_processed / total_lines * 100) if total_lines > 0 else 0 - print(f" Progress: {pct:.1f}% ({lines_processed}/{total_lines} lines, {len(entries)} entries)", end='\r') - - # Grep output format: filename:FJ: key1=value1 key2=value2 ... - parts = line.split(':', 2) + pct = ( + (lines_processed / total_lines * 100) if total_lines > 0 else 0 + ) + print( + f" Progress: {pct:.1f}% ({lines_processed}/{total_lines} lines, {len(entries)} entries)", + end="\r", + ) + + # Grep output format: filename:content + parts = line.split(":", 2) if len(parts) < 3: continue filename = os.path.basename(parts[0]) content = parts[2] - # Remove "FJ:" prefix if present - if content.startswith('FJ:'): - content = content[3:].strip() + # Remove prefix if specified + if prefix_to_remove and content.startswith(prefix_to_remove): + content = content[len(prefix_to_remove) :].strip() # Parse key-value pairs - entry = {'file': filename} + entry = {"file": filename} for kv_pair in content.split(): - if '=' in kv_pair: - key, value = kv_pair.split('=', 1) + if "=" in kv_pair: + key, value = kv_pair.split("=", 1) + # Remove trailing commas + value = value.rstrip(",") entry[key] = parse_value(value) # Only add entry if it has more than just the filename @@ -357,25 +402,43 @@ def parse_fj_logs(log_files: List[str]) -> List[Dict[str, Any]]: # Clear progress line if lines_processed > 0: - print(f" Found {len(entries)} entries from {total_lines} lines ") + print( + f" Found {len(entries)} entries from {total_lines} lines " + ) return entries +def parse_fj_logs(log_files: List[str]) -> List[Dict[str, Any]]: + """Parse legacy Feasibility Jump logs (original format).""" + return parse_single_line_logs( + log_files, "FJ:", "FJ (Feasibility Jump)", "FJ:" + ) + + +def parse_cpufj_logs(log_files: List[str]) -> List[Dict[str, Any]]: + """Parse CPU Feasibility Jump feature logs.""" + return parse_single_line_logs( + log_files, "CPUFJ_FEATURES", "CPUFJ (CPU Feasibility Jump)" + ) + + def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: """Print statistics about parsed entries.""" if not entries: print(f"\n No entries found for {algorithm}") return - unique_files = set(entry['file'] for entry in entries) - avg_entries_per_file = len(entries) / len(unique_files) if unique_files else 0 + unique_files = set(entry["file"] for entry in entries) + avg_entries_per_file = ( + len(entries) / len(unique_files) if unique_files else 0 + ) # Check if 'iter' field exists - has_iter = all('iter' in entry for entry in entries) + has_iter = all("iter" in entry for entry in entries) if has_iter: - iter_values = [entry['iter'] for entry in entries] + iter_values = [entry["iter"] for entry in entries] min_iter = min(iter_values) max_iter = max(iter_values) avg_iter = sum(iter_values) / len(iter_values) @@ -383,7 +446,9 @@ def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: print(f"\n Total entries: {len(entries)}") print(f" Unique files: {len(unique_files)}") print(f" Avg entries per file: {avg_entries_per_file:.2f}") - print(f" Iterations (target): min={min_iter}, max={max_iter}, avg={avg_iter:.2f}") + print( + f" Iterations (target): min={min_iter}, max={max_iter}, avg={avg_iter:.2f}" + ) else: print(f"\n Total entries: {len(entries)}") print(f" Unique files: {len(unique_files)}") @@ -391,7 +456,7 @@ def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: # Show sample entry if entries: - print(f"\n Sample entry (first):") + print("\n Sample entry (first):") sample = entries[0] for key, value in sorted(sample.items()): print(f" {key}: {value}") @@ -399,51 +464,55 @@ def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: def main(): parser = argparse.ArgumentParser( - description='Parse algorithm feature logs and export to Feather format for training', + description="Parse algorithm feature logs and export to Feather format for training", formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=f""" + epilog=""" Supported Algorithms: - FP - Feasibility Pump (parses FP_FEATURES and FP_RESULT logs) - PDLP - LP Solver (parses PDLP_FEATURES and PDLP_RESULT logs) - CP - Constraint Propagation (parses CP_FEATURES and CP_RESULT logs) - FJ - Feasibility Jump (parses legacy FJ: format) + FP - Feasibility Pump (parses FP_FEATURES and FP_RESULT logs) + PDLP - LP Solver (parses PDLP_FEATURES and PDLP_RESULT logs) + CP - Constraint Propagation (parses CP_FEATURES and CP_RESULT logs) + FJ - Feasibility Jump (parses legacy FJ: format) + CPUFJ - CPU Feasibility Jump (parses CPUFJ_FEATURES single-line logs) Examples: python determinism_logs_parse.py logs/ --algorithm FP -o fp_data.feather python determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.feather python determinism_logs_parse.py logs/ --algorithm CP -o cp_data.feather python determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.feather + python determinism_logs_parse.py logs/ --algorithm CPUFJ -o cpufj_data.feather # Limit to first 10 files for testing python determinism_logs_parse.py logs/ --algorithm FP --max-files 10 - """ + """, ) parser.add_argument( - 'input_dir', - help='Directory containing .log files to parse' + "input_dir", help="Directory containing .log files to parse" ) parser.add_argument( - '--algorithm', '-a', + "--algorithm", + "-a", required=True, choices=SUPPORTED_ALGORITHMS, - help='Algorithm to parse logs for' + help="Algorithm to parse logs for", ) parser.add_argument( - '-o', '--output', + "-o", + "--output", default=None, - help='Output Feather file path (default: _data.feather)' + help="Output Feather file path (default: _data.feather)", ) parser.add_argument( - '--verbose', '-v', - action='store_true', - help='Print verbose output including warnings' + "--verbose", + "-v", + action="store_true", + help="Print verbose output including warnings", ) parser.add_argument( - '--max-files', + "--max-files", type=int, default=None, - help='Limit number of log files to process (useful for testing)' + help="Limit number of log files to process (useful for testing)", ) args = parser.parse_args() @@ -454,7 +523,7 @@ def main(): # Find all .log files in the input directory print(f"\nScanning {args.input_dir} for .log files...") - log_files = glob.glob(os.path.join(args.input_dir, '*.log')) + log_files = glob.glob(os.path.join(args.input_dir, "*.log")) if not log_files: print(f"Error: No .log files found in {args.input_dir}") @@ -465,27 +534,40 @@ def main(): # Apply max-files limit if specified if args.max_files is not None and args.max_files > 0: if args.max_files < len(log_files): - log_files = log_files[:args.max_files] + log_files = log_files[: args.max_files] print(f"Limiting to first {args.max_files} files (--max-files)") else: - print(f"Note: --max-files={args.max_files} is >= total files, using all files") + print( + f"Note: --max-files={args.max_files} is >= total files, using all files" + ) # Parse logs based on algorithm - if args.algorithm == 'FP': + if args.algorithm == "FP": entries = parse_fp_logs(log_files) - elif args.algorithm == 'PDLP': + elif args.algorithm == "PDLP": entries = parse_pdlp_logs(log_files) - elif args.algorithm == 'CP': + elif args.algorithm == "CP": entries = parse_cp_logs(log_files) - elif args.algorithm == 'FJ': + elif args.algorithm == "FJ": entries = parse_fj_logs(log_files) + elif args.algorithm == "CPUFJ": + entries = parse_cpufj_logs(log_files) else: print(f"Error: Unsupported algorithm: {args.algorithm}") return 1 if not entries: print(f"\nError: No entries found for {args.algorithm}") - print(f"Make sure your logs contain {args.algorithm}_FEATURES and {args.algorithm}_RESULT lines") + if args.algorithm in ["FP", "PDLP", "CP"]: + print( + f"Make sure your logs contain {args.algorithm}_FEATURES and {args.algorithm}_RESULT lines" + ) + elif args.algorithm == "FJ": + print("Make sure your logs contain FJ: lines with key=value pairs") + elif args.algorithm == "CPUFJ": + print( + "Make sure your logs contain CPUFJ_FEATURES lines with key=value pairs" + ) return 1 # Print statistics @@ -494,8 +576,19 @@ def main(): # Convert to DataFrame df = pd.DataFrame(entries) + # Convert all non-string columns to numeric types FIRST + # This ensures proper type inference before validation + print("\nConverting column types...") + for col in df.columns: + if col not in ["file"]: # Keep 'file' as string + # Try to convert to numeric, coercing errors to NaN + df[col] = pd.to_numeric(df[col], errors="coerce") + print( + f" ✓ Converted {len([c for c in df.columns if c != 'file'])} columns to numeric types" + ) + # Validate: Check for NaN and infinite values - print(f"\nValidating data integrity...") + print("\nValidating data integrity...") # Check for NaN nan_counts = df.isna().sum() @@ -509,89 +602,134 @@ def main(): if inf_count > 0: inf_counts[col] = inf_count - has_issues = len(columns_with_nan) > 0 or len(inf_counts) > 0 - - if has_issues: - print(f"\n{'='*70}") - print(f"ERROR: Invalid data detected (NaN/inf values)!") - print(f"{'='*70}") - print(f"\nColumns with invalid values:") - for col, count in columns_with_nan.items(): - pct = (count / len(df)) * 100 - print(f" {col}: {count} NaN ({pct:.1f}%)") - for col, count in inf_counts.items(): - pct = (count / len(df)) * 100 - print(f" {col}: {count} infinite ({pct:.1f}%)") + # Collect all problematic columns + problematic_columns = set() + problematic_columns.update(columns_with_nan.index) + problematic_columns.update(inf_counts.keys()) + + if problematic_columns: + print(f"\n{'=' * 70}") + print("⚠️ WARNING: Invalid data detected (NaN/inf values)!") + print(f"{'=' * 70}") + print("\nColumns with invalid values (will be removed):") + for col in sorted(problematic_columns): + nan_count = ( + nan_counts.get(col, 0) if col in columns_with_nan.index else 0 + ) + inf_count = inf_counts.get(col, 0) + total_invalid = nan_count + inf_count + pct = (total_invalid / len(df)) * 100 + + issues = [] + if nan_count > 0: + issues.append(f"{nan_count} NaN") + if inf_count > 0: + issues.append(f"{inf_count} inf") + + print(f" ❌ {col}: {', '.join(issues)} ({pct:.1f}%)") # Show which log files have the issues rows_with_issues_mask = df.isna().any(axis=1) for col in inf_counts.keys(): if col in df.columns: rows_with_issues_mask |= np.isinf(df[col]) - rows_with_nan = df[rows_with_issues_mask] - problematic_files = rows_with_nan['file'].unique() - print(f"\nProblematic log files ({len(problematic_files)} total):") - for i, filename in enumerate(sorted(problematic_files)[:20], 1): - count_in_file = len(rows_with_nan[rows_with_nan['file'] == filename]) - print(f" {i}. {filename}: {count_in_file} invalid entries") - if len(problematic_files) > 20: - print(f" ... and {len(problematic_files) - 20} more files") - - print(f"\nSample rows with invalid data:") - cols_to_show = ['file'] - cols_to_show.extend([col for col in columns_with_nan.index]) - cols_to_show.extend([col for col in inf_counts.keys() if col not in columns_with_nan.index]) - cols_to_show = [col for col in cols_to_show if col in df.columns] - print(rows_with_nan[cols_to_show].head(10)) - - print(f"\nPossible causes:") - print(f" 1. Algorithm failed/crashed during execution (produced 'nan' in logs)") - print(f" 2. Incomplete log entries (missing FEATURES or RESULT lines)") - print(f" 3. Log format doesn't match expected pattern") - print(f"\nAction required:") - print(f" - Review the problematic log files listed above") - print(f" - Fix the underlying issues causing NaN values") - print(f" - Re-run the algorithm on those problem instances") - print(f"{'='*70}\n") - return 1 - - print(f" ✅ No missing values detected") + rows_with_issues = df[rows_with_issues_mask] + problematic_files = rows_with_issues["file"].unique() + print( + f"\nAffected log files: {len(problematic_files)} files, {len(rows_with_issues)} entries" + ) + if len(problematic_files) <= 10: + for filename in sorted(problematic_files): + count_in_file = len( + rows_with_issues[rows_with_issues["file"] == filename] + ) + print( + f" - {filename}: {count_in_file} entries with invalid values" + ) + else: + print(f" (Showing first 10 of {len(problematic_files)} files)") + for i, filename in enumerate(sorted(problematic_files)[:10], 1): + count_in_file = len( + rows_with_issues[rows_with_issues["file"] == filename] + ) + print(f" {i}. {filename}: {count_in_file} entries") + + # Remove problematic columns + original_column_count = len(df.columns) + df = df.drop(columns=list(problematic_columns)) + removed_count = len(problematic_columns) + remaining_count = len(df.columns) + + print(f"\n✓ Removed {removed_count} problematic column(s)") + print(f" Original columns: {original_column_count}") + print(f" Remaining columns: {remaining_count}") + print(f" Kept all {len(df)} entries") + print(f"{'=' * 70}") + + # Check if we have any meaningful columns left (excluding metadata) + feature_cols_remaining = [ + col + for col in df.columns + if col not in ["file", "iter", "iterations"] + ] + if len(feature_cols_remaining) < 5: + print( + f"\n⚠️ WARNING: Very few features remaining ({len(feature_cols_remaining)})!" + ) + print( + " This may not be sufficient for training a regression model." + ) + print(" Consider fixing the underlying issues in your logs.") + else: + print(" ✅ No invalid values detected") # Filter out negative iterations (invalid data) - if 'iter' in df.columns: - negative_mask = df['iter'] < 0 + if "iter" in df.columns: + negative_mask = df["iter"] < 0 negative_count = negative_mask.sum() if negative_count > 0: - print(f"\n⚠️ Found {negative_count} entries with negative iterations ({negative_count/len(df)*100:.2f}%)") + print( + f"\n⚠️ Found {negative_count} entries with negative iterations ({negative_count / len(df) * 100:.2f}%)" + ) # Show which files have negative iterations negative_entries = df[negative_mask] - problematic_files = negative_entries['file'].unique() + problematic_files = negative_entries["file"].unique() if len(problematic_files) <= 10: - print(f" Affected files: {', '.join(sorted(problematic_files))}") + print( + f" Affected files: {', '.join(sorted(problematic_files))}" + ) else: print(f" Affected files: {len(problematic_files)} files") # Drop negative iterations df = df[~negative_mask].reset_index(drop=True) - print(f" → Dropped negative entries, remaining: {len(df)} entries") + print( + f" → Dropped negative entries, remaining: {len(df)} entries" + ) # Check if we have any data left if len(df) == 0: - print(f"\n❌ Error: No valid entries remaining after filtering!") - print(f" All entries had negative iterations.") + print( + "\n❌ Error: No valid entries remaining after filtering!" + ) + print(" All entries had negative iterations.") return 1 # Print obtained features (all columns) print(f"\nObtained Features ({len(df.columns)} total):") - print(f"{'='*70}") + print(f"{'=' * 70}") # Separate metadata from actual features - metadata_cols = ['file'] - target_cols = ['iter', 'iterations'] # Target variable (if present) + metadata_cols = ["file"] + target_cols = ["iter", "iterations"] # Target variable (if present) - feature_cols = [col for col in df.columns if col not in metadata_cols and col not in target_cols] + feature_cols = [ + col + for col in df.columns + if col not in metadata_cols and col not in target_cols + ] # Print in categories if metadata_cols: @@ -608,37 +746,109 @@ def main(): for i, col in enumerate(sorted(feature_cols), 1): # Print 3 features per line for readability if i % 3 == 1: - print(f" ", end="") + print(" ", end="") print(f"{col:30s}", end="") if i % 3 == 0: print() # Newline after 3 features if len(feature_cols) % 3 != 0: print() # Final newline if needed - print(f"{'='*70}") + print(f"{'=' * 70}") + + # Final validation: Ensure NO NaN values remain in the DataFrame + print("\nFinal validation before saving...") + remaining_nans = df.isna().sum() + columns_with_remaining_nans = remaining_nans[remaining_nans > 0] + + if len(columns_with_remaining_nans) > 0: + print(f"\n{'=' * 70}") + print("❌ CRITICAL ERROR: NaN values still present after cleaning!") + print(f"{'=' * 70}") + print("\nColumns with remaining NaN values:") + for col, count in columns_with_remaining_nans.items(): + pct = (count / len(df)) * 100 + print(f" - {col}: {count} NaN values ({pct:.1f}%)") + + print("\nRemoving these columns to ensure clean output...") + df = df.drop(columns=list(columns_with_remaining_nans.index)) + print( + f" ✓ Removed {len(columns_with_remaining_nans)} additional column(s)" + ) + print(f" Remaining columns: {len(df.columns)}") + + # Check if we have any meaningful columns left + feature_cols_remaining = [ + col + for col in df.columns + if col not in ["file", "iter", "iterations"] + ] + if len(feature_cols_remaining) == 0: + print( + "\n❌ FATAL ERROR: No feature columns remaining after NaN removal!" + ) + print(" All columns contained NaN values.") + print(" Cannot proceed with saving - no valid data to save.") + return 1 + + print(f"{'=' * 70}") + + # Double-check: verify no NaN or inf values exist + total_nans = df.isna().sum().sum() + if total_nans > 0: + print( + f"\n❌ FATAL ERROR: {total_nans} NaN values still present despite cleaning!" + ) + print(" This should not happen - please report this as a bug.") + return 1 + + # Check for infinite values + numeric_cols = df.select_dtypes(include=[np.number]).columns + total_infs = 0 + for col in numeric_cols: + total_infs += np.isinf(df[col]).sum() + + if total_infs > 0: + print( + f"\n❌ FATAL ERROR: {total_infs} infinite values still present despite cleaning!" + ) + print(" This should not happen - please report this as a bug.") + # Show which columns still have inf + for col in numeric_cols: + inf_count = np.isinf(df[col]).sum() + if inf_count > 0: + print(f" - {col}: {inf_count} inf values") + return 1 + + print(" ✅ No NaN or infinite values detected - data is clean") # Save to Feather file (Apache Arrow format for fast I/O) print(f"\nSaving {len(df)} entries to {args.output}...") - df.to_feather(args.output, compression='lz4') + df.to_feather(args.output, compression="lz4") # Get file size file_size_bytes = os.path.getsize(args.output) file_size_mb = file_size_bytes / (1024 * 1024) - print(f"\n{'='*70}") + print(f"\n{'=' * 70}") print(f"✓ Success! Saved {len(df)} entries to {args.output}") print(f" File size: {file_size_mb:.2f} MB") - print(f"{'='*70}") - print(f"\nNext steps:") - print(f" 1. View available features:") - print(f" python scripts/train_regressor.py {args.output} --regressor xgboost --list-features") - print(f" 2. Train a model:") - print(f" python scripts/train_regressor.py {args.output} --regressor xgboost --seed 42") - print(f" 3. Train with early stopping and C++ export:") - print(f" python scripts/train_regressor.py {args.output} --regressor xgboost --seed 42 --early-stopping 20 --treelite-compile 8") + print(f"{'=' * 70}") + print("\nNext steps:") + print(" 1. View available features:") + print( + f" python scripts/train_regressor.py {args.output} --regressor xgboost --list-features" + ) + print(" 2. Train a model:") + print( + f" python scripts/train_regressor.py {args.output} --regressor xgboost --seed 42" + ) + print(" 3. Train with early stopping and C++ export:") + print( + f" python scripts/train_regressor.py {args.output} --regressor xgboost --seed 42 --early-stopping 20 --treelite-compile 8" + ) return 0 -if __name__ == '__main__': +if __name__ == "__main__": exit(main()) diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index dc12fa3844..24204b6502 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +# SPDX-License-Identifier: Apache-2.0 # All rights reserved. # SPDX-License-Identifier: Apache-2.0 # @@ -36,16 +37,19 @@ import os from typing import List, Dict, Any, Tuple import warnings -warnings.filterwarnings('ignore', category=UserWarning) + +warnings.filterwarnings("ignore", category=UserWarning) AVAILABLE_REGRESSORS = [ - 'linear', - 'poly2', 'poly3', 'poly4', - 'xgboost', - 'lightgbm', - 'random_forest', - 'gradient_boosting' + "linear", + "poly2", + "poly3", + "poly4", + "xgboost", + "lightgbm", + "random_forest", + "gradient_boosting", ] # ============================================================================ @@ -58,38 +62,54 @@ # 'time', # 'avg_constraint_range', # 'binary_ratio', - 'avg_obj_coeff_magnitude', - 'n_of_minimums_for_exit', - 'feasibility_run', - 'fixed_var_ratio', - 'unbounded_var_ratio', - 'obj_var_ratio', - 'avg_related_vars_per_var', - 'avg_constraint_range', - 'nnz_variance', - 'avg_variable_range', - 'min_nnz_per_row', - 'constraint_var_ratio', - 'avg_var_degree', - 'equality_ratio', - 'integer_ratio', - 'binary_ratio', - 'max_related_vars', - 'problem_size_score', - 'structural_complexity', - 'tight_constraint_ratio', - 'tolerance', - 'time_limit', - 'tolerance', - 'primal_objective', - 'dual_objective', - 'gap', - 'l2_primal_residual', - 'l2_dual_residual', - 'detect_infeasibility', - 'iteration_limit', - 'termination', - 'check_infeasibility' + "avg_obj_coeff_magnitude", + "n_of_minimums_for_exit", + "feasibility_run", + "fixed_var_ratio", + "unbounded_var_ratio", + "obj_var_ratio", + "avg_related_vars_per_var", + "avg_constraint_range", + "nnz_variance", + "avg_variable_range", + "min_nnz_per_row", + "constraint_var_ratio", + "avg_var_degree", + "equality_ratio", + "integer_ratio", + "binary_ratio", + "max_related_vars", + "problem_size_score", + "structural_complexity", + "tight_constraint_ratio", + "tolerance", + "time_limit", + "tolerance", + "primal_objective", + "dual_objective", + "gap", + "l2_primal_residual", + "l2_dual_residual", + "detect_infeasibility", + "iteration_limit", + "termination", + "check_infeasibility", + "iter_since_best", + "tid", + "curr_obj", + "obj_weight", + "L3_miss", + "L2_miss", + "L1_miss", + "stores_per_iter", + "loads_per_iter", + "is_feas", + "feas_found", + "viol_ratio", + "eval_intensity", + "nnz_per_move", + "iter", + "max_weight", ] # Alternatively, specify ONLY the features you want to use @@ -103,39 +123,49 @@ # ============================================================================ -def load_data(data_path: str, target_col: str = 'iter') -> pd.DataFrame: +def load_data(data_path: str, target_col: str = "iter") -> pd.DataFrame: """Load data file (supports .feather and legacy .pkl formats).""" ext = os.path.splitext(data_path)[1].lower() - if ext == '.feather': + if ext == ".feather": # Fast Apache Arrow format df = pd.read_feather(data_path) - elif ext == '.pkl': + elif ext == ".pkl": # Legacy pickle support - with open(data_path, 'rb') as f: + with open(data_path, "rb") as f: data = pickle.load(f) if not isinstance(data, list): - raise ValueError(f"Expected list of dictionaries, got {type(data)}") + raise ValueError( + f"Expected list of dictionaries, got {type(data)}" + ) if len(data) == 0: raise ValueError("Empty dataset") df = pd.DataFrame(data) else: - raise ValueError(f"Unsupported file format: {ext}. Use .feather (recommended) or .pkl") + raise ValueError( + f"Unsupported file format: {ext}. Use .feather (recommended) or .pkl" + ) # Validate required columns - if 'file' not in df.columns: + if "file" not in df.columns: raise ValueError("Missing required 'file' column in data") if target_col not in df.columns: - raise ValueError(f"Missing target column '{target_col}' in data. Available columns: {list(df.columns)}") + raise ValueError( + f"Missing target column '{target_col}' in data. Available columns: {list(df.columns)}" + ) return df -def split_by_files(df: pd.DataFrame, test_size: float = 0.2, - random_state: int = None, stratify_by: str = None) -> Tuple[pd.DataFrame, pd.DataFrame]: +def split_by_files( + df: pd.DataFrame, + test_size: float = 0.2, + random_state: int = None, + stratify_by: str = None, +) -> Tuple[pd.DataFrame, pd.DataFrame]: """ Split data into train/test sets based on unique files. Ensures all entries from a file go to either train or test, not both. @@ -144,36 +174,43 @@ def split_by_files(df: pd.DataFrame, test_size: float = 0.2, ---- stratify_by: Optional column name to stratify split (e.g., 'iter' for balanced target distribution) """ - unique_files = df['file'].unique() + unique_files = df["file"].unique() # Optionally stratify by target distribution if stratify_by: # Create stratification labels based on quantiles of the specified column - file_stats = df.groupby('file')[stratify_by].median() - stratify_labels = pd.qcut(file_stats, q=min(5, len(unique_files)), labels=False, duplicates='drop') + file_stats = df.groupby("file")[stratify_by].median() + stratify_labels = pd.qcut( + file_stats, + q=min(5, len(unique_files)), + labels=False, + duplicates="drop", + ) train_files, test_files = train_test_split( unique_files, test_size=test_size, random_state=random_state, - stratify=stratify_labels + stratify=stratify_labels, ) else: train_files, test_files = train_test_split( - unique_files, - test_size=test_size, - random_state=random_state + unique_files, test_size=test_size, random_state=random_state ) - train_df = df[df['file'].isin(train_files)].copy() - test_df = df[df['file'].isin(test_files)].copy() + train_df = df[df["file"].isin(train_files)].copy() + test_df = df[df["file"].isin(test_files)].copy() - print(f"\nData Split:") + print("\nData Split:") print(f" Total entries: {len(df)}") print(f" Train entries: {len(train_df)} ({len(train_files)} files)") print(f" Test entries: {len(test_df)} ({len(test_files)} files)") # Check distribution similarity (use stratify_by column if provided, otherwise first numeric column) - target_col = stratify_by if stratify_by else df.select_dtypes(include=[np.number]).columns[0] + target_col = ( + stratify_by + if stratify_by + else df.select_dtypes(include=[np.number]).columns[0] + ) if target_col in train_df.columns and target_col in test_df.columns: train_target_mean = train_df[target_col].mean() test_target_mean = test_df[target_col].mean() @@ -181,30 +218,53 @@ def split_by_files(df: pd.DataFrame, test_size: float = 0.2, test_target_std = test_df[target_col].std() print(f"\nTarget ('{target_col}') Distribution:") - print(f" Train: mean={train_target_mean:.2f}, std={train_target_std:.2f}") - print(f" Test: mean={test_target_mean:.2f}, std={test_target_std:.2f}") + print( + f" Train: mean={train_target_mean:.2f}, std={train_target_std:.2f}" + ) + print( + f" Test: mean={test_target_mean:.2f}, std={test_target_std:.2f}" + ) - mean_diff_pct = abs(train_target_mean - test_target_mean) / train_target_mean * 100 if train_target_mean != 0 else 0 + mean_diff_pct = ( + abs(train_target_mean - test_target_mean) / train_target_mean * 100 + if train_target_mean != 0 + else 0 + ) if mean_diff_pct > 10: - print(f" ⚠️ Warning: Train/test target means differ by {mean_diff_pct:.1f}%") - print(f" Consider using stratified split or different random seed") + print( + f" ⚠️ Warning: Train/test target means differ by {mean_diff_pct:.1f}%" + ) + print( + " Consider using stratified split or different random seed" + ) return train_df, test_df -def list_available_features(df: pd.DataFrame, target_col: str = 'iter') -> List[str]: +def list_available_features( + df: pd.DataFrame, target_col: str = "iter" +) -> List[str]: """ List all available numeric features in the dataset. Helper function to see what features can be selected/excluded. """ # Drop target and metadata columns - cols_to_drop = [target_col, 'file', 'iter', 'iterations'] # Drop common target column names - X = df.drop(columns=[c for c in cols_to_drop if c in df.columns], errors='ignore') + cols_to_drop = [ + target_col, + "file", + "iter", + "iterations", + ] # Drop common target column names + X = df.drop( + columns=[c for c in cols_to_drop if c in df.columns], errors="ignore" + ) X = X.select_dtypes(include=[np.number]) return sorted(X.columns.tolist()) -def validate_data_quality(df: pd.DataFrame, target_col: str = 'iter', verbose: bool = True) -> Tuple[bool, Dict[str, Any]]: +def validate_data_quality( + df: pd.DataFrame, target_col: str = "iter", verbose: bool = True +) -> Tuple[bool, Dict[str, Any]]: """ Validate data quality and report issues. @@ -213,16 +273,16 @@ def validate_data_quality(df: pd.DataFrame, target_col: str = 'iter', verbose: b (is_valid, report_dict) """ report = { - 'target_issues': [], - 'feature_issues': [], - 'rows_with_issues': [], - 'is_valid': True + "target_issues": [], + "feature_issues": [], + "rows_with_issues": [], + "is_valid": True, } # Check target variable if target_col not in df.columns: - report['is_valid'] = False - report['target_issues'].append(f"Missing '{target_col}' column") + report["is_valid"] = False + report["target_issues"].append(f"Missing '{target_col}' column") return False, report y = df[target_col] @@ -230,16 +290,20 @@ def validate_data_quality(df: pd.DataFrame, target_col: str = 'iter', verbose: b # Check for NaN in target nan_count = y.isna().sum() if nan_count > 0: - report['is_valid'] = False - report['target_issues'].append(f"Target has {nan_count} NaN values ({nan_count/len(y)*100:.1f}%)") - report['rows_with_issues'].extend(df[y.isna()].index.tolist()) + report["is_valid"] = False + report["target_issues"].append( + f"Target has {nan_count} NaN values ({nan_count / len(y) * 100:.1f}%)" + ) + report["rows_with_issues"].extend(df[y.isna()].index.tolist()) # Check for inf in target inf_count = np.isinf(y).sum() if inf_count > 0: - report['is_valid'] = False - report['target_issues'].append(f"Target has {inf_count} infinite values ({inf_count/len(y)*100:.1f}%)") - report['rows_with_issues'].extend(df[np.isinf(y)].index.tolist()) + report["is_valid"] = False + report["target_issues"].append( + f"Target has {inf_count} infinite values ({inf_count / len(y) * 100:.1f}%)" + ) + report["rows_with_issues"].extend(df[np.isinf(y)].index.tolist()) # Check for extreme values in target if not y.isna().all() and not np.isinf(y).all(): @@ -248,12 +312,16 @@ def validate_data_quality(df: pd.DataFrame, target_col: str = 'iter', verbose: b y_max = y_clean.max() y_min = y_clean.min() if y_max > 1e10: - report['target_issues'].append(f"Target has very large values (max={y_max:.2e})") + report["target_issues"].append( + f"Target has very large values (max={y_max:.2e})" + ) if y_min < -1e10: - report['target_issues'].append(f"Target has very large negative values (min={y_min:.2e})") + report["target_issues"].append( + f"Target has very large negative values (min={y_min:.2e})" + ) # Check features - X = df.drop(columns=[target_col, 'file'], errors='ignore') + X = df.drop(columns=[target_col, "file"], errors="ignore") X_numeric = X.select_dtypes(include=[np.number]) for col in X_numeric.columns: @@ -264,68 +332,84 @@ def validate_data_quality(df: pd.DataFrame, target_col: str = 'iter', verbose: b if nan_count > 0: pct = nan_count / len(col_data) * 100 if pct > 10: # Only report if > 10% are NaN - report['feature_issues'].append(f"{col}: {nan_count} NaN ({pct:.1f}%)") + report["feature_issues"].append( + f"{col}: {nan_count} NaN ({pct:.1f}%)" + ) # Check for inf inf_count = np.isinf(col_data).sum() if inf_count > 0: - report['is_valid'] = False + report["is_valid"] = False pct = inf_count / len(col_data) * 100 - report['feature_issues'].append(f"{col}: {inf_count} infinite ({pct:.1f}%)") - report['rows_with_issues'].extend(df[np.isinf(col_data)].index.tolist()) + report["feature_issues"].append( + f"{col}: {inf_count} infinite ({pct:.1f}%)" + ) + report["rows_with_issues"].extend( + df[np.isinf(col_data)].index.tolist() + ) # Deduplicate row indices - report['rows_with_issues'] = sorted(list(set(report['rows_with_issues']))) + report["rows_with_issues"] = sorted(list(set(report["rows_with_issues"]))) # Print report if verbose - if verbose and (report['target_issues'] or report['feature_issues']): - print("\n" + "="*70) + if verbose and (report["target_issues"] or report["feature_issues"]): + print("\n" + "=" * 70) print("DATA QUALITY ISSUES DETECTED") - print("="*70) + print("=" * 70) - if report['target_issues']: + if report["target_issues"]: print("\nTarget Variable Issues:") - for issue in report['target_issues']: + for issue in report["target_issues"]: print(f" ❌ {issue}") - if report['feature_issues']: - print(f"\nFeature Issues ({len(report['feature_issues'])} features affected):") - for issue in report['feature_issues'][:10]: # Show first 10 + if report["feature_issues"]: + print( + f"\nFeature Issues ({len(report['feature_issues'])} features affected):" + ) + for issue in report["feature_issues"][:10]: # Show first 10 print(f" ⚠️ {issue}") - if len(report['feature_issues']) > 10: - print(f" ... and {len(report['feature_issues']) - 10} more features") + if len(report["feature_issues"]) > 10: + print( + f" ... and {len(report['feature_issues']) - 10} more features" + ) - if report['rows_with_issues']: + if report["rows_with_issues"]: print(f"\nAffected Rows: {len(report['rows_with_issues'])} total") - if len(report['rows_with_issues']) <= 10: + if len(report["rows_with_issues"]) <= 10: print(f" Row indices: {report['rows_with_issues']}") else: print(f" First 10: {report['rows_with_issues'][:10]}") # Show sample problematic rows with filenames - if 'file' in df.columns: - print(f"\n Sample problematic entries:") - sample_indices = report['rows_with_issues'][:5] + if "file" in df.columns: + print("\n Sample problematic entries:") + sample_indices = report["rows_with_issues"][:5] for idx in sample_indices: if idx < len(df): - filename = df.iloc[idx].get('file', 'unknown') - iter_val = df.iloc[idx].get('iter', 'N/A') - print(f" Row {idx}: file={filename}, iter={iter_val}") + filename = df.iloc[idx].get("file", "unknown") + iter_val = df.iloc[idx].get("iter", "N/A") + print( + f" Row {idx}: file={filename}, iter={iter_val}" + ) print("\nSuggested Actions:") - if not report['is_valid']: + if not report["is_valid"]: print(" 1. Remove rows with invalid data: --drop-invalid-rows") print(" 2. Check your log files for data collection issues") print(" 3. Verify algorithm didn't produce invalid results") else: - print(" Data is valid but has some NaN values in features (will be handled)") + print( + " Data is valid but has some NaN values in features (will be handled)" + ) - print("="*70 + "\n") + print("=" * 70 + "\n") - return report['is_valid'], report + return report["is_valid"], report -def prepare_features(df: pd.DataFrame, target_col: str = 'iter') -> Tuple[pd.DataFrame, pd.Series, List[str]]: +def prepare_features( + df: pd.DataFrame, target_col: str = "iter" +) -> Tuple[pd.DataFrame, pd.Series, List[str]]: """ Prepare features and target from DataFrame. Excludes 'file' and target column from features. @@ -335,7 +419,7 @@ def prepare_features(df: pd.DataFrame, target_col: str = 'iter') -> Tuple[pd.Dat y = df[target_col].copy() # Drop non-feature columns - X = df.drop(columns=[target_col, 'file']) + X = df.drop(columns=[target_col, "file"]) # Ensure all features are numeric non_numeric = X.select_dtypes(exclude=[np.number]).columns.tolist() @@ -348,21 +432,31 @@ def prepare_features(df: pd.DataFrame, target_col: str = 'iter') -> Tuple[pd.Dat if FEATURES_TO_INCLUDE_ONLY: # Use only specified features - available_features = [f for f in FEATURES_TO_INCLUDE_ONLY if f in X.columns] - missing_features = [f for f in FEATURES_TO_INCLUDE_ONLY if f not in X.columns] + available_features = [ + f for f in FEATURES_TO_INCLUDE_ONLY if f in X.columns + ] + missing_features = [ + f for f in FEATURES_TO_INCLUDE_ONLY if f not in X.columns + ] if missing_features: - print(f"Warning: Requested features not found in data: {missing_features}") + print( + f"Warning: Requested features not found in data: {missing_features}" + ) X = X[available_features] - print(f"Feature selection: Using only {len(available_features)} specified features") + print( + f"Feature selection: Using only {len(available_features)} specified features" + ) elif FEATURES_TO_EXCLUDE: # Exclude specified features features_to_drop = [f for f in FEATURES_TO_EXCLUDE if f in X.columns] if features_to_drop: X = X.drop(columns=features_to_drop) - print(f"Feature selection: Excluded {len(features_to_drop)} features: {features_to_drop}") + print( + f"Feature selection: Excluded {len(features_to_drop)} features: {features_to_drop}" + ) feature_names = X.columns.tolist() @@ -373,143 +467,161 @@ def prepare_features(df: pd.DataFrame, target_col: str = 'iter') -> Tuple[pd.Dat ) if len(feature_names) != original_feature_count: - print(f" Using {len(feature_names)} of {original_feature_count} available features") + print( + f" Using {len(feature_names)} of {original_feature_count} available features" + ) return X, y, feature_names -def create_regressor(regressor_type: str, random_state: int = None, - tune_hyperparams: bool = False, verbose: bool = True): +def create_regressor( + regressor_type: str, + random_state: int = None, + tune_hyperparams: bool = False, + verbose: bool = True, +): """ Create a regression model with optional preprocessing pipeline. Returns: (model, needs_scaling) """ - if regressor_type == 'linear': + if regressor_type == "linear": model = LinearRegression() needs_scaling = True - elif regressor_type.startswith('poly'): + elif regressor_type.startswith("poly"): degree = int(regressor_type[-1]) - model = Pipeline([ - ('poly', PolynomialFeatures(degree=degree, include_bias=False)), - ('regressor', Ridge(alpha=1.0)) # Ridge to handle multicollinearity - ]) + model = Pipeline( + [ + ( + "poly", + PolynomialFeatures(degree=degree, include_bias=False), + ), + ( + "regressor", + Ridge(alpha=1.0), + ), # Ridge to handle multicollinearity + ] + ) needs_scaling = True - elif regressor_type == 'xgboost': + elif regressor_type == "xgboost": try: import xgboost as xgb except ImportError: - raise ImportError("XGBoost not installed. Install with: pip install xgboost") + raise ImportError( + "XGBoost not installed. Install with: pip install xgboost" + ) params = { - 'objective': 'reg:squarederror', - 'random_state': random_state, - 'n_estimators': 100, - 'max_depth': 6, - 'tree_method': 'hist', - 'learning_rate': 0.1, - 'verbosity': 1 if verbose else 0, + "objective": "reg:squarederror", + "random_state": random_state, + "n_estimators": 100, + "max_depth": 6, + "tree_method": "hist", + "learning_rate": 0.1, + "verbosity": 1 if verbose else 0, # Regularization to prevent overfitting - 'min_child_weight': 3, # Minimum sum of weights in a leaf - 'gamma': 0.1, # Minimum loss reduction for split - 'subsample': 0.8, # Fraction of samples per tree - 'colsample_bytree': 0.8, # Fraction of features per tree - 'reg_alpha': 0.1, # L1 regularization - 'reg_lambda': 1.0, # L2 regularization + "min_child_weight": 3, # Minimum sum of weights in a leaf + "gamma": 0.1, # Minimum loss reduction for split + "subsample": 0.8, # Fraction of samples per tree + "colsample_bytree": 0.8, # Fraction of features per tree + "reg_alpha": 0.1, # L1 regularization + "reg_lambda": 1.0, # L2 regularization } if tune_hyperparams: # Stronger regularization for tuned version - params.update({ - 'n_estimators': 200, - 'max_depth': 5, # Shallower trees - 'learning_rate': 0.05, # Lower learning rate - 'min_child_weight': 5, # Higher minimum weight - 'gamma': 0.2, # More conservative splits - 'subsample': 0.7, # More aggressive subsampling - 'colsample_bytree': 0.7, - 'reg_alpha': 0.5, # Stronger L1 - 'reg_lambda': 2.0, # Stronger L2 - }) + params.update( + { + "n_estimators": 200, + "max_depth": 5, # Shallower trees + "learning_rate": 0.05, # Lower learning rate + "min_child_weight": 5, # Higher minimum weight + "gamma": 0.2, # More conservative splits + "subsample": 0.7, # More aggressive subsampling + "colsample_bytree": 0.7, + "reg_alpha": 0.5, # Stronger L1 + "reg_lambda": 2.0, # Stronger L2 + } + ) model = xgb.XGBRegressor(**params) needs_scaling = False - elif regressor_type == 'lightgbm': + elif regressor_type == "lightgbm": try: import lightgbm as lgb except ImportError: - raise ImportError("LightGBM not installed. Install with: pip install lightgbm") + raise ImportError( + "LightGBM not installed. Install with: pip install lightgbm" + ) params = { - 'objective': 'regression', - 'random_state': random_state, - 'n_estimators': 100, - 'max_depth': 6, - 'learning_rate': 0.1, - 'verbosity': 1 if verbose else -1, + "objective": "regression", + "random_state": random_state, + "n_estimators": 100, + "max_depth": 4, + "learning_rate": 0.1, + "verbosity": 1 if verbose else -1, # Regularization to prevent overfitting - 'min_child_weight': 3, - 'min_split_gain': 0.1, - 'subsample': 0.8, - 'colsample_bytree': 0.8, - 'reg_alpha': 0.1, - 'reg_lambda': 1.0, + "min_child_weight": 3, + "min_split_gain": 0.1, + "subsample": 0.8, + "colsample_bytree": 0.8, + "reg_alpha": 0.1, + "reg_lambda": 1.0, } if tune_hyperparams: # Stronger regularization for tuned version - params.update({ - 'n_estimators': 200, - 'max_depth': 5, - 'learning_rate': 0.05, - 'min_child_weight': 5, - 'min_split_gain': 0.2, - 'subsample': 0.7, - 'colsample_bytree': 0.7, - 'reg_alpha': 0.5, - 'reg_lambda': 2.0, - }) + params.update( + { + "n_estimators": 200, + "max_depth": 5, + "learning_rate": 0.05, + "min_child_weight": 5, + "min_split_gain": 0.2, + "subsample": 0.7, + "colsample_bytree": 0.7, + "reg_alpha": 0.5, + "reg_lambda": 2.0, + } + ) model = lgb.LGBMRegressor(**params) needs_scaling = False - elif regressor_type == 'random_forest': + elif regressor_type == "random_forest": params = { - 'random_state': random_state, - 'n_estimators': 100, - 'max_depth': None, - 'min_samples_split': 2, - 'verbose': 1 if verbose else 0 + "random_state": random_state, + "n_estimators": 100, + "max_depth": None, + "min_samples_split": 2, + "verbose": 1 if verbose else 0, } if tune_hyperparams: - params.update({ - 'n_estimators': 200, - 'max_depth': 20, - 'min_samples_split': 5 - }) + params.update( + {"n_estimators": 200, "max_depth": 20, "min_samples_split": 5} + ) model = RandomForestRegressor(**params) needs_scaling = False - elif regressor_type == 'gradient_boosting': + elif regressor_type == "gradient_boosting": params = { - 'random_state': random_state, - 'n_estimators': 100, - 'max_depth': 5, - 'learning_rate': 0.1, - 'verbose': 1 if verbose else 0 + "random_state": random_state, + "n_estimators": 100, + "max_depth": 5, + "learning_rate": 0.1, + "verbose": 1 if verbose else 0, } if tune_hyperparams: - params.update({ - 'n_estimators': 200, - 'max_depth': 7, - 'learning_rate': 0.05 - }) + params.update( + {"n_estimators": 200, "max_depth": 7, "learning_rate": 0.05} + ) model = GradientBoostingRegressor(**params) needs_scaling = False @@ -520,40 +632,54 @@ def create_regressor(regressor_type: str, random_state: int = None, return model, needs_scaling -def get_feature_importance(model, feature_names: List[str], - regressor_type: str) -> None: +def get_feature_importance( + model, feature_names: List[str], regressor_type: str +) -> None: """Extract and print feature importance if available.""" - print(f"\nFeature Importance:") + print("\nFeature Importance:") try: - if regressor_type in ['xgboost', 'lightgbm', 'random_forest', 'gradient_boosting']: + if regressor_type in [ + "xgboost", + "lightgbm", + "random_forest", + "gradient_boosting", + ]: # Tree-based models have feature_importances_ importances = model.feature_importances_ indices = np.argsort(importances)[::-1] for i, idx in enumerate(indices, 1): - print(f" {i:3d}. {feature_names[idx]:40s}: {importances[idx]:.6f}") + print( + f" {i:3d}. {feature_names[idx]:40s}: {importances[idx]:.6f}" + ) - elif regressor_type == 'linear': + elif regressor_type == "linear": # Linear regression coefficients and intercept print(f"\nIntercept: {model.intercept_:.6f}\n") - print(f"Coefficients:") + print("Coefficients:") # Print each feature with its coefficient - for i, (feature_name, coef) in enumerate(zip(feature_names, model.coef_), 1): + for i, (feature_name, coef) in enumerate( + zip(feature_names, model.coef_), 1 + ): print(f" {i:3d}. {feature_name:40s}: {coef:.6f}") # Also show sorted by absolute value for importance ranking - print(f"\nRanked by absolute magnitude:") + print("\nRanked by absolute magnitude:") coefs_abs = np.abs(model.coef_) indices = np.argsort(coefs_abs)[::-1] for i, idx in enumerate(indices, 1): - print(f" {i:3d}. {feature_names[idx]:40s}: {model.coef_[idx]:.6f} (|{coefs_abs[idx]:.6f}|)") + print( + f" {i:3d}. {feature_names[idx]:40s}: {model.coef_[idx]:.6f} (|{coefs_abs[idx]:.6f}|)" + ) - elif regressor_type.startswith('poly'): + elif regressor_type.startswith("poly"): # For polynomial, get feature names and coefficients from the Ridge step - poly_features = model.named_steps['poly'].get_feature_names_out(feature_names) - coefs = np.abs(model.named_steps['regressor'].coef_) + poly_features = model.named_steps["poly"].get_feature_names_out( + feature_names + ) + coefs = np.abs(model.named_steps["regressor"].coef_) indices = np.argsort(coefs)[::-1] print(f" (Showing top 50 of {len(indices)} polynomial features)") @@ -570,11 +696,20 @@ def get_feature_importance(model, feature_names: List[str], print(f" Could not extract feature importance: {e}") -def evaluate_model(model, X_train, y_train, X_test, y_test, - feature_names: List[str], regressor_type: str, - cv_folds: int = 5, verbose: int = 0, - skip_cv: bool = False, X_test_original: pd.DataFrame = None, - test_df: pd.DataFrame = None) -> Tuple[float, float]: +def evaluate_model( + model, + X_train, + y_train, + X_test, + y_test, + feature_names: List[str], + regressor_type: str, + cv_folds: int = 5, + verbose: int = 0, + skip_cv: bool = False, + X_test_original: pd.DataFrame = None, + test_df: pd.DataFrame = None, +) -> Tuple[float, float]: """Evaluate model and print metrics. Returns (train_r2, test_r2). Args: @@ -586,18 +721,24 @@ def evaluate_model(model, X_train, y_train, X_test, y_test, if not skip_cv: print(f"\nCross-Validation on Training Set ({cv_folds}-fold):") try: - cv_scores = cross_val_score(model, X_train, y_train, - cv=cv_folds, - scoring='neg_mean_squared_error', - n_jobs=-1, - verbose=verbose) + cv_scores = cross_val_score( + model, + X_train, + y_train, + cv=cv_folds, + scoring="neg_mean_squared_error", + n_jobs=-1, + verbose=verbose, + ) cv_rmse = np.sqrt(-cv_scores) print(f" CV RMSE: {cv_rmse.mean():.4f} (+/- {cv_rmse.std():.4f})") except Exception as e: - print(f" CV failed (likely due to early stopping): {str(e)[:100]}") - print(f" Skipping cross-validation...") + print( + f" CV failed (likely due to early stopping): {str(e)[:100]}" + ) + print(" Skipping cross-validation...") else: - print(f"\nSkipping cross-validation (incompatible with early stopping)") + print("\nSkipping cross-validation (incompatible with early stopping)") # Training set metrics y_train_pred = model.predict(X_train) @@ -606,7 +747,7 @@ def evaluate_model(model, X_train, y_train, X_test, y_test, train_mae = mean_absolute_error(y_train, y_train_pred) train_r2 = r2_score(y_train, y_train_pred) - print(f"\nTraining Set Metrics:") + print("\nTraining Set Metrics:") print(f" MSE: {train_mse:.4f}") print(f" RMSE: {train_rmse:.4f}") print(f" MAE: {train_mae:.4f}") @@ -619,7 +760,7 @@ def evaluate_model(model, X_train, y_train, X_test, y_test, test_mae = mean_absolute_error(y_test, y_test_pred) test_r2 = r2_score(y_test, y_test_pred) - print(f"\nTest Set Metrics:") + print("\nTest Set Metrics:") print(f" MSE: {test_mse:.4f}") print(f" RMSE: {test_rmse:.4f}") print(f" MAE: {test_mae:.4f}") @@ -629,20 +770,26 @@ def evaluate_model(model, X_train, y_train, X_test, y_test, get_feature_importance(model, feature_names, regressor_type) # Sample predictions - print(f"\n20 Sample Predictions from Test Set:") - print(f" {'Actual':>10s} {'Predicted':>10s} {'Error':>10s} {'Error %':>10s}") - print(f" {'-'*10} {'-'*10} {'-'*10} {'-'*10}") + print("\n20 Sample Predictions from Test Set:") + print( + f" {'Actual':>10s} {'Predicted':>10s} {'Error':>10s} {'Error %':>10s}" + ) + print(f" {'-' * 10} {'-' * 10} {'-' * 10} {'-' * 10}") - sample_indices = np.random.choice(len(y_test), min(20, len(y_test)), replace=False) + sample_indices = np.random.choice( + len(y_test), min(20, len(y_test)), replace=False + ) for idx in sample_indices: actual = y_test.iloc[idx] predicted = y_test_pred[idx] error = actual - predicted error_pct = (error / actual * 100) if actual != 0 else 0 - print(f" {actual:10.2f} {predicted:10.2f} {error:10.2f} {error_pct:9.2f}%") + print( + f" {actual:10.2f} {predicted:10.2f} {error:10.2f} {error_pct:9.2f}%" + ) # Show worst predictions with feature values - print(f"\n5 Worst Predictions (Largest Absolute Error):") + print("\n5 Worst Predictions (Largest Absolute Error):") abs_errors = np.abs(y_test_pred - y_test.values) worst_indices = np.argsort(abs_errors)[-5:][::-1] @@ -657,11 +804,13 @@ def evaluate_model(model, X_train, y_train, X_test, y_test, # Get filename if available filename = "" - if test_df is not None and 'file' in test_df.columns: + if test_df is not None and "file" in test_df.columns: filename = f" (file: {test_df.iloc[idx]['file']})" - print(f"\n #{rank} - Actual: {actual:.2f}, Predicted: {predicted:.2f}, " - f"Error: {error:.2f} ({error_pct:.1f}%){filename}") + print( + f"\n #{rank} - Actual: {actual:.2f}, Predicted: {predicted:.2f}, " + f"Error: {error:.2f} ({error_pct:.1f}%){filename}" + ) # Get feature values (handle both DataFrame and array) if isinstance(X_display, pd.DataFrame): @@ -672,10 +821,12 @@ def evaluate_model(model, X_train, y_train, X_test, y_test, feature_values = X_display[idx] # Display features compactly (5 per line) - print(f" Features:", end="") - for i, (feat_name, feat_val) in enumerate(zip(feature_names, feature_values)): + print(" Features:", end="") + for i, (feat_name, feat_val) in enumerate( + zip(feature_names, feature_values) + ): if i % 5 == 0: - print(f"\n ", end="") + print("\n ", end="") # Format feature value if isinstance(feat_val, (int, np.integer)): print(f"{feat_name}={feat_val}", end=" ") @@ -686,11 +837,17 @@ def evaluate_model(model, X_train, y_train, X_test, y_test, return train_r2, test_r2 -def compile_model_treelite(model, regressor_type: str, output_dir: str, - num_threads: int, X_train=None, - annotate: bool = False, quantize: bool = False, - feature_names: List[str] = None, - model_name: str = None) -> None: +def compile_model_treelite( + model, + regressor_type: str, + output_dir: str, + num_threads: int, + X_train=None, + annotate: bool = False, + quantize: bool = False, + feature_names: List[str] = None, + model_name: str = None, +) -> None: """Compile XGBoost/LightGBM model to C source files using TL2cgen. Args: @@ -705,14 +862,16 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, feature_names: List of feature names in expected order (optional) model_name: Name prefix for functions (optional, derived from training file) """ - if regressor_type not in ['xgboost', 'lightgbm']: - print(f"Warning: TL2cgen compilation only supported for XGBoost and LightGBM, skipping for {regressor_type}") + if regressor_type not in ["xgboost", "lightgbm"]: + print( + f"Warning: TL2cgen compilation only supported for XGBoost and LightGBM, skipping for {regressor_type}" + ) return try: import treelite import tl2cgen - except ImportError as e: + except ImportError: missing = [] try: import treelite @@ -723,7 +882,9 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, except ImportError: missing.append("tl2cgen") - print(f"Warning: {', '.join(missing)} not installed. Install with: pip install {' '.join(missing)}") + print( + f"Warning: {', '.join(missing)} not installed. Install with: pip install {' '.join(missing)}" + ) print("Skipping C code generation.") return @@ -733,17 +894,23 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, if quantize: optimization_info.append("quantization") - opt_str = f" with {', '.join(optimization_info)}" if optimization_info else "" - print(f"\nGenerating C source code with TL2cgen (threads={num_threads}){opt_str}...") + opt_str = ( + f" with {', '.join(optimization_info)}" if optimization_info else "" + ) + print( + f"\nGenerating C source code with TL2cgen (threads={num_threads}){opt_str}..." + ) # Convert model to treelite format using frontend API try: - if regressor_type == 'xgboost': + if regressor_type == "xgboost": tl_model = treelite.frontend.from_xgboost(model.get_booster()) - elif regressor_type == 'lightgbm': + elif regressor_type == "lightgbm": tl_model = treelite.frontend.from_lightgbm(model.booster_) except Exception as e: - print(f"Warning: Failed to convert {regressor_type} model to treelite: {e}") + print( + f"Warning: Failed to convert {regressor_type} model to treelite: {e}" + ) return # Annotate branches if requested and training data is available @@ -752,72 +919,80 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, try: print(" Annotating branches with training data...") # Convert to numpy array if it's a DataFrame - if hasattr(X_train, 'values'): + if hasattr(X_train, "values"): X_train_array = X_train.values.astype(np.float32) else: X_train_array = np.asarray(X_train, dtype=np.float32) - dmat = tl2cgen.DMatrix(X_train_array, dtype='float32') - annotation_path = os.path.join(output_dir, f'{regressor_type}_annotation.json') - tl2cgen.annotate_branch(tl_model, dmat=dmat, path=annotation_path, verbose=False) + dmat = tl2cgen.DMatrix(X_train_array, dtype="float32") + annotation_path = os.path.join( + output_dir, f"{regressor_type}_annotation.json" + ) + tl2cgen.annotate_branch( + tl_model, dmat=dmat, path=annotation_path, verbose=False + ) print(f" Branch annotations saved to: {annotation_path}") except Exception as e: print(f" Warning: Branch annotation failed: {e}") print(" Continuing without branch annotation") annotation_path = None elif annotate and X_train is None: - print(" Warning: Branch annotation requested but no training data available") + print( + " Warning: Branch annotation requested but no training data available" + ) print(" Skipping branch annotation") # Generate C source files using TL2cgen - source_dir = os.path.join(output_dir, f'{regressor_type}_c_code') + source_dir = os.path.join(output_dir, f"{regressor_type}_c_code") try: - #params = {'parallel_comp': num_threads} + # params = {'parallel_comp': num_threads} params = {} # Add quantization parameter if requested if quantize: - params['quantize'] = 1 # Enable quantization in code generation + params["quantize"] = 1 # Enable quantization in code generation # Add annotation file if available if annotation_path: - params['annotate_in'] = annotation_path + params["annotate_in"] = annotation_path tl2cgen.generate_c_code( - tl_model, - dirpath=source_dir, - params=params, - verbose=False + tl_model, dirpath=source_dir, params=params, verbose=False ) # Post-process generated files - header_path = os.path.join(source_dir, 'header.h') - main_path = os.path.join(source_dir, 'main.c') - quantize_path = os.path.join(source_dir, 'quantize.c') - recipe_path = os.path.join(source_dir, 'recipe.json') + header_path = os.path.join(source_dir, "header.h") + main_path = os.path.join(source_dir, "main.c") + quantize_path = os.path.join(source_dir, "quantize.c") + recipe_path = os.path.join(source_dir, "recipe.json") # Rename all .c files to .cpp and wrap in class if model_name: try: import glob - c_files = glob.glob(os.path.join(source_dir, '*.c')) + + c_files = glob.glob(os.path.join(source_dir, "*.c")) for c_file in c_files: - cpp_file = c_file[:-2] + '.cpp' + cpp_file = c_file[:-2] + ".cpp" # Read content - with open(c_file, 'r') as f: + with open(c_file, "r") as f: content = f.read() # Split content into includes and rest - lines = content.split('\n') + lines = content.split("\n") include_lines = [] code_lines = [] in_includes = True for line in lines: - if in_includes and (line.strip().startswith('#include') or line.strip().startswith('#') or line.strip() == ''): + if in_includes and ( + line.strip().startswith("#include") + or line.strip().startswith("#") + or line.strip() == "" + ): include_lines.append(line) else: in_includes = False @@ -825,41 +1000,48 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, # Prefix function definitions with ClassName:: (for .cpp files, not class wrapping) import re + processed_lines = [] for line in code_lines: # Detect function definitions (return_type function_name(...)) - if line and not line.strip().startswith('//') and not line.strip().startswith('/*'): + if ( + line + and not line.strip().startswith("//") + and not line.strip().startswith("/*") + ): # Check if it's a function definition # Pattern: type name(...) or type* name(...) etc. - func_pattern = r'^(\s*)((?:const\s+)?(?:unsigned\s+)?(?:struct\s+)?[\w_]+(?:\s*\*)*\s+)([\w_]+)(\s*\()' + func_pattern = r"^(\s*)((?:const\s+)?(?:unsigned\s+)?(?:struct\s+)?[\w_]+(?:\s*\*)*\s+)([\w_]+)(\s*\()" match = re.match(func_pattern, line) - if match and '::' not in line: # Don't add if already qualified + if ( + match and "::" not in line + ): # Don't add if already qualified indent = match.group(1) return_type = match.group(2) func_name = match.group(3) - rest = line[match.end(3):] + rest = line[match.end(3) :] # Prefix function name with class name - line = f'{indent}{return_type}{model_name}::{func_name}{rest}' + line = f"{indent}{return_type}{model_name}::{func_name}{rest}" processed_lines.append(line) code_lines = processed_lines # Don't wrap in class for .cpp files - just output the definitions - includes_str = '\n'.join(include_lines) - code_str = '\n'.join(code_lines) + includes_str = "\n".join(include_lines) + code_str = "\n".join(code_lines) # For .cpp files, no class wrapper needed - cpp_content = f'{includes_str}\n\n{code_str}\n' + cpp_content = f"{includes_str}\n\n{code_str}\n" # Write to .cpp file - with open(cpp_file, 'w') as f: + with open(cpp_file, "w") as f: f.write(cpp_content) # Remove original .c file os.remove(c_file) # Update paths for further processing - main_path = main_path[:-2] + '.cpp' - quantize_path = quantize_path[:-2] + '.cpp' + main_path = main_path[:-2] + ".cpp" + quantize_path = quantize_path[:-2] + ".cpp" print(f" Renamed {len(c_files)} .c files to .cpp") except Exception as e: @@ -869,18 +1051,23 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, # Since all features are always provided, replace !(data[X].missing != -1) with false if os.path.exists(main_path): try: - with open(main_path, 'r') as f: + with open(main_path, "r") as f: content = f.read() # Replace pattern !(data[N].missing != -1) with false import re + original_content = content - content = re.sub(r'!\(data\[\d+\]\.missing != -1\)', 'false', content) + content = re.sub( + r"!\(data\[\d+\]\.missing != -1\)", "false", content + ) if content != original_content: - with open(main_path, 'w') as f: + with open(main_path, "w") as f: f.write(content) - print(f" Optimized main.cpp by removing unnecessary missing data checks") + print( + " Optimized main.cpp by removing unnecessary missing data checks" + ) except Exception as e: print(f" Warning: Failed to optimize main.cpp: {e}") @@ -888,11 +1075,11 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, defines_to_move = [] if model_name and os.path.exists(header_path): try: - with open(header_path, 'r') as f: + with open(header_path, "r") as f: content = f.read() # Split content into includes, defines to move, and rest - lines = content.split('\n') + lines = content.split("\n") include_lines = [] code_lines = [] in_includes = True @@ -901,25 +1088,34 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, while i < len(lines): line = lines[i] - if in_includes and (line.strip().startswith('#include') or line.strip() == ''): + if in_includes and ( + line.strip().startswith("#include") + or line.strip() == "" + ): include_lines.append(line) i += 1 # Detect macros to move to main.cpp - elif line.strip().startswith('#if defined(__clang__)') or line.strip().startswith('#define N_TARGET') or line.strip().startswith('#define MAX_N_CLASS'): + elif ( + line.strip().startswith("#if defined(__clang__)") + or line.strip().startswith("#define N_TARGET") + or line.strip().startswith("#define MAX_N_CLASS") + ): in_includes = False # Capture the entire #if block or single #define - if line.strip().startswith('#if defined(__clang__)'): + if line.strip().startswith("#if defined(__clang__)"): # Capture the entire #if...#endif block macro_block = [] macro_block.append(line) i += 1 - while i < len(lines) and not lines[i].strip().startswith('#endif'): + while i < len(lines) and not lines[ + i + ].strip().startswith("#endif"): macro_block.append(lines[i]) i += 1 if i < len(lines): macro_block.append(lines[i]) # Include #endif i += 1 - defines_to_move.append('\n'.join(macro_block)) + defines_to_move.append("\n".join(macro_block)) else: # Single #define line defines_to_move.append(line) @@ -931,96 +1127,118 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, # Add static keyword to function declarations import re + processed_lines = [] for line in code_lines: # Detect function declarations/definitions (return_type function_name(...)) # Match lines that look like function declarations but don't already have static - if line and not line.strip().startswith('//') and not line.strip().startswith('/*'): + if ( + line + and not line.strip().startswith("//") + and not line.strip().startswith("/*") + ): # Check if it's a function declaration/definition # Pattern: type name(...) or type* name(...) or type name[...](...) etc. - func_pattern = r'^(\s*)((?:const\s+)?(?:unsigned\s+)?(?:struct\s+)?[\w_]+(?:\s*\*)*\s+)([\w_]+)\s*\(' + func_pattern = r"^(\s*)((?:const\s+)?(?:unsigned\s+)?(?:struct\s+)?[\w_]+(?:\s*\*)*\s+)([\w_]+)\s*\(" match = re.match(func_pattern, line) - if match and 'static' not in line: + if match and "static" not in line: indent = match.group(1) return_type = match.group(2) # Add static keyword - line = f'{indent}static {return_type}{line[len(indent)+len(return_type):]}' + line = f"{indent}static {return_type}{line[len(indent) + len(return_type) :]}" processed_lines.append(line) code_lines = processed_lines # Wrap code in class declaration - includes_str = '\n'.join(include_lines) - code_str = '\n'.join(code_lines) + includes_str = "\n".join(include_lines) + code_str = "\n".join(code_lines) - wrapped_content = f'#pragma once\n\n{includes_str}\n\nclass {model_name} {{\npublic:\n{code_str}\n}}; // class {model_name}\n' + wrapped_content = f"#pragma once\n\n{includes_str}\n\nclass {model_name} {{\npublic:\n{code_str}\n}}; // class {model_name}\n" - with open(header_path, 'w') as f: + with open(header_path, "w") as f: f.write(wrapped_content) - print(f" Wrapped header.h in class '{model_name}' with #pragma once") + print( + f" Wrapped header.h in class '{model_name}' with #pragma once" + ) except Exception as e: print(f" Warning: Failed to wrap header.h: {e}") # Add defines to main.cpp (moved from header.h) if defines_to_move and os.path.exists(main_path): try: - with open(main_path, 'r') as f: + with open(main_path, "r") as f: content = f.read() # Insert defines after includes (look for where code starts - typically after blank line after includes) - defines_str = '\n'.join(defines_to_move) + defines_str = "\n".join(defines_to_move) # Find the first non-include, non-blank line to insert before - lines = content.split('\n') + lines = content.split("\n") insert_pos = 0 for i, line in enumerate(lines): - if line.strip() and not line.strip().startswith('#include'): + if line.strip() and not line.strip().startswith( + "#include" + ): insert_pos = i break # Insert defines at the position lines.insert(insert_pos, defines_str) - lines.insert(insert_pos + 1, '') # Add blank line after defines + lines.insert( + insert_pos + 1, "" + ) # Add blank line after defines - content = '\n'.join(lines) - with open(main_path, 'w') as f: + content = "\n".join(lines) + with open(main_path, "w") as f: f.write(content) - print(f" Moved {len(defines_to_move)} macro definition(s) from header.h to main.cpp") + print( + f" Moved {len(defines_to_move)} macro definition(s) from header.h to main.cpp" + ) except Exception as e: print(f" Warning: Failed to add defines to main.cpp: {e}") # Add feature names to header and implementation - if feature_names and os.path.exists(header_path) and os.path.exists(main_path): + if ( + feature_names + and os.path.exists(header_path) + and os.path.exists(main_path) + ): try: # Append to header.h (inside class) - with open(header_path, 'r') as f: + with open(header_path, "r") as f: content = f.read() # Insert before closing class - insertion = f'\n // Feature names\n static constexpr int NUM_FEATURES = {len(feature_names)};\n static const char* feature_names[NUM_FEATURES];\n' - content = content.replace(f'}}; // class {model_name}\n', f'{insertion}}}; // class {model_name}\n') + insertion = f"\n // Feature names\n static constexpr int NUM_FEATURES = {len(feature_names)};\n static const char* feature_names[NUM_FEATURES];\n" + content = content.replace( + f"}}; // class {model_name}\n", + f"{insertion}}}; // class {model_name}\n", + ) - with open(header_path, 'w') as f: + with open(header_path, "w") as f: f.write(content) # Append to main.cpp (at the end of the file, outside any class) - with open(main_path, 'r') as f: + with open(main_path, "r") as f: content = f.read() # Append feature array definition at the end of the file - feature_array = f'\n// Feature names array\nconst char* {model_name}::feature_names[{model_name}::NUM_FEATURES] = {{\n' + feature_array = f"\n// Feature names array\nconst char* {model_name}::feature_names[{model_name}::NUM_FEATURES] = {{\n" for i, name in enumerate(feature_names): - comma = ',' if i < len(feature_names) - 1 else '' + comma = "," if i < len(feature_names) - 1 else "" feature_array += f' "{name}"{comma}\n' - feature_array += '};\n' + feature_array += "};\n" # Append to end of file - content = content.rstrip() + '\n' + feature_array + content = content.rstrip() + "\n" + feature_array - with open(main_path, 'w') as f: + with open(main_path, "w") as f: f.write(content) - print(f" Added {len(feature_names)} feature names to header.h and main.cpp") + print( + f" Added {len(feature_names)} feature names to header.h and main.cpp" + ) except Exception as e: print(f" Warning: Failed to add feature names: {e}") @@ -1028,7 +1246,7 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, if os.path.exists(recipe_path): try: os.remove(recipe_path) - print(f" Removed recipe.json") + print(" Removed recipe.json") except Exception as e: print(f" Warning: Failed to remove recipe.json: {e}") @@ -1040,66 +1258,74 @@ def compile_model_treelite(model, regressor_type: str, output_dir: str, opt_suffix = f" ({', '.join(opt_msg)})" if opt_msg else "" print(f"C source code generated to: {source_dir}/") - print(f" Contains optimized model source code{opt_suffix} ready for compilation") + print( + f" Contains optimized model source code{opt_suffix} ready for compilation" + ) except Exception as e: print(f"Warning: TL2cgen code generation failed: {e}") print(" Model saved in standard format only.") -def save_model(model, scaler, regressor_type: str, output_dir: str, - feature_names: List[str]) -> None: +def save_model( + model, + scaler, + regressor_type: str, + output_dir: str, + feature_names: List[str], +) -> None: """Save trained model and preprocessing components to disk.""" os.makedirs(output_dir, exist_ok=True) # Save metadata metadata = { - 'regressor_type': regressor_type, - 'feature_names': feature_names, - 'has_scaler': scaler is not None + "regressor_type": regressor_type, + "feature_names": feature_names, + "has_scaler": scaler is not None, } - metadata_path = os.path.join(output_dir, f'{regressor_type}_metadata.pkl') - with open(metadata_path, 'wb') as f: + metadata_path = os.path.join(output_dir, f"{regressor_type}_metadata.pkl") + with open(metadata_path, "wb") as f: pickle.dump(metadata, f) print(f"\nSaved metadata to: {metadata_path}") # Save scaler if exists if scaler is not None: - scaler_path = os.path.join(output_dir, f'{regressor_type}_scaler.pkl') + scaler_path = os.path.join(output_dir, f"{regressor_type}_scaler.pkl") joblib.dump(scaler, scaler_path) print(f"Saved scaler to: {scaler_path}") # Save model - if regressor_type == 'xgboost': + if regressor_type == "xgboost": # Save as UBJ with gzip compression - model_path = os.path.join(output_dir, f'{regressor_type}_model.ubj') + model_path = os.path.join(output_dir, f"{regressor_type}_model.ubj") model.save_model(model_path) # Gzip the file import gzip import shutil - with open(model_path, 'rb') as f_in: - with gzip.open(model_path + '.gz', 'wb') as f_out: + + with open(model_path, "rb") as f_in: + with gzip.open(model_path + ".gz", "wb") as f_out: shutil.copyfileobj(f_in, f_out) os.remove(model_path) # Remove uncompressed version print(f"Saved model to: {model_path}.gz") - elif regressor_type == 'lightgbm': + elif regressor_type == "lightgbm": # Save LightGBM model as text file - model_path = os.path.join(output_dir, f'{regressor_type}_model.txt') + model_path = os.path.join(output_dir, f"{regressor_type}_model.txt") model.booster_.save_model(model_path) print(f"Saved model to: {model_path}") else: # Save sklearn models as joblib (more efficient than pickle for large arrays) - model_path = os.path.join(output_dir, f'{regressor_type}_model.joblib') + model_path = os.path.join(output_dir, f"{regressor_type}_model.joblib") joblib.dump(model, model_path) print(f"Saved model to: {model_path}") def main(): parser = argparse.ArgumentParser( - description='Train regression models to predict algorithm iterations', + description="Train regression models to predict algorithm iterations", formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=f""" + epilog=""" Available regressors: linear - Linear Regression poly2, poly3, poly4 - Polynomial Regression (degree 2, 3, 4) @@ -1124,88 +1350,93 @@ def main(): # Legacy pickle format python train_regressor.py data.pkl --regressor xgboost --seed 42 - """ + """, ) - parser.add_argument('input_pkl', help='Input data file (.feather or .pkl) with log data') parser.add_argument( - '--regressor', '-r', + "input_pkl", help="Input data file (.feather or .pkl) with log data" + ) + parser.add_argument( + "--regressor", + "-r", required=True, choices=AVAILABLE_REGRESSORS, - help='Type of regressor to train' + help="Type of regressor to train", ) parser.add_argument( - '--output-dir', '-o', - default='./models', - help='Output directory for saved models (default: ./models)' + "--output-dir", + "-o", + default="./models", + help="Output directory for saved models (default: ./models)", ) parser.add_argument( - '--seed', '-s', + "--seed", + "-s", type=int, default=None, - help='Random seed for reproducibility (optional)' + help="Random seed for reproducibility (optional)", ) parser.add_argument( - '--tune', - action='store_true', - help='Enable hyperparameter tuning (uses predefined tuned parameters)' + "--tune", + action="store_true", + help="Enable hyperparameter tuning (uses predefined tuned parameters)", ) parser.add_argument( - '--cv-folds', + "--cv-folds", type=int, default=5, - help='Number of cross-validation folds (default: 5)' + help="Number of cross-validation folds (default: 5)", ) parser.add_argument( - '--test-size', + "--test-size", type=float, default=0.2, - help='Proportion of files to use for testing (default: 0.2)' + help="Proportion of files to use for testing (default: 0.2)", ) parser.add_argument( - '--no-progress', - action='store_true', - help='Disable training progress output' + "--no-progress", + action="store_true", + help="Disable training progress output", ) parser.add_argument( - '--list-features', - action='store_true', - help='List all available features in the dataset and exit' + "--list-features", + action="store_true", + help="List all available features in the dataset and exit", ) parser.add_argument( - '--stratify-split', - action='store_true', - help='Stratify train/test split by target distribution (ensures balanced iter values)' + "--stratify-split", + action="store_true", + help="Stratify train/test split by target distribution (ensures balanced iter values)", ) parser.add_argument( - '--early-stopping', + "--early-stopping", type=int, default=0, - metavar='N', - help='Enable early stopping for tree models (default: 20 rounds, use 0 to disable)' + metavar="N", + help="Enable early stopping for tree models (default: 20 rounds, use 0 to disable)", ) parser.add_argument( - '--treelite-compile', + "--treelite-compile", type=int, default=1, - metavar='THREADS', - help='Export XGBoost/LightGBM model as optimized C source code with TL2cgen (includes branch annotation and quantization)' + metavar="THREADS", + help="Export XGBoost/LightGBM model as optimized C source code with TL2cgen (includes branch annotation and quantization)", ) parser.add_argument( - '--drop-invalid-rows', - action='store_true', - help='Drop rows with NaN or infinite values in target variable (instead of failing)' + "--drop-invalid-rows", + action="store_true", + help="Drop rows with NaN or infinite values in target variable (instead of failing)", ) parser.add_argument( - '--check-data', - action='store_true', - help='Run data quality checks and exit (no training)' + "--check-data", + action="store_true", + help="Run data quality checks and exit (no training)", ) parser.add_argument( - '--target', + "--target", type=str, - default='iter', - help='Target column to predict (default: iter). Examples: iter, time_ms, iterations' + default="iter", + help="Target column to predict (default: iter). Examples: iter, time_ms, iterations", ) args = parser.parse_args() @@ -1223,17 +1454,19 @@ def main(): # Report file format ext = os.path.splitext(args.input_pkl)[1].lower() - if ext == '.feather': - print(f" Format: Apache Arrow/Feather (fast I/O)") - elif ext == '.pkl': - print(f" Format: Pickle (legacy)") + if ext == ".feather": + print(" Format: Apache Arrow/Feather (fast I/O)") + elif ext == ".pkl": + print(" Format: Pickle (legacy)") # Extract model name from input file (for prefixing generated C functions) model_name = os.path.splitext(os.path.basename(args.input_pkl))[0] # Validate data quality print("\nValidating data quality...") - is_valid, report = validate_data_quality(df, target_col=args.target, verbose=True) + is_valid, report = validate_data_quality( + df, target_col=args.target, verbose=True + ) # If just checking data, exit here if args.check_data: @@ -1241,27 +1474,37 @@ def main(): print("\n✅ Data quality check passed! Ready for training.") return 0 else: - print("\n❌ Data quality check failed! Fix issues before training.") + print( + "\n❌ Data quality check failed! Fix issues before training." + ) return 1 # Handle invalid data if not is_valid: if args.drop_invalid_rows: - print(f"\nDropping {len(report['rows_with_issues'])} rows with invalid data...") - df_clean = df.drop(index=report['rows_with_issues']) + print( + f"\nDropping {len(report['rows_with_issues'])} rows with invalid data..." + ) + df_clean = df.drop(index=report["rows_with_issues"]) df = df_clean.reset_index(drop=True) print(f" Remaining: {len(df)} entries") # Re-validate is_valid_after, _ = validate_data_quality(df, verbose=False) if not is_valid_after: - print("❌ Error: Data still invalid after dropping rows. Check your data.") + print( + "❌ Error: Data still invalid after dropping rows. Check your data." + ) return 1 print(" ✅ Data is now valid") else: print("\n❌ Training aborted due to invalid data.") - print(" Use --drop-invalid-rows to automatically remove invalid rows, or") - print(" Use --check-data to just run validation without training") + print( + " Use --drop-invalid-rows to automatically remove invalid rows, or" + ) + print( + " Use --check-data to just run validation without training" + ) return 1 # If listing features, do that and exit @@ -1270,36 +1513,45 @@ def main(): # Also list potential target columns numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist() - potential_targets = [col for col in numeric_cols if col not in features] + potential_targets = [ + col for col in numeric_cols if col not in features + ] - print(f"\n{'='*70}") + print(f"\n{'=' * 70}") print(f"Available features in dataset ({len(features)} total):") - print(f"{'='*70}") + print(f"{'=' * 70}") for i, feat in enumerate(features, 1): print(f" {i:3d}. {feat}") if potential_targets: - print(f"\n{'='*70}") - print(f"Potential target columns ({len(potential_targets)} total):") - print(f"{'='*70}") + print(f"\n{'=' * 70}") + print( + f"Potential target columns ({len(potential_targets)} total):" + ) + print(f"{'=' * 70}") for i, col in enumerate(potential_targets, 1): marker = " (current)" if col == args.target else "" print(f" {i:3d}. {col}{marker}") - print(f"\nTo exclude features, edit FEATURES_TO_EXCLUDE in the script:") + print("\nTo exclude features, edit FEATURES_TO_EXCLUDE in the script:") print(f" {__file__}") - print(f"\nTo use only specific features, edit FEATURES_TO_INCLUDE_ONLY") - print(f"\nTo change target column, use: --target ") + print("\nTo use only specific features, edit FEATURES_TO_INCLUDE_ONLY") + print("\nTo change target column, use: --target ") return # Split data by files stratify_by = args.target if args.stratify_split else None - train_df, test_df = split_by_files(df, test_size=args.test_size, - random_state=args.seed, - stratify_by=stratify_by) + train_df, test_df = split_by_files( + df, + test_size=args.test_size, + random_state=args.seed, + stratify_by=stratify_by, + ) # Prepare features - X_train, y_train, feature_names = prepare_features(train_df, target_col=args.target) + X_train, y_train, feature_names = prepare_features( + train_df, target_col=args.target + ) X_test, y_test, _ = prepare_features(test_df, target_col=args.target) print(f"\nFeatures: {len(feature_names)}") @@ -1311,7 +1563,7 @@ def main(): args.regressor, random_state=args.seed, tune_hyperparams=args.tune, - verbose=not args.no_progress + verbose=not args.no_progress, ) # Apply scaling if needed @@ -1327,42 +1579,73 @@ def main(): X_test_scaled = X_test # Train model - if args.regressor.startswith('poly'): + if args.regressor.startswith("poly"): degree = int(args.regressor[-1]) n_features = X_train_scaled.shape[1] from math import comb - n_poly_features = sum(comb(n_features + d - 1, d) for d in range(1, degree + 1)) - print(f" Generating {n_poly_features} polynomial features (degree {degree})...") + + n_poly_features = sum( + comb(n_features + d - 1, d) for d in range(1, degree + 1) + ) + print( + f" Generating {n_poly_features} polynomial features (degree {degree})..." + ) # Use early stopping for tree-based models if requested - if args.early_stopping and args.early_stopping > 0 and args.regressor in ['xgboost', 'lightgbm', 'gradient_boosting']: - if args.regressor == 'xgboost': - print(f" Using early stopping (patience={args.early_stopping} rounds)...") + if ( + args.early_stopping + and args.early_stopping > 0 + and args.regressor in ["xgboost", "lightgbm", "gradient_boosting"] + ): + if args.regressor == "xgboost": + print( + f" Using early stopping (patience={args.early_stopping} rounds)..." + ) # Set early stopping parameter model.set_params(early_stopping_rounds=args.early_stopping) model.fit( - X_train_scaled, y_train, + X_train_scaled, + y_train, eval_set=[(X_test_scaled, y_test)], - verbose=False + verbose=False, ) # Report best iteration - best_iteration = model.best_iteration if hasattr(model, 'best_iteration') else model.n_estimators - print(f" Best iteration: {best_iteration} (out of {model.n_estimators} max)") - elif args.regressor == 'lightgbm': - print(f" Using early stopping (patience={args.early_stopping} rounds)...") + best_iteration = ( + model.best_iteration + if hasattr(model, "best_iteration") + else model.n_estimators + ) + print( + f" Best iteration: {best_iteration} (out of {model.n_estimators} max)" + ) + elif args.regressor == "lightgbm": + print( + f" Using early stopping (patience={args.early_stopping} rounds)..." + ) model.fit( - X_train_scaled, y_train, + X_train_scaled, + y_train, eval_set=[(X_test_scaled, y_test)], callbacks=[ - __import__('lightgbm').early_stopping(stopping_rounds=args.early_stopping, verbose=False) - ] + __import__("lightgbm").early_stopping( + stopping_rounds=args.early_stopping, verbose=False + ) + ], ) # Report best iteration - best_iteration = model.best_iteration_ if hasattr(model, 'best_iteration_') else model.n_estimators - print(f" Best iteration: {best_iteration} (out of {model.n_estimators} max)") + best_iteration = ( + model.best_iteration_ + if hasattr(model, "best_iteration_") + else model.n_estimators + ) + print( + f" Best iteration: {best_iteration} (out of {model.n_estimators} max)" + ) else: # gradient_boosting # Gradient Boosting uses n_iter_no_change parameter - print(f" Note: Use --tune with gradient_boosting for early stopping") + print( + " Note: Use --tune with gradient_boosting for early stopping" + ) model.fit(X_train_scaled, y_train) else: model.fit(X_train_scaled, y_train) @@ -1371,11 +1654,20 @@ def main(): # Evaluate model skip_cv = args.early_stopping is not None and args.early_stopping > 0 - train_r2, test_r2 = evaluate_model(model, X_train_scaled, y_train, X_test_scaled, y_test, - feature_names, args.regressor, cv_folds=args.cv_folds, - verbose=2 if not args.no_progress else 0, - skip_cv=skip_cv, X_test_original=X_test_original, - test_df=test_df) + train_r2, test_r2 = evaluate_model( + model, + X_train_scaled, + y_train, + X_test_scaled, + y_test, + feature_names, + args.regressor, + cv_folds=args.cv_folds, + verbose=2 if not args.no_progress else 0, + skip_cv=skip_cv, + X_test_original=X_test_original, + test_df=test_df, + ) # Save model save_model(model, scaler, args.regressor, args.output_dir, feature_names) @@ -1392,19 +1684,19 @@ def main(): args.output_dir, args.treelite_compile, X_train=X_train_for_annotation, - annotate=True, # Always enable branch annotation - quantize=True, # Always enable quantization + annotate=True, # Always enable branch annotation + quantize=True, # Always enable quantization feature_names=feature_names, - model_name=model_name + model_name=model_name, ) - print("\n" + "="*70) + print("\n" + "=" * 70) print("Training completed successfully!") - print("="*70) - print(f"\nFinal R² Scores:") + print("=" * 70) + print("\nFinal R² Scores:") print(f" Train R²: {train_r2:.4f}") print(f" Test R²: {test_r2:.4f}") -if __name__ == '__main__': +if __name__ == "__main__": main() From 37ca39e897152f379fae28908b703fd00ce1aedc Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 10 Nov 2025 17:33:58 +0000 Subject: [PATCH 072/225] BnB features logging --- .../linear_programming/cuopt/run_mip.cpp | 2 +- cpp/src/dual_simplex/branch_and_bound.cpp | 252 +++++++++++++++++- cpp/src/dual_simplex/branch_and_bound.hpp | 61 +++++ cpp/src/mip/diversity/diversity_manager.cu | 4 +- cpp/src/mip/local_search/local_search.cuh | 4 +- cpp/src/utilities/timer.hpp | 2 +- 6 files changed, 317 insertions(+), 8 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 6897defca0..5dfd373d7d 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -207,7 +207,7 @@ int run_single_file(std::string file_path, settings.tolerances.absolute_tolerance = 1e-6; settings.presolve = true; - settings.heuristics_only = true; + // settings.heuristics_only = true; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index a0de45642b..36b8aa7c5a 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -34,6 +34,8 @@ namespace cuopt::linear_programming::dual_simplex { namespace { +static constexpr double FEATURE_LOG_INTERVAL = 0.25; // Log at most every 500ms + template bool is_fractional(f_t x, variable_type_t var_type, f_t integer_tol) { @@ -214,6 +216,9 @@ branch_and_bound_t::branch_and_bound_t( mutex_upper_.lock(); upper_bound_ = inf; mutex_upper_.unlock(); + + // Compute static problem features for regression model + compute_static_features(); } template @@ -544,6 +549,162 @@ branch_and_bound_t::child_selection(mip_node_t* node_ptr) } } +template +void branch_and_bound_t::compute_static_features() +{ + const auto& A = original_lp_.A; + static_features_.n_rows = A.m; + static_features_.n_cols = A.n; + static_features_.n_nonzeros = A.col_start[A.n]; + static_features_.density = (f_t)static_features_.n_nonzeros / ((f_t)A.m * A.n); + + // Count variable types + static_features_.n_binary = 0; + static_features_.n_integer = 0; + static_features_.n_continuous = 0; + for (const auto& vt : var_types_) { + if (vt == variable_type_t::BINARY) { + static_features_.n_binary++; + } else if (vt == variable_type_t::INTEGER) { + static_features_.n_integer++; + } else { + static_features_.n_continuous++; + } + } + static_features_.integrality_ratio = + (f_t)(static_features_.n_binary + static_features_.n_integer) / A.n; + + // Compute row statistics (constraint sizes) + std::vector row_nnz(A.m, 0); + for (i_t j = 0; j < A.n; j++) { + for (i_t k = A.col_start[j]; k < A.col_start[j + 1]; k++) { + row_nnz[A.i[k]]++; + } + } + + static_features_.max_row_nnz = 0; + f_t sum_row_nnz = 0; + for (i_t i = 0; i < A.m; i++) { + static_features_.max_row_nnz = std::max(static_features_.max_row_nnz, row_nnz[i]); + sum_row_nnz += row_nnz[i]; + } + static_features_.avg_row_nnz = sum_row_nnz / A.m; + + // Compute row coefficient of variation + f_t row_variance = 0; + for (i_t i = 0; i < A.m; i++) { + f_t diff = row_nnz[i] - static_features_.avg_row_nnz; + row_variance += diff * diff; + } + row_variance /= A.m; + f_t row_std = std::sqrt(row_variance); + static_features_.row_nnz_cv = + static_features_.avg_row_nnz > 0 ? row_std / static_features_.avg_row_nnz : 0.0; + + // Compute column statistics (variable degrees) + static_features_.max_col_nnz = 0; + f_t sum_col_nnz = 0; + for (i_t j = 0; j < A.n; j++) { + i_t col_nnz = A.col_start[j + 1] - A.col_start[j]; + static_features_.max_col_nnz = std::max(static_features_.max_col_nnz, col_nnz); + sum_col_nnz += col_nnz; + } + static_features_.avg_col_nnz = sum_col_nnz / A.n; + + // Compute column coefficient of variation + f_t col_variance = 0; + for (i_t j = 0; j < A.n; j++) { + i_t col_nnz = A.col_start[j + 1] - A.col_start[j]; + f_t diff = col_nnz - static_features_.avg_col_nnz; + col_variance += diff * diff; + } + col_variance /= A.n; + f_t col_std = std::sqrt(col_variance); + static_features_.col_nnz_cv = + static_features_.avg_col_nnz > 0 ? col_std / static_features_.avg_col_nnz : 0.0; +} + +template +void branch_and_bound_t::flush_pending_features() +{ + // Must be called with mutex_feature_log_ already locked + if (!has_pending_features_) return; + + constexpr int LINE_BUFFER_SIZE = 512; + char line_buffer[LINE_BUFFER_SIZE]; + + snprintf(line_buffer, + LINE_BUFFER_SIZE, + "BB_NODE_FEATURES " + "node_id=%d depth=%d time=%.6f " + "n_rows=%d n_cols=%d n_nnz=%d density=%.6f " + "n_bin=%d n_int=%d n_cont=%d int_ratio=%.4f " + "avg_row_nnz=%.2f max_row_nnz=%d row_nnz_cv=%.4f " + "avg_col_nnz=%.2f max_col_nnz=%d col_nnz_cv=%.4f " + "n_bounds_chg=%d cutoff_gap=%.4f basis_from_parent=%d " + "simplex_iters=%d n_refact=%d lp_time=%.6f bound_str_time=%.6f var_sel_time=%.6f " + "n_frac=%d strong_branch=%d n_sb_cand=%d sb_time=%.6f " + "lp_status=%d node_status=%d\n", + last_features_.node_id, + last_features_.node_depth, + last_features_.total_node_time, + last_features_.n_rows, + last_features_.n_cols, + last_features_.n_nonzeros, + last_features_.density, + last_features_.n_binary, + last_features_.n_integer, + last_features_.n_continuous, + last_features_.integrality_ratio, + last_features_.avg_row_nnz, + last_features_.max_row_nnz, + last_features_.row_nnz_cv, + last_features_.avg_col_nnz, + last_features_.max_col_nnz, + last_features_.col_nnz_cv, + last_features_.n_bounds_changed, + last_features_.cutoff_gap_ratio, + last_features_.basis_from_parent ? 1 : 0, + last_features_.simplex_iterations, + last_features_.n_refactorizations, + last_features_.lp_solve_time, + last_features_.bound_str_time, + last_features_.variable_sel_time, + last_features_.n_fractional, + last_features_.strong_branch_performed ? 1 : 0, + last_features_.n_strong_branch_candidates, + last_features_.strong_branch_time, + last_features_.lp_status, + last_features_.node_status); + + // Single printf call + settings_.log.printf("%s", line_buffer); + + has_pending_features_ = false; +} + +template +void branch_and_bound_t::log_node_features( + const node_solve_features_t& features) +{ + mutex_feature_log_.lock(); + + f_t current_time = toc(stats_.start_time); + f_t time_since_last_log = current_time - last_feature_log_time_; + + // Always store the latest features + last_features_ = features; + has_pending_features_ = true; + + // Log if enough time has passed (500ms) + if (time_since_last_log >= FEATURE_LOG_INTERVAL) { + flush_pending_features(); + last_feature_log_time_ = current_time; + } + + mutex_feature_log_.unlock(); +} + template node_status_t branch_and_bound_t::solve_node(search_tree_t& search_tree, mip_node_t* node_ptr, @@ -554,7 +715,13 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& char thread_type) { raft::common::nvtx::range scope("BB::solve_node"); - f_t abs_fathom_tol = settings_.absolute_mip_gap_tol / 10; + + // Initialize feature tracking for this node + node_solve_features_t features = static_features_; + f_t node_start_time = tic(); + features.node_id = node_ptr->node_id; + features.node_depth = node_ptr->depth; + f_t abs_fathom_tol = settings_.absolute_mip_gap_tol / 10; lp_solution_t leaf_solution(leaf_problem.num_rows, leaf_problem.num_cols); std::vector& leaf_vstatus = node_ptr->vstatus; @@ -566,6 +733,20 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& // two vectors at each node and potentially cause memory issues node_ptr->get_variable_bounds(leaf_problem.lower, leaf_problem.upper, bounds_changed); + // Track how many bounds changed + for (const auto& changed : bounds_changed) { + if (changed) features.n_bounds_changed++; + } + + // Track cutoff gap ratio + if (upper_bound < inf) { + features.cutoff_gap_ratio = + (upper_bound - node_ptr->lower_bound) / std::max(std::abs(upper_bound), f_t(1.0)); + } + + // Track if we have parent's basis + features.basis_from_parent = !leaf_vstatus.empty(); + simplex_solver_settings_t lp_settings = settings_; lp_settings.set_log(false); lp_settings.cut_off = upper_bound + settings_.dual_tol; @@ -577,8 +758,10 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& bool feasible; { raft::common::nvtx::range scope_bs("BB::bound_strengthening"); + f_t bs_start_time = tic(); feasible = bound_strengthening(row_sense, lp_settings, leaf_problem, Arow, var_types_, bounds_changed); + features.bound_str_time = toc(bs_start_time); } dual::status_t lp_status = dual::status_t::DUAL_UNBOUNDED; @@ -608,8 +791,15 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& lp_status = convert_lp_status_to_dual_status(second_status); } - stats_.total_lp_solve_time += toc(lp_start_time); + f_t lp_time = toc(lp_start_time); + stats_.total_lp_solve_time += lp_time; stats_.total_lp_iters += node_iter; + + // Track LP solve metrics + features.lp_solve_time = lp_time; + features.simplex_iterations = node_iter; + // Note: We don't directly track refactorizations here, would need instrumentation in + // dual_phase2 } if (lp_status == dual::status_t::DUAL_UNBOUNDED) { @@ -617,6 +807,13 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& node_ptr->lower_bound = inf; search_tree.graphviz_node(log, node_ptr, "infeasible", 0.0); search_tree.update_tree(node_ptr, node_status_t::INFEASIBLE); + + // Log features before return + features.lp_status = static_cast(lp_status); + features.node_status = static_cast(node_status_t::INFEASIBLE); + features.total_node_time = toc(node_start_time); + log_node_features(features); + return node_status_t::INFEASIBLE; } else if (lp_status == dual::status_t::CUTOFF) { @@ -625,6 +822,13 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& f_t leaf_objective = compute_objective(leaf_problem, leaf_solution.x); search_tree.graphviz_node(log, node_ptr, "cut off", leaf_objective); search_tree.update_tree(node_ptr, node_status_t::FATHOMED); + + // Log features before return + features.lp_status = static_cast(lp_status); + features.node_status = static_cast(node_status_t::FATHOMED); + features.total_node_time = toc(node_start_time); + log_node_features(features); + return node_status_t::FATHOMED; } else if (lp_status == dual::status_t::OPTIMAL) { @@ -633,6 +837,8 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& i_t leaf_num_fractional = fractional_variables(settings_, leaf_solution.x, var_types_, leaf_fractional); + features.n_fractional = leaf_num_fractional; + f_t leaf_objective = compute_objective(leaf_problem, leaf_solution.x); node_ptr->lower_bound = leaf_objective; search_tree.graphviz_node(log, node_ptr, "lower bound", leaf_objective); @@ -643,27 +849,57 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& add_feasible_solution(leaf_objective, leaf_solution.x, node_ptr->depth, thread_type); search_tree.graphviz_node(log, node_ptr, "integer feasible", leaf_objective); search_tree.update_tree(node_ptr, node_status_t::INTEGER_FEASIBLE); + + // Log features before return + features.lp_status = static_cast(lp_status); + features.node_status = static_cast(node_status_t::INTEGER_FEASIBLE); + features.total_node_time = toc(node_start_time); + log_node_features(features); + return node_status_t::INTEGER_FEASIBLE; } else if (leaf_objective <= upper_bound + abs_fathom_tol) { // Choose fractional variable to branch on + f_t var_sel_start = tic(); const i_t branch_var = pc_.variable_selection(leaf_fractional, leaf_solution.x, lp_settings.log); + features.variable_sel_time = toc(var_sel_start); assert(leaf_vstatus.size() == leaf_problem.num_cols); search_tree.branch( node_ptr, branch_var, leaf_solution.x[branch_var], leaf_vstatus, original_lp_, log); node_ptr->status = node_status_t::HAS_CHILDREN; + + // Log features before return + features.lp_status = static_cast(lp_status); + features.node_status = static_cast(node_status_t::HAS_CHILDREN); + features.total_node_time = toc(node_start_time); + log_node_features(features); + return node_status_t::HAS_CHILDREN; } else { search_tree.graphviz_node(log, node_ptr, "fathomed", leaf_objective); search_tree.update_tree(node_ptr, node_status_t::FATHOMED); + + // Log features before return + features.lp_status = static_cast(lp_status); + features.node_status = static_cast(node_status_t::FATHOMED); + features.total_node_time = toc(node_start_time); + log_node_features(features); + return node_status_t::FATHOMED; } } else if (lp_status == dual::status_t::TIME_LIMIT) { search_tree.graphviz_node(log, node_ptr, "timeout", 0.0); search_tree.update_tree(node_ptr, node_status_t::TIME_LIMIT); + + // Log features before return + features.lp_status = static_cast(lp_status); + features.node_status = static_cast(node_status_t::TIME_LIMIT); + features.total_node_time = toc(node_start_time); + log_node_features(features); + return node_status_t::TIME_LIMIT; } else { @@ -680,6 +916,13 @@ node_status_t branch_and_bound_t::solve_node(search_tree_t& search_tree.graphviz_node(log, node_ptr, "numerical", 0.0); search_tree.update_tree(node_ptr, node_status_t::NUMERICAL); + + // Log features before return + features.lp_status = static_cast(lp_status); + features.node_status = static_cast(node_status_t::NUMERICAL); + features.total_node_time = toc(node_start_time); + log_node_features(features); + return node_status_t::NUMERICAL; } } @@ -1227,6 +1470,11 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } } + // Flush any pending features + mutex_feature_log_.lock(); + if (has_pending_features_) { flush_pending_features(); } + mutex_feature_log_.unlock(); + f_t lower_bound = heap_.size() > 0 ? heap_.top()->lower_bound : search_tree.root.lower_bound; return set_final_solution(solution, lower_bound); } diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index 5b304addd5..e60ce59cf4 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -45,6 +45,51 @@ enum class mip_exploration_status_t { template void upper_bound_callback(f_t upper_bound); +// Feature tracking for solve_node regression model +template +struct node_solve_features_t { + // Static problem features (compute once) + i_t n_rows{0}; + i_t n_cols{0}; + i_t n_nonzeros{0}; + f_t density{0.0}; + i_t n_binary{0}; + i_t n_integer{0}; + i_t n_continuous{0}; + f_t integrality_ratio{0.0}; + f_t avg_row_nnz{0.0}; + i_t max_row_nnz{0}; + f_t avg_col_nnz{0.0}; + i_t max_col_nnz{0}; + f_t row_nnz_cv{0.0}; + f_t col_nnz_cv{0.0}; + + // Dynamic node state + i_t node_id{0}; + i_t node_depth{0}; + i_t n_bounds_changed{0}; + f_t cutoff_gap_ratio{0.0}; + bool basis_from_parent{false}; + + // LP solve metrics + i_t simplex_iterations{0}; + i_t n_refactorizations{0}; + f_t lp_solve_time{0.0}; + f_t bound_str_time{0.0}; + f_t variable_sel_time{0.0}; + + // Outcome metrics + i_t n_fractional{0}; + bool strong_branch_performed{false}; + i_t n_strong_branch_candidates{0}; + f_t strong_branch_time{0.0}; + i_t lp_status{0}; // Convert dual::status_t to int + i_t node_status{0}; // Convert node_status_t to int + + // Computed at node end + f_t total_node_time{0.0}; +}; + template struct diving_root_t { mip_node_t node; @@ -203,6 +248,22 @@ class branch_and_bound_t { // its blocks the progression of the lower bound. omp_atomic_t lower_bound_ceiling_; + // Feature tracking for solve_node regression model + node_solve_features_t static_features_; // Static problem features, computed once + omp_mutex_t mutex_feature_log_; // Protect feature logging + node_solve_features_t last_features_; // Last captured features + f_t last_feature_log_time_{0.0}; // Time of last feature log + bool has_pending_features_{false}; // Whether we have features to log + + // Helper to compute static features once + void compute_static_features(); + + // Helper to log node solve features with time-based throttling + void log_node_features(const node_solve_features_t& features); + + // Helper to flush any pending features at end of solve + void flush_pending_features(); + // Set the final solution. mip_status_t set_final_solution(mip_solution_t& solution, f_t lower_bound); diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 39e79a1bb6..6bcec10bfb 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -355,7 +355,7 @@ solution_t diversity_manager_t::run_solver() population.allocate_solutions(); ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop if (!context.settings.deterministic) { -#if 1 +#if 0 ls.start_cpufj_scratch_threads(population); std::this_thread::sleep_for(std::chrono::seconds(30)); ls.stop_cpufj_scratch_threads(); @@ -458,7 +458,7 @@ solution_t diversity_manager_t::run_solver() lp_rounded_sol.round_nearest(); lp_rounded_sol.compute_feasibility(); population.add_solution(std::move(lp_rounded_sol)); - if (!context.settings.deterministic) { ls.start_cpufj_lptopt_scratch_threads(population); } + // if (!context.settings.deterministic) { ls.start_cpufj_lptopt_scratch_threads(population); } } population.add_solutions_from_vec(std::move(initial_sol_vector)); diff --git a/cpp/src/mip/local_search/local_search.cuh b/cpp/src/mip/local_search/local_search.cuh index 09aafa54fe..c1c11fd39e 100644 --- a/cpp/src/mip/local_search/local_search.cuh +++ b/cpp/src/mip/local_search/local_search.cuh @@ -142,8 +142,8 @@ class local_search_t { feasibility_pump_t fp; std::mt19937 rng; - std::array, 8> ls_cpu_fj; - std::array, 1> scratch_cpu_fj; + std::array, 0> ls_cpu_fj; + std::array, 0> scratch_cpu_fj; cpu_fj_thread_t scratch_cpu_fj_on_lp_opt; problem_t problem_with_objective_cut; bool cutting_plane_added_for_active_run{false}; diff --git a/cpp/src/utilities/timer.hpp b/cpp/src/utilities/timer.hpp index 77b48ea780..bb74e75b97 100644 --- a/cpp/src/utilities/timer.hpp +++ b/cpp/src/utilities/timer.hpp @@ -46,7 +46,7 @@ class timer_t { line, caller); assert(false && "unexpected timer"); - __builtin_trap(); + //__builtin_trap(); } return elapsed; } From 5c9e59ae0a3da957f8a66237643046fa1236fc19 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 12 Nov 2025 14:39:03 +0000 Subject: [PATCH 073/225] add memory instrumentation wrappers --- cpp/src/dual_simplex/branch_and_bound.cpp | 92 +-- cpp/src/mip/diversity/diversity_manager.cu | 2 +- .../mip/feasibility_jump/feasibility_jump.cuh | 6 + .../feasibility_jump_impl_common.cuh | 22 +- .../feasibility_jump_kernels.cu | 14 +- cpp/src/mip/feasibility_jump/fj_cpu.cu | 322 +++++++-- cpp/src/mip/feasibility_jump/fj_cpu.cuh | 93 ++- cpp/src/mip/local_search/local_search.cuh | 2 +- cpp/src/utilities/memory_instrumentation.hpp | 673 ++++++++++++++++++ scripts/determinism_logs_parse.py | 19 +- scripts/train_regressor.py | 25 +- 11 files changed, 1113 insertions(+), 157 deletions(-) create mode 100644 cpp/src/utilities/memory_instrumentation.hpp diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 36b8aa7c5a..49953a926c 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -633,52 +633,52 @@ void branch_and_bound_t::flush_pending_features() constexpr int LINE_BUFFER_SIZE = 512; char line_buffer[LINE_BUFFER_SIZE]; - snprintf(line_buffer, - LINE_BUFFER_SIZE, - "BB_NODE_FEATURES " - "node_id=%d depth=%d time=%.6f " - "n_rows=%d n_cols=%d n_nnz=%d density=%.6f " - "n_bin=%d n_int=%d n_cont=%d int_ratio=%.4f " - "avg_row_nnz=%.2f max_row_nnz=%d row_nnz_cv=%.4f " - "avg_col_nnz=%.2f max_col_nnz=%d col_nnz_cv=%.4f " - "n_bounds_chg=%d cutoff_gap=%.4f basis_from_parent=%d " - "simplex_iters=%d n_refact=%d lp_time=%.6f bound_str_time=%.6f var_sel_time=%.6f " - "n_frac=%d strong_branch=%d n_sb_cand=%d sb_time=%.6f " - "lp_status=%d node_status=%d\n", - last_features_.node_id, - last_features_.node_depth, - last_features_.total_node_time, - last_features_.n_rows, - last_features_.n_cols, - last_features_.n_nonzeros, - last_features_.density, - last_features_.n_binary, - last_features_.n_integer, - last_features_.n_continuous, - last_features_.integrality_ratio, - last_features_.avg_row_nnz, - last_features_.max_row_nnz, - last_features_.row_nnz_cv, - last_features_.avg_col_nnz, - last_features_.max_col_nnz, - last_features_.col_nnz_cv, - last_features_.n_bounds_changed, - last_features_.cutoff_gap_ratio, - last_features_.basis_from_parent ? 1 : 0, - last_features_.simplex_iterations, - last_features_.n_refactorizations, - last_features_.lp_solve_time, - last_features_.bound_str_time, - last_features_.variable_sel_time, - last_features_.n_fractional, - last_features_.strong_branch_performed ? 1 : 0, - last_features_.n_strong_branch_candidates, - last_features_.strong_branch_time, - last_features_.lp_status, - last_features_.node_status); - - // Single printf call - settings_.log.printf("%s", line_buffer); + // snprintf(line_buffer, + // LINE_BUFFER_SIZE, + // "BB_NODE_FEATURES " + // "node_id=%d depth=%d time=%.6f " + // "n_rows=%d n_cols=%d n_nnz=%d density=%.6f " + // "n_bin=%d n_int=%d n_cont=%d int_ratio=%.4f " + // "avg_row_nnz=%.2f max_row_nnz=%d row_nnz_cv=%.4f " + // "avg_col_nnz=%.2f max_col_nnz=%d col_nnz_cv=%.4f " + // "n_bounds_chg=%d cutoff_gap=%.4f basis_from_parent=%d " + // "simplex_iters=%d n_refact=%d lp_time=%.6f bound_str_time=%.6f var_sel_time=%.6f " + // "n_frac=%d strong_branch=%d n_sb_cand=%d sb_time=%.6f " + // "lp_status=%d node_status=%d\n", + // last_features_.node_id, + // last_features_.node_depth, + // last_features_.total_node_time, + // last_features_.n_rows, + // last_features_.n_cols, + // last_features_.n_nonzeros, + // last_features_.density, + // last_features_.n_binary, + // last_features_.n_integer, + // last_features_.n_continuous, + // last_features_.integrality_ratio, + // last_features_.avg_row_nnz, + // last_features_.max_row_nnz, + // last_features_.row_nnz_cv, + // last_features_.avg_col_nnz, + // last_features_.max_col_nnz, + // last_features_.col_nnz_cv, + // last_features_.n_bounds_changed, + // last_features_.cutoff_gap_ratio, + // last_features_.basis_from_parent ? 1 : 0, + // last_features_.simplex_iterations, + // last_features_.n_refactorizations, + // last_features_.lp_solve_time, + // last_features_.bound_str_time, + // last_features_.variable_sel_time, + // last_features_.n_fractional, + // last_features_.strong_branch_performed ? 1 : 0, + // last_features_.n_strong_branch_candidates, + // last_features_.strong_branch_time, + // last_features_.lp_status, + // last_features_.node_status); + + // // Single printf call + // settings_.log.printf("%s", line_buffer); has_pending_features_ = false; } diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 6bcec10bfb..82c51fe648 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -355,7 +355,7 @@ solution_t diversity_manager_t::run_solver() population.allocate_solutions(); ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop if (!context.settings.deterministic) { -#if 0 +#if 1 ls.start_cpufj_scratch_threads(population); std::this_thread::sleep_for(std::chrono::seconds(30)); ls.stop_cpufj_scratch_threads(); diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh index e65828ea8f..ae06d2e178 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cuh +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cuh @@ -136,6 +136,12 @@ struct fj_staged_score_t { int32_t base{std::numeric_limits::lowest()}; int32_t bonus{std::numeric_limits::lowest()}; + fj_staged_score_t() = default; + fj_staged_score_t(const fj_staged_score_t&) = default; + fj_staged_score_t(fj_staged_score_t&&) = default; + fj_staged_score_t& operator=(const fj_staged_score_t&) = default; + fj_staged_score_t& operator=(fj_staged_score_t&&) = default; + HDI bool operator<(fj_staged_score_t other) const noexcept { return base == other.base ? bonus < other.bonus : base < other.base; diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_impl_common.cuh b/cpp/src/mip/feasibility_jump/feasibility_jump_impl_common.cuh index fbc5a7b395..95de8bc1eb 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_impl_common.cuh +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_impl_common.cuh @@ -28,20 +28,22 @@ HDI f_t fj_kahan_babushka_neumaier_sum(Iterator begin, Iterator end) } // Returns the current slack, and the variable delta that would nullify this slack ("tighten" it) -template +template HDI thrust::tuple get_mtm_for_bound( const typename fj_t::climber_data_t::view_t& fj, i_t var_idx, i_t cstr_idx, f_t cstr_coeff, f_t bound, - f_t sign) + f_t sign, + const ArrayType& assignment, + const ArrayType& lhs_vector) { f_t delta_ij = 0; f_t slack = 0; - f_t old_val = fj.incumbent_assignment[var_idx]; + f_t old_val = assignment[var_idx]; - f_t lhs = fj.incumbent_lhs[cstr_idx] * sign; + f_t lhs = lhs_vector[cstr_idx] * sign; f_t rhs = bound * sign; slack = rhs - lhs; // bound might be infinite. let the caller handle this case @@ -50,22 +52,24 @@ HDI thrust::tuple get_mtm_for_bound( return {delta_ij, slack}; } -template +template HDI thrust::tuple get_mtm_for_constraint( const typename fj_t::climber_data_t::view_t& fj, i_t var_idx, i_t cstr_idx, f_t cstr_coeff, f_t c_lb, - f_t c_ub) + f_t c_ub, + const ArrayType& assignment, + const ArrayType& lhs_vector) { f_t sign = -1; f_t delta_ij = 0; f_t slack = 0; - f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx); + f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx, c_lb, c_ub); - f_t old_val = fj.incumbent_assignment[var_idx]; + f_t old_val = assignment[var_idx]; // process each bound as two separate constraints f_t bounds[2] = {c_lb, c_ub}; @@ -77,7 +81,7 @@ HDI thrust::tuple get_mtm_for_constraint( // factor to correct the lhs/rhs to turn a lb <= lhs <= ub constraint into // two virtual constraints lhs <= ub and -lhs <= -lb sign = bound_idx == 0 ? -1 : 1; - f_t lhs = fj.incumbent_lhs[cstr_idx] * sign; + f_t lhs = lhs_vector[cstr_idx] * sign; f_t rhs = bounds[bound_idx] * sign; slack = rhs - lhs; diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index a7195fabea..584d3d8540 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -308,8 +308,8 @@ DI std::pair::move_score_info_t> compute_best_mtm( f_t c_lb = fj.pb.constraint_lower_bounds[cstr_idx]; f_t c_ub = fj.pb.constraint_upper_bounds[cstr_idx]; f_t new_val; - auto [delta_ij, sign, slack, cstr_tolerance] = - get_mtm_for_constraint(fj, var_idx, cstr_idx, cstr_coeff, c_lb, c_ub); + auto [delta_ij, sign, slack, cstr_tolerance] = get_mtm_for_constraint( + fj, var_idx, cstr_idx, cstr_coeff, c_lb, c_ub, fj.incumbent_assignment, fj.incumbent_lhs); if (fj.pb.is_integer_var(var_idx)) { new_val = cstr_coeff * sign > 0 ? floor(old_val + delta_ij + fj.pb.tolerances.integrality_tolerance) @@ -750,8 +750,14 @@ DI void update_lift_moves(typename fj_t::climber_data_t::view_t fj) // Process each bound separately, as both are satified and may both be finite // otherwise range constraints aren't correctly handled for (auto [bound, sign] : {std::make_tuple(c_lb, -1), std::make_tuple(c_ub, 1)}) { - auto [delta, slack] = - get_mtm_for_bound(fj, var_idx, cstr_idx, cstr_coeff, bound, sign); + auto [delta, slack] = get_mtm_for_bound(fj, + var_idx, + cstr_idx, + cstr_coeff, + bound, + sign, + fj.incumbent_assignment, + fj.incumbent_lhs); if (cstr_coeff * sign < 0) { if (fj.pb.is_integer_var(var_idx)) delta = ceil(delta); diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index 262bd2f35f..bee5d7c96e 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -5,6 +5,28 @@ */ /* clang-format on */ +/* + * MEMORY OPERATION ANNOTATIONS: + * + * This file contains detailed comments marking all memory operations (array reads and writes) + * within loops for the CPU Feasibility Jump (CPUFJ) algorithm. These annotations are intended + * to help estimate the memory bandwidth requirements and runtime of this memory-bound workload. + * + * Key annotations: + * - "MEMORY OPS:" marks the start of a loop that performs memory operations + * - "ARRAY READ:" marks array read operations with array name and count per iteration + * - "ARRAY WRITE:" marks array write operations with array name and count per iteration + * - "CRITICAL LOOP" or "HOTTEST LOOP" marks the most frequently executed loops + * - "Total per iteration:" summarizes memory ops per loop iteration + * + * Important notation: + * - n_vars: number of variables + * - n_cstrs: number of constraints + * - avg_var_degree: average number of constraints per variable + * - avg_cstr_degree: average number of variables per constraint + * - total_nnz: total non-zeros in constraint matrix + */ + #include #include "feasibility_jump.cuh" @@ -258,12 +280,16 @@ static void precompute_problem_features(fj_cpu_climber_t& fj_cpu) // Count variable types - use host vectors fj_cpu.n_binary_vars = 0; fj_cpu.n_integer_vars = 0; + // MEMORY OPS: Loop over all variables (n_vars iterations) for (i_t i = 0; i < (i_t)fj_cpu.h_is_binary_variable.size(); i++) { + // ARRAY READ: h_is_binary_variable[i] - 1 read per iteration if (fj_cpu.h_is_binary_variable[i]) { fj_cpu.n_binary_vars++; } else if (fj_cpu.h_var_types[i] == var_t::INTEGER) { + // ARRAY READ: h_var_types[i] - 1 read per iteration (conditional) fj_cpu.n_integer_vars++; } + // Total per iteration: 2 array reads } i_t total_nnz = fj_cpu.h_reverse_offsets.back(); @@ -275,17 +301,24 @@ static void precompute_problem_features(fj_cpu_climber_t& fj_cpu) // Compute variable degree statistics (max, cv) fj_cpu.max_var_degree = 0; std::vector var_degrees(n_vars); + // MEMORY OPS: Loop over all variables (n_vars iterations) for (i_t i = 0; i < n_vars; i++) { - i_t degree = fj_cpu.h_reverse_offsets[i + 1] - fj_cpu.h_reverse_offsets[i]; + // ARRAY READ: h_reverse_offsets[i] and h_reverse_offsets[i+1] - 2 reads per iteration + i_t degree = fj_cpu.h_reverse_offsets[i + 1] - fj_cpu.h_reverse_offsets[i]; + // ARRAY WRITE: var_degrees[i] - 1 write per iteration var_degrees[i] = degree; fj_cpu.max_var_degree = std::max(fj_cpu.max_var_degree, degree); + // Total per iteration: 2 reads + 1 write = 3 memory ops } // Compute variable degree coefficient of variation double var_deg_variance = 0.0; + // MEMORY OPS: Loop over all variables (n_vars iterations) for (i_t i = 0; i < n_vars; i++) { + // ARRAY READ: var_degrees[i] - 1 read per iteration double diff = var_degrees[i] - fj_cpu.avg_var_degree; var_deg_variance += diff * diff; + // Total per iteration: 1 read } var_deg_variance /= n_vars; double var_degree_std = std::sqrt(var_deg_variance); @@ -296,17 +329,24 @@ static void precompute_problem_features(fj_cpu_climber_t& fj_cpu) // Compute constraint degree statistics (max, cv) fj_cpu.max_cstr_degree = 0; std::vector cstr_degrees(n_cstrs); + // MEMORY OPS: Loop over all constraints (n_cstrs iterations) for (i_t i = 0; i < n_cstrs; i++) { - i_t degree = fj_cpu.h_offsets[i + 1] - fj_cpu.h_offsets[i]; + // ARRAY READ: h_offsets[i] and h_offsets[i+1] - 2 reads per iteration + i_t degree = fj_cpu.h_offsets[i + 1] - fj_cpu.h_offsets[i]; + // ARRAY WRITE: cstr_degrees[i] - 1 write per iteration cstr_degrees[i] = degree; fj_cpu.max_cstr_degree = std::max(fj_cpu.max_cstr_degree, degree); + // Total per iteration: 2 reads + 1 write = 3 memory ops } // Compute constraint degree coefficient of variation double cstr_deg_variance = 0.0; + // MEMORY OPS: Loop over all constraints (n_cstrs iterations) for (i_t i = 0; i < n_cstrs; i++) { + // ARRAY READ: cstr_degrees[i] - 1 read per iteration double diff = cstr_degrees[i] - fj_cpu.avg_cstr_degree; cstr_deg_variance += diff * diff; + // Total per iteration: 1 read } cstr_deg_variance /= n_cstrs; double cstr_degree_std = std::sqrt(cstr_deg_variance); @@ -319,7 +359,9 @@ static void precompute_problem_features(fj_cpu_climber_t& fj_cpu) template static void log_regression_features(fj_cpu_climber_t& fj_cpu, double time_window_ms, - double total_time_ms) + double total_time_ms, + size_t mem_loads_bytes, + size_t mem_stores_bytes) { i_t total_nnz = fj_cpu.h_reverse_offsets.back(); i_t n_vars = fj_cpu.h_reverse_offsets.size() - 1; @@ -394,6 +436,12 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, } #endif + // Compute memory statistics + double mem_loads_mb = mem_loads_bytes / 1e6; + double mem_stores_mb = mem_stores_bytes / 1e6; + double mem_total_mb = (mem_loads_bytes + mem_stores_bytes) / 1e6; + double mem_bandwidth_gb_per_sec = (mem_total_mb / 1000.0) / (time_window_ms / 1000.0); + // Print everything on a single line using precomputed features CUOPT_LOG_DEBUG( "%sCPUFJ_FEATURES iter=%d time_window=%.2f " @@ -408,7 +456,8 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, "cstr_reuse=%.2f var_reuse=%.2f working_set_kb=%.1f " "cstr_coverage=%.4f var_coverage=%.4f " "L1_miss=%.2f L3_miss=%.2f loads_per_iter=%.0f stores_per_iter=%.0f " - "viol_ratio=%.4f nnz_per_move=%.2f eval_intensity=%.2f", + "viol_ratio=%.4f nnz_per_move=%.2f eval_intensity=%.2f " + "mem_loads_mb=%.3f mem_stores_mb=%.3f mem_total_mb=%.3f mem_bandwidth_gb_s=%.3f", fj_cpu.log_prefix.c_str(), fj_cpu.iterations, time_window_ms, @@ -449,7 +498,11 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, stores_per_iter, violated_ratio, nnz_per_move, - eval_intensity); + eval_intensity, + mem_loads_mb, + mem_stores_mb, + mem_total_mb, + mem_bandwidth_gb_per_sec); // Reset window counters fj_cpu.nnz_processed_window = 0; @@ -483,16 +536,16 @@ static inline bool tabu_check(fj_cpu_climber_t& fj_cpu, } template -static bool check_variable_feasibility(const typename fj_t::climber_data_t::view_t& fj, +static bool check_variable_feasibility(const fj_cpu_climber_t& fj_cpu, bool check_integer = true) { - for (i_t var_idx = 0; var_idx < fj.pb.n_variables; var_idx += 1) { - auto val = fj.incumbent_assignment[var_idx]; - bool feasible = fj.pb.check_variable_within_bounds(var_idx, val); + for (i_t var_idx = 0; var_idx < fj_cpu.view.pb.n_variables; var_idx += 1) { + auto val = fj_cpu.h_assignment[var_idx]; + bool feasible = fj_cpu.view.pb.check_variable_within_bounds(var_idx, val); if (!feasible) return false; - if (check_integer && fj.pb.is_integer_var(var_idx) && - !fj.pb.is_integer(fj.incumbent_assignment[var_idx])) + if (check_integer && fj_cpu.view.pb.is_integer_var(var_idx) && + !fj_cpu.view.pb.is_integer(fj_cpu.h_assignment[var_idx])) return false; } return true; @@ -505,6 +558,7 @@ static inline std::pair compute_score(fj_cpu_climber_t timer(fj_cpu.compute_score_times); + // ARRAY READ: h_obj_coeffs[var_idx] - 1 read f_t obj_diff = fj_cpu.h_obj_coeffs[var_idx] * delta; cuopt_assert(isfinite(delta), ""); @@ -517,19 +571,29 @@ static inline std::pair compute_score(fj_cpu_climber_t( fj_cpu.view, var_idx, delta, cstr_idx, cstr_coeff, c_lb, c_ub, fj_cpu.h_lhs[cstr_idx]); base_feas_sum += cstr_base_feas; bonus_robust_sum += cstr_bonus_robust; + // Total per iteration: ~6-7 array reads (h_reverse_constraints, h_reverse_coefficients, + // cached_cstr_bounds (2 values), h_lhs, h_cstr_left_weights, h_cstr_right_weights) } f_t base_obj = 0; @@ -557,15 +621,21 @@ static inline std::pair compute_score(fj_cpu_climber_t static void smooth_weights(fj_cpu_climber_t& fj_cpu) { + // MEMORY OPS: Loop over all constraints (n_cstrs iterations) for (i_t cstr_idx = 0; cstr_idx < fj_cpu.view.pb.n_constraints; cstr_idx++) { // consider only satisfied constraints if (fj_cpu.violated_constraints.count(cstr_idx)) continue; + // ARRAY READ: h_cstr_left_weights[cstr_idx] - 1 read per iteration (if not violated) f_t weight_l = max((f_t)0, fj_cpu.h_cstr_left_weights[cstr_idx] - 1); + // ARRAY READ: h_cstr_right_weights[cstr_idx] - 1 read per iteration (if not violated) f_t weight_r = max((f_t)0, fj_cpu.h_cstr_right_weights[cstr_idx] - 1); - fj_cpu.h_cstr_left_weights[cstr_idx] = weight_l; + // ARRAY WRITE: h_cstr_left_weights[cstr_idx] - 1 write per iteration (if not violated) + fj_cpu.h_cstr_left_weights[cstr_idx] = weight_l; + // ARRAY WRITE: h_cstr_right_weights[cstr_idx] - 1 write per iteration (if not violated) fj_cpu.h_cstr_right_weights[cstr_idx] = weight_r; + // Total per iteration (for satisfied constraints): 2 reads + 2 writes = 4 memory ops } if (fj_cpu.h_objective_weight > 0 && fj_cpu.h_incumbent_objective >= fj_cpu.h_best_objective) { @@ -586,18 +656,24 @@ static void update_weights(fj_cpu_climber_t& fj_cpu) return; } + // MEMORY OPS: Loop over violated constraints (typically small: <100 iterations) for (auto cstr_idx : fj_cpu.violated_constraints) { + // ARRAY READ: h_lhs[cstr_idx] - 1 read per iteration f_t curr_incumbent_lhs = fj_cpu.h_lhs[cstr_idx]; + // ARRAY READ: h_cstr_lb[cstr_idx] - 1 read per iteration f_t curr_lower_excess = fj_cpu.view.lower_excess_score(cstr_idx, curr_incumbent_lhs, fj_cpu.h_cstr_lb[cstr_idx]); + // ARRAY READ: h_cstr_ub[cstr_idx] - 1 read per iteration f_t curr_upper_excess = fj_cpu.view.upper_excess_score(cstr_idx, curr_incumbent_lhs, fj_cpu.h_cstr_ub[cstr_idx]); f_t curr_excess_score = curr_lower_excess + curr_upper_excess; f_t old_weight; if (curr_lower_excess < 0.) { + // ARRAY READ: h_cstr_left_weights[cstr_idx] - 1 read per iteration (conditional) old_weight = fj_cpu.h_cstr_left_weights[cstr_idx]; } else { + // ARRAY READ: h_cstr_right_weights[cstr_idx] - 1 read per iteration (conditional) old_weight = fj_cpu.h_cstr_right_weights[cstr_idx]; } @@ -610,18 +686,25 @@ static void update_weights(fj_cpu_climber_t& fj_cpu) new_weight = round(new_weight); if (curr_lower_excess < 0.) { + // ARRAY WRITE: h_cstr_left_weights[cstr_idx] - 1 write per iteration (conditional) fj_cpu.h_cstr_left_weights[cstr_idx] = new_weight; fj_cpu.max_weight = max(fj_cpu.max_weight, new_weight); } else { + // ARRAY WRITE: h_cstr_right_weights[cstr_idx] - 1 write per iteration (conditional) fj_cpu.h_cstr_right_weights[cstr_idx] = new_weight; fj_cpu.max_weight = max(fj_cpu.max_weight, new_weight); } // Invalidate related cached move scores auto [relvar_offset_begin, relvar_offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + // MEMORY OPS: Inner loop over variables in this constraint (avg_cstr_degree iterations per + // outer iteration) for (auto i = relvar_offset_begin; i < relvar_offset_end; i++) { + // ARRAY WRITE: cached_mtm_moves[i].first - 1 write per inner iteration fj_cpu.cached_mtm_moves[i].first = 0; + // Total per inner iteration: 1 write } + // Total per outer iteration: 4 reads + 1 write + (avg_cstr_degree writes in inner loop) } if (fj_cpu.violated_constraints.empty()) { fj_cpu.h_objective_weight += 1; } @@ -648,27 +731,38 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, i_t previous_viol = fj_cpu.violated_constraints.size(); + // MEMORY OPS: CRITICAL LOOP - Loop over all constraints involving this variable (avg_var_degree + // iterations) This loop is called ONCE PER ITERATION and updates constraint LHS values for (auto i = offset_begin; i < offset_end; i++) { cuopt_assert(i < (i_t)fj_cpu.h_reverse_constraints.size(), ""); - auto [c_lb, c_ub] = fj_cpu.cached_cstr_bounds[i]; + // ARRAY READ: cached_cstr_bounds[i] - 1 read per iteration (reads 2 f_t values) + auto [c_lb, c_ub] = fj_cpu.cached_cstr_bounds[i].get(); + // ARRAY READ: h_reverse_constraints[i] - 1 read per iteration auto cstr_idx = fj_cpu.h_reverse_constraints[i]; fj_cpu.unique_cstrs_accessed_window.insert(cstr_idx); + // ARRAY READ: h_reverse_coefficients[i] - 1 read per iteration auto cstr_coeff = fj_cpu.h_reverse_coefficients[i]; + // ARRAY READ: h_lhs[cstr_idx] - 1 read per iteration (indirect indexing) f_t old_lhs = fj_cpu.h_lhs[cstr_idx]; // Kahan compensated summation - f_t y = cstr_coeff * delta - fj_cpu.h_lhs_sumcomp[cstr_idx]; - f_t t = old_lhs + y; + // ARRAY READ: h_lhs_sumcomp[cstr_idx] - 1 read per iteration + f_t y = cstr_coeff * delta - fj_cpu.h_lhs_sumcomp[cstr_idx]; + f_t t = old_lhs + y; + // ARRAY WRITE: h_lhs_sumcomp[cstr_idx] - 1 write per iteration fj_cpu.h_lhs_sumcomp[cstr_idx] = (t - old_lhs) - y; - fj_cpu.h_lhs[cstr_idx] = t; - f_t new_lhs = fj_cpu.h_lhs[cstr_idx]; - f_t old_cost = fj_cpu.view.excess_score(cstr_idx, old_lhs, c_lb, c_ub); - f_t new_cost = fj_cpu.view.excess_score(cstr_idx, new_lhs, c_lb, c_ub); - f_t cstr_tolerance = fj_cpu.view.get_corrected_tolerance(cstr_idx, c_lb, c_ub); + // ARRAY WRITE: h_lhs[cstr_idx] - 1 write per iteration + fj_cpu.h_lhs[cstr_idx] = t; + // ARRAY READ: h_lhs[cstr_idx] - 1 read per iteration (just written) + f_t new_lhs = fj_cpu.h_lhs[cstr_idx]; + f_t old_cost = fj_cpu.view.excess_score(cstr_idx, old_lhs, c_lb, c_ub); + f_t new_cost = fj_cpu.view.excess_score(cstr_idx, new_lhs, c_lb, c_ub); + f_t cstr_tolerance = fj_cpu.view.get_corrected_tolerance(cstr_idx, c_lb, c_ub); // trigger early lhs recomputation if the sumcomp term gets too large // to avoid large numerical errors + // ARRAY READ: h_lhs_sumcomp[cstr_idx] - 1 read per iteration if (fabs(fj_cpu.h_lhs_sumcomp[cstr_idx]) > BIGVAL_THRESHOLD) fj_cpu.trigger_early_lhs_recomputation = true; @@ -687,9 +781,14 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, // Invalidate related cached move scores auto [relvar_offset_begin, relvar_offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + // MEMORY OPS: Inner loop over variables in this constraint (avg_cstr_degree iterations per + // outer iteration) for (auto i = relvar_offset_begin; i < relvar_offset_end; i++) { + // ARRAY WRITE: cached_mtm_moves[i].first - 1 write per inner iteration fj_cpu.cached_mtm_moves[i].first = 0; + // Total per inner iteration: 1 write } + // Total per outer iteration: 7 reads + 3 writes + (avg_cstr_degree writes in inner loop) } if (previous_viol > 0 && fj_cpu.violated_constraints.empty()) { @@ -697,27 +796,31 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, } // update the assignment and objective proper + // ARRAY READ: h_assignment[var_idx] - 1 read f_t new_val = fj_cpu.h_assignment[var_idx] + delta; if (fj_cpu.view.pb.is_integer_var(var_idx)) { cuopt_assert(fj_cpu.view.pb.integer_equal(new_val, round(new_val)), "new_val is not integer"); new_val = round(new_val); } + // ARRAY WRITE: h_assignment[var_idx] - 1 write fj_cpu.h_assignment[var_idx] = new_val; cuopt_assert(fj_cpu.view.pb.check_variable_within_bounds(var_idx, new_val), "assignment not within bounds"); cuopt_assert(isfinite(new_val), "assignment is not finite"); + // ARRAY READ: h_obj_coeffs[var_idx] - 1 read fj_cpu.h_incumbent_objective += fj_cpu.h_obj_coeffs[var_idx] * delta; if (fj_cpu.h_incumbent_objective < fj_cpu.h_best_objective && fj_cpu.violated_constraints.empty()) { // recompute the LHS values to cancel out accumulation errors, then check if feasibility remains recompute_lhs(fj_cpu); - if (fj_cpu.violated_constraints.empty() && check_variable_feasibility(fj_cpu.view)) { + if (fj_cpu.violated_constraints.empty() && check_variable_feasibility(fj_cpu)) { cuopt_assert(fj_cpu.satisfied_constraints.size() == fj_cpu.view.pb.n_constraints, ""); fj_cpu.h_best_objective = fj_cpu.h_incumbent_objective - fj_cpu.settings.parameters.breakthrough_move_epsilon; + // ARRAY WRITE: h_best_assignment = h_assignment - n_vars writes (vector copy) fj_cpu.h_best_assignment = fj_cpu.h_assignment; fj_cpu.iterations_since_best = 0; // Reset counter on improvement CUOPT_LOG_TRACE("%sCPUFJ: new best objective: %g", @@ -734,18 +837,25 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, rng.next_u32() % (fj_cpu.settings.parameters.tabu_tenure_max - fj_cpu.settings.parameters.tabu_tenure_min); if (delta > 0) { - fj_cpu.h_tabu_lastinc[var_idx] = fj_cpu.iterations; + // ARRAY WRITE: h_tabu_lastinc[var_idx] - 1 write + fj_cpu.h_tabu_lastinc[var_idx] = fj_cpu.iterations; + // ARRAY WRITE: h_tabu_nodec_until[var_idx] - 1 write fj_cpu.h_tabu_nodec_until[var_idx] = fj_cpu.iterations + tabu_tenure; + // ARRAY WRITE: h_tabu_noinc_until[var_idx] - 1 write fj_cpu.h_tabu_noinc_until[var_idx] = fj_cpu.iterations + tabu_tenure / 2; // CUOPT_LOG_TRACE("CPU: tabu nodec_until: %d", fj_cpu.h_tabu_nodec_until[var_idx]); } else { - fj_cpu.h_tabu_lastdec[var_idx] = fj_cpu.iterations; + // ARRAY WRITE: h_tabu_lastdec[var_idx] - 1 write + fj_cpu.h_tabu_lastdec[var_idx] = fj_cpu.iterations; + // ARRAY WRITE: h_tabu_noinc_until[var_idx] - 1 write fj_cpu.h_tabu_noinc_until[var_idx] = fj_cpu.iterations + tabu_tenure; + // ARRAY WRITE: h_tabu_nodec_until[var_idx] - 1 write fj_cpu.h_tabu_nodec_until[var_idx] = fj_cpu.iterations + tabu_tenure / 2; - // CUOPT_LOG_TRACE("CPU: tabu noinc_until: %d", fj_cpu.h_tabu_noinc_until[var_idx]); + // CUOPT_LOG_TRACE("CPU: tabu noinc_until: %d", fj_cpu.h_tabu_nodec_until[var_idx]); } - + // ARRAY WRITE: flip_move_computed - n_vars writes (fill with false) std::fill(fj_cpu.flip_move_computed.begin(), fj_cpu.flip_move_computed.end(), false); + // ARRAY WRITE: var_bitmap - n_vars writes (fill with false) std::fill(fj_cpu.var_bitmap.begin(), fj_cpu.var_bitmap.end(), false); fj_cpu.iter_mtm_vars.clear(); } @@ -762,13 +872,20 @@ static thrust::tuple find_mtm_move( fj_staged_score_t best_score = fj_staged_score_t::invalid(); // collect all the variables that are involved in the target constraints + // MEMORY OPS: Outer loop over target constraints (sample_size iterations, typically 15-100) for (size_t cstr_idx : target_cstrs) { auto [offset_begin, offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + // MEMORY OPS: Inner loop over variables in each constraint (avg_cstr_degree per outer + // iteration) for (auto i = offset_begin; i < offset_end; i++) { + // ARRAY READ: h_variables[i] - 1 read per iteration i_t var_idx = fj_cpu.h_variables[i]; + // ARRAY READ: var_bitmap[var_idx] - 1 read per iteration if (fj_cpu.var_bitmap[var_idx]) continue; fj_cpu.iter_mtm_vars.push_back(var_idx); + // ARRAY WRITE: var_bitmap[var_idx] - 1 write per iteration (if not already set) fj_cpu.var_bitmap[var_idx] = true; + // Total per inner iteration: 2 reads + 1 write (conditional) } } // estimate the amount of nnzs to consider @@ -781,16 +898,22 @@ static thrust::tuple find_mtm_move( f_t nnz_pick_probability = 1; if (nnz_sum > fj_cpu.nnz_samples) nnz_pick_probability = (f_t)fj_cpu.nnz_samples / nnz_sum; + // MEMORY OPS: HOTTEST LOOP - Outer loop over target constraints (sample_size iterations) for (size_t cstr_idx : target_cstrs) { - f_t cstr_tol = fj_cpu.view.get_corrected_tolerance(cstr_idx); + auto [c_lb, c_ub] = fj_cpu.cached_cstr_bounds[cstr_idx].get(); + f_t cstr_tol = fj_cpu.view.get_corrected_tolerance(cstr_idx, c_lb, c_ub); cuopt_assert(cstr_idx < fj_cpu.h_cstr_lb.size(), "cstr_idx is out of bounds"); auto [offset_begin, offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + // MEMORY OPS: Inner loop over variables (avg_cstr_degree per outer iteration) for (auto i = offset_begin; i < offset_end; i++) { // early cached check + // ARRAY READ: cached_mtm_moves[i] - 1 read per iteration if (auto& cached_move = fj_cpu.cached_mtm_moves[i]; cached_move.first != 0) { if (best_score < cached_move.second) { + // ARRAY READ: h_variables[i] - 1 read per iteration (cache hit) auto var_idx = fj_cpu.h_variables[i]; + // ARRAY READ: h_assignment[var_idx] - 1 read per iteration (cache hit) if (fj_cpu.view.pb.check_variable_within_bounds( var_idx, fj_cpu.h_assignment[var_idx] + cached_move.first)) { best_score = cached_move.second; @@ -800,6 +923,7 @@ static thrust::tuple find_mtm_move( // fj_cpu.h_assignment[var_idx] + cached_move.first), "best move not within bounds"); } fj_cpu.hit_count++; + // Total per cache hit: 3 reads continue; } @@ -807,24 +931,39 @@ static thrust::tuple find_mtm_move( if (nnz_pick_probability < 1) if (rng.next_float() > nnz_pick_probability) continue; + // ARRAY READ: h_variables[i] - 1 read per iteration (cache miss) auto var_idx = fj_cpu.h_variables[i]; + // ARRAY READ: h_assignment[var_idx] - 1 read per iteration (cache miss) f_t val = fj_cpu.h_assignment[var_idx]; f_t new_val = val; f_t delta = 0; // Special case for binary variables + // ARRAY READ: h_is_binary_variable[var_idx] - 1 read per iteration if (fj_cpu.h_is_binary_variable[var_idx]) { + // ARRAY READ: flip_move_computed[var_idx] - 1 read per iteration (conditional) if (fj_cpu.flip_move_computed[var_idx]) continue; + // ARRAY WRITE: flip_move_computed[var_idx] - 1 write per iteration (conditional) fj_cpu.flip_move_computed[var_idx] = true; new_val = 1 - val; } else { + // ARRAY READ: h_coefficients[i] - 1 read per iteration (non-binary) auto cstr_coeff = fj_cpu.h_coefficients[i]; - f_t c_lb = fj_cpu.h_cstr_lb[cstr_idx]; - f_t c_ub = fj_cpu.h_cstr_ub[cstr_idx]; - auto [delta, sign, slack, cstr_tolerance] = get_mtm_for_constraint( - fj_cpu.view, var_idx, cstr_idx, cstr_coeff, c_lb, c_ub); + // ARRAY READ: h_cstr_lb[cstr_idx] - 1 read per iteration (non-binary) + f_t c_lb = fj_cpu.h_cstr_lb[cstr_idx]; + // ARRAY READ: h_cstr_ub[cstr_idx] - 1 read per iteration (non-binary) + f_t c_ub = fj_cpu.h_cstr_ub[cstr_idx]; + auto [delta, sign, slack, cstr_tolerance] = + get_mtm_for_constraint(fj_cpu.view, + var_idx, + cstr_idx, + cstr_coeff, + c_lb, + c_ub, + fj_cpu.h_assignment, + fj_cpu.h_lhs); if (fj_cpu.view.pb.is_integer_var(var_idx)) { new_val = cstr_coeff * sign > 0 ? floor(val + delta + fj_cpu.view.pb.tolerances.integrality_tolerance) @@ -833,10 +972,11 @@ static thrust::tuple find_mtm_move( new_val = val + delta; } // fallback - if (new_val < get_lower(fj_cpu.h_var_bounds[var_idx]) || - new_val > get_upper(fj_cpu.h_var_bounds[var_idx])) { - new_val = cstr_coeff * sign > 0 ? get_lower(fj_cpu.h_var_bounds[var_idx]) - : get_upper(fj_cpu.h_var_bounds[var_idx]); + // ARRAY READ: h_var_bounds[var_idx] - 1 read per iteration (non-binary, conditional) + if (new_val < get_lower(fj_cpu.h_var_bounds[var_idx].get()) || + new_val > get_upper(fj_cpu.h_var_bounds[var_idx].get())) { + new_val = cstr_coeff * sign > 0 ? get_lower(fj_cpu.h_var_bounds[var_idx].get()) + : get_upper(fj_cpu.h_var_bounds[var_idx].get()); } } if (!isfinite(new_val)) continue; @@ -844,15 +984,18 @@ static thrust::tuple find_mtm_move( "new_val is not within bounds"); delta = new_val - val; // more permissive tabu in the case of local minima - if (tabu_check(fj_cpu, var_idx, delta, localmin)) continue; + if (tabu_check(fj_cpu, var_idx, delta, localmin)) continue; if (fabs(delta) < cstr_tol) continue; auto move = fj_move_t{var_idx, delta}; cuopt_assert(move.var_idx < fj_cpu.h_assignment.size(), "move.var_idx is out of bounds"); cuopt_assert(move.var_idx >= 0, "move.var_idx is not positive"); + // CRITICAL: compute_score() does ~6-7 array reads per constraint (see compute_score + // annotations) auto [score, infeasibility] = compute_score(fj_cpu, var_idx, delta); - fj_cpu.cached_mtm_moves[i] = std::make_pair(delta, score); + // ARRAY WRITE: cached_mtm_moves[i] - 1 write per iteration (cache miss) + fj_cpu.cached_mtm_moves[i] = std::make_pair(delta, score); fj_cpu.miss_count++; // reject this move if it would increase the target variable to a numerically unstable value if (fj_cpu.view.move_numerically_stable( @@ -862,6 +1005,7 @@ static thrust::tuple find_mtm_move( best_move = move; } } + // Total per cache miss: ~8-11 reads + 1 write + compute_score overhead } } @@ -870,7 +1014,9 @@ static thrust::tuple find_mtm_move( fj_cpu.h_best_objective < std::numeric_limits::infinity() && fj_cpu.h_incumbent_objective >= fj_cpu.h_best_objective + fj_cpu.settings.parameters.breakthrough_move_epsilon) { + // MEMORY OPS: Loop over objective variables (num_obj_vars iterations, typically small) for (auto var_idx : fj_cpu.h_objective_vars) { + // ARRAY READ: h_assignment[var_idx] - 1 read per iteration f_t old_val = fj_cpu.h_assignment[var_idx]; f_t new_val = get_breakthrough_move(fj_cpu.view, var_idx); @@ -883,8 +1029,9 @@ static thrust::tuple find_mtm_move( cuopt_assert(move.var_idx < fj_cpu.h_assignment.size(), "move.var_idx is out of bounds"); cuopt_assert(move.var_idx >= 0, "move.var_idx is not positive"); - if (tabu_check(fj_cpu, var_idx, delta)) continue; + if (tabu_check(fj_cpu, var_idx, delta)) continue; + // CRITICAL: compute_score() does ~6-7 array reads per constraint involved auto [score, infeasibility] = compute_score(fj_cpu, var_idx, delta); cuopt_assert(fj_cpu.view.pb.check_variable_within_bounds(var_idx, new_val), ""); @@ -897,6 +1044,7 @@ static thrust::tuple find_mtm_move( best_move = move; } } + // Total per iteration: 1 read + compute_score overhead } } @@ -945,29 +1093,44 @@ static void recompute_lhs(fj_cpu_climber_t& fj_cpu) fj_cpu.violated_constraints.clear(); fj_cpu.satisfied_constraints.clear(); fj_cpu.total_violations = 0; + // MEMORY OPS: CRITICAL LOOP - Loop over all constraints (n_cstrs iterations) + // This is called periodically to recompute all LHS values from scratch for (i_t cstr_idx = 0; cstr_idx < fj_cpu.view.pb.n_constraints; ++cstr_idx) { auto [offset_begin, offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + auto [c_lb, c_ub] = fj_cpu.cached_cstr_bounds[cstr_idx].get(); + // MEMORY OPS: For each constraint, reads avg_cstr_degree elements from: + // - h_coefficients[offset_begin:offset_end] - avg_cstr_degree reads + // - h_variables[offset_begin:offset_end] - avg_cstr_degree reads (indirect indexing) + // - h_assignment[h_variables[i]] - avg_cstr_degree reads (indirect indexing) auto delta_it = - thrust::make_transform_iterator(thrust::make_counting_iterator(0), [fj = fj_cpu.view](i_t j) { - return fj.pb.coefficients[j] * fj.incumbent_assignment[fj.pb.variables[j]]; + thrust::make_transform_iterator(thrust::make_counting_iterator(0), [&fj_cpu](i_t j) { + return fj_cpu.h_coefficients[j] * fj_cpu.h_assignment[fj_cpu.h_variables[j]]; }); + // ARRAY WRITE: h_lhs[cstr_idx] - 1 write per constraint fj_cpu.h_lhs[cstr_idx] = fj_kahan_babushka_neumaier_sum(delta_it + offset_begin, delta_it + offset_end); + // ARRAY WRITE: h_lhs_sumcomp[cstr_idx] - 1 write per constraint fj_cpu.h_lhs_sumcomp[cstr_idx] = 0; - f_t cstr_tolerance = fj_cpu.view.get_corrected_tolerance(cstr_idx); - f_t new_cost = fj_cpu.view.excess_score(cstr_idx, fj_cpu.h_lhs[cstr_idx]); + f_t cstr_tolerance = fj_cpu.view.get_corrected_tolerance(cstr_idx, c_lb, c_ub); + // ARRAY READ: h_lhs[cstr_idx] - 1 read per constraint + f_t new_cost = fj_cpu.view.excess_score(cstr_idx, fj_cpu.h_lhs[cstr_idx]); if (new_cost < -cstr_tolerance) { fj_cpu.violated_constraints.insert(cstr_idx); fj_cpu.total_violations += new_cost; } else { fj_cpu.satisfied_constraints.insert(cstr_idx); } + // Total per constraint: (3 * avg_cstr_degree) reads + 2 writes + 1 read = (3 * avg_cstr_degree + // + 1) reads + 2 writes } // compute incumbent objective + // MEMORY OPS: Reads all n_vars elements from h_assignment and h_obj_coeffs + // ARRAY READ: h_assignment (n_vars reads) and h_obj_coeffs (n_vars reads) fj_cpu.h_incumbent_objective = thrust::inner_product( fj_cpu.h_assignment.begin(), fj_cpu.h_assignment.end(), fj_cpu.h_obj_coeffs.begin(), 0.); + // Total: 2 * n_vars reads } template @@ -979,15 +1142,20 @@ static thrust::tuple find_lift_move( fj_move_t best_move = fj_move_t{-1, 0}; fj_staged_score_t best_score = fj_staged_score_t::zero(); + // MEMORY OPS: Loop over objective variables (num_obj_vars iterations) + // This is called when in the feasible region to find improving moves for (auto var_idx : fj_cpu.h_objective_vars) { cuopt_assert(var_idx < fj_cpu.h_obj_coeffs.size(), "var_idx is out of bounds"); cuopt_assert(var_idx >= 0, "var_idx is out of bounds"); + // ARRAY READ: h_obj_coeffs[var_idx] - 1 read per iteration f_t obj_coeff = fj_cpu.h_obj_coeffs[var_idx]; f_t delta = -std::numeric_limits::infinity(); - f_t val = fj_cpu.h_assignment[var_idx]; + // ARRAY READ: h_assignment[var_idx] - 1 read per iteration + f_t val = fj_cpu.h_assignment[var_idx]; // special path for binary variables + // ARRAY READ: h_is_binary_variable[var_idx] - 1 read per iteration if (fj_cpu.h_is_binary_variable[var_idx]) { cuopt_assert(fj_cpu.view.pb.is_integer(val), "binary variable is not integer"); cuopt_assert(fj_cpu.view.pb.integer_equal(val, 0) || fj_cpu.view.pb.integer_equal(val, 1), @@ -996,24 +1164,38 @@ static thrust::tuple find_lift_move( // flip move wouldn't improve if (delta * obj_coeff >= 0) continue; } else { - f_t lfd_lb = get_lower(fj_cpu.h_var_bounds[var_idx]) - val; - f_t lfd_ub = get_upper(fj_cpu.h_var_bounds[var_idx]) - val; + // ARRAY READ: h_var_bounds[var_idx] - 1 read per iteration (non-binary) + f_t lfd_lb = get_lower(fj_cpu.h_var_bounds[var_idx].get()) - val; + f_t lfd_ub = get_upper(fj_cpu.h_var_bounds[var_idx].get()) - val; auto [offset_begin, offset_end] = fj_cpu.view.pb.reverse_range_for_var(var_idx); + // MEMORY OPS: Inner loop over constraints involving this variable (avg_var_degree iterations + // per outer iteration) for (i_t j = offset_begin; j < offset_end; j += 1) { - auto cstr_idx = fj_cpu.view.pb.reverse_constraints[j]; - auto cstr_coeff = fj_cpu.view.pb.reverse_coefficients[j]; - f_t c_lb = fj_cpu.view.pb.constraint_lower_bounds[cstr_idx]; - f_t c_ub = fj_cpu.view.pb.constraint_upper_bounds[cstr_idx]; + // ARRAY READ: h_reverse_constraints[j] - 1 read per inner iteration + auto cstr_idx = fj_cpu.h_reverse_constraints[j]; + // ARRAY READ: h_reverse_coefficients[j] - 1 read per inner iteration + auto cstr_coeff = fj_cpu.h_reverse_coefficients[j]; + // ARRAY READ: h_cstr_lb[cstr_idx] - 1 read per inner iteration (indirect) + f_t c_lb = fj_cpu.h_cstr_lb[cstr_idx]; + // ARRAY READ: h_cstr_ub[cstr_idx] - 1 read per inner iteration (indirect) + f_t c_ub = fj_cpu.h_cstr_ub[cstr_idx]; f_t cstr_tolerance = fj_cpu.view.get_corrected_tolerance(cstr_idx, c_lb, c_ub); cuopt_assert(c_lb <= c_ub, "invalid bounds"); + // ARRAY READ: h_lhs[cstr_idx] - 1 read per inner iteration cuopt_assert(fj_cpu.view.cstr_satisfied(cstr_idx, fj_cpu.h_lhs[cstr_idx]), "cstr should be satisfied"); // Process each bound separately, as both are satified and may both be finite // otherwise range constraints aren't correctly handled for (auto [bound, sign] : {std::make_tuple(c_lb, -1), std::make_tuple(c_ub, 1)}) { - auto [delta, slack] = - get_mtm_for_bound(fj_cpu.view, var_idx, cstr_idx, cstr_coeff, bound, sign); + auto [delta, slack] = get_mtm_for_bound(fj_cpu.view, + var_idx, + cstr_idx, + cstr_coeff, + bound, + sign, + fj_cpu.h_assignment, + fj_cpu.h_lhs); if (cstr_coeff * sign < 0) { if (fj_cpu.view.pb.is_integer_var(var_idx)) delta = ceil(delta); @@ -1039,6 +1221,8 @@ static thrust::tuple find_lift_move( } } if (lfd_lb >= lfd_ub) break; + // Total per inner iteration: 5 reads (h_reverse_constraints, h_reverse_coefficients, + // h_cstr_lb, h_cstr_ub, h_lhs) } // invalid crossing bounds @@ -1054,7 +1238,7 @@ static thrust::tuple find_lift_move( if (!isfinite(delta)) delta = 0; if (fj_cpu.view.pb.integer_equal(delta, (f_t)0)) continue; - if (tabu_check(fj_cpu, var_idx, delta)) continue; + if (tabu_check(fj_cpu, var_idx, delta)) continue; cuopt_assert(delta * obj_coeff < 0, "lift move doesn't improve the objective!"); @@ -1068,6 +1252,7 @@ static thrust::tuple find_lift_move( best_score = score; best_move = move; } + // Total per outer iteration: 3 reads + (5 * avg_var_degree reads in inner loop for non-binary) } return thrust::make_tuple(best_move, best_score); @@ -1085,9 +1270,11 @@ static void perturb(fj_cpu_climber_t& fj_cpu) std::mt19937(fj_cpu.settings.seed + fj_cpu.iterations)); raft::random::PCGenerator rng(fj_cpu.settings.seed + fj_cpu.iterations, 0, 0); + // MEMORY OPS: Loop over sampled variables (2 iterations typically) for (auto var_idx : sampled_vars) { - f_t lb = ceil(std::max(get_lower(fj_cpu.h_var_bounds[var_idx]), -1e7)); - f_t ub = floor(std::min(get_upper(fj_cpu.h_var_bounds[var_idx]), 1e7)); + // ARRAY READ: h_var_bounds[var_idx] - 1 read per iteration + f_t lb = ceil(std::max(get_lower(fj_cpu.h_var_bounds[var_idx].get()), -1e7)); + f_t ub = floor(std::min(get_upper(fj_cpu.h_var_bounds[var_idx].get()), 1e7)); f_t val = lb + (ub - lb) * rng.next_double(); if (fj_cpu.view.pb.is_integer_var(var_idx)) { val = std::round(val); @@ -1096,7 +1283,9 @@ static void perturb(fj_cpu_climber_t& fj_cpu) cuopt_assert(fj_cpu.view.pb.check_variable_within_bounds(var_idx, val), "value is out of bounds"); + // ARRAY WRITE: h_assignment[var_idx] - 1 write per iteration fj_cpu.h_assignment[var_idx] = val; + // Total per iteration: 1 read + 1 write } recompute_lhs(fj_cpu); @@ -1217,12 +1406,20 @@ static void init_fj_cpu(fj_cpu_climber_t& fj_cpu, std::make_pair(0, fj_staged_score_t::zero())); fj_cpu.cached_cstr_bounds.resize(fj_cpu.h_reverse_coefficients.size()); + // MEMORY OPS: INITIALIZATION - Loop over all variables (n_vars iterations) for (i_t var_idx = 0; var_idx < (i_t)fj_cpu.view.pb.n_variables; ++var_idx) { auto [offset_begin, offset_end] = fj_cpu.view.pb.reverse_range_for_var(var_idx); + // MEMORY OPS: Inner loop over constraints per variable (avg_var_degree iterations per outer + // iteration) for (i_t i = offset_begin; i < offset_end; ++i) { + // ARRAY READ: h_reverse_constraints[i] - 1 read per inner iteration + // ARRAY READ: h_cstr_lb[h_reverse_constraints[i]] - 1 read per inner iteration (indirect) + // ARRAY READ: h_cstr_ub[h_reverse_constraints[i]] - 1 read per inner iteration (indirect) + // ARRAY WRITE: cached_cstr_bounds[i] - 1 write per inner iteration (2 f_t values) fj_cpu.cached_cstr_bounds[i] = std::make_pair(fj_cpu.h_cstr_lb[fj_cpu.h_reverse_constraints[i]], fj_cpu.h_cstr_ub[fj_cpu.h_reverse_constraints[i]]); + // Total per inner iteration: 3 reads + 1 write (2 values) } } @@ -1392,8 +1589,8 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l update_weights(fj_cpu); if (should_perturb) { perturb(fj_cpu); - for (auto& cached_move : fj_cpu.cached_mtm_moves) - cached_move.first = 0; + for (size_t i = 0; i < fj_cpu.cached_mtm_moves.size(); i++) + fj_cpu.cached_mtm_moves[i].first = 0; } thrust::tie(move, score) = find_mtm_move_viol(fj_cpu, 1, true); // pick a single random violated constraint @@ -1407,8 +1604,11 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l // number of violated constraints is usually small (<100). recomputing from all LHSs is cheap // and more numerically precise than just adding to the accumulator in apply_move fj_cpu.total_violations = 0; + // MEMORY OPS: Loop over violated constraints (typically <100 iterations per main iteration) for (auto cstr_idx : fj_cpu.violated_constraints) { + // ARRAY READ: h_lhs[cstr_idx] - 1 read per iteration fj_cpu.total_violations += fj_cpu.view.excess_score(cstr_idx, fj_cpu.h_lhs[cstr_idx]); + // Total per iteration: 1 read } if (fj_cpu.iterations % fj_cpu.log_interval == 0) { CUOPT_LOG_TRACE( @@ -1454,7 +1654,13 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l #ifdef __linux__ collect_and_print_papi_metrics(fj_cpu); #endif - log_regression_features(fj_cpu, time_window_ms, total_time_ms); + + // Collect memory statistics + auto [loads, stores] = fj_cpu.memory_manifold.collect_and_flush(); + + // Log all features including memory statistics + log_regression_features(fj_cpu, time_window_ms, total_time_ms, loads, stores); + fj_cpu.last_feature_log_time = now; } diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cuh b/cpp/src/mip/feasibility_jump/fj_cpu.cuh index c1f5316f8a..76db1ce983 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cuh +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cuh @@ -12,6 +12,7 @@ #include #include +#include namespace cuopt::linear_programming::detail { @@ -19,7 +20,36 @@ namespace cuopt::linear_programming::detail { // Maintaining a single source of truth for all members would be nice template struct fj_cpu_climber_t { - fj_cpu_climber_t() = default; + fj_cpu_climber_t() + { + // Initialize memory manifold with all ins_vector members + memory_manifold = instrumentation_manifold_t{h_reverse_coefficients, + h_reverse_constraints, + h_reverse_offsets, + h_coefficients, + h_offsets, + h_variables, + h_obj_coeffs, + h_var_bounds, + h_cstr_lb, + h_cstr_ub, + h_var_types, + h_is_binary_variable, + h_objective_vars, + h_binary_indices, + h_tabu_nodec_until, + h_tabu_noinc_until, + h_tabu_lastdec, + h_tabu_lastinc, + h_lhs, + h_lhs_sumcomp, + h_cstr_left_weights, + h_cstr_right_weights, + h_assignment, + h_best_assignment, + cached_cstr_bounds, + iter_mtm_vars}; + } fj_cpu_climber_t(const fj_cpu_climber_t& other) = delete; fj_cpu_climber_t& operator=(const fj_cpu_climber_t& other) = delete; @@ -30,33 +60,33 @@ struct fj_cpu_climber_t { fj_settings_t settings; typename fj_t::climber_data_t::view_t view; // Host copies of device data as struct members - std::vector h_reverse_coefficients; - std::vector h_reverse_constraints; - std::vector h_reverse_offsets; - std::vector h_coefficients; - std::vector h_offsets; - std::vector h_variables; - std::vector h_obj_coeffs; - std::vector::type> h_var_bounds; - std::vector h_cstr_lb; - std::vector h_cstr_ub; - std::vector h_var_types; - std::vector h_is_binary_variable; - std::vector h_objective_vars; - std::vector h_binary_indices; - - std::vector h_tabu_nodec_until; - std::vector h_tabu_noinc_until; - std::vector h_tabu_lastdec; - std::vector h_tabu_lastinc; - - std::vector h_lhs; - std::vector h_lhs_sumcomp; - std::vector h_cstr_left_weights; - std::vector h_cstr_right_weights; + ins_vector h_reverse_coefficients; + ins_vector h_reverse_constraints; + ins_vector h_reverse_offsets; + ins_vector h_coefficients; + ins_vector h_offsets; + ins_vector h_variables; + ins_vector h_obj_coeffs; + ins_vector::type> h_var_bounds; + ins_vector h_cstr_lb; + ins_vector h_cstr_ub; + ins_vector h_var_types; + ins_vector h_is_binary_variable; + ins_vector h_objective_vars; + ins_vector h_binary_indices; + + ins_vector h_tabu_nodec_until; + ins_vector h_tabu_noinc_until; + ins_vector h_tabu_lastdec; + ins_vector h_tabu_lastinc; + + ins_vector h_lhs; + ins_vector h_lhs_sumcomp; + ins_vector h_cstr_left_weights; + ins_vector h_cstr_right_weights; f_t max_weight; - std::vector h_assignment; - std::vector h_best_assignment; + ins_vector h_assignment; + ins_vector h_best_assignment; f_t h_objective_weight; f_t h_incumbent_objective; f_t h_best_objective; @@ -84,16 +114,16 @@ struct fj_cpu_climber_t { // vector is actually likely beneficial here since we're memory bound std::vector flip_move_computed; - ; + // CSR nnz offset -> (delta, score) std::vector> cached_mtm_moves; // CSC (transposed!) nnz-offset-indexed constraint bounds (lb, ub) // std::pair better compile down to 16 bytes!! GCC do your job! - std::vector> cached_cstr_bounds; + ins_vector> cached_cstr_bounds; std::vector var_bitmap; - std::vector iter_mtm_vars; + ins_vector iter_mtm_vars; i_t mtm_viol_samples{25}; i_t mtm_sat_samples{15}; @@ -142,6 +172,9 @@ struct fj_cpu_climber_t { double var_degree_cv{0.0}; double cstr_degree_cv{0.0}; double problem_density{0.0}; + + // Memory instrumentation manifold + instrumentation_manifold_t memory_manifold; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/local_search/local_search.cuh b/cpp/src/mip/local_search/local_search.cuh index c1c11fd39e..3e038b547f 100644 --- a/cpp/src/mip/local_search/local_search.cuh +++ b/cpp/src/mip/local_search/local_search.cuh @@ -143,7 +143,7 @@ class local_search_t { std::mt19937 rng; std::array, 0> ls_cpu_fj; - std::array, 0> scratch_cpu_fj; + std::array, 1> scratch_cpu_fj; cpu_fj_thread_t scratch_cpu_fj_on_lp_opt; problem_t problem_with_objective_cut; bool cutting_plane_added_for_active_run{false}; diff --git a/cpp/src/utilities/memory_instrumentation.hpp b/cpp/src/utilities/memory_instrumentation.hpp new file mode 100644 index 0000000000..2cf0d3b1ed --- /dev/null +++ b/cpp/src/utilities/memory_instrumentation.hpp @@ -0,0 +1,673 @@ +/* clang-format off */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* clang-format on */ + +/** + * @file memory_instrumentation.hpp + * @brief Memory access instrumentation utilities + * + * This file provides wrapper classes for tracking memory reads and writes. + * + * Usage: + * - Define CUOPT_ENABLE_MEMORY_INSTRUMENTATION to enable tracking + * - When undefined, all instrumentation becomes zero-overhead passthrough + * (record_*() calls inline away, no counter storage overhead) + * + * Example: + * ins_vector vec; // Instrumented std::vector + * vec.push_back(42); + * auto val = vec[0]; + * // When enabled: tracking occurs, counters accumulate + * // When disabled: direct passthrough, compiler optimizes away all overhead + */ + +#pragma once + +#include +#include +#include +#include +#include + +#define CUOPT_ENABLE_MEMORY_INSTRUMENTATION 1 + +namespace cuopt { + +// Define CUOPT_ENABLE_MEMORY_INSTRUMENTATION to enable memory tracking +// If undefined, instrumentation becomes a zero-overhead passthrough + +// Base class for memory operation instrumentation +struct memory_instrumentation_base_t { +#ifdef CUOPT_ENABLE_MEMORY_INSTRUMENTATION + __host__ __device__ void reset_counters() const { byte_loads = byte_stores = 0; } + + template + __host__ __device__ void record_load() const + { + byte_loads += sizeof(T); + } + + template + __host__ __device__ void record_store() const + { + byte_stores += sizeof(T); + } + + template + __host__ __device__ void record_rmw() const + { + byte_loads += sizeof(T); + byte_stores += sizeof(T); + } + + mutable size_t byte_loads{0}; + mutable size_t byte_stores{0}; +#else + // No-op methods when instrumentation is disabled - these inline away to zero overhead + __host__ __device__ void reset_counters() const {} + template + __host__ __device__ void record_load() const + { + } + template + __host__ __device__ void record_store() const + { + } + template + __host__ __device__ void record_rmw() const + { + } +#endif // CUOPT_ENABLE_MEMORY_INSTRUMENTATION +}; + +#ifdef CUOPT_ENABLE_MEMORY_INSTRUMENTATION + +// Manifold class to collect statistics from multiple instrumented objects +class instrumentation_manifold_t { + public: + instrumentation_manifold_t() = default; + + // Construct with initializer list of instrumented objects + instrumentation_manifold_t( + std::initializer_list> instrumented) + : instrumented_(instrumented) + { + } + + // Add an instrumented object to track + void add(memory_instrumentation_base_t& instrumented) { instrumented_.push_back(instrumented); } + + // Collect total loads and stores from all instrumented objects, then flush all counters + std::pair collect_and_flush() + { + size_t total_loads = 0; + size_t total_stores = 0; + + for (auto& instr : instrumented_) { + total_loads += instr.get().byte_loads; + total_stores += instr.get().byte_stores; + instr.get().reset_counters(); + } + + return {total_loads, total_stores}; + } + + private: + std::vector> instrumented_; +}; + +#else + +// No-op manifold when instrumentation is disabled +class instrumentation_manifold_t { + public: + instrumentation_manifold_t() = default; + instrumentation_manifold_t( + std::initializer_list>) + { + } + void add(memory_instrumentation_base_t&) {} + std::pair collect_and_flush() { return {0, 0}; } +}; + +#endif // CUOPT_ENABLE_MEMORY_INSTRUMENTATION + +// Helper traits to detect container capabilities +namespace type_traits_utils { + +template +struct has_reserve : std::false_type {}; + +template +struct has_reserve().reserve(size_t{}))>> : std::true_type { +}; + +template +struct has_capacity : std::false_type {}; + +template +struct has_capacity().capacity())>> : std::true_type {}; + +template +struct has_shrink_to_fit : std::false_type {}; + +template +struct has_shrink_to_fit().shrink_to_fit())>> + : std::true_type {}; + +template +struct has_push_back : std::false_type {}; + +template +struct has_push_back< + T, + std::void_t().push_back(std::declval()))>> + : std::true_type {}; + +template +struct has_emplace_back : std::false_type {}; + +template +struct has_emplace_back().emplace_back())>> + : std::true_type {}; + +template +struct has_pop_back : std::false_type {}; + +template +struct has_pop_back().pop_back())>> : std::true_type {}; + +template +struct has_data : std::false_type {}; + +template +struct has_data().data())>> : std::true_type {}; + +template +struct has_resize : std::false_type {}; + +template +struct has_resize().resize(size_t{}))>> : std::true_type {}; + +template +struct has_clear : std::false_type {}; + +template +struct has_clear().clear())>> : std::true_type {}; + +template +struct has_max_size : std::false_type {}; + +template +struct has_max_size().max_size())>> : std::true_type {}; + +template +struct has_front : std::false_type {}; + +template +struct has_front().front())>> : std::true_type {}; + +template +struct has_back : std::false_type {}; + +template +struct has_back().back())>> : std::true_type {}; + +} // namespace type_traits_utils + +// Memory operation instrumentation wrapper for container-like types +template +struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { + // Standard container type traits + using value_type = std::remove_reference_t()[0])>; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + + static_assert(std::is_standard_layout_v, + "value_type must have standard layout for memory instrumentation"); + static constexpr size_t type_size = sizeof(value_type); + + // Proxy class to track reads and writes for a single element + class element_proxy_t { + public: + element_proxy_t(value_type& ref, memop_instrumentation_wrapper_t& wrapper) + : ref_(ref), wrapper_(wrapper) + { + } + + element_proxy_t& operator=(const value_type& value) + { + wrapper_.template record_store(); + ref_ = value; + return *this; + } + element_proxy_t& operator=(const element_proxy_t& other) + { + wrapper_.template record_store(); + other.wrapper_.template record_load(); + ref_ = other.ref_; + return *this; + } + + operator value_type() const + { + wrapper_.template record_load(); + return ref_; + } + + // Allow implicit conversion to reference for functions expecting references + operator value_type&() { return ref_; } + + operator const value_type&() const { return ref_; } + + // Member access operator for structured types (e.g., type_2) + value_type* operator->() { return &ref_; } + + const value_type* operator->() const { return &ref_; } + + // Get underlying element reference (records a load) + value_type& get() + { + wrapper_.template record_load(); + return ref_; + } + + const value_type& get() const + { + wrapper_.template record_load(); + return ref_; + } + + element_proxy_t& operator+=(const value_type& value) + { + wrapper_.template record_rmw(); + ref_ += value; + return *this; + } + element_proxy_t& operator-=(const value_type& value) + { + wrapper_.template record_rmw(); + ref_ -= value; + return *this; + } + element_proxy_t& operator*=(const value_type& value) + { + wrapper_.template record_rmw(); + ref_ *= value; + return *this; + } + element_proxy_t& operator/=(const value_type& value) + { + wrapper_.template record_rmw(); + ref_ /= value; + return *this; + } + element_proxy_t& operator++() + { + wrapper_.template record_rmw(); + ++ref_; + return *this; + } + element_proxy_t& operator--() + { + wrapper_.template record_rmw(); + --ref_; + return *this; + } + + value_type operator++(int) + { + wrapper_.template record_rmw(); + return ref_++; + } + value_type operator--(int) + { + wrapper_.template record_rmw(); + return ref_--; + } + + value_type& ref_; + memop_instrumentation_wrapper_t& wrapper_; + }; + + // Instrumented iterator that tracks memory accesses + template + class instrumented_iterator_t { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = memop_instrumentation_wrapper_t::value_type; + using difference_type = std::ptrdiff_t; + using pointer = std::conditional_t; + using reference = std::conditional_t; + using wrapper_ptr = std::conditional_t; + + instrumented_iterator_t(IterT iter, wrapper_ptr wrapper) : iter_(iter), wrapper_(wrapper) {} + + // Dereference - returns proxy for non-const, tracks load for const + auto operator*() const + { + if constexpr (IsConst) { + wrapper_->byte_loads += sizeof(value_type); + return *iter_; + } else { + return element_proxy_t(*iter_, *wrapper_); + } + } + + auto operator->() const { return &(*iter_); } + + instrumented_iterator_t& operator++() + { + ++iter_; + return *this; + } + + instrumented_iterator_t operator++(int) + { + auto tmp = *this; + ++iter_; + return tmp; + } + + instrumented_iterator_t& operator--() + { + --iter_; + return *this; + } + + instrumented_iterator_t operator--(int) + { + auto tmp = *this; + --iter_; + return tmp; + } + + instrumented_iterator_t& operator+=(difference_type n) + { + iter_ += n; + return *this; + } + + instrumented_iterator_t& operator-=(difference_type n) + { + iter_ -= n; + return *this; + } + + instrumented_iterator_t operator+(difference_type n) const + { + return instrumented_iterator_t(iter_ + n, wrapper_); + } + + instrumented_iterator_t operator-(difference_type n) const + { + return instrumented_iterator_t(iter_ - n, wrapper_); + } + + difference_type operator-(const instrumented_iterator_t& other) const + { + return iter_ - other.iter_; + } + + auto operator[](difference_type n) const { return *(*this + n); } + + bool operator==(const instrumented_iterator_t& other) const { return iter_ == other.iter_; } + bool operator!=(const instrumented_iterator_t& other) const { return iter_ != other.iter_; } + bool operator<(const instrumented_iterator_t& other) const { return iter_ < other.iter_; } + bool operator<=(const instrumented_iterator_t& other) const { return iter_ <= other.iter_; } + bool operator>(const instrumented_iterator_t& other) const { return iter_ > other.iter_; } + bool operator>=(const instrumented_iterator_t& other) const { return iter_ >= other.iter_; } + + IterT base() const { return iter_; } + + // Allow iterator_traits to access the underlying iterator + friend struct std::iterator_traits; + + private: + IterT iter_; + wrapper_ptr wrapper_; + }; + + // Iterator type definitions (must come after instrumented_iterator_t) + using iterator = instrumented_iterator_t().begin()), false>; + using const_iterator = instrumented_iterator_t().begin()), true>; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + // Constructors + memop_instrumentation_wrapper_t() : array() + { + if constexpr (type_traits_utils::has_data::value) { + data_ptr = array.data(); + } else { + data_ptr = nullptr; + } + } + + // Copy/move from underlying type + memop_instrumentation_wrapper_t(const T& array) : array(array) + { + if constexpr (type_traits_utils::has_data::value) { + data_ptr = const_cast(array.data()); + } else { + data_ptr = nullptr; + } + } + memop_instrumentation_wrapper_t(T&& array) : array(std::move(array)) + { + if constexpr (type_traits_utils::has_data::value) { + data_ptr = array.data(); + } else { + data_ptr = nullptr; + } + } + + // Forwarding constructor for underlying container initialization + // Only enabled for types that aren't the wrapper itself or the underlying type + template , memop_instrumentation_wrapper_t> && + !std::is_same_v, T> && + (sizeof...(Args) > 0 || !std::is_convertible_v)>> + explicit memop_instrumentation_wrapper_t(Arg&& arg, Args&&... args) + : array(std::forward(arg), std::forward(args)...) + { + if constexpr (type_traits_utils::has_data::value) { + data_ptr = array.data(); + } else { + data_ptr = nullptr; + } + } + + // Copy/move from wrapper + memop_instrumentation_wrapper_t(const memop_instrumentation_wrapper_t&) = default; + memop_instrumentation_wrapper_t(memop_instrumentation_wrapper_t&&) = default; + memop_instrumentation_wrapper_t& operator=(const memop_instrumentation_wrapper_t&) = default; + memop_instrumentation_wrapper_t& operator=(memop_instrumentation_wrapper_t&&) = default; + + element_proxy_t operator[](size_type index) { return element_proxy_t(array[index], *this); } + + __host__ __device__ value_type operator[](size_type index) const + { + this->template record_load(); + // really ugly hack because otherwise nvcc complains about vector operator[] being __host__ only + if constexpr (type_traits_utils::has_data::value) { + return data_ptr[index]; + } else { + return array[index]; + } + } + + template + std::enable_if_t::value, element_proxy_t> front() + { + return element_proxy_t(array.front(), *this); + } + + template + std::enable_if_t::value, value_type> front() const + { + this->template record_load(); + return array.front(); + } + + template + std::enable_if_t::value, element_proxy_t> back() + { + return element_proxy_t(array.back(), *this); + } + + template + std::enable_if_t::value, value_type> back() const + { + this->template record_load(); + return array.back(); + } + + // Iterators + iterator begin() noexcept { return iterator(std::begin(array), this); } + const_iterator begin() const noexcept { return const_iterator(std::begin(array), this); } + const_iterator cbegin() const noexcept { return const_iterator(std::begin(array), this); } + + iterator end() noexcept { return iterator(std::end(array), this); } + const_iterator end() const noexcept { return const_iterator(std::end(array), this); } + const_iterator cend() const noexcept { return const_iterator(std::end(array), this); } + + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(std::end(array)); } + const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } + + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } + const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } + + // Capacity + bool empty() const noexcept { return std::begin(array) == std::end(array); } + size_type size() const noexcept { return std::distance(std::begin(array), std::end(array)); } + + // Conditional methods - only available if underlying type supports them + template + std::enable_if_t::value, size_type> max_size() const noexcept + { + return array.max_size(); + } + + template + std::enable_if_t::value, size_type> capacity() const noexcept + { + return array.capacity(); + } + + template + std::enable_if_t::value> reserve(size_type new_cap) + { + array.reserve(new_cap); + if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + } + + template + std::enable_if_t::value> shrink_to_fit() + { + array.shrink_to_fit(); + if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + } + + template + std::enable_if_t::value> clear() noexcept + { + array.clear(); + if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + } + + template + std::enable_if_t::value> push_back(const value_type& value) + { + // we should probably take into account possible copies done by std::vector. oh well. + // hot loops shouldn't be doing such operations anyway + this->template record_store(); + array.push_back(value); + if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + } + + template + std::enable_if_t::value> push_back(value_type&& value) + { + this->template record_store(); + array.push_back(std::move(value)); + if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + } + + template + std::enable_if_t::value> emplace_back(Args&&... args) + { + this->template record_store(); + array.emplace_back(std::forward(args)...); + if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + } + + template + std::enable_if_t::value> pop_back() + { + this->template record_load(); // Reading the element before removal + array.pop_back(); + if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + } + + template + std::enable_if_t::value> resize(size_type count) + { + size_type old_size = array.size(); + array.resize(count); + if (count > old_size) { + this->byte_stores += (count - old_size) * type_size; // New elements initialized + } + if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + } + + template + std::enable_if_t::value> resize(size_type count, + const value_type& value) + { + size_type old_size = array.size(); + array.resize(count, value); + if (count > old_size) { this->byte_stores += (count - old_size) * type_size; } + if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + } + + template + std::enable_if_t::value, value_type*> data() noexcept + { + return array.data(); + } + + template + std::enable_if_t::value, const value_type*> data() const noexcept + { + return array.data(); + } + + // Access to underlying array + operator T&() { return array; } + operator const T&() const { return array; } + + T&& release_array() { return std::move(array); } + + T array; + value_type* data_ptr{nullptr}; +}; + +// Convenience alias for instrumented std::vector +template +using ins_vector = memop_instrumentation_wrapper_t>; + +} // namespace cuopt diff --git a/scripts/determinism_logs_parse.py b/scripts/determinism_logs_parse.py index 09ea08627d..cee653c0b1 100755 --- a/scripts/determinism_logs_parse.py +++ b/scripts/determinism_logs_parse.py @@ -25,6 +25,7 @@ - CP (Constraint Propagation): CP_FEATURES and CP_RESULT logs - FJ (Feasibility Jump): Legacy FJ: format - CPUFJ (CPU Feasibility Jump): CPUFJ_FEATURES single-line logs +- BB (Branch and Bound): BB_NODE_FEATURES single-line logs IMPORTANT - Grep Specificity: The parser uses EXACT pattern matching with grep to filter logs efficiently. @@ -49,6 +50,7 @@ python determinism_logs_parse.py --algorithm CP [-o output.feather] python determinism_logs_parse.py --algorithm FJ [-o output.feather] python determinism_logs_parse.py --algorithm CPUFJ [-o output.feather] + python determinism_logs_parse.py --algorithm BB [-o output.feather] """ import argparse @@ -60,7 +62,7 @@ from typing import List, Dict, Any, Optional -SUPPORTED_ALGORITHMS = ["FP", "PDLP", "CP", "FJ", "CPUFJ"] +SUPPORTED_ALGORITHMS = ["FP", "PDLP", "CP", "FJ", "CPUFJ", "BB"] def parse_value(value_str: str) -> Any: @@ -423,6 +425,13 @@ def parse_cpufj_logs(log_files: List[str]) -> List[Dict[str, Any]]: ) +def parse_bb_logs(log_files: List[str]) -> List[Dict[str, Any]]: + """Parse Branch and Bound node feature logs.""" + return parse_single_line_logs( + log_files, "BB_NODE_FEATURES", "BB (Branch and Bound)" + ) + + def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: """Print statistics about parsed entries.""" if not entries: @@ -473,6 +482,7 @@ def main(): CP - Constraint Propagation (parses CP_FEATURES and CP_RESULT logs) FJ - Feasibility Jump (parses legacy FJ: format) CPUFJ - CPU Feasibility Jump (parses CPUFJ_FEATURES single-line logs) + BB - Branch and Bound (parses BB_NODE_FEATURES single-line logs) Examples: python determinism_logs_parse.py logs/ --algorithm FP -o fp_data.feather @@ -480,6 +490,7 @@ def main(): python determinism_logs_parse.py logs/ --algorithm CP -o cp_data.feather python determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.feather python determinism_logs_parse.py logs/ --algorithm CPUFJ -o cpufj_data.feather + python determinism_logs_parse.py logs/ --algorithm BB -o bb_data.feather # Limit to first 10 files for testing python determinism_logs_parse.py logs/ --algorithm FP --max-files 10 @@ -552,6 +563,8 @@ def main(): entries = parse_fj_logs(log_files) elif args.algorithm == "CPUFJ": entries = parse_cpufj_logs(log_files) + elif args.algorithm == "BB": + entries = parse_bb_logs(log_files) else: print(f"Error: Unsupported algorithm: {args.algorithm}") return 1 @@ -568,6 +581,10 @@ def main(): print( "Make sure your logs contain CPUFJ_FEATURES lines with key=value pairs" ) + elif args.algorithm == "BB": + print( + "Make sure your logs contain BB_NODE_FEATURES lines with key=value pairs" + ) return 1 # Print statistics diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index 24204b6502..393558cf52 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -97,19 +97,30 @@ "iter_since_best", "tid", "curr_obj", - "obj_weight", + # "obj_weight", "L3_miss", "L2_miss", "L1_miss", "stores_per_iter", "loads_per_iter", - "is_feas", - "feas_found", - "viol_ratio", - "eval_intensity", - "nnz_per_move", + # "is_feas", + # "feas_found", + # "viol_ratio", + # "eval_intensity", + # "nnz_per_move", "iter", - "max_weight", + # "max_weight", + "avg_cstr_deg", + "avg_var_deg", + "lp_time", + "node_id", + "var_sel_time", + "bound_str_time", + "sb_time", + "lp_status", + "node_status", + "depth", + "cutoff_gap", ] # Alternatively, specify ONLY the features you want to use From 0fc30c5a8dfd1e5c07da781c71b60995456bc1f5 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 12 Nov 2025 17:46:03 +0000 Subject: [PATCH 074/225] don't clog w/ improvements --- cpp/src/mip/local_search/local_search.cu | 28 ++++++++++++------------ scripts/train_regressor.py | 10 +++++++++ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index de4fa9e831..3e45bcae82 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -83,20 +83,20 @@ void local_search_t::start_cpufj_scratch_threads(population_t 0); cpu_fj.fj_cpu->log_prefix = "******* scratch " + std::to_string(counter) + ": "; - if (!context.settings.deterministic) { - cpu_fj.fj_cpu->improvement_callback = [this, &population, &cpu_fj]( - f_t obj, const std::vector& h_vec) { - population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); - if (obj < local_search_best_obj) { - CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", - context.problem_ptr->get_user_obj_from_solver_obj(obj), - context.problem_ptr->get_user_obj_from_solver_obj( - population.is_feasible() ? population.best_feasible().get_objective() - : std::numeric_limits::max())); - local_search_best_obj = obj; - } - }; - } + // if (!context.settings.deterministic) { + // cpu_fj.fj_cpu->improvement_callback = [this, &population, &cpu_fj]( + // f_t obj, const std::vector& h_vec) { + // population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); + // if (obj < local_search_best_obj) { + // CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", + // context.problem_ptr->get_user_obj_from_solver_obj(obj), + // context.problem_ptr->get_user_obj_from_solver_obj( + // population.is_feasible() ? population.best_feasible().get_objective() + // : std::numeric_limits::max())); + // local_search_best_obj = obj; + // } + // }; + // } counter++; }; diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index 393558cf52..0f251c3a29 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -121,6 +121,10 @@ "node_status", "depth", "cutoff_gap", + "mem_bandwidth_gb_s", + "max_cstr_deg", + "viol_ratio", + "nnz_per_move", ] # Alternatively, specify ONLY the features you want to use @@ -130,6 +134,12 @@ # 'n_variables', # 'n_constraints', # 'sparsity', + # "n_vars", + # "n_cstrs", + # "total_nnz", + # "mem_total_mb", + # "mem_store_mb", + # "mem_load_mb", ] # ============================================================================ From 8bd70e35c76b1abc1db6c4f6ab744b4ba5138257 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 13 Nov 2025 11:34:22 +0000 Subject: [PATCH 075/225] fixed iter count --- cpp/src/mip/diversity/diversity_manager.cu | 4 +++- cpp/src/mip/local_search/local_search.cu | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 37e399d73b..2458e0ae5c 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -359,7 +359,9 @@ solution_t diversity_manager_t::run_solver() if (!context.settings.deterministic) { #if 1 ls.start_cpufj_scratch_threads(population); - std::this_thread::sleep_for(std::chrono::seconds(30)); + // 30'000 iters + ls.ls_cpu_fj[0].wait_for_cpu_solver(); + // std::this_thread::sleep_for(std::chrono::seconds(30)); ls.stop_cpufj_scratch_threads(); exit(0); #endif diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 3e45bcae82..a7a1e48105 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -64,7 +64,8 @@ void local_search_t::start_cpufj_scratch_threads(population_t default_weights(context.problem_ptr->n_constraints, 1.); fj_settings_t cpu_fj_settings{}; - cpu_fj_settings.time_limit = std::numeric_limits::infinity(); + cpu_fj_settings.time_limit = std::numeric_limits::infinity(); + cpu_fj_settings.iteration_limit = 30000; // would like 30 samples per instance solution_t solution(*context.problem_ptr); thrust::fill(solution.handle_ptr->get_thrust_policy(), From 7bcf3baabc5e9f46f83e7e99633bfcfa2c4b3c82 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 13 Nov 2025 13:38:54 +0000 Subject: [PATCH 076/225] bugfix --- cpp/src/mip/diversity/diversity_manager.cu | 2 +- cpp/src/mip/local_search/local_search.cu | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 2458e0ae5c..ea60ca1313 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -360,7 +360,7 @@ solution_t diversity_manager_t::run_solver() #if 1 ls.start_cpufj_scratch_threads(population); // 30'000 iters - ls.ls_cpu_fj[0].wait_for_cpu_solver(); + ls.scratch_cpu_fj[0].wait_for_cpu_solver(); // std::this_thread::sleep_for(std::chrono::seconds(30)); ls.stop_cpufj_scratch_threads(); exit(0); diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index a7a1e48105..28be6dd4f4 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -65,7 +65,7 @@ void local_search_t::start_cpufj_scratch_threads(population_t::infinity(); - cpu_fj_settings.iteration_limit = 30000; // would like 30 samples per instance + cpu_fj_settings.iteration_limit = 10000; // would like 10 samples per instance solution_t solution(*context.problem_ptr); thrust::fill(solution.handle_ptr->get_thrust_policy(), From 4bfb96fddd2dd1a6ebb54c46ffa97b7c7efc3ab3 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 13 Nov 2025 18:33:13 +0000 Subject: [PATCH 077/225] more memops logging --- .../linear_programming/cuopt/run_mip.cpp | 1 + cpp/src/CMakeLists.txt | 4 +- .../feasibility_jump_impl_common.cuh | 21 +- .../feasibility_jump_kernels.cu | 13 +- cpp/src/mip/feasibility_jump/fj_cpu.cu | 265 +- cpp/src/mip/feasibility_jump/fj_cpu.cuh | 57 +- .../mip/feasibility_jump/load_balancing.cuh | 26 +- cpp/src/mip/local_search/local_search.cu | 2 +- cpp/src/mip/solver_context.cuh | 2 + cpp/src/utilities/memory_instrumentation.hpp | 76 +- .../utilities/models/cpufj_predictor/header.h | 35 + .../utilities/models/cpufj_predictor/main.cpp | 5412 +++++++++++++++++ .../models/cpufj_predictor/quantize.cpp | 293 + cpp/src/utilities/work_unit_predictor.cpp | 2 + scripts/train_regressor.py | 14 +- 15 files changed, 6056 insertions(+), 167 deletions(-) create mode 100644 cpp/src/utilities/models/cpufj_predictor/header.h create mode 100644 cpp/src/utilities/models/cpufj_predictor/main.cpp create mode 100644 cpp/src/utilities/models/cpufj_predictor/quantize.cpp diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 5dfd373d7d..ef3d3760f1 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -207,6 +207,7 @@ int run_single_file(std::string file_path, settings.tolerances.absolute_tolerance = 1e-6; settings.presolve = true; + settings.presolve = false; // settings.heuristics_only = true; cuopt::linear_programming::benchmark_info_t benchmark_info; diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 6d4de5f965..c07cae0376 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -9,7 +9,9 @@ set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_predictor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/quantize.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/quantize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/quantize.cpp) add_subdirectory(linear_programming) add_subdirectory(math_optimization) diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_impl_common.cuh b/cpp/src/mip/feasibility_jump/feasibility_jump_impl_common.cuh index 95de8bc1eb..4a3bffb607 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_impl_common.cuh +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_impl_common.cuh @@ -107,7 +107,9 @@ HDI std::pair feas_score_constraint( f_t cstr_coeff, f_t c_lb, f_t c_ub, - f_t current_lhs) + f_t current_lhs, + f_t left_weight, + f_t right_weight) { cuopt_assert(isfinite(delta), "invalid delta"); cuopt_assert(cstr_coeff != 0 && isfinite(cstr_coeff), "invalid coefficient"); @@ -127,14 +129,13 @@ HDI std::pair feas_score_constraint( // TODO: broadcast left/right weights to a csr_offset-indexed table? local minimums // usually occur on a rarer basis (around 50 iteratiosn to 1 local minimum) // likely unreasonable and overkill however - f_t cstr_weight = - bound_idx == 0 ? fj.cstr_left_weights[cstr_idx] : fj.cstr_right_weights[cstr_idx]; - f_t sign = bound_idx == 0 ? -1 : 1; - f_t rhs = bounds[bound_idx] * sign; - f_t old_lhs = current_lhs * sign; - f_t new_lhs = (current_lhs + cstr_coeff * delta) * sign; - f_t old_slack = rhs - old_lhs; - f_t new_slack = rhs - new_lhs; + f_t cstr_weight = bound_idx == 0 ? left_weight : right_weight; + f_t sign = bound_idx == 0 ? -1 : 1; + f_t rhs = bounds[bound_idx] * sign; + f_t old_lhs = current_lhs * sign; + f_t new_lhs = (current_lhs + cstr_coeff * delta) * sign; + f_t old_slack = rhs - old_lhs; + f_t new_slack = rhs - new_lhs; cuopt_assert(isfinite(cstr_weight), "invalid weight"); cuopt_assert(cstr_weight >= 0, "invalid weight"); @@ -142,7 +143,7 @@ HDI std::pair feas_score_constraint( cuopt_assert(isfinite(new_lhs), ""); cuopt_assert(isfinite(old_slack) && isfinite(new_slack), ""); - f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx); + f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx, c_lb, c_ub); bool old_viol = fj.excess_score(cstr_idx, current_lhs, c_lb, c_ub) < -cstr_tolerance; bool new_viol = diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 584d3d8540..7c8fb0433c 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -206,8 +206,17 @@ DI typename fj_t::move_score_info_t compute_new_score( f_t c_lb = fj.pb.constraint_lower_bounds[cstr_idx]; f_t c_ub = fj.pb.constraint_upper_bounds[cstr_idx]; - auto [cstr_base_feas, cstr_bonus_robust] = feas_score_constraint( - fj, var_idx, delta, cstr_idx, cstr_coeff, c_lb, c_ub, fj.incumbent_lhs[cstr_idx]); + auto [cstr_base_feas, cstr_bonus_robust] = + feas_score_constraint(fj, + var_idx, + delta, + cstr_idx, + cstr_coeff, + c_lb, + c_ub, + fj.incumbent_lhs[cstr_idx], + fj.cstr_left_weights[cstr_idx], + fj.cstr_right_weights[cstr_idx]); base_feas += cstr_base_feas; bonus_robust += cstr_bonus_robust; diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index 59b6dca568..f587455b25 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -36,8 +36,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -54,10 +56,10 @@ namespace cuopt::linear_programming::detail { static constexpr double BIGVAL_THRESHOLD = 1e20; -#ifdef __linux__ -// Global mutex to protect PAPI metric printing across multiple threads -static std::mutex papi_print_mutex; -#endif +// #ifdef __linux__ +// // Global mutex to protect PAPI metric printing across multiple threads +// static std::mutex papi_print_mutex; +// #endif template class timing_raii_t { @@ -204,64 +206,64 @@ static void initialize_papi(fj_cpu_climber_t& fj_cpu) CUOPT_LOG_TRACE("%sPAPI initialized successfully", fj_cpu.log_prefix.c_str()); } -template -static void collect_and_print_papi_metrics(fj_cpu_climber_t& fj_cpu) -{ - if (!fj_cpu.papi_initialized) return; - - std::vector values(fj_cpu.papi_events.size(), 0); - int retval = PAPI_read(fj_cpu.papi_event_set, values.data()); - if (retval != PAPI_OK) { - CUOPT_LOG_TRACE("%sPAPI read failed", fj_cpu.log_prefix.c_str()); - return; - } - - // Get thread ID - pid_t tid = syscall(SYS_gettid); - - // Build map of actual values indexed by event position - std::vector all_values(8, -1); - int value_idx = 0; - for (size_t i = 0; i < fj_cpu.papi_events.size(); i++) { - if (fj_cpu.papi_events[i] != -1) { all_values[i] = values[value_idx++]; } - } - - // Compute derived metrics - double l1_miss_rate = -1.0; - if (all_values[0] > 0 && all_values[1] != -1) { - l1_miss_rate = (double)all_values[1] / all_values[0] * 100.0; - } - - double l2_miss_rate = -1.0; - if (all_values[2] > 0 && all_values[3] != -1) { - l2_miss_rate = (double)all_values[3] / all_values[2] * 100.0; - } - - // Lock to ensure thread-safe printing - std::lock_guard lock(papi_print_mutex); - - // Print everything on a single compact line - CUOPT_LOG_DEBUG( - "%sPAPI iter=%d tid=%d L1_DCA=%lld L1_DCM=%lld L2_DCA=%lld L2_DCM=%lld L3_TCA=%lld " - "L3_TCM=%lld LD_INS=%lld SR_INS=%lld L1_miss=%.2f%% L2_miss=%.2f%%", - fj_cpu.log_prefix.c_str(), - fj_cpu.iterations, - tid, - all_values[0], - all_values[1], - all_values[2], - all_values[3], - all_values[4], - all_values[5], - all_values[6], - all_values[7], - l1_miss_rate, - l2_miss_rate); - - // Reset counters for the next 1000 iterations - retval = PAPI_reset(fj_cpu.papi_event_set); - if (retval != PAPI_OK) { CUOPT_LOG_TRACE("%sPAPI reset failed", fj_cpu.log_prefix.c_str()); } -} +// template +// static void collect_and_print_papi_metrics(fj_cpu_climber_t& fj_cpu) +// { +// if (!fj_cpu.papi_initialized) return; + +// std::vector values(fj_cpu.papi_events.size(), 0); +// int retval = PAPI_read(fj_cpu.papi_event_set, values.data()); +// if (retval != PAPI_OK) { +// CUOPT_LOG_TRACE("%sPAPI read failed", fj_cpu.log_prefix.c_str()); +// return; +// } + +// // Get thread ID +// pid_t tid = syscall(SYS_gettid); + +// // Build map of actual values indexed by event position +// std::vector all_values(8, -1); +// int value_idx = 0; +// for (size_t i = 0; i < fj_cpu.papi_events.size(); i++) { +// if (fj_cpu.papi_events[i] != -1) { all_values[i] = values[value_idx++]; } +// } + +// // Compute derived metrics +// double l1_miss_rate = -1.0; +// if (all_values[0] > 0 && all_values[1] != -1) { +// l1_miss_rate = (double)all_values[1] / all_values[0] * 100.0; +// } + +// double l2_miss_rate = -1.0; +// if (all_values[2] > 0 && all_values[3] != -1) { +// l2_miss_rate = (double)all_values[3] / all_values[2] * 100.0; +// } + +// // Lock to ensure thread-safe printing +// std::lock_guard lock(papi_print_mutex); + +// // Print everything on a single compact line +// CUOPT_LOG_DEBUG( +// "%sPAPI iter=%d tid=%d L1_DCA=%lld L1_DCM=%lld L2_DCA=%lld L2_DCM=%lld L3_TCA=%lld " +// "L3_TCM=%lld LD_INS=%lld SR_INS=%lld L1_miss=%.2f%% L2_miss=%.2f%%", +// fj_cpu.log_prefix.c_str(), +// fj_cpu.iterations, +// tid, +// all_values[0], +// all_values[1], +// all_values[2], +// all_values[3], +// all_values[4], +// all_values[5], +// all_values[6], +// all_values[7], +// l1_miss_rate, +// l2_miss_rate); + +// // Reset counters for the next 1000 iterations +// retval = PAPI_reset(fj_cpu.papi_event_set); +// if (retval != PAPI_OK) { CUOPT_LOG_TRACE("%sPAPI reset failed", fj_cpu.log_prefix.c_str()); } +// } template static void cleanup_papi(fj_cpu_climber_t& fj_cpu) @@ -442,6 +444,15 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, double mem_total_mb = (mem_loads_bytes + mem_stores_bytes) / 1e6; double mem_bandwidth_gb_per_sec = (mem_total_mb / 1000.0) / (time_window_ms / 1000.0); + // Build per-wrapper memory statistics string + std::stringstream wrapper_stats; + auto per_wrapper_stats = fj_cpu.memory_manifold.collect_per_wrapper(); + for (const auto& [name, loads, stores] : per_wrapper_stats) { + wrapper_stats << " " << name << "_loads=" << loads << " " << name << "_stores=" << stores; + } + + fj_cpu.memory_manifold.flush(); + // Print everything on a single line using precomputed features CUOPT_LOG_DEBUG( "%sCPUFJ_FEATURES iter=%d time_window=%.2f " @@ -457,7 +468,7 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, "cstr_coverage=%.4f var_coverage=%.4f " "L1_miss=%.2f L3_miss=%.2f loads_per_iter=%.0f stores_per_iter=%.0f " "viol_ratio=%.4f nnz_per_move=%.2f eval_intensity=%.2f " - "mem_loads_mb=%.3f mem_stores_mb=%.3f mem_total_mb=%.3f mem_bandwidth_gb_s=%.3f", + "mem_loads_mb=%.3f mem_stores_mb=%.3f mem_total_mb=%.3f mem_bandwidth_gb_s=%.3f%s", fj_cpu.log_prefix.c_str(), fj_cpu.iterations, time_window_ms, @@ -502,7 +513,8 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, mem_loads_mb, mem_stores_mb, mem_total_mb, - mem_bandwidth_gb_per_sec); + mem_bandwidth_gb_per_sec, + wrapper_stats.str().c_str()); // Reset window counters fj_cpu.nnz_processed_window = 0; @@ -520,6 +532,40 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, fj_cpu.unique_vars_accessed_window.clear(); } +// Local implementations that use instrumented vectors +template +static inline std::pair reverse_range_for_var(fj_cpu_climber_t& fj_cpu, + i_t var_idx) +{ + cuopt_assert(var_idx >= 0 && var_idx < fj_cpu.view.pb.n_variables, + "Variable should be within the range"); + return std::make_pair(fj_cpu.h_reverse_offsets[var_idx], fj_cpu.h_reverse_offsets[var_idx + 1]); +} + +template +static inline std::pair range_for_constraint(fj_cpu_climber_t& fj_cpu, + i_t cstr_idx) +{ + return std::make_pair(fj_cpu.h_offsets[cstr_idx], fj_cpu.h_offsets[cstr_idx + 1]); +} + +template +static inline bool check_variable_within_bounds(fj_cpu_climber_t& fj_cpu, + i_t var_idx, + f_t val) +{ + const f_t int_tol = fj_cpu.view.pb.tolerances.integrality_tolerance; + auto bounds = fj_cpu.h_var_bounds[var_idx].get(); + bool within_bounds = val <= (get_upper(bounds) + int_tol) && val >= (get_lower(bounds) - int_tol); + return within_bounds; +} + +template +static inline bool is_integer_var(fj_cpu_climber_t& fj_cpu, i_t var_idx) +{ + return var_t::INTEGER == fj_cpu.h_var_types[var_idx]; +} + template static inline bool tabu_check(fj_cpu_climber_t& fj_cpu, i_t var_idx, @@ -536,15 +582,15 @@ static inline bool tabu_check(fj_cpu_climber_t& fj_cpu, } template -static bool check_variable_feasibility(const fj_cpu_climber_t& fj_cpu, +static bool check_variable_feasibility(fj_cpu_climber_t& fj_cpu, bool check_integer = true) { for (i_t var_idx = 0; var_idx < fj_cpu.view.pb.n_variables; var_idx += 1) { auto val = fj_cpu.h_assignment[var_idx]; - bool feasible = fj_cpu.view.pb.check_variable_within_bounds(var_idx, val); + bool feasible = check_variable_within_bounds(fj_cpu, var_idx, val); if (!feasible) return false; - if (check_integer && fj_cpu.view.pb.is_integer_var(var_idx) && + if (check_integer && is_integer_var(fj_cpu, var_idx) && !fj_cpu.view.pb.is_integer(fj_cpu.h_assignment[var_idx])) return false; } @@ -568,7 +614,7 @@ static inline std::pair compute_score(fj_cpu_climber_t(fj_cpu, var_idx); fj_cpu.nnz_processed_window += (offset_end - offset_begin); // MEMORY OPS: Loop over all constraints involving this variable (avg_var_degree iterations) @@ -587,8 +633,17 @@ static inline std::pair compute_score(fj_cpu_climber_t( - fj_cpu.view, var_idx, delta, cstr_idx, cstr_coeff, c_lb, c_ub, fj_cpu.h_lhs[cstr_idx]); + auto [cstr_base_feas, cstr_bonus_robust] = + feas_score_constraint(fj_cpu.view, + var_idx, + delta, + cstr_idx, + cstr_coeff, + c_lb, + c_ub, + fj_cpu.h_lhs[cstr_idx], + fj_cpu.h_cstr_left_weights[cstr_idx], + fj_cpu.h_cstr_right_weights[cstr_idx]); base_feas_sum += cstr_base_feas; bonus_robust_sum += cstr_bonus_robust; @@ -696,7 +751,8 @@ static void update_weights(fj_cpu_climber_t& fj_cpu) } // Invalidate related cached move scores - auto [relvar_offset_begin, relvar_offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + auto [relvar_offset_begin, relvar_offset_end] = + range_for_constraint(fj_cpu, cstr_idx); // MEMORY OPS: Inner loop over variables in this constraint (avg_cstr_degree iterations per // outer iteration) for (auto i = relvar_offset_begin; i < relvar_offset_end; i++) { @@ -722,7 +778,7 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, cuopt_assert(var_idx < fj_cpu.view.pb.n_variables, "variable index out of bounds"); // Update the LHSs of all involved constraints. - auto [offset_begin, offset_end] = fj_cpu.view.pb.reverse_range_for_var(var_idx); + auto [offset_begin, offset_end] = reverse_range_for_var(fj_cpu, var_idx); // Track work metrics for regression model fj_cpu.nnz_processed_window += (offset_end - offset_begin); @@ -780,7 +836,8 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, cuopt_assert(isfinite(fj_cpu.h_lhs[cstr_idx]), "assignment should be finite"); // Invalidate related cached move scores - auto [relvar_offset_begin, relvar_offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + auto [relvar_offset_begin, relvar_offset_end] = + range_for_constraint(fj_cpu, cstr_idx); // MEMORY OPS: Inner loop over variables in this constraint (avg_cstr_degree iterations per // outer iteration) for (auto i = relvar_offset_begin; i < relvar_offset_end; i++) { @@ -798,14 +855,14 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, // update the assignment and objective proper // ARRAY READ: h_assignment[var_idx] - 1 read f_t new_val = fj_cpu.h_assignment[var_idx] + delta; - if (fj_cpu.view.pb.is_integer_var(var_idx)) { + if (is_integer_var(fj_cpu, var_idx)) { cuopt_assert(fj_cpu.view.pb.integer_equal(new_val, round(new_val)), "new_val is not integer"); new_val = round(new_val); } // ARRAY WRITE: h_assignment[var_idx] - 1 write fj_cpu.h_assignment[var_idx] = new_val; - cuopt_assert(fj_cpu.view.pb.check_variable_within_bounds(var_idx, new_val), + cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, new_val)), "assignment not within bounds"); cuopt_assert(isfinite(new_val), "assignment is not finite"); @@ -874,7 +931,7 @@ static thrust::tuple find_mtm_move( // collect all the variables that are involved in the target constraints // MEMORY OPS: Outer loop over target constraints (sample_size iterations, typically 15-100) for (size_t cstr_idx : target_cstrs) { - auto [offset_begin, offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + auto [offset_begin, offset_end] = range_for_constraint(fj_cpu, cstr_idx); // MEMORY OPS: Inner loop over variables in each constraint (avg_cstr_degree per outer // iteration) for (auto i = offset_begin; i < offset_end; i++) { @@ -891,7 +948,7 @@ static thrust::tuple find_mtm_move( // estimate the amount of nnzs to consider i_t nnz_sum = 0; for (auto var_idx : fj_cpu.iter_mtm_vars) { - auto [offset_begin, offset_end] = fj_cpu.view.pb.reverse_range_for_var(var_idx); + auto [offset_begin, offset_end] = reverse_range_for_var(fj_cpu, var_idx); nnz_sum += offset_end - offset_begin; } @@ -904,7 +961,7 @@ static thrust::tuple find_mtm_move( f_t cstr_tol = fj_cpu.view.get_corrected_tolerance(cstr_idx, c_lb, c_ub); cuopt_assert(cstr_idx < fj_cpu.h_cstr_lb.size(), "cstr_idx is out of bounds"); - auto [offset_begin, offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + auto [offset_begin, offset_end] = range_for_constraint(fj_cpu, cstr_idx); // MEMORY OPS: Inner loop over variables (avg_cstr_degree per outer iteration) for (auto i = offset_begin; i < offset_end; i++) { // early cached check @@ -914,12 +971,12 @@ static thrust::tuple find_mtm_move( // ARRAY READ: h_variables[i] - 1 read per iteration (cache hit) auto var_idx = fj_cpu.h_variables[i]; // ARRAY READ: h_assignment[var_idx] - 1 read per iteration (cache hit) - if (fj_cpu.view.pb.check_variable_within_bounds( - var_idx, fj_cpu.h_assignment[var_idx] + cached_move.first)) { + if (check_variable_within_bounds( + fj_cpu, var_idx, fj_cpu.h_assignment[var_idx] + cached_move.first)) { best_score = cached_move.second; best_move = fj_move_t{var_idx, cached_move.first}; } - // cuopt_assert(fj_cpu.view.pb.check_variable_within_bounds(var_idx, + // cuopt_assert(check_variable_within_bounds(fj_cpu, var_idx, // fj_cpu.h_assignment[var_idx] + cached_move.first), "best move not within bounds"); } fj_cpu.hit_count++; @@ -964,7 +1021,7 @@ static thrust::tuple find_mtm_move( c_ub, fj_cpu.h_assignment, fj_cpu.h_lhs); - if (fj_cpu.view.pb.is_integer_var(var_idx)) { + if (is_integer_var(fj_cpu, var_idx)) { new_val = cstr_coeff * sign > 0 ? floor(val + delta + fj_cpu.view.pb.tolerances.integrality_tolerance) : ceil(val + delta - fj_cpu.view.pb.tolerances.integrality_tolerance); @@ -980,7 +1037,7 @@ static thrust::tuple find_mtm_move( } } if (!isfinite(new_val)) continue; - cuopt_assert(fj_cpu.view.pb.check_variable_within_bounds(var_idx, new_val), + cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, new_val)), "new_val is not within bounds"); delta = new_val - val; // more permissive tabu in the case of local minima @@ -1034,7 +1091,7 @@ static thrust::tuple find_mtm_move( // CRITICAL: compute_score() does ~6-7 array reads per constraint involved auto [score, infeasibility] = compute_score(fj_cpu, var_idx, delta); - cuopt_assert(fj_cpu.view.pb.check_variable_within_bounds(var_idx, new_val), ""); + cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, new_val)), ""); cuopt_assert(isfinite(delta), ""); if (fj_cpu.view.move_numerically_stable( @@ -1096,7 +1153,7 @@ static void recompute_lhs(fj_cpu_climber_t& fj_cpu) // MEMORY OPS: CRITICAL LOOP - Loop over all constraints (n_cstrs iterations) // This is called periodically to recompute all LHS values from scratch for (i_t cstr_idx = 0; cstr_idx < fj_cpu.view.pb.n_constraints; ++cstr_idx) { - auto [offset_begin, offset_end] = fj_cpu.view.pb.range_for_constraint(cstr_idx); + auto [offset_begin, offset_end] = range_for_constraint(fj_cpu, cstr_idx); auto [c_lb, c_ub] = fj_cpu.cached_cstr_bounds[cstr_idx].get(); // MEMORY OPS: For each constraint, reads avg_cstr_degree elements from: // - h_coefficients[offset_begin:offset_end] - avg_cstr_degree reads @@ -1167,7 +1224,7 @@ static thrust::tuple find_lift_move( // ARRAY READ: h_var_bounds[var_idx] - 1 read per iteration (non-binary) f_t lfd_lb = get_lower(fj_cpu.h_var_bounds[var_idx].get()) - val; f_t lfd_ub = get_upper(fj_cpu.h_var_bounds[var_idx].get()) - val; - auto [offset_begin, offset_end] = fj_cpu.view.pb.reverse_range_for_var(var_idx); + auto [offset_begin, offset_end] = reverse_range_for_var(fj_cpu, var_idx); // MEMORY OPS: Inner loop over constraints involving this variable (avg_var_degree iterations // per outer iteration) for (i_t j = offset_begin; j < offset_end; j += 1) { @@ -1198,9 +1255,9 @@ static thrust::tuple find_lift_move( fj_cpu.h_lhs); if (cstr_coeff * sign < 0) { - if (fj_cpu.view.pb.is_integer_var(var_idx)) delta = ceil(delta); + if (is_integer_var(fj_cpu, var_idx)) delta = ceil(delta); } else { - if (fj_cpu.view.pb.is_integer_var(var_idx)) delta = floor(delta); + if (is_integer_var(fj_cpu, var_idx)) delta = floor(delta); } // skip this variable if there is no slack @@ -1210,7 +1267,7 @@ static thrust::tuple find_lift_move( } else { lfd_lb = 0; } - } else if (!fj_cpu.view.pb.check_variable_within_bounds(var_idx, val + delta)) { + } else if (!check_variable_within_bounds(fj_cpu, var_idx, val + delta)) { continue; } else { if (cstr_coeff * sign < 0) { @@ -1228,8 +1285,8 @@ static thrust::tuple find_lift_move( // invalid crossing bounds if (lfd_lb >= lfd_ub) { lfd_lb = lfd_ub = 0; } - if (!fj_cpu.view.pb.check_variable_within_bounds(var_idx, val + lfd_lb)) { lfd_lb = 0; } - if (!fj_cpu.view.pb.check_variable_within_bounds(var_idx, val + lfd_ub)) { lfd_ub = 0; } + if (!check_variable_within_bounds(fj_cpu, var_idx, val + lfd_lb)) { lfd_lb = 0; } + if (!check_variable_within_bounds(fj_cpu, var_idx, val + lfd_ub)) { lfd_ub = 0; } // Now that the life move domain is computed, compute the correct lift move cuopt_assert(isfinite(val), "invalid assignment value"); @@ -1276,12 +1333,12 @@ static void perturb(fj_cpu_climber_t& fj_cpu) f_t lb = ceil(std::max(get_lower(fj_cpu.h_var_bounds[var_idx].get()), -1e7)); f_t ub = floor(std::min(get_upper(fj_cpu.h_var_bounds[var_idx].get()), 1e7)); f_t val = lb + (ub - lb) * rng.next_double(); - if (fj_cpu.view.pb.is_integer_var(var_idx)) { + if (is_integer_var(fj_cpu, var_idx)) { val = std::round(val); val = std::min(std::max(val, lb), ub); } - cuopt_assert(fj_cpu.view.pb.check_variable_within_bounds(var_idx, val), + cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, val)), "value is out of bounds"); // ARRAY WRITE: h_assignment[var_idx] - 1 write per iteration fj_cpu.h_assignment[var_idx] = val; @@ -1408,7 +1465,7 @@ static void init_fj_cpu(fj_cpu_climber_t& fj_cpu, fj_cpu.cached_cstr_bounds.resize(fj_cpu.h_reverse_coefficients.size()); // MEMORY OPS: INITIALIZATION - Loop over all variables (n_vars iterations) for (i_t var_idx = 0; var_idx < (i_t)fj_cpu.view.pb.n_variables; ++var_idx) { - auto [offset_begin, offset_end] = fj_cpu.view.pb.reverse_range_for_var(var_idx); + auto [offset_begin, offset_end] = reverse_range_for_var(fj_cpu, var_idx); // MEMORY OPS: Inner loop over constraints per variable (avg_var_degree iterations per outer // iteration) for (i_t i = offset_begin; i < offset_end; ++i) { @@ -1651,17 +1708,29 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l std::chrono::duration_cast>(now - loop_start).count() * 1000.0; -#ifdef __linux__ - collect_and_print_papi_metrics(fj_cpu); -#endif + // #ifdef __linux__ + // collect_and_print_papi_metrics(fj_cpu); + // #endif // Collect memory statistics - auto [loads, stores] = fj_cpu.memory_manifold.collect_and_flush(); + auto [loads, stores] = fj_cpu.memory_manifold.collect(); // Log all features including memory statistics log_regression_features(fj_cpu, time_window_ms, total_time_ms, loads, stores); fj_cpu.last_feature_log_time = now; + + std::map features_map; + features_map["n_vars"] = (float)fj_cpu.h_reverse_offsets.size() - 1; + features_map["n_cstrs"] = (float)fj_cpu.h_offsets.size() - 1; + features_map["total_nnz"] = (float)fj_cpu.h_reverse_offsets.back(); + features_map["mem_total_mb"] = (float)(loads + stores) / 1e6; + float time_prediction = std::max( + (f_t)0.0, + (f_t)ceil(context.work_unit_predictors.cpufj_predictor.predict_scalar(features_map))); + CUOPT_LOG_DEBUG("FJ determ: Estimated time for 1000 iters: %f, error %f", + time_prediction, + time_prediction - time_window_ms); } cuopt_func_call(sanity_checks(fj_cpu)); diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cuh b/cpp/src/mip/feasibility_jump/fj_cpu.cuh index b5ff178707..27f9d79115 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cuh +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cuh @@ -28,33 +28,38 @@ template struct fj_cpu_climber_t { fj_cpu_climber_t() { +#define ADD_INSTRUMENTED(var) \ + std::make_pair(#var, std::ref(static_cast(var))) + // Initialize memory manifold with all ins_vector members - memory_manifold = instrumentation_manifold_t{h_reverse_coefficients, - h_reverse_constraints, - h_reverse_offsets, - h_coefficients, - h_offsets, - h_variables, - h_obj_coeffs, - h_var_bounds, - h_cstr_lb, - h_cstr_ub, - h_var_types, - h_is_binary_variable, - h_objective_vars, - h_binary_indices, - h_tabu_nodec_until, - h_tabu_noinc_until, - h_tabu_lastdec, - h_tabu_lastinc, - h_lhs, - h_lhs_sumcomp, - h_cstr_left_weights, - h_cstr_right_weights, - h_assignment, - h_best_assignment, - cached_cstr_bounds, - iter_mtm_vars}; + memory_manifold = instrumentation_manifold_t{ADD_INSTRUMENTED(h_reverse_coefficients), + ADD_INSTRUMENTED(h_reverse_constraints), + ADD_INSTRUMENTED(h_reverse_offsets), + ADD_INSTRUMENTED(h_coefficients), + ADD_INSTRUMENTED(h_offsets), + ADD_INSTRUMENTED(h_variables), + ADD_INSTRUMENTED(h_obj_coeffs), + ADD_INSTRUMENTED(h_var_bounds), + ADD_INSTRUMENTED(h_cstr_lb), + ADD_INSTRUMENTED(h_cstr_ub), + ADD_INSTRUMENTED(h_var_types), + ADD_INSTRUMENTED(h_is_binary_variable), + ADD_INSTRUMENTED(h_objective_vars), + ADD_INSTRUMENTED(h_binary_indices), + ADD_INSTRUMENTED(h_tabu_nodec_until), + ADD_INSTRUMENTED(h_tabu_noinc_until), + ADD_INSTRUMENTED(h_tabu_lastdec), + ADD_INSTRUMENTED(h_tabu_lastinc), + ADD_INSTRUMENTED(h_lhs), + ADD_INSTRUMENTED(h_lhs_sumcomp), + ADD_INSTRUMENTED(h_cstr_left_weights), + ADD_INSTRUMENTED(h_cstr_right_weights), + ADD_INSTRUMENTED(h_assignment), + ADD_INSTRUMENTED(h_best_assignment), + ADD_INSTRUMENTED(cached_cstr_bounds), + ADD_INSTRUMENTED(iter_mtm_vars)}; + +#undef ADD_INSTRUMENTED } fj_cpu_climber_t(const fj_cpu_climber_t& other) = delete; fj_cpu_climber_t& operator=(const fj_cpu_climber_t& other) = delete; diff --git a/cpp/src/mip/feasibility_jump/load_balancing.cuh b/cpp/src/mip/feasibility_jump/load_balancing.cuh index b6c9ea1f9d..379fdbde6c 100644 --- a/cpp/src/mip/feasibility_jump/load_balancing.cuh +++ b/cpp/src/mip/feasibility_jump/load_balancing.cuh @@ -336,8 +336,17 @@ __global__ void load_balancing_compute_scores_binary( auto c_lb = fj.constraint_lower_bounds_csr[csr_offset]; auto c_ub = fj.constraint_upper_bounds_csr[csr_offset]; - auto [cstr_base_feas, cstr_bonus_robust] = feas_score_constraint( - fj, var_idx, delta, cstr_idx, cstr_coeff, c_lb, c_ub, fj.incumbent_lhs[cstr_idx]); + auto [cstr_base_feas, cstr_bonus_robust] = + feas_score_constraint(fj, + var_idx, + delta, + cstr_idx, + cstr_coeff, + c_lb, + c_ub, + fj.incumbent_lhs[cstr_idx], + fj.cstr_left_weights[cstr_idx], + fj.cstr_right_weights[cstr_idx]); base_feas += cstr_base_feas; bonus_robust += cstr_bonus_robust; @@ -537,8 +546,17 @@ __launch_bounds__(TPB_loadbalance, 16) __global__ cuopt_assert(c_lb == fj.pb.constraint_lower_bounds[cstr_idx], "bound sanity check failed"); cuopt_assert(c_ub == fj.pb.constraint_upper_bounds[cstr_idx], "bound sanity check failed"); - auto [cstr_base_feas, cstr_bonus_robust] = feas_score_constraint( - fj, var_idx, delta, cstr_idx, cstr_coeff, c_lb, c_ub, fj.incumbent_lhs[cstr_idx]); + auto [cstr_base_feas, cstr_bonus_robust] = + feas_score_constraint(fj, + var_idx, + delta, + cstr_idx, + cstr_coeff, + c_lb, + c_ub, + fj.incumbent_lhs[cstr_idx], + fj.cstr_left_weights[cstr_idx], + fj.cstr_right_weights[cstr_idx]); base_feas += cstr_base_feas; bonus_robust += cstr_bonus_robust; diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 28be6dd4f4..30bf5ac06f 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -65,7 +65,7 @@ void local_search_t::start_cpufj_scratch_threads(population_t::infinity(); - cpu_fj_settings.iteration_limit = 10000; // would like 10 samples per instance + cpu_fj_settings.iteration_limit = 30000; // would like 10 samples per instance solution_t solution(*context.problem_ptr); thrust::fill(solution.handle_ptr->get_thrust_policy(), diff --git a/cpp/src/mip/solver_context.cuh b/cpp/src/mip/solver_context.cuh index 1be253d6f1..306fc0dc5e 100644 --- a/cpp/src/mip/solver_context.cuh +++ b/cpp/src/mip/solver_context.cuh @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,7 @@ namespace cuopt::linear_programming::detail { struct mip_solver_work_unit_predictors_t { work_unit_predictor_t fj_predictor{}; + work_unit_predictor_t cpufj_predictor{}; }; // Aggregate structure containing the global context of the solving process for convenience: diff --git a/cpp/src/utilities/memory_instrumentation.hpp b/cpp/src/utilities/memory_instrumentation.hpp index 2cf0d3b1ed..2975a1e360 100644 --- a/cpp/src/utilities/memory_instrumentation.hpp +++ b/cpp/src/utilities/memory_instrumentation.hpp @@ -28,7 +28,9 @@ #include #include +#include #include +#include #include #include @@ -90,33 +92,67 @@ class instrumentation_manifold_t { public: instrumentation_manifold_t() = default; - // Construct with initializer list of instrumented objects + // Construct with initializer list of (description, instrumented object) pairs instrumentation_manifold_t( - std::initializer_list> instrumented) - : instrumented_(instrumented) + std::initializer_list< + std::pair>> instrumented) { + for (const auto& [name, instr] : instrumented) { + instrumented_.insert_or_assign(name, instr); + } } - // Add an instrumented object to track - void add(memory_instrumentation_base_t& instrumented) { instrumented_.push_back(instrumented); } + // Add an instrumented object to track with a description + void add(const std::string& description, memory_instrumentation_base_t& instrumented) + { + instrumented_.insert_or_assign(description, std::ref(instrumented)); + } - // Collect total loads and stores from all instrumented objects, then flush all counters - std::pair collect_and_flush() + // Collect total loads and stores across all instrumented objects + std::pair collect() { size_t total_loads = 0; size_t total_stores = 0; - for (auto& instr : instrumented_) { + for (auto& [name, instr] : instrumented_) { total_loads += instr.get().byte_loads; total_stores += instr.get().byte_stores; - instr.get().reset_counters(); } return {total_loads, total_stores}; } + // Collect per-wrapper statistics + std::vector> collect_per_wrapper() + { + std::vector> results; + results.reserve(instrumented_.size()); + + for (auto& [name, instr] : instrumented_) { + results.emplace_back(name, instr.get().byte_loads, instr.get().byte_stores); + } + + return results; + } + + // Collect total loads and stores, then flush counters + std::pair collect_and_flush() + { + auto result = collect(); + flush(); + return result; + } + + void flush() + { + for (auto& [name, instr] : instrumented_) { + instr.get().reset_counters(); + } + } + private: - std::vector> instrumented_; + std::unordered_map> + instrumented_; }; #else @@ -126,11 +162,15 @@ class instrumentation_manifold_t { public: instrumentation_manifold_t() = default; instrumentation_manifold_t( - std::initializer_list>) + std::initializer_list< + std::pair>>) { } - void add(memory_instrumentation_base_t&) {} + void add(const std::string&, memory_instrumentation_base_t&) {} + std::pair collect() { return {0, 0}; } + std::vector> collect_per_wrapper() { return {}; } std::pair collect_and_flush() { return {0, 0}; } + void flush() {} }; #endif // CUOPT_ENABLE_MEMORY_INSTRUMENTATION @@ -262,15 +302,15 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { return ref_; } - // Allow implicit conversion to reference for functions expecting references - operator value_type&() { return ref_; } + // // Allow implicit conversion to reference for functions expecting references + // operator value_type&() { return ref_; } - operator const value_type&() const { return ref_; } + // operator const value_type&() const { return ref_; } - // Member access operator for structured types (e.g., type_2) - value_type* operator->() { return &ref_; } + // // Member access operator for structured types (e.g., type_2) + // value_type* operator->() { return &ref_; } - const value_type* operator->() const { return &ref_; } + // const value_type* operator->() const { return &ref_; } // Get underlying element reference (records a load) value_type& get() diff --git a/cpp/src/utilities/models/cpufj_predictor/header.h b/cpp/src/utilities/models/cpufj_predictor/header.h new file mode 100644 index 0000000000..800072da21 --- /dev/null +++ b/cpp/src/utilities/models/cpufj_predictor/header.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +class cpufj_predictor { + public: + union Entry { + int missing; + double fvalue; + int qvalue; + }; + + static int32_t get_num_target(void); + static void get_num_class(int32_t* out); + static int32_t get_num_feature(void); + static const char* get_threshold_type(void); + static const char* get_leaf_output_type(void); + static void predict(union Entry* data, int pred_margin, double* result); + static void postprocess(double* result); + static int quantize(double val, unsigned fid); + + // Feature names + static constexpr int NUM_FEATURES = 4; + static const char* feature_names[NUM_FEATURES]; +}; // class cpufj_data diff --git a/cpp/src/utilities/models/cpufj_predictor/main.cpp b/cpp/src/utilities/models/cpufj_predictor/main.cpp new file mode 100644 index 0000000000..3f4bf08374 --- /dev/null +++ b/cpp/src/utilities/models/cpufj_predictor/main.cpp @@ -0,0 +1,5412 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "header.h" + +#if defined(__clang__) || defined(__GNUC__) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif +#define N_TARGET 1 +#define MAX_N_CLASS 1 + +const unsigned char is_categorical[] = { + 0, + 0, + 0, + 0, +}; +static const int32_t num_class[] = { + 1, +}; + +int32_t cpufj_predictor::get_num_target(void) { return N_TARGET; } +void cpufj_predictor::get_num_class(int32_t* out) +{ + for (int i = 0; i < N_TARGET; ++i) { + out[i] = num_class[i]; + } +} +int32_t cpufj_predictor::get_num_feature(void) { return 4; } +const char* cpufj_predictor::get_threshold_type(void) { return "float64"; } +const char* cpufj_predictor::get_leaf_output_type(void) { return "float64"; } + +void cpufj_predictor::predict(union Entry* data, int pred_margin, double* result) +{ + // Quantize data + for (int i = 0; i < 4; ++i) { + if (data[i].missing != -1 && !is_categorical[i]) { + data[i].qvalue = quantize(data[i].fvalue, i); + } + } + + unsigned int tmp; + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += 156.82697390964927; + } else { + result[0] += 160.51072839782955; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 24))) { + result[0] += 167.07152558194775; + } else { + result[0] += 173.16044577743776; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 60))) { + result[0] += 197.67049116907555; + } else { + result[0] += 193.73228835720744; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 204.19401403625514; + } else { + result[0] += 233.21262399057034; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + result[0] += 271.7565681764123; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 384.56067848347016; + } else { + result[0] += 490.5143183873164; + } + } + } + if (LIKELY(false || (data[2].qvalue <= 50))) { + if (LIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[3].qvalue <= 114))) { + if (LIKELY(false || (data[3].qvalue <= 74))) { + result[0] += -13.943317103763608; + } else { + result[0] += -10.411451244915156; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -1.6607479643348968; + } else { + result[0] += 1.873995960655583; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += 13.317445150475752; + } else { + result[0] += -3.2833804644580917; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 42))) { + result[0] += 28.787436474976477; + } else { + result[0] += 22.808869505615306; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 116))) { + result[0] += 3.5889216009186664; + } else { + result[0] += 5.910052863589392; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 210))) { + result[0] += 89.99770637090484; + } else { + result[0] += 127.79448643063864; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 191.2644805975275; + } else { + result[0] += 286.4874098858173; + } + } + } + if (LIKELY(false || (data[2].qvalue <= 50))) { + if (LIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[3].qvalue <= 114))) { + if (LIKELY(false || (data[3].qvalue <= 76))) { + result[0] += -12.539408056743746; + } else { + result[0] += -9.311441710639189; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -1.4947050956018029; + } else { + result[0] += 1.6866257811476963; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += 11.98745272164608; + } else { + result[0] += -2.9554856143381034; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 22.718528792836054; + } else { + result[0] += 52.594838808257; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 116))) { + result[0] += 3.2306536812875586; + } else { + result[0] += 5.321595177064682; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 79.5197036158342; + } else { + result[0] += 112.02363049281628; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 172.34821010044644; + } else { + result[0] += 257.92682935697115; + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -11.281112483305671; + } else { + result[0] += -8.601904513350851; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 24))) { + result[0] += -4.457196969778106; + } else { + result[0] += 0.6775720991155234; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 60))) { + result[0] += 18.28399618068051; + } else { + result[0] += 15.34024990698243; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 23.56902428310345; + } else { + result[0] += 47.371626254377695; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + result[0] += 72.50774203412541; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 155.30278921274038; + } else { + result[0] += 232.21351482872598; + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[3].qvalue <= 114))) { + if (LIKELY(false || (data[3].qvalue <= 78))) { + result[0] += -10.157360048782069; + } else { + result[0] += -7.469901314898597; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -1.2836068490987675; + } else { + result[0] += 1.4928852675361874; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 148))) { + if (LIKELY(false || (data[0].qvalue <= 68))) { + result[0] += 11.862787216911165; + } else { + result[0] += -3.825179380397125; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + result[0] += 16.467439176084763; + } else { + result[0] += 22.870842463159736; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 116))) { + result[0] += -4.329979207208883; + } else { + result[0] += -2.4277052596041226; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 206))) { + result[0] += 62.899179793202485; + } else { + result[0] += 89.86758030525095; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 144.9081294228374; + } else { + result[0] += 209.06360369591349; + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[3].qvalue <= 118))) { + if (LIKELY(false || (data[3].qvalue <= 80))) { + result[0] += -9.111244309609868; + } else { + result[0] += -6.450300041406144; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 146))) { + result[0] += -0.908464880592677; + } else { + result[0] += 1.3456266411166913; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 144))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 9.598829002806253; + } else { + result[0] += -3.458980194428218; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + result[0] += 14.685884008167873; + } else { + result[0] += 19.70403341262041; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 116))) { + result[0] += -3.8977343521833427; + } else { + result[0] += -2.185981180004069; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 202))) { + result[0] += 53.636361895007624; + } else { + result[0] += 73.84635747136831; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 54))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 109.30218868014822; + } else { + result[0] += 130.5223189467278; + } + } else { + result[0] += 188.22157308443514; + } + } + } + if (LIKELY(false || (data[2].qvalue <= 50))) { + if (LIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[3].qvalue <= 114))) { + if (LIKELY(false || (data[3].qvalue <= 74))) { + result[0] += -8.242985704258855; + } else { + result[0] += -6.103964758757681; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 122))) { + result[0] += -3.0003459256849077; + } else { + result[0] += 0.5938833671659278; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 148))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += 9.054639032941743; + } else { + result[0] += -3.3592089510831986; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 18.040143668653474; + } else { + result[0] += 13.175784904763427; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 236))) { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 118))) { + result[0] += -3.214705374718561; + } else { + result[0] += 0.31848526393665993; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 202))) { + result[0] += 48.27447091272866; + } else { + result[0] += 66.18473238316001; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 97.70898775652987; + } else { + result[0] += 117.56467429340749; + } + } else { + result[0] += 169.45732965745194; + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -7.402034186828849; + } else { + result[0] += -5.63837490580214; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 24))) { + result[0] += -3.2673346602099738; + } else { + result[0] += 0.4720904756386771; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 60))) { + result[0] += 11.693457400016031; + } else { + result[0] += 10.03852332044065; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 15.437715046858271; + } else { + result[0] += 36.647436411890496; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + result[0] += 47.53118689085973; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 106.35664032988495; + } else { + result[0] += 152.56374281850964; + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -6.661840917407535; + } else { + result[0] += -5.074574846660258; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 24))) { + result[0] += -2.94088446825355; + } else { + result[0] += 0.4248859491848843; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 60))) { + result[0] += 10.52521173349777; + } else { + result[0] += 9.034789845952849; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 13.894155617598422; + } else { + result[0] += 33.00796459750472; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 42.823961164678224; + } else { + result[0] += 36.98781896591187; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 95.8378507576408; + } else { + result[0] += 137.35430146859977; + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 12))) { + result[0] += -5.922535414763472; + } else { + result[0] += -3.9535302977307456; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 24))) { + result[0] += -2.647051125523947; + } else { + result[0] += 0.38240139128726525; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 60))) { + result[0] += 9.473680911813272; + } else { + result[0] += 8.131417758482929; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 42))) { + result[0] += 12.474218356244899; + } else { + result[0] += 28.32144924784816; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 38.542156344073405; + } else { + result[0] += 33.34683068752289; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 86.359383424193; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 130.56753145370016; + } else { + result[0] += 115.99602850603911; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -5.403420533141926; + } else { + result[0] += -4.075259880702465; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 24))) { + result[0] += -2.3825756020703164; + } else { + result[0] += 0.34416491637505503; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 62))) { + result[0] += 6.690473585247339; + } else { + result[0] += 7.675205616748645; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 11.251804458709806; + } else { + result[0] += 26.917319955760036; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 34.68847201632427; + } else { + result[0] += 30.064251933097843; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 77.81834311684409; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 117.59088283047355; + } else { + result[0] += 104.46758514965971; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[3].qvalue <= 96))) { + if (UNLIKELY(false || (data[3].qvalue <= 22))) { + result[0] += -5.645610033293023; + } else { + result[0] += -4.712904234092419; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 68))) { + result[0] += -1.7467118855770272; + } else { + result[0] += -6.7806729780163355; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -3.3298901489085715; + } else { + result[0] += 0.9879303535654625; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 156))) { + result[0] += 4.765656733422152; + } else { + result[0] += 10.436091866890687; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 118))) { + result[0] += -19.231066348531115; + } else { + result[0] += -15.589280898150276; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 218))) { + if (UNLIKELY(false || (data[3].qvalue <= 200))) { + result[0] += 25.747728264474212; + } else { + result[0] += 40.04473103921778; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 64.38265010597043; + } else { + result[0] += 95.58272802311622; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[3].qvalue <= 98))) { + if (LIKELY(false || (data[3].qvalue <= 74))) { + result[0] += -4.409030071223973; + } else { + result[0] += -3.5647114194984613; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 68))) { + result[0] += -1.3542190966799321; + } else { + result[0] += -6.103673214709666; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -2.999404705305745; + } else { + result[0] += 0.8891527883879043; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 156))) { + result[0] += 4.289215943564471; + } else { + result[0] += 9.392568858155158; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 116))) { + result[0] += -17.598092511782436; + } else { + result[0] += -16.123547517184555; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 216))) { + if (UNLIKELY(false || (data[3].qvalue <= 200))) { + result[0] += 23.17394211472633; + } else { + result[0] += 35.171000449187375; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 55.995760587885144; + } else { + result[0] += 86.04514530858954; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 22))) { + if (LIKELY(false || (data[1].qvalue <= 12))) { + result[0] += -3.8888486100132043; + } else { + result[0] += -2.5693198969248536; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 0.301134908182464; + } else { + result[0] += 4.7321266664777495; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (UNLIKELY(false || (data[0].qvalue <= 60))) { + result[0] += 5.997381523357489; + } else { + result[0] += 5.0375213284435185; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 8.559482811493893; + } else { + result[0] += 22.27496138368804; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 25.27353965981968; + } else { + result[0] += 21.13871028184891; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 55.52418384593922; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 87.85257342192293; + } else { + result[0] += 76.03356082097153; + } + } + } + } + if (LIKELY(false || (data[2].qvalue <= 50))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[3].qvalue <= 90))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + result[0] += -3.5064268267703387; + } else { + result[0] += -6.065722823494794; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 68))) { + result[0] += -1.4039415524212295; + } else { + result[0] += -5.997225846159178; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -2.9652186181689757; + } else { + result[0] += 0.7283126984643262; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 154))) { + result[0] += 2.789907904600108; + } else { + result[0] += 7.718533347299168; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + result[0] += -18.00435797338746; + } else { + if (LIKELY(false || (data[3].qvalue <= 214))) { + if (UNLIKELY(false || (data[3].qvalue <= 198))) { + result[0] += 17.330223423965826; + } else { + result[0] += 27.46055877083792; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 46.12967429173014; + } else { + result[0] += 70.70123466855004; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[3].qvalue <= 102))) { + if (UNLIKELY(false || (data[3].qvalue <= 22))) { + result[0] += -3.888827166320002; + } else { + result[0] += -3.04723191570044; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 34))) { + result[0] += -0.7424769082893876; + } else { + result[0] += -5.398447803084307; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -2.670943097291571; + } else { + result[0] += 0.6554928174570391; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 158))) { + result[0] += 2.9919951855699423; + } else { + result[0] += 7.128361704676317; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 118))) { + result[0] += -16.31666198848907; + } else { + result[0] += -13.216809526331284; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 220))) { + if (LIKELY(false || (data[3].qvalue <= 206))) { + result[0] += 18.698365978047963; + } else { + result[0] += 30.323819222880072; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 54))) { + result[0] += 48.19287170494928; + } else { + result[0] += 66.97776871619591; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[0].qvalue <= 22))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -2.8871386766483713; + } else { + result[0] += -2.194022438199927; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 24))) { + result[0] += -1.2643512510325772; + } else { + result[0] += 0.2946143237550731; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 42))) { + if (UNLIKELY(false || (data[0].qvalue <= 28))) { + result[0] += 6.18334904875928; + } else { + result[0] += 3.4917401374748973; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 42))) { + result[0] += 6.637851347005048; + } else { + result[0] += 17.628961355963423; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 18.427701194716725; + } else { + result[0] += 15.068101975917816; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 39.624109077872816; + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 65.43777984806364; + } else { + result[0] += 54.793414778446135; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[2].qvalue <= 52))) { + if (LIKELY(false || (data[3].qvalue <= 86))) { + result[0] += -2.5929923519240776; + } else { + result[0] += -1.2308422313969718; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 116))) { + result[0] += -16.777735206670346; + } else { + result[0] += -15.496213790630472; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + if (LIKELY(false || (data[3].qvalue <= 166))) { + result[0] += 3.153837529703604; + } else { + result[0] += 10.653343513120348; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 1.2707685237496547; + } else { + result[0] += -7.577582551412722; + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 50))) { + if (LIKELY(false || (data[0].qvalue <= 70))) { + if (UNLIKELY(false || (data[3].qvalue <= 216))) { + result[0] += 5.086264511434565; + } else { + result[0] += 4.004129196876291; + } + } else { + result[0] += 18.011869379087937; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 214))) { + if (UNLIKELY(false || (data[3].qvalue <= 200))) { + result[0] += 12.716813581606937; + } else { + result[0] += 21.19923662562386; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + result[0] += 33.0928592549443; + } else { + result[0] += 48.6196284504888; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (LIKELY(false || (data[3].qvalue <= 104))) { + result[0] += -2.2842871895780763; + } else { + result[0] += -0.6940580648135516; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 118))) { + result[0] += -14.884714915484075; + } else { + result[0] += -12.21852856355555; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (LIKELY(false || (data[0].qvalue <= 68))) { + result[0] += 1.1320691013866366; + } else { + result[0] += -6.824281661647207; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 168))) { + result[0] += 3.133632898542928; + } else { + result[0] += 10.26787174244473; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 56))) { + if (UNLIKELY(false || (data[3].qvalue <= 216))) { + if (UNLIKELY(false || (data[0].qvalue <= 66))) { + result[0] += 0.3519373175810123; + } else { + result[0] += 4.883879225540344; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 234))) { + result[0] += 3.4475335994592777; + } else { + result[0] += 4.289605817529637; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 214))) { + if (UNLIKELY(false || (data[3].qvalue <= 198))) { + result[0] += 10.944053551737898; + } else { + result[0] += 18.520104025075135; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 31.9020659327635; + } else { + result[0] += 47.722870097304835; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (LIKELY(false || (data[3].qvalue <= 84))) { + result[0] += -2.1126641262849875; + } else { + result[0] += -1.0034167734194253; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 114))) { + result[0] += -14.666533134028597; + } else { + result[0] += -13.091642320227521; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -2.67357385169376; + } else { + result[0] += 0.317539563282027; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 172))) { + result[0] += 2.9924052201762423; + } else { + result[0] += 10.018166909712392; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 56))) { + if (UNLIKELY(false || (data[3].qvalue <= 216))) { + if (UNLIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 0.3173503774211839; + } else { + result[0] += 4.396111790242462; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 234))) { + result[0] += 3.1028551632609607; + } else { + result[0] += 3.8610566164274576; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 212))) { + if (UNLIKELY(false || (data[3].qvalue <= 196))) { + result[0] += 9.079411213991389; + } else { + result[0] += 15.631981353661537; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 230))) { + result[0] += 24.953231107660457; + } else { + result[0] += 38.732870695055425; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (LIKELY(false || (data[3].qvalue <= 106))) { + result[0] += -1.8507802734637893; + } else { + result[0] += -0.4780342275095875; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 116))) { + result[0] += -12.281350727645211; + } else { + result[0] += -11.20561954498291; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -2.4082419443490855; + } else { + result[0] += 0.2857906276722831; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 164))) { + result[0] += 1.8193510979340806; + } else { + result[0] += 7.12452591373636; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 56))) { + if (UNLIKELY(false || (data[3].qvalue <= 216))) { + if (UNLIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 0.28616249149215633; + } else { + result[0] += 3.957059242184614; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 236))) { + result[0] += 2.8268370028874497; + } else { + result[0] += 3.686134112733879; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 212))) { + if (LIKELY(false || (data[3].qvalue <= 200))) { + result[0] += 9.321451071849161; + } else { + result[0] += 15.02085895338778; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 24.772053316683984; + } else { + result[0] += 39.09600893416962; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 150))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (LIKELY(false || (data[3].qvalue <= 124))) { + result[0] += -1.6348218331553022; + } else { + result[0] += 0.16446761997202702; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 114))) { + result[0] += -11.997166693345555; + } else { + result[0] += -10.593410837279578; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 176))) { + result[0] += -0.26077036500572887; + } else { + result[0] += 0.26721227295908545; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 174))) { + result[0] += 3.50927093938965; + } else { + result[0] += 9.399961201456877; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 56))) { + if (UNLIKELY(false || (data[3].qvalue <= 218))) { + if (UNLIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 0.25803962657163887; + } else { + result[0] += 3.413014759107607; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 230))) { + result[0] += 2.36220596981744; + } else { + result[0] += 2.9185929060991107; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 218))) { + if (LIKELY(false || (data[3].qvalue <= 204))) { + result[0] += 9.258923590762315; + } else { + result[0] += 15.922847318580807; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 25.53935408466994; + } else { + result[0] += 35.194870544491394; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + if (LIKELY(false || (data[2].qvalue <= 52))) { + if (LIKELY(false || (data[3].qvalue <= 82))) { + result[0] += -1.5559766022197978; + } else { + result[0] += -0.6821089851736815; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 118))) { + result[0] += -9.793275858100605; + } else { + result[0] += -7.646164855957032; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -2.185564344146035; + } else { + result[0] += 0.23136153315509006; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 164))) { + result[0] += 1.4281790213369323; + } else { + result[0] += 5.862740580154604; + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 52))) { + if (LIKELY(false || (data[0].qvalue <= 70))) { + if (UNLIKELY(false || (data[3].qvalue <= 214))) { + result[0] += 3.1620136193354766; + } else { + result[0] += 2.374787311783622; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 12.394186796439712; + } else { + result[0] += 7.221490024859086; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 222))) { + if (LIKELY(false || (data[3].qvalue <= 202))) { + result[0] += 7.724518171783787; + } else { + result[0] += 14.107049853741623; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 25.09318574808453; + } else { + result[0] += 33.43533238246624; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 150))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 110))) { + result[0] += -1.348448669905975; + } else { + result[0] += 0.3221871974704163; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 126))) { + result[0] += -8.519520394077722; + } else { + result[0] += -4.128400709863002; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 16))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + result[0] += 0.20971876754977306; + } else { + result[0] += 2.000764232879495; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 176))) { + result[0] += 3.2250018408327294; + } else { + result[0] += 9.091428443343196; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 56))) { + if (UNLIKELY(false || (data[2].qvalue <= 46))) { + result[0] += -0.07772423557166397; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 206))) { + result[0] += 7.465886690298717; + } else { + result[0] += 2.199134462061703; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 212))) { + if (UNLIKELY(false || (data[3].qvalue <= 196))) { + result[0] += 5.510852046313868; + } else { + result[0] += 10.349949332911033; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + result[0] += 17.106146295672758; + } else { + result[0] += 26.416688466239478; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 152))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 120))) { + result[0] += -1.1991422347150693; + } else { + result[0] += 0.46237283922421873; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 126))) { + result[0] += -7.6684254163131875; + } else { + result[0] += -3.7949608540857165; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 16))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += 0.18039368614868972; + } else { + result[0] += 1.85527472375075; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 172))) { + result[0] += 2.72143013117147; + } else { + result[0] += 6.582942954251957; + } + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 52))) { + if (LIKELY(false || (data[0].qvalue <= 70))) { + if (UNLIKELY(false || (data[3].qvalue <= 214))) { + result[0] += 2.622990881969633; + } else { + result[0] += 1.9174728788131057; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 10.617562740828639; + } else { + result[0] += 5.75643968641758; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 214))) { + if (UNLIKELY(false || (data[3].qvalue <= 196))) { + result[0] += 4.573403401881785; + } else { + result[0] += 9.61596988143479; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 17.169701079810455; + } else { + result[0] += 25.958129802402993; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 152))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (LIKELY(false || (data[3].qvalue <= 92))) { + result[0] += -1.130718809808625; + } else { + result[0] += -0.2762627382286883; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 114))) { + result[0] += -8.235156694808095; + } else { + result[0] += -6.951272825766222; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 32))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += 0.16235716809342288; + } else { + result[0] += 1.707373695884848; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 176))) { + result[0] += 2.8296562924600828; + } else { + result[0] += 7.488739806649071; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 56))) { + if (UNLIKELY(false || (data[0].qvalue <= 66))) { + result[0] += -0.32786249211636087; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 206))) { + result[0] += 6.490628203531107; + } else { + result[0] += 1.7818277591800613; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 212))) { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + result[0] += 3.807200463755219; + } else { + result[0] += 8.111758332078471; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 226))) { + result[0] += 13.144779116022248; + } else { + result[0] += 20.863007074513657; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 70))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 22))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -1.0274672289090832; + } else { + result[0] += -0.6823801552731604; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + result[0] += 1.0283586431978333; + } else { + result[0] += -0.23713978185064843; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[0].qvalue <= 62))) { + result[0] += 2.1905285297360657; + } else { + result[0] += 6.504341335474002; + } + } else { + result[0] += 0.9435717207184171; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (UNLIKELY(false || (data[1].qvalue <= 74))) { + if (UNLIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 3.5741883312100953; + } else { + result[0] += 4.626694408655167; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 74))) { + result[0] += 6.38776066924514; + } else { + result[0] += 7.9433524366525505; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 27.524095430198624; + } else { + result[0] += 17.93763900587164; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 154))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 108))) { + result[0] += -0.8928303502649112; + } else { + result[0] += 0.1926267660463724; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 122))) { + result[0] += -7.0074780337366285; + } else { + result[0] += -3.8229517707148526; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 16))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + result[0] += 0.17887769899557107; + } else { + result[0] += 1.2261906364605684; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 178))) { + result[0] += 2.7207480610255765; + } else { + result[0] += 8.3997445237607; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 56))) { + if (LIKELY(false || (data[3].qvalue <= 236))) { + if (UNLIKELY(false || (data[3].qvalue <= 218))) { + result[0] += 1.928126817594764; + } else { + result[0] += 1.3411342581129906; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + result[0] += 1.9525385201789491; + } else { + result[0] += 2.4821401437493256; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 212))) { + if (UNLIKELY(false || (data[3].qvalue <= 198))) { + result[0] += 3.713535595855519; + } else { + result[0] += 7.180656615177431; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 226))) { + result[0] += 11.193661980597666; + } else { + result[0] += 17.555807665127727; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[3].qvalue <= 154))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -0.7003375835348792; + } else { + result[0] += -3.7302757340894694; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 116))) { + result[0] += -6.599387818444294; + } else { + result[0] += -5.72489429399885; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 32))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += 0.15262275808585868; + } else { + result[0] += 1.1954652973697173; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 178))) { + result[0] += 2.4487311442319593; + } else { + result[0] += 7.490679951544426; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 56))) { + if (UNLIKELY(false || (data[3].qvalue <= 214))) { + if (UNLIKELY(false || (data[0].qvalue <= 66))) { + result[0] += -0.4618247323919986; + } else { + result[0] += 2.1528753353821717; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 234))) { + result[0] += 1.2097848819662675; + } else { + result[0] += 1.752838457626999; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 212))) { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + result[0] += 2.420871486643817; + } else { + result[0] += 6.029272785166614; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 230))) { + result[0] += 10.227185956634905; + } else { + result[0] += 15.974606997518203; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 124))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += -2.865659975119095; + } else { + result[0] += -0.6896181889893488; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 116))) { + result[0] += -5.944063382797502; + } else { + result[0] += -5.094536373408761; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 52))) { + if (LIKELY(false || (data[3].qvalue <= 160))) { + result[0] += 0.6397310298301389; + } else { + result[0] += 2.9220460179454832; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 138))) { + result[0] += -2.666624663971065; + } else { + result[0] += 0.4270094967652458; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[3].qvalue <= 236))) { + if (UNLIKELY(false || (data[3].qvalue <= 218))) { + result[0] += 1.521282712441143; + } else { + result[0] += 1.0810978861849587; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + result[0] += 1.582856585015188; + } else { + result[0] += 2.0611044482921446; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 216))) { + if (LIKELY(false || (data[3].qvalue <= 206))) { + result[0] += 4.439645479292941; + } else { + result[0] += 7.705468000634004; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 11.427783097865925; + } else { + result[0] += 17.19202195652448; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 124))) { + if (LIKELY(false || (data[2].qvalue <= 28))) { + if (UNLIKELY(false || (data[3].qvalue <= 24))) { + result[0] += -1.2367334931972405; + } else { + result[0] += -0.568679959736126; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 112))) { + result[0] += -8.32192050869182; + } else { + result[0] += -5.006960492312537; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 42))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 0.4550818668115695; + } else { + result[0] += -3.0743532402463507; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 160))) { + result[0] += 0.13097623312980142; + } else { + result[0] += 2.786328266103951; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + if (LIKELY(false || (data[1].qvalue <= 72))) { + result[0] += 1.0925176197221544; + } else { + result[0] += 7.582882419296458; + } + } else { + result[0] += -3.9018135684874005; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 224))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 4.15326719153954; + } else { + result[0] += 7.678964746257113; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 78))) { + result[0] += 19.747446627865543; + } else { + result[0] += 12.500771902929218; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 124))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (UNLIKELY(false || (data[3].qvalue <= 22))) { + result[0] += -1.167979990569673; + } else { + result[0] += -0.5133742356132379; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 114))) { + result[0] += -5.603649494204901; + } else { + result[0] += -4.409295198837661; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 52))) { + if (LIKELY(false || (data[3].qvalue <= 160))) { + result[0] += 0.5480854924955038; + } else { + result[0] += 2.363109929409574; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 138))) { + result[0] += -2.302846027254356; + } else { + result[0] += 0.34306161983695516; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 0.9832828581009277; + } else { + result[0] += 6.834192669120016; + } + } else { + result[0] += -3.521149036128347; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 224))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 3.738054988080166; + } else { + result[0] += 6.911557381148448; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 78))) { + result[0] += 17.78481683344929; + } else { + result[0] += 11.252482971264655; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 56))) { + if (LIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.5726678321736987; + } else { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += -0.14924717663033055; + } else { + result[0] += 6.235019242422922; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 60))) { + result[0] += 1.162497192248416; + } else { + result[0] += 2.0120424899788536; + } + } else { + result[0] += 0.47169244387428955; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (UNLIKELY(false || (data[2].qvalue <= 42))) { + if (UNLIKELY(false || (data[2].qvalue <= 34))) { + result[0] += -0.357844108120637; + } else { + result[0] += 1.7605654400603017; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 70))) { + result[0] += 6.52940587259161; + } else { + result[0] += 3.3080480937743113; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 16.01724653501452; + } else { + result[0] += 8.753001223254058; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 156))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -0.4137669463081151; + } else { + result[0] += -2.6409307308959122; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 114))) { + result[0] += -5.376507786444898; + } else { + result[0] += -4.389560249429967; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 32))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += 0.025008362302337356; + } else { + result[0] += 1.019241657971192; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 72))) { + result[0] += 2.1411528979907484; + } else { + result[0] += -0.47484663873831007; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 0.8378079743141229; + } else { + result[0] += 5.832806958458091; + } + } else { + result[0] += -3.500358439422236; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 222))) { + if (LIKELY(false || (data[3].qvalue <= 206))) { + result[0] += 2.851221265160206; + } else { + result[0] += 5.566558597933049; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 78))) { + result[0] += 14.425347984875641; + } else { + result[0] += 9.578878534245574; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 124))) { + if (LIKELY(false || (data[2].qvalue <= 28))) { + if (UNLIKELY(false || (data[3].qvalue <= 22))) { + result[0] += -0.9195523527649098; + } else { + result[0] += -0.3719639557405976; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 74))) { + result[0] += -7.786786672274272; + } else { + result[0] += -3.9362138288079898; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += 0.6022720589428014; + } else { + result[0] += 7.092300986713834; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 190))) { + result[0] += -2.1043147125075716; + } else { + result[0] += 0.12674302444068922; + } + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 50))) { + if (LIKELY(false || (data[3].qvalue <= 232))) { + if (UNLIKELY(false || (data[3].qvalue <= 218))) { + result[0] += 1.031090335758177; + } else { + result[0] += 0.5392873356886984; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + result[0] += 0.9291700147586669; + } else { + result[0] += 1.5196304502959777; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 216))) { + if (LIKELY(false || (data[3].qvalue <= 206))) { + result[0] += 2.5829880193644055; + } else { + result[0] += 4.763908216949989; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 6.935099835666378; + } else { + result[0] += 10.036216690922197; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 164))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 72))) { + if (LIKELY(false || (data[1].qvalue <= 6))) { + result[0] += -0.43590572608869044; + } else { + result[0] += -2.4316455018950878; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 42))) { + result[0] += -0.04380141068835597; + } else { + result[0] += 1.7110383905553694; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 144))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += -3.530168794490836; + } else { + result[0] += -1.0214699319685518; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 148))) { + result[0] += -6.775844377790179; + } else { + result[0] += -10.790508870091934; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 42))) { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[3].qvalue <= 188))) { + result[0] += -0.02275360025237618; + } else { + result[0] += -0.5838206132835355; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 1.7417272007672004; + } else { + result[0] += 0.660660463610099; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 208))) { + if (LIKELY(false || (data[3].qvalue <= 200))) { + result[0] += 1.9156509211867474; + } else { + result[0] += 3.135028625615758; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + result[0] += 4.960511180617512; + } else { + result[0] += 8.359091821368834; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 194))) { + if (LIKELY(false || (data[3].qvalue <= 160))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 70))) { + result[0] += -0.42133291745401114; + } else { + result[0] += -0.010851476443648929; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -2.4223382968632965; + } else { + result[0] += -6.899222971006882; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 16))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += -0.03413012978096565; + } else { + result[0] += 0.5317297186286644; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 2.211563570567631; + } else { + result[0] += -0.16486923946003673; + } + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 50))) { + if (LIKELY(false || (data[3].qvalue <= 234))) { + if (UNLIKELY(false || (data[3].qvalue <= 218))) { + result[0] += 0.8620758848535985; + } else { + result[0] += 0.47621186848670294; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + result[0] += 0.8507697571985345; + } else { + result[0] += 1.3028689973708651; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 216))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 2.41099243454025; + } else { + result[0] += 4.090173047276771; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 5.632316419302413; + } else { + result[0] += 8.200667309946828; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 166))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (LIKELY(false || (data[3].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + result[0] += -0.38327232438457565; + } else { + result[0] += -2.1415671585396967; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 68))) { + result[0] += -0.048803059306061464; + } else { + result[0] += -1.7470427286358465; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 118))) { + if (UNLIKELY(false || (data[3].qvalue <= 114))) { + result[0] += -3.8643859740923037; + } else { + result[0] += -3.030297649090965; + } + } else { + result[0] += -1.569212761065539; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 42))) { + if (UNLIKELY(false || (data[0].qvalue <= 54))) { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + result[0] += 1.1504605549094933; + } else { + result[0] += -0.02918860846727582; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + result[0] += 2.077977738321157; + } else { + result[0] += 0.5359649431721014; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 212))) { + if (LIKELY(false || (data[3].qvalue <= 202))) { + result[0] += 1.652849064266966; + } else { + result[0] += 2.954255485765737; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 76))) { + result[0] += -0.5669657233750427; + } else { + result[0] += 5.676198660468206; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 168))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[2].qvalue <= 38))) { + if (UNLIKELY(false || (data[3].qvalue <= 40))) { + result[0] += -0.5244874333393534; + } else { + result[0] += -0.18256259456848412; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 74))) { + result[0] += -5.51355459890058; + } else { + result[0] += 1.411684886661277; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 144))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += -2.6606175581636338; + } else { + result[0] += -0.47356034191920265; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 152))) { + result[0] += -5.942664098495093; + } else { + result[0] += -10.448295749482655; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 38))) { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[3].qvalue <= 188))) { + result[0] += -0.011800565116617453; + } else { + result[0] += -0.5202224220078566; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 1.7775453851705354; + } else { + result[0] += 0.49081054656235107; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 222))) { + if (LIKELY(false || (data[3].qvalue <= 204))) { + result[0] += 1.614115694236453; + } else { + result[0] += 3.0704803932483227; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 76))) { + result[0] += -0.5108923716568848; + } else { + result[0] += 6.589366885775977; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 42))) { + if (LIKELY(false || (data[0].qvalue <= 30))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.2892059588012491; + } else { + if (LIKELY(false || (data[0].qvalue <= 14))) { + result[0] += -0.08035600794549216; + } else { + result[0] += -0.4720559073325542; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 0.8967780238095124; + } else { + result[0] += 6.595205778394427; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + result[0] += -0.3520288227121683; + } else { + result[0] += 0.38974464605775183; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (UNLIKELY(false || (data[2].qvalue <= 38))) { + if (LIKELY(false || (data[1].qvalue <= 64))) { + result[0] += 0.7589099157010143; + } else { + result[0] += -1.366375525846731; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 66))) { + result[0] += 4.22967240474964; + } else { + result[0] += 1.4943104257462279; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 9.129350705249179; + } else { + result[0] += 3.068747078527702; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 168))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[2].qvalue <= 42))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -0.18545114512070737; + } else { + result[0] += -1.7692822476814012; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 74))) { + result[0] += -4.801651736039382; + } else { + result[0] += 1.3646239105274842; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 142))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += -2.5169209574656417; + } else { + result[0] += -0.18660289084267964; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 148))) { + result[0] += -4.541809815605482; + } else { + result[0] += -8.652151740789414; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 1.3663672464257006; + } else { + result[0] += 3.04170718867983; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 0.2559705229975156; + } else { + result[0] += 2.9168273909132068; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 4.97017162342233; + } else { + result[0] += 11.503874847499691; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 0.4665647305038452; + } else { + result[0] += 5.509817603230935; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 162))) { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + result[0] += -1.3283728127725134; + } else { + result[0] += -6.130559274355571; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 62))) { + result[0] += -0.2896825481817099; + } else { + result[0] += -0.07162487032383137; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 118))) { + if (UNLIKELY(false || (data[3].qvalue <= 114))) { + result[0] += -3.1167449558905838; + } else { + result[0] += -2.3599501347059975; + } + } else { + result[0] += -1.059417066398789; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 42))) { + if (UNLIKELY(false || (data[0].qvalue <= 54))) { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + result[0] += 0.834224641552816; + } else { + result[0] += -0.015370587395285587; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + result[0] += 0.9254950158058017; + } else { + result[0] += 0.36421699256211726; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 212))) { + if (LIKELY(false || (data[3].qvalue <= 200))) { + result[0] += 1.0215455914435474; + } else { + result[0] += 1.9619948363613522; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 76))) { + result[0] += -0.6542972311475775; + } else { + result[0] += 3.9645617071617623; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 42))) { + if (LIKELY(false || (data[0].qvalue <= 22))) { + if (LIKELY(false || (data[1].qvalue <= 14))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.22003805724566253; + } else { + result[0] += -0.05306048893366094; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 16))) { + result[0] += -1.3451441339924273; + } else { + result[0] += -1.5488482760393083; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + if (UNLIKELY(false || (data[2].qvalue <= 10))) { + result[0] += 0.21580833438485533; + } else { + result[0] += 1.177993229883637; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 56))) { + result[0] += -0.2994307611075257; + } else { + result[0] += 0.305894106833898; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (UNLIKELY(false || (data[2].qvalue <= 34))) { + if (LIKELY(false || (data[2].qvalue <= 32))) { + result[0] += 0.6300423600302785; + } else { + result[0] += -0.4677024667015657; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 60))) { + result[0] += 2.92097058914907; + } else { + result[0] += 0.9749046067206072; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 7.280391073314689; + } else { + result[0] += 1.8221294908582069; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 170))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[2].qvalue <= 42))) { + if (UNLIKELY(false || (data[3].qvalue <= 20))) { + result[0] += -0.5336229335357556; + } else { + result[0] += -0.12326033481423136; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 120))) { + result[0] += -3.7782573749400954; + } else { + result[0] += 1.2031945074524026; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 142))) { + if (LIKELY(false || (data[3].qvalue <= 136))) { + result[0] += -2.0138202324018293; + } else { + result[0] += 0.4287253040269907; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 150))) { + result[0] += -4.26031586774496; + } else { + result[0] += -8.189678469057437; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[3].qvalue <= 188))) { + result[0] += 0.03067279449262314; + } else { + result[0] += -0.4287195475348111; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 1.8796244512104765; + } else { + result[0] += 0.3056819085450995; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 226))) { + if (LIKELY(false || (data[3].qvalue <= 202))) { + result[0] += 0.9636725612848687; + } else { + result[0] += 1.932067083622997; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 76))) { + result[0] += -0.6860055204538198; + } else { + result[0] += 4.570361189431599; + } + } + } + } + if (LIKELY(false || (data[1].qvalue <= 42))) { + if (LIKELY(false || (data[0].qvalue <= 22))) { + if (LIKELY(false || (data[1].qvalue <= 14))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.1814554734769651; + } else { + result[0] += -0.03493688255810839; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 16))) { + result[0] += -1.1580987454621139; + } else { + result[0] += -1.3413217625073497; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 0.6065784994119716; + } else { + result[0] += 6.64156315122332; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 56))) { + result[0] += -0.2710747772797691; + } else { + result[0] += 0.24590059195978556; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (UNLIKELY(false || (data[2].qvalue <= 38))) { + if (LIKELY(false || (data[1].qvalue <= 64))) { + result[0] += 0.52839377206309; + } else { + result[0] += -1.3097734584676666; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 66))) { + result[0] += 3.0327157128432702; + } else { + result[0] += 0.8301613199220086; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 6.102585921419179; + } else { + result[0] += 1.1868023468270625; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 170))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[2].qvalue <= 42))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -0.1099508760937471; + } else { + result[0] += -1.4799074079672025; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 74))) { + result[0] += -3.9533722562056326; + } else { + result[0] += 1.0527497525643543; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 142))) { + if (LIKELY(false || (data[3].qvalue <= 136))) { + result[0] += -1.874856650108779; + } else { + result[0] += 0.36151445149430145; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 152))) { + result[0] += -4.013347158834968; + } else { + result[0] += -7.940386810302735; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + if (LIKELY(false || (data[3].qvalue <= 206))) { + result[0] += 0.8194996574746792; + } else { + result[0] += 1.9073466437063578; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 0.1914437036803978; + } else { + result[0] += 1.9462096802861646; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 3.445255372400778; + } else { + result[0] += 9.341778161720354; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 0.3243846697838024; + } else { + result[0] += 3.292591407943689; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 160))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + result[0] += -0.9774807565883121; + } else { + result[0] += -5.423777811527253; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += -0.12073383773519127; + } else { + result[0] += 0.9911425235059078; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 142))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += -1.8435700376685866; + } else { + result[0] += -0.10737506746612407; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 148))) { + result[0] += -3.3288382873336477; + } else { + result[0] += -5.702344606187609; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + if (LIKELY(false || (data[3].qvalue <= 202))) { + result[0] += 0.6121670947641649; + } else { + result[0] += 1.5478825951517399; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 0.15159187149918565; + } else { + result[0] += 1.7525244521134302; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 3.1021024062818072; + } else { + result[0] += 8.417132546913868; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 0.29207001641087005; + } else { + result[0] += 2.9643452592336215; + } + } + } + } + if (LIKELY(false || (data[2].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.14003554272994065; + } else { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 46))) { + result[0] += -0.015514364763912126; + } else { + result[0] += 1.0510222973719732; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 30))) { + result[0] += -0.27117390740511166; + } else { + result[0] += 0.14354185812943476; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (LIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 62))) { + result[0] += 0.5018898936445311; + } else { + result[0] += 2.5336810498326523; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 2.3642130662022995; + } else { + result[0] += 0.07374194009502717; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 4.874216369284443; + } else { + result[0] += 0.44699526391786304; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 170))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (UNLIKELY(false || (data[3].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + result[0] += -0.35060793036231724; + } else { + result[0] += -5.047665471633276; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 38))) { + result[0] += -0.07028608501406199; + } else { + result[0] += 0.7109949202510606; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 144))) { + if (LIKELY(false || (data[3].qvalue <= 122))) { + result[0] += -1.8132749471478289; + } else { + result[0] += -0.4798894299710876; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 152))) { + result[0] += -3.6392164039153325; + } else { + result[0] += -6.78748151870001; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + if (LIKELY(false || (data[0].qvalue <= 32))) { + result[0] += 0.6818792886595983; + } else { + result[0] += 2.0346700524088903; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 70))) { + result[0] += 0.1650370627648028; + } else { + result[0] += 1.4122047675458287; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 2.743139011175984; + } else { + result[0] += 7.534331532576863; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 0.25562844944051427; + } else { + result[0] += 2.4035809405106767; + } + } + } + } + if (LIKELY(false || (data[2].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.1149636651708558; + } else { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 46))) { + result[0] += -0.005786597801570078; + } else { + result[0] += 0.9536320919638706; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 30))) { + result[0] += -0.25774071878779176; + } else { + result[0] += 0.12494917135148927; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (LIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += 0.416251413237658; + } else { + result[0] += 4.65468264502448; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 1.9891757155656817; + } else { + result[0] += 0.06062405904969436; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 4.150901784204815; + } else { + result[0] += 0.1636860781066988; + } + } + } + if (LIKELY(false || (data[2].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.10346745600044038; + } else { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 46))) { + result[0] += -0.005207970482558494; + } else { + result[0] += 0.8583114141430566; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 30))) { + result[0] += -0.2319702347159344; + } else { + result[0] += 0.11246252282811312; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (LIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 62))) { + result[0] += 0.36237019282195543; + } else { + result[0] += 2.185361301747475; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 1.7916299525787094; + } else { + result[0] += 0.05456246786985419; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 3.7383578847668653; + } else { + result[0] += 0.1474179036193099; + } + } + } + if (LIKELY(false || (data[2].qvalue <= 20))) { + if (LIKELY(false || (data[0].qvalue <= 22))) { + if (LIKELY(false || (data[1].qvalue <= 14))) { + if (LIKELY(false || (data[0].qvalue <= 16))) { + result[0] += -0.07824623744678852; + } else { + result[0] += 2.4147477472795025; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 20))) { + result[0] += -0.9602189832977626; + } else { + result[0] += -1.2336897440823642; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[2].qvalue <= 10))) { + result[0] += 0.17808383312156362; + } else { + result[0] += 0.7764366538076664; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 30))) { + result[0] += -0.20877644063379633; + } else { + result[0] += 0.10122370049456518; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (LIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += 0.3366817124844217; + } else { + result[0] += 3.9891647478052095; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 1.613702605033743; + } else { + result[0] += 0.04910696472777197; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 3.3668153467865807; + } else { + result[0] += 0.13276691204199761; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 170))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + if (LIKELY(false || (data[2].qvalue <= 28))) { + result[0] += -0.8003296552631252; + } else { + result[0] += -5.168554219404857; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 28))) { + result[0] += -0.04191612245860841; + } else { + result[0] += -0.31322033502373364; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 142))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += -1.6260716001993314; + } else { + result[0] += -0.07253028186242319; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 152))) { + result[0] += -3.059761641596405; + } else { + result[0] += -6.156701689220611; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (LIKELY(false || (data[1].qvalue <= 36))) { + if (UNLIKELY(false || (data[3].qvalue <= 176))) { + result[0] += 0.8382331706298907; + } else { + result[0] += 0.1470265483651418; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 68))) { + result[0] += 1.4136662728343927; + } else { + result[0] += 0.42639604831684264; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (UNLIKELY(false || (data[1].qvalue <= 76))) { + result[0] += 0.2137965369719358; + } else { + result[0] += 1.5807652149640596; + } + } else { + result[0] += 3.582011053264141; + } + } + } + if (LIKELY(false || (data[2].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.0809220032676647; + } else { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 46))) { + result[0] += 0.009295531913092564; + } else { + result[0] += 0.7015704698474159; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 56))) { + result[0] += -0.18839625520645842; + } else { + result[0] += 0.13815172995417008; + } + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (LIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += 0.2833961104028503; + } else { + result[0] += 3.9494598141232053; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 1.3130535747634955; + } else { + result[0] += 0.04139692407062909; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 2.875092684213369; + } else { + result[0] += -0.03741247869342383; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 198))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 170))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += -0.9280781442850904; + } else { + result[0] += -0.05519963885812408; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 18))) { + result[0] += 0.13575882302728218; + } else { + result[0] += 1.3530812716367793; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -1.0690103616905782; + } else { + result[0] += -2.214087039734459; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + result[0] += -0.6739287079004651; + } else { + result[0] += 0.08192197004299273; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + if (LIKELY(false || (data[1].qvalue <= 72))) { + result[0] += 0.16779935065337956; + } else { + result[0] += 2.7936280922346484; + } + } else { + result[0] += -6.01828142584824; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 216))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 0.6464965410008258; + } else { + result[0] += 1.2127821355938402; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 56))) { + result[0] += 2.304393523269968; + } else { + result[0] += -0.03369406329707866; + } + } + } + } + if (LIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 44))) { + if (LIKELY(false || (data[2].qvalue <= 6))) { + result[0] += -0.053129047160861614; + } else { + result[0] += -0.6797773812157768; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 50))) { + result[0] += 0.4863998644119598; + } else { + result[0] += 3.517865025900506; + } + } + } else { + result[0] += -0.18996639918014335; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (UNLIKELY(false || (data[0].qvalue <= 36))) { + if (LIKELY(false || (data[0].qvalue <= 34))) { + result[0] += 0.21126964469127507; + } else { + result[0] += 0.9292041399773837; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 0.38898047466443736; + } else { + result[0] += 0.0343316630215836; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 2.3603220532788822; + } else { + result[0] += -0.030345338012543195; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 198))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 170))) { + if (LIKELY(false || (data[2].qvalue <= 12))) { + result[0] += -0.03196203652937306; + } else { + result[0] += -0.32291491116843224; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 14))) { + result[0] += 0.1059633693894297; + } else { + result[0] += 1.1109217701835943; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -0.9750124564919093; + } else { + result[0] += -2.011326445104133; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + result[0] += -0.6277616333681295; + } else { + result[0] += 0.052653140828700706; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + if (LIKELY(false || (data[1].qvalue <= 72))) { + result[0] += 0.14758938512729353; + } else { + result[0] += 2.5144118215047015; + } + } else { + result[0] += -5.434481533329662; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 218))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 0.5607584407428231; + } else { + result[0] += 1.097345812808785; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 56))) { + result[0] += 2.188365555677249; + } else { + result[0] += -0.027329945962853226; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 196))) { + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[3].qvalue <= 174))) { + if (UNLIKELY(false || (data[3].qvalue <= 16))) { + result[0] += -0.339980565686603; + } else { + result[0] += -0.03185759232203992; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 42))) { + result[0] += 0.1301433477915698; + } else { + result[0] += 1.580000992364498; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 190))) { + result[0] += -1.685654765519544; + } else { + result[0] += -1.0927866830676793; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + result[0] += -0.5651196538004228; + } else { + result[0] += -0.10798605171852133; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + if (LIKELY(false || (data[0].qvalue <= 74))) { + result[0] += 0.13680401406260853; + } else { + result[0] += 2.8238424551720716; + } + } else { + result[0] += -4.904288046301866; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 226))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 0.45463644431627187; + } else { + result[0] += 1.0130458030200111; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 2.236542576950049; + } else { + result[0] += -0.024613159223933895; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 198))) { + if (LIKELY(false || (data[1].qvalue <= 72))) { + if (LIKELY(false || (data[3].qvalue <= 172))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -0.032568909202472296; + } else { + result[0] += -0.4469851314428541; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 30))) { + result[0] += 0.11294361883379918; + } else { + result[0] += 1.0056842141087612; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 190))) { + result[0] += -1.517244761721867; + } else { + result[0] += -0.9837414394446418; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + result[0] += -0.508728386740654; + } else { + result[0] += 0.03608265112965852; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 0.11915447057376925; + } else { + result[0] += 2.0825805265428143; + } + } else { + result[0] += -4.425821080207825; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 226))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 0.4592645630622676; + } else { + result[0] += 0.9118048684410376; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 2.0132186895684288; + } else { + result[0] += -0.022167118571104448; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 198))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 160))) { + if (LIKELY(false || (data[2].qvalue <= 12))) { + result[0] += -0.020052189264742216; + } else { + result[0] += -0.4390079826397957; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 18))) { + result[0] += 0.06723042230135214; + } else { + result[0] += 0.6321455651905148; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 192))) { + if (LIKELY(false || (data[3].qvalue <= 142))) { + result[0] += -0.6849792752988858; + } else { + result[0] += -1.5843785915044357; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + result[0] += -0.45796421690851963; + } else { + result[0] += 0.026405314510040745; + } + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 50))) { + if (LIKELY(false || (data[3].qvalue <= 230))) { + if (UNLIKELY(false || (data[3].qvalue <= 218))) { + result[0] += 0.3385478130008445; + } else { + result[0] += -0.10764744508292204; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 236))) { + result[0] += 0.20488484351232758; + } else { + result[0] += 0.47811499378647015; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 212))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 0.4209993070324332; + } else { + result[0] += 0.7287778763739126; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 1.187064839378982; + } else { + result[0] += -0.01996407508027334; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 176))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[0].qvalue <= 60))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -0.027774129306039258; + } else { + result[0] += -0.8385662967485399; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 120))) { + result[0] += -3.519087969462077; + } else { + result[0] += 0.7293787751512967; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 148))) { + if (LIKELY(false || (data[3].qvalue <= 118))) { + result[0] += -1.1172824090308644; + } else { + result[0] += -0.20902441584933884; + } + } else { + result[0] += -5.111647744634573; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + result[0] += 1.8609278771357982; + } else { + result[0] += 0.12480108283901199; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 196))) { + result[0] += -0.08227578925901015; + } else { + result[0] += 0.25625433402161485; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.9637861349021059; + } else { + result[0] += 5.291859932724311; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.9927995753498028; + } else { + result[0] += 0.14113094589752082; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 160))) { + if (LIKELY(false || (data[2].qvalue <= 12))) { + if (LIKELY(false || (data[3].qvalue <= 130))) { + if (UNLIKELY(false || (data[3].qvalue <= 46))) { + result[0] += -0.13887715472257542; + } else { + result[0] += 0.01749027468530986; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += 0.8747182175833713; + } else { + result[0] += 3.3873552828056868; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + result[0] += -2.0719160557169487; + } else { + result[0] += -0.9527323590531762; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 112))) { + result[0] += -4.278540461588714; + } else { + result[0] += -0.22532110219787005; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + if (LIKELY(false || (data[3].qvalue <= 204))) { + result[0] += 0.13994283376545083; + } else { + result[0] += 0.628766931454757; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 168))) { + result[0] += -0.2077112285433431; + } else { + result[0] += 0.0971080345193495; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.8677912951323141; + } else { + result[0] += 4.768073993215755; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.8939668307876266; + } else { + result[0] += 0.1270565048333717; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 160))) { + if (LIKELY(false || (data[2].qvalue <= 12))) { + if (LIKELY(false || (data[3].qvalue <= 130))) { + if (UNLIKELY(false || (data[3].qvalue <= 36))) { + result[0] += -0.166060933557046; + } else { + result[0] += 0.004294648853588787; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += 0.7873344634071392; + } else { + result[0] += 3.0512455477455793; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + if (UNLIKELY(false || (data[1].qvalue <= 16))) { + result[0] += 3.321445830033885; + } else { + result[0] += -1.8410162796558962; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 0.5648229020291158; + } else { + result[0] += -0.40508307941792243; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (LIKELY(false || (data[1].qvalue <= 38))) { + if (LIKELY(false || (data[3].qvalue <= 218))) { + result[0] += 0.116824997205243; + } else { + result[0] += 0.0017106091707403482; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 200))) { + result[0] += 0.09749333921964695; + } else { + result[0] += 0.512061380766423; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (UNLIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.8049728208270159; + } else { + result[0] += 0.11438560255422983; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.7813580416089511; + } else { + result[0] += 4.296131911715682; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 160))) { + if (LIKELY(false || (data[2].qvalue <= 12))) { + if (LIKELY(false || (data[3].qvalue <= 132))) { + if (UNLIKELY(false || (data[3].qvalue <= 50))) { + result[0] += -0.10165509214748221; + } else { + result[0] += 0.027573987237460126; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += 0.8965004316498251; + } else { + result[0] += 2.7484863860847417; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + result[0] += -1.6997008955624044; + } else { + result[0] += -0.678268726322754; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 118))) { + result[0] += -1.114816829956962; + } else { + result[0] += -0.07990083268080249; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (LIKELY(false || (data[3].qvalue <= 178))) { + result[0] += 0.08501821194709441; + } else { + result[0] += 0.7073547907959752; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 196))) { + result[0] += -0.09598099681424313; + } else { + result[0] += 0.1882562171309855; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.7035334691233133; + } else { + result[0] += 3.8709025872240264; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 76))) { + result[0] += -0.03477044912415934; + } else { + result[0] += 0.6388646830943916; + } + } + } + } + if (LIKELY(false || (data[2].qvalue <= 4))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.034231165787913284; + } else { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += -0.008538500447320395; + } else { + result[0] += 0.030661489227495748; + } + } + } else { + result[0] += -0.6560679714832831; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[2].qvalue <= 46))) { + if (LIKELY(false || (data[0].qvalue <= 46))) { + result[0] += 0.05367898474501495; + } else { + result[0] += 0.44735028827783085; + } + } else { + result[0] += 6.229511664254325; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + result[0] += -0.14752365398422523; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 0.38398941999281766; + } else { + result[0] += 0.04404838897933778; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 176))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (UNLIKELY(false || (data[3].qvalue <= 0))) { + if (LIKELY(false || (data[2].qvalue <= 28))) { + result[0] += -0.7549177495924217; + } else { + result[0] += -4.158142198324204; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 42))) { + result[0] += -0.02530038694314765; + } else { + result[0] += 0.5546607631795957; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 148))) { + if (LIKELY(false || (data[3].qvalue <= 136))) { + result[0] += -0.6813185032064286; + } else { + result[0] += 0.29119811621696806; + } + } else { + result[0] += -4.5671472818360614; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (LIKELY(false || (data[1].qvalue <= 40))) { + if (LIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 0.06089688550626267; + } else { + result[0] += -0.9885366824653841; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 180))) { + result[0] += 0.8258845435182254; + } else { + result[0] += 5.761587441940307; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (UNLIKELY(false || (data[3].qvalue <= 196))) { + result[0] += -0.07740265934103074; + } else { + result[0] += 0.16502792587020448; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 80))) { + result[0] += 0.3005847261842887; + } else { + result[0] += 1.4347149255319134; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 152))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + result[0] += -0.010806768205029583; + } else { + result[0] += -3.3904279910193553; + } + } else { + result[0] += 3.6360500988061872; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 56))) { + if (UNLIKELY(false || (data[1].qvalue <= 48))) { + result[0] += -1.3970841696833907; + } else { + result[0] += -0.5310959589164045; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 136))) { + result[0] += -0.43780724551513983; + } else { + result[0] += 0.38160829548283537; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 238))) { + if (LIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + result[0] += 0.1260601616574519; + } else { + result[0] += 2.3280856465403716; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 188))) { + result[0] += -6.748794538567707; + } else { + result[0] += 0.03388208818968886; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.4861732172767124; + } else { + result[0] += 3.34139486286105; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.6939914549665678; + } else { + result[0] += 0.012280274946395664; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 176))) { + if (LIKELY(false || (data[1].qvalue <= 54))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + result[0] += -0.464813733628163; + } else { + result[0] += -3.2180759784840705; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 48))) { + result[0] += -0.07920781023396078; + } else { + result[0] += 0.019886892967067187; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 146))) { + if (UNLIKELY(false || (data[1].qvalue <= 60))) { + result[0] += -1.3944523714625319; + } else { + result[0] += -0.6633490458105974; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 58))) { + result[0] += -0.4361867431137107; + } else { + result[0] += 0.09866640502908253; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (LIKELY(false || (data[1].qvalue <= 40))) { + if (LIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 0.04221310244085489; + } else { + result[0] += -0.8607172810069976; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 180))) { + result[0] += 0.7308448858222341; + } else { + result[0] += 5.17753285443306; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 198))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 0.12604388146867565; + } else { + result[0] += -0.5198739781442024; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + result[0] += 0.0001483644827354292; + } else { + result[0] += 0.4382489570445441; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 12))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += 0.2715686357969588; + } else { + result[0] += -0.17382652834003876; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 24))) { + result[0] += -0.5295756487690885; + } else { + result[0] += -1.247911300102006; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -4.132038760807204; + } else { + result[0] += -2.048248645525712; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 236))) { + if (LIKELY(false || (data[3].qvalue <= 152))) { + if (LIKELY(false || (data[2].qvalue <= 12))) { + result[0] += 0.009045345990511855; + } else { + result[0] += -0.36819514234182105; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 0.15071853324608264; + } else { + result[0] += 0.0023504781977204342; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.4615429381822286; + } else { + result[0] += 2.967287079375618; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.39428092203572945; + } else { + result[0] += -0.027793250841637182; + } + } + } + } + if (LIKELY(false || (data[2].qvalue <= 4))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (LIKELY(false || (data[1].qvalue <= 2))) { + if (UNLIKELY(false || (data[0].qvalue <= 0))) { + result[0] += 0.02095625550210984; + } else { + result[0] += -0.02470681499709372; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + result[0] += -0.006936135990179306; + } else { + result[0] += 0.028313332943848205; + } + } + } else { + result[0] += -0.45814704971029124; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[2].qvalue <= 46))) { + if (LIKELY(false || (data[0].qvalue <= 46))) { + result[0] += 0.059813599712526144; + } else { + result[0] += 0.4052626695215156; + } + } else { + result[0] += 6.220781436647688; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + result[0] += -0.14287609636708531; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 0.28909888374082054; + } else { + result[0] += 0.0223884762447394; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 178))) { + if (LIKELY(false || (data[1].qvalue <= 52))) { + if (UNLIKELY(false || (data[3].qvalue <= 10))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + result[0] += -0.20717493847839827; + } else { + result[0] += -2.852040849850889; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 30))) { + result[0] += -0.008160190303522096; + } else { + result[0] += 0.27599937314740675; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 146))) { + if (UNLIKELY(false || (data[1].qvalue <= 60))) { + result[0] += -1.1881260380224656; + } else { + result[0] += -0.5625438965153544; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 58))) { + result[0] += -0.403338839639409; + } else { + result[0] += 0.10268439573411683; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 184))) { + if (LIKELY(false || (data[1].qvalue <= 40))) { + if (LIKELY(false || (data[3].qvalue <= 182))) { + result[0] += -0.013368157221764328; + } else { + result[0] += 0.11043304821096991; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 180))) { + result[0] += 1.5817916361431956; + } else { + result[0] += 4.191132561870824; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 236))) { + if (UNLIKELY(false || (data[3].qvalue <= 196))) { + result[0] += -0.05418531103810077; + } else { + result[0] += 0.11582636097487908; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 80))) { + result[0] += 0.21679116682674598; + } else { + result[0] += 0.9937182303717; + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (LIKELY(false || (data[0].qvalue <= 74))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + result[0] += -0.020289773669981; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + result[0] += 0.07888539366069491; + } else { + result[0] += -0.0043942118674004035; + } + } + } else { + result[0] += -0.8819353631826549; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 1.166342015953883; + } else { + result[0] += -0.21953414862872633; + } + } + if (LIKELY(false || (data[3].qvalue <= 178))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + if (LIKELY(false || (data[3].qvalue <= 128))) { + result[0] += -0.014985389955920162; + } else { + result[0] += 0.5030502722795092; + } + } else { + result[0] += 3.209527899147808; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[3].qvalue <= 146))) { + result[0] += -1.359497708335802; + } else { + result[0] += -0.048409546375607654; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 0.2462483312344813; + } else { + result[0] += -0.15689708895777443; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (LIKELY(false || (data[1].qvalue <= 40))) { + if (UNLIKELY(false || (data[3].qvalue <= 182))) { + result[0] += -0.011608086477160455; + } else { + result[0] += 0.054071549124002416; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 180))) { + result[0] += 1.4247202960054501; + } else { + result[0] += 4.292557497723102; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 196))) { + if (LIKELY(false || (data[1].qvalue <= 72))) { + result[0] += 0.11744769877683264; + } else { + result[0] += -0.6228392828110428; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + result[0] += -0.01810292778067102; + } else { + result[0] += 0.33713691230011644; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 32))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += 0.271129972670566; + } else { + result[0] += -0.0968952898723599; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 24))) { + result[0] += -0.4419965803483365; + } else { + result[0] += -1.1427374036977256; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -2.964990935176611; + } else { + result[0] += -2.0024242342435397; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 236))) { + if (LIKELY(false || (data[3].qvalue <= 152))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + result[0] += 0.019913351953155762; + } else { + result[0] += -0.3451557406567103; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 68))) { + result[0] += 0.09431825657210167; + } else { + result[0] += -0.040080507458776934; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.2811388814670214; + } else { + result[0] += 2.54007776661795; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.33998703221387494; + } else { + result[0] += -0.11096001409545336; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 54))) { + if (LIKELY(false || (data[0].qvalue <= 46))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 18))) { + result[0] += -0.10381124910582794; + } else { + result[0] += -0.036053796621203345; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 18))) { + result[0] += -0.4795079805215589; + } else { + result[0] += -0.3343803406439497; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (UNLIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -1.3594113743305207; + } else { + result[0] += -0.8394250588417054; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -3.2252649987262227; + } else { + result[0] += -1.8098834428420432; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 234))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += 0.03700457930168985; + } else { + result[0] += 17.47663833459218; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 138))) { + result[0] += -0.5601782767579584; + } else { + result[0] += 0.017180070918654807; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.2387817133710211; + } else { + result[0] += -4.100601076265661; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 0.843944808030713; + } else { + result[0] += -0.2201947167753442; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 200))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 188))) { + if (LIKELY(false || (data[0].qvalue <= 66))) { + result[0] += -0.0005495605667856106; + } else { + result[0] += -3.58726428370322; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 66))) { + result[0] += -0.3697917053822814; + } else { + result[0] += 2.6442676259341997; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 194))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + result[0] += -0.1322526748337809; + } else { + result[0] += -0.9165228440058644; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 198))) { + result[0] += 0.015399111888784775; + } else { + result[0] += -0.03293383786820958; + } + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 50))) { + if (UNLIKELY(false || (data[3].qvalue <= 206))) { + result[0] += -1.74668638865153; + } else { + if (UNLIKELY(false || (data[3].qvalue <= 214))) { + result[0] += 0.41404376746989746; + } else { + result[0] += -0.04320938041815753; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 208))) { + if (LIKELY(false || (data[0].qvalue <= 72))) { + result[0] += 0.15385322801835455; + } else { + result[0] += 0.8175728843952048; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 0.4756074244340375; + } else { + result[0] += -0.1983103516972138; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 56))) { + if (LIKELY(false || (data[0].qvalue <= 24))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 26))) { + result[0] += -0.08484652763250765; + } else { + result[0] += -0.028152113688230847; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 6))) { + result[0] += -0.4321762504373764; + } else { + result[0] += -0.2651707672412985; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -1.0555429015159608; + } else { + result[0] += -0.7582275236447653; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -2.9167088172746745; + } else { + result[0] += -1.4978527782972042; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 234))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[2].qvalue <= 38))) { + result[0] += 0.02016281760562264; + } else { + result[0] += 0.5381136571935687; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + result[0] += -0.4649008843759111; + } else { + result[0] += 0.06919020340221031; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.15702109026459932; + } else { + result[0] += 2.1580531330984467; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.21686901261145552; + } else { + result[0] += -0.1001601769605728; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 42))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 8))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += 0.19641092388804365; + } else { + result[0] += -0.055276022085065206; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 8))) { + result[0] += -0.3392746616594763; + } else { + result[0] += -0.7843794295296335; + } + } + } else { + if (LIKELY(false || (data[2].qvalue <= 44))) { + result[0] += -2.238581113219261; + } else { + result[0] += -1.3538284465441337; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 200))) { + if (LIKELY(false || (data[1].qvalue <= 62))) { + if (LIKELY(false || (data[3].qvalue <= 188))) { + result[0] += 0.017158471900924056; + } else { + result[0] += 1.4687521383872566; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 192))) { + result[0] += -0.48506636615045484; + } else { + result[0] += -0.047229123064139726; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += -0.003343629263232336; + } else { + result[0] += -3.737171672379098; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.24658213105746754; + } else { + result[0] += 0.7366530590168956; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 60))) { + if (LIKELY(false || (data[0].qvalue <= 24))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 18))) { + result[0] += -0.07999229789885372; + } else { + result[0] += -0.024486695046509094; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 6))) { + result[0] += -0.3591096819916043; + } else { + result[0] += -0.19716339864139187; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -0.8995184892360121; + } else { + result[0] += -0.6091088518301646; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -2.423594094981318; + } else { + result[0] += -1.2236526361795572; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 236))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[2].qvalue <= 38))) { + result[0] += 0.017590391024004715; + } else { + result[0] += 0.48376995639076664; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 192))) { + result[0] += -0.4336376838327545; + } else { + result[0] += 0.04774652419020946; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.08141260086235247; + } else { + result[0] += 1.8715363113490904; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.2612265991556262; + } else { + result[0] += -0.11460675569674739; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 178))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + if (LIKELY(false || (data[3].qvalue <= 128))) { + result[0] += -0.010014087252989079; + } else { + result[0] += 0.44165882919228355; + } + } else { + result[0] += 2.767749158126721; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + result[0] += -1.265884517270139; + } else { + result[0] += -0.326555396705971; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 34))) { + result[0] += 0.17874279127753523; + } else { + result[0] += -0.10461815372106785; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (LIKELY(false || (data[1].qvalue <= 40))) { + if (UNLIKELY(false || (data[3].qvalue <= 182))) { + result[0] += -0.0291664181175232; + } else { + result[0] += 0.0321031669500698; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 180))) { + result[0] += 1.2655710841109187; + } else { + result[0] += 3.8452706585121152; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 200))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 0.07346629730705066; + } else { + result[0] += -0.2790375844518317; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + result[0] += -0.030938073075268954; + } else { + result[0] += 0.2584663489831829; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 8))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 8))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += 0.1897708646960375; + } else { + result[0] += -0.10591389661914655; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 8))) { + result[0] += -0.27429189760805434; + } else { + result[0] += -0.6438250486029867; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 22))) { + result[0] += -1.9822659349441527; + } else { + result[0] += -1.1856181401364942; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 176))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += 0.004593246195533139; + } else { + result[0] += 2.4929798120346622; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + result[0] += -0.9223952289248065; + } else { + result[0] += -0.044695212630605684; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (LIKELY(false || (data[1].qvalue <= 42))) { + result[0] += 0.0022744264617153534; + } else { + result[0] += 0.9887394270605384; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + result[0] += -0.05980165741450547; + } else { + result[0] += 0.06763695160928662; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 58))) { + if (LIKELY(false || (data[0].qvalue <= 46))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += 0.23353499906984243; + } else { + result[0] += -0.030567457307068176; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 6))) { + result[0] += -0.3026360332604611; + } else { + result[0] += -0.15352749305440105; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (UNLIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -0.9208640919129055; + } else { + result[0] += -0.4870238716204962; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -2.028915634984555; + } else { + result[0] += -0.9910290916149432; + } + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 230))) { + if (LIKELY(false || (data[3].qvalue <= 218))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 0.012556621103070601; + } else { + result[0] += 0.27622010195921237; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + result[0] += 0.4583737829879677; + } else { + result[0] += -0.1957218213752224; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.13572258094742976; + } else { + result[0] += 1.654014792332844; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 0.10699664077497309; + } else { + result[0] += -0.26287929402538607; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 34))) { + if (LIKELY(false || (data[1].qvalue <= 20))) { + if (LIKELY(false || (data[1].qvalue <= 0))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += 0.21031418917361988; + } else { + result[0] += -0.05299115840815619; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 8))) { + result[0] += -0.21890068136134136; + } else { + result[0] += -0.5242896185723835; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 22))) { + result[0] += -1.6681880847613018; + } else { + result[0] += -0.9499344626594992; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 230))) { + if (LIKELY(false || (data[3].qvalue <= 218))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += 0.003964550672321244; + } else { + result[0] += 0.248609910955958; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 80))) { + result[0] += -0.17615619642542008; + } else { + result[0] += 0.41270129405980494; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.1074364755511174; + } else { + result[0] += -3.3754073861750165; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 56))) { + result[0] += 0.5143961430823207; + } else { + result[0] += -0.23675266527233682; + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 10))) { + if (UNLIKELY(false || (data[2].qvalue <= 0))) { + if (LIKELY(false || (data[1].qvalue <= 4))) { + if (LIKELY(false || (data[0].qvalue <= 6))) { + result[0] += -0.02091550687275058; + } else { + result[0] += 0.05471821127697738; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 8))) { + result[0] += -0.06265624329964264; + } else { + result[0] += -0.003474992384641048; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 4))) { + if (LIKELY(false || (data[0].qvalue <= 2))) { + result[0] += -0.05813859439852901; + } else { + result[0] += 1.685043735887323; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 2))) { + result[0] += 0.005356865798300517; + } else { + result[0] += -0.009951643674742359; + } + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + if (LIKELY(false || (data[1].qvalue <= 26))) { + result[0] += 0.04421518555621426; + } else { + result[0] += 0.3861890671974087; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 46))) { + result[0] += 2.733613769617948; + } else { + result[0] += 5.786070743288313; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 26))) { + if (LIKELY(false || (data[1].qvalue <= 50))) { + result[0] += -0.482797800051755; + } else { + result[0] += 2.5325565222740174; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 36))) { + result[0] += 0.09547398872773903; + } else { + result[0] += -0.046573119312707335; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 4))) { + if (LIKELY(false || (data[2].qvalue <= 28))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += 0.08210078603934584; + } else { + result[0] += -0.11656505933452627; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 8))) { + result[0] += -0.20987820191852383; + } else { + result[0] += -0.5384319635554019; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 44))) { + result[0] += -1.9617384305207626; + } else { + result[0] += -1.2668292170304518; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 178))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += 0.003420268764872335; + } else { + result[0] += 2.213071489783301; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + result[0] += -0.7776470923789272; + } else { + result[0] += -0.04327273641201862; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (LIKELY(false || (data[1].qvalue <= 40))) { + result[0] += 0.00861831306648558; + } else { + result[0] += 1.848056525397011; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 196))) { + result[0] += -0.04828241624484346; + } else { + result[0] += 0.05563476812699414; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 64))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 26))) { + result[0] += -0.05078687692889694; + } else { + result[0] += -0.014356568909767928; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 24))) { + result[0] += -0.17534628583710493; + } else { + result[0] += -0.5621430105402849; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -1.7740939281297767; + } else { + result[0] += -1.145018720260033; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + if (LIKELY(false || (data[3].qvalue <= 130))) { + result[0] += 0.018993981486318878; + } else { + result[0] += 0.5365927638406978; + } + } else { + result[0] += 15.05643708864848; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 152))) { + if (LIKELY(false || (data[0].qvalue <= 56))) { + result[0] += -0.4728617369458554; + } else { + result[0] += 0.20752761050703802; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 38))) { + result[0] += 0.09305968936956194; + } else { + result[0] += -0.009524064343686378; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 52))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += 0.18727818329014223; + } else { + result[0] += -0.025890677188174106; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 24))) { + result[0] += -0.15753058727404748; + } else { + result[0] += -0.5067434101778527; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -1.604397966136103; + } else { + result[0] += -1.0349207672706018; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 234))) { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += 0.020542942769893005; + } else { + result[0] += 13.61352872212728; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 152))) { + result[0] += -0.20312529336650453; + } else { + result[0] += 0.019356250676449485; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.1529675914478733; + } else { + result[0] += -3.046054223456034; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 78))) { + result[0] += 0.4578596813081651; + } else { + result[0] += -0.21317670897106453; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 100))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (LIKELY(false || (data[0].qvalue <= 12))) { + if (LIKELY(false || (data[3].qvalue <= 88))) { + result[0] += -0.009515774573242132; + } else { + result[0] += 0.10722986880689861; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 24))) { + result[0] += -0.08947096205225108; + } else { + result[0] += -0.4843778472947222; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -1.9798399291992188; + } else { + result[0] += -1.155246785481771; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 12))) { + if (LIKELY(false || (data[3].qvalue <= 132))) { + if (LIKELY(false || (data[3].qvalue <= 126))) { + result[0] += 0.058783065329708964; + } else { + result[0] += 0.20552873062508797; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 48))) { + result[0] += 0.5012681102076195; + } else { + result[0] += 1.1655076967659646; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 152))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += -0.4052823818071666; + } else { + result[0] += 0.2663182250508323; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 0.09131312159878124; + } else { + result[0] += -0.023826012683033533; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 14))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (LIKELY(false || (data[0].qvalue <= 22))) { + if (LIKELY(false || (data[1].qvalue <= 16))) { + result[0] += -0.05587497206644211; + } else { + result[0] += -0.18871587509128254; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -0.471164876208749; + } else { + result[0] += -0.2899516754150391; + } + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 64))) { + result[0] += -1.2615578046052351; + } else { + result[0] += -0.824327716093797; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 230))) { + if (LIKELY(false || (data[3].qvalue <= 218))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + result[0] += -0.0018346942873173101; + } else { + result[0] += 0.16034261671985583; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + result[0] += 0.3582006235217019; + } else { + result[0] += -0.15805343752114553; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.09120323986998596; + } else { + result[0] += -2.746553548719825; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 240))) { + result[0] += 0.020237036833509074; + } else { + result[0] += 0.5211988226836327; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 178))) { + if (LIKELY(false || (data[2].qvalue <= 12))) { + if (LIKELY(false || (data[3].qvalue <= 130))) { + if (LIKELY(false || (data[0].qvalue <= 48))) { + result[0] += -0.005991282302809805; + } else { + result[0] += 0.21808256969336925; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 48))) { + result[0] += 0.3648159058387132; + } else { + result[0] += 1.0501265993764848; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 14))) { + if (LIKELY(false || (data[3].qvalue <= 140))) { + result[0] += -0.7560950076917835; + } else { + result[0] += -0.011609365583746693; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 114))) { + result[0] += -0.9929890502662195; + } else { + result[0] += -0.018430796040420066; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (UNLIKELY(false || (data[0].qvalue <= 38))) { + if (LIKELY(false || (data[3].qvalue <= 182))) { + result[0] += 1.4941535253935205; + } else { + result[0] += 5.01069572504829; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 40))) { + result[0] += -1.6721194921221052; + } else { + result[0] += 0.06600910509196743; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + if (LIKELY(false || (data[2].qvalue <= 50))) { + result[0] += 0.06599082621417188; + } else { + result[0] += -0.5629778590534413; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 50))) { + result[0] += -0.031091763815894752; + } else { + result[0] += 0.12480377527902276; + } + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 38))) { + if (LIKELY(false || (data[2].qvalue <= 36))) { + if (LIKELY(false || (data[0].qvalue <= 14))) { + if (UNLIKELY(false || (data[3].qvalue <= 2))) { + result[0] += 0.1757544212263416; + } else { + result[0] += -0.03321362620945462; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 24))) { + result[0] += -0.1209793900016442; + } else { + result[0] += -0.3390527655087508; + } + } + } else { + result[0] += -0.9544804904460907; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 228))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + if (LIKELY(false || (data[3].qvalue <= 188))) { + result[0] += 0.00850193251229063; + } else { + result[0] += 1.253315189034531; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 192))) { + result[0] += -0.3207565361013706; + } else { + result[0] += 0.01040093706091699; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 78))) { + if (LIKELY(false || (data[0].qvalue <= 76))) { + result[0] += 0.07622775564010142; + } else { + result[0] += 0.6434668296100172; + } + } else { + result[0] += -0.25382570830590884; + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 66))) { + if (LIKELY(false || (data[2].qvalue <= 36))) { + if (UNLIKELY(false || (data[3].qvalue <= 6))) { + if (UNLIKELY(false || (data[3].qvalue <= 0))) { + result[0] += -0.15891414485012728; + } else { + result[0] += -0.06083332526536694; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 30))) { + result[0] += -0.025560713454775653; + } else { + result[0] += -0.009423279209306063; + } + } + } else { + result[0] += -0.8614186177253723; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[2].qvalue <= 12))) { + if (LIKELY(false || (data[3].qvalue <= 130))) { + result[0] += 0.014946627847899083; + } else { + result[0] += 0.3928370868714558; + } + } else { + result[0] += 12.309830587704978; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 146))) { + if (UNLIKELY(false || (data[2].qvalue <= 40))) { + result[0] += -0.7278951537054682; + } else { + result[0] += 0.10042924111104164; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 0.048389262179927645; + } else { + result[0] += -0.024802333325858735; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 228))) { + if (LIKELY(false || (data[3].qvalue <= 218))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 0.0026677754941615505; + } else { + result[0] += -0.11795346042813584; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 214))) { + result[0] += 0.25660651477274304; + } else { + result[0] += 0.12738714081176464; + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 80))) { + if (UNLIKELY(false || (data[3].qvalue <= 222))) { + result[0] += -0.36182422209697296; + } else { + result[0] += -0.09068035614479159; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 226))) { + result[0] += 0.29626333166012725; + } else { + result[0] += 0.501580802754658; + } + } + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + if (LIKELY(false || (data[1].qvalue <= 74))) { + result[0] += 0.04606551220989991; + } else { + result[0] += 1.6768793160307642; + } + } else { + result[0] += -2.4957903161863; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 56))) { + if (LIKELY(false || (data[3].qvalue <= 240))) { + result[0] += -0.001995007368938002; + } else { + result[0] += 0.8600447828586285; + } + } else { + result[0] += -0.22613400126344585; + } + } + } + if (LIKELY(false || (data[3].qvalue <= 100))) { + if (LIKELY(false || (data[2].qvalue <= 28))) { + if (LIKELY(false || (data[1].qvalue <= 10))) { + if (LIKELY(false || (data[3].qvalue <= 88))) { + result[0] += -0.007032593132406662; + } else { + result[0] += 0.10789191115072033; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 94))) { + result[0] += -0.14776386905895944; + } else { + result[0] += -0.04740764971575276; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 44))) { + result[0] += -1.5670684486389161; + } else { + result[0] += -0.7503883746818261; + } + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 10))) { + result[0] += 2.769067636305286; + } else { + if (UNLIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[3].qvalue <= 134))) { + result[0] += 0.07602934068056531; + } else { + result[0] += 2.8183202544858474; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 146))) { + result[0] += -0.16233532230561315; + } else { + result[0] += 0.01202817781399166; + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 54))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + if (LIKELY(false || (data[0].qvalue <= 48))) { + if (LIKELY(false || (data[0].qvalue <= 36))) { + result[0] += 0.0029886117550836865; + } else { + result[0] += -0.06408425115195845; + } + } else { + result[0] += 0.26602660123013633; + } + } else { + result[0] += -0.07929562686095666; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + if (LIKELY(false || (data[2].qvalue <= 46))) { + if (UNLIKELY(false || (data[2].qvalue <= 36))) { + result[0] += 3.7119815171616426; + } else { + result[0] += 0.547443312065942; + } + } else { + result[0] += 4.166473594393049; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 48))) { + if (UNLIKELY(false || (data[0].qvalue <= 60))) { + result[0] += 0.0017977573714891022; + } else { + result[0] += 0.23845299048938143; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 76))) { + result[0] += -0.037150860814943704; + } else { + result[0] += 0.14564257745376; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 178))) { + if (LIKELY(false || (data[0].qvalue <= 60))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + if (LIKELY(false || (data[0].qvalue <= 48))) { + result[0] += -0.007183108829239199; + } else { + result[0] += 0.2394491795303417; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 140))) { + result[0] += -0.6344022675495424; + } else { + result[0] += 0.007260844712462359; + } + } + } else { + if (LIKELY(false || (data[3].qvalue <= 142))) { + if (UNLIKELY(false || (data[3].qvalue <= 138))) { + result[0] += -0.08417562885148172; + } else { + result[0] += 0.5906773558105556; + } + } else { + if (LIKELY(false || (data[0].qvalue <= 68))) { + result[0] += 0.18764238988337378; + } else { + result[0] += -2.326712134777498; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (UNLIKELY(false || (data[0].qvalue <= 38))) { + if (LIKELY(false || (data[3].qvalue <= 182))) { + result[0] += 1.339431215953403; + } else { + result[0] += 4.521662556143368; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 52))) { + result[0] += 0.7292394052527168; + } else { + result[0] += -0.012391112078889212; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 194))) { + if (LIKELY(false || (data[2].qvalue <= 50))) { + result[0] += 0.05246317581619821; + } else { + result[0] += -0.4745475472223324; + } + } else { + if (LIKELY(false || (data[2].qvalue <= 50))) { + result[0] += -0.024886056556697605; + } else { + result[0] += 0.10675635333641575; + } + } + } + } + if (LIKELY(false || (data[3].qvalue <= 178))) { + if (LIKELY(false || (data[1].qvalue <= 46))) { + if (LIKELY(false || (data[1].qvalue <= 44))) { + if (LIKELY(false || (data[3].qvalue <= 170))) { + result[0] += -0.0035541639556744805; + } else { + result[0] += 0.23776616044647245; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 22))) { + result[0] += 1.6540061310768128; + } else { + result[0] += -0.007316129265448475; + } + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 24))) { + if (UNLIKELY(false || (data[3].qvalue <= 156))) { + result[0] += -0.9207533250588412; + } else { + result[0] += -0.19604021761441262; + } + } else { + if (UNLIKELY(false || (data[2].qvalue <= 26))) { + result[0] += 0.39069615612658914; + } else { + result[0] += -0.07078300532699774; + } + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 186))) { + if (LIKELY(false || (data[1].qvalue <= 42))) { + if (UNLIKELY(false || (data[3].qvalue <= 182))) { + result[0] += -0.0537103470292483; + } else { + result[0] += 0.01762700340556235; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 180))) { + result[0] += 0.6253090474974919; + } else { + result[0] += 2.807307553162299; + } + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 202))) { + if (LIKELY(false || (data[2].qvalue <= 48))) { + result[0] += 0.04721793457525833; + } else { + result[0] += -0.17271753814495214; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 76))) { + result[0] += -0.01901583571747938; + } else { + result[0] += 0.1447237594141037; + } + } + } + } + if (LIKELY(false || (data[0].qvalue <= 78))) { + if (LIKELY(false || (data[0].qvalue <= 76))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + if (LIKELY(false || (data[0].qvalue <= 50))) { + result[0] += 0.0021335393865604085; + } else { + result[0] += -0.07177648191161112; + } + } else { + if (UNLIKELY(false || (data[1].qvalue <= 30))) { + result[0] += 2.634354959784485; + } else { + result[0] += 0.004247555141275988; + } + } + } else { + result[0] += 0.45583923039077984; + } + } else { + result[0] += -0.24432320779772257; + } + if (UNLIKELY(false || (data[3].qvalue <= 6))) { + if (LIKELY(false || (data[0].qvalue <= 58))) { + if (LIKELY(false || (data[0].qvalue <= 46))) { + if (LIKELY(false || (data[1].qvalue <= 18))) { + result[0] += -0.053137056565348964; + } else { + result[0] += -0.1615091542830301; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 50))) { + result[0] += -0.48585747241973887; + } else { + result[0] += -0.2905982590516409; + } + } + } else { + result[0] += -1.0460150627295177; + } + } else { + if (LIKELY(false || (data[3].qvalue <= 228))) { + if (LIKELY(false || (data[3].qvalue <= 218))) { + if (LIKELY(false || (data[3].qvalue <= 208))) { + result[0] += -0.0007595050498410175; + } else { + result[0] += 0.17149519474492791; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 68))) { + result[0] += 0.2696969214216211; + } else { + result[0] += -0.148602349719807; + } + } + } else { + if (LIKELY(false || (data[0].qvalue <= 78))) { + if (LIKELY(false || (data[1].qvalue <= 76))) { + result[0] += 0.041070141212163; + } else { + result[0] += 0.27300094236384387; + } + } else { + result[0] += -0.220040733112148; + } + } + } + if (UNLIKELY(false || (data[3].qvalue <= 66))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + if (UNLIKELY(false || (data[3].qvalue <= 8))) { + if (LIKELY(false || (data[0].qvalue <= 24))) { + result[0] += -0.04786216375242139; + } else { + result[0] += -0.2854615034882365; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 44))) { + result[0] += -0.0157676019115889; + } else { + result[0] += -0.00516823734649734; + } + } + } else { + result[0] += -0.8536656070967852; + } + } else { + if (LIKELY(false || (data[1].qvalue <= 28))) { + if (LIKELY(false || (data[0].qvalue <= 54))) { + if (LIKELY(false || (data[3].qvalue <= 130))) { + result[0] += 0.01178608871677298; + } else { + result[0] += 0.31573188820888726; + } + } else { + result[0] += 10.428866252899171; + } + } else { + if (UNLIKELY(false || (data[3].qvalue <= 146))) { + if (LIKELY(false || (data[0].qvalue <= 60))) { + result[0] += -0.39214777400557815; + } else { + result[0] += 0.15464362462318024; + } + } else { + if (UNLIKELY(false || (data[0].qvalue <= 36))) { + result[0] += 0.05795968864116782; + } else { + result[0] += -0.018277034463468993; + } + } + } + } + + // Apply base_scores + result[0] += 0; + + // Apply postprocessor + if (!pred_margin) { postprocess(result); } +} + +void cpufj_predictor::postprocess(double* result) +{ + // Do nothing +} + +// Feature names array +const char* cpufj_predictor::feature_names[cpufj_predictor::NUM_FEATURES] = { + "n_vars", "n_cstrs", "total_nnz", "mem_total_mb"}; diff --git a/cpp/src/utilities/models/cpufj_predictor/quantize.cpp b/cpp/src/utilities/models/cpufj_predictor/quantize.cpp new file mode 100644 index 0000000000..0b292d03fb --- /dev/null +++ b/cpp/src/utilities/models/cpufj_predictor/quantize.cpp @@ -0,0 +1,293 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "header.h" + +static const double threshold[] = { + 940.50000000000011, + 962.50000000000011, + 964.50000000000011, + 1001.5000000000001, + 1010.5000000000001, + 1088.5000000000002, + 1250.5000000000002, + 1435.5000000000002, + 3395.5000000000005, + 3603.0000000000005, + 4535.5000000000009, + 5197.5000000000009, + 5419.0000000000009, + 5949.5000000000009, + 5959.5000000000009, + 5962.5000000000009, + 5963.5000000000009, + 5964.5000000000009, + 5966.5000000000009, + 5969.5000000000009, + 5972.5000000000009, + 5984.0000000000009, + 6803.5000000000009, + 7344.0000000000009, + 8291.0000000000018, + 9184.0000000000018, + 9992.0000000000018, + 10730.500000000002, + 12831.000000000002, + 14626.500000000002, + 15810.500000000002, + 16371.000000000002, + 17411.000000000004, + 18723.500000000004, + 19548.000000000004, + 20752.500000000004, + 23386.000000000004, + 35418.500000000007, + 49402.000000000007, + 53915.500000000007, + 598.50000000000011, + 1717.5000000000002, + 1767.5000000000002, + 1847.5000000000002, + 2005.5000000000002, + 2290.0000000000005, + 2305.5000000000005, + 2649.0000000000005, + 5179.5000000000009, + 5250.5000000000009, + 5362.5000000000009, + 6412.5000000000009, + 7682.0000000000009, + 9243.0000000000018, + 10037.000000000002, + 12675.000000000002, + 18404.000000000004, + 19600.500000000004, + 21814.500000000004, + 23409.000000000004, + 23497.000000000004, + 23508.000000000004, + 23558.000000000004, + 23603.500000000004, + 23652.500000000004, + 23744.500000000004, + 23818.500000000004, + 23827.000000000004, + 23874.000000000004, + 23897.500000000004, + 23920.500000000004, + 23949.500000000004, + 23978.000000000004, + 24015.000000000004, + 24102.000000000004, + 24164.500000000004, + 26354.000000000004, + 40391.500000000007, + 58010.500000000007, + 64224.000000000007, + 64894.000000000007, + 5656.5000000000009, + 7438.5000000000009, + 7622.5000000000009, + 18609.000000000004, + 23424.500000000004, + 24989.500000000004, + 29342.000000000004, + 43154.000000000007, + 46402.000000000007, + 46815.000000000007, + 47052.000000000007, + 47178.000000000007, + 47358.000000000007, + 47388.000000000007, + 47485.000000000007, + 47659.000000000007, + 47752.500000000007, + 47839.500000000007, + 48007.000000000007, + 48085.500000000007, + 48300.500000000007, + 48410.000000000007, + 54272.500000000007, + 57404.000000000007, + 76382.000000000015, + 83368.000000000015, + 108268.00000000001, + 170279.00000000003, + 186892.00000000003, + 3.8570000000000007, + 9.8880000000000017, + 11.190500000000002, + 11.561500000000001, + 11.861500000000001, + 12.013500000000002, + 12.3085, + 12.4625, + 12.647500000000003, + 13.102500000000001, + 13.439500000000001, + 14.092500000000003, + 15.505500000000003, + 16.265500000000003, + 16.586500000000004, + 16.893500000000003, + 17.051500000000001, + 17.255500000000001, + 17.432500000000001, + 17.537500000000005, + 17.814500000000006, + 18.095500000000005, + 18.167500000000004, + 18.334500000000002, + 18.546500000000005, + 18.743500000000001, + 18.851500000000005, + 18.949500000000004, + 19.151500000000002, + 19.201500000000006, + 19.275500000000005, + 19.453500000000002, + 19.604500000000005, + 19.704500000000003, + 20.009500000000006, + 20.944500000000001, + 21.655500000000004, + 24.058500000000006, + 56.284500000000001, + 58.642500000000005, + 63.514000000000003, + 66.785000000000011, + 67.949500000000015, + 70.709000000000017, + 72.252000000000024, + 74.410500000000013, + 76.675500000000014, + 78.813500000000019, + 81.035000000000011, + 86.201000000000008, + 87.71850000000002, + 90.722000000000023, + 93.919500000000014, + 95.885000000000005, + 98.094500000000025, + 101.17400000000002, + 105.48300000000002, + 119.34800000000001, + 135.33800000000005, + 148.20100000000002, + 155.32500000000002, + 202.12250000000003, + 215.85450000000003, + 222.94300000000001, + 227.80600000000001, + 231.09150000000002, + 235.98200000000006, + 238.51450000000003, + 289.45400000000001, + 328.42950000000002, + 358.75900000000007, + 402.21050000000008, + 420.89000000000004, + 436.57650000000007, + 443.69200000000006, + 453.70450000000005, + 462.34700000000004, + 471.61900000000009, + 478.55100000000004, + 486.96150000000006, + 495.03150000000005, + 501.28050000000007, + 505.49750000000006, + 510.61650000000003, + 518.64250000000004, + 524.98750000000007, + 530.02200000000005, + 538.52950000000021, + 548.17750000000012, + 563.13350000000003, + 585.95900000000017, + 606.86650000000009, + 622.19850000000008, + 632.28650000000016, + 662.49250000000006, + 870.10250000000008, + 885.30550000000005, + 899.1785000000001, + 914.79400000000021, + 932.5870000000001, + 945.74600000000009, + 1009.9455000000002, + 1021.0850000000002, + 1048.7770000000003, + 1084.9000000000003, + 1115.9705000000001, + 1145.3940000000002, + 1175.9330000000002, + 1208.2720000000002, + 1235.9550000000002, + 1253.6805000000002, + 1266.5855000000004, + 1276.1255000000003, + 1282.5315000000003, + 1291.7940000000001, + 1295.7590000000002, + 1299.4545000000001, + 1307.3780000000004, + 1312.9645000000003, + 1322.5440000000001, + 1465.1910000000003, +}; + +static const int th_begin[] = { + 0, + 40, + 81, + 110, +}; + +static const int th_len[] = { + 40, + 41, + 29, + 121, +}; + +/* + * \brief Function to convert a feature value into bin index. + * \param val Feature value, in floating-point + * \param fid Feature identifier + * \return bin Index corresponding to given feature value + */ +int cpufj_predictor::quantize(double val, unsigned fid) +{ + const size_t offset = th_begin[fid]; + const double* array = &threshold[offset]; + int len = th_len[fid]; + int low = 0; + int high = len; + int mid; + double mval; + // It is possible th_begin[i] == [total_num_threshold]. This means that + // all features i, (i+1), ... are not used for any of the splits in the model. + // So in this case, just return something + if (offset == 231 || val < array[0]) { return -10; } + while (low + 1 < high) { + mid = (low + high) / 2; + mval = array[mid]; + if (val == mval) { + return mid * 2; + } else if (val < mval) { + high = mid; + } else { + low = mid; + } + } + if (array[low] == val) { + return low * 2; + } else if (high == len) { + return len * 2; + } else { + return low * 2 + 1; + } +} diff --git a/cpp/src/utilities/work_unit_predictor.cpp b/cpp/src/utilities/work_unit_predictor.cpp index 4d273d3b60..68d2a8f4d7 100644 --- a/cpp/src/utilities/work_unit_predictor.cpp +++ b/cpp/src/utilities/work_unit_predictor.cpp @@ -24,6 +24,7 @@ #include #include +#include "models/cpufj_predictor/header.h" #include "models/fj_predictor/header.h" namespace cuopt { @@ -81,5 +82,6 @@ float work_unit_predictor_t::predict_scalar( } template class work_unit_predictor_t; +template class work_unit_predictor_t; } // namespace cuopt diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index 0f251c3a29..c729a82925 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -133,13 +133,13 @@ # Example usage (uncomment to use only specific features): # 'n_variables', # 'n_constraints', - # 'sparsity', - # "n_vars", - # "n_cstrs", - # "total_nnz", - # "mem_total_mb", - # "mem_store_mb", - # "mem_load_mb", + #'sparsity', + "n_vars", + "n_cstrs", + "total_nnz", + "mem_total_mb", + # "mem_stores_mb", + # "mem_loads_mb", ] # ============================================================================ From f1afa59cff60ff30fd2808f07ab6aeb4070c7c3e Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 14 Nov 2025 09:17:04 +0000 Subject: [PATCH 078/225] fewer --- cpp/src/mip/local_search/local_search.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 30bf5ac06f..28be6dd4f4 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -65,7 +65,7 @@ void local_search_t::start_cpufj_scratch_threads(population_t::infinity(); - cpu_fj_settings.iteration_limit = 30000; // would like 10 samples per instance + cpu_fj_settings.iteration_limit = 10000; // would like 10 samples per instance solution_t solution(*context.problem_ptr); thrust::fill(solution.handle_ptr->get_thrust_policy(), From cd2595ece81ce958d46d3a30df9203f7faf19e67 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 14 Nov 2025 12:57:47 +0000 Subject: [PATCH 079/225] log-transform for regressor --- scripts/train_regressor.py | 540 ++++++++++++++++++++++++++++++++++--- 1 file changed, 497 insertions(+), 43 deletions(-) diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index c729a82925..f26874a21e 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -27,7 +27,7 @@ import pickle import numpy as np import pandas as pd -from sklearn.model_selection import train_test_split, cross_val_score +from sklearn.model_selection import train_test_split, cross_val_score, KFold from sklearn.preprocessing import StandardScaler, PolynomialFeatures from sklearn.linear_model import LinearRegression, Ridge from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor @@ -125,6 +125,22 @@ "max_cstr_deg", "viol_ratio", "nnz_per_move", + "h_cstr_right_weights_loads", + "h_cstr_left_weights_loads", + "h_cstr_right_weights_stores", + "h_cstr_left_weights_stores", + "total_viol", + "feas_found", + "obj_weight", + "h_tabu_nodec_until_stores", + "h_tabu_nodec_until_loads", + "h_tabu_noinc_until_stores", + "h_tabu_noinc_until_loads", + "h_tabu_lastdec_stores", + "h_tabu_lastdec_loads", + "h_tabu_lastinc_stores", + "h_tabu_lastinc_loads", + "max_weight", ] # Alternatively, specify ONLY the features you want to use @@ -134,12 +150,14 @@ # 'n_variables', # 'n_constraints', #'sparsity', - "n_vars", - "n_cstrs", - "total_nnz", - "mem_total_mb", - # "mem_stores_mb", - # "mem_loads_mb", + # "n_vars", + # "n_cstrs", + # #"total_nnz", + # "mem_total_mb", + # #"cache_hit_rate", + # #"cstr_deg_cv" + # "mem_stores_mb", + # "mem_loads_mb", ] # ============================================================================ @@ -221,10 +239,33 @@ def split_by_files( train_df = df[df["file"].isin(train_files)].copy() test_df = df[df["file"].isin(test_files)].copy() + # Validate no data leakage: ensure no overlap between train and test files + train_files_set = set(train_files) + test_files_set = set(test_files) + overlap = train_files_set.intersection(test_files_set) + + if overlap: + raise ValueError( + f"Data leakage detected! {len(overlap)} file(s) appear in both train and test sets:\n" + f" {list(overlap)[:10]}{'...' if len(overlap) > 10 else ''}" + ) + + # Verify the actual dataframes have no file overlap + train_files_in_df = set(train_df["file"].unique()) + test_files_in_df = set(test_df["file"].unique()) + actual_overlap = train_files_in_df.intersection(test_files_in_df) + + if actual_overlap: + raise ValueError( + f"Data leakage detected in dataframes! {len(actual_overlap)} file(s) appear in both:\n" + f" {list(actual_overlap)[:10]}{'...' if len(actual_overlap) > 10 else ''}" + ) + print("\nData Split:") print(f" Total entries: {len(df)}") print(f" Train entries: {len(train_df)} ({len(train_files)} files)") print(f" Test entries: {len(test_df)} ({len(test_files)} files)") + print(" ✓ Verified: Zero file overlap between train and test sets") # Check distribution similarity (use stratify_by column if provided, otherwise first numeric column) target_col = ( @@ -568,7 +609,7 @@ def create_regressor( ) model = xgb.XGBRegressor(**params) - needs_scaling = False + needs_scaling = True elif regressor_type == "lightgbm": try: @@ -581,8 +622,8 @@ def create_regressor( params = { "objective": "regression", "random_state": random_state, - "n_estimators": 100, - "max_depth": 4, + "n_estimators": 150, + "max_depth": 6, "learning_rate": 0.1, "verbosity": 1 if verbose else -1, # Regularization to prevent overfitting @@ -611,7 +652,7 @@ def create_regressor( ) model = lgb.LGBMRegressor(**params) - needs_scaling = False + needs_scaling = True elif regressor_type == "random_forest": params = { @@ -730,6 +771,9 @@ def evaluate_model( skip_cv: bool = False, X_test_original: pd.DataFrame = None, test_df: pd.DataFrame = None, + log_transform: bool = False, + y_train_original: pd.Series = None, + y_test_original: pd.Series = None, ) -> Tuple[float, float]: """Evaluate model and print metrics. Returns (train_r2, test_r2). @@ -737,12 +781,16 @@ def evaluate_model( ---- X_test_original: Unscaled X_test for displaying feature values test_df: Original test dataframe with 'file' column + log_transform: If True, predictions are in log-space and need inverse transform + y_train_original: Original (non-log) training targets (if log_transform=True) + y_test_original: Original (non-log) test targets (if log_transform=True) """ # Cross-validation on training set (skip if using early stopping) if not skip_cv: print(f"\nCross-Validation on Training Set ({cv_folds}-fold):") try: - cv_scores = cross_val_score( + # Compute RMSE and R² in log-space + cv_scores_mse = cross_val_score( model, X_train, y_train, @@ -751,8 +799,97 @@ def evaluate_model( n_jobs=-1, verbose=verbose, ) - cv_rmse = np.sqrt(-cv_scores) - print(f" CV RMSE: {cv_rmse.mean():.4f} (+/- {cv_rmse.std():.4f})") + cv_scores_r2 = cross_val_score( + model, + X_train, + y_train, + cv=cv_folds, + scoring="r2", + n_jobs=-1, + verbose=verbose, + ) + cv_rmse = np.sqrt(-cv_scores_mse) + + if log_transform: + print(" Log-space metrics:") + print( + f" CV RMSE: {cv_rmse.mean():.4f} (+/- {cv_rmse.std():.4f})" + ) + print( + f" CV R²: {cv_scores_r2.mean():.4f} (+/- {cv_scores_r2.std():.4f})" + ) + + # Also compute metrics in original space + kfold = KFold(n_splits=cv_folds, shuffle=True, random_state=42) + + cv_mape_scores = [] + cv_r2_original_scores = [] + + for train_idx, val_idx in kfold.split(X_train): + X_train_fold = ( + X_train.iloc[train_idx] + if hasattr(X_train, "iloc") + else X_train[train_idx] + ) + y_train_fold = ( + y_train.iloc[train_idx] + if hasattr(y_train, "iloc") + else y_train[train_idx] + ) + X_val_fold = ( + X_train.iloc[val_idx] + if hasattr(X_train, "iloc") + else X_train[val_idx] + ) + y_val_original_fold = ( + y_train_original.iloc[val_idx] + if hasattr(y_train_original, "iloc") + else y_train_original[val_idx] + ) + + # Train on fold + model_fold = type(model)(**model.get_params()) + model_fold.fit(X_train_fold, y_train_fold) + + # Predict and transform back + y_pred_log = model_fold.predict(X_val_fold) + y_pred_original = np.exp(y_pred_log) + + # Compute metrics in original space + mape = ( + np.mean( + np.abs( + (y_val_original_fold - y_pred_original) + / y_val_original_fold + ) + ) + * 100 + ) + r2_original = r2_score( + y_val_original_fold, y_pred_original + ) + + cv_mape_scores.append(mape) + cv_r2_original_scores.append(r2_original) + + cv_mape = np.array(cv_mape_scores) + cv_r2_original = np.array(cv_r2_original_scores) + + print(" Original-space metrics:") + print( + f" CV MAPE: {cv_mape.mean():.2f}% (+/- {cv_mape.std():.2f}%)" + ) + print( + f" CV R²: {cv_r2_original.mean():.4f} (+/- {cv_r2_original.std():.4f})" + ) + else: + print( + f" CV RMSE: {cv_rmse.mean():.4f} (+/- {cv_rmse.std():.4f})" + ) + print( + f" CV R²: {cv_scores_r2.mean():.4f} (+/- {cv_scores_r2.std():.4f})" + ) + except Exception as e: print( f" CV failed (likely due to early stopping): {str(e)[:100]}" @@ -763,29 +900,119 @@ def evaluate_model( # Training set metrics y_train_pred = model.predict(X_train) - train_mse = mean_squared_error(y_train, y_train_pred) - train_rmse = np.sqrt(train_mse) - train_mae = mean_absolute_error(y_train, y_train_pred) - train_r2 = r2_score(y_train, y_train_pred) - print("\nTraining Set Metrics:") - print(f" MSE: {train_mse:.4f}") - print(f" RMSE: {train_rmse:.4f}") - print(f" MAE: {train_mae:.4f}") - print(f" R²: {train_r2:.4f}") + # If log-transformed, also compute metrics in original space + if log_transform: + # Inverse transform predictions + y_train_pred_original = np.exp(y_train_pred) + y_test_pred_log = model.predict(X_test) + y_test_pred_original = np.exp(y_test_pred_log) + + # Metrics in log-space + train_mse_log = mean_squared_error(y_train, y_train_pred) + train_rmse_log = np.sqrt(train_mse_log) + train_r2_log = r2_score(y_train, y_train_pred) + + # Metrics in original space + train_mse = mean_squared_error(y_train_original, y_train_pred_original) + train_rmse = np.sqrt(train_mse) + train_mae = mean_absolute_error( + y_train_original, y_train_pred_original + ) + train_r2 = r2_score(y_train_original, y_train_pred_original) + train_mape = ( + np.mean( + np.abs( + (y_train_original - y_train_pred_original) + / y_train_original + ) + ) + * 100 + ) + + print("\nTraining Set Metrics (Original Space):") + print(f" MSE: {train_mse:.4f}") + print(f" RMSE: {train_rmse:.4f}") + print(f" MAE: {train_mae:.4f}") + print(f" MAPE: {train_mape:.2f}% (optimized metric)") + print(f" R²: {train_r2:.4f}") + print("\nTraining Set Metrics (Log Space):") + print(f" RMSE: {train_rmse_log:.4f}") + print(f" R²: {train_r2_log:.4f}") + else: + train_mse = mean_squared_error(y_train, y_train_pred) + train_rmse = np.sqrt(train_mse) + train_mae = mean_absolute_error(y_train, y_train_pred) + train_r2 = r2_score(y_train, y_train_pred) + + print("\nTraining Set Metrics:") + print(f" MSE: {train_mse:.4f}") + print(f" RMSE: {train_rmse:.4f}") + print(f" MAE: {train_mae:.4f}") + print(f" R²: {train_r2:.4f}") # Test set metrics - y_test_pred = model.predict(X_test) - test_mse = mean_squared_error(y_test, y_test_pred) - test_rmse = np.sqrt(test_mse) - test_mae = mean_absolute_error(y_test, y_test_pred) - test_r2 = r2_score(y_test, y_test_pred) - - print("\nTest Set Metrics:") - print(f" MSE: {test_mse:.4f}") - print(f" RMSE: {test_rmse:.4f}") - print(f" MAE: {test_mae:.4f}") - print(f" R²: {test_r2:.4f}") + if log_transform: + # Already computed above + test_mse_log = mean_squared_error(y_test, y_test_pred_log) + test_rmse_log = np.sqrt(test_mse_log) + test_r2_log = r2_score(y_test, y_test_pred_log) + + # Metrics in original space + test_mse = mean_squared_error(y_test_original, y_test_pred_original) + test_rmse = np.sqrt(test_mse) + test_mae = mean_absolute_error(y_test_original, y_test_pred_original) + test_r2 = r2_score(y_test_original, y_test_pred_original) + test_mape = ( + np.mean( + np.abs( + (y_test_original - y_test_pred_original) / y_test_original + ) + ) + * 100 + ) + + print("\nTest Set Metrics (Original Space):") + print(f" MSE: {test_mse:.4f}") + print(f" RMSE: {test_rmse:.4f}") + print(f" MAE: {test_mae:.4f}") + print(f" MAPE: {test_mape:.2f}% (optimized metric)") + print(f" R²: {test_r2:.4f}") + print("\nTest Set Metrics (Log Space):") + print(f" RMSE: {test_rmse_log:.4f}") + print(f" R²: {test_r2_log:.4f}") + + # Use original space predictions for sample display + y_test_pred = y_test_pred_original + y_test = y_test_original + else: + y_test_pred = model.predict(X_test) + test_mse = mean_squared_error(y_test, y_test_pred) + test_rmse = np.sqrt(test_mse) + test_mae = mean_absolute_error(y_test, y_test_pred) + test_r2 = r2_score(y_test, y_test_pred) + + print("\nTest Set Metrics:") + print(f" MSE: {test_mse:.4f}") + print(f" RMSE: {test_rmse:.4f}") + print(f" MAE: {test_mae:.4f}") + print(f" R²: {test_r2:.4f}") + + # If R² is negative, show baseline comparison for debugging + if test_r2 < 0: + y_test_mean = np.mean(y_test) + baseline_pred = np.full_like(y_test_pred, y_test_mean) + baseline_mse = mean_squared_error(y_test, baseline_pred) + baseline_rmse = np.sqrt(baseline_mse) + print("\n WARNING: Negative R² detected!") + print( + f" This means the model is worse than predicting the mean: {y_test_mean:.4f}" + ) + print(f" Baseline (mean) RMSE: {baseline_rmse:.4f}") + print(f" Model RMSE: {test_rmse:.4f}") + print( + f" Model is {test_rmse / baseline_rmse:.2f}x worse than baseline" + ) # Feature importance get_feature_importance(model, feature_names, regressor_type) @@ -868,6 +1095,7 @@ def compile_model_treelite( quantize: bool = False, feature_names: List[str] = None, model_name: str = None, + log_transform: bool = False, ) -> None: """Compile XGBoost/LightGBM model to C source files using TL2cgen. @@ -882,6 +1110,7 @@ def compile_model_treelite( quantize: Whether to use quantization in code generation feature_names: List of feature names in expected order (optional) model_name: Name prefix for functions (optional, derived from training file) + log_transform: Whether model predicts in log-space (will add exp() wrapper) """ if regressor_type not in ["xgboost", "lightgbm"]: print( @@ -1083,12 +1312,55 @@ def compile_model_treelite( r"!\(data\[\d+\]\.missing != -1\)", "false", content ) + # If log_transform is used, wrap return values with exp() + if log_transform: + # Add include if not present + if ( + "#include " not in content + and "#include" not in content + ): + # Find the last #include and add after it + include_match = None + for match in re.finditer( + r'#include\s*[<"].*?[>"]', content + ): + include_match = match + if include_match: + insert_pos = include_match.end() + content = ( + content[:insert_pos] + + "\n#include " + + content[insert_pos:] + ) + + # Replace "return sum;" with "return std::exp(sum);" + # Match various return patterns in the predict function + content = re.sub( + r"(\s+)return\s+(sum|result|pred)\s*;", + r"\1return std::exp(\2);", + content, + ) + + # Also handle single-line returns like "return value;" + content = re.sub( + r"(\s+)return\s+([\w\.]+)\s*;", + lambda m: f"{m.group(1)}return std::exp({m.group(2)});" + if m.group(2) not in ["true", "false", "0", "1"] + else m.group(0), + content, + ) + + print( + " Added exp() transformation to convert log-space predictions to original space" + ) + if content != original_content: with open(main_path, "w") as f: f.write(content) - print( - " Optimized main.cpp by removing unnecessary missing data checks" - ) + if not log_transform: + print( + " Optimized main.cpp by removing unnecessary missing data checks" + ) except Exception as e: print(f" Warning: Failed to optimize main.cpp: {e}") @@ -1293,6 +1565,7 @@ def save_model( regressor_type: str, output_dir: str, feature_names: List[str], + log_transform: bool = False, ) -> None: """Save trained model and preprocessing components to disk.""" os.makedirs(output_dir, exist_ok=True) @@ -1302,6 +1575,7 @@ def save_model( "regressor_type": regressor_type, "feature_names": feature_names, "has_scaler": scaler is not None, + "log_transform": log_transform, } metadata_path = os.path.join(output_dir, f"{regressor_type}_metadata.pkl") @@ -1369,6 +1643,15 @@ def main(): # Train with automatic removal of invalid rows python train_regressor.py data.feather --regressor xgboost --drop-invalid-rows --seed 42 + # Stratify train/test split by target column (ensures balanced distribution) + python train_regressor.py data.feather --regressor xgboost --stratify-split --seed 42 + + # Stratify split by a specific column (e.g., time_ms) + python train_regressor.py data.feather --regressor xgboost --stratify-split time_ms --seed 42 + + # Optimize for relative error (recommended for targets spanning multiple orders of magnitude) + python train_regressor.py data.feather --regressor xgboost --log-transform --seed 42 + # Legacy pickle format python train_regressor.py data.pkl --regressor xgboost --seed 42 """, @@ -1426,8 +1709,12 @@ def main(): ) parser.add_argument( "--stratify-split", - action="store_true", - help="Stratify train/test split by target distribution (ensures balanced iter values)", + type=str, + nargs="?", + const="__target__", + default=None, + metavar="COLUMN", + help="Stratify train/test split by specified column distribution. If no column specified, uses target column. Example: --stratify-split time_ms or just --stratify-split for target column", ) parser.add_argument( "--early-stopping", @@ -1459,6 +1746,11 @@ def main(): default="iter", help="Target column to predict (default: iter). Examples: iter, time_ms, iterations", ) + parser.add_argument( + "--log-transform", + action="store_true", + help="Use log-transform on target variable to optimize for relative error instead of absolute error. Recommended when target values span multiple orders of magnitude.", + ) args = parser.parse_args() @@ -1561,7 +1853,22 @@ def main(): return # Split data by files - stratify_by = args.target if args.stratify_split else None + # Handle stratify-split argument + if args.stratify_split is None: + stratify_by = None + elif args.stratify_split == "__target__": + stratify_by = args.target + print(f"Stratifying split by target column: '{args.target}'") + else: + stratify_by = args.stratify_split + if stratify_by not in df.columns: + print( + f"\n❌ Error: Stratify column '{stratify_by}' not found in dataset" + ) + print(f"Available columns: {list(df.columns)}") + return 1 + print(f"Stratifying split by column: '{stratify_by}'") + train_df, test_df = split_by_files( df, test_size=args.test_size, @@ -1578,6 +1885,130 @@ def main(): print(f"\nFeatures: {len(feature_names)}") print(f"Target: {args.target} (prediction target)") + # Apply log transform if requested (for relative error optimization) + if args.log_transform: + # Check for non-positive values before log transform + if np.any(y_train <= 0) or np.any(y_test <= 0): + n_nonpositive_train = np.sum(y_train <= 0) + n_nonpositive_test = np.sum(y_test <= 0) + print( + "\n❌ Error: Cannot apply log-transform with non-positive target values!" + ) + print(f" Train set: {n_nonpositive_train} non-positive values") + print(f" Test set: {n_nonpositive_test} non-positive values") + print( + f" Target range: [{np.min(y_train):.2f}, {np.max(y_train):.2f}]" + ) + print( + "\nSuggestion: Add a small constant (e.g., +1) to all target values before log" + ) + return 1 + + print( + "\nApplying log-transform to target variable (optimizes for relative error)" + ) + y_train_original = y_train.copy() + y_test_original = y_test.copy() + + y_train = np.log(y_train) + y_test = np.log(y_test) + + print( + f" Original target range: [{np.min(y_train_original):.2f}, {np.max(y_train_original):.2f}]" + ) + print( + f" Log-space target range: [{np.min(y_train):.4f}, {np.max(y_train):.4f}]" + ) + else: + y_train_original = None + y_test_original = None + + # Enhanced diagnostics for XGBoost compatibility (only show if problems found) + X_train_array = X_train.values if hasattr(X_train, "values") else X_train + + # XGBoost internal limits (approximate) + xgb_max_safe = 1e38 # XGBoost uses float32 internally + + problematic_features = [] + problem_details = [] + + for i, col_name in enumerate(feature_names): + col_data = X_train_array[:, i] + + n_nan = np.sum(np.isnan(col_data)) + n_inf = np.sum(np.isinf(col_data)) + n_posinf = np.sum(np.isposinf(col_data)) + n_neginf = np.sum(np.isneginf(col_data)) + + # Get statistics on finite values + finite_mask = np.isfinite(col_data) + if np.any(finite_mask): + finite_data = col_data[finite_mask] + col_min = np.min(finite_data) + col_max = np.max(finite_data) + col_mean = np.mean(finite_data) + col_std = np.std(finite_data) + abs_max = max(abs(col_min), abs(col_max)) + else: + col_min = col_max = col_mean = col_std = abs_max = np.nan + + # Check if values are too large for XGBoost + is_problematic = ( + n_nan > 0 + or n_inf > 0 + or (not np.isnan(abs_max) and abs_max > xgb_max_safe) + ) + + if is_problematic: + problematic_features.append(col_name) + detail = f"\n⚠️ '{col_name}':" + if n_nan > 0: + detail += f"\n NaN: {n_nan:8d} ({100 * n_nan / len(col_data):6.2f}%)" + if n_posinf > 0: + detail += f"\n +Inf: {n_posinf:8d} ({100 * n_posinf / len(col_data):6.2f}%)" + if n_neginf > 0: + detail += f"\n -Inf: {n_neginf:8d} ({100 * n_neginf / len(col_data):6.2f}%)" + if not np.isnan(abs_max): + detail += f"\n Range: [{col_min:.6e}, {col_max:.6e}]" + detail += f"\n Max abs: {abs_max:.6e}" + if abs_max > xgb_max_safe: + detail += f"\n ❌ TOO LARGE! Exceeds XGBoost safe limit (~{xgb_max_safe:.2e})" + detail += f"\n Mean: {col_mean:.6e}" + detail += f"\n Std: {col_std:.6e}" + problem_details.append(detail) + + # Check target variable + n_nan_target = np.sum(np.isnan(y_train)) + n_inf_target = np.sum(np.isinf(y_train)) + if n_nan_target > 0 or n_inf_target > 0: + problematic_features.append(f"TARGET[{args.target}]") + detail = f"\n⚠️ Target '{args.target}':" + if n_nan_target > 0: + detail += f"\n NaN: {n_nan_target} ({100 * n_nan_target / len(y_train):.2f}%)" + if n_inf_target > 0: + detail += f"\n Inf: {n_inf_target} ({100 * n_inf_target / len(y_train):.2f}%)" + problem_details.append(detail) + + # Only print if problems found + if len(problematic_features) > 0: + print("\n" + "=" * 70) + print("⚠️ FEATURE VALUE PROBLEMS DETECTED") + print("=" * 70) + for detail in problem_details: + print(detail) + print("\n" + "=" * 70) + print(f"❌ Found {len(problematic_features)} problematic feature(s):") + for feat in problematic_features: + print(f" - {feat}") + print("\nTo fix:") + print( + " 1. Add these features to FEATURES_TO_EXCLUDE at top of script" + ) + print( + " 2. Or investigate why these features have extreme/invalid values" + ) + print("=" * 70 + "\n") + # Create model print(f"\nTraining {args.regressor} regressor...") model, needs_scaling = create_regressor( @@ -1589,12 +2020,24 @@ def main(): # Apply scaling if needed scaler = None + needs_scaling = False X_test_original = X_test.copy() # Keep unscaled version for display if needs_scaling: print(" Applying StandardScaler to features...") scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) + + # Sanity check: verify scaling worked correctly + print( + f" Scaled features - mean: {np.mean(X_train_scaled):.6f}, std: {np.std(X_train_scaled):.6f}" + ) + if np.any(np.isnan(X_train_scaled)) or np.any( + np.isinf(X_train_scaled) + ): + print(" WARNING: NaN or Inf detected in scaled training data!") + if np.any(np.isnan(X_test_scaled)) or np.any(np.isinf(X_test_scaled)): + print(" WARNING: NaN or Inf detected in scaled test data!") else: X_train_scaled = X_train X_test_scaled = X_test @@ -1688,15 +2131,25 @@ def main(): skip_cv=skip_cv, X_test_original=X_test_original, test_df=test_df, + log_transform=args.log_transform, + y_train_original=y_train_original, + y_test_original=y_test_original, ) # Save model - save_model(model, scaler, args.regressor, args.output_dir, feature_names) + save_model( + model, + scaler, + args.regressor, + args.output_dir, + feature_names, + log_transform=args.log_transform, + ) # Compile with TL2cgen if requested (with optimizations enabled by default) if args.treelite_compile is not None: - # Use unscaled training data for branch annotation - # Only tree-based models (XGBoost, LightGBM) don't need scaling + # Use unscaled training data for branch annotation when scaling is not applied + # Note: All models now use scaling for consistency X_train_for_annotation = X_train if not needs_scaling else None compile_model_treelite( @@ -1709,6 +2162,7 @@ def main(): quantize=True, # Always enable quantization feature_names=feature_names, model_name=model_name, + log_transform=args.log_transform, ) print("\n" + "=" * 70) From 8dfdfd8080bc25386a231cfcf479c6bb23b2a801 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 18 Nov 2025 15:07:26 +0000 Subject: [PATCH 080/225] fixes --- cpp/src/dual_simplex/solve.cpp | 2 +- cpp/src/linear_programming/pdlp.cu | 2 +- cpp/src/linear_programming/solve.cu | 2 +- cpp/src/mip/diversity/diversity_manager.cu | 18 +++----------- cpp/src/mip/diversity/diversity_manager.cuh | 1 - cpp/src/mip/feasibility_jump/fj_cpu.cu | 24 +------------------ .../local_search/rounding/bounds_repair.cu | 2 +- cpp/src/utilities/memory_instrumentation.hpp | 1 + cpp/src/utilities/timer.hpp | 2 +- 9 files changed, 10 insertions(+), 44 deletions(-) diff --git a/cpp/src/dual_simplex/solve.cpp b/cpp/src/dual_simplex/solve.cpp index 3431ca345c..7e59e83c37 100644 --- a/cpp/src/dual_simplex/solve.cpp +++ b/cpp/src/dual_simplex/solve.cpp @@ -192,7 +192,7 @@ lp_status_t solve_linear_program_with_advanced_basis( edge_norms.clear(); dual::status_t phase1_status; { - raft::common::nvtx::range scope_phase1("DualSimplex::phase1"); + raft::common::nvtx::range scope_phase1("DualSimplex::phase2"); phase1_status = dual_phase2(1, 1, start_time, diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index 1e1c03aa15..ba156920f4 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -257,7 +257,7 @@ static bool time_limit_reached(const timer_t& timer) bool elapsed = timer.elapsed_time() >= timer.get_time_limit(); if (elapsed) { CUOPT_LOG_ERROR("**** PDLP Time limit reached: %f *****", timer.get_time_limit()); - cuopt_assert(false, "unexpected timer"); + // cuopt_assert(false, "unexpected timer"); } return elapsed; } diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 92119cd105..42e32a030b 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -113,7 +113,7 @@ static void set_Stable2() pdlp_hyper_params::major_iteration = 40; pdlp_hyper_params::min_iteration_restart = 10; pdlp_hyper_params::restart_strategy = 1; - pdlp_hyper_params::never_restart_to_average = true; + pdlp_hyper_params::never_restart_to_average = false; pdlp_hyper_params::host_default_reduction_exponent = 0.3; pdlp_hyper_params::host_default_growth_exponent = 0.6; pdlp_hyper_params::host_default_primal_weight_update_smoothing = 0.5; diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index ea60ca1313..f7f9fdbd2c 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -229,7 +229,6 @@ void diversity_manager_t::generate_quick_feasible_solution() work_limit_timer_t sol_timer(context.gpu_heur_loop, generate_fast_solution_time); // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); - sol_timer.record_work(0); if (solution.get_feasible()) { population.run_solution_callbacks(solution); initial_sol_vector.emplace_back(std::move(solution)); @@ -325,12 +324,6 @@ solution_t diversity_manager_t::run_solver() const f_t time_limit = timer.remaining_time(); const f_t lp_time_limit = std::min(diversity_config.max_time_on_lp, time_limit * diversity_config.time_ratio_on_init_lp); - - if (context.settings.deterministic) { - remaining_work_limit = context.settings.work_limit; - CUOPT_LOG_INFO("Deterministic mode, remaining work limit: %f", time_limit); - } - // to automatically compute the solving time on scope exit auto timer_raii_guard = cuopt::scope_guard([&]() { stats.total_solve_time = timer.timer.elapsed_time(); }); @@ -357,7 +350,7 @@ solution_t diversity_manager_t::run_solver() // Run CPUFJ early to find quick initial solutions ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop if (!context.settings.deterministic) { -#if 1 +#if 0 ls.start_cpufj_scratch_threads(population); // 30'000 iters ls.scratch_cpu_fj[0].wait_for_cpu_solver(); @@ -376,7 +369,6 @@ solution_t diversity_manager_t::run_solver() if (check_b_b_preemption()) { return population.best_feasible(); } if (!diversity_config.fj_only_run) { compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); - probing_timer.record_work(0); } if (check_b_b_preemption()) { return population.best_feasible(); } @@ -386,7 +378,7 @@ solution_t diversity_manager_t::run_solver() bool bb_thread_solution_exists = simplex_solution_exists.load(); if (bb_thread_solution_exists) { ls.lp_optimal_exists = true; - } else if (!diversity_config.fj_only_run || true) { + } else if (!diversity_config.fj_only_run) { relaxed_lp_settings_t lp_settings; lp_settings.time_limit = lp_time_limit; lp_settings.work_limit = lp_time_limit; @@ -830,11 +822,7 @@ void diversity_manager_t::set_simplex_solution(const std::vector& template bool diversity_manager_t::work_limit_reached() { - if (context.settings.deterministic) { - return remaining_work_limit <= 0; - } else { - return work_limit_reached(); - } + return timer.check_time_limit(); } #if MIP_INSTANTIATE_FLOAT diff --git a/cpp/src/mip/diversity/diversity_manager.cuh b/cpp/src/mip/diversity/diversity_manager.cuh index 256e2762ef..b04aa26ef2 100644 --- a/cpp/src/mip/diversity/diversity_manager.cuh +++ b/cpp/src/mip/diversity/diversity_manager.cuh @@ -80,7 +80,6 @@ class diversity_manager_t { std::atomic simplex_solution_exists{false}; local_search_t ls; cuopt::work_limit_timer_t timer; - f_t remaining_work_limit{std::numeric_limits::infinity()}; bound_prop_recombiner_t bound_prop_recombiner; fp_recombiner_t fp_recombiner; line_segment_recombiner_t line_segment_recombiner; diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index f587455b25..31d1b8cc32 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -5,28 +5,6 @@ */ /* clang-format on */ -/* - * MEMORY OPERATION ANNOTATIONS: - * - * This file contains detailed comments marking all memory operations (array reads and writes) - * within loops for the CPU Feasibility Jump (CPUFJ) algorithm. These annotations are intended - * to help estimate the memory bandwidth requirements and runtime of this memory-bound workload. - * - * Key annotations: - * - "MEMORY OPS:" marks the start of a loop that performs memory operations - * - "ARRAY READ:" marks array read operations with array name and count per iteration - * - "ARRAY WRITE:" marks array write operations with array name and count per iteration - * - "CRITICAL LOOP" or "HOTTEST LOOP" marks the most frequently executed loops - * - "Total per iteration:" summarizes memory ops per loop iteration - * - * Important notation: - * - n_vars: number of variables - * - n_cstrs: number of constraints - * - avg_var_degree: average number of constraints per variable - * - avg_cstr_degree: average number of variables per constraint - * - total_nnz: total non-zeros in constraint matrix - */ - #include #include "feasibility_jump.cuh" @@ -1716,7 +1694,7 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l auto [loads, stores] = fj_cpu.memory_manifold.collect(); // Log all features including memory statistics - log_regression_features(fj_cpu, time_window_ms, total_time_ms, loads, stores); + // log_regression_features(fj_cpu, time_window_ms, total_time_ms, loads, stores); fj_cpu.last_feature_log_time = now; diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index d6cd62d2ee..507c978753 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -68,7 +68,7 @@ f_t bounds_repair_t::get_ii_violation(problem_t& problem) min_act = bound_presolve.upd.min_activity.data(), max_act = bound_presolve.upd.max_activity.data(), cstr_violations_up = cstr_violations_up.data(), - cstr_violations_down = cstr_violations_down.data()] __device__(i_t cstr_idx) -> f_t { + cstr_violations_down = cstr_violations_down.data()] __device__(i_t cstr_idx) { f_t cnst_lb = pb_v.constraint_lower_bounds[cstr_idx]; f_t cnst_ub = pb_v.constraint_upper_bounds[cstr_idx]; f_t eps = get_cstr_tolerance( diff --git a/cpp/src/utilities/memory_instrumentation.hpp b/cpp/src/utilities/memory_instrumentation.hpp index 2975a1e360..55cce572d6 100644 --- a/cpp/src/utilities/memory_instrumentation.hpp +++ b/cpp/src/utilities/memory_instrumentation.hpp @@ -23,6 +23,7 @@ * // When enabled: tracking occurs, counters accumulate * // When disabled: direct passthrough, compiler optimizes away all overhead */ +// Thank you Cursor! #pragma once diff --git a/cpp/src/utilities/timer.hpp b/cpp/src/utilities/timer.hpp index bb74e75b97..8e53d1a4cb 100644 --- a/cpp/src/utilities/timer.hpp +++ b/cpp/src/utilities/timer.hpp @@ -45,7 +45,7 @@ class timer_t { file, line, caller); - assert(false && "unexpected timer"); + // assert(false && "unexpected timer"); //__builtin_trap(); } return elapsed; From 8f260672001e24c0db382b7e39b29e11e5fae2e6 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 18 Nov 2025 17:24:51 +0000 Subject: [PATCH 081/225] restore fj scratch --- cpp/src/mip/diversity/diversity_manager.cu | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index f7f9fdbd2c..6cdd8a63f9 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -360,6 +360,8 @@ solution_t diversity_manager_t::run_solver() #endif } + if (!context.settings.deterministic) { ls.start_cpufj_scratch_threads(population); } + // before probing cache or LP, run FJ to generate initial primal feasible solution const f_t time_ratio_of_probing_cache = diversity_config.time_ratio_of_probing_cache; const f_t max_time_on_probing = diversity_config.max_time_on_probing; @@ -454,7 +456,7 @@ solution_t diversity_manager_t::run_solver() lp_rounded_sol.round_nearest(); lp_rounded_sol.compute_feasibility(); population.add_solution(std::move(lp_rounded_sol)); - // if (!context.settings.deterministic) { ls.start_cpufj_lptopt_scratch_threads(population); } + if (!context.settings.deterministic) { ls.start_cpufj_lptopt_scratch_threads(population); } } population.add_solutions_from_vec(std::move(initial_sol_vector)); From 759b8964affe1ba7dbd9cd0479eec6e366a34b45 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 25 Nov 2025 12:59:28 +0000 Subject: [PATCH 082/225] fix build --- cpp/src/dual_simplex/branch_and_bound.cpp | 156 ++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index ae6cbfc766..9284520b63 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -237,6 +237,162 @@ branch_and_bound_t::branch_and_bound_t( compute_static_features(); } +template +void branch_and_bound_t::compute_static_features() +{ + const auto& A = original_lp_.A; + static_features_.n_rows = A.m; + static_features_.n_cols = A.n; + static_features_.n_nonzeros = A.col_start[A.n]; + static_features_.density = (f_t)static_features_.n_nonzeros / ((f_t)A.m * A.n); + + // Count variable types + static_features_.n_binary = 0; + static_features_.n_integer = 0; + static_features_.n_continuous = 0; + for (const auto& vt : var_types_) { + if (vt == variable_type_t::BINARY) { + static_features_.n_binary++; + } else if (vt == variable_type_t::INTEGER) { + static_features_.n_integer++; + } else { + static_features_.n_continuous++; + } + } + static_features_.integrality_ratio = + (f_t)(static_features_.n_binary + static_features_.n_integer) / A.n; + + // Compute row statistics (constraint sizes) + std::vector row_nnz(A.m, 0); + for (i_t j = 0; j < A.n; j++) { + for (i_t k = A.col_start[j]; k < A.col_start[j + 1]; k++) { + row_nnz[A.i[k]]++; + } + } + + static_features_.max_row_nnz = 0; + f_t sum_row_nnz = 0; + for (i_t i = 0; i < A.m; i++) { + static_features_.max_row_nnz = std::max(static_features_.max_row_nnz, row_nnz[i]); + sum_row_nnz += row_nnz[i]; + } + static_features_.avg_row_nnz = sum_row_nnz / A.m; + + // Compute row coefficient of variation + f_t row_variance = 0; + for (i_t i = 0; i < A.m; i++) { + f_t diff = row_nnz[i] - static_features_.avg_row_nnz; + row_variance += diff * diff; + } + row_variance /= A.m; + f_t row_std = std::sqrt(row_variance); + static_features_.row_nnz_cv = + static_features_.avg_row_nnz > 0 ? row_std / static_features_.avg_row_nnz : 0.0; + + // Compute column statistics (variable degrees) + static_features_.max_col_nnz = 0; + f_t sum_col_nnz = 0; + for (i_t j = 0; j < A.n; j++) { + i_t col_nnz = A.col_start[j + 1] - A.col_start[j]; + static_features_.max_col_nnz = std::max(static_features_.max_col_nnz, col_nnz); + sum_col_nnz += col_nnz; + } + static_features_.avg_col_nnz = sum_col_nnz / A.n; + + // Compute column coefficient of variation + f_t col_variance = 0; + for (i_t j = 0; j < A.n; j++) { + i_t col_nnz = A.col_start[j + 1] - A.col_start[j]; + f_t diff = col_nnz - static_features_.avg_col_nnz; + col_variance += diff * diff; + } + col_variance /= A.n; + f_t col_std = std::sqrt(col_variance); + static_features_.col_nnz_cv = + static_features_.avg_col_nnz > 0 ? col_std / static_features_.avg_col_nnz : 0.0; +} + +template +void branch_and_bound_t::flush_pending_features() +{ + // Must be called with mutex_feature_log_ already locked + if (!has_pending_features_) return; + + constexpr int LINE_BUFFER_SIZE = 512; + char line_buffer[LINE_BUFFER_SIZE]; + + snprintf(line_buffer, + LINE_BUFFER_SIZE, + "BB_NODE_FEATURES " + "node_id=%d depth=%d time=%.6f " + "n_rows=%d n_cols=%d n_nnz=%d density=%.6f " + "n_bin=%d n_int=%d n_cont=%d int_ratio=%.4f " + "avg_row_nnz=%.2f max_row_nnz=%d row_nnz_cv=%.4f " + "avg_col_nnz=%.2f max_col_nnz=%d col_nnz_cv=%.4f " + "n_bounds_chg=%d cutoff_gap=%.4f basis_from_parent=%d " + "simplex_iters=%d n_refact=%d lp_time=%.6f bound_str_time=%.6f var_sel_time=%.6f " + "n_frac=%d strong_branch=%d n_sb_cand=%d sb_time=%.6f " + "lp_status=%d node_status=%d\n", + last_features_.node_id, + last_features_.node_depth, + last_features_.total_node_time, + last_features_.n_rows, + last_features_.n_cols, + last_features_.n_nonzeros, + last_features_.density, + last_features_.n_binary, + last_features_.n_integer, + last_features_.n_continuous, + last_features_.integrality_ratio, + last_features_.avg_row_nnz, + last_features_.max_row_nnz, + last_features_.row_nnz_cv, + last_features_.avg_col_nnz, + last_features_.max_col_nnz, + last_features_.col_nnz_cv, + last_features_.n_bounds_changed, + last_features_.cutoff_gap_ratio, + last_features_.basis_from_parent ? 1 : 0, + last_features_.simplex_iterations, + last_features_.n_refactorizations, + last_features_.lp_solve_time, + last_features_.bound_str_time, + last_features_.variable_sel_time, + last_features_.n_fractional, + last_features_.strong_branch_performed ? 1 : 0, + last_features_.n_strong_branch_candidates, + last_features_.strong_branch_time, + last_features_.lp_status, + last_features_.node_status); + + // Single printf call + settings_.log.printf("%s", line_buffer); + + has_pending_features_ = false; +} + +template +void branch_and_bound_t::log_node_features( + const node_solve_features_t& features) +{ + mutex_feature_log_.lock(); + + f_t current_time = toc(exploration_stats_.start_time); + f_t time_since_last_log = current_time - last_feature_log_time_; + + // Always store the latest features + last_features_ = features; + has_pending_features_ = true; + + // Log if enough time has passed (500ms) + if (time_since_last_log >= FEATURE_LOG_INTERVAL) { + flush_pending_features(); + last_feature_log_time_ = current_time; + } + + mutex_feature_log_.unlock(); +} + template f_t branch_and_bound_t::get_upper_bound() { From dc30219eeda5ec967dbbee9e2e244de6c1f5fcfe Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 25 Nov 2025 14:38:01 +0000 Subject: [PATCH 083/225] tmp dual simplex instrument --- .../linear_programming/cuopt/run_mip.cpp | 2 - .../dual_simplex/dual_simplex_features.hpp | 153 ++++++++++ cpp/src/dual_simplex/phase2.cpp | 266 ++++++++++++------ cpp/src/dual_simplex/sparse_vector.cpp | 52 +++- cpp/src/dual_simplex/sparse_vector.hpp | 20 +- cpp/src/mip/problem/problem_helpers.cuh | 3 + 6 files changed, 394 insertions(+), 102 deletions(-) create mode 100644 cpp/src/dual_simplex/dual_simplex_features.hpp diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 30afac4ef3..fe06f1638c 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -206,8 +206,6 @@ int run_single_file(std::string file_path, settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; settings.presolve = true; - - settings.presolve = false; // settings.heuristics_only = true; cuopt::linear_programming::benchmark_info_t benchmark_info; diff --git a/cpp/src/dual_simplex/dual_simplex_features.hpp b/cpp/src/dual_simplex/dual_simplex_features.hpp new file mode 100644 index 0000000000..f7f8807c82 --- /dev/null +++ b/cpp/src/dual_simplex/dual_simplex_features.hpp @@ -0,0 +1,153 @@ +/* clang-format off */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* clang-format on */ + +#pragma once + +#include +#include +#include + +#include + +namespace cuopt::linear_programming::dual_simplex { + +/** + * @brief Feature collection structure for dual simplex runtime prediction. + * + * This structure collects features that can be used to train regression models + * for predicting the runtime of dual_phase2_with_advanced_basis. + */ +template +struct dual_simplex_features_t { + // Model/Problem Features (static) + i_t num_rows{0}; // m - number of constraints + i_t num_cols{0}; // n - number of variables + i_t num_nonzeros{0}; // nnz - total nonzeros in constraint matrix + f_t matrix_density{0.0}; // nnz / (m * n) + f_t avg_nnz_per_col{0.0}; // nnz / n + f_t avg_nnz_per_row{0.0}; // nnz / m + i_t num_bounded_vars{0}; // variables with finite lower AND upper bounds + i_t num_free_vars{0}; // variables with infinite bounds on both sides + i_t num_fixed_vars{0}; // variables where lower == upper + + // Iteration-based features (dynamic) + i_t iteration{0}; // current iteration count + i_t start_iteration{0}; // iteration at start of this call + i_t num_refactors{0}; // number of basis refactorizations + i_t num_basis_updates{0}; // basis updates since last refactor + i_t sparse_delta_z_count{0}; // iterations using sparse delta_z + i_t dense_delta_z_count{0}; // iterations using dense delta_z + i_t total_bound_flips{0}; // cumulative bound flips + + // Sparsity during solve + i_t num_infeasibilities{0}; // size of infeasibility_indices + f_t delta_y_nz_percentage{0.0}; // sparsity of BTran result + + // Phase-specific features + i_t phase{0}; // 1 or 2 + bool slack_basis{false}; // whether starting from slack basis + bool initialize_basis{false}; // whether basis factorization performed initially + + // Settings that impact runtime + i_t refactor_frequency{0}; // from settings + + // Memory access statistics (aggregated from instrumentation) + size_t byte_loads{0}; // total bytes loaded + size_t byte_stores{0}; // total bytes stored + + /** + * @brief Initialize static features from problem data. + */ + void init_from_problem(const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + i_t phase_, + bool slack_basis_, + bool initialize_basis_) + { + num_rows = lp.num_rows; + num_cols = lp.num_cols; + num_nonzeros = lp.A.col_start[lp.num_cols]; + + const f_t total_elements = static_cast(num_rows) * static_cast(num_cols); + matrix_density = (total_elements > 0) ? num_nonzeros / total_elements : 0.0; + avg_nnz_per_col = (num_cols > 0) ? static_cast(num_nonzeros) / num_cols : 0.0; + avg_nnz_per_row = (num_rows > 0) ? static_cast(num_nonzeros) / num_rows : 0.0; + + // Count bound types + num_bounded_vars = 0; + num_free_vars = 0; + num_fixed_vars = 0; + constexpr f_t inf_val = std::numeric_limits::infinity(); + for (i_t j = 0; j < num_cols; ++j) { + const bool has_lower = lp.lower[j] > -inf_val; + const bool has_upper = lp.upper[j] < inf_val; + if (has_lower && has_upper) { + if (lp.lower[j] == lp.upper[j]) { + num_fixed_vars++; + } else { + num_bounded_vars++; + } + } else if (!has_lower && !has_upper) { + num_free_vars++; + } + } + + phase = phase_; + slack_basis = slack_basis_; + initialize_basis = initialize_basis_; + refactor_frequency = settings.refactor_frequency; + } + + /** + * @brief Print all features on a single line in key=value format. + * + * Format: DS_FEATURES: iter=N m=M n=N nnz=K ... + */ + void log_features(const simplex_solver_settings_t& settings) const + { + settings.log.printf( + "DS_FEATURES: iter=%d m=%d n=%d nnz=%d density=%.6e avg_nnz_col=%.2f avg_nnz_row=%.2f " + "bounded=%d free=%d fixed=%d phase=%d refact_freq=%d num_refacts=%d num_updates=%d " + "sparse_dz=%d dense_dz=%d bound_flips=%d num_infeas=%d dy_nz_pct=%.2f " + "byte_loads=%zu byte_stores=%zu\n", + iteration, + num_rows, + num_cols, + num_nonzeros, + matrix_density, + avg_nnz_per_col, + avg_nnz_per_row, + num_bounded_vars, + num_free_vars, + num_fixed_vars, + phase, + refactor_frequency, + num_refactors, + num_basis_updates, + sparse_delta_z_count, + dense_delta_z_count, + total_bound_flips, + num_infeasibilities, + delta_y_nz_percentage, + byte_loads, + byte_stores); + } + + /** + * @brief Reset per-interval counters (called after each logging interval). + */ + void reset_interval_counters() + { + byte_loads = 0; + byte_stores = 0; + } +}; + +// Feature logging interval (every N iterations) +constexpr int FEATURE_LOG_INTERVAL = 100; + +} // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 12b4e79285..226bbe463b 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,9 @@ namespace cuopt::linear_programming::dual_simplex { +// Import instrumented vector type for memory tracking +using cuopt::ins_vector; + namespace phase2 { // Computes vectors farkas_y, farkas_zl, farkas_zu that satisfy @@ -271,12 +275,12 @@ template void compute_reduced_cost_update(const lp_problem_t& lp, const std::vector& basic_list, const std::vector& nonbasic_list, - const std::vector& delta_y, + const ins_vector& delta_y, i_t leaving_index, i_t direction, - std::vector& delta_z_mark, - std::vector& delta_z_indices, - std::vector& delta_z) + ins_vector& delta_z_mark, + ins_vector& delta_z_indices, + ins_vector& delta_z) { const i_t m = lp.num_rows; const i_t n = lp.num_cols; @@ -310,10 +314,10 @@ void compute_delta_z(const csc_matrix_t& A_transpose, const sparse_vector_t& delta_y, i_t leaving_index, i_t direction, - std::vector& nonbasic_mark, - std::vector& delta_z_mark, - std::vector& delta_z_indices, - std::vector& delta_z) + ins_vector& nonbasic_mark, + ins_vector& delta_z_mark, + ins_vector& delta_z_indices, + ins_vector& delta_z) { // delta_zN = - N'*delta_y const i_t nz_delta_y = delta_y.i.size(); @@ -436,9 +440,9 @@ void compute_primal_variables(const basis_update_mpf_t& ft, template void clear_delta_z(i_t entering_index, i_t leaving_index, - std::vector& delta_z_mark, - std::vector& delta_z_indices, - std::vector& delta_z) + ins_vector& delta_z_mark, + ins_vector& delta_z_indices, + ins_vector& delta_z) { for (i_t k = 0; k < delta_z_indices.size(); k++) { const i_t j = delta_z_indices[k]; @@ -454,7 +458,7 @@ template void clear_delta_x(const std::vector& basic_list, i_t entering_index, sparse_vector_t& scaled_delta_xB_sparse, - std::vector& delta_x) + ins_vector& delta_x) { const i_t scaled_delta_xB_nz = scaled_delta_xB_sparse.i.size(); for (i_t k = 0; k < scaled_delta_xB_nz; ++k) { @@ -519,7 +523,7 @@ void vstatus_changes(const std::vector& vstatus, template void compute_bounded_info(const std::vector& lower, const std::vector& upper, - std::vector& bounded_variables) + ins_vector& bounded_variables) { const size_t n = lower.size(); for (size_t j = 0; j < n; j++) { @@ -624,8 +628,8 @@ f_t compute_initial_primal_infeasibilities(const lp_problem_t& lp, const simplex_solver_settings_t& settings, const std::vector& basic_list, const std::vector& x, - std::vector& squared_infeasibilities, - std::vector& infeasibility_indices) + ins_vector& squared_infeasibilities, + ins_vector& infeasibility_indices) { const i_t m = lp.num_rows; const i_t n = lp.num_cols; @@ -653,8 +657,8 @@ void update_single_primal_infeasibility(const std::vector& lower, const std::vector& upper, const std::vector& x, f_t primal_tol, - std::vector& squared_infeasibilities, - std::vector& infeasibility_indices, + ins_vector& squared_infeasibilities, + ins_vector& infeasibility_indices, i_t j, f_t& primal_inf) { @@ -696,8 +700,8 @@ void update_primal_infeasibilities(const lp_problem_t& lp, i_t entering_index, i_t leaving_index, std::vector& basic_change_list, - std::vector& squared_infeasibilities, - std::vector& infeasibility_indices, + ins_vector& squared_infeasibilities, + ins_vector& infeasibility_indices, f_t& primal_inf) { const f_t primal_tol = settings.primal_tol; @@ -726,8 +730,8 @@ void update_primal_infeasibilities(const lp_problem_t& lp, } template -void clean_up_infeasibilities(std::vector& squared_infeasibilities, - std::vector& infeasibility_indices) +void clean_up_infeasibilities(ins_vector& squared_infeasibilities, + ins_vector& infeasibility_indices) { bool needs_clean_up = false; for (i_t k = 0; k < infeasibility_indices.size(); ++k) { @@ -756,9 +760,9 @@ i_t steepest_edge_pricing_with_infeasibilities(const lp_problem_t& lp, const simplex_solver_settings_t& settings, const std::vector& x, const std::vector& dy_steepest_edge, - const std::vector& basic_mark, - std::vector& squared_infeasibilities, - std::vector& infeasibility_indices, + const ins_vector& basic_mark, + ins_vector& squared_infeasibilities, + ins_vector& infeasibility_indices, i_t& direction, i_t& basic_leaving, f_t& max_val) @@ -1029,20 +1033,21 @@ i_t phase2_ratio_test(const lp_problem_t& lp, template i_t flip_bounds(const lp_problem_t& lp, const simplex_solver_settings_t& settings, - const std::vector& bounded_variables, + const ins_vector& bounded_variables, const std::vector& objective, const std::vector& z, - const std::vector& delta_z_indices, + const ins_vector& delta_z_indices, const std::vector& nonbasic_list, i_t entering_index, std::vector& vstatus, - std::vector& delta_x, - std::vector& mark, - std::vector& atilde, - std::vector& atilde_index) + ins_vector& delta_x_flip, + ins_vector& atilde_mark, + ins_vector& atilde, + ins_vector& atilde_index) { i_t num_flipped = 0; - for (i_t j : delta_z_indices) { + for (i_t k = 0; k < delta_z_indices.size(); ++k) { + const i_t j = delta_z_indices[k]; if (j == entering_index) { continue; } if (!bounded_variables[j]) { continue; } // x_j is now a nonbasic bounded variable that will not enter the basis this @@ -1051,8 +1056,8 @@ i_t flip_bounds(const lp_problem_t& lp, settings.dual_tol; // lower to 1e-7 or less will cause 25fv47 and d2q06c to cycle if (vstatus[j] == variable_status_t::NONBASIC_LOWER && z[j] < -dual_tol) { const f_t delta = lp.upper[j] - lp.lower[j]; - scatter_dense(lp.A, j, -delta, atilde, mark, atilde_index); - delta_x[j] += delta; + scatter_dense(lp.A, j, -delta, atilde.array, atilde_mark.array, atilde_index.array); + delta_x_flip[j] += delta; vstatus[j] = variable_status_t::NONBASIC_UPPER; #ifdef BOUND_FLIP_DEBUG settings.log.printf( @@ -1061,8 +1066,8 @@ i_t flip_bounds(const lp_problem_t& lp, num_flipped++; } else if (vstatus[j] == variable_status_t::NONBASIC_UPPER && z[j] > dual_tol) { const f_t delta = lp.lower[j] - lp.upper[j]; - scatter_dense(lp.A, j, -delta, atilde, mark, atilde_index); - delta_x[j] += delta; + scatter_dense(lp.A, j, -delta, atilde.array, atilde_mark.array, atilde_index.array); + delta_x_flip[j] += delta; vstatus[j] = variable_status_t::NONBASIC_LOWER; #ifdef BOUND_FLIP_DEBUG settings.log.printf( @@ -1245,12 +1250,12 @@ i_t update_steepest_edge_norms(const simplex_solver_settings_t& settin const sparse_vector_t& scaled_delta_xB, i_t basic_leaving_index, i_t entering_index, - std::vector& v, + ins_vector& v, + sparse_vector_t& v_sparse, std::vector& delta_y_steepest_edge) { - i_t m = basic_list.size(); const i_t delta_y_nz = delta_y_sparse.i.size(); - sparse_vector_t v_sparse(m, 0); + v_sparse.clear(); // B^T delta_y = - direction * e_basic_leaving_index // We want B v = - B^{-T} e_basic_leaving_index ft.b_solve(delta_y_sparse, v_sparse); @@ -1365,7 +1370,7 @@ i_t check_steepest_edge_norms(const simplex_solver_settings_t& setting template i_t compute_perturbation(const lp_problem_t& lp, const simplex_solver_settings_t& settings, - const std::vector& delta_z_indices, + const ins_vector& delta_z_indices, std::vector& z, std::vector& objective, f_t& sum_perturb) @@ -1414,8 +1419,8 @@ i_t compute_perturbation(const lp_problem_t& lp, template void reset_basis_mark(const std::vector& basic_list, const std::vector& nonbasic_list, - std::vector& basic_mark, - std::vector& nonbasic_mark) + ins_vector& basic_mark, + ins_vector& nonbasic_mark) { const i_t m = basic_list.size(); const i_t n = nonbasic_mark.size(); @@ -1483,8 +1488,8 @@ void compute_delta_y(const basis_update_mpf_t& ft, template i_t update_dual_variables(const sparse_vector_t& delta_y_sparse, - const std::vector& delta_z_indices, - const std::vector& delta_z, + const ins_vector& delta_z_indices, + const ins_vector& delta_z, f_t step_length, i_t leaving_index, std::vector& y, @@ -1510,21 +1515,23 @@ i_t update_dual_variables(const sparse_vector_t& delta_y_sparse, template void adjust_for_flips(const basis_update_mpf_t& ft, const std::vector& basic_list, - const std::vector& delta_z_indices, - std::vector& atilde_index, - std::vector& atilde, - std::vector& atilde_mark, + const ins_vector& delta_z_indices, + ins_vector& atilde_index, + ins_vector& atilde, + ins_vector& atilde_mark, + sparse_vector_t& atilde_sparse, sparse_vector_t& delta_xB_0_sparse, - std::vector& delta_x_flip, + ins_vector& delta_x_flip, std::vector& x) { - const i_t m = basic_list.size(); const i_t atilde_nz = atilde_index.size(); // B*delta_xB_0 = atilde - sparse_vector_t atilde_sparse(m, atilde_nz); + atilde_sparse.clear(); + atilde_sparse.i.reserve(atilde_nz); + atilde_sparse.x.reserve(atilde_nz); for (i_t k = 0; k < atilde_nz; ++k) { - atilde_sparse.i[k] = atilde_index[k]; - atilde_sparse.x[k] = atilde[atilde_index[k]]; + atilde_sparse.i.push_back(atilde_index[k]); + atilde_sparse.x.push_back(atilde[atilde_index[k]]); } ft.b_solve(atilde_sparse, delta_xB_0_sparse); const i_t delta_xB_0_nz = delta_xB_0_sparse.i.size(); @@ -1533,7 +1540,8 @@ void adjust_for_flips(const basis_update_mpf_t& ft, x[j] += delta_xB_0_sparse.x[k]; } - for (i_t j : delta_z_indices) { + for (i_t k = 0; k < delta_z_indices.size(); ++k) { + const i_t j = delta_z_indices[k]; x[j] += delta_x_flip[j]; delta_x_flip[j] = 0.0; } @@ -1620,7 +1628,7 @@ i_t compute_delta_x(const lp_problem_t& lp, template void update_primal_variables(const sparse_vector_t& scaled_delta_xB_sparse, const std::vector& basic_list, - const std::vector& delta_x, + const ins_vector& delta_x, i_t entering_index, std::vector& x) { @@ -1638,7 +1646,7 @@ template void update_objective(const std::vector& basic_list, const std::vector& changed_basic_indices, const std::vector& objective, - const std::vector& delta_x, + const ins_vector& delta_x, i_t entering_index, f_t& obj) { @@ -1781,8 +1789,8 @@ void check_primal_infeasibilities(const lp_problem_t& lp, const simplex_solver_settings_t& settings, const std::vector& basic_list, const std::vector& x, - const std::vector& squared_infeasibilities, - const std::vector& infeasibility_indices) + const ins_vector& squared_infeasibilities, + const ins_vector& infeasibility_indices) { const i_t m = basic_list.size(); for (i_t k = 0; k < m; ++k) { @@ -1812,8 +1820,8 @@ void check_primal_infeasibilities(const lp_problem_t& lp, template void check_basic_infeasibilities(const std::vector& basic_list, - const std::vector& basic_mark, - const std::vector& infeasibility_indices, + const ins_vector& basic_mark, + const ins_vector& infeasibility_indices, i_t info) { for (i_t k = 0; k < infeasibility_indices.size(); ++k) { @@ -1856,8 +1864,8 @@ template void check_basis_mark(const simplex_solver_settings_t& settings, const std::vector& basic_list, const std::vector& nonbasic_list, - const std::vector& basic_mark, - const std::vector& nonbasic_mark) + const ins_vector& basic_mark, + const ins_vector& nonbasic_mark) { const i_t m = basic_list.size(); const i_t n = basic_mark.size(); @@ -2339,26 +2347,28 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } const i_t iter_limit = settings.iteration_limit; - std::vector delta_y(m, 0.0); - std::vector delta_z(n, 0.0); - std::vector delta_x(n, 0.0); - std::vector delta_x_flip(n, 0.0); - std::vector atilde(m, 0.0); - std::vector atilde_mark(m, 0); - std::vector atilde_index; - std::vector nonbasic_mark(n); - std::vector basic_mark(n); - std::vector delta_z_mark(n, 0); - std::vector delta_z_indices; - std::vector v(m, 0.0); - std::vector squared_infeasibilities; - std::vector infeasibility_indices; + + // Instrumented vectors for memory access tracking + ins_vector delta_y(m, 0.0); + ins_vector delta_z(n, 0.0); + ins_vector delta_x(n, 0.0); + ins_vector delta_x_flip(n, 0.0); + ins_vector atilde(m, 0.0); + ins_vector atilde_mark(m, 0); + ins_vector atilde_index; + ins_vector nonbasic_mark(n); + ins_vector basic_mark(n); + ins_vector delta_z_mark(n, 0); + ins_vector delta_z_indices; + ins_vector v(m, 0.0); + ins_vector squared_infeasibilities; + ins_vector infeasibility_indices; delta_z_indices.reserve(n); phase2::reset_basis_mark(basic_list, nonbasic_list, basic_mark, nonbasic_mark); - std::vector bounded_variables(n, 0); + ins_vector bounded_variables(n, 0); phase2::compute_bounded_info(lp.lower, lp.upper, bounded_variables); f_t primal_infeasibility = phase2::compute_initial_primal_infeasibilities( @@ -2374,10 +2384,66 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, f_t obj = compute_objective(lp, x); const i_t start_iter = iter; - i_t sparse_delta_z = 0; - i_t dense_delta_z = 0; + i_t sparse_delta_z = 0; + i_t dense_delta_z = 0; + i_t num_refactors = 0; + i_t total_bound_flips = 0; + f_t delta_y_nz_percentage = 0.0; phase2::phase2_timers_t timers(false); + // Feature collection for regression training + dual_simplex_features_t features; + features.init_from_problem(lp, settings, phase, slack_basis != 0, initialize_basis); + features.start_iteration = iter; + + // Sparse vectors for main loop (declared outside loop for instrumentation) + sparse_vector_t delta_y_sparse(m, 0); + sparse_vector_t UTsol_sparse(m, 0); + sparse_vector_t delta_xB_0_sparse(m, 0); + sparse_vector_t utilde_sparse(m, 0); + sparse_vector_t scaled_delta_xB_sparse(m, 0); + sparse_vector_t rhs_sparse(m, 0); + sparse_vector_t v_sparse(m, 0); // For steepest edge norms + sparse_vector_t atilde_sparse(m, 0); // For flip adjustments + + // Create instrumentation manifold + instrumentation_manifold_t manifold; + manifold.add("delta_y", delta_y); + manifold.add("delta_z", delta_z); + manifold.add("delta_x", delta_x); + manifold.add("delta_x_flip", delta_x_flip); + manifold.add("atilde", atilde); + manifold.add("atilde_mark", atilde_mark); + manifold.add("atilde_index", atilde_index); + manifold.add("nonbasic_mark", nonbasic_mark); + manifold.add("basic_mark", basic_mark); + manifold.add("delta_z_mark", delta_z_mark); + manifold.add("delta_z_indices", delta_z_indices); + manifold.add("v", v); + manifold.add("squared_infeasibilities", squared_infeasibilities); + manifold.add("infeasibility_indices", infeasibility_indices); + manifold.add("bounded_variables", bounded_variables); + + // Add sparse vector internal arrays to manifold + manifold.add("delta_y_sparse.i", delta_y_sparse.i); + manifold.add("delta_y_sparse.x", delta_y_sparse.x); + manifold.add("UTsol_sparse.i", UTsol_sparse.i); + manifold.add("UTsol_sparse.x", UTsol_sparse.x); + manifold.add("delta_xB_0_sparse.i", delta_xB_0_sparse.i); + manifold.add("delta_xB_0_sparse.x", delta_xB_0_sparse.x); + manifold.add("utilde_sparse.i", utilde_sparse.i); + manifold.add("utilde_sparse.x", utilde_sparse.x); + manifold.add("scaled_delta_xB_sparse.i", scaled_delta_xB_sparse.i); + manifold.add("scaled_delta_xB_sparse.x", scaled_delta_xB_sparse.x); + manifold.add("rhs_sparse.i", rhs_sparse.i); + manifold.add("rhs_sparse.x", rhs_sparse.x); + manifold.add("v_sparse.i", v_sparse.i); + manifold.add("v_sparse.x", v_sparse.x); + manifold.add("atilde_sparse.i", atilde_sparse.i); + manifold.add("atilde_sparse.x", atilde_sparse.x); + + // Note: Features are logged inline with DS_FEATURES: prefix + while (iter < iter_limit) { // Pricing i_t direction = 0; @@ -2428,8 +2494,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, // BTran // BT*delta_y = -delta_zB = -sigma*ei timers.start_timer(); - sparse_vector_t delta_y_sparse(m, 0); - sparse_vector_t UTsol_sparse(m, 0); + delta_y_sparse.clear(); + UTsol_sparse.clear(); { raft::common::nvtx::range scope_btran("DualSimplex::btran"); phase2::compute_delta_y(ft, basic_leaving_index, direction, delta_y_sparse, UTsol_sparse); @@ -2458,8 +2524,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, for (i_t k = 0; k < nz_delta_y; k++) { if (std::abs(delta_y_sparse.x[k]) > 1e-12) { delta_y_nz0++; } } - const f_t delta_y_nz_percentage = delta_y_nz0 / static_cast(m) * 100.0; - const bool use_transpose = delta_y_nz_percentage <= 30.0; + delta_y_nz_percentage = delta_y_nz0 / static_cast(m) * 100.0; + const bool use_transpose = delta_y_nz_percentage <= 30.0; if (use_transpose) { sparse_delta_z++; phase2::compute_delta_z(A_transpose, @@ -2473,7 +2539,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } else { dense_delta_z++; // delta_zB = sigma*ei - delta_y_sparse.to_dense(delta_y); + delta_y_sparse.to_dense(delta_y.array); phase2::compute_reduced_cost_update(lp, basic_list, nonbasic_list, @@ -2720,8 +2786,9 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, atilde_index); timers.flip_time += timers.stop_timer(); + total_bound_flips += num_flipped; - sparse_vector_t delta_xB_0_sparse(m, 0); + delta_xB_0_sparse.clear(); if (num_flipped > 0) { timers.start_timer(); phase2::adjust_for_flips(ft, @@ -2730,6 +2797,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, atilde_index, atilde, atilde_mark, + atilde_sparse, delta_xB_0_sparse, delta_x_flip, x); @@ -2737,9 +2805,9 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } timers.start_timer(); - sparse_vector_t utilde_sparse(m, 0); - sparse_vector_t scaled_delta_xB_sparse(m, 0); - sparse_vector_t rhs_sparse(lp.A, entering_index); + utilde_sparse.clear(); + scaled_delta_xB_sparse.clear(); + rhs_sparse.from_csc_column(lp.A, entering_index); { raft::common::nvtx::range scope_ftran("DualSimplex::ftran"); if (phase2::compute_delta_x(lp, @@ -2780,6 +2848,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, basic_leaving_index, entering_index, v, + v_sparse, delta_y_steepest_edge); #ifdef STEEPEST_EDGE_DEBUG if (steepest_edge_status == -1) { @@ -2901,6 +2970,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, #endif if (should_refactor) { raft::common::nvtx::range scope_refactor("DualSimplex::refactorization"); + num_refactors++; bool should_recompute_x = false; if (ft.refactor_basis(lp.A, settings, basic_list, nonbasic_list, vstatus) > 0) { should_recompute_x = true; @@ -2962,6 +3032,28 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, phase2::clear_delta_z(entering_index, leaving_index, delta_z_mark, delta_z_indices, delta_z); f_t now = toc(start_time); + + // Feature logging for regression training (every FEATURE_LOG_INTERVAL iterations) + if ((iter % FEATURE_LOG_INTERVAL) == 0) { + // Collect aggregated memory access statistics + auto [total_loads, total_stores] = manifold.collect_and_flush(); + features.byte_loads = total_loads; + features.byte_stores = total_stores; + + // Update dynamic features + features.iteration = iter; + features.num_refactors = num_refactors; + features.num_basis_updates = ft.num_updates(); + features.sparse_delta_z_count = sparse_delta_z; + features.dense_delta_z_count = dense_delta_z; + features.total_bound_flips = total_bound_flips; + features.num_infeasibilities = infeasibility_indices.size(); + features.delta_y_nz_percentage = delta_y_nz_percentage; + + // Log all features on a single line + features.log_features(settings); + } + if ((iter - start_iter) < settings.first_iteration_log || (iter % settings.iteration_log_frequency) == 0) { if (phase == 1 && iter == 1) { diff --git a/cpp/src/dual_simplex/sparse_vector.cpp b/cpp/src/dual_simplex/sparse_vector.cpp index 2d47456505..b9a8a4af7b 100644 --- a/cpp/src/dual_simplex/sparse_vector.cpp +++ b/cpp/src/dual_simplex/sparse_vector.cpp @@ -13,6 +13,8 @@ namespace cuopt::linear_programming::dual_simplex { +using cuopt::ins_vector; + template sparse_vector_t::sparse_vector_t(const csc_matrix_t& A, i_t col) { @@ -28,6 +30,23 @@ sparse_vector_t::sparse_vector_t(const csc_matrix_t& A, i_t } } +template +void sparse_vector_t::from_csc_column(const csc_matrix_t& A, i_t col) +{ + const i_t col_start = A.col_start[col]; + const i_t col_end = A.col_start[col + 1]; + n = A.m; + const i_t nz = col_end - col_start; + i.clear(); + x.clear(); + i.reserve(nz); + x.reserve(nz); + for (i_t k = col_start; k < col_end; ++k) { + i.push_back(A.i[k]); + x.push_back(A.x[k]); + } +} + template void sparse_vector_t::from_dense(const std::vector& in) { @@ -53,8 +72,8 @@ void sparse_vector_t::to_csc(csc_matrix_t& A) const A.col_start.resize(2); A.col_start[0] = 0; A.col_start[1] = i.size(); - A.i = i; - A.x = x; + A.i = i.array; + A.x = x.array; } template @@ -78,16 +97,26 @@ void sparse_vector_t::scatter(std::vector& x_dense) const } } +template +void sparse_vector_t::scatter(ins_vector& x_dense) const +{ + // Assumes x_dense is already cleared + const i_t nz = i.size(); + for (i_t k = 0; k < nz; ++k) { + x_dense[i[k]] += x[k]; + } +} + template void sparse_vector_t::inverse_permute_vector(const std::vector& p) { assert(p.size() == n); i_t nz = i.size(); - std::vector i_perm(nz); + ins_vector i_perm(nz); for (i_t k = 0; k < nz; ++k) { i_perm[k] = p[i[k]]; } - i = i_perm; + i = std::move(i_perm); } template @@ -99,11 +128,11 @@ void sparse_vector_t::inverse_permute_vector(const std::vector& p i_t nz = i.size(); y.n = n; y.x = x; - std::vector i_perm(nz); + ins_vector i_perm(nz); for (i_t k = 0; k < nz; ++k) { i_perm[k] = p[i[k]]; } - y.i = i_perm; + y.i = std::move(i_perm); } template @@ -154,21 +183,22 @@ void sparse_vector_t::sort() } else { // Use a n log n sort const i_t nz = i.size(); - std::vector i_sorted(nz); - std::vector x_sorted(nz); + ins_vector i_sorted(nz); + ins_vector x_sorted(nz); std::vector perm(nz); for (i_t k = 0; k < nz; ++k) { perm[k] = k; } - std::vector& iunsorted = i; + // Need to capture the underlying array for the lambda + auto& iunsorted = i.array; std::sort( perm.begin(), perm.end(), [&iunsorted](i_t a, i_t b) { return iunsorted[a] < iunsorted[b]; }); for (i_t k = 0; k < nz; ++k) { i_sorted[k] = i[perm[k]]; x_sorted[k] = x[perm[k]]; } - i = i_sorted; - x = x_sorted; + i = std::move(i_sorted); + x = std::move(x_sorted); } // Check diff --git a/cpp/src/dual_simplex/sparse_vector.hpp b/cpp/src/dual_simplex/sparse_vector.hpp index 7acfdc8b5e..5cac6b7dd1 100644 --- a/cpp/src/dual_simplex/sparse_vector.hpp +++ b/cpp/src/dual_simplex/sparse_vector.hpp @@ -9,11 +9,15 @@ #include #include +#include #include namespace cuopt::linear_programming::dual_simplex { +// Import instrumented vector type +using cuopt::ins_vector; + // A sparse vector stored as a list of nonzero coefficients and their indices template class sparse_vector_t { @@ -34,6 +38,8 @@ class sparse_vector_t { // scatter a sparse vector into a dense vector. Assumes x_dense is already cleared or // preinitialized void scatter(std::vector& x_dense) const; + // scatter into instrumented vector + void scatter(ins_vector& x_dense) const; // inverse permute the current sparse vector void inverse_permute_vector(const std::vector& p); // inverse permute a sparse vector into another sparse vector @@ -47,9 +53,19 @@ class sparse_vector_t { void negate(); f_t find_coefficient(i_t index) const; + // Clear the sparse vector (removes all entries, keeps capacity) + void clear() + { + i.clear(); + x.clear(); + } + + // Reset from a column of a CSC matrix + void from_csc_column(const csc_matrix_t& A, i_t col); + i_t n; - std::vector i; - std::vector x; + ins_vector i; + ins_vector x; }; } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/mip/problem/problem_helpers.cuh b/cpp/src/mip/problem/problem_helpers.cuh index 660e51c69c..4f9a587646 100644 --- a/cpp/src/mip/problem/problem_helpers.cuh +++ b/cpp/src/mip/problem/problem_helpers.cuh @@ -371,6 +371,9 @@ static void csrsort_cusparse(rmm::device_uvector& values, i_t cols, const raft::handle_t* handle_ptr) { + // skip if the matrix is empty + if (values.size() == 0) { return; } + auto stream = offsets.stream(); cusparseHandle_t handle; cusparseCreate(&handle); From 1ca01ba082c41b17c83da715d3bdb04e4ae69d6f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 25 Nov 2025 16:29:58 +0000 Subject: [PATCH 084/225] dual simplex feature logging --- cpp/src/dual_simplex/barrier.cu | 2 +- cpp/src/dual_simplex/cusparse_view.cu | 6 +- .../dual_simplex/dual_simplex_features.hpp | 13 ++- cpp/src/dual_simplex/folding.cpp | 10 +-- cpp/src/dual_simplex/phase2.cpp | 32 +++++--- cpp/src/dual_simplex/singletons.cpp | 8 +- cpp/src/dual_simplex/sparse_matrix.cpp | 59 +++++++++++++- cpp/src/dual_simplex/sparse_matrix.hpp | 44 ++++++---- cpp/src/dual_simplex/sparse_vector.cpp | 11 +++ cpp/src/dual_simplex/sparse_vector.hpp | 4 +- cpp/src/utilities/memory_instrumentation.hpp | 80 +++++++++++++++---- 11 files changed, 210 insertions(+), 59 deletions(-) diff --git a/cpp/src/dual_simplex/barrier.cu b/cpp/src/dual_simplex/barrier.cu index bc630c06c5..d194b9be1b 100644 --- a/cpp/src/dual_simplex/barrier.cu +++ b/cpp/src/dual_simplex/barrier.cu @@ -1260,7 +1260,7 @@ class iteration_data_t { // v = alpha * A * w + beta * v = alpha * A * Dinv * A^T * y + beta * v matrix_vector_multiply(A, alpha, w, beta, v); if (debug) { - printf("||A|| = %.16e\n", vector_norm2(A.x)); + printf("||A|| = %.16e\n", vector_norm2(A.x.array)); printf("||w|| = %.16e\n", vector_norm2(w)); printf("||v|| = %.16e\n", vector_norm2(v)); } diff --git a/cpp/src/dual_simplex/cusparse_view.cu b/cpp/src/dual_simplex/cusparse_view.cu index b253e56243..d9bae07392 100644 --- a/cpp/src/dual_simplex/cusparse_view.cu +++ b/cpp/src/dual_simplex/cusparse_view.cu @@ -148,9 +148,9 @@ cusparse_view_t::cusparse_view_t(raft::handle_t const* handle_ptr, A_indices_ = device_copy(indices, handle_ptr->get_stream()); A_data_ = device_copy(data, handle_ptr->get_stream()); - A_T_offsets_ = device_copy(A.col_start, handle_ptr->get_stream()); - A_T_indices_ = device_copy(A.i, handle_ptr->get_stream()); - A_T_data_ = device_copy(A.x, handle_ptr->get_stream()); + A_T_offsets_ = device_copy(A.col_start.array, handle_ptr->get_stream()); + A_T_indices_ = device_copy(A.i.array, handle_ptr->get_stream()); + A_T_data_ = device_copy(A.x.array, handle_ptr->get_stream()); cusparseCreateCsr(&A_, rows, diff --git a/cpp/src/dual_simplex/dual_simplex_features.hpp b/cpp/src/dual_simplex/dual_simplex_features.hpp index f7f8807c82..9024cbad59 100644 --- a/cpp/src/dual_simplex/dual_simplex_features.hpp +++ b/cpp/src/dual_simplex/dual_simplex_features.hpp @@ -59,6 +59,9 @@ struct dual_simplex_features_t { size_t byte_loads{0}; // total bytes loaded size_t byte_stores{0}; // total bytes stored + // Runtime for the interval (in seconds) + f_t interval_runtime{0.0}; + /** * @brief Initialize static features from problem data. */ @@ -113,7 +116,7 @@ struct dual_simplex_features_t { "DS_FEATURES: iter=%d m=%d n=%d nnz=%d density=%.6e avg_nnz_col=%.2f avg_nnz_row=%.2f " "bounded=%d free=%d fixed=%d phase=%d refact_freq=%d num_refacts=%d num_updates=%d " "sparse_dz=%d dense_dz=%d bound_flips=%d num_infeas=%d dy_nz_pct=%.2f " - "byte_loads=%zu byte_stores=%zu\n", + "byte_loads=%zu byte_stores=%zu runtime=%.6f\n", iteration, num_rows, num_cols, @@ -134,7 +137,8 @@ struct dual_simplex_features_t { num_infeasibilities, delta_y_nz_percentage, byte_loads, - byte_stores); + byte_stores, + interval_runtime); } /** @@ -142,8 +146,9 @@ struct dual_simplex_features_t { */ void reset_interval_counters() { - byte_loads = 0; - byte_stores = 0; + byte_loads = 0; + byte_stores = 0; + interval_runtime = 0.0; } }; diff --git a/cpp/src/dual_simplex/folding.cpp b/cpp/src/dual_simplex/folding.cpp index 6eae5eb1ed..3f5dc41ab1 100644 --- a/cpp/src/dual_simplex/folding.cpp +++ b/cpp/src/dual_simplex/folding.cpp @@ -37,8 +37,8 @@ constexpr int8_t kInactive = 0; template void find_vertices_to_refine(const std::unordered_set& refining_color_vertices, - const std::vector& offset, - const std::vector& vertex_list, + const cuopt::ins_vector& offset, + const cuopt::ins_vector& vertex_list, const std::vector& color_map, std::vector& marked_vertices, std::vector& vertices_to_refine, @@ -75,9 +75,9 @@ template void compute_sums_of_refined_vertices(i_t refining_color, const std::unordered_set& refining_color_vertices, const std::vector& vertices_to_refine, - const std::vector& offsets, - const std::vector& vertex_list, - const std::vector& weight_list, + const cuopt::ins_vector& offsets, + const cuopt::ins_vector& vertex_list, + const cuopt::ins_vector& weight_list, const std::vector& color_map, std::vector& vertex_to_sum, std::vector& max_sum_by_color) diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 226bbe463b..58a3312b75 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -699,7 +699,7 @@ void update_primal_infeasibilities(const lp_problem_t& lp, const std::vector& x, i_t entering_index, i_t leaving_index, - std::vector& basic_change_list, + ins_vector& basic_change_list, ins_vector& squared_infeasibilities, ins_vector& infeasibility_indices, f_t& primal_inf) @@ -906,7 +906,7 @@ f_t first_stage_harris(const lp_problem_t& lp, const std::vector& vstatus, const std::vector& nonbasic_list, std::vector& z, - std::vector& delta_z) + ins_vector& delta_z) { const i_t n = lp.num_cols; const i_t m = lp.num_rows; @@ -940,7 +940,7 @@ i_t second_stage_harris(const lp_problem_t& lp, const std::vector& vstatus, const std::vector& nonbasic_list, const std::vector& z, - const std::vector& delta_z, + const ins_vector& delta_z, f_t max_step_length, f_t& step_length, i_t& nonbasic_entering) @@ -983,7 +983,7 @@ i_t phase2_ratio_test(const lp_problem_t& lp, const std::vector& vstatus, const std::vector& nonbasic_list, std::vector& z, - std::vector& delta_z, + ins_vector& delta_z, f_t& step_length, i_t& nonbasic_entering) { @@ -1056,7 +1056,7 @@ i_t flip_bounds(const lp_problem_t& lp, settings.dual_tol; // lower to 1e-7 or less will cause 25fv47 and d2q06c to cycle if (vstatus[j] == variable_status_t::NONBASIC_LOWER && z[j] < -dual_tol) { const f_t delta = lp.upper[j] - lp.lower[j]; - scatter_dense(lp.A, j, -delta, atilde.array, atilde_mark.array, atilde_index.array); + scatter_dense(lp.A, j, -delta, atilde, atilde_mark, atilde_index); delta_x_flip[j] += delta; vstatus[j] = variable_status_t::NONBASIC_UPPER; #ifdef BOUND_FLIP_DEBUG @@ -1066,7 +1066,7 @@ i_t flip_bounds(const lp_problem_t& lp, num_flipped++; } else if (vstatus[j] == variable_status_t::NONBASIC_UPPER && z[j] > dual_tol) { const f_t delta = lp.lower[j] - lp.upper[j]; - scatter_dense(lp.A, j, -delta, atilde.array, atilde_mark.array, atilde_index.array); + scatter_dense(lp.A, j, -delta, atilde, atilde_mark, atilde_index); delta_x_flip[j] += delta; vstatus[j] = variable_status_t::NONBASIC_LOWER; #ifdef BOUND_FLIP_DEBUG @@ -1565,12 +1565,12 @@ i_t compute_delta_x(const lp_problem_t& lp, i_t basic_leaving_index, i_t direction, const std::vector& basic_list, - const std::vector& delta_x_flip, + const ins_vector& delta_x_flip, const sparse_vector_t& rhs_sparse, const std::vector& x, sparse_vector_t& utilde_sparse, sparse_vector_t& scaled_delta_xB_sparse, - std::vector& delta_x) + ins_vector& delta_x) { f_t delta_x_leaving = direction == 1 ? lp.lower[leaving_index] - x[leaving_index] : lp.upper[leaving_index] - x[leaving_index]; @@ -1644,7 +1644,7 @@ void update_primal_variables(const sparse_vector_t& scaled_delta_xB_sp template void update_objective(const std::vector& basic_list, - const std::vector& changed_basic_indices, + const ins_vector& changed_basic_indices, const std::vector& objective, const ins_vector& delta_x, i_t entering_index, @@ -2442,6 +2442,14 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, manifold.add("atilde_sparse.i", atilde_sparse.i); manifold.add("atilde_sparse.x", atilde_sparse.x); + // Add A_transpose matrix arrays to manifold for memory tracking + manifold.add("A_transpose.col_start", A_transpose.col_start); + manifold.add("A_transpose.i", A_transpose.i); + manifold.add("A_transpose.x", A_transpose.x); + + // Track iteration interval start time for runtime measurement + f_t interval_start_time = toc(start_time); + // Note: Features are logged inline with DS_FEATURES: prefix while (iter < iter_limit) { @@ -2539,7 +2547,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } else { dense_delta_z++; // delta_zB = sigma*ei - delta_y_sparse.to_dense(delta_y.array); + delta_y_sparse.to_dense(delta_y); phase2::compute_reduced_cost_update(lp, basic_list, nonbasic_list, @@ -3040,6 +3048,10 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, features.byte_loads = total_loads; features.byte_stores = total_stores; + // Compute interval runtime + features.interval_runtime = now - interval_start_time; + interval_start_time = now; + // Update dynamic features features.iteration = iter; features.num_refactors = num_refactors; diff --git a/cpp/src/dual_simplex/singletons.cpp b/cpp/src/dual_simplex/singletons.cpp index ff6bcbac25..c4656889b9 100644 --- a/cpp/src/dual_simplex/singletons.cpp +++ b/cpp/src/dual_simplex/singletons.cpp @@ -200,8 +200,8 @@ i_t find_singletons(const csc_matrix_t& A, // Find column singletons row_col_graph_t graph{Cdeg.begin(), col_perm.begin(), - A.col_start.cbegin(), - A.i.cbegin(), + A.col_start.array.cbegin(), + A.i.array.cbegin(), Rdeg.begin(), row_perm.begin(), Rp.cbegin(), @@ -235,8 +235,8 @@ i_t find_singletons(const csc_matrix_t& A, Rj.cbegin(), Cdeg.begin(), col_perm.begin(), - A.col_start.cbegin(), - A.i.cbegin()}; + A.col_start.array.cbegin(), + A.i.array.cbegin()}; #ifdef SINGLETON_DEBUG printf("Searching for row singletons %ld\n", singleton_queue.size()); #endif diff --git a/cpp/src/dual_simplex/sparse_matrix.cpp b/cpp/src/dual_simplex/sparse_matrix.cpp index 4e522d9daf..81bbbda117 100644 --- a/cpp/src/dual_simplex/sparse_matrix.cpp +++ b/cpp/src/dual_simplex/sparse_matrix.cpp @@ -8,9 +8,12 @@ // #include #include #include +#include #include +using cuopt::ins_vector; + // #include // #include @@ -34,8 +37,8 @@ void csc_matrix_t::reallocate(i_t new_nz) this->nz_max = new_nz; } -template -void cumulative_sum(std::vector& inout, std::vector& output) +template +void cumulative_sum(std::vector& inout, OutputVector& output) { i_t n = inout.size(); assert(output.size() == n + 1); @@ -603,6 +606,41 @@ void scatter_dense(const csc_matrix_t& A, } } +// Instrumented vector overload: x <- x + alpha * A(:, j) +template +void scatter_dense(const csc_matrix_t& A, i_t j, f_t alpha, ins_vector& x) +{ + const i_t col_start = A.col_start[j]; + const i_t col_end = A.col_start[j + 1]; + for (i_t p = col_start; p < col_end; ++p) { + const i_t i = A.i[p]; + const f_t ax = A.x[p]; + x[i] += alpha * ax; + } +} + +// Instrumented vector overload: x <- x + alpha * A(:, j) with mark/indices tracking +template +void scatter_dense(const csc_matrix_t& A, + i_t j, + f_t alpha, + ins_vector& x, + ins_vector& mark, + ins_vector& indices) +{ + const i_t col_start = A.col_start[j]; + const i_t col_end = A.col_start[j + 1]; + for (i_t p = col_start; p < col_end; ++p) { + const i_t i = A.i[p]; + const f_t ax = A.x[p]; + x[i] += alpha * ax; + if (!mark[i]) { + mark[i] = 1; + indices.push_back(i); + } + } +} + // Compute C = A*B where C is m x n, A is m x k, and B = k x n // Do this by computing C(:, j) = A*B(:, j) = sum (i=1 to k) A(:, k)*B(i, j) template @@ -848,7 +886,10 @@ template class csc_matrix_t; template class csr_matrix_t; -template void cumulative_sum(std::vector& inout, std::vector& output); +template void cumulative_sum>(std::vector& inout, + std::vector& output); +template void cumulative_sum>(std::vector& inout, + ins_vector& output); template int coo_to_csc(const std::vector& Ai, const std::vector& Aj, @@ -876,6 +917,18 @@ template void scatter_dense(const csc_matrix_t& A, std::vector& mark, std::vector& indices); +template void scatter_dense(const csc_matrix_t& A, + int j, + double alpha, + ins_vector& x); + +template void scatter_dense(const csc_matrix_t& A, + int j, + double alpha, + ins_vector& x, + ins_vector& mark, + ins_vector& indices); + template int multiply(const csc_matrix_t& A, const csc_matrix_t& B, csc_matrix_t& C); diff --git a/cpp/src/dual_simplex/sparse_matrix.hpp b/cpp/src/dual_simplex/sparse_matrix.hpp index c14e6d0f14..26f1bce979 100644 --- a/cpp/src/dual_simplex/sparse_matrix.hpp +++ b/cpp/src/dual_simplex/sparse_matrix.hpp @@ -9,12 +9,16 @@ #include #include +#include #include #include #include #include +// Import instrumented vector +using cuopt::ins_vector; + namespace cuopt::linear_programming::dual_simplex { template @@ -110,12 +114,12 @@ class csc_matrix_t { size_t hash() const; - i_t m; // number of rows - i_t n; // number of columns - i_t nz_max; // maximum number of entries - std::vector col_start; // column pointers (size n + 1) - std::vector i; // row indices, size nz_max - std::vector x; // numerical values, size nz_max + i_t m; // number of rows + i_t n; // number of columns + i_t nz_max; // maximum number of entries + ins_vector col_start; // column pointers (size n + 1) + ins_vector i; // row indices, size nz_max + ins_vector x; // numerical values, size nz_max static_assert(std::is_signed_v); // Require signed integers (we make use of this // to avoid extra space / computation) @@ -139,18 +143,18 @@ class csr_matrix_t { // Ensures no repeated column indices within a row void check_matrix() const; - i_t nz_max; // maximum number of nonzero entries - i_t m; // number of rows - i_t n; // number of cols - std::vector row_start; // row pointers (size m + 1) - std::vector j; // column inidices, size nz_max - std::vector x; // numerical valuse, size nz_max + i_t nz_max; // maximum number of nonzero entries + i_t m; // number of rows + i_t n; // number of cols + ins_vector row_start; // row pointers (size m + 1) + ins_vector j; // column inidices, size nz_max + ins_vector x; // numerical valuse, size nz_max static_assert(std::is_signed_v); }; -template -void cumulative_sum(std::vector& inout, std::vector& output); +template +void cumulative_sum(std::vector& inout, OutputVector& output); template i_t coo_to_csc(const std::vector& Ai, @@ -180,6 +184,18 @@ void scatter_dense(const csc_matrix_t& A, std::vector& mark, std::vector& indices); +// Instrumented vector overloads +template +void scatter_dense(const csc_matrix_t& A, i_t j, f_t alpha, ins_vector& x); + +template +void scatter_dense(const csc_matrix_t& A, + i_t j, + f_t alpha, + ins_vector& x, + ins_vector& mark, + ins_vector& indices); + // Compute C = A*B where C is m x n, A is m x k, and B = k x n // Do this by computing C(:, j) = A*B(:, j) = sum (i=1 to k) A(:, k)*B(i, j) template diff --git a/cpp/src/dual_simplex/sparse_vector.cpp b/cpp/src/dual_simplex/sparse_vector.cpp index b9a8a4af7b..111d3ce829 100644 --- a/cpp/src/dual_simplex/sparse_vector.cpp +++ b/cpp/src/dual_simplex/sparse_vector.cpp @@ -87,6 +87,17 @@ void sparse_vector_t::to_dense(std::vector& x_dense) const } } +template +void sparse_vector_t::to_dense(ins_vector& x_dense) const +{ + x_dense.clear(); + x_dense.resize(n, 0.0); + const i_t nz = i.size(); + for (i_t k = 0; k < nz; ++k) { + x_dense[i[k]] = x[k]; + } +} + template void sparse_vector_t::scatter(std::vector& x_dense) const { diff --git a/cpp/src/dual_simplex/sparse_vector.hpp b/cpp/src/dual_simplex/sparse_vector.hpp index 5cac6b7dd1..f3d6769ab4 100644 --- a/cpp/src/dual_simplex/sparse_vector.hpp +++ b/cpp/src/dual_simplex/sparse_vector.hpp @@ -22,7 +22,7 @@ using cuopt::ins_vector; template class sparse_vector_t { public: - sparse_vector_t() : n(0), i({}), x({}) {} + sparse_vector_t() : n(0), i(), x() {} // Construct a sparse vector of dimension n with nz nonzero coefficients sparse_vector_t(i_t n, i_t nz) : n(n), i(nz), x(nz) {} // Construct a sparse vector from a dense vector. @@ -35,6 +35,8 @@ class sparse_vector_t { void to_csc(csc_matrix_t& A) const; // convert a sparse vector into a dense vector. Dense vector is cleared and resized. void to_dense(std::vector& x_dense) const; + // convert a sparse vector into an instrumented dense vector. + void to_dense(ins_vector& x_dense) const; // scatter a sparse vector into a dense vector. Assumes x_dense is already cleared or // preinitialized void scatter(std::vector& x_dense) const; diff --git a/cpp/src/utilities/memory_instrumentation.hpp b/cpp/src/utilities/memory_instrumentation.hpp index 55cce572d6..52c07be3db 100644 --- a/cpp/src/utilities/memory_instrumentation.hpp +++ b/cpp/src/utilities/memory_instrumentation.hpp @@ -37,6 +37,12 @@ #define CUOPT_ENABLE_MEMORY_INSTRUMENTATION 1 +#ifdef __NVCC__ +#define HDI inline __host__ __device__ +#else +#define HDI inline +#endif + namespace cuopt { // Define CUOPT_ENABLE_MEMORY_INSTRUMENTATION to enable memory tracking @@ -45,22 +51,22 @@ namespace cuopt { // Base class for memory operation instrumentation struct memory_instrumentation_base_t { #ifdef CUOPT_ENABLE_MEMORY_INSTRUMENTATION - __host__ __device__ void reset_counters() const { byte_loads = byte_stores = 0; } + HDI void reset_counters() const { byte_loads = byte_stores = 0; } template - __host__ __device__ void record_load() const + HDI void record_load() const { byte_loads += sizeof(T); } template - __host__ __device__ void record_store() const + HDI void record_store() const { byte_stores += sizeof(T); } template - __host__ __device__ void record_rmw() const + HDI void record_rmw() const { byte_loads += sizeof(T); byte_stores += sizeof(T); @@ -70,17 +76,17 @@ struct memory_instrumentation_base_t { mutable size_t byte_stores{0}; #else // No-op methods when instrumentation is disabled - these inline away to zero overhead - __host__ __device__ void reset_counters() const {} + HDI void reset_counters() const {} template - __host__ __device__ void record_load() const + HDI void record_load() const { } template - __host__ __device__ void record_store() const + HDI void record_store() const { } template - __host__ __device__ void record_rmw() const + HDI void record_rmw() const { } #endif // CUOPT_ENABLE_MEMORY_INSTRUMENTATION @@ -530,15 +536,61 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { } } - // Copy/move from wrapper - memop_instrumentation_wrapper_t(const memop_instrumentation_wrapper_t&) = default; - memop_instrumentation_wrapper_t(memop_instrumentation_wrapper_t&&) = default; - memop_instrumentation_wrapper_t& operator=(const memop_instrumentation_wrapper_t&) = default; - memop_instrumentation_wrapper_t& operator=(memop_instrumentation_wrapper_t&&) = default; + // Copy constructor - must update data_ptr to point to our own array + memop_instrumentation_wrapper_t(const memop_instrumentation_wrapper_t& other) + : memory_instrumentation_base_t(other), array(other.array) + { + if constexpr (type_traits_utils::has_data::value) { + data_ptr = array.data(); + } else { + data_ptr = nullptr; + } + } + + // Move constructor - must update data_ptr to point to our own array + memop_instrumentation_wrapper_t(memop_instrumentation_wrapper_t&& other) noexcept + : memory_instrumentation_base_t(std::move(other)), array(std::move(other.array)) + { + if constexpr (type_traits_utils::has_data::value) { + data_ptr = array.data(); + } else { + data_ptr = nullptr; + } + } + + // Copy assignment - must update data_ptr to point to our own array + memop_instrumentation_wrapper_t& operator=(const memop_instrumentation_wrapper_t& other) + { + if (this != &other) { + memory_instrumentation_base_t::operator=(other); + array = other.array; + if constexpr (type_traits_utils::has_data::value) { + data_ptr = array.data(); + } else { + data_ptr = nullptr; + } + } + return *this; + } + + // Move assignment - must update data_ptr to point to our own array + memop_instrumentation_wrapper_t& operator=(memop_instrumentation_wrapper_t&& other) noexcept + { + if (this != &other) { + memory_instrumentation_base_t::operator=(std::move(other)); + array = std::move(other.array); + if constexpr (type_traits_utils::has_data::value) { + data_ptr = array.data(); + } else { + data_ptr = nullptr; + } + } + return *this; + } element_proxy_t operator[](size_type index) { return element_proxy_t(array[index], *this); } - __host__ __device__ value_type operator[](size_type index) const + HDI value_type operator[](size_type index) const { this->template record_load(); // really ugly hack because otherwise nvcc complains about vector operator[] being __host__ only From 5894ffe031dbe0e19fc5a2c38fe04f5d44a00ea8 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 18 Nov 2025 11:18:49 +0000 Subject: [PATCH 085/225] add support for building with clang --- build.sh | 16 ++++++++++++++-- ci/tsan_suppressions.txt | 6 ++++++ cpp/CMakeLists.txt | 19 ++++++++++++++++++- cpp/include/cuopt/error.hpp | 2 -- .../utilities/internals.hpp | 1 + cpp/src/linear_programming/pdlp.cu | 2 +- .../utilities/cython_solve.cu | 3 +-- cpp/src/linear_programming/utils.cuh | 6 +++--- cpp/src/mip/diversity/lns/rins.cu | 4 ++-- cpp/src/mip/local_search/local_search.cu | 3 +-- cpp/src/mip/presolve/gf2_presolve.hpp | 4 ++++ cpp/src/mip/presolve/third_party_presolve.cpp | 4 ++++ cpp/src/mip/utilities/cpu_worker_thread.cuh | 8 +++++--- cpp/src/routing/crossovers/ox_recombiner.cuh | 4 ++-- .../distance_engine/waypoint_matrix_test.cpp | 6 +++--- .../c_api_tests/c_api_test.c | 1 + cpp/tests/routing/level0/l0_ges_test.cu | 4 ++-- .../level0/l0_objective_function_test.cu | 2 +- cpp/tests/routing/level0/l0_routing_test.cu | 2 +- .../routing/level0/l0_vehicle_order_match.cu | 2 +- .../routing/level0/l0_vehicle_types_test.cu | 2 +- 21 files changed, 72 insertions(+), 29 deletions(-) create mode 100644 ci/tsan_suppressions.txt diff --git a/build.sh b/build.sh index 2903684f6f..e040baa958 100755 --- a/build.sh +++ b/build.sh @@ -15,7 +15,7 @@ REPODIR=$(cd "$(dirname "$0")"; pwd) LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build} LIBMPS_PARSER_BUILD_DIR=${LIBMPS_PARSER_BUILD_DIR:=${REPODIR}/cpp/libmps_parser/build} -VALIDARGS="clean libcuopt libmps_parser cuopt_mps_parser cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" +VALIDARGS="clean libcuopt libmps_parser cuopt_mps_parser cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" HELP="$0 [ ...] [ ...] where is: clean - remove all existing build artifacts and configuration (start over) @@ -32,7 +32,8 @@ HELP="$0 [ ...] [ ...] -g - build for debug -a - Enable assertion (by default in debug mode) -b - Build with benchmark settings - -fsanitize - Build with sanitizer + -fsanitize - Build with AddressSanitizer and UndefinedBehaviorSanitizer + -tsan - Build with ThreadSanitizer (cannot be used with -fsanitize) -n - no install step --no-fetch-rapids - don't fetch rapids dependencies -l= - log level. Options are: TRACE | DEBUG | INFO | WARN | ERROR | CRITICAL | OFF. Default=INFO @@ -76,6 +77,7 @@ BUILD_ALL_GPU_ARCH=0 BUILD_CI_ONLY=0 BUILD_LP_ONLY=0 BUILD_SANITIZER=0 +BUILD_TSAN=0 SKIP_C_PYTHON_ADAPTERS=0 SKIP_TESTS_BUILD=0 SKIP_ROUTING_BUILD=0 @@ -230,6 +232,9 @@ fi if hasArg -fsanitize; then BUILD_SANITIZER=1 fi +if hasArg -tsan; then + BUILD_TSAN=1 +fi if hasArg --skip-c-python-adapters; then SKIP_C_PYTHON_ADAPTERS=1 fi @@ -298,6 +303,12 @@ if [ ${BUILD_LP_ONLY} -eq 1 ] && [ ${SKIP_C_PYTHON_ADAPTERS} -eq 0 ]; then exit 1 fi +if [ ${BUILD_SANITIZER} -eq 1 ] && [ ${BUILD_TSAN} -eq 1 ]; then + echo "ERROR: -fsanitize and -tsan cannot be used together" + echo "AddressSanitizer and ThreadSanitizer are mutually exclusive" + exit 1 +fi + if [ ${BUILD_ALL_GPU_ARCH} -eq 1 ]; then CUOPT_CMAKE_CUDA_ARCHITECTURES="RAPIDS" echo "Building for *ALL* supported GPU architectures..." @@ -344,6 +355,7 @@ if buildAll || hasArg libcuopt; then -DFETCH_RAPIDS=${FETCH_RAPIDS} \ -DBUILD_LP_ONLY=${BUILD_LP_ONLY} \ -DBUILD_SANITIZER=${BUILD_SANITIZER} \ + -DBUILD_TSAN=${BUILD_TSAN} \ -DSKIP_C_PYTHON_ADAPTERS=${SKIP_C_PYTHON_ADAPTERS} \ -DBUILD_TESTS=$((1 - ${SKIP_TESTS_BUILD})) \ -DSKIP_ROUTING_BUILD=${SKIP_ROUTING_BUILD} \ diff --git a/ci/tsan_suppressions.txt b/ci/tsan_suppressions.txt new file mode 100644 index 0000000000..b6f413e370 --- /dev/null +++ b/ci/tsan_suppressions.txt @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. +# SPDX-License-Identifier: Apache-2.0 + +# Ignore races in external header-only libraries +race:tbb +race:Papilo diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 4391c53df7..f770d80c81 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -81,9 +81,22 @@ endif(CMAKE_COMPILER_IS_GNUCXX) # 2. (Optional) To run with a debugger (gdb or cuda-gdb) use the additional ASAN option alloc_dealloc_mismatch=0 if(BUILD_SANITIZER) list(APPEND CUOPT_CXX_FLAGS -fsanitize=address,undefined -fno-omit-frame-pointer -g -Wno-error=maybe-uninitialized) + if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + list(APPEND CUOPT_CXX_FLAGS -Wno-error=maybe-uninitialized) + endif() add_link_options(-fsanitize=address,undefined) endif(BUILD_SANITIZER) +# To use ThreadSanitizer: +# 1. Build with clang and the -tsan flag +# 2. Run the binary with env var set: OMP_TOOL_LIBRARIES=/usr/lib/llvm-17/lib/libarcher.so ARCHER_OPTIONS='verbose=1' TSAN_OPTIONS='suppresions=ci/tsan_suppressions.txt:ignore_noninstrumented_modules=1:halt_on_error=1' +# Replace with local llvm install path. libarcher.so must be presetn +if(BUILD_TSAN) + message(STATUS "Building with ThreadSanitizer enabled") + list(APPEND CUOPT_CXX_FLAGS -fsanitize=thread -fno-omit-frame-pointer -g) + add_link_options(-fsanitize=thread) +endif(BUILD_TSAN) + if(DEFINE_ASSERT) add_definitions(-DASSERT_MODE) endif(DEFINE_ASSERT) @@ -117,7 +130,11 @@ if(${CMAKE_CUDA_COMPILER_VERSION} VERSION_GREATER_EQUAL 12.9) set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -static-global-template-stub=false") endif() list(APPEND CUOPT_CUDA_FLAGS -Werror=cross-execution-space-call -Wno-deprecated-declarations -Xcompiler=-Werror --default-stream=per-thread) -list(APPEND CUOPT_CUDA_FLAGS -Xcompiler=-Wall -Wno-error=non-template-friend) +if("${CMAKE_CUDA_HOST_COMPILER}" MATCHES "clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + list(APPEND CUOPT_CUDA_FLAGS -Xcompiler=-Wall) +else() + list(APPEND CUOPT_CUDA_FLAGS -Xcompiler=-Wall -Wno-error=non-template-friend) +endif() list(APPEND CUOPT_CUDA_FLAGS -Xfatbin=-compress-all) if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 12.9 AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 13.0) list(APPEND CUOPT_CUDA_FLAGS -Xfatbin=--compress-level=3) diff --git a/cpp/include/cuopt/error.hpp b/cpp/include/cuopt/error.hpp index b6086245db..a83413515e 100644 --- a/cpp/include/cuopt/error.hpp +++ b/cpp/include/cuopt/error.hpp @@ -33,8 +33,6 @@ enum class error_type_t { */ struct logic_error : public std::logic_error { - explicit logic_error() = default; - logic_error(const logic_error& exception) = default; // Move constructor diff --git a/cpp/include/cuopt/linear_programming/utilities/internals.hpp b/cpp/include/cuopt/linear_programming/utilities/internals.hpp index 84c96a7164..86e7246faf 100644 --- a/cpp/include/cuopt/linear_programming/utilities/internals.hpp +++ b/cpp/include/cuopt/linear_programming/utilities/internals.hpp @@ -62,6 +62,7 @@ namespace linear_programming { class base_solution_t { public: + virtual ~base_solution_t() = default; virtual bool is_mip() const = 0; }; diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index 5d982bcc96..d78f1d1f48 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -1510,7 +1510,7 @@ void pdlp_solver_t::compute_initial_step_size() const auto& cusparse_view_ = pdhg_solver_.get_cusparse_view(); - int sing_iters = 0; + [[maybe_unused]] int sing_iters = 0; for (int i = 0; i < max_iterations; ++i) { ++sing_iters; // d_q = d_z diff --git a/cpp/src/linear_programming/utilities/cython_solve.cu b/cpp/src/linear_programming/utilities/cython_solve.cu index 111f3caf04..38968c0508 100644 --- a/cpp/src/linear_programming/utilities/cython_solve.cu +++ b/cpp/src/linear_programming/utilities/cython_solve.cu @@ -295,8 +295,7 @@ std::pair>, double> call_batch_solve( #pragma omp parallel for num_threads(max_thread) for (std::size_t i = 0; i < size; ++i) - list[i] = - std::move(call_solve(data_models[i], solver_settings, cudaStreamNonBlocking, is_batch_mode)); + list[i] = call_solve(data_models[i], solver_settings, cudaStreamNonBlocking, is_batch_mode); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start_solver); diff --git a/cpp/src/linear_programming/utils.cuh b/cpp/src/linear_programming/utils.cuh index 0da5d25ceb..2333283f39 100644 --- a/cpp/src/linear_programming/utils.cuh +++ b/cpp/src/linear_programming/utils.cuh @@ -62,9 +62,9 @@ struct max_abs_value { template i_t conditional_major(uint64_t total_pdlp_iterations) { - uint64_t step = 10; - uint64_t threshold = 1000; - uint64_t iteration = 0; + uint64_t step = 10; + uint64_t threshold = 1000; + [[maybe_unused]] uint64_t iteration = 0; [[maybe_unused]] constexpr uint64_t max_u64 = std::numeric_limits::max(); diff --git a/cpp/src/mip/diversity/lns/rins.cu b/cpp/src/mip/diversity/lns/rins.cu index 1a79bdb053..0d2da90cde 100644 --- a/cpp/src/mip/diversity/lns/rins.cu +++ b/cpp/src/mip/diversity/lns/rins.cu @@ -246,8 +246,8 @@ void rins_t::run_rins() branch_and_bound_settings.num_diving_threads = 1; branch_and_bound_settings.log.log = false; branch_and_bound_settings.log.log_prefix = "[RINS] "; - branch_and_bound_settings.solution_callback = [this, &rins_solution_queue]( - std::vector& solution, f_t objective) { + branch_and_bound_settings.solution_callback = [&rins_solution_queue](std::vector& solution, + f_t objective) { rins_solution_queue.push_back(solution); }; dual_simplex::branch_and_bound_t branch_and_bound(branch_and_bound_problem, diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index a8e06440aa..6d64f62ab1 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -80,8 +80,7 @@ void local_search_t::start_cpufj_scratch_threads(population_t 0); cpu_fj.fj_cpu->log_prefix = "******* scratch " + std::to_string(counter) + ": "; - cpu_fj.fj_cpu->improvement_callback = [this, &population, &cpu_fj]( - f_t obj, const std::vector& h_vec) { + cpu_fj.fj_cpu->improvement_callback = [&population](f_t obj, const std::vector& h_vec) { population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); if (obj < local_search_best_obj) { CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", diff --git a/cpp/src/mip/presolve/gf2_presolve.hpp b/cpp/src/mip/presolve/gf2_presolve.hpp index 19d4e7d813..53e79d2c93 100644 --- a/cpp/src/mip/presolve/gf2_presolve.hpp +++ b/cpp/src/mip/presolve/gf2_presolve.hpp @@ -7,13 +7,17 @@ #pragma once +#if !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overflow" // ignore boost error for pip wheel build +#endif #include #include #include #include +#if !defined(__clang__) #pragma GCC diagnostic pop +#endif namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 22827c6e28..f3faf3dd7d 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -14,11 +14,15 @@ #include +#if !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overflow" // ignore boost error for pip wheel build +#endif #include #include +#if !defined(__clang__) #pragma GCC diagnostic pop +#endif namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip/utilities/cpu_worker_thread.cuh b/cpp/src/mip/utilities/cpu_worker_thread.cuh index 0f1671c94a..8d0b6d71ee 100644 --- a/cpp/src/mip/utilities/cpu_worker_thread.cuh +++ b/cpp/src/mip/utilities/cpu_worker_thread.cuh @@ -60,6 +60,7 @@ template cpu_worker_thread_base_t::~cpu_worker_thread_base_t() { // Note: We don't call on_terminate() here since the derived object is already destroyed. + CUOPT_LOG_DEBUG("Destroying CPU worker thread"); join_worker(); } @@ -83,12 +84,14 @@ void cpu_worker_thread_base_t::cpu_worker_thread() std::lock_guard lock(cpu_mutex); cpu_thread_done = true; } + cpu_cv.notify_all(); } } template void cpu_worker_thread_base_t::request_termination() { + CUOPT_LOG_DEBUG("Requesting termination of CPU worker thread"); bool should_terminate = false; { std::lock_guard lock(cpu_mutex); @@ -131,9 +134,8 @@ void cpu_worker_thread_base_t::start_cpu_solver() template bool cpu_worker_thread_base_t::wait_for_cpu_solver() { - while (!cpu_thread_done && !cpu_thread_terminate) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + std::unique_lock lock(cpu_mutex); + cpu_cv.wait(lock, [this] { return cpu_thread_done || cpu_thread_terminate; }); return static_cast(this)->get_result(); } diff --git a/cpp/src/routing/crossovers/ox_recombiner.cuh b/cpp/src/routing/crossovers/ox_recombiner.cuh index 17823c28b8..7f965de2ff 100644 --- a/cpp/src/routing/crossovers/ox_recombiner.cuh +++ b/cpp/src/routing/crossovers/ox_recombiner.cuh @@ -336,7 +336,7 @@ struct OX { int i = routes_number; if (optimal_routes_search) { i = optimal_routes_number; } int end_index = offspring.size() - 1; - double cost_n, cost_p, total_delta = 0.; + [[maybe_unused]] double cost_n, cost_p, total_delta = 0.; std::vector>>> routes_to_add; std::vector tmp_route; @@ -530,7 +530,7 @@ struct OX { "Mismatch number of edges"); for (size_t j = 0; j < h_transpose_graph[i].size(); ++j) { auto [ref_edge, ref_weight, ref_veh] = h_transpose_graph[i][j]; - bool found = false; + [[maybe_unused]] bool found = false; for (int x = 0; x < tmp_transpose.row_sizes[i]; ++x) { auto edge = tmp_transpose.indices[transpose_offset + x]; auto veh = tmp_transpose.buckets[transpose_offset + x]; diff --git a/cpp/tests/distance_engine/waypoint_matrix_test.cpp b/cpp/tests/distance_engine/waypoint_matrix_test.cpp index 80288bc6fa..1f9bacf0ec 100644 --- a/cpp/tests/distance_engine/waypoint_matrix_test.cpp +++ b/cpp/tests/distance_engine/waypoint_matrix_test.cpp @@ -44,7 +44,7 @@ class waypoint_matrix_waypoints_sequence_test_t this->expected_sequence_offsets = param.sequence_offsets; } - void TearDown() {} + void TearDown() override {} void test_compute_waypoint_sequence() { @@ -131,7 +131,7 @@ class waypoint_matrix_shortest_path_cost_t this->weights.data()); } - void TearDown() {} + void TearDown() override {} void test_compute_shortest_path_costs() { @@ -192,7 +192,7 @@ class waypoint_matrix_cost_matrix_test_t this->weights.data()); } - void TearDown() {} + void TearDown() override {} void test_compute_cost_matrix() { diff --git a/cpp/tests/linear_programming/c_api_tests/c_api_test.c b/cpp/tests/linear_programming/c_api_tests/c_api_test.c index 25aef6d258..3b3032176b 100644 --- a/cpp/tests/linear_programming/c_api_tests/c_api_test.c +++ b/cpp/tests/linear_programming/c_api_tests/c_api_test.c @@ -53,6 +53,7 @@ const char* termination_status_to_string(cuopt_int_t termination_status) case CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND: return "Feasible found"; } + return "Unknown"; } diff --git a/cpp/tests/routing/level0/l0_ges_test.cu b/cpp/tests/routing/level0/l0_ges_test.cu index 22373f7045..afd0a26276 100644 --- a/cpp/tests/routing/level0/l0_ges_test.cu +++ b/cpp/tests/routing/level0/l0_ges_test.cu @@ -55,7 +55,7 @@ class routing_ges_test_t : public ::testing::TestWithParam>, this->populate_device_vectors(); } - void TearDown() {} + void TearDown() override {} assignment_t solve(const cuopt::routing::data_model_view_t& data_model, const cuopt::routing::solver_settings_t& solver_settings, @@ -163,7 +163,7 @@ class simple_routes_ges_test_t : public ::testing::TestWithParampopulate_device_vectors(); } - void TearDown() {} + void TearDown() override {} assignment_t solve(const cuopt::routing::data_model_view_t& data_model, const cuopt::routing::solver_settings_t& solver_settings, diff --git a/cpp/tests/routing/level0/l0_objective_function_test.cu b/cpp/tests/routing/level0/l0_objective_function_test.cu index 9355750269..4491398497 100644 --- a/cpp/tests/routing/level0/l0_objective_function_test.cu +++ b/cpp/tests/routing/level0/l0_objective_function_test.cu @@ -25,7 +25,7 @@ template class objective_function_test_t : public base_test_t, public ::testing::TestWithParam> { public: - objective_function_test_t() : base_test_t(512, 5E-2, 0) {} + objective_function_test_t() : base_test_t(512, 0, 0) {} void SetUp() override { auto p = GetParam(); diff --git a/cpp/tests/routing/level0/l0_routing_test.cu b/cpp/tests/routing/level0/l0_routing_test.cu index 4d7bbad024..735b6e4bc1 100644 --- a/cpp/tests/routing/level0/l0_routing_test.cu +++ b/cpp/tests/routing/level0/l0_routing_test.cu @@ -320,7 +320,7 @@ class routing_retail_test_t : public base_test_t, this->populate_device_vectors(); } - void TearDown() {} + void TearDown() override {} void test_cvrptw() { diff --git a/cpp/tests/routing/level0/l0_vehicle_order_match.cu b/cpp/tests/routing/level0/l0_vehicle_order_match.cu index 6c4d40ab79..4b1b9fdd37 100644 --- a/cpp/tests/routing/level0/l0_vehicle_order_match.cu +++ b/cpp/tests/routing/level0/l0_vehicle_order_match.cu @@ -24,7 +24,7 @@ namespace test { template class vehicle_order_test_t : public base_test_t, public ::testing::TestWithParam { public: - vehicle_order_test_t() : base_test_t(512, 5E-2, 0) {} + vehicle_order_test_t() : base_test_t(512, 0, 0) {} void SetUp() override { this->not_matching_constraints_fraction = GetParam(); diff --git a/cpp/tests/routing/level0/l0_vehicle_types_test.cu b/cpp/tests/routing/level0/l0_vehicle_types_test.cu index f7f2476836..4e46d31d69 100644 --- a/cpp/tests/routing/level0/l0_vehicle_types_test.cu +++ b/cpp/tests/routing/level0/l0_vehicle_types_test.cu @@ -23,7 +23,7 @@ namespace test { template class vehicle_types_test_t : public base_test_t, public ::testing::Test { public: - vehicle_types_test_t() : base_test_t(512, 5E-2, 0) {} + vehicle_types_test_t() : base_test_t(512, 0, 0) {} void SetUp() override { this->n_locations = input_.n_locations; From 9911c3ab654ea0b0ee2955c79ee5d1f237ccdfb6 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 18 Nov 2025 12:34:27 +0000 Subject: [PATCH 086/225] remove debug calls --- cpp/src/mip/utilities/cpu_worker_thread.cuh | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/src/mip/utilities/cpu_worker_thread.cuh b/cpp/src/mip/utilities/cpu_worker_thread.cuh index 8d0b6d71ee..e437486cda 100644 --- a/cpp/src/mip/utilities/cpu_worker_thread.cuh +++ b/cpp/src/mip/utilities/cpu_worker_thread.cuh @@ -60,7 +60,6 @@ template cpu_worker_thread_base_t::~cpu_worker_thread_base_t() { // Note: We don't call on_terminate() here since the derived object is already destroyed. - CUOPT_LOG_DEBUG("Destroying CPU worker thread"); join_worker(); } @@ -91,7 +90,6 @@ void cpu_worker_thread_base_t::cpu_worker_thread() template void cpu_worker_thread_base_t::request_termination() { - CUOPT_LOG_DEBUG("Requesting termination of CPU worker thread"); bool should_terminate = false; { std::lock_guard lock(cpu_mutex); From 24b828e53aabe7fe6016ac197019fbdee848d5d9 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 18 Nov 2025 14:02:20 +0000 Subject: [PATCH 087/225] fix cmakelists --- cpp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index f770d80c81..bcd62598cd 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -80,7 +80,7 @@ endif(CMAKE_COMPILER_IS_GNUCXX) # 1. Run the binary with env var set: LD_PRELOAD="$(gcc -print-file-name=libasan.so)" ASAN_OPTIONS='protect_shadow_gap=0:replace_intrin=0' # 2. (Optional) To run with a debugger (gdb or cuda-gdb) use the additional ASAN option alloc_dealloc_mismatch=0 if(BUILD_SANITIZER) - list(APPEND CUOPT_CXX_FLAGS -fsanitize=address,undefined -fno-omit-frame-pointer -g -Wno-error=maybe-uninitialized) + list(APPEND CUOPT_CXX_FLAGS -fsanitize=address,undefined -fno-omit-frame-pointer -g) if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") list(APPEND CUOPT_CXX_FLAGS -Wno-error=maybe-uninitialized) endif() From d52bcb0079a45af830621a6efe591022a80054d4 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 25 Nov 2025 16:46:38 +0000 Subject: [PATCH 088/225] move suppressiosn --- cpp/CMakeLists.txt | 2 +- {ci => cpp}/tsan_suppressions.txt | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {ci => cpp}/tsan_suppressions.txt (100%) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index bcd62598cd..b4713a8487 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -89,7 +89,7 @@ endif(BUILD_SANITIZER) # To use ThreadSanitizer: # 1. Build with clang and the -tsan flag -# 2. Run the binary with env var set: OMP_TOOL_LIBRARIES=/usr/lib/llvm-17/lib/libarcher.so ARCHER_OPTIONS='verbose=1' TSAN_OPTIONS='suppresions=ci/tsan_suppressions.txt:ignore_noninstrumented_modules=1:halt_on_error=1' +# 2. Run the binary with env var set: OMP_TOOL_LIBRARIES=/usr/lib/llvm-17/lib/libarcher.so ARCHER_OPTIONS='verbose=1' TSAN_OPTIONS='suppresions=cpp/tsan_suppressions.txt:ignore_noninstrumented_modules=1:halt_on_error=1' # Replace with local llvm install path. libarcher.so must be presetn if(BUILD_TSAN) message(STATUS "Building with ThreadSanitizer enabled") diff --git a/ci/tsan_suppressions.txt b/cpp/tsan_suppressions.txt similarity index 100% rename from ci/tsan_suppressions.txt rename to cpp/tsan_suppressions.txt From ef69eb7af6b7b4f006aaad7ddf2965fd298900d3 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 25 Nov 2025 18:22:58 +0000 Subject: [PATCH 089/225] PDLP features --- cpp/src/linear_programming/pdlp.cu | 74 +++++++++++++++++++++++++++- cpp/src/linear_programming/pdlp.cuh | 9 ++++ cpp/src/mip/relaxed_lp/relaxed_lp.cu | 63 ++++++++++++++++++++--- 3 files changed, 138 insertions(+), 8 deletions(-) diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index ba156920f4..d73b29706a 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -1181,6 +1181,40 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co bool warm_start_was_given = settings_.get_pdlp_warm_start_data().last_restart_duality_gap_dual_solution_.size() != 0; + // Reset iteration metrics at the start of the solver run + total_spmv_ops_ = 0; + + // Compute sparsity metrics once (they don't change during solve) + { + const i_t n_vars = problem_ptr->n_variables; + const i_t n_cstrs = problem_ptr->n_constraints; + const int64_t nnz = problem_ptr->nnz; + + cached_sparsity_ = (n_cstrs > 0 && n_vars > 0) + ? static_cast(nnz) / (static_cast(n_cstrs) * n_vars) + : 0.0; + cached_nnz_stddev_ = 0.0; + cached_unbalancedness_ = 0.0; + + if (problem_ptr->offsets.size() == static_cast(n_cstrs + 1) && n_cstrs > 0) { + // Copy offsets to host for efficient computation + std::vector h_offsets(n_cstrs + 1); + raft::copy(h_offsets.data(), problem_ptr->offsets.data(), n_cstrs + 1, stream_view_); + RAFT_CUDA_TRY(cudaStreamSynchronize(stream_view_)); + + const double mean_nnz = static_cast(nnz) / n_cstrs; + double variance_sum = 0.0; + for (i_t row = 0; row < n_cstrs; ++row) { + const double row_nnz = static_cast(h_offsets[row + 1] - h_offsets[row]); + const double diff = row_nnz - mean_nnz; + variance_sum += diff * diff; + } + const double variance = variance_sum / n_cstrs; + cached_nnz_stddev_ = std::sqrt(variance); + cached_unbalancedness_ = (mean_nnz > 0) ? cached_nnz_stddev_ / mean_nnz : 0.0; + } + } + if (!inside_mip_) { CUOPT_LOG_INFO( " Iter Primal Obj. Dual Obj. Gap Primal Res. Dual Res. Time"); @@ -1320,11 +1354,19 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co take_step(total_pdlp_iterations_, (total_pdlp_iterations_ + 1) % pdlp_hyper_params::major_iteration == 0); + // Count SpMV operations: take_step does approximately 3 SpMV ops per iteration + // (A @ x in dual update, A^T @ y in primal update or cached, A^T @ y' in step size) + constexpr int64_t spmv_ops_per_iteration = 3; + total_spmv_ops_ += spmv_ops_per_iteration; + if (pdlp_hyper_params::use_reflected_primal_dual) { if (pdlp_hyper_params::use_fixed_point_error && (total_pdlp_iterations_ + 1) % pdlp_hyper_params::major_iteration == 0 || - has_restarted) + has_restarted) { compute_fixed_error(has_restarted); // May set has_restarted to false + // compute_fixed_error does 1 additional SpMV + total_spmv_ops_ += 1; + } halpern_update(); } @@ -1333,6 +1375,36 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co ++internal_solver_iterations_; if (pdlp_hyper_params::never_restart_to_average) restart_strategy_.increment_iteration_since_last_restart(); + + // Log PDLP_RESULT metrics every pdlp_log_interval iterations + constexpr i_t pdlp_log_interval = 50; + if (total_pdlp_iterations_ % pdlp_log_interval == 0) { + const int64_t nnz = problem_ptr->nnz; + const int64_t total_nnz = total_spmv_ops_ * nnz; + const f_t elapsed_sec = timer.elapsed_time(); + const double nnz_per_sec = + (elapsed_sec > 0) ? static_cast(total_nnz) / elapsed_sec : 0.0; + const double nnz_per_iter = static_cast(total_nnz) / pdlp_log_interval; + + CUOPT_LOG_INFO( + "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6f nnz_stddev=%.6f " + "unbalancedness=%.6f " + "iters=%d time_ms=%.0f spmv_ops=%ld total_nnz=%.2e nnz/s=%.2e nnz/iter=%.2e", + problem_ptr->n_variables, + problem_ptr->n_constraints, + nnz, + cached_sparsity_, + cached_nnz_stddev_, + cached_unbalancedness_, + total_pdlp_iterations_, + elapsed_sec * 1000.0, + total_spmv_ops_, + static_cast(total_nnz), + nnz_per_sec, + nnz_per_iter); + // Reset counters for next interval + total_spmv_ops_ = 0; + } } return optimization_problem_solution_t{pdlp_termination_status_t::NumericalError, stream_view_}; diff --git a/cpp/src/linear_programming/pdlp.cuh b/cpp/src/linear_programming/pdlp.cuh index 663c617d46..42eeeae710 100644 --- a/cpp/src/linear_programming/pdlp.cuh +++ b/cpp/src/linear_programming/pdlp.cuh @@ -68,6 +68,7 @@ class pdlp_solver_t { f_t get_relative_dual_tolerance_factor() const; f_t get_relative_primal_tolerance_factor() const; detail::pdlp_termination_strategy_t& get_current_termination_strategy(); + int64_t get_total_spmv_ops() const { return total_spmv_ops_; } void set_problem_ptr(problem_t* problem_ptr_); @@ -197,6 +198,14 @@ class pdlp_solver_t { i_t total_pdlp_iterations_{0}; i_t internal_solver_iterations_{0}; + // Iteration metrics for performance tracking + int64_t total_spmv_ops_{0}; // Total SpMV operations performed + + // Cached sparsity metrics (computed once at start of solve) + double cached_sparsity_{0.0}; + double cached_nnz_stddev_{0.0}; + double cached_unbalancedness_{0.0}; + // Initial solution rmm::device_uvector initial_primal_; rmm::device_uvector initial_dual_; diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index b2c9631e0b..b5f04f7ead 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -169,13 +169,62 @@ optimization_problem_solution_t get_relaxed_lp_solution( elapsed_ms, solver_response.get_additional_termination_information().number_of_steps_taken); - // // === PDLP PREDICTOR RESULTS - START === - // auto term_info = solver_response.get_additional_termination_information(); - // CUOPT_LOG_INFO("PDLP_RESULT: iterations=%d time_ms=%lld termination=%d", - // term_info.number_of_steps_taken, - // elapsed_ms, - // (int)solver_response.get_termination_status()); - // // === PDLP PREDICTOR RESULTS - END === + // === PDLP PREDICTOR RESULTS - START === + auto term_info = solver_response.get_additional_termination_information(); + const i_t n_vars = op_problem.n_variables; + const i_t n_cstrs = op_problem.n_constraints; + const int64_t nnz = op_problem.nnz; + const int64_t total_spmv = lp_solver.get_total_spmv_ops(); + const int64_t total_nnz = total_spmv * nnz; + const double nnz_per_sec = + (elapsed_ms > 0) ? static_cast(total_nnz) / (elapsed_ms / 1000.0) : 0.0; + const double nnz_per_iter = (term_info.number_of_steps_taken > 0) + ? static_cast(total_nnz) / term_info.number_of_steps_taken + : 0.0; + + // Compute sparsity metrics + const double sparsity = (n_cstrs > 0 && n_vars > 0) + ? static_cast(nnz) / (static_cast(n_cstrs) * n_vars) + : 0.0; + double nnz_stddev = 0.0; + double unbalancedness = 0.0; + if (op_problem.offsets.size() == static_cast(n_cstrs + 1) && n_cstrs > 0) { + std::vector h_offsets(n_cstrs + 1); + raft::copy(h_offsets.data(), + op_problem.offsets.data(), + n_cstrs + 1, + op_problem.handle_ptr->get_stream()); + op_problem.handle_ptr->sync_stream(); + + const double mean_nnz = static_cast(nnz) / n_cstrs; + double variance_sum = 0.0; + for (i_t row = 0; row < n_cstrs; ++row) { + const double row_nnz = static_cast(h_offsets[row + 1] - h_offsets[row]); + const double diff = row_nnz - mean_nnz; + variance_sum += diff * diff; + } + const double variance = variance_sum / n_cstrs; + nnz_stddev = std::sqrt(variance); + unbalancedness = (mean_nnz > 0) ? nnz_stddev / mean_nnz : 0.0; + } + + CUOPT_LOG_INFO( + "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f " + "iters=%d time_ms=%lld term=%d spmv_ops=%ld total_nnz=%.2e nnz/s=%.2e nnz/iter=%.2e", + n_vars, + n_cstrs, + nnz, + sparsity, + nnz_stddev, + unbalancedness, + term_info.number_of_steps_taken, + elapsed_ms, + static_cast(solver_response.get_termination_status()), + total_spmv, + static_cast(total_nnz), + nnz_per_sec, + nnz_per_iter); + // === PDLP PREDICTOR RESULTS - END === return solver_response; } From 7ac230fe4e8cf3cb7f78295de779756eb8219f0b Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 26 Nov 2025 14:49:50 +0000 Subject: [PATCH 090/225] script tweaks --- scripts/determinism_logs_parse.py | 35 +++++++++++++++++++++++++------ scripts/train_regressor.py | 5 +++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/scripts/determinism_logs_parse.py b/scripts/determinism_logs_parse.py index cee653c0b1..5031656417 100755 --- a/scripts/determinism_logs_parse.py +++ b/scripts/determinism_logs_parse.py @@ -21,11 +21,12 @@ Supports parsing of: - FP (Feasibility Pump): FP_FEATURES and FP_RESULT logs -- PDLP (LP Solver): PDLP_FEATURES and PDLP_RESULT logs +- PDLP (LP Solver): PDLP_RESULT single-line logs - CP (Constraint Propagation): CP_FEATURES and CP_RESULT logs - FJ (Feasibility Jump): Legacy FJ: format - CPUFJ (CPU Feasibility Jump): CPUFJ_FEATURES single-line logs - BB (Branch and Bound): BB_NODE_FEATURES single-line logs +- DS (Dual Simplex): DS_FEATURES single-line logs IMPORTANT - Grep Specificity: The parser uses EXACT pattern matching with grep to filter logs efficiently. @@ -51,6 +52,7 @@ python determinism_logs_parse.py --algorithm FJ [-o output.feather] python determinism_logs_parse.py --algorithm CPUFJ [-o output.feather] python determinism_logs_parse.py --algorithm BB [-o output.feather] + python determinism_logs_parse.py --algorithm DS [-o output.feather] """ import argparse @@ -62,7 +64,7 @@ from typing import List, Dict, Any, Optional -SUPPORTED_ALGORITHMS = ["FP", "PDLP", "CP", "FJ", "CPUFJ", "BB"] +SUPPORTED_ALGORITHMS = ["FP", "PDLP", "CP", "FJ", "CPUFJ", "BB", "DS"] def parse_value(value_str: str) -> Any: @@ -310,8 +312,10 @@ def parse_fp_logs(log_files: List[str]) -> List[Dict[str, Any]]: def parse_pdlp_logs(log_files: List[str]) -> List[Dict[str, Any]]: - """Parse PDLP (LP Solver) feature and result logs.""" - return parse_generic_algorithm_logs(log_files, "PDLP", "LP Solver") + """Parse PDLP (LP Solver) result logs.""" + return parse_single_line_logs( + log_files, "PDLP_RESULT:", "PDLP (LP Solver)", "PDLP_RESULT:" + ) def parse_cp_logs(log_files: List[str]) -> List[Dict[str, Any]]: @@ -432,6 +436,13 @@ def parse_bb_logs(log_files: List[str]) -> List[Dict[str, Any]]: ) +def parse_ds_logs(log_files: List[str]) -> List[Dict[str, Any]]: + """Parse Dual Simplex feature logs.""" + return parse_single_line_logs( + log_files, "DS_FEATURES:", "DS (Dual Simplex)", "DS_FEATURES:" + ) + + def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: """Print statistics about parsed entries.""" if not entries: @@ -478,11 +489,12 @@ def main(): epilog=""" Supported Algorithms: FP - Feasibility Pump (parses FP_FEATURES and FP_RESULT logs) - PDLP - LP Solver (parses PDLP_FEATURES and PDLP_RESULT logs) + PDLP - LP Solver (parses PDLP_RESULT single-line logs) CP - Constraint Propagation (parses CP_FEATURES and CP_RESULT logs) FJ - Feasibility Jump (parses legacy FJ: format) CPUFJ - CPU Feasibility Jump (parses CPUFJ_FEATURES single-line logs) BB - Branch and Bound (parses BB_NODE_FEATURES single-line logs) + DS - Dual Simplex (parses DS_FEATURES single-line logs) Examples: python determinism_logs_parse.py logs/ --algorithm FP -o fp_data.feather @@ -491,6 +503,7 @@ def main(): python determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.feather python determinism_logs_parse.py logs/ --algorithm CPUFJ -o cpufj_data.feather python determinism_logs_parse.py logs/ --algorithm BB -o bb_data.feather + python determinism_logs_parse.py logs/ --algorithm DS -o ds_data.feather # Limit to first 10 files for testing python determinism_logs_parse.py logs/ --algorithm FP --max-files 10 @@ -565,16 +578,22 @@ def main(): entries = parse_cpufj_logs(log_files) elif args.algorithm == "BB": entries = parse_bb_logs(log_files) + elif args.algorithm == "DS": + entries = parse_ds_logs(log_files) else: print(f"Error: Unsupported algorithm: {args.algorithm}") return 1 if not entries: print(f"\nError: No entries found for {args.algorithm}") - if args.algorithm in ["FP", "PDLP", "CP"]: + if args.algorithm in ["FP", "CP"]: print( f"Make sure your logs contain {args.algorithm}_FEATURES and {args.algorithm}_RESULT lines" ) + elif args.algorithm == "PDLP": + print( + "Make sure your logs contain PDLP_RESULT: lines with key=value pairs" + ) elif args.algorithm == "FJ": print("Make sure your logs contain FJ: lines with key=value pairs") elif args.algorithm == "CPUFJ": @@ -585,6 +604,10 @@ def main(): print( "Make sure your logs contain BB_NODE_FEATURES lines with key=value pairs" ) + elif args.algorithm == "DS": + print( + "Make sure your logs contain DS_FEATURES: lines with key=value pairs" + ) return 1 # Print statistics diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index f26874a21e..9c17e9e767 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -141,6 +141,11 @@ "h_tabu_lastinc_stores", "h_tabu_lastinc_loads", "max_weight", + "fixed", + "phase", + "iters", + "nnz/s", + "nnz/iter", ] # Alternatively, specify ONLY the features you want to use From 24d7ccbb49d200daae94ee4581fe270f37f6aea8 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 26 Nov 2025 15:53:11 +0000 Subject: [PATCH 091/225] more feature loggign --- cpp/src/linear_programming/pdlp.cu | 92 ++++--- cpp/src/linear_programming/pdlp.cuh | 6 + cpp/src/linear_programming/pdlp_features.hpp | 245 +++++++++++++++++++ cpp/src/mip/relaxed_lp/relaxed_lp.cu | 5 + 4 files changed, 313 insertions(+), 35 deletions(-) create mode 100644 cpp/src/linear_programming/pdlp_features.hpp diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index d73b29706a..fc55fece35 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -1215,6 +1215,16 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co } } + // Initialize feature tracking for runtime prediction + features_.init_from_problem(problem_ptr->n_variables, + problem_ptr->n_constraints, + problem_ptr->nnz, + static_cast(cached_sparsity_), + static_cast(cached_nnz_stddev_), + static_cast(cached_unbalancedness_), + warm_start_was_given); + interval_start_time_ = std::chrono::high_resolution_clock::now(); + if (!inside_mip_) { CUOPT_LOG_INFO( " Iter Primal Obj. Dual Obj. Gap Primal Res. Dual Res. Time"); @@ -1329,8 +1339,22 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co average_termination_strategy_.get_convergence_information(), // Needed for KKT restart best_primal_weight_ // Needed for cuPDLP+ restart ); + + // Track restart for feature logging + if (has_restarted) { features_.record_restart(); } } + // Update convergence metrics from termination strategy (for feature logging) + const auto& conv_info = current_termination_strategy_.get_convergence_information(); + const f_t kkt = restart_strategy_.compute_kkt_score(conv_info.get_l2_primal_residual(), + conv_info.get_l2_dual_residual(), + conv_info.get_gap(), + primal_weight_); + features_.update_convergence(conv_info.get_l2_primal_residual().value(stream_view_), + conv_info.get_l2_dual_residual().value(stream_view_), + conv_info.get_gap().value(stream_view_), + kkt); + if (!pdlp_hyper_params::rescale_for_restart) { // We don't need to rescale average because what matters is weighted_average_solution // getting the scaled accumulation @@ -1351,18 +1375,21 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co #ifdef CUPDLP_DEBUG_MODE printf("Is Major %d\n", (total_pdlp_iterations_ + 1) % pdlp_hyper_params::major_iteration == 0); #endif - take_step(total_pdlp_iterations_, - (total_pdlp_iterations_ + 1) % pdlp_hyper_params::major_iteration == 0); + // Determine if the NEXT iteration will be a major iteration (for take_step) + const bool next_is_major = + (total_pdlp_iterations_ + 1) % pdlp_hyper_params::major_iteration == 0; + + take_step(total_pdlp_iterations_, next_is_major); + + // Track iteration for features (2 SpMV per regular iteration in Stable3) + features_.record_regular_iteration(); - // Count SpMV operations: take_step does approximately 3 SpMV ops per iteration - // (A @ x in dual update, A^T @ y in primal update or cached, A^T @ y' in step size) - constexpr int64_t spmv_ops_per_iteration = 3; + // Count SpMV operations for legacy tracking + constexpr int64_t spmv_ops_per_iteration = 2; total_spmv_ops_ += spmv_ops_per_iteration; if (pdlp_hyper_params::use_reflected_primal_dual) { - if (pdlp_hyper_params::use_fixed_point_error && - (total_pdlp_iterations_ + 1) % pdlp_hyper_params::major_iteration == 0 || - has_restarted) { + if (pdlp_hyper_params::use_fixed_point_error && (next_is_major || has_restarted)) { compute_fixed_error(has_restarted); // May set has_restarted to false // compute_fixed_error does 1 additional SpMV total_spmv_ops_ += 1; @@ -1371,38 +1398,33 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co halpern_update(); } + // Track major iteration for features (after compute_fixed_error adds +1 SpMV) + if (is_major_iteration || is_conditional_major) { features_.record_major_iteration(); } + ++total_pdlp_iterations_; ++internal_solver_iterations_; if (pdlp_hyper_params::never_restart_to_average) restart_strategy_.increment_iteration_since_last_restart(); - // Log PDLP_RESULT metrics every pdlp_log_interval iterations - constexpr i_t pdlp_log_interval = 50; - if (total_pdlp_iterations_ % pdlp_log_interval == 0) { - const int64_t nnz = problem_ptr->nnz; - const int64_t total_nnz = total_spmv_ops_ * nnz; - const f_t elapsed_sec = timer.elapsed_time(); - const double nnz_per_sec = - (elapsed_sec > 0) ? static_cast(total_nnz) / elapsed_sec : 0.0; - const double nnz_per_iter = static_cast(total_nnz) / pdlp_log_interval; - - CUOPT_LOG_INFO( - "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6f nnz_stddev=%.6f " - "unbalancedness=%.6f " - "iters=%d time_ms=%.0f spmv_ops=%ld total_nnz=%.2e nnz/s=%.2e nnz/iter=%.2e", - problem_ptr->n_variables, - problem_ptr->n_constraints, - nnz, - cached_sparsity_, - cached_nnz_stddev_, - cached_unbalancedness_, - total_pdlp_iterations_, - elapsed_sec * 1000.0, - total_spmv_ops_, - static_cast(total_nnz), - nnz_per_sec, - nnz_per_iter); - // Reset counters for next interval + // Update step parameters for feature tracking + features_.update_step_params(step_size_.value(stream_view_), + primal_weight_.value(stream_view_)); + + // Log PDLP features at regular intervals + if (features_.should_log()) { + // Compute elapsed time for this interval + auto now = std::chrono::high_resolution_clock::now(); + auto interval_ms = std::chrono::duration(now - interval_start_time_).count(); + features_.interval_time_ms = interval_ms; + + // Log the features + features_.log_features(); + + // Reset interval counters and timer + features_.reset_interval_counters(); + interval_start_time_ = now; + + // Reset legacy spmv counter for consistency total_spmv_ops_ = 0; } } diff --git a/cpp/src/linear_programming/pdlp.cuh b/cpp/src/linear_programming/pdlp.cuh index 42eeeae710..fc68f7bc71 100644 --- a/cpp/src/linear_programming/pdlp.cuh +++ b/cpp/src/linear_programming/pdlp.cuh @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #include #include "linear_programming/termination_strategy/convergence_information.hpp" @@ -201,6 +203,10 @@ class pdlp_solver_t { // Iteration metrics for performance tracking int64_t total_spmv_ops_{0}; // Total SpMV operations performed + // Feature tracking for runtime prediction (Stable3 mode) + pdlp_features_t features_; + std::chrono::high_resolution_clock::time_point interval_start_time_; + // Cached sparsity metrics (computed once at start of solve) double cached_sparsity_{0.0}; double cached_nnz_stddev_{0.0}; diff --git a/cpp/src/linear_programming/pdlp_features.hpp b/cpp/src/linear_programming/pdlp_features.hpp new file mode 100644 index 0000000000..95595e921e --- /dev/null +++ b/cpp/src/linear_programming/pdlp_features.hpp @@ -0,0 +1,245 @@ +/* clang-format off */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* clang-format on */ + +#pragma once + +#include + +#include +#include +#include + +namespace cuopt::linear_programming::detail { + +// Feature logging interval (every N iterations) +constexpr int PDLP_FEATURE_LOG_INTERVAL = 50; + +/** + * @brief Feature collection structure for PDLP Stable3 runtime prediction. + * + * This structure collects features that can be used to train regression models + * for predicting the runtime of PDLP iterations in Stable3 mode. + * + * Stable3 key characteristics: + * - No adaptive step size retries (deterministic SpMV count) + * - Uses reflected primal/dual (Halpern update) + * - Uses fixed point error computation on major iterations + * - Uses conditional major iterations (frequency increases with iteration count) + * - Never restarts to average (only to current solution) + */ +template +struct pdlp_features_t { + // ========================================================================= + // Problem Features (static, set once at initialization) + // ========================================================================= + i_t n_vars{0}; // Number of variables + i_t n_cstrs{0}; // Number of constraints + int64_t nnz{0}; // Number of nonzeros in constraint matrix + f_t sparsity{0.0}; // nnz / (n_vars * n_cstrs) + f_t nnz_stddev{0.0}; // Standard deviation of row nnz counts + f_t unbalancedness{0.0}; // nnz_stddev / mean_nnz_per_row + + // ========================================================================= + // Interval Counters (reset after each log) + // ========================================================================= + i_t interval_iters{0}; // Iterations in this logging interval + i_t interval_major{0}; // Major iterations in this interval + i_t interval_restarts{0}; // Restarts in this interval + + // ========================================================================= + // SpMV Metrics + // ========================================================================= + // In Stable3: regular iter = 2 SpMV, major iter = 3 SpMV (fixed point error) + int64_t interval_spmv_ops{0}; // SpMV operations in this interval + + // ========================================================================= + // Cumulative Counters (never reset) + // ========================================================================= + i_t total_iters{0}; // Total iterations since solver start + i_t total_restarts{0}; // Total restarts since solver start + i_t iters_since_restart{0}; // Iterations since last restart + + // ========================================================================= + // Convergence Metrics (snapshot at log time, from last major iteration) + // ========================================================================= + f_t primal_res{0.0}; // L2 primal residual + f_t dual_res{0.0}; // L2 dual residual + f_t gap{0.0}; // Duality gap + f_t kkt_score{0.0}; // KKT score (used for restart decisions) + + // ========================================================================= + // Step Parameters (can change on restarts) + // ========================================================================= + f_t step_size{0.0}; // Current step size + f_t primal_weight{0.0}; // Current primal weight + + // ========================================================================= + // Timing + // ========================================================================= + f_t interval_time_ms{0.0}; // Time elapsed in this interval (milliseconds) + + // ========================================================================= + // Warm Start Info + // ========================================================================= + bool has_warm_start{false}; // Whether warm start was provided + + /** + * @brief Initialize static problem features. + * + * Called once at solver initialization. Computes sparsity metrics from + * the problem structure. + */ + void init_from_problem(i_t n_variables, + i_t n_constraints, + int64_t num_nonzeros, + f_t computed_sparsity, + f_t computed_nnz_stddev, + f_t computed_unbalancedness, + bool warm_start) + { + n_vars = n_variables; + n_cstrs = n_constraints; + nnz = num_nonzeros; + sparsity = computed_sparsity; + nnz_stddev = computed_nnz_stddev; + unbalancedness = computed_unbalancedness; + has_warm_start = warm_start; + } + + /** + * @brief Record a regular iteration. + * + * In Stable3, each regular iteration does 2 SpMV operations: + * - compute_At_y(): A^T @ y + * - compute_A_x(): A @ x + */ + void record_regular_iteration() + { + ++interval_iters; + ++total_iters; + ++iters_since_restart; + interval_spmv_ops += 2; + } + + /** + * @brief Record a major iteration. + * + * Major iterations do an additional SpMV for fixed point error computation. + * They also involve termination checks and potential restarts. + */ + void record_major_iteration() + { + ++interval_major; + // Fixed point error computation adds 1 SpMV + interval_spmv_ops += 1; + } + + /** + * @brief Record a restart event. + */ + void record_restart() + { + ++interval_restarts; + ++total_restarts; + iters_since_restart = 0; + } + + /** + * @brief Update convergence metrics from termination strategy. + * + * Called during major iterations when convergence info is computed. + */ + void update_convergence(f_t l2_primal_residual, + f_t l2_dual_residual, + f_t duality_gap, + f_t computed_kkt_score) + { + primal_res = l2_primal_residual; + dual_res = l2_dual_residual; + gap = duality_gap; + kkt_score = computed_kkt_score; + } + + /** + * @brief Update step parameters. + * + * Called when step size or primal weight changes (typically after restarts). + */ + void update_step_params(f_t current_step_size, f_t current_primal_weight) + { + step_size = current_step_size; + primal_weight = current_primal_weight; + } + + /** + * @brief Log all features in key=value format. + * + * Format: PDLP_RESULT: n_vars=N n_cstrs=M nnz=K ... + * + * This format is parsed by determinism_logs_parse.py with --algorithm PDLP + */ + void log_features() const + { + // Compute derived metrics + const int64_t total_nnz_processed = interval_spmv_ops * nnz; + const double nnz_per_sec = (interval_time_ms > 0) ? static_cast(total_nnz_processed) / + (interval_time_ms / 1000.0) + : 0.0; + + CUOPT_LOG_INFO( + "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6e nnz_stddev=%.4f " + "unbalancedness=%.4f interval_iters=%d interval_major=%d interval_restarts=%d " + "spmv_ops=%ld total_iters=%d total_restarts=%d iters_since_restart=%d " + "primal_res=%.6e dual_res=%.6e gap=%.6e kkt=%.6e " + "step_size=%.6e primal_weight=%.6e time_ms=%.2f nnz_per_s=%.2e warm_start=%d", + n_vars, + n_cstrs, + nnz, + sparsity, + nnz_stddev, + unbalancedness, + interval_iters, + interval_major, + interval_restarts, + interval_spmv_ops, + total_iters, + total_restarts, + iters_since_restart, + primal_res, + dual_res, + gap, + kkt_score, + step_size, + primal_weight, + interval_time_ms, + nnz_per_sec, + static_cast(has_warm_start)); + } + + /** + * @brief Reset per-interval counters. + * + * Called after each log to start fresh for the next interval. + */ + void reset_interval_counters() + { + interval_iters = 0; + interval_major = 0; + interval_restarts = 0; + interval_spmv_ops = 0; + interval_time_ms = 0.0; + } + + /** + * @brief Check if it's time to log features. + * + * Returns true every PDLP_FEATURE_LOG_INTERVAL iterations. + */ + bool should_log() const { return (interval_iters >= PDLP_FEATURE_LOG_INTERVAL); } +}; + +} // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index b5f04f7ead..6a336e7c00 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -100,6 +100,11 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.per_constraint_residual = settings.per_constraint_residual; pdlp_settings.first_primal_feasible = settings.return_first_feasible; pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; + // Stable3 has a simpler and more predictable codepath + // if (work_limit != std::numeric_limits::infinity()) + { + pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable3; + } set_pdlp_solver_mode(pdlp_settings); // TODO: set Stable3 here? pdlp_solver_t lp_solver(op_problem, pdlp_settings); From d35bed2ecbce50eac9487c791d47be1eb2bcb184 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 27 Nov 2025 09:34:16 +0000 Subject: [PATCH 092/225] fix logging --- cpp/src/linear_programming/pdlp_features.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cpp/src/linear_programming/pdlp_features.hpp b/cpp/src/linear_programming/pdlp_features.hpp index 95595e921e..76f0169144 100644 --- a/cpp/src/linear_programming/pdlp_features.hpp +++ b/cpp/src/linear_programming/pdlp_features.hpp @@ -7,8 +7,6 @@ #pragma once -#include - #include #include #include @@ -190,7 +188,7 @@ struct pdlp_features_t { (interval_time_ms / 1000.0) : 0.0; - CUOPT_LOG_INFO( + printf( "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6e nnz_stddev=%.4f " "unbalancedness=%.4f interval_iters=%d interval_major=%d interval_restarts=%d " "spmv_ops=%ld total_iters=%d total_restarts=%d iters_since_restart=%d " From 19139467259785482e4e9abcea15469db6fc7562 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 27 Nov 2025 13:49:24 +0000 Subject: [PATCH 093/225] basic dual simplex and pdlp predictors --- cpp/src/CMakeLists.txt | 6 +- .../dual_simplex/dual_simplex_features.hpp | 2 +- cpp/src/dual_simplex/phase2.cpp | 102 +- cpp/src/linear_programming/solve.cu | 2 +- cpp/src/mip/diversity/diversity_manager.cu | 4 +- cpp/src/mip/feasibility_jump/fj_cpu.cu | 7 +- cpp/src/mip/solver.cu | 14 +- cpp/src/mip/solver_context.cuh | 2 + .../models/dualsimplex_predictor/header.h | 35 + .../models/dualsimplex_predictor/main.cpp | 19265 ++++++++++++++++ .../models/dualsimplex_predictor/quantize.cpp | 1891 ++ cpp/src/utilities/models/fj.ubj.gz | Bin 12153 -> 0 bytes .../utilities/models/pdlp_predictor/header.h | 35 + .../utilities/models/pdlp_predictor/main.cpp | 16893 ++++++++++++++ .../models/pdlp_predictor/quantize.cpp | 1006 + cpp/src/utilities/version_info.cpp | 30 + cpp/src/utilities/version_info.hpp | 3 +- cpp/src/utilities/work_unit_predictor.cpp | 9 +- 18 files changed, 39269 insertions(+), 37 deletions(-) create mode 100644 cpp/src/utilities/models/dualsimplex_predictor/header.h create mode 100644 cpp/src/utilities/models/dualsimplex_predictor/main.cpp create mode 100644 cpp/src/utilities/models/dualsimplex_predictor/quantize.cpp delete mode 100644 cpp/src/utilities/models/fj.ubj.gz create mode 100644 cpp/src/utilities/models/pdlp_predictor/header.h create mode 100644 cpp/src/utilities/models/pdlp_predictor/main.cpp create mode 100644 cpp/src/utilities/models/pdlp_predictor/quantize.cpp diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index c07cae0376..3267f3e802 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -11,7 +11,11 @@ set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/quantize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/quantize.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/quantize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/dualsimplex_predictor/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/dualsimplex_predictor/quantize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/pdlp_predictor/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/pdlp_predictor/quantize.cpp) add_subdirectory(linear_programming) add_subdirectory(math_optimization) diff --git a/cpp/src/dual_simplex/dual_simplex_features.hpp b/cpp/src/dual_simplex/dual_simplex_features.hpp index 9024cbad59..73e88970eb 100644 --- a/cpp/src/dual_simplex/dual_simplex_features.hpp +++ b/cpp/src/dual_simplex/dual_simplex_features.hpp @@ -112,7 +112,7 @@ struct dual_simplex_features_t { */ void log_features(const simplex_solver_settings_t& settings) const { - settings.log.printf( + printf( "DS_FEATURES: iter=%d m=%d n=%d nnz=%d density=%.6e avg_nnz_col=%.2f avg_nnz_row=%.2f " "bounded=%d free=%d fixed=%d phase=%d refact_freq=%d num_refacts=%d num_updates=%d " "sparse_dz=%d dense_dz=%d bound_flips=%d num_infeas=%d dy_nz_pct=%.2f " diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 58a3312b75..7cd1c420fd 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -17,12 +17,18 @@ #include #include +#include +#include + +#include + #include #include #include #include #include +#include namespace cuopt::linear_programming::dual_simplex { @@ -2237,6 +2243,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, dual::status_t status = dual::status_t::UNSET; + raft::common::nvtx::push_range("DualSimplex::phase2_advanced_init"); + // Perturbed objective std::vector objective = lp.objective; @@ -2381,7 +2389,10 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, csc_matrix_t A_transpose(1, 1, 0); lp.A.transpose(A_transpose); - f_t obj = compute_objective(lp, x); + f_t obj = compute_objective(lp, x); + + raft::common::nvtx::pop_range(); + const i_t start_iter = iter; i_t sparse_delta_z = 0; @@ -2453,6 +2464,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, // Note: Features are logged inline with DS_FEATURES: prefix while (iter < iter_limit) { + raft::common::nvtx::range scope_main("DualSimplex::phase2_main_loop"); // Pricing i_t direction = 0; i_t basic_leaving_index = -1; @@ -2534,29 +2546,32 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } delta_y_nz_percentage = delta_y_nz0 / static_cast(m) * 100.0; const bool use_transpose = delta_y_nz_percentage <= 30.0; - if (use_transpose) { - sparse_delta_z++; - phase2::compute_delta_z(A_transpose, - delta_y_sparse, - leaving_index, - direction, - nonbasic_mark, - delta_z_mark, - delta_z_indices, - delta_z); - } else { - dense_delta_z++; - // delta_zB = sigma*ei - delta_y_sparse.to_dense(delta_y); - phase2::compute_reduced_cost_update(lp, - basic_list, - nonbasic_list, - delta_y, - leaving_index, - direction, - delta_z_mark, - delta_z_indices, - delta_z); + { + raft::common::nvtx::range scope_compute_delta_z("DualSimplex::delta_z"); + if (use_transpose) { + sparse_delta_z++; + phase2::compute_delta_z(A_transpose, + delta_y_sparse, + leaving_index, + direction, + nonbasic_mark, + delta_z_mark, + delta_z_indices, + delta_z); + } else { + dense_delta_z++; + // delta_zB = sigma*ei + delta_y_sparse.to_dense(delta_y); + phase2::compute_reduced_cost_update(lp, + basic_list, + nonbasic_list, + delta_y, + leaving_index, + direction, + delta_z_mark, + delta_z_indices, + delta_z); + } } timers.delta_z_time += timers.stop_timer(); @@ -3064,6 +3079,43 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, // Log all features on a single line features.log_features(settings); + + // Populate features map for predictor + cuopt::work_unit_predictor_t dualsimplex_predictor{}; + std::map features_map; + features_map["m"] = static_cast(features.num_rows); + features_map["n"] = static_cast(features.num_cols); + features_map["nnz"] = static_cast(features.num_nonzeros); + features_map["density"] = static_cast(features.matrix_density); + features_map["avg_nnz_col"] = static_cast(features.avg_nnz_per_col); + features_map["avg_nnz_row"] = static_cast(features.avg_nnz_per_row); + features_map["bounded"] = static_cast(features.num_bounded_vars); + features_map["free"] = static_cast(features.num_free_vars); + features_map["refact_freq"] = static_cast(features.refactor_frequency); + features_map["num_refacts"] = static_cast(features.num_refactors); + features_map["num_updates"] = static_cast(features.num_basis_updates); + features_map["sparse_dz"] = static_cast(features.sparse_delta_z_count); + features_map["dense_dz"] = static_cast(features.dense_delta_z_count); + features_map["bound_flips"] = static_cast(features.total_bound_flips); + features_map["num_infeas"] = static_cast(features.num_infeasibilities); + features_map["dy_nz_pct"] = static_cast(features.delta_y_nz_percentage); + features_map["byte_loads"] = static_cast(features.byte_loads); + features_map["byte_stores"] = static_cast(features.byte_stores); + auto time_prediction = + std::max((f_t)0.0, (f_t)dualsimplex_predictor.predict_scalar(features_map)); + f_t baseline_max_clock = 3800; // 3.8Ghz + f_t max_clock = cuopt::get_cpu_max_clock_mhz(); + f_t scaling_factor = baseline_max_clock / max_clock; + f_t adjusted_time_prediction = time_prediction * scaling_factor; + printf( + "DualSimplex determ: Estimated time for %d iters: %f, actual time: %f, error %f, CPU max " + "clock: %f MHz, scaling factor: %f\n", + FEATURE_LOG_INTERVAL, + adjusted_time_prediction, + features.interval_runtime, + adjusted_time_prediction - features.interval_runtime, + max_clock, + scaling_factor); } if ((iter - start_iter) < settings.first_iteration_log || @@ -3106,6 +3158,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, ft.print_stats(); } } + + printf("Iterations: %d, time taken: %f\n", iter, toc(start_time)); return status; } diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 42e32a030b..5416923680 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -818,7 +818,7 @@ optimization_problem_solution_t solve_lp(optimization_problem_t problem(op_problem); - double presolve_time = 0.0; + [[maybe_unused]] double presolve_time = 0.0; std::unique_ptr> presolver; auto run_presolve = settings.presolve; run_presolve = run_presolve && settings.get_pdlp_warm_start_data().total_pdlp_iterations_ == -1; diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 6cdd8a63f9..96f93d267a 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -92,8 +92,8 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t 1) { - int config_id = -1; // Default value - const char* env_config_id = std::getenv("CUOPT_CONFIG_ID"); + [[maybe_unused]] int config_id = -1; // Default value + const char* env_config_id = std::getenv("CUOPT_CONFIG_ID"); if (env_config_id != nullptr) { try { config_id = std::stoi(env_config_id); diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index 31d1b8cc32..43f747d862 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -1706,9 +1706,10 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l float time_prediction = std::max( (f_t)0.0, (f_t)ceil(context.work_unit_predictors.cpufj_predictor.predict_scalar(features_map))); - CUOPT_LOG_DEBUG("FJ determ: Estimated time for 1000 iters: %f, error %f", - time_prediction, - time_prediction - time_window_ms); + // CUOPT_LOG_DEBUG("FJ determ: Estimated time for 1000 iters: %f, actual time: %f, error %f", + // time_prediction, + // time_window_ms, + // time_prediction - time_window_ms); } cuopt_func_call(sanity_checks(fj_cpu)); diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index ec1f3cbf76..55d71306eb 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -177,9 +177,17 @@ solution_t mip_solver_t::run_solver() } CUOPT_LOG_INFO("Using %d CPU threads for B&B", branch_and_bound_settings.num_threads); - i_t num_threads = branch_and_bound_settings.num_threads; - i_t num_bfs_threads = std::max(1, num_threads / 4); - i_t num_diving_threads = std::max(1, num_threads - num_bfs_threads); + CUOPT_LOG_ERROR("AAAAAA\n"); + + i_t num_threads = branch_and_bound_settings.num_threads; + i_t num_bfs_threads = std::max(1, num_threads / 4); + i_t num_diving_threads = num_threads - num_bfs_threads; + // deterministic mode: no diving for now + if (context.settings.deterministic) { + num_threads = 1; + num_bfs_threads = 1; + num_diving_threads = 0; + } branch_and_bound_settings.num_bfs_threads = num_bfs_threads; branch_and_bound_settings.num_diving_threads = num_diving_threads; diff --git a/cpp/src/mip/solver_context.cuh b/cpp/src/mip/solver_context.cuh index 306fc0dc5e..b4df2578e9 100644 --- a/cpp/src/mip/solver_context.cuh +++ b/cpp/src/mip/solver_context.cuh @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -29,6 +30,7 @@ namespace cuopt::linear_programming::detail { struct mip_solver_work_unit_predictors_t { work_unit_predictor_t fj_predictor{}; work_unit_predictor_t cpufj_predictor{}; + work_unit_predictor_t pdlp_predictor{}; }; // Aggregate structure containing the global context of the solving process for convenience: diff --git a/cpp/src/utilities/models/dualsimplex_predictor/header.h b/cpp/src/utilities/models/dualsimplex_predictor/header.h new file mode 100644 index 0000000000..990607ebad --- /dev/null +++ b/cpp/src/utilities/models/dualsimplex_predictor/header.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +class dualsimplex_predictor { + public: + union Entry { + int missing; + float fvalue; + int qvalue; + }; + + static int32_t get_num_target(void); + static void get_num_class(int32_t* out); + static int32_t get_num_feature(void); + static const char* get_threshold_type(void); + static const char* get_leaf_output_type(void); + static void predict(union Entry* data, int pred_margin, double* result); + static void postprocess(double* result); + static int quantize(float val, unsigned fid); + + // Feature names + static constexpr int NUM_FEATURES = 18; + static const char* feature_names[NUM_FEATURES]; +}; // class dualsimplex_predictor diff --git a/cpp/src/utilities/models/dualsimplex_predictor/main.cpp b/cpp/src/utilities/models/dualsimplex_predictor/main.cpp new file mode 100644 index 0000000000..571527421c --- /dev/null +++ b/cpp/src/utilities/models/dualsimplex_predictor/main.cpp @@ -0,0 +1,19265 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "header.h" + +#if defined(__clang__) || defined(__GNUC__) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif +#define N_TARGET 1 +#define MAX_N_CLASS 1 + +const unsigned char is_categorical[] = { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; +static const int32_t num_class[] = { + 1, +}; + +int32_t dualsimplex_predictor::get_num_target(void) { return std::exp(N_TARGET); } +void dualsimplex_predictor::get_num_class(int32_t* out) +{ + for (int i = 0; i < N_TARGET; ++i) { + out[i] = num_class[i]; + } +} +int32_t dualsimplex_predictor::get_num_feature(void) { return std::exp(18); } +const char* dualsimplex_predictor::get_threshold_type(void) { return "float32"; } +const char* dualsimplex_predictor::get_leaf_output_type(void) { return "float32"; } + +void dualsimplex_predictor::predict(union Entry* data, int pred_margin, double* result) +{ + // Quantize data + for (int i = 0; i < 18; ++i) { + if (data[i].missing != -1 && !is_categorical[i]) { + data[i].qvalue = quantize(data[i].fvalue, i); + } + } + + unsigned int tmp; + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 30))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += -0.23892987; + } else { + result[0] += -0.41049096; + } + } else { + result[0] += -0.32070833; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { + result[0] += -0.3301402; + } else { + result[0] += -0.27258733; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 158))) { + result[0] += -0.2263252; + } else { + result[0] += -0.03232357; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 244))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += -0.21953094; + } else { + result[0] += -0.2782724; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 252))) { + result[0] += -0.10610497; + } else { + result[0] += -0.18065642; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 238))) { + result[0] += -0.14425437; + } else { + result[0] += -0.027287258; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { + result[0] += -0.06345942; + } else { + result[0] += -0.026511494; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { + result[0] += -0.20900369; + } else { + result[0] += -0.03617132; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += -0.17355898; + } else { + result[0] += -0.094911605; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 174))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { + result[0] += -0.064165816; + } else { + result[0] += -0.12049206; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { + result[0] += -0.11783655; + } else { + result[0] += 0.031279184; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 14))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + result[0] += -0.16023064; + } else { + result[0] += -0.01693966; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 92))) { + result[0] += -0.03719565; + } else { + result[0] += 0.002473806; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 180))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 194))) { + result[0] += 0.015176967; + } else { + result[0] += -0.033581402; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { + result[0] += 0.060070585; + } else { + result[0] += 0.102768324; + } + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 210))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 152))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + result[0] += -0.018653875; + } else { + result[0] += 0.21873346; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 158))) { + result[0] += 0.020794097; + } else { + result[0] += 0.0017005502; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 54))) { + result[0] += -0.15759692; + } else { + result[0] += 0.037903294; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + result[0] += 0.073502585; + } else { + result[0] += 0.1481389; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 72))) { + result[0] += -0.20302705; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { + result[0] += -0.0740808; + } else { + result[0] += -0.0013614334; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { + result[0] += 0.007832853; + } else { + result[0] += 0.05149386; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 286))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + result[0] += 0.038612913; + } else { + result[0] += -0.011268199; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { + result[0] += 0.12736425; + } else { + result[0] += 0.057369012; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 270))) { + result[0] += 0.09891705; + } else { + result[0] += 0.1404978; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 200))) { + result[0] += -0.09467173; + } else { + result[0] += 0.058953542; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 98))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 12))) { + result[0] += 0.24495018; + } else { + result[0] += 0.4303154; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += 0.23759733; + } else { + result[0] += 0.31459442; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { + result[0] += 0.18160371; + } else { + result[0] += 0.119219065; + } + } else { + result[0] += 0.2541432; + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += -0.13811207; + } else { + result[0] += -0.39534476; + } + } else { + result[0] += -0.3041533; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 20))) { + result[0] += -0.29433212; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 78))) { + result[0] += -0.19736992; + } else { + result[0] += -0.25767314; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += -0.19146764; + } else { + result[0] += -0.15695396; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { + result[0] += -0.23099203; + } else { + result[0] += -0.117888466; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 16))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { + result[0] += -0.1496231; + } else { + result[0] += -0.10690468; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 160))) { + result[0] += -0.027002404; + } else { + result[0] += -0.08140517; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { + result[0] += -0.18216138; + } else { + result[0] += -0.023672506; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { + result[0] += -0.12947556; + } else { + result[0] += -0.04930939; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { + result[0] += -0.10542603; + } else { + result[0] += -0.05690325; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 70))) { + result[0] += -0.11127614; + } else { + result[0] += 0.029446835; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 24))) { + result[0] += -0.07772863; + } else { + result[0] += -0.02781359; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 86))) { + result[0] += -0.13588855; + } else { + result[0] += -0.0629831; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 180))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 298))) { + result[0] += 0.008945032; + } else { + result[0] += -0.030025298; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { + result[0] += 0.053789187; + } else { + result[0] += 0.09317514; + } + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 172))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 150))) { + result[0] += -0.01571287; + } else { + result[0] += 0.01397138; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 22))) { + result[0] += -0.004348435; + } else { + result[0] += 0.28950748; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 62))) { + result[0] += 0.073582016; + } else { + result[0] += 0.15677379; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { + result[0] += -0.031289082; + } else { + result[0] += 0.06539955; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { + result[0] += -0.18994586; + } else { + result[0] += -0.066469155; + } + } else { + result[0] += 0.026545838; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 82))) { + result[0] += 0.0066967257; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 122))) { + result[0] += 0.04904222; + } else { + result[0] += 0.012652091; + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 308))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 54))) { + result[0] += 0.036342237; + } else { + result[0] += -0.0249609; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { + result[0] += 0.10429803; + } else { + result[0] += 0.3026218; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 252))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.19158468; + } else { + result[0] += -0.07063005; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 264))) { + result[0] += -0.019653698; + } else { + result[0] += 0.029620511; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 328))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 222))) { + result[0] += 0.16111112; + } else { + result[0] += 0.25347957; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 128))) { + result[0] += 0.084318936; + } else { + result[0] += 0.14822438; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 92))) { + result[0] += 0.22045271; + } else { + result[0] += 0.26758388; + } + } else { + result[0] += 0.46477053; + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 134))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += -0.1654226; + } else { + result[0] += -0.36575767; + } + } else { + result[0] += -0.28903162; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { + result[0] += -0.24255578; + } else { + result[0] += -0.27643654; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { + result[0] += -0.29263017; + } else { + result[0] += -0.20599626; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { + result[0] += -0.18999758; + } else { + result[0] += -0.15109059; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { + result[0] += -0.11279365; + } else { + result[0] += -0.027640833; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { + result[0] += -0.12278713; + } else { + result[0] += -0.029810125; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 100))) { + result[0] += -0.21111389; + } else { + result[0] += -0.13208562; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 146))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 78))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { + result[0] += -0.11854438; + } else { + result[0] += -0.17666526; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 64))) { + result[0] += -0.012537091; + } else { + result[0] += -0.081729345; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 80))) { + result[0] += -0.10848161; + } else { + result[0] += -0.06910487; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + result[0] += -0.048394956; + } else { + result[0] += -0.10724862; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 18))) { + result[0] += -0.05678125; + } else { + result[0] += -0.15847282; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { + result[0] += -0.02826718; + } else { + result[0] += 0.016257456; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 170))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 194))) { + result[0] += 0.009919471; + } else { + result[0] += -0.02828365; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { + result[0] += 0.048264306; + } else { + result[0] += 0.084261395; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 220))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 204))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 154))) { + result[0] += -0.015983945; + } else { + result[0] += 0.0104940515; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + result[0] += 0.059158504; + } else { + result[0] += -0.013891051; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 166))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += -0.05883024; + } else { + result[0] += -0.15169065; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 50))) { + result[0] += 0.017066706; + } else { + result[0] += 0.046345506; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 302))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + result[0] += 0.114555776; + } else { + result[0] += 0.40284094; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + result[0] += 0.027867997; + } else { + result[0] += -0.05320992; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 160))) { + result[0] += 0.02757929; + } else { + result[0] += 0.0005367231; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { + result[0] += 0.0925671; + } else { + result[0] += -0.04869043; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 54))) { + result[0] += 0.033666294; + } else { + result[0] += -0.019405624; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 138))) { + result[0] += 0.085760355; + } else { + result[0] += 0.11623438; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 136))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 78))) { + result[0] += -0.078748904; + } else { + result[0] += -0.03139619; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { + result[0] += 0.020764524; + } else { + result[0] += 0.15907182; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 156))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += 0.19595024; + } else { + result[0] += 0.25730672; + } + } else { + result[0] += 0.41746393; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 90))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 20))) { + result[0] += 0.18315254; + } else { + result[0] += 0.3391243; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { + result[0] += 0.1157012; + } else { + result[0] += 0.20929395; + } + } + } + } + } + } + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 108))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 74))) { + result[0] += -0.14199895; + } else { + result[0] += -0.0956778; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 140))) { + result[0] += -0.010430599; + } else { + result[0] += 0.019095603; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { + result[0] += -0.3010808; + } else { + result[0] += -0.23643827; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { + result[0] += -0.20573525; + } else { + result[0] += -0.02070453; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 204))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 68))) { + result[0] += -0.120953314; + } else { + result[0] += -0.075053215; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { + result[0] += 0.023893813; + } else { + result[0] += -0.05592366; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += -0.16090266; + } else { + result[0] += -0.22481023; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 86))) { + result[0] += -0.14591117; + } else { + result[0] += -0.081503354; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 124))) { + result[0] += 0.014306935; + } else { + result[0] += -0.15783915; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 156))) { + result[0] += -0.04105756; + } else { + result[0] += -0.014158033; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 142))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 82))) { + result[0] += -0.120835006; + } else { + result[0] += -0.073639594; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 60))) { + result[0] += -0.19654752; + } else { + result[0] += -0.14132342; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 192))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 26))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 76))) { + result[0] += 0.01790429; + } else { + result[0] += -0.00943239; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 238))) { + result[0] += 0.04468555; + } else { + result[0] += 0.011115446; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 62))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 270))) { + result[0] += 0.07178088; + } else { + result[0] += 0.011843091; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { + result[0] += 0.0443505; + } else { + result[0] += 0.14387575; + } + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 254))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 148))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { + result[0] += -0.04890412; + } else { + result[0] += -0.0113124; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 190))) { + result[0] += 0.015577379; + } else { + result[0] += -0.0047443947; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 300))) { + result[0] += 0.03677338; + } else { + result[0] += 0.08133157; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 58))) { + result[0] += 0.11096779; + } else { + result[0] += 0.15190493; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 186))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 70))) { + result[0] += 0.0974144; + } else { + result[0] += 0.012945512; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 228))) { + result[0] += -0.016480766; + } else { + result[0] += 0.020737674; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 176))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { + result[0] += -0.105966404; + } else { + result[0] += -0.01857656; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { + result[0] += -0.033622157; + } else { + result[0] += 0.042780038; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { + result[0] += -0.0662734; + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 140))) { + result[0] += 0.07991419; + } else { + result[0] += 0.039648328; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 172))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { + result[0] += 0.10550159; + } else { + result[0] += 0.03400041; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { + result[0] += 0.11472396; + } else { + result[0] += 0.1589642; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 8))) { + result[0] += 0.14915675; + } else { + result[0] += 0.3244665; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += 0.17384307; + } else { + result[0] += 0.23245731; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { + result[0] += 0.13902494; + } else { + result[0] += 0.07743486; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + result[0] += 0.19507462; + } else { + result[0] += 0.12790385; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 14))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += -0.12810235; + } else { + result[0] += -0.30201578; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { + result[0] += -0.25788355; + } else { + result[0] += -0.21340947; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { + result[0] += -0.21370807; + } else { + result[0] += -0.167573; + } + } else { + result[0] += -0.024952482; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 100))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { + result[0] += -0.171652; + } else { + result[0] += -0.12910992; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 110))) { + result[0] += -0.07442858; + } else { + result[0] += -0.12269157; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 12))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { + result[0] += -0.11061738; + } else { + result[0] += -0.06790245; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 160))) { + result[0] += -0.017483674; + } else { + result[0] += -0.059728812; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 146))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 82))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { + result[0] += -0.13760419; + } else { + result[0] += -0.01986711; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { + result[0] += -0.0773085; + } else { + result[0] += 0.004780628; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 62))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 144))) { + result[0] += -0.08369659; + } else { + result[0] += -0.0037746632; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + result[0] += -0.033646163; + } else { + result[0] += -0.09390003; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 64))) { + result[0] += -0.10188579; + } else { + result[0] += -0.022887342; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { + result[0] += -0.025158664; + } else { + result[0] += 0.011464008; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 170))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 204))) { + result[0] += 0.009676366; + } else { + result[0] += -0.025687149; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { + result[0] += 0.03910928; + } else { + result[0] += 0.06689557; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 220))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 204))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 80))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { + result[0] += 0.039443213; + } else { + result[0] += 0.20666161; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 128))) { + result[0] += -0.01548522; + } else { + result[0] += 0.015606319; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { + result[0] += -0.0018438485; + } else { + result[0] += -0.03514717; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { + result[0] += -0.029947493; + } else { + result[0] += -0.056570943; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 302))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + result[0] += 0.15045454; + } else { + result[0] += 0.01461363; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + result[0] += 0.031331427; + } else { + result[0] += 0.2708533; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += -0.044682816; + } else { + result[0] += 0.019505082; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 278))) { + result[0] += 0.10829522; + } else { + result[0] += 0.07073908; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + result[0] += 0.027972206; + } else { + result[0] += 0.005189055; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { + result[0] += -0.066226505; + } else { + result[0] += 0.0877003; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 148))) { + result[0] += 0.055681467; + } else { + result[0] += -0.06665366; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 280))) { + result[0] += 0.0809778; + } else { + result[0] += 0.1468879; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { + result[0] += 0.15474413; + } else { + result[0] += 0.20727856; + } + } else { + result[0] += 0.35305986; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 158))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { + result[0] += 0.07976506; + } else { + result[0] += 0.1597683; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + result[0] += 0.13799594; + } else { + result[0] += 0.23070168; + } + } + } + } + } + } + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 104))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.24169908; + } else { + result[0] += -0.107800476; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { + result[0] += 0.0015814465; + } else { + result[0] += -0.14862476; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 82))) { + result[0] += -0.2477706; + } else { + result[0] += -0.1950383; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { + result[0] += -0.17119522; + } else { + result[0] += -0.045236852; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 178))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { + result[0] += 0.025135875; + } else { + result[0] += -0.08694006; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { + result[0] += 0.029477531; + } else { + result[0] += -0.04847209; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 116))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + result[0] += 0.011227789; + } else { + result[0] += -0.09084964; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 82))) { + result[0] += -0.13153963; + } else { + result[0] += -0.18369597; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 152))) { + result[0] += -0.061374586; + } else { + result[0] += -0.111760534; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 276))) { + result[0] += -0.03116385; + } else { + result[0] += -0.085914634; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 142))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + result[0] += 0.0005968995; + } else { + result[0] += -0.08622521; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 70))) { + result[0] += -0.17008883; + } else { + result[0] += -0.11829443; + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 34))) { + result[0] += 0.0151279615; + } else { + result[0] += 0.037952267; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 58))) { + result[0] += 0.059345204; + } else { + result[0] += 0.11877208; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 206))) { + result[0] += -0.12507677; + } else { + result[0] += -0.025324045; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 102))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 246))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + result[0] += 0.044818092; + } else { + result[0] += -0.01875449; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 148))) { + result[0] += -0.11071397; + } else { + result[0] += -0.059991468; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { + result[0] += 0.049274206; + } else { + result[0] += 0.012975462; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { + result[0] += -0.0189122; + } else { + result[0] += 0.060573917; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 148))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + result[0] += 0.02068209; + } else { + result[0] += -0.027593452; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 60))) { + result[0] += -0.008190083; + } else { + result[0] += 0.01192194; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 268))) { + result[0] += 0.02700766; + } else { + result[0] += 0.07031421; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 58))) { + result[0] += 0.09329746; + } else { + result[0] += 0.12738243; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 336))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { + result[0] += 0.04464999; + } else { + result[0] += 0.0775819; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 62))) { + result[0] += -0.05400176; + } else { + result[0] += 0.0332122; + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 60))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { + result[0] += 0.06202929; + } else { + result[0] += 0.0011356722; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + result[0] += 0.093274996; + } else { + result[0] += 0.12987015; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 124))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { + result[0] += 0.15837678; + } else { + result[0] += 0.3200806; + } + } else { + result[0] += 0.34475276; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { + result[0] += 0.09141225; + } else { + result[0] += 0.15360029; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += 0.14422126; + } else { + result[0] += 0.18972561; + } + } + } + } + } + } + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 106))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 102))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 38))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { + result[0] += -0.22463696; + } else { + result[0] += -0.17236648; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { + result[0] += -0.15470378; + } else { + result[0] += -0.014106827; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 80))) { + result[0] += -0.104941204; + } else { + result[0] += -0.06797316; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { + result[0] += 0.0034419482; + } else { + result[0] += -0.04432465; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 220))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 66))) { + result[0] += -0.09611558; + } else { + result[0] += -0.05750701; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { + result[0] += 0.02356928; + } else { + result[0] += -0.045556568; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { + result[0] += -0.12279941; + } else { + result[0] += -0.16899776; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 124))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { + result[0] += -0.017295085; + } else { + result[0] += 0.031519514; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 156))) { + result[0] += -0.16593443; + } else { + result[0] += -0.110011; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 112))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { + result[0] += -0.010664171; + } else { + result[0] += -0.032727893; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 142))) { + result[0] += -0.07378478; + } else { + result[0] += -0.13850205; + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 134))) { + result[0] += 0.007132952; + } else { + result[0] += 0.033364255; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 62))) { + result[0] += 0.053948194; + } else { + result[0] += 0.105476275; + } + } + } else { + result[0] += -0.07528159; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 254))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 154))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + result[0] += 0.017853899; + } else { + result[0] += -0.021608775; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 190))) { + result[0] += 0.012284887; + } else { + result[0] += -0.0040449556; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + result[0] += 0.027741803; + } else { + result[0] += 0.063282035; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 56))) { + result[0] += 0.08446597; + } else { + result[0] += 0.116814755; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 200))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 70))) { + result[0] += 0.0801467; + } else { + result[0] += 0.00028896204; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 206))) { + result[0] += -0.0006912606; + } else { + result[0] += 0.038264047; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 152))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { + result[0] += -0.09579014; + } else { + result[0] += -0.011496059; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 124))) { + result[0] += 0.028059239; + } else { + result[0] += -0.029746488; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 332))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 60))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { + result[0] += 0.04334253; + } else { + result[0] += 0.094181366; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { + result[0] += 0.010113914; + } else { + result[0] += 0.12312211; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 12))) { + result[0] += 0.008063223; + } else { + result[0] += 0.05981284; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 44))) { + result[0] += 0.055709254; + } else { + result[0] += 0.102501765; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 126))) { + result[0] += 0.19393599; + } else { + result[0] += 0.31415454; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += 0.12953043; + } else { + result[0] += 0.17184684; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 152))) { + result[0] += 0.105829634; + } else { + result[0] += 0.05405576; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 342))) { + result[0] += 0.11501894; + } else { + result[0] += 0.14885037; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 142))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { + result[0] += -0.22360039; + } else { + result[0] += -0.16961168; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 62))) { + result[0] += -0.17449044; + } else { + result[0] += -0.13112317; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { + result[0] += -0.19438948; + } else { + result[0] += -0.07163301; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 58))) { + result[0] += -0.12211982; + } else { + result[0] += -0.1598212; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 104))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { + result[0] += -0.1360238; + } else { + result[0] += -0.08778509; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 172))) { + result[0] += -0.058016848; + } else { + result[0] += -0.01712372; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += -0.10796758; + } else { + result[0] += -0.1462996; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { + result[0] += -0.026550526; + } else { + result[0] += -0.09057524; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { + result[0] += -0.1045504; + } else { + result[0] += -0.06835998; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + result[0] += -0.16272134; + } else { + result[0] += -0.10771642; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 236))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { + result[0] += -0.1004568; + } else { + result[0] += -0.02189359; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 12))) { + result[0] += -0.11555834; + } else { + result[0] += -0.064784154; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 110))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 78))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { + result[0] += -0.09062075; + } else { + result[0] += -0.01616345; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 144))) { + result[0] += -0.046594307; + } else { + result[0] += -0.00848129; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 220))) { + result[0] += 0.048658125; + } else { + result[0] += 0.015779605; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += -0.003939248; + } else { + result[0] += -0.031186854; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 222))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 254))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 154))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + result[0] += 0.0046192957; + } else { + result[0] += 0.02478583; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + result[0] += 0.24694644; + } else { + result[0] += -0.104915835; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + result[0] += -0.0028355815; + } else { + result[0] += -0.03066038; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { + result[0] += -0.038436286; + } else { + result[0] += 0.03750279; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 78))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 186))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 318))) { + result[0] += -0.012060843; + } else { + result[0] += -0.0011414163; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 314))) { + result[0] += -0.008850611; + } else { + result[0] += 0.015718905; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 262))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { + result[0] += 0.05882001; + } else { + result[0] += 0.036198247; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 184))) { + result[0] += -0.065134026; + } else { + result[0] += -0.01131267; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 122))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { + result[0] += 0.2215176; + } else { + result[0] += -0.10633468; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + result[0] += 0.1440095; + } else { + result[0] += 0.051038798; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 254))) { + result[0] += -0.014680609; + } else { + result[0] += 0.014171252; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 348))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 224))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 94))) { + result[0] += 0.056311846; + } else { + result[0] += 0.1058489; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 362))) { + result[0] += 0.037274715; + } else { + result[0] += 0.11873694; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 34))) { + result[0] += 0.11126106; + } else { + result[0] += 0.14717433; + } + } else { + result[0] += 0.26775053; + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 134))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + result[0] += -0.20885597; + } else { + result[0] += -0.16570954; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += -0.13637789; + } else { + result[0] += -0.1756009; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { + result[0] += -0.21495152; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { + result[0] += -0.15078692; + } else { + result[0] += -0.11473024; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + result[0] += -0.08377748; + } else { + result[0] += -0.020055672; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + result[0] += -0.12106979; + } else { + result[0] += -0.092178635; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 16))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { + result[0] += -0.07951072; + } else { + result[0] += -0.04535841; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 52))) { + result[0] += 0.015479415; + } else { + result[0] += -0.034244347; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 202))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { + result[0] += -0.10090798; + } else { + result[0] += -0.072621964; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + result[0] += -0.08769948; + } else { + result[0] += -0.020480268; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 52))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 158))) { + result[0] += -0.06127919; + } else { + result[0] += 0.0065026283; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { + result[0] += -0.06342202; + } else { + result[0] += -0.021188881; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 72))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 100))) { + result[0] += -0.04954914; + } else { + result[0] += 0.00018559814; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 44))) { + result[0] += 0.011565405; + } else { + result[0] += -0.011750548; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 146))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { + result[0] += 0.075431995; + } else { + result[0] += -0.005443739; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 62))) { + result[0] += 0.025297; + } else { + result[0] += 0.044371624; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 234))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 206))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 112))) { + result[0] += -0.012151074; + } else { + result[0] += 0.0095630875; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.19431071; + } else { + result[0] += 0.039510164; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + result[0] += -0.002731852; + } else { + result[0] += -0.02748406; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { + result[0] += -0.039663028; + } else { + result[0] += -0.002819218; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 294))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + result[0] += 0.012211016; + } else { + result[0] += 0.04506604; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { + result[0] += -8.271459e-05; + } else { + result[0] += -0.031217247; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 10))) { + result[0] += -0.027022822; + } else { + result[0] += 0.015010217; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + result[0] += 0.08849927; + } else { + result[0] += 0.04585655; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 330))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + result[0] += 0.015790751; + } else { + result[0] += 0.0709383; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 302))) { + result[0] += 0.014195338; + } else { + result[0] += 0.043147992; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 306))) { + result[0] += 0.027121753; + } else { + result[0] += 0.06687878; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { + result[0] += 0.06856555; + } else { + result[0] += 0.11084541; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 12))) { + result[0] += 0.023235757; + } else { + result[0] += 0.12572078; + } + } else { + result[0] += 0.24674617; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 362))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { + result[0] += 0.08457372; + } else { + result[0] += 0.04069808; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { + result[0] += 0.11791612; + } else { + result[0] += 0.07502523; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 142))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += -0.054601975; + } else { + result[0] += -0.17477903; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 48))) { + result[0] += -0.00046666022; + } else { + result[0] += -0.13438587; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 8))) { + result[0] += -0.07183619; + } else { + result[0] += -0.17046754; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { + result[0] += -0.10375444; + } else { + result[0] += -0.14129147; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + result[0] += -0.07520628; + } else { + result[0] += -0.01688747; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 14))) { + result[0] += -0.11755419; + } else { + result[0] += -0.08725922; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 28))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 2))) { + result[0] += -0.06106487; + } else { + result[0] += -0.09542205; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 202))) { + result[0] += 0.008344304; + } else { + result[0] += -0.03182082; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 64))) { + result[0] += -0.061504174; + } else { + result[0] += -0.13946581; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + result[0] += -0.13620088; + } else { + result[0] += -0.08506644; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 236))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 78))) { + result[0] += -0.10902418; + } else { + result[0] += -0.016639745; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 244))) { + result[0] += -0.10518378; + } else { + result[0] += -0.05821876; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { + result[0] += -0.06888045; + } else { + result[0] += -0.0376487; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 88))) { + result[0] += -0.012150378; + } else { + result[0] += 0.015397436; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 216))) { + result[0] += 0.039432753; + } else { + result[0] += 0.013889983; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += -0.0010314513; + } else { + result[0] += -0.02555494; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 232))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 206))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 200))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 80))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { + result[0] += 0.030333001; + } else { + result[0] += 0.17310323; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { + result[0] += 0.02483304; + } else { + result[0] += 0.0021163383; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { + result[0] += -0.017784828; + } else { + result[0] += -0.039406788; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 234))) { + result[0] += -0.0026142828; + } else { + result[0] += 0.03959627; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 284))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { + result[0] += 0.019107591; + } else { + result[0] += -0.011726064; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { + result[0] += 0.096682; + } else { + result[0] += 0.22430603; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + result[0] += 0.012120399; + } else { + result[0] += -0.03992904; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + result[0] += 0.06968047; + } else { + result[0] += 0.04038763; + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 304))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + result[0] += 0.11028786; + } else { + result[0] += 0.040963266; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + result[0] += 0.19369291; + } else { + result[0] += 0.0747213; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 78))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += -0.057252772; + } else { + result[0] += -0.14311497; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 232))) { + result[0] += -0.0102287065; + } else { + result[0] += 0.016080577; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 330))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { + result[0] += 0.07653522; + } else { + result[0] += 0.13459362; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 130))) { + result[0] += 0.03455645; + } else { + result[0] += 0.06964803; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += 0.09500605; + } else { + result[0] += 0.12430712; + } + } else { + result[0] += 0.22509204; + } + } + } + } + } + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 74))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 20))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 48))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += -0.16930752; + } else { + result[0] += -0.1269749; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += -0.06503186; + } else { + result[0] += -0.12498716; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { + result[0] += -0.1927099; + } else { + result[0] += -0.08201694; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 38))) { + result[0] += -0.068333164; + } else { + result[0] += -0.13142657; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 12))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { + result[0] += 0.042084176; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.14493789; + } else { + result[0] += -0.09196159; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 98))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { + result[0] += 0.03108189; + } else { + result[0] += -0.067129865; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + result[0] += 0.0054919203; + } else { + result[0] += -0.05392629; + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 144))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 98))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { + result[0] += -0.21116984; + } else { + result[0] += -0.0651527; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { + result[0] += 0.116855875; + } else { + result[0] += 0.050092816; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 148))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 20))) { + result[0] += -0.08852083; + } else { + result[0] += -0.04572432; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 270))) { + result[0] += -0.0053127306; + } else { + result[0] += 0.024463532; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 32))) { + result[0] += -0.22384882; + } else { + result[0] += -0.093135744; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 16))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 74))) { + result[0] += -0.031588506; + } else { + result[0] += -0.08342363; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 116))) { + result[0] += 0.012553929; + } else { + result[0] += 0.058796555; + } + } + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 146))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 300))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 46))) { + result[0] += 0.08138124; + } else { + result[0] += 0.13923828; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 202))) { + result[0] += 0.021075254; + } else { + result[0] += 0.064726695; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 98))) { + result[0] += 0.016024219; + } else { + result[0] += -0.014299775; + } + } else { + result[0] += -0.04494404; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 82))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + result[0] += -0.03340462; + } else { + result[0] += -0.002975365; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { + result[0] += -0.13237435; + } else { + result[0] += -0.098908305; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + result[0] += -0.019236477; + } else { + result[0] += -0.033017647; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { + result[0] += 0.0251749; + } else { + result[0] += 0.0012758941; + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + result[0] += 0.016069753; + } else { + result[0] += -0.010248724; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + result[0] += -0.028139247; + } else { + result[0] += -0.06801506; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 238))) { + result[0] += 0.031309973; + } else { + result[0] += 0.07697828; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 258))) { + result[0] += 0.03671503; + } else { + result[0] += 0.0007923256; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 282))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { + result[0] += 0.02349461; + } else { + result[0] += -0.00087367493; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { + result[0] += 0.03977532; + } else { + result[0] += 0.096183844; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { + result[0] += -0.08533253; + } else { + result[0] += -0.06052809; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 238))) { + result[0] += -0.055827565; + } else { + result[0] += -0.0045159357; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 152))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { + result[0] += -0.17435847; + } else { + result[0] += -0.09712316; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 88))) { + result[0] += -0.121227026; + } else { + result[0] += -0.09049445; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 90))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 148))) { + result[0] += -0.07094587; + } else { + result[0] += -0.13034694; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + result[0] += -0.0074528246; + } else { + result[0] += -0.1171114; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + result[0] += -0.03242543; + } else { + result[0] += -0.09674471; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 136))) { + result[0] += -0.060467966; + } else { + result[0] += -0.08785518; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 28))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 54))) { + result[0] += -0.047304705; + } else { + result[0] += -0.070193194; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 238))) { + result[0] += -0.027161488; + } else { + result[0] += 0.0049651144; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 124))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + result[0] += -0.05672009; + } else { + result[0] += -0.133577; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { + result[0] += -0.034619935; + } else { + result[0] += -0.074495845; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { + result[0] += -0.016871883; + } else { + result[0] += 0.0060241153; + } + } else { + result[0] += -0.07992915; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 256))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { + result[0] += -0.059571445; + } else { + result[0] += -0.026049837; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 164))) { + result[0] += 0.0035159283; + } else { + result[0] += -0.047432255; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { + result[0] += -0.009207371; + } else { + result[0] += 0.026254697; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 28))) { + result[0] += -0.083354376; + } else { + result[0] += -0.025358835; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 234))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { + result[0] += 0.004427155; + } else { + result[0] += 0.030195776; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + result[0] += 0.043274168; + } else { + result[0] += 0.0072428077; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 92))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 164))) { + result[0] += -0.052554864; + } else { + result[0] += 0.0033617232; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { + result[0] += -0.037242856; + } else { + result[0] += -0.008167746; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 112))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.094510235; + } else { + result[0] += 0.01284566; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 26))) { + result[0] += 0.07989215; + } else { + result[0] += 0.17329922; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 76))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += 0.058576096; + } else { + result[0] += -0.055972952; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + result[0] += 0.21819083; + } else { + result[0] += 0.09395635; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 130))) { + result[0] += 0.01516776; + } else { + result[0] += 0.033623938; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 106))) { + result[0] += 0.043214235; + } else { + result[0] += 0.119155124; + } + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 38))) { + result[0] += 0.09584941; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + result[0] += 0.080481224; + } else { + result[0] += 0.03998689; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 318))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 126))) { + result[0] += 0.026821358; + } else { + result[0] += -0.031004164; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 202))) { + result[0] += 0.06501947; + } else { + result[0] += 0.19631971; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { + result[0] += 0.07131386; + } else { + result[0] += 0.028573189; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 314))) { + result[0] += 0.083791435; + } else { + result[0] += 0.17529456; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 124))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 22))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { + result[0] += -0.01719068; + } else { + result[0] += -0.064043045; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { + result[0] += -0.1791604; + } else { + result[0] += -0.13283774; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { + result[0] += -0.056413215; + } else { + result[0] += -0.14871566; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { + result[0] += -0.09970745; + } else { + result[0] += -0.010530283; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 34))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { + result[0] += -0.019443454; + } else { + result[0] += -0.09627802; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { + result[0] += -0.072033755; + } else { + result[0] += -0.029162338; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { + result[0] += -0.04072467; + } else { + result[0] += -0.07095223; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { + result[0] += 9.782781e-05; + } else { + result[0] += -0.028554544; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += -0.10857904; + } else { + result[0] += -0.17578779; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 148))) { + result[0] += -0.05417463; + } else { + result[0] += -0.08070096; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 300))) { + result[0] += -0.06944825; + } else { + result[0] += -0.0195438; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 96))) { + result[0] += -0.032838166; + } else { + result[0] += -0.013822776; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 298))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 232))) { + result[0] += -0.034293767; + } else { + result[0] += -0.010428538; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 38))) { + result[0] += -0.008245598; + } else { + result[0] += 0.0061710696; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { + result[0] += -0.00058508094; + } else { + result[0] += -0.048794635; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { + result[0] += 0.014517671; + } else { + result[0] += 0.029143566; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 210))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 162))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 10))) { + result[0] += -0.051883638; + } else { + result[0] += -0.121891774; + } + } else { + result[0] += 0.006774173; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { + result[0] += -0.05183841; + } else { + result[0] += -0.023333719; + } + } else { + result[0] += 0.041755553; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 128))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += -0.021229211; + } else { + result[0] += 0.023228422; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 76))) { + result[0] += 0.0065774354; + } else { + result[0] += -0.0068485746; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 28))) { + result[0] += 0.049541216; + } else { + result[0] += 0.21175821; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 10))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 292))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.1843134; + } else { + result[0] += 0.011620779; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 280))) { + result[0] += -0.049533408; + } else { + result[0] += 0.019996347; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 272))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + result[0] += 0.07229465; + } else { + result[0] += 0.019160887; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + result[0] += 0.0056915763; + } else { + result[0] += 0.04898624; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 328))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 56))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 124))) { + result[0] += 0.010618052; + } else { + result[0] += 0.039828878; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + result[0] += 0.08413791; + } else { + result[0] += 0.05419789; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 128))) { + result[0] += 0.04146382; + } else { + result[0] += 0.08484615; + } + } else { + result[0] += 0.17491151; + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 146))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 30))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { + result[0] += -0.14357229; + } else { + result[0] += -0.06613964; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + result[0] += -0.07836775; + } else { + result[0] += 0.008682779; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 20))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { + result[0] += -0.085223936; + } else { + result[0] += -0.10550176; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 84))) { + result[0] += -0.04918786; + } else { + result[0] += -0.0712194; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { + result[0] += -0.0485091; + } else { + result[0] += -0.07085284; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 154))) { + result[0] += -0.04310399; + } else { + result[0] += -0; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 168))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { + result[0] += -0.03786544; + } else { + result[0] += -0.06086583; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 24))) { + result[0] += -0.026798641; + } else { + result[0] += -0.0029160806; + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 66))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { + result[0] += -0.041853126; + } else { + result[0] += -0.067471944; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.10217434; + } else { + result[0] += -0.031203955; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { + result[0] += -0.020556139; + } else { + result[0] += 0.0036511559; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { + result[0] += -0.0052104336; + } else { + result[0] += 0.022399493; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 266))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { + result[0] += -0.050368812; + } else { + result[0] += -0.015018652; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 98))) { + result[0] += 0.050732918; + } else { + result[0] += -0.017873917; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { + result[0] += 0.16741006; + } else { + result[0] += 0.08613744; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { + result[0] += 0.02335409; + } else { + result[0] += -0.0018855215; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 234))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 200))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 172))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + result[0] += 0.0073416415; + } else { + result[0] += 0.029692546; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 146))) { + result[0] += -0.0061891414; + } else { + result[0] += -0.021139516; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 378))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 224))) { + result[0] += -0.018264985; + } else { + result[0] += -0.0061430032; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + result[0] += 0.024317574; + } else { + result[0] += -0.023863515; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 294))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 90))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.06744468; + } else { + result[0] += 0.2128239; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + result[0] += 0.013560965; + } else { + result[0] += -0.010385789; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 156))) { + result[0] += 0.036912605; + } else { + result[0] += 0.008162293; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { + result[0] += 0.07527434; + } else { + result[0] += 0.013765368; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 362))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 74))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 132))) { + result[0] += -0.040987097; + } else { + result[0] += 0.009021326; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { + result[0] += 0.026009029; + } else { + result[0] += 0.08366104; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 34))) { + result[0] += 0.064916424; + } else { + result[0] += 0.09441544; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 4))) { + result[0] += 0.08905909; + } else { + result[0] += 0.020877834; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 318))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 126))) { + result[0] += 0.02257039; + } else { + result[0] += -0.025986714; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 202))) { + result[0] += 0.05338145; + } else { + result[0] += 0.15790914; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 220))) { + result[0] += 0.018759893; + } else { + result[0] += 0.056597423; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 318))) { + result[0] += 0.06810229; + } else { + result[0] += 0.16054772; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 156))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { + result[0] += -0.06675883; + } else { + result[0] += -0.04909338; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 80))) { + result[0] += -0.045537602; + } else { + result[0] += -0.021407653; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 102))) { + result[0] += -0.036747407; + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 38))) { + result[0] += -0.0055241673; + } else { + result[0] += 0.0064017037; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { + result[0] += -0.1232142; + } else { + result[0] += -0.08944999; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + result[0] += -0.103527024; + } else { + result[0] += -0.06685146; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 8))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 110))) { + result[0] += -0.087132424; + } else { + result[0] += -0.0155411465; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { + result[0] += 0.007360445; + } else { + result[0] += -0.042419024; + } + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 104))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 102))) { + result[0] += -0.02354617; + } else { + result[0] += 0.0067315414; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { + result[0] += -0.02450421; + } else { + result[0] += -0.058597732; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 32))) { + result[0] += 0.023825407; + } else { + result[0] += 0.18043439; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 8))) { + result[0] += -0.043022137; + } else { + result[0] += 0.029165251; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 222))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 208))) { + result[0] += -0.07280388; + } else { + result[0] += -0.046989717; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += 0.016371334; + } else { + result[0] += -0.0062531256; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { + result[0] += -0.00938953; + } else { + result[0] += 0.011327556; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 98))) { + result[0] += -0.057005074; + } else { + result[0] += -0.011778428; + } + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 268))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 264))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 46))) { + result[0] += 0.030093227; + } else { + result[0] += 0.11276569; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + result[0] += 0.0048071328; + } else { + result[0] += -0.030283172; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { + result[0] += 0.07801795; + } else { + result[0] += 0.010139103; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { + result[0] += 0.015687512; + } else { + result[0] += 0.057320017; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 128))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 162))) { + result[0] += 0.16135572; + } else { + result[0] += 0.10743537; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { + result[0] += 0.0299298; + } else { + result[0] += -0.025237197; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 62))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { + result[0] += 0.01356712; + } else { + result[0] += 0.027944017; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 284))) { + result[0] += 0.09368516; + } else { + result[0] += 0.00735568; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 212))) { + result[0] += 0.06613142; + } else { + result[0] += 0.030379802; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 296))) { + result[0] += 0.037492864; + } else { + result[0] += 0.08968493; + } + } + } else { + result[0] += 0.14285138; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 240))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 122))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 294))) { + result[0] += 0.10012319; + } else { + result[0] += 0.04607804; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { + result[0] += 0.00882749; + } else { + result[0] += 0.034421023; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 188))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { + result[0] += 0.020126143; + } else { + result[0] += 0.056154665; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 344))) { + result[0] += 0.06664233; + } else { + result[0] += 0.028045407; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 124))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { + result[0] += -0.08264944; + } else { + result[0] += -0.017300455; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 132))) { + result[0] += -0.09584019; + } else { + result[0] += -0.03871246; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 8))) { + result[0] += -0.027366564; + } else { + result[0] += 0.005001399; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { + result[0] += -0.015287772; + } else { + result[0] += -0.06076094; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + result[0] += -0.01928839; + } else { + result[0] += -0.05802682; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 14))) { + result[0] += -0.069114976; + } else { + result[0] += -0.050886452; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 168))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 58))) { + result[0] += -0.02828979; + } else { + result[0] += -0.049417857; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 54))) { + result[0] += 0.018446451; + } else { + result[0] += -0.017286932; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { + result[0] += -0; + } else { + result[0] += -0.1095063; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + result[0] += -0.112913184; + } else { + result[0] += -0.041024093; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 236))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { + result[0] += -0.036767628; + } else { + result[0] += -0.0056491303; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 50))) { + result[0] += -0.05497813; + } else { + result[0] += -0.02247507; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 124))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 94))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { + result[0] += -0.04725503; + } else { + result[0] += -0.022799945; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + result[0] += -0.03265686; + } else { + result[0] += 0.0007802085; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 54))) { + result[0] += 0.008465467; + } else { + result[0] += 0.022264598; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { + result[0] += -0.008557126; + } else { + result[0] += -0.050682377; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 230))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 56))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 84))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { + result[0] += -0.23723964; + } else { + result[0] += -0.0674392; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 96))) { + result[0] += 0.017011134; + } else { + result[0] += -0.023735067; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 156))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { + result[0] += -0.021249495; + } else { + result[0] += 0.0016016475; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { + result[0] += 0.004315733; + } else { + result[0] += 0.029064924; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 30))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 18))) { + result[0] += 0.016252542; + } else { + result[0] += 0.07114914; + } + } else { + result[0] += 0.18867145; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 4))) { + result[0] += -0.043993976; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 52))) { + result[0] += 0.04012975; + } else { + result[0] += 0.0784916; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 68))) { + result[0] += -0.1251441; + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 124))) { + result[0] += 0.1326; + } else { + result[0] += 0.07419976; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + result[0] += 0.070240654; + } else { + result[0] += 0.0142424395; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + result[0] += 0.008635036; + } else { + result[0] += 0.05565952; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 104))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 326))) { + result[0] += 0.0021902567; + } else { + result[0] += 0.12992251; + } + } else { + result[0] += 0.14213897; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 342))) { + result[0] += 0.018112415; + } else { + result[0] += 0.048771314; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { + result[0] += 0.040432267; + } else { + result[0] += 0.06690516; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 158))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 4))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { + result[0] += -0.084643945; + } else { + result[0] += -0.05658353; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { + result[0] += -0.14270312; + } else { + result[0] += -0.096725866; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 10))) { + result[0] += -0.072900295; + } else { + result[0] += -0.050103154; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { + result[0] += 0.0026472413; + } else { + result[0] += -0.034194175; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 210))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 172))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + result[0] += -0.08699354; + } else { + result[0] += -0.029962752; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 258))) { + result[0] += -0.012024823; + } else { + result[0] += -0.040718153; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 38))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 278))) { + result[0] += 1.4413594e-05; + } else { + result[0] += -0.01927815; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 52))) { + result[0] += -0.004854994; + } else { + result[0] += 0.0106780855; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 88))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 28))) { + result[0] += -0.07230939; + } else { + result[0] += -0.029629577; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { + result[0] += -0.018495014; + } else { + result[0] += 0.019453213; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 90))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + result[0] += -0.01461862; + } else { + result[0] += 0.10096019; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + result[0] += -0.029421015; + } else { + result[0] += 9.543482e-05; + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 90))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 60))) { + result[0] += -0.021362139; + } else { + result[0] += -0.063853346; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 62))) { + result[0] += 0.00073504547; + } else { + result[0] += 0.019957332; + } + } + } else { + result[0] += 0.09791734; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 294))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 60))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 18))) { + result[0] += -0.005831562; + } else { + result[0] += -0.06499735; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 132))) { + result[0] += -0.0071320483; + } else { + result[0] += -0.055945735; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 58))) { + result[0] += 0.0037681882; + } else { + result[0] += 0.05542149; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 82))) { + result[0] += 0.011588092; + } else { + result[0] += -0.0017960105; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { + result[0] += -0.16209017; + } else { + result[0] += 0.019222626; + } + } else { + result[0] += 0.13090767; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 270))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 14))) { + result[0] += -0.030162899; + } else { + result[0] += -0.008859159; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 184))) { + result[0] += 0.028086677; + } else { + result[0] += 0.0010727844; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + result[0] += 0.07769736; + } else { + result[0] += 0.114956; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 62))) { + result[0] += 0.026280258; + } else { + result[0] += 0.063517205; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 198))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 184))) { + result[0] += 0.028841827; + } else { + result[0] += 0.07754768; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 220))) { + result[0] += 0.023495244; + } else { + result[0] += 0.058550116; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 230))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { + result[0] += 0.006005039; + } else { + result[0] += 0.0330562; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 182))) { + result[0] += 0.02288873; + } else { + result[0] += 0.052012473; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 92))) { + result[0] += -0.023153892; + } else { + result[0] += 0.06317775; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 122))) { + result[0] += 0.09178891; + } else { + result[0] += 0.17105517; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 162))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { + result[0] += -0.051588424; + } else { + result[0] += -0.03475411; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { + result[0] += -0.0234789; + } else { + result[0] += 0.0038708851; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 214))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + result[0] += -0.008086841; + } else { + result[0] += -0.027431283; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 68))) { + result[0] += -0.001538304; + } else { + result[0] += 0.0073204334; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { + result[0] += -0.05180776; + } else { + result[0] += 0.005863587; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { + result[0] += -0.09820559; + } else { + result[0] += -0.042626407; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { + result[0] += -0.048510984; + } else { + result[0] += -0.071686305; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 58))) { + result[0] += -0.04765085; + } else { + result[0] += -0.010419096; + } + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 28))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { + result[0] += 0.06322976; + } else { + result[0] += -0.03347231; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 12))) { + result[0] += -0.17174184; + } else { + result[0] += -0.08866473; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { + result[0] += -0.019928426; + } else { + result[0] += -0.07354166; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { + result[0] += -0.020242494; + } else { + result[0] += -0.040445287; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + result[0] += -0.008752908; + } else { + result[0] += 0.09380058; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 176))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 198))) { + result[0] += 0.017689208; + } else { + result[0] += -0.0005958941; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 2))) { + result[0] += -0.0046095774; + } else { + result[0] += -0.038875457; + } + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 272))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { + result[0] += -0.046054542; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 132))) { + result[0] += -0.21343382; + } else { + result[0] += -0.087919936; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 50))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + result[0] += 0.012674006; + } else { + result[0] += 0.11185076; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { + result[0] += 0.041192535; + } else { + result[0] += 0.070588894; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { + result[0] += 0.0028114773; + } else { + result[0] += 0.019441808; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { + result[0] += 0.011045033; + } else { + result[0] += -0.022086669; + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 306))) { + result[0] += 0.071599804; + } else { + result[0] += 0.03955371; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 130))) { + result[0] += 0.010970717; + } else { + result[0] += 0.03211321; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 334))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 68))) { + result[0] += 0.059979778; + } else { + result[0] += 0.036047976; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { + result[0] += 0.10536631; + } else { + result[0] += 0.024781441; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 322))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 254))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 210))) { + result[0] += -0.052476525; + } else { + result[0] += -0.019687535; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + result[0] += 0.012497628; + } else { + result[0] += -0.011794323; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 362))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 120))) { + result[0] += 0.013189244; + } else { + result[0] += 0.03505987; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { + result[0] += 0.056928415; + } else { + result[0] += 0.022988325; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 126))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 22))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { + result[0] += -0.058271427; + } else { + result[0] += 0.0006807263; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { + result[0] += -0.10462822; + } else { + result[0] += -0.068169676; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 12))) { + result[0] += -0.0072206096; + } else { + result[0] += 0.0307555; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 50))) { + result[0] += -0.050685406; + } else { + result[0] += -0.023581887; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 296))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + result[0] += -0.005921537; + } else { + result[0] += -0.042507444; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += -0.052036572; + } else { + result[0] += -0.034811404; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + result[0] += 0.0008109753; + } else { + result[0] += -0.023712752; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { + result[0] += -0.081094466; + } else { + result[0] += -0.017712194; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { + result[0] += -0.09813706; + } else { + result[0] += -0.03229853; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 236))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 82))) { + result[0] += -0.028393775; + } else { + result[0] += -0.0027526347; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { + result[0] += -0.047838878; + } else { + result[0] += -0.026909484; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 18))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 12))) { + result[0] += -0.13414939; + } else { + result[0] += -0.05853174; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 8))) { + result[0] += -0.08385962; + } else { + result[0] += -0.029232157; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 112))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 94))) { + result[0] += -0.012187723; + } else { + result[0] += -0.00030637285; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + result[0] += -0.05476123; + } else { + result[0] += -0.002212812; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 234))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 62))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 56))) { + result[0] += 0.034629773; + } else { + result[0] += 0.090757936; + } + } else { + result[0] += -0.015683705; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 4))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + result[0] += 0.0014823362; + } else { + result[0] += -0.11727782; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 328))) { + result[0] += -0.011885715; + } else { + result[0] += 0.06458332; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + result[0] += 0.009296111; + } else { + result[0] += -0.0022434364; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 390))) { + result[0] += 0.09074904; + } else { + result[0] += 0.022570081; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + result[0] += -0.010527322; + } else { + result[0] += 0.0112762675; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { + result[0] += -0.026221529; + } else { + result[0] += -0.0167725; + } + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 128))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 156))) { + result[0] += 0.134217; + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 242))) { + result[0] += 0.055591363; + } else { + result[0] += 0.09201013; + } + } + } else { + result[0] += -0.07688349; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 10))) { + result[0] += 0.008139768; + } else { + result[0] += 0.027951911; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + result[0] += 0.03482144; + } else { + result[0] += 0.12041791; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 330))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 56))) { + result[0] += 0.019060235; + } else { + result[0] += 0.03520998; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { + result[0] += 0.05164655; + } else { + result[0] += 0.10352349; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 62))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 4))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { + result[0] += -0.05586126; + } else { + result[0] += -0.0062299026; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { + result[0] += -0.08666905; + } else { + result[0] += -0.057932485; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 106))) { + result[0] += -0.037010778; + } else { + result[0] += -0.060017806; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { + result[0] += 0.008048224; + } else { + result[0] += -0.025649264; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += -0.018599523; + } else { + result[0] += -0.063888915; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { + result[0] += -0.055958636; + } else { + result[0] += 0.0054995283; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 242))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { + result[0] += -0.015452884; + } else { + result[0] += -0.0031880846; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { + result[0] += 6.0049097e-06; + } else { + result[0] += 0.011630835; + } + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 80))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 104))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + result[0] += -0.012825543; + } else { + result[0] += 0.0061619915; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + result[0] += -0.035948735; + } else { + result[0] += -0.005659299; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 22))) { + result[0] += -0.0034629384; + } else { + result[0] += 0.11748111; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { + result[0] += -0.045964625; + } else { + result[0] += 0.027189994; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 220))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 288))) { + result[0] += -0.043134686; + } else { + result[0] += -0.008114795; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 136))) { + result[0] += 0.022731418; + } else { + result[0] += -0.0039848676; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { + result[0] += -0.0025353373; + } else { + result[0] += 0.017748738; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 68))) { + result[0] += -0.00209215; + } else { + result[0] += -0.026470816; + } + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 268))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += -0.0426252; + } else { + result[0] += -0.17894174; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 46))) { + result[0] += 0.034646492; + } else { + result[0] += 0.08830755; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + result[0] += 0.0036898192; + } else { + result[0] += 0.10474273; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 278))) { + result[0] += 0.0591173; + } else { + result[0] += 0.10110114; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { + result[0] += 0.017330313; + } else { + result[0] += -0.03201217; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 92))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { + result[0] += 0.020424707; + } else { + result[0] += 0.06761751; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 374))) { + result[0] += 0.0006410443; + } else { + result[0] += 0.025116405; + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 342))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 228))) { + result[0] += 0.002580609; + } else { + result[0] += 0.015501295; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 26))) { + result[0] += -0.021991013; + } else { + result[0] += 0.1534664; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 272))) { + result[0] += -0.004891763; + } else { + result[0] += 0.024440682; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 344))) { + result[0] += 0.04597979; + } else { + result[0] += 0.013181034; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + result[0] += 0.08511517; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 12))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { + result[0] += 0.017548429; + } else { + result[0] += -0.051454138; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 312))) { + result[0] += 0.0665479; + } else { + result[0] += 0.03926411; + } + } + } + } + } + } + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 52))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 4))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 120))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += -0.034856033; + } else { + result[0] += 0.035472337; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.082092844; + } else { + result[0] += -0.057657477; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 96))) { + result[0] += 0.042515777; + } else { + result[0] += -0.018864583; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 142))) { + result[0] += -0.057759553; + } else { + result[0] += -0.020723581; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 26))) { + result[0] += 0.054745015; + } else { + result[0] += 0.017524384; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 164))) { + result[0] += -0.03406791; + } else { + result[0] += -0.08434524; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 28))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { + result[0] += 0.017600825; + } else { + result[0] += -0.03500613; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { + result[0] += -0.046339255; + } else { + result[0] += -0.018127348; + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { + result[0] += -0.037658464; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { + result[0] += 0.0075216624; + } else { + result[0] += 0.0364659; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 134))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { + result[0] += 0.033616688; + } else { + result[0] += 0.083399825; + } + } else { + result[0] += 0.12963086; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.074191265; + } else { + result[0] += 0.007478449; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 42))) { + result[0] += 0.098748855; + } else { + result[0] += 0.030946104; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + result[0] += -0.026153265; + } else { + result[0] += -0.15095265; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 74))) { + result[0] += -0.018939627; + } else { + result[0] += -0.06403255; + } + } + } + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + result[0] += -0.021922093; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 134))) { + result[0] += -0.08688069; + } else { + result[0] += -0.033859696; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 86))) { + result[0] += -0.016842697; + } else { + result[0] += -0.0006648888; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 242))) { + result[0] += -0.023793818; + } else { + result[0] += -0.035974815; + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 302))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { + result[0] += 0.012690376; + } else { + result[0] += -0.0033918878; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { + result[0] += -0.005548475; + } else { + result[0] += -0.036335457; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 206))) { + result[0] += -0.0005064619; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 376))) { + result[0] += 0.03696196; + } else { + result[0] += 0.015889136; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 84))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 44))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 108))) { + result[0] += -0.019162795; + } else { + result[0] += -0.0054707523; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + result[0] += 0.0037607402; + } else { + result[0] += -0.009698882; + } + } + } else { + result[0] += -0.069641024; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 256))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { + result[0] += 0.045991335; + } else { + result[0] += 0.0303468; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + result[0] += 0.018556785; + } else { + result[0] += -0.028926486; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 222))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 126))) { + result[0] += 0.014895338; + } else { + result[0] += -0.0033647448; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + result[0] += 0.031980734; + } else { + result[0] += 0.0069378153; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 28))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 30))) { + result[0] += -0.091816425; + } else { + result[0] += -0.053907175; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += -0.035644125; + } else { + result[0] += -0.08623389; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + result[0] += -0.038305838; + } else { + result[0] += -0.022053199; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 34))) { + result[0] += -0.041282233; + } else { + result[0] += -0.011869354; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 136))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 118))) { + result[0] += -0.008556094; + } else { + result[0] += -0.023536053; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { + result[0] += -0.023365794; + } else { + result[0] += -0.0052509154; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { + result[0] += -0.008722498; + } else { + result[0] += -0.08987408; + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { + result[0] += 0.032462854; + } else { + result[0] += -0.013032225; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 44))) { + result[0] += 0.0020141487; + } else { + result[0] += 0.016696744; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 172))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { + result[0] += 0.0063471817; + } else { + result[0] += 0.01982216; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { + result[0] += 0.00396263; + } else { + result[0] += -0.017814338; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 310))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 22))) { + result[0] += -0.042612687; + } else { + result[0] += -0.0046601035; + } + } else { + result[0] += -0.007730845; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 66))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { + result[0] += -0.039969202; + } else { + result[0] += -0.116455294; + } + } else { + result[0] += -0.02463033; + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 208))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 60))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 144))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 42))) { + result[0] += 0.022728456; + } else { + result[0] += -0.023419065; + } + } else { + result[0] += 0.06361882; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 74))) { + result[0] += -0.025523862; + } else { + result[0] += -0.051117957; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += -0.006611984; + } else { + result[0] += -0.04705615; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += 0.010768503; + } else { + result[0] += -0.0008824992; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 298))) { + result[0] += -0.040758982; + } else { + result[0] += -0.008637625; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 130))) { + result[0] += 0.029029582; + } else { + result[0] += 0.08626362; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 302))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { + result[0] += 0.011424046; + } else { + result[0] += 0.04683126; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 72))) { + result[0] += -0.033311624; + } else { + result[0] += 0.076620966; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 264))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 106))) { + result[0] += -0.01699209; + } else { + result[0] += -0.032285534; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 104))) { + result[0] += 0.008964994; + } else { + result[0] += -0.01650845; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 60))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 316))) { + result[0] += 0.025268046; + } else { + result[0] += 0.055473793; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { + result[0] += 0.08008851; + } else { + result[0] += 0.01032548; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 326))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 138))) { + result[0] += 0.0054397047; + } else { + result[0] += 0.02308716; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { + result[0] += 0.08063211; + } else { + result[0] += 0.03532891; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 10))) { + result[0] += -0.04106638; + } else { + result[0] += -0.02793704; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { + result[0] += 0.022730371; + } else { + result[0] += -0.015745891; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 38))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += -0.056902584; + } else { + result[0] += -0.11115749; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 52))) { + result[0] += -0.0027035407; + } else { + result[0] += -0.0484084; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 64))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + result[0] += -0.06669981; + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { + result[0] += -0.058218993; + } else { + result[0] += -0.01607107; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { + result[0] += -0.004913365; + } else { + result[0] += 0.010270695; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 288))) { + result[0] += -0.013211744; + } else { + result[0] += -0.029026702; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 96))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { + result[0] += -0.017454801; + } else { + result[0] += 0.0241193; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.07878145; + } else { + result[0] += -0.021389706; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 150))) { + result[0] += 0.005198505; + } else { + result[0] += -0.025203273; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + result[0] += -0.0036238257; + } else { + result[0] += 0.030112168; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 206))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { + result[0] += -0.014281181; + } else { + result[0] += 0.044825673; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { + result[0] += 0.00095986744; + } else { + result[0] += 0.014824574; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 78))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 248))) { + result[0] += -0.004780053; + } else { + result[0] += 0.023393003; + } + } else { + result[0] += -0.05524193; + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 240))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 264))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 200))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + result[0] += 0.008043467; + } else { + result[0] += -0.035642993; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + result[0] += 0.025907112; + } else { + result[0] += 0.0037340687; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { + result[0] += 0.0053138766; + } else { + result[0] += -0.019315336; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + result[0] += 0.012754604; + } else { + result[0] += -0.017428217; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { + result[0] += -0.018819278; + } else { + result[0] += 0.005015341; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 322))) { + result[0] += -0.046993464; + } else { + result[0] += -0.00060153054; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 334))) { + result[0] += 0.032121107; + } else { + result[0] += 0.07720378; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 206))) { + result[0] += -0.01248775; + } else { + result[0] += 0.021081064; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 220))) { + result[0] += -0.002822095; + } else { + result[0] += 0.010395023; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 24))) { + result[0] += -0.00028166902; + } else { + result[0] += 0.05817884; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 368))) { + result[0] += 0.033362225; + } else { + result[0] += 0.059052836; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 76))) { + result[0] += -0.022754088; + } else { + result[0] += 0.032660067; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 164))) { + result[0] += 0.05608514; + } else { + result[0] += 0.028797118; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 310))) { + result[0] += 0.0028316458; + } else { + result[0] += 0.023484427; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { + result[0] += 0.029160103; + } else { + result[0] += 0.06451139; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 212))) { + result[0] += -0.008154633; + } else { + result[0] += 0.02224008; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 102))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 66))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 34))) { + result[0] += -0.025441715; + } else { + result[0] += -0; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.09877158; + } else { + result[0] += -0.05378988; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { + result[0] += -0.02562209; + } else { + result[0] += -0.042403318; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { + result[0] += 0.02021136; + } else { + result[0] += -0.01660019; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 28))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + result[0] += 0.01719434; + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { + result[0] += -0.018652868; + } else { + result[0] += -0.072789736; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += -0.0050430708; + } else { + result[0] += -0.020398756; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 88))) { + result[0] += 0.0035772503; + } else { + result[0] += 0.04239833; + } + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 222))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + result[0] += -0.008277955; + } else { + result[0] += -0.02545519; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { + result[0] += 0.035175905; + } else { + result[0] += -0.0068374695; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 274))) { + result[0] += 0.0034994117; + } else { + result[0] += -0.012190169; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { + result[0] += -0.0075547704; + } else { + result[0] += 0.01670718; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { + result[0] += -0.043555766; + } else { + result[0] += -0.09016201; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 176))) { + result[0] += -0.043037914; + } else { + result[0] += -2.9695482e-05; + } + } + } else { + result[0] += -0.024823932; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 230))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 6))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += 0.05400118; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { + result[0] += -0.07639449; + } else { + result[0] += -0.016434733; + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 148))) { + result[0] += -0.05022436; + } else { + result[0] += 0.00233022; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 126))) { + result[0] += -0.055654902; + } else { + result[0] += -0.13723731; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 104))) { + result[0] += -0.003547279; + } else { + result[0] += 0.0030764283; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { + result[0] += 0.016396696; + } else { + result[0] += 0.07968046; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + result[0] += 0.017225603; + } else { + result[0] += 0.037653413; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.077591464; + } else { + result[0] += 0.15476403; + } + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 238))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { + result[0] += -0.016144833; + } else { + result[0] += 0.032200087; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 282))) { + result[0] += 0.06538327; + } else { + result[0] += 0.10325643; + } + } + } else { + result[0] += -0.10228916; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 272))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + result[0] += -0.008800676; + } else { + result[0] += 0.009970379; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += -0.015078469; + } else { + result[0] += -0.03748675; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + result[0] += 0.034970764; + } else { + result[0] += -0.026505647; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { + result[0] += 0.037449267; + } else { + result[0] += 0.012034763; + } + } + } + } + } + } + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 160))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 64))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 32))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + result[0] += -0.042211335; + } else { + result[0] += -0.0153662665; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.08464201; + } else { + result[0] += -0.039633475; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 18))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 32))) { + result[0] += -0.0023176766; + } else { + result[0] += 0.011839011; + } + } else { + result[0] += -0.024568608; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 114))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { + result[0] += -0.011506539; + } else { + result[0] += -0.041527543; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 30))) { + result[0] += 0.006311471; + } else { + result[0] += 0.096107006; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { + result[0] += -0.07760205; + } else { + result[0] += -0.024386143; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { + result[0] += 0.0042932476; + } else { + result[0] += -0.024321787; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 278))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 108))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 78))) { + result[0] += -0.00662189; + } else { + result[0] += 0.01475962; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 130))) { + result[0] += 0.03366244; + } else { + result[0] += 0.01162212; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 114))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 28))) { + result[0] += 0.030401085; + } else { + result[0] += 0.1034078; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 76))) { + result[0] += 0.06511612; + } else { + result[0] += 0.004562137; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 258))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 132))) { + result[0] += -0.017277284; + } else { + result[0] += -0.00040395462; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 274))) { + result[0] += -0.028246973; + } else { + result[0] += -0.043952625; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 212))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + result[0] += -0.045117795; + } else { + result[0] += -0.00930117; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { + result[0] += 0.013196451; + } else { + result[0] += -0.019043565; + } + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 310))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 42))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 84))) { + result[0] += -0.026572356; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 164))) { + result[0] += 0.051293522; + } else { + result[0] += -0; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { + result[0] += 0.08133901; + } else { + result[0] += 0.05325532; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 114))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { + result[0] += -0.015609048; + } else { + result[0] += -0.038890243; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 102))) { + result[0] += -0.008053626; + } else { + result[0] += 0.0021804231; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { + result[0] += 0.013441979; + } else { + result[0] += 0.0015576767; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { + result[0] += -0.012684277; + } else { + result[0] += 0.0018506636; + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 66))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { + result[0] += 0.1326864; + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { + result[0] += 0.0802758; + } else { + result[0] += 0.04734662; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 162))) { + result[0] += -0.0053350735; + } else { + result[0] += -0.040489998; + } + } else { + result[0] += 0.057940014; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 228))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 318))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 38))) { + result[0] += -0.00303559; + } else { + result[0] += 0.027338777; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 98))) { + result[0] += 0.021263791; + } else { + result[0] += 0.0065736147; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 220))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { + result[0] += 0.036004547; + } else { + result[0] += 0.00041290582; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { + result[0] += 0.003415088; + } else { + result[0] += 0.03065363; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 112))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 182))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 30))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { + result[0] += -0.061424643; + } else { + result[0] += -0.004366158; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 12))) { + result[0] += -0.029432973; + } else { + result[0] += 0.024416348; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 44))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 38))) { + result[0] += -0.028231425; + } else { + result[0] += 0.013277724; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { + result[0] += -0.05933566; + } else { + result[0] += -0.033664998; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 64))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 276))) { + result[0] += 0.0039472985; + } else { + result[0] += -0.010128739; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 96))) { + result[0] += -0.032275613; + } else { + result[0] += -0.007163971; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 148))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { + result[0] += -0.009553356; + } else { + result[0] += -0.024487672; + } + } else { + result[0] += -0.04257847; + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 106))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + result[0] += -0.017238883; + } else { + result[0] += -0.005016202; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + result[0] += -0.031886365; + } else { + result[0] += -0.013730754; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 64))) { + result[0] += 0.008240308; + } else { + result[0] += 0.03919262; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 38))) { + result[0] += 0.005747178; + } else { + result[0] += -0.058204617; + } + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + result[0] += -0.055713423; + } else { + result[0] += -0.126311; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 160))) { + result[0] += -0.010013968; + } else { + result[0] += -0.03667828; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 62))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 90))) { + result[0] += -0.021773633; + } else { + result[0] += -0.060646553; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { + result[0] += -0.028524075; + } else { + result[0] += -0.0014835782; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 240))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 250))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 58))) { + result[0] += -0.009143605; + } else { + result[0] += 0.0004789403; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { + result[0] += 0.017569097; + } else { + result[0] += 0.06969844; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.053881604; + } else { + result[0] += 0.030100072; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 28))) { + result[0] += 0.07642787; + } else { + result[0] += 0.14859438; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 276))) { + result[0] += -0.0064661647; + } else { + result[0] += 0.0021556418; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { + result[0] += -0.042718846; + } else { + result[0] += 0.004742034; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { + result[0] += 0.0150559945; + } else { + result[0] += 0.02904155; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { + result[0] += 0.030410746; + } else { + result[0] += -0.00021071863; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 130))) { + result[0] += -0.0019750185; + } else { + result[0] += 0.010826028; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 92))) { + result[0] += -0.027783621; + } else { + result[0] += 0.040064003; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 34))) { + result[0] += 0.025106177; + } else { + result[0] += 0.04847647; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { + result[0] += -0.020649737; + } else { + result[0] += 0.047101017; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { + result[0] += 0.0425664; + } else { + result[0] += 0.010982556; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 320))) { + result[0] += 0.0027861602; + } else { + result[0] += 0.024253966; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + result[0] += 0.040306833; + } else { + result[0] += 0.019420182; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 250))) { + result[0] += -0.07389223; + } else { + result[0] += -0.018163418; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 76))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 32))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 134))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + result[0] += -0.033120375; + } else { + result[0] += -0.019771839; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + result[0] += 0.033069532; + } else { + result[0] += -0.024330398; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + result[0] += -0.00891195; + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 260))) { + result[0] += -0.06895655; + } else { + result[0] += -0.029440561; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 112))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { + result[0] += -0.008321228; + } else { + result[0] += -0.032401457; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 28))) { + result[0] += 0.01681263; + } else { + result[0] += -0.011843439; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { + result[0] += 0.009037083; + } else { + result[0] += 0.084029466; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { + result[0] += -0.039284047; + } else { + result[0] += 0.0024618404; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 30))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 138))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 284))) { + result[0] += -0.004148552; + } else { + result[0] += -0.028972754; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 182))) { + result[0] += -0.060149025; + } else { + result[0] += -0.023092858; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 246))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 124))) { + result[0] += -0.0048736627; + } else { + result[0] += 0.011900749; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 152))) { + result[0] += -0.027425656; + } else { + result[0] += -0.0049680914; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + result[0] += 0.018673597; + } else { + result[0] += -0.002092194; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 158))) { + result[0] += 0.025586123; + } else { + result[0] += 0.043332465; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 140))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 134))) { + result[0] += -0.009704775; + } else { + result[0] += 0.033585932; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { + result[0] += -0.04070072; + } else { + result[0] += -0.02585305; + } + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 132))) { + result[0] += 0.026704356; + } else { + result[0] += -0.0026301623; + } + } else { + result[0] += -0.083959244; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 116))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + result[0] += 0.00727385; + } else { + result[0] += -0.00793132; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 34))) { + result[0] += -0.0025618703; + } else { + result[0] += 0.03066427; + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 80))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 192))) { + result[0] += -0.0036948516; + } else { + result[0] += 0.014927791; + } + } else { + result[0] += 0.06770062; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 112))) { + result[0] += 0.021252338; + } else { + result[0] += 0.08691488; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { + result[0] += 0.030034242; + } else { + result[0] += 0.0126524735; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 84))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 294))) { + result[0] += 0.03448388; + } else { + result[0] += 0.012207389; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 82))) { + result[0] += -0.011361829; + } else { + result[0] += 0.010800744; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 374))) { + result[0] += -0.019659309; + } else { + result[0] += -0.010299957; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 222))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 128))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { + result[0] += -0.022616882; + } else { + result[0] += 0.0058049723; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 210))) { + result[0] += -0.05356568; + } else { + result[0] += -0.011999037; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + result[0] += 0.007046343; + } else { + result[0] += 0.04422299; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 200))) { + result[0] += -0.023396257; + } else { + result[0] += -0.015149494; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 110))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 38))) { + result[0] += -0.012656443; + } else { + result[0] += 0.04053043; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 30))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { + result[0] += -0.04405209; + } else { + result[0] += -0.07002534; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.0050658057; + } else { + result[0] += -0.035065096; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 44))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { + result[0] += -0.016180787; + } else { + result[0] += 0.035941508; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 62))) { + result[0] += 0.0028223798; + } else { + result[0] += -0.028867302; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { + result[0] += -0.0008212989; + } else { + result[0] += -0.019350568; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 100))) { + result[0] += -0.012334666; + } else { + result[0] += -0.02596775; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + result[0] += -0.03201983; + } else { + result[0] += -0.009261032; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.043002192; + } else { + result[0] += -0.004841908; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { + result[0] += -0.034105346; + } else { + result[0] += -0.001769832; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 18))) { + result[0] += -0.026078701; + } else { + result[0] += -0.0034125247; + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 68))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 204))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 72))) { + result[0] += -0.035768237; + } else { + result[0] += 0.007326287; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { + result[0] += -0.013266384; + } else { + result[0] += 0.0073778555; + } + } + } else { + result[0] += 0.04042886; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 262))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 314))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + result[0] += -0.031073779; + } else { + result[0] += 0.009902022; + } + } else { + result[0] += -0.10413245; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 304))) { + result[0] += -0.0015145864; + } else { + result[0] += 0.0044673397; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { + result[0] += 0.016366184; + } else { + result[0] += 0.06682373; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + result[0] += 0.1125614; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 158))) { + result[0] += 0.015199658; + } else { + result[0] += -0.047179397; + } + } else { + result[0] += 0.07871269; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 304))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 140))) { + result[0] += 0.037271794; + } else { + result[0] += 0.055074245; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 350))) { + result[0] += 0.01456887; + } else { + result[0] += 0.043336138; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 62))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + result[0] += -0.011651535; + } else { + result[0] += 0.02064044; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 326))) { + result[0] += 0.040328953; + } else { + result[0] += 0.016403003; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 12))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 262))) { + result[0] += -0.0107279355; + } else { + result[0] += 0.005842201; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { + result[0] += -0.038425323; + } else { + result[0] += 0.021354888; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { + result[0] += 0.045149498; + } else { + result[0] += -0; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 272))) { + result[0] += 0.0030525408; + } else { + result[0] += 0.016887192; + } + } + } + } + } + } + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 80))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 80))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { + result[0] += -0.022748465; + } else { + result[0] += -0.0015031536; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 58))) { + result[0] += 0.038551223; + } else { + result[0] += -0.02340021; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { + result[0] += -0.022547202; + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 64))) { + result[0] += 0.006482385; + } else { + result[0] += 0.043839242; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 84))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + result[0] += -0.032270882; + } else { + result[0] += 0.041581474; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 74))) { + result[0] += -0.02748827; + } else { + result[0] += -0.06621357; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { + result[0] += -0.15229563; + } else { + result[0] += -0.056743264; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += 0.0090522; + } else { + result[0] += -0.021619566; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { + result[0] += 0.011768992; + } else { + result[0] += 0.034092333; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 174))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 296))) { + result[0] += -0.0050243097; + } else { + result[0] += 0.0058637843; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 218))) { + result[0] += -0.022230322; + } else { + result[0] += -0; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 234))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 178))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += -0.0040832614; + } else { + result[0] += -0.026436746; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 188))) { + result[0] += 0.0104423; + } else { + result[0] += -0.009707513; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 146))) { + result[0] += 0.021574568; + } else { + result[0] += -0.01677997; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += -0.01483564; + } else { + result[0] += -0.044649884; + } + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 266))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 270))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 58))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + result[0] += 0.027206311; + } else { + result[0] += -0.0112104425; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + result[0] += 0.002884076; + } else { + result[0] += -0.008946375; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 242))) { + result[0] += -0.021231398; + } else { + result[0] += -0.007831926; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + result[0] += -0.010156151; + } else { + result[0] += 0.006795832; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += -0; + } else { + result[0] += 0.01808864; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 156))) { + result[0] += 0.0849195; + } else { + result[0] += 0.040363234; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 208))) { + result[0] += 0.018572433; + } else { + result[0] += -0.016821038; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 238))) { + result[0] += -0.013755165; + } else { + result[0] += 0.010804783; + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 124))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { + result[0] += 0.03828322; + } else { + result[0] += 0.0062655816; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { + result[0] += -0.039595883; + } else { + result[0] += -0.00876256; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 162))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { + result[0] += 0.028006807; + } else { + result[0] += 0.011207984; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 104))) { + result[0] += -0; + } else { + result[0] += 0.04828812; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 236))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 134))) { + result[0] += 0.019871203; + } else { + result[0] += -0.0020931112; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { + result[0] += -0.054860026; + } else { + result[0] += -0.016543666; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 126))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + result[0] += 0.017333688; + } else { + result[0] += -0.014761999; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { + result[0] += 0.0047273724; + } else { + result[0] += 0.018399872; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 8))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { + result[0] += -0.07626998; + } else { + result[0] += 0.020595873; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + result[0] += 0.0014158572; + } else { + result[0] += 0.039809644; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 26))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 28))) { + result[0] += -0.047608785; + } else { + result[0] += -0.08177885; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { + result[0] += -0.018627753; + } else { + result[0] += -0.03965402; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 152))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + result[0] += -0.016198985; + } else { + result[0] += -0.034763936; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { + result[0] += 0.0029702466; + } else { + result[0] += -0.010453141; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 80))) { + result[0] += 0.002711076; + } else { + result[0] += -0.040787876; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { + result[0] += -0.0030517352; + } else { + result[0] += -0.018486565; + } + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 204))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 296))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { + result[0] += -0.01466268; + } else { + result[0] += 0.0016207602; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + result[0] += 0.014294909; + } else { + result[0] += -0.00088145724; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 270))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 230))) { + result[0] += 0.017696586; + } else { + result[0] += 0.0033850037; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 96))) { + result[0] += 0.003758549; + } else { + result[0] += -0.008070237; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 108))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 38))) { + result[0] += -0.012763503; + } else { + result[0] += 0.015098961; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 260))) { + result[0] += -0.06455403; + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { + result[0] += -0.017203202; + } else { + result[0] += 0.0007284813; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 260))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 264))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + result[0] += -0.0004502135; + } else { + result[0] += -0.013600699; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 104))) { + result[0] += 0.0078685535; + } else { + result[0] += 0.052134164; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { + result[0] += -0.047534015; + } else { + result[0] += 0.021326948; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.056880232; + } else { + result[0] += 0.09747363; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 92))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 242))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 276))) { + result[0] += 0.023308432; + } else { + result[0] += 0.011319171; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += 0.051132478; + } else { + result[0] += 0.02103916; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 238))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 288))) { + result[0] += 0.006215774; + } else { + result[0] += -0.0061574043; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 330))) { + result[0] += 0.0041284435; + } else { + result[0] += 0.023122046; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 288))) { + result[0] += 0.034480914; + } else { + result[0] += 0.01336052; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.056260873; + } else { + result[0] += 0.042514727; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 172))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + result[0] += 0.02588587; + } else { + result[0] += -0.032997902; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 316))) { + result[0] += -0.0100605395; + } else { + result[0] += 0.020955171; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 12))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 320))) { + result[0] += 0.0012241629; + } else { + result[0] += 0.010477929; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { + result[0] += -0.038784113; + } else { + result[0] += 0.019064387; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { + result[0] += 0.014094616; + } else { + result[0] += -0.0027222696; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { + result[0] += 0.043263398; + } else { + result[0] += 0.016619174; + } + } + } + } + } + } + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 164))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 106))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 52))) { + result[0] += -0.07700386; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { + result[0] += -0.027042255; + } else { + result[0] += 0.0029873038; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { + result[0] += -0.063133374; + } else { + result[0] += -0.0046702675; + } + } else { + result[0] += -0.097486384; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 110))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 258))) { + result[0] += -0.011726489; + } else { + result[0] += -0.058912832; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { + result[0] += -0.0659786; + } else { + result[0] += -0.02953866; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 58))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 90))) { + result[0] += 0.0242778; + } else { + result[0] += -0.0023044557; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 118))) { + result[0] += 0.029503396; + } else { + result[0] += -0.015513777; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 278))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { + result[0] += -0.003784867; + } else { + result[0] += 0.012548617; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 26))) { + result[0] += 0.0061970223; + } else { + result[0] += 0.023837453; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 330))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { + result[0] += 0.050173163; + } else { + result[0] += -0; + } + } else { + result[0] += -0.007900632; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 86))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 288))) { + result[0] += -0.027921408; + } else { + result[0] += -0.0038356972; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 126))) { + result[0] += -0.0057223137; + } else { + result[0] += 0.010252192; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 188))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 232))) { + result[0] += -0.0093339635; + } else { + result[0] += -0.002617002; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + result[0] += -0.01492499; + } else { + result[0] += -0.025125777; + } + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 308))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 42))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { + result[0] += 0.03968758; + } else { + result[0] += -0.001241333; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { + result[0] += -0.01191957; + } else { + result[0] += 0.002430247; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 116))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 54))) { + result[0] += -0; + } else { + result[0] += 0.08623359; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { + result[0] += -0.0005277793; + } else { + result[0] += 0.01962237; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { + result[0] += -0.0047836783; + } else { + result[0] += -0.015227089; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { + result[0] += 0.008447981; + } else { + result[0] += 0.04540634; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 232))) { + result[0] += -0.022486782; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + result[0] += -0; + } else { + result[0] += -0.015179853; + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 16))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 4))) { + result[0] += -0.041306626; + } else { + result[0] += -0.008218481; + } + } else { + result[0] += 0.042096373; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 316))) { + result[0] += 0.022573106; + } else { + result[0] += 0.07205055; + } + } else { + result[0] += 0.028815215; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 318))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 38))) { + result[0] += -0.0054102; + } else { + result[0] += 0.019898819; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 98))) { + result[0] += 0.013574933; + } else { + result[0] += 0.0023076402; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { + result[0] += 0.023586577; + } else { + result[0] += -0.004840189; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { + result[0] += -0.0051436094; + } else { + result[0] += 0.020131659; + } + } + } + } + } + } + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 162))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 88))) { + result[0] += -0.01574367; + } else { + result[0] += 0.0011124586; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 46))) { + result[0] += -0.07613191; + } else { + result[0] += -0.01636207; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 74))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 24))) { + result[0] += 0.014280135; + } else { + result[0] += 0.037306037; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 24))) { + result[0] += -0.024091965; + } else { + result[0] += 0.0048261676; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 124))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 202))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { + result[0] += -0.04113116; + } else { + result[0] += -0.061350565; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 50))) { + result[0] += -0.03143156; + } else { + result[0] += -0.0036942845; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { + result[0] += 0.024413232; + } else { + result[0] += -0.01646574; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { + result[0] += -0.016089493; + } else { + result[0] += 0.006952781; + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 144))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 274))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 20))) { + result[0] += -0.023807703; + } else { + result[0] += 0.0015813807; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 332))) { + result[0] += 0.029507706; + } else { + result[0] += -0.009649085; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 36))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 24))) { + result[0] += 0.0048679006; + } else { + result[0] += -0.043877617; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { + result[0] += 0.014281779; + } else { + result[0] += 0.047004733; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 164))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { + result[0] += -0.0021749143; + } else { + result[0] += -0.012218022; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { + result[0] += 0.0060172398; + } else { + result[0] += 0.033813428; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 228))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { + result[0] += -0.029434139; + } else { + result[0] += -0.014660637; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + result[0] += -0.008835907; + } else { + result[0] += 0.0022141964; + } + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 310))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 212))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 42))) { + result[0] += 0.023493351; + } else { + result[0] += -0.0012283614; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + result[0] += 0.002782393; + } else { + result[0] += 0.019489653; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 116))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 160))) { + result[0] += 0.07486535; + } else { + result[0] += -0.009162361; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { + result[0] += -0.0022064948; + } else { + result[0] += 0.018015308; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { + result[0] += -0.01574024; + } else { + result[0] += -0.0039653988; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 140))) { + result[0] += 0.020983376; + } else { + result[0] += 0.0030928531; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 238))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 244))) { + result[0] += -0.018868484; + } else { + result[0] += -0.012392565; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 234))) { + result[0] += 0.00805167; + } else { + result[0] += -0.011693968; + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 96))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 84))) { + result[0] += -0.038049206; + } else { + result[0] += -0.002771721; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 320))) { + result[0] += 0.012239266; + } else { + result[0] += 0.03609361; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { + result[0] += 0.027658317; + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 136))) { + result[0] += 0.051759947; + } else { + result[0] += 0.094818436; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 230))) { + result[0] += 0.005940085; + } else { + result[0] += 0.017185425; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 300))) { + result[0] += -0.02267663; + } else { + result[0] += -0.00748341; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 116))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 22))) { + result[0] += 0.005678715; + } else { + result[0] += 0.02415133; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 76))) { + result[0] += -0.026114738; + } else { + result[0] += 0.06968238; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 102))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 144))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { + result[0] += -0.042726804; + } else { + result[0] += -0.0135103455; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { + result[0] += -0.007055785; + } else { + result[0] += 0.014698033; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { + result[0] += -0.007809716; + } else { + result[0] += -0.025070379; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + result[0] += -0; + } else { + result[0] += 0.02416715; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 242))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + result[0] += -0.004747329; + } else { + result[0] += -0.030286456; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 38))) { + result[0] += 0.004049407; + } else { + result[0] += -0.003883451; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 280))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 44))) { + result[0] += 0.0048826905; + } else { + result[0] += 0.01918045; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { + result[0] += -0.010770427; + } else { + result[0] += 0.010454954; + } + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 126))) { + result[0] += -0.04999471; + } else { + result[0] += -0.1126702; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 8))) { + result[0] += -0.0024832468; + } else { + result[0] += 0.034831993; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 82))) { + result[0] += 0.018345565; + } else { + result[0] += -0.03647999; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 72))) { + result[0] += -0.025135571; + } else { + result[0] += -0; + } + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 42))) { + result[0] += -0.031185156; + } else { + result[0] += -0.011169832; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { + result[0] += -0.014554634; + } else { + result[0] += 0.05174926; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 134))) { + result[0] += 0.033395614; + } else { + result[0] += -0.015813565; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 274))) { + result[0] += 0.00033646842; + } else { + result[0] += 0.0078946445; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 114))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 154))) { + result[0] += 0.008154803; + } else { + result[0] += -0.014786914; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.06375181; + } else { + result[0] += 0.014291716; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 46))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += -0.0068405718; + } else { + result[0] += -0.034137234; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 288))) { + result[0] += 0.027196486; + } else { + result[0] += -0.029254882; + } + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 104))) { + result[0] += -0.00011478314; + } else { + result[0] += -0.008755667; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { + result[0] += 0.00041385577; + } else { + result[0] += 0.01698925; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 324))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + result[0] += -0.008199277; + } else { + result[0] += -0.01730309; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { + result[0] += 0.0039300146; + } else { + result[0] += 0.015076749; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 232))) { + result[0] += -0.0025358626; + } else { + result[0] += -0.014611224; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 16))) { + result[0] += 0.021280123; + } else { + result[0] += -0.008725259; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { + result[0] += 0.04309545; + } else { + result[0] += 0.0142627945; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { + result[0] += 0.024396539; + } else { + result[0] += 0.0048766937; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 16))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 28))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 70))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 68))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { + result[0] += -0.024164472; + } else { + result[0] += 0.008937138; + } + } else { + result[0] += 0.038625453; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 76))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { + result[0] += -0.04647706; + } else { + result[0] += -0.019371232; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 154))) { + result[0] += -0.007284378; + } else { + result[0] += -0.041731447; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 14))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + result[0] += -0.049801502; + } else { + result[0] += 0.007428868; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { + result[0] += 0.061795365; + } else { + result[0] += 0.022740616; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + result[0] += 0.022688469; + } else { + result[0] += -0.016254207; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { + result[0] += 0.012587378; + } else { + result[0] += -0.019465229; + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 60))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 294))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 70))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += -0.016392779; + } else { + result[0] += 0.0076769763; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 132))) { + result[0] += -0.0070873285; + } else { + result[0] += -0.017376283; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 180))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 58))) { + result[0] += -0.008054349; + } else { + result[0] += 0.00049257686; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { + result[0] += 0.010894717; + } else { + result[0] += 0.0022375863; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 260))) { + result[0] += -0.018788002; + } else { + result[0] += 0.009024569; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 266))) { + result[0] += -0.0017262193; + } else { + result[0] += 0.015350415; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 172))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { + result[0] += 0.01940038; + } else { + result[0] += 0.0023542785; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { + result[0] += 0.003066004; + } else { + result[0] += -0.010361836; + } + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 290))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 104))) { + result[0] += -0.00023940679; + } else { + result[0] += -0.024299331; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 140))) { + result[0] += 0.018921724; + } else { + result[0] += -0.012017216; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 304))) { + result[0] += -0.009317097; + } else { + result[0] += 0.0070780194; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { + result[0] += 0.029872233; + } else { + result[0] += 0.0070561725; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + result[0] += -0.012874908; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 140))) { + result[0] += -0.03384575; + } else { + result[0] += -0.09957864; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 92))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + result[0] += 0.007720273; + } else { + result[0] += 0.050167393; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 288))) { + result[0] += 0.030057851; + } else { + result[0] += 0.012027963; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { + result[0] += -0.03683686; + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { + result[0] += -0.018220117; + } else { + result[0] += 0.0014780884; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 328))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { + result[0] += 0.02194166; + } else { + result[0] += -0.00030332725; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 84))) { + result[0] += -0.007878302; + } else { + result[0] += -0.016368117; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 104))) { + result[0] += -0.015569873; + } else { + result[0] += -0.00039696257; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 2))) { + result[0] += 0.022349734; + } else { + result[0] += 0.06912997; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + result[0] += -0.0011338064; + } else { + result[0] += -0.015420935; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { + result[0] += -0.0004335566; + } else { + result[0] += 0.012481102; + } + } + } + } + } + } + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 226))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 150))) { + result[0] += -0.008768398; + } else { + result[0] += -0.031017033; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + result[0] += -0.0019974862; + } else { + result[0] += -0.010033133; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 282))) { + result[0] += 0.0018652402; + } else { + result[0] += 0.028417757; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + result[0] += -0.02573219; + } else { + result[0] += 0.00014164243; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + result[0] += -0.012296872; + } else { + result[0] += 0.018667158; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 52))) { + result[0] += 0.0111978315; + } else { + result[0] += 0.00019159181; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 104))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + result[0] += -0.018547757; + } else { + result[0] += 0.00039879596; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { + result[0] += -0.025369791; + } else { + result[0] += -0.006152769; + } + } + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + result[0] += 0.0284749; + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 4))) { + result[0] += -0.12034156; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 90))) { + result[0] += 0.01555708; + } else { + result[0] += -0.04060086; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + result[0] += -0.00899754; + } else { + result[0] += -0.041497704; + } + } else { + result[0] += 0.012691744; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { + result[0] += -0.011100016; + } else { + result[0] += -0.065193616; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { + result[0] += -0.026525358; + } else { + result[0] += -0.007171394; + } + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 74))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { + result[0] += 0.017233951; + } else { + result[0] += 0.047289252; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 292))) { + result[0] += -0.020638349; + } else { + result[0] += 0.02203339; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { + result[0] += -0.028595468; + } else { + result[0] += 0.035886366; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + result[0] += 0.023079382; + } else { + result[0] += -0.0015904735; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { + result[0] += 0.01960182; + } else { + result[0] += 0.004229198; + } + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 110))) { + result[0] += 0.042509545; + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 78))) { + result[0] += 0.01140577; + } else { + result[0] += 0.03407; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 134))) { + result[0] += -0.002405419; + } else { + result[0] += -0.010636522; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { + result[0] += 0.012451133; + } else { + result[0] += -0.0039769937; + } + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { + result[0] += -0.01115589; + } else { + result[0] += 0.01320644; + } + } else { + result[0] += -0.013309436; + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 24))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 18))) { + result[0] += -0.054960348; + } else { + result[0] += -0.008308151; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 116))) { + result[0] += 0.040893547; + } else { + result[0] += -0.029395109; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 108))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 14))) { + result[0] += -0.011575009; + } else { + result[0] += 0.0045230165; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + result[0] += -0.01649066; + } else { + result[0] += 0.0117938975; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 28))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 92))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 44))) { + result[0] += -0.017786995; + } else { + result[0] += 0.0030950578; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 38))) { + result[0] += -0.044471394; + } else { + result[0] += 0.022061352; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 52))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { + result[0] += -0.014604069; + } else { + result[0] += 0.016241714; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { + result[0] += 0.018912865; + } else { + result[0] += 0.0019412668; + } + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 306))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 64))) { + result[0] += -0.006083592; + } else { + result[0] += 0.046224184; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += 0.015739193; + } else { + result[0] += -0.022769345; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { + result[0] += 0.03625956; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 212))) { + result[0] += 0.00036286406; + } else { + result[0] += 0.009678096; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { + result[0] += -0.010175624; + } else { + result[0] += 0.0050961208; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 46))) { + result[0] += -0.0105675245; + } else { + result[0] += -0.026643395; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 226))) { + result[0] += -0; + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 86))) { + result[0] += 0.023571983; + } else { + result[0] += -0; + } + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 222))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 106))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 54))) { + result[0] += -0.07242959; + } else { + result[0] += -0.00901923; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 190))) { + result[0] += 0.0003989732; + } else { + result[0] += -0.014672895; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 56))) { + result[0] += -0.0052175657; + } else { + result[0] += 0.038002204; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { + result[0] += -0.030148897; + } else { + result[0] += -0.006241556; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { + result[0] += -5.1738414e-05; + } else { + result[0] += 0.0063861213; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { + result[0] += 0.022810614; + } else { + result[0] += -0.016576035; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 234))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + result[0] += -0.0050715203; + } else { + result[0] += 0.005531682; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 238))) { + result[0] += -0.01301794; + } else { + result[0] += 0.00030336596; + } + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 180))) { + result[0] += -0.0094006555; + } else { + result[0] += 0.02063076; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + result[0] += 0.010461995; + } else { + result[0] += 0.0025512462; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 62))) { + result[0] += 0.010800346; + } else { + result[0] += 0.052793957; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 16))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { + result[0] += -0.02969619; + } else { + result[0] += -0.01354564; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 126))) { + result[0] += -0.0019324312; + } else { + result[0] += 0.018997952; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 228))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { + result[0] += 0.01697753; + } else { + result[0] += -0.0008568109; + } + } else { + result[0] += -0.0515616; + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 106))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 86))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { + result[0] += -0.036433112; + } else { + result[0] += -0.011925978; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 56))) { + result[0] += 0.03285799; + } else { + result[0] += 0.0010874722; + } + } + } else { + result[0] += -0.060846817; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 92))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 34))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += -0.012450369; + } else { + result[0] += -0.04985917; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 24))) { + result[0] += 0.0078088613; + } else { + result[0] += -0.00958295; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 168))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 24))) { + result[0] += -0.043195866; + } else { + result[0] += -0.014012662; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 26))) { + result[0] += -0.019311419; + } else { + result[0] += 0.007864608; + } + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 152))) { + result[0] += -0.012414033; + } else { + result[0] += 0.029668832; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + result[0] += -0.0030567853; + } else { + result[0] += 0.01868884; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 254))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { + result[0] += 0.026466904; + } else { + result[0] += -0.00078509795; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 256))) { + result[0] += -0.017463585; + } else { + result[0] += -0.004959351; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { + result[0] += 0.054318376; + } else { + result[0] += 0.016744314; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + result[0] += -0.008082875; + } else { + result[0] += 0.004647791; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 136))) { + result[0] += -0.019794006; + } else { + result[0] += 0.037679102; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + result[0] += 0.044813335; + } else { + result[0] += -0.0018291153; + } + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 68))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 76))) { + result[0] += -0.011341448; + } else { + result[0] += -0.060769796; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { + result[0] += -0.00012510354; + } else { + result[0] += 0.019792138; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 110))) { + result[0] += -0.005990359; + } else { + result[0] += 0.017361904; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + result[0] += 0.008660458; + } else { + result[0] += 0.0014748373; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 220))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 272))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 118))) { + result[0] += -0.0027236396; + } else { + result[0] += -0.029521126; + } + } else { + result[0] += -0; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += -0.058680933; + } else { + result[0] += 0.0022811424; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 206))) { + result[0] += -0.017125372; + } else { + result[0] += 0.008216227; + } + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { + result[0] += -0.01995611; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 30))) { + result[0] += 0.021725012; + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 220))) { + result[0] += 0.050011344; + } else { + result[0] += 0.014028007; + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 56))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 104))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 44))) { + result[0] += 0.021376295; + } else { + result[0] += 0.0036938381; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 274))) { + result[0] += -0.0331207; + } else { + result[0] += -0.005488401; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 8))) { + result[0] += 0.015083553; + } else { + result[0] += -0.016786192; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 30))) { + result[0] += -0.037689086; + } else { + result[0] += -0.019093947; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 8))) { + result[0] += 0.059979796; + } else { + result[0] += 0.0069023515; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 290))) { + result[0] += -0.024388736; + } else { + result[0] += -0; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 52))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 64))) { + result[0] += 0.030230714; + } else { + result[0] += -0.002469063; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += -0.00685675; + } else { + result[0] += -0.019919405; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 40))) { + result[0] += 0.007914934; + } else { + result[0] += -0.0072328295; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 168))) { + result[0] += -0.023456104; + } else { + result[0] += -0.007590213; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 278))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 268))) { + result[0] += -0.00084484427; + } else { + result[0] += 0.008248358; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 98))) { + result[0] += 0.008069568; + } else { + result[0] += -0.01922637; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 58))) { + result[0] += -0.0034377978; + } else { + result[0] += 0.02219229; + } + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { + result[0] += -0.0008113146; + } else { + result[0] += -0.011595509; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 30))) { + result[0] += -0.00054977357; + } else { + result[0] += 0.0060809394; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { + result[0] += -0.013630775; + } else { + result[0] += 0.029257402; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { + result[0] += 0.01473201; + } else { + result[0] += 0.004033634; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 326))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + result[0] += 0.0005843948; + } else { + result[0] += -0.015655208; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 368))) { + result[0] += -0.007192479; + } else { + result[0] += 0.006543958; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { + result[0] += 0.01452767; + } else { + result[0] += 0.0055364254; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { + result[0] += 0.026855484; + } else { + result[0] += -0.005540358; + } + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 88))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 92))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 60))) { + result[0] += 0.04485644; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { + result[0] += -0.042145204; + } else { + result[0] += 7.136203e-05; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 32))) { + result[0] += -0.005530101; + } else { + result[0] += -0.041934248; + } + } else { + result[0] += -0.07572525; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 246))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 282))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + result[0] += 0.025418038; + } else { + result[0] += -0.0084718885; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 214))) { + result[0] += -0.019256549; + } else { + result[0] += 0.00998631; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 98))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { + result[0] += -0.054605898; + } else { + result[0] += -0.011759637; + } + } else { + result[0] += -0.046184976; + } + } + } + } + } + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 176))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 36))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { + result[0] += -0.022035845; + } else { + result[0] += 0.046122007; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + result[0] += 0.0077522853; + } else { + result[0] += -0.02736899; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 128))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 120))) { + result[0] += -0.0018629817; + } else { + result[0] += 0.02747905; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += 0.02887457; + } else { + result[0] += -0.013981772; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { + result[0] += -0.01683332; + } else { + result[0] += -0.0050804014; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 52))) { + result[0] += 0.03178656; + } else { + result[0] += -0.0057087517; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 90))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 152))) { + result[0] += -0.014723192; + } else { + result[0] += -0.04805673; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 144))) { + result[0] += -0.017328734; + } else { + result[0] += -0.03817048; + } + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { + result[0] += -0.0028061648; + } else { + result[0] += 0.002789692; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + result[0] += -0.025860507; + } else { + result[0] += -0.0057830475; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 330))) { + result[0] += 0.006883178; + } else { + result[0] += -0.010597587; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { + result[0] += -0.03156317; + } else { + result[0] += -0.00012491019; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 36))) { + result[0] += -0.049023475; + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { + result[0] += -0.015445833; + } else { + result[0] += -0.038803425; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 164))) { + result[0] += -0.0042772954; + } else { + result[0] += -0.02979202; + } + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 4))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 18))) { + result[0] += -0.105805; + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 156))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 218))) { + result[0] += -0.013860464; + } else { + result[0] += -0.047610518; + } + } else { + result[0] += 0.015889838; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 150))) { + result[0] += 0.0039272998; + } else { + result[0] += 0.04100207; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { + result[0] += 0.066427186; + } else { + result[0] += 0.019468648; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + result[0] += -0.013523097; + } else { + result[0] += -0.035645615; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 302))) { + result[0] += -0.017095027; + } else { + result[0] += 0.008768201; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 84))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 88))) { + result[0] += -0.0047009015; + } else { + result[0] += 0.0095943585; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { + result[0] += -0.024147784; + } else { + result[0] += -0.007935442; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 100))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 360))) { + result[0] += -0.025299666; + } else { + result[0] += -0.015776524; + } + } else { + result[0] += -0.00817149; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { + result[0] += -0.013373877; + } else { + result[0] += 0.06051935; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 112))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 54))) { + result[0] += 0.013844582; + } else { + result[0] += -0.018000482; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 116))) { + result[0] += 0.025009016; + } else { + result[0] += 0.0011969743; + } + } + } + } + } + } + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 6))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 46))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 96))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { + result[0] += -0.005377132; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { + result[0] += 0.033936527; + } else { + result[0] += 0.06770065; + } + } else { + result[0] += -0; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 34))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 28))) { + result[0] += -0.016085489; + } else { + result[0] += 0.012783072; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 26))) { + result[0] += -0.038858794; + } else { + result[0] += -0.004968185; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 36))) { + result[0] += 0.04801505; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { + result[0] += 0.01772366; + } else { + result[0] += -0.026647871; + } + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 84))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + result[0] += -0.02566973; + } else { + result[0] += 0.028348282; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 120))) { + result[0] += -0.043836653; + } else { + result[0] += -0.012338279; + } + } + } else { + result[0] += -0.10324136; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 36))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 2))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 12))) { + result[0] += -0.033641063; + } else { + result[0] += 0.00022004887; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 116))) { + result[0] += -0.044643078; + } else { + result[0] += -0.008762143; + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { + result[0] += 0.0021203114; + } else { + result[0] += -0.017186115; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 88))) { + result[0] += -0.019821104; + } else { + result[0] += 0.0021318241; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 184))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 222))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { + result[0] += 0.0009353072; + } else { + result[0] += -0.0056581837; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { + result[0] += -0.0009079633; + } else { + result[0] += 0.008418952; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 108))) { + result[0] += -0; + } else { + result[0] += -0.03857514; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { + result[0] += 0.0028833076; + } else { + result[0] += 0.030077396; + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 86))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 66))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 44))) { + result[0] += 0.0097111445; + } else { + result[0] += -0.004844829; + } + } else { + result[0] += 0.022152675; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 58))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 36))) { + result[0] += 0.012857464; + } else { + result[0] += 0.04939564; + } + } else { + result[0] += 0.0038712837; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 96))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 142))) { + result[0] += 0.022889886; + } else { + result[0] += -0.025423696; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { + result[0] += 0.0012433341; + } else { + result[0] += -0.027690545; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 42))) { + result[0] += 0.019493537; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { + result[0] += 0.04888856; + } else { + result[0] += 0.0047187754; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { + result[0] += -0.038507808; + } else { + result[0] += -0.0059399325; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 190))) { + result[0] += -0.00081904867; + } else { + result[0] += 0.023750668; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 62))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 180))) { + result[0] += 0.007010121; + } else { + result[0] += 0.00020330278; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { + result[0] += 0.022976872; + } else { + result[0] += -0.008908423; + } + } + } + } + } + } + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 16))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 20))) { + result[0] += 0.04029838; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { + result[0] += -0.0076732375; + } else { + result[0] += -0.044110678; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 140))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 138))) { + result[0] += 0.013151512; + } else { + result[0] += 0.04138301; + } + } else { + result[0] += -0.031063614; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 38))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 244))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { + result[0] += -0.01187767; + } else { + result[0] += -0.024798945; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + result[0] += -0.014814547; + } else { + result[0] += -0.0693584; + } + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { + result[0] += -0.019700082; + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 12))) { + result[0] += 0.020823775; + } else { + result[0] += -0.0007175183; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { + result[0] += -0.04782397; + } else { + result[0] += -0.00023042485; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 18))) { + result[0] += 0.011482454; + } else { + result[0] += -0.025828404; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 68))) { + result[0] += 0.07567041; + } else { + result[0] += 0.03777515; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 36))) { + result[0] += 0.019245345; + } else { + result[0] += -0.007408004; + } + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { + result[0] += -0.0023852098; + } else { + result[0] += -0.01571614; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + result[0] += -0.0008245474; + } else { + result[0] += -0.01959662; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { + result[0] += 0.01735154; + } else { + result[0] += -0.01864585; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 104))) { + result[0] += -0.0019429872; + } else { + result[0] += 0.001837825; + } + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 86))) { + result[0] += 0.013189456; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { + result[0] += 0.007143569; + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 44))) { + result[0] += 0.025064811; + } else { + result[0] += 0.0470048; + } + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 138))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 2))) { + result[0] += -0.028255967; + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 330))) { + result[0] += 0.0038780514; + } else { + result[0] += 0.027806832; + } + } else { + result[0] += -0.030956248; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 144))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 98))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { + result[0] += 0.0049754553; + } else { + result[0] += 0.07370368; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 54))) { + result[0] += 0.0024109604; + } else { + result[0] += -0.021205995; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + result[0] += 0.006072202; + } else { + result[0] += 0.021960644; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { + result[0] += -0.016412217; + } else { + result[0] += 0.02891737; + } + } + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 176))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { + result[0] += -0.013008319; + } else { + result[0] += 0.014044277; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 8))) { + result[0] += -0.04008707; + } else { + result[0] += 0.011214378; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 8))) { + result[0] += -0.021072047; + } else { + result[0] += -0.032819312; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 16))) { + result[0] += -0.014122496; + } else { + result[0] += -0.0027466726; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { + result[0] += 0.003206402; + } else { + result[0] += 0.023088232; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { + result[0] += -0.029293472; + } else { + result[0] += -0.00044284094; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 88))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { + result[0] += -0.037337154; + } else { + result[0] += -0.054280948; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + result[0] += 0.000117012045; + } else { + result[0] += -0.009735559; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { + result[0] += -0.024764068; + } else { + result[0] += 0.0052268724; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { + result[0] += 0.014035463; + } else { + result[0] += 0.042725824; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 78))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 118))) { + result[0] += -0.016555734; + } else { + result[0] += 0.0024362335; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 132))) { + result[0] += 0.013627543; + } else { + result[0] += 0.0015473928; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 168))) { + result[0] += 0.04074187; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { + result[0] += 0.029437272; + } else { + result[0] += 0.0063492656; + } + } + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 36))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { + result[0] += -0.007086392; + } else { + result[0] += -0.028627744; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { + result[0] += -0.01253838; + } else { + result[0] += 0.020658642; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { + result[0] += -0.015626702; + } else { + result[0] += -0.07547299; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + result[0] += -0.098590024; + } else { + result[0] += -0.016383274; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 156))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 262))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 238))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 148))) { + result[0] += -0.005975528; + } else { + result[0] += -0.017585494; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 134))) { + result[0] += -0.06808423; + } else { + result[0] += -0.022014553; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 292))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 128))) { + result[0] += -0.010686524; + } else { + result[0] += 0.011010774; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 284))) { + result[0] += 0.03166331; + } else { + result[0] += 0.0053174673; + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 82))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 258))) { + result[0] += -0.049204163; + } else { + result[0] += -0.0052781263; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 108))) { + result[0] += 0.007684131; + } else { + result[0] += -0.01196891; + } + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 168))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 16))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 120))) { + result[0] += -0; + } else { + result[0] += 0.025176907; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { + result[0] += -0.009614422; + } else { + result[0] += -0.025206959; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 216))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 82))) { + result[0] += -0.0041216165; + } else { + result[0] += 0.042651124; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 84))) { + result[0] += -0.038773373; + } else { + result[0] += 0.023100061; + } + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.006539114; + } else { + result[0] += -0.062450953; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + result[0] += -0.004460583; + } else { + result[0] += 0.0019909479; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + result[0] += -0.00021329732; + } else { + result[0] += 0.011201916; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { + result[0] += 0.01424145; + } else { + result[0] += 0.0014522666; + } + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 254))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 50))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + result[0] += 0.033733904; + } else { + result[0] += -0.0006381403; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 28))) { + result[0] += -0.015036389; + } else { + result[0] += 0.0023278003; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 2))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 280))) { + result[0] += -0.020510865; + } else { + result[0] += 0.02703706; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 52))) { + result[0] += 0.022050345; + } else { + result[0] += -0.0019060083; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 118))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 314))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 300))) { + result[0] += -0.019168613; + } else { + result[0] += 0.006976162; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 264))) { + result[0] += -0.015453433; + } else { + result[0] += 0.0013623396; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 60))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 206))) { + result[0] += 0.004762128; + } else { + result[0] += -0.006663674; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { + result[0] += 0.025200857; + } else { + result[0] += -0.005030225; + } + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 54))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 44))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 42))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 2))) { + result[0] += -0.012792389; + } else { + result[0] += 0.017205698; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + result[0] += -0.042184174; + } else { + result[0] += 0.004857573; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 66))) { + result[0] += -0.028872743; + } else { + result[0] += -0.006002301; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { + result[0] += -0.0013448104; + } else { + result[0] += -0.04370745; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 214))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 302))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { + result[0] += 0.0069149467; + } else { + result[0] += -0.0046409685; + } + } else { + result[0] += 0.02378996; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { + result[0] += 0.012926725; + } else { + result[0] += -0.008747402; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { + result[0] += -0.046444967; + } else { + result[0] += -0.018540045; + } + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 134))) { + result[0] += -0.09067112; + } else { + result[0] += -0.010558448; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { + result[0] += -0.023714645; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 194))) { + result[0] += -0.010494131; + } else { + result[0] += 0.01534995; + } + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 308))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 110))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + result[0] += -0.03064735; + } else { + result[0] += 0.0034591525; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { + result[0] += 0.026732335; + } else { + result[0] += 0.008400806; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { + result[0] += -0.005901861; + } else { + result[0] += 0.009023737; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + result[0] += -0.012533008; + } else { + result[0] += -0.0020645715; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { + result[0] += -0.013980149; + } else { + result[0] += 0.0019120906; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 54))) { + result[0] += -0.0057765585; + } else { + result[0] += 0.017128821; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { + result[0] += -0.018316587; + } else { + result[0] += -0.005805789; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { + result[0] += 0.03265365; + } else { + result[0] += -0.0010156564; + } + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 240))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 122))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 184))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 132))) { + result[0] += 0.014779656; + } else { + result[0] += -0.020928297; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 206))) { + result[0] += 0.07677117; + } else { + result[0] += -0.004149875; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 254))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 108))) { + result[0] += 0.0061775236; + } else { + result[0] += -0.002984956; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 192))) { + result[0] += -0.008194422; + } else { + result[0] += -0.05728069; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 192))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 280))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { + result[0] += 0.022544114; + } else { + result[0] += 0.0022025593; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { + result[0] += -0.004058608; + } else { + result[0] += 0.014417312; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 318))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 338))) { + result[0] += 0.0165982; + } else { + result[0] += 0.038578045; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { + result[0] += -0.015475737; + } else { + result[0] += 0.01142684; + } + } + } + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 20))) { + result[0] += -0.0928144; + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 180))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 46))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { + result[0] += -0.020081904; + } else { + result[0] += 0.002259192; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + result[0] += -0.07451444; + } else { + result[0] += -0.0156236235; + } + } + } else { + result[0] += -0.07355082; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 108))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 38))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { + result[0] += -0.037877556; + } else { + result[0] += 0.0013853526; + } + } else { + result[0] += -0.050581098; + } + } else { + result[0] += -0; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 178))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 176))) { + result[0] += -0.0043627243; + } else { + result[0] += -0.020921404; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { + result[0] += 0.01141603; + } else { + result[0] += -0.0036946; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 228))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 94))) { + result[0] += -0.027430529; + } else { + result[0] += 0.00075300987; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 74))) { + result[0] += -0.05395777; + } else { + result[0] += -0.013114258; + } + } + } + } + } + } + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 226))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 270))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 266))) { + result[0] += -0.0013336329; + } else { + result[0] += -0.012101962; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { + result[0] += -0.012478279; + } else { + result[0] += -0.042821463; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 108))) { + result[0] += -0.00018370214; + } else { + result[0] += 0.018782655; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { + result[0] += 0.0019190798; + } else { + result[0] += -0.0032492876; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 114))) { + result[0] += -0.041382577; + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { + result[0] += 0.019699065; + } else { + result[0] += -0.023365228; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 242))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 152))) { + result[0] += -0.014805789; + } else { + result[0] += -5.5329445e-05; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 276))) { + result[0] += 0.031025484; + } else { + result[0] += 0.00553753; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 282))) { + result[0] += 0.03033769; + } else { + result[0] += 0.053280126; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + result[0] += 0.0059688077; + } else { + result[0] += -0.027727133; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 314))) { + result[0] += 0.009310781; + } else { + result[0] += -0.008495542; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 40))) { + result[0] += 0.038052257; + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 12))) { + result[0] += 0.012323047; + } else { + result[0] += -0.023586523; + } + } + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 144))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 130))) { + result[0] += 0.0018252156; + } else { + result[0] += -0.00692114; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 312))) { + result[0] += 0.032022245; + } else { + result[0] += -0.006053284; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 338))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 132))) { + result[0] += -0.019810393; + } else { + result[0] += -0.0073889843; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 36))) { + result[0] += 0.016521052; + } else { + result[0] += 0.0061617037; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 76))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 244))) { + result[0] += 0.012791178; + } else { + result[0] += -0.020304035; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 258))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { + result[0] += 0.009074631; + } else { + result[0] += -0.0016534543; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { + result[0] += 0.027765274; + } else { + result[0] += -0.0001559796; + } + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 234))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 258))) { + result[0] += -0.009072641; + } else { + result[0] += 0.008848451; + } + } else { + result[0] += -0.012117184; + } + } + } + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 6))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 148))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { + result[0] += 0.009714896; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 18))) { + result[0] += -0.035071608; + } else { + result[0] += -0.015023454; + } + } + } else { + result[0] += -0.073392645; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 132))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 38))) { + result[0] += -0.0044015315; + } else { + result[0] += 0.012927837; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 4))) { + result[0] += -0.0023782412; + } else { + result[0] += -0.021760104; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 42))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 112))) { + result[0] += -0.0147892255; + } else { + result[0] += 0.0073062084; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { + result[0] += -0.026250172; + } else { + result[0] += 0.0071073617; + } + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 0))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 162))) { + result[0] += 0.040685426; + } else { + result[0] += 0.0120480135; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 308))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 18))) { + result[0] += -0.06254916; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 98))) { + result[0] += -0.0067300186; + } else { + result[0] += -0.0331382; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + result[0] += -0.019220524; + } else { + result[0] += 0.009100321; + } + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 206))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + result[0] += 0.009733505; + } else { + result[0] += -0.004346859; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + result[0] += 8.9312605e-05; + } else { + result[0] += -0.003453482; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 280))) { + result[0] += -0.0062282663; + } else { + result[0] += 0.0049122893; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + result[0] += -0.002204797; + } else { + result[0] += 0.0064123496; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 44))) { + result[0] += 0.06116004; + } else { + result[0] += 0.010365079; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 146))) { + result[0] += -0.018945072; + } else { + result[0] += -0.010433878; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 118))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 168))) { + result[0] += 0.01741377; + } else { + result[0] += 0.07594368; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 170))) { + result[0] += -0.0018027859; + } else { + result[0] += 0.016592477; + } + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { + result[0] += 0.043439865; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 82))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 58))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { + result[0] += -0.036944382; + } else { + result[0] += -0.012312688; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + result[0] += 0.043840874; + } else { + result[0] += -0.014361912; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + result[0] += -0.004802558; + } else { + result[0] += 0.007698638; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { + result[0] += -0.017537218; + } else { + result[0] += -0.009052196; + } + } + } + } + } + } + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 10))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 18))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { + result[0] += -0.011083994; + } else { + result[0] += -0.033235386; + } + } else { + result[0] += 0.03099644; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { + result[0] += -0.020493729; + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 102))) { + result[0] += 0.03927904; + } else { + result[0] += -0.0013705323; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 172))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 80))) { + result[0] += 0.06984255; + } else { + result[0] += 0.024543915; + } + } else { + result[0] += 0.008717417; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + result[0] += -0.034200173; + } else { + result[0] += 0.020928657; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 40))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 34))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 30))) { + result[0] += -0.018539859; + } else { + result[0] += 0.006696008; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { + result[0] += 0.0006399334; + } else { + result[0] += 0.023955312; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 46))) { + result[0] += 0.0061688256; + } else { + result[0] += 0.044047784; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 156))) { + result[0] += -0.016056351; + } else { + result[0] += 0.0054231877; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { + result[0] += -0.0025266295; + } else { + result[0] += 0.018242473; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 270))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 104))) { + result[0] += -0.017715478; + } else { + result[0] += -0.04547394; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 152))) { + result[0] += 0.013087104; + } else { + result[0] += -0.029149592; + } + } + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 264))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 188))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 22))) { + result[0] += 0.0041657714; + } else { + result[0] += -0.022834495; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { + result[0] += 0.016674465; + } else { + result[0] += 0.032325614; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 176))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 154))) { + result[0] += -0.017698428; + } else { + result[0] += 0.03289035; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { + result[0] += 0.0020410677; + } else { + result[0] += -0.020820608; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 74))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 70))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 50))) { + result[0] += -4.355009e-05; + } else { + result[0] += -0.032748487; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 78))) { + result[0] += 0.035664223; + } else { + result[0] += -0.0039311326; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + result[0] += -0.0035012837; + } else { + result[0] += 0.02525419; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + result[0] += -0.0004375557; + } else { + result[0] += -0.032447618; + } + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 68))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 284))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 202))) { + result[0] += 0.06691403; + } else { + result[0] += 0.026116902; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 228))) { + result[0] += -0.0024726556; + } else { + result[0] += 0.0035805923; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 112))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { + result[0] += 0.008209762; + } else { + result[0] += 0.046135128; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 142))) { + result[0] += -0.0095024295; + } else { + result[0] += 0.0053031766; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 156))) { + result[0] += 0.02656824; + } else { + result[0] += -0.00404437; + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 40))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 46))) { + result[0] += 0.06587588; + } else { + result[0] += 0.0105197765; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 70))) { + result[0] += 0.0041911616; + } else { + result[0] += -0.012468158; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { + result[0] += 0.009541268; + } else { + result[0] += 0.058547605; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 152))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 290))) { + result[0] += -0.021359572; + } else { + result[0] += -0.004128001; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { + result[0] += 0.011844903; + } else { + result[0] += 0.023288697; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 316))) { + result[0] += -7.127906e-05; + } else { + result[0] += 0.0055441954; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 44))) { + result[0] += -0.033032846; + } else { + result[0] += 0.008700618; + } + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 28))) { + result[0] += -0.020282162; + } else { + result[0] += -0; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 252))) { + result[0] += 0.0372028; + } else { + result[0] += 0.020493207; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 114))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 192))) { + result[0] += 0.0023391566; + } else { + result[0] += -0.012876572; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { + result[0] += 0.028000802; + } else { + result[0] += 0.008097276; + } + } + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 12))) { + result[0] += -0.030859623; + } else { + result[0] += 0.059427258; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 296))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 12))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 158))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + result[0] += 0.023903606; + } else { + result[0] += -0.01342114; + } + } else { + result[0] += -0.03418419; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 52))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { + result[0] += -0.014690627; + } else { + result[0] += -0.046283614; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { + result[0] += -0.018795181; + } else { + result[0] += -0.005358623; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 162))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 300))) { + result[0] += -0.013268234; + } else { + result[0] += 0.011121328; + } + } else { + result[0] += -0.0279394; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 176))) { + result[0] += 0.03626206; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 326))) { + result[0] += -0.0057000555; + } else { + result[0] += 0.0147212865; + } + } + } + } + } else { + result[0] += -0.06643397; + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 48))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 126))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { + result[0] += -0.0010974932; + } else { + result[0] += 0.020823766; + } + } else { + result[0] += 0.0449019; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 34))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + result[0] += -0.008742712; + } else { + result[0] += 0.016725272; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 36))) { + result[0] += -0.026789738; + } else { + result[0] += 0.005821276; + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { + result[0] += -0.033222493; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 40))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + result[0] += -0.0058179474; + } else { + result[0] += 0.04347635; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 112))) { + result[0] += -0.015091679; + } else { + result[0] += 0.0024027491; + } + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 32))) { + result[0] += 0.0399952; + } else { + result[0] += -0.00027889368; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { + result[0] += -0.01870556; + } else { + result[0] += 0.0057035587; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { + result[0] += 0.02278183; + } else { + result[0] += -0.008421278; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { + result[0] += -0.042912465; + } else { + result[0] += -0; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 242))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 194))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 110))) { + result[0] += 0.003376493; + } else { + result[0] += 0.011122108; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { + result[0] += 0.03045251; + } else { + result[0] += 0.012394785; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { + result[0] += -0.027083784; + } else { + result[0] += 0.0126619935; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { + result[0] += 0.008825673; + } else { + result[0] += 0.044847894; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 144))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 10))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { + result[0] += 0.023729367; + } else { + result[0] += -0.016748834; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 64))) { + result[0] += -0.020931631; + } else { + result[0] += -0.05402078; + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 30))) { + result[0] += 0.020663498; + } else { + result[0] += 0.00616918; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 20))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 122))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { + result[0] += -0.017938469; + } else { + result[0] += 0.03910426; + } + } else { + result[0] += 0.0053874785; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 14))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.022196533; + } else { + result[0] += -0.00451347; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + result[0] += 0.00037458728; + } else { + result[0] += 0.01688151; + } + } + } + } + } + } + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 254))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { + result[0] += 0.027586067; + } else { + result[0] += -0.029394219; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { + result[0] += -0.008463705; + } else { + result[0] += -0.028911496; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 194))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 154))) { + result[0] += -0.0010301826; + } else { + result[0] += 0.0035315962; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { + result[0] += -0.0011929054; + } else { + result[0] += -0.00760438; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 62))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + result[0] += 0.011337014; + } else { + result[0] += 0.0015774952; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { + result[0] += -0.0043284963; + } else { + result[0] += 0.0071344683; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 256))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 206))) { + result[0] += 0.0046818857; + } else { + result[0] += 0.01623973; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 332))) { + result[0] += -0.025880137; + } else { + result[0] += 0.00024090149; + } + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 44))) { + result[0] += 0.062326457; + } else { + result[0] += -0.012353135; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 154))) { + result[0] += -0.01557938; + } else { + result[0] += -0.007663093; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 114))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 156))) { + result[0] += 0.07925903; + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 160))) { + result[0] += -0.022038413; + } else { + result[0] += 0.010596351; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { + result[0] += 0.009866733; + } else { + result[0] += 0.04229749; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 78))) { + result[0] += -0.027659763; + } else { + result[0] += -0.0035324506; + } + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 168))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 130))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 84))) { + result[0] += -0.01782277; + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 212))) { + result[0] += -0; + } else { + result[0] += -0.01930575; + } + } + } else { + result[0] += 0.0040967353; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 402))) { + result[0] += -0.021184763; + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 140))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + result[0] += -0.0055727926; + } else { + result[0] += 0.009987557; + } + } else { + result[0] += -0.016817084; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { + result[0] += -0.011004246; + } else { + result[0] += 0.0059432825; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { + result[0] += -0.0151095195; + } else { + result[0] += -0.0033481915; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 92))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { + result[0] += 0.0145778125; + } else { + result[0] += -0.0010819718; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 182))) { + result[0] += -0.01369258; + } else { + result[0] += 0.010227516; + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 238))) { + result[0] += -0.009665318; + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 180))) { + result[0] += 0.026355088; + } else { + result[0] += -0.0077078748; + } + } + } + } + } + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 230))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 268))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { + result[0] += -0.0125539; + } else { + result[0] += 0.014108579; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + result[0] += 0.021760508; + } else { + result[0] += -0.019894226; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 268))) { + result[0] += -0.029441832; + } else { + result[0] += -0.010534534; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 112))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 68))) { + result[0] += -0.031425703; + } else { + result[0] += -0.019633126; + } + } else { + result[0] += -0.01066619; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { + result[0] += -0.0006799324; + } else { + result[0] += -0.027934993; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + result[0] += -0.015189849; + } else { + result[0] += -0.0026831003; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 236))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 166))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + result[0] += -1.1786798e-05; + } else { + result[0] += 0.02334137; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 290))) { + result[0] += 0.0047730226; + } else { + result[0] += 0.01909187; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 284))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 246))) { + result[0] += 0.024219202; + } else { + result[0] += 0.04463927; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 28))) { + result[0] += -0; + } else { + result[0] += 0.057420652; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 240))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 202))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { + result[0] += 0.005736035; + } else { + result[0] += -0.018226644; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { + result[0] += -0.014074256; + } else { + result[0] += -0.00035597832; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 250))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { + result[0] += -0.00022620684; + } else { + result[0] += 0.021930484; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 212))) { + result[0] += -0.00031011814; + } else { + result[0] += 0.018323947; + } + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + result[0] += 0.029248917; + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 292))) { + result[0] += -0.014892972; + } else { + result[0] += 0.017006356; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 252))) { + result[0] += 0.0011380663; + } else { + result[0] += 0.0074514025; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { + result[0] += -0.027498512; + } else { + result[0] += -0.00029022736; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 256))) { + result[0] += 0.034170583; + } else { + result[0] += 0.022245234; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 328))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 324))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 298))) { + result[0] += -0.027805215; + } else { + result[0] += -0.017054409; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 294))) { + result[0] += -0.016550997; + } else { + result[0] += -0.0043134186; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + result[0] += -0.00029596835; + } else { + result[0] += -0.030671025; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + result[0] += 0.022249522; + } else { + result[0] += -0.014686073; + } + } + } + } + } + } + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 116))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 16))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { + result[0] += -0.014927131; + } else { + result[0] += -0.0050526834; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += 0.0031292983; + } else { + result[0] += -0.0021366577; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { + result[0] += -0.019448534; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + result[0] += 0.031444933; + } else { + result[0] += 0.010930794; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 152))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 20))) { + result[0] += -0.017589707; + } else { + result[0] += -0.00019221655; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + result[0] += -0.020101383; + } else { + result[0] += -0; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 170))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + result[0] += 0.0022330189; + } else { + result[0] += 0.012290857; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 14))) { + result[0] += -0.0006709326; + } else { + result[0] += 0.0086814575; + } + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 156))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 154))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0; + } else { + result[0] += 0.067005776; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 102))) { + result[0] += 0.07170707; + } else { + result[0] += -0.014048599; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 166))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 164))) { + result[0] += -0.030913204; + } else { + result[0] += -0.007337024; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 160))) { + result[0] += 0.065959476; + } else { + result[0] += 0.0030737682; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 176))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 112))) { + result[0] += -0.016045328; + } else { + result[0] += -0.008878782; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { + result[0] += 0.00972886; + } else { + result[0] += 0.03835055; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 130))) { + result[0] += -0.009170124; + } else { + result[0] += 0.0057587116; + } + } + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { + result[0] += -0.015629305; + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 142))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 140))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + result[0] += -0.004336093; + } else { + result[0] += 0.015490188; + } + } else { + result[0] += -0.01513085; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { + result[0] += 0.00940407; + } else { + result[0] += 0.032563876; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 178))) { + result[0] += -0.01413842; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 244))) { + result[0] += 0.0270089; + } else { + result[0] += -0.00225509; + } + } else { + result[0] += -0.008365121; + } + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 110))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 168))) { + result[0] += -0.0023444246; + } else { + result[0] += 0.007994309; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { + result[0] += 0.00061645266; + } else { + result[0] += -0.04576966; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 62))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 20))) { + result[0] += 0.01534583; + } else { + result[0] += 0.039503228; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { + result[0] += 0.01222286; + } else { + result[0] += -0.0050080977; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { + result[0] += -0.00016676041; + } else { + result[0] += 0.045995515; + } + } else { + result[0] += -0.019292504; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 270))) { + result[0] += -0.0106395595; + } else { + result[0] += 0.024322292; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 196))) { + result[0] += 0.0038009014; + } else { + result[0] += -0.006002572; + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 112))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 104))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { + result[0] += 0.00059230387; + } else { + result[0] += 0.02190473; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + result[0] += -0.0012604637; + } else { + result[0] += -0.025116524; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { + result[0] += 0.01818419; + } else { + result[0] += -0.0041689784; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 54))) { + result[0] += -0.0046932534; + } else { + result[0] += 0.012534521; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 100))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 212))) { + result[0] += -0.0053321766; + } else { + result[0] += 3.2746604e-05; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 160))) { + result[0] += 0.04412394; + } else { + result[0] += -0.019410737; + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 148))) { + result[0] += -0.015239002; + } else { + result[0] += -0.007977055; + } + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 134))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 150))) { + result[0] += 0.026527822; + } else { + result[0] += -0.015368457; + } + } else { + result[0] += -0.05488323; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 240))) { + result[0] += -0.07350321; + } else { + result[0] += -0.01900329; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 12))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 96))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { + result[0] += 0.0044366815; + } else { + result[0] += 0.05480617; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { + result[0] += -0.011877809; + } else { + result[0] += 0.01703535; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { + result[0] += -0.02123229; + } else { + result[0] += -0.07076668; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 242))) { + result[0] += -0.0073331865; + } else { + result[0] += -0.039508924; + } + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 32))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 108))) { + result[0] += 0.028880654; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 8))) { + result[0] += -0.015295302; + } else { + result[0] += 0.0052049416; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 100))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + result[0] += -0.0003331708; + } else { + result[0] += 0.025730435; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 280))) { + result[0] += -0.0062754015; + } else { + result[0] += 0.013416797; + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 82))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { + result[0] += -0; + } else { + result[0] += -0.031346504; + } + } else { + result[0] += 0.0021177256; + } + } + } + } + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { + result[0] += -0.0009686186; + } else { + result[0] += -0.027624989; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 222))) { + result[0] += 0.012920478; + } else { + result[0] += 0.03591418; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 262))) { + result[0] += -0.027306026; + } else { + result[0] += -0.010613002; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + result[0] += 0.028817087; + } else { + result[0] += -0.021536713; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 218))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 150))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 60))) { + result[0] += -0.014519043; + } else { + result[0] += -0.0013858235; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { + result[0] += 0.011594709; + } else { + result[0] += -0.0014000179; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 312))) { + result[0] += -0.005692505; + } else { + result[0] += 0.009161465; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { + result[0] += -3.463562e-05; + } else { + result[0] += 0.004579789; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 296))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 260))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 164))) { + result[0] += -0.00439987; + } else { + result[0] += -0.020940205; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += -0.062401365; + } else { + result[0] += -0.011520316; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 150))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 326))) { + result[0] += 0.016910944; + } else { + result[0] += 0.002540021; + } + } else { + result[0] += -0.010653024; + } + } + } else { + result[0] += -0.056521356; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 384))) { + result[0] += 0.03409591; + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { + result[0] += -0; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 386))) { + result[0] += -0.0031993948; + } else { + result[0] += -0.02678071; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 254))) { + result[0] += 0.031704232; + } else { + result[0] += 0.005365833; + } + } + } + } + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 6))) { + result[0] += 0.0014249064; + } else { + result[0] += -0.01947671; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 94))) { + result[0] += 0.026210293; + } else { + result[0] += 0.003069733; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + result[0] += -0.02217819; + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 40))) { + result[0] += 0.023008201; + } else { + result[0] += 0.0086527765; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 14))) { + result[0] += 0.028070768; + } else { + result[0] += -0; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 16))) { + result[0] += -0.025724366; + } else { + result[0] += -0.008844643; + } + } + } else { + result[0] += -0.047070276; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 134))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 44))) { + result[0] += 0.004072014; + } else { + result[0] += -0.0108394185; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { + result[0] += 0.03304889; + } else { + result[0] += 0.0103535205; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 114))) { + result[0] += -0.023122396; + } else { + result[0] += -0.010224661; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 178))) { + result[0] += 0.001644756; + } else { + result[0] += -0.002623936; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { + result[0] += -0.0045392714; + } else { + result[0] += -0.025003826; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 318))) { + result[0] += 0.015446856; + } else { + result[0] += -0.034815937; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 34))) { + result[0] += -0.00036249825; + } else { + result[0] += 0.004029076; + } + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 86))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 48))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { + result[0] += 0.0035753883; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 160))) { + result[0] += -0.046707038; + } else { + result[0] += -0.026563475; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 136))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 48))) { + result[0] += 0.011157422; + } else { + result[0] += 0.025159726; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 228))) { + result[0] += -0.0022385176; + } else { + result[0] += 0.009932112; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 214))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 186))) { + result[0] += -0.0044850186; + } else { + result[0] += 0.007804101; + } + } else { + result[0] += 0.023426604; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 312))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 90))) { + result[0] += -0.0033166704; + } else { + result[0] += -0.010364393; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 142))) { + result[0] += -0.013235124; + } else { + result[0] += 0.015832936; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 104))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 98))) { + result[0] += 0.022907645; + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 158))) { + result[0] += 0.00681618; + } else { + result[0] += -0.012386585; + } + } + } else { + result[0] += 0.04736806; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + result[0] += -0.0017811867; + } else { + result[0] += 0.024726752; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 300))) { + result[0] += -0.024868438; + } else { + result[0] += -0.008288312; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 300))) { + result[0] += 0.023393013; + } else { + result[0] += 0.000140571; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { + result[0] += -0.0001585513; + } else { + result[0] += 0.0125155775; + } + } + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 136))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 106))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 100))) { + result[0] += 0.00032556072; + } else { + result[0] += 0.02669864; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 144))) { + result[0] += -0.01502426; + } else { + result[0] += -0.007827666; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 108))) { + result[0] += -0.042772368; + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { + result[0] += 0.0073166452; + } else { + result[0] += -0.003959548; + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 104))) { + result[0] += 0.00850385; + } else { + result[0] += -0.0007850647; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 174))) { + result[0] += 0.022124942; + } else { + result[0] += 0.0037423302; + } + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 12))) { + result[0] += -0.03591499; + } else { + result[0] += 0.04963535; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { + result[0] += -0.014330697; + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 142))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 140))) { + result[0] += 0.00258422; + } else { + result[0] += -0.011271856; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { + result[0] += 0.008900406; + } else { + result[0] += 0.025473619; + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 226))) { + result[0] += -0.013987194; + } else { + result[0] += -0.0071587036; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 404))) { + result[0] += -0.007607888; + } else { + result[0] += 0.021506222; + } + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 164))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { + result[0] += 0.017460436; + } else { + result[0] += -0.0049960585; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 48))) { + result[0] += 0.03522423; + } else { + result[0] += 0.0048457957; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 92))) { + result[0] += -0.025125725; + } else { + result[0] += -0; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 4))) { + result[0] += -0.058159776; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 292))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { + result[0] += -0.0017305056; + } else { + result[0] += -0.01667433; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 306))) { + result[0] += -0.027741238; + } else { + result[0] += 0.009779043; + } + } + } + } + } else { + result[0] += 0.009787267; + } + } + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 342))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 136))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 218))) { + result[0] += -0.00073015277; + } else { + result[0] += 0.0008299645; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { + result[0] += -0.003797351; + } else { + result[0] += -0.026151488; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 164))) { + result[0] += -0.0062180604; + } else { + result[0] += -0.018368756; + } + } else { + result[0] += 0.03389421; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 202))) { + result[0] += -0.001612652; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 242))) { + result[0] += 0.029014328; + } else { + result[0] += 0.018497443; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 366))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 298))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 326))) { + result[0] += -0.02712582; + } else { + result[0] += -0.014455877; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 296))) { + result[0] += -0.017788123; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 306))) { + result[0] += 0.003964351; + } else { + result[0] += -0.0068141944; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 304))) { + result[0] += 0.046189606; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + result[0] += 0.024067063; + } else { + result[0] += -0; + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { + result[0] += 0.002387124; + } else { + result[0] += -0.021191007; + } + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 316))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 48))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 140))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { + result[0] += 0.017918406; + } else { + result[0] += 0.0007088012; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { + result[0] += 0.008024058; + } else { + result[0] += 0.03525953; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 50))) { + result[0] += -0.017354771; + } else { + result[0] += 0.011550955; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += 0.016495919; + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 50))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 82))) { + result[0] += 0.0069151595; + } else { + result[0] += -0.009860008; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + result[0] += -0.02526379; + } else { + result[0] += -0.010914913; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { + result[0] += 0.023363909; + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 188))) { + result[0] += -0.017829457; + } else { + result[0] += 0.0066434382; + } + } else { + result[0] += 0.031978313; + } + } else { + result[0] += -0.026702777; + } + } + } + } + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 18))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 72))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { + result[0] += 0.0016082926; + } else { + result[0] += -0.01875299; + } + } else { + result[0] += 0.0011138814; + } + } else { + result[0] += -0.05503887; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 116))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += -0.034357756; + } else { + result[0] += -0.00018943613; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 10))) { + result[0] += -0.015174734; + } else { + result[0] += 0.006123707; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 54))) { + result[0] += -0; + } else { + result[0] += -0.032295134; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { + result[0] += 0.022839814; + } else { + result[0] += -0.0064965202; + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 114))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 78))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 10))) { + result[0] += -0; + } else { + result[0] += -0.025930112; + } + } else { + result[0] += -0.040734638; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { + result[0] += 0.0056574023; + } else { + result[0] += 0.045485523; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 30))) { + result[0] += -0.014559778; + } else { + result[0] += -0.0022656727; + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 178))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 22))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { + result[0] += 0.009212374; + } else { + result[0] += -0.023791295; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 122))) { + result[0] += -0; + } else { + result[0] += -0.030348688; + } + } + } else { + result[0] += 0.011739956; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 116))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 318))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 80))) { + result[0] += 0.00070217456; + } else { + result[0] += -0.01572331; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { + result[0] += 0.0059441226; + } else { + result[0] += -0.015418323; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 86))) { + result[0] += 0.0038127878; + } else { + result[0] += -0.0063350433; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { + result[0] += 0.023485564; + } else { + result[0] += -0.0005745096; + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 28))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 396))) { + result[0] += -0.008549287; + } else { + result[0] += 0.03102727; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 76))) { + result[0] += 0.03656302; + } else { + result[0] += 0.0037815608; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { + result[0] += 0.036549788; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 80))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 202))) { + result[0] += -0.0316082; + } else { + result[0] += -0.011285119; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + result[0] += 0.044548456; + } else { + result[0] += -0.011524561; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + result[0] += -0.0036371031; + } else { + result[0] += 0.0054557663; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 250))) { + result[0] += -0.010413558; + } else { + result[0] += -0.005399991; + } + } + } + } + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 244))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 92))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 26))) { + result[0] += -0.02938572; + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 36))) { + result[0] += -0.0011291165; + } else { + result[0] += -0.023937851; + } + } else { + result[0] += 0.014182518; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 48))) { + result[0] += 0.09786916; + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 46))) { + result[0] += -0.039628785; + } else { + result[0] += -0; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 174))) { + result[0] += 0.008892446; + } else { + result[0] += -0.002574872; + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { + result[0] += 0.0067228116; + } else { + result[0] += 0.039215095; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 50))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + result[0] += -0.007054515; + } else { + result[0] += 0.035221007; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 274))) { + result[0] += -0.030953402; + } else { + result[0] += -0.008404141; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 146))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 106))) { + result[0] += -0.011643419; + } else { + result[0] += -0.05229321; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { + result[0] += -0.016430408; + } else { + result[0] += -0.039948575; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { + result[0] += -0.015282759; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 80))) { + result[0] += 0.007892172; + } else { + result[0] += 0.078224584; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 148))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { + result[0] += -0.0037962466; + } else { + result[0] += -0.015912866; + } + } else { + result[0] += 0.013302639; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 340))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { + result[0] += -0.00012223067; + } else { + result[0] += -0.012624851; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 256))) { + result[0] += 0.027083063; + } else { + result[0] += 0.015543193; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 20))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { + result[0] += -0.020818194; + } else { + result[0] += -0.006904413; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 320))) { + result[0] += 0.049202304; + } else { + result[0] += 0.009114965; + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 34))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 82))) { + result[0] += 0.003950732; + } else { + result[0] += -0.011314066; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + result[0] += 0.0068105734; + } else { + result[0] += 0.028154967; + } + } + } else { + result[0] += -0.021689637; + } + } + } + } + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 296))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { + result[0] += -0.004839664; + } else { + result[0] += 0.00013240986; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { + result[0] += 0.012408934; + } else { + result[0] += -0.00019027379; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 318))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 260))) { + result[0] += -0.0021018793; + } else { + result[0] += 0.013699087; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 262))) { + result[0] += -0.046322387; + } else { + result[0] += -0; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 260))) { + result[0] += 0.02145579; + } else { + result[0] += 0.010288753; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 358))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 286))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 112))) { + result[0] += -0.026122052; + } else { + result[0] += -0.014107632; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 12))) { + result[0] += -0.004089996; + } else { + result[0] += -0.017688856; + } + } + } else { + result[0] += 0.021606075; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 12))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 326))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 200))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 166))) { + result[0] += -0.00194148; + } else { + result[0] += -0.012741702; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 210))) { + result[0] += 0.036462914; + } else { + result[0] += -0.008751659; + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 102))) { + result[0] += -0.0035663412; + } else { + result[0] += -0.02093569; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 0))) { + result[0] += 0.014931956; + } else { + result[0] += 0.038147915; + } + } else { + result[0] += 0.0034323465; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 14))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { + result[0] += -0; + } else { + result[0] += 0.02190816; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 54))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + result[0] += 0.0031788598; + } else { + result[0] += -0.017724153; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 46))) { + result[0] += 0.00170696; + } else { + result[0] += -0.021163156; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 186))) { + result[0] += -0.015714565; + } else { + result[0] += 0.055118978; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { + result[0] += 0.0018268081; + } else { + result[0] += 0.012591888; + } + } + } + } + } + } + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 258))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 144))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += -0.005291102; + } else { + result[0] += -0.029125659; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { + result[0] += 0.003732911; + } else { + result[0] += -0.018547367; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 240))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 152))) { + result[0] += 0.0017839748; + } else { + result[0] += -0.008801985; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 118))) { + result[0] += 0.0042691375; + } else { + result[0] += 0.03554597; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 208))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 224))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 284))) { + result[0] += -0.003548905; + } else { + result[0] += -0.012224059; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 226))) { + result[0] += 0.0020274771; + } else { + result[0] += 0.019364875; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { + result[0] += 0.016102543; + } else { + result[0] += -0.013677455; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 248))) { + result[0] += -0.05339232; + } else { + result[0] += -0.00876738; + } + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 106))) { + result[0] += -0.013133674; + } else { + result[0] += -0.0521831; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { + result[0] += 0.008359418; + } else { + result[0] += 0.038568888; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 102))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 60))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 122))) { + result[0] += 0.0013149813; + } else { + result[0] += -0.0191629; + } + } else { + result[0] += -0.030204315; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 54))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 78))) { + result[0] += 0.024884596; + } else { + result[0] += 0.0094683515; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 50))) { + result[0] += 0.0068674767; + } else { + result[0] += -0.007323086; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 94))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 104))) { + result[0] += -0.0076974086; + } else { + result[0] += -0.029438093; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 110))) { + result[0] += 0.03408525; + } else { + result[0] += -0.006439855; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 62))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 76))) { + result[0] += 0.0068958416; + } else { + result[0] += 0.04007944; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { + result[0] += 0.00047964975; + } else { + result[0] += 0.0132646635; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 88))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 86))) { + result[0] += 0.0022136592; + } else { + result[0] += 0.026103059; + } + } else { + result[0] += -0.0099972095; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 120))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { + result[0] += -0.011941642; + } else { + result[0] += 0.0021292144; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 20))) { + result[0] += -0.014601464; + } else { + result[0] += -0.0005000638; + } + } + } + } + } + } + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 224))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 246))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { + result[0] += 0.000114969465; + } else { + result[0] += -0.0023860415; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { + result[0] += -0.035199355; + } else { + result[0] += -0.017500581; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 314))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + result[0] += 0.00627641; + } else { + result[0] += -0.0026602177; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { + result[0] += -0.008631534; + } else { + result[0] += 0.008578693; + } + } + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 30))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 216))) { + result[0] += 0.0028546187; + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 14))) { + result[0] += 0.014946878; + } else { + result[0] += 0.041622277; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 250))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + result[0] += 0.030365026; + } else { + result[0] += 0.005026304; + } + } else { + result[0] += -0.030540321; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + result[0] += -0.034515306; + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 150))) { + result[0] += 0.04119061; + } else { + result[0] += 0.018316824; + } + } else { + result[0] += -0.017891763; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 54))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { + result[0] += -0.028790927; + } else { + result[0] += -0.0069620805; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += 0.04099756; + } else { + result[0] += -0.003357871; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 260))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 118))) { + result[0] += -0.0026708224; + } else { + result[0] += -0.011060624; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + result[0] += 0.0011356926; + } else { + result[0] += 0.041363157; + } + } + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 190))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { + result[0] += 0.024280345; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 320))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 0))) { + result[0] += 0.010236905; + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 182))) { + result[0] += -0.02040131; + } else { + result[0] += -0.008407633; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 326))) { + result[0] += 0.012552517; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + result[0] += -0.021955565; + } else { + result[0] += -0.0035318558; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 120))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 302))) { + result[0] += 0.009495072; + } else { + result[0] += 0.00013142404; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { + result[0] += -0.006438227; + } else { + result[0] += -0.030158589; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 306))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 276))) { + result[0] += -0; + } else { + result[0] += 0.018983908; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { + result[0] += -0.044639092; + } else { + result[0] += 0.00017055031; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 346))) { + result[0] += -0.008808951; + } else { + result[0] += -0.032180067; + } + } else { + result[0] += 0.008250392; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { + result[0] += 0.056063425; + } else { + result[0] += -0.0065511647; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { + result[0] += -0.0092850765; + } else { + result[0] += 0.0021918796; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 30))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + result[0] += 0.007402791; + } else { + result[0] += -0.011684748; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += -0.006889455; + } else { + result[0] += 0.01800547; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 36))) { + result[0] += -0.024754832; + } else { + result[0] += -0.0028222399; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 30))) { + result[0] += -0.020031106; + } else { + result[0] += 0.0067768777; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 52))) { + result[0] += 0.041279774; + } else { + result[0] += 0.012748748; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 144))) { + result[0] += -0; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { + result[0] += 0.015250462; + } else { + result[0] += 0.026752282; + } + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 216))) { + result[0] += -0.02535589; + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 156))) { + result[0] += 0.006471269; + } else { + result[0] += 0.029563783; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 170))) { + result[0] += -0.025211362; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 186))) { + result[0] += -0.023914715; + } else { + result[0] += -0.009117383; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 154))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + result[0] += 0.0012569004; + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 118))) { + result[0] += -0.008115943; + } else { + result[0] += -0.026224598; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 340))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 196))) { + result[0] += 0.042526796; + } else { + result[0] += 0.022222739; + } + } else { + result[0] += 0.012392859; + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 12))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { + result[0] += -0.028314574; + } else { + result[0] += -0.002670089; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { + result[0] += 0.0057236534; + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 120))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 50))) { + result[0] += -0.017261336; + } else { + result[0] += -0.007837756; + } + } else { + result[0] += -0.0055499817; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 216))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 78))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 56))) { + result[0] += -0.0009610457; + } else { + result[0] += 0.0076379874; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 16))) { + result[0] += 0.05212106; + } else { + result[0] += 0.02506253; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + result[0] += -0.0006034739; + } else { + result[0] += 0.021560185; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { + result[0] += -0.034164276; + } else { + result[0] += -0.004662303; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 78))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { + result[0] += -0.001952683; + } else { + result[0] += 0.037713937; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 152))) { + result[0] += -0.008823862; + } else { + result[0] += -0.0014422481; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 76))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 242))) { + result[0] += 0.014311755; + } else { + result[0] += -0.018534161; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { + result[0] += 0.0027180712; + } else { + result[0] += -0.0037262347; + } + } + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { + result[0] += -0.00358946; + } else { + result[0] += 0.00086048665; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 298))) { + result[0] += -0.020779364; + } else { + result[0] += -0; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 184))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 34))) { + result[0] += -0.039312568; + } else { + result[0] += -0.005182448; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 82))) { + result[0] += 0.0015322726; + } else { + result[0] += -0.0016843865; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 312))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { + result[0] += -0.006095136; + } else { + result[0] += 0.028541317; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 188))) { + result[0] += 0.015441231; + } else { + result[0] += -0.01365337; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { + result[0] += 0.028764328; + } else { + result[0] += -0; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + result[0] += 0.01359984; + } else { + result[0] += -0.0053497027; + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 118))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 216))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 254))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 38))) { + result[0] += 0.0073710456; + } else { + result[0] += 0.00038945695; + } + } else { + result[0] += -0.00408677; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { + result[0] += -0.016966945; + } else { + result[0] += -0; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 130))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 106))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 70))) { + result[0] += 0.0059221475; + } else { + result[0] += 0.018299853; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { + result[0] += 0.005721331; + } else { + result[0] += -0.010450677; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 214))) { + result[0] += 0.03640103; + } else { + result[0] += 0.01191198; + } + } + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 96))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 88))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { + result[0] += -0.036111545; + } else { + result[0] += -0; + } + } else { + result[0] += 0.013476851; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 74))) { + result[0] += -0.02820078; + } else { + result[0] += -0.08100428; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { + result[0] += 0.001866552; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { + result[0] += -0.021744216; + } else { + result[0] += -0; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 48))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 42))) { + result[0] += -0.004817111; + } else { + result[0] += -0.019636374; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 70))) { + result[0] += 0.017743232; + } else { + result[0] += -0.010167501; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 66))) { + result[0] += -0.05105884; + } else { + result[0] += -0.011944066; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 102))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 68))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 24))) { + result[0] += -0; + } else { + result[0] += 0.02911661; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 66))) { + result[0] += -0.010146369; + } else { + result[0] += 0.009323521; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { + result[0] += -0.03494624; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { + result[0] += -0.003336961; + } else { + result[0] += -0.02113694; + } + } + } + } + } + } + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 166))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 114))) { + result[0] += -0.0013070774; + } else { + result[0] += 0.0005617281; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { + result[0] += -0.00045852616; + } else { + result[0] += 0.021694412; + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 190))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { + result[0] += -0.009019866; + } else { + result[0] += 0.0010552766; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 382))) { + result[0] += 0.042176425; + } else { + result[0] += -0.019407976; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 94))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 22))) { + result[0] += -0.008323395; + } else { + result[0] += -0.0172386; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 96))) { + result[0] += 0.0036288549; + } else { + result[0] += -0.013044089; + } + } + } else { + result[0] += 0.00021139935; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 158))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 74))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 78))) { + result[0] += -0.00079369405; + } else { + result[0] += 0.04720639; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 390))) { + result[0] += 0.05325351; + } else { + result[0] += -0.043466292; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 66))) { + result[0] += -0.032283835; + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 198))) { + result[0] += -0.0008110626; + } else { + result[0] += 0.050249096; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 68))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 158))) { + result[0] += -0.010387595; + } else { + result[0] += -0.0034609255; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 110))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 394))) { + result[0] += 0.0030221925; + } else { + result[0] += 0.028211419; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 114))) { + result[0] += -0.0093119275; + } else { + result[0] += 0.0069152773; + } + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 164))) { + result[0] += -0.009060017; + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 126))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 46))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { + result[0] += -0.006120693; + } else { + result[0] += 0.009823619; + } + } else { + result[0] += -0.009353681; + } + } else { + result[0] += -0.0127736945; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { + result[0] += 0.013670566; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 86))) { + result[0] += -0.004946747; + } else { + result[0] += 0.0048705908; + } + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 136))) { + result[0] += -0.010391137; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 92))) { + result[0] += 0.004601255; + } else { + result[0] += -0.0049207225; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 22))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { + result[0] += -0.018509652; + } else { + result[0] += -0.0028016684; + } + } else { + result[0] += 0.012803587; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 14))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.013896561; + } else { + result[0] += -0.009757071; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 2))) { + result[0] += 0.035226237; + } else { + result[0] += 0.003883094; + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 108))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 72))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { + result[0] += 0.009042918; + } else { + result[0] += 0.03158817; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 138))) { + result[0] += 0.007958132; + } else { + result[0] += 0.03468901; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 146))) { + result[0] += -0.0020058847; + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 174))) { + result[0] += 0.026864871; + } else { + result[0] += 0.014916946; + } + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 216))) { + result[0] += -0.022077216; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { + result[0] += 0.007497831; + } else { + result[0] += 0.028885541; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 250))) { + result[0] += -0.02360869; + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 174))) { + result[0] += -0.018704783; + } else { + result[0] += -0.0076525756; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + result[0] += 9.27179e-05; + } else { + result[0] += 0.031941853; + } + } else { + result[0] += 0.009628982; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 350))) { + result[0] += -0.021527562; + } else { + result[0] += -0.0072527933; + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 74))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 104))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 70))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 60))) { + result[0] += 0.02288001; + } else { + result[0] += -0.016261838; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 146))) { + result[0] += -0.03769898; + } else { + result[0] += -0.018141152; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 46))) { + result[0] += -0.0023420886; + } else { + result[0] += 0.021080358; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 140))) { + result[0] += -0.012038226; + } else { + result[0] += 0.008399843; + } + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 72))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { + result[0] += 0.002315519; + } else { + result[0] += 0.062828414; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { + result[0] += 0.0130506335; + } else { + result[0] += -0.007596428; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 40))) { + result[0] += 0.007198368; + } else { + result[0] += -0.0058715134; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + result[0] += 0.02399823; + } else { + result[0] += -0.008211115; + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 84))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 26))) { + result[0] += 0.02461793; + } else { + result[0] += 0.008668258; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 28))) { + result[0] += -0.030365562; + } else { + result[0] += 0.010669919; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 94))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 16))) { + result[0] += -0.018534234; + } else { + result[0] += -0.0031345394; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + result[0] += -0.010914135; + } else { + result[0] += -0.00012393964; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 214))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { + result[0] += 0.022872802; + } else { + result[0] += 0.0050913426; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 132))) { + result[0] += 0.0009941938; + } else { + result[0] += -0.0025577117; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += 0.012239212; + } else { + result[0] += 0.032537233; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 74))) { + result[0] += -0.012231956; + } else { + result[0] += 0.00096511666; + } + } + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 244))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 50))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 100))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 190))) { + result[0] += -0.015980741; + } else { + result[0] += 0.013686332; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 106))) { + result[0] += 0.030151868; + } else { + result[0] += 0.008428541; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 192))) { + result[0] += 0.001345482; + } else { + result[0] += 0.02078061; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 126))) { + result[0] += -0.029191677; + } else { + result[0] += 0.00064531795; + } + } + } + } else { + result[0] += 0.035184488; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 30))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 212))) { + result[0] += 0.009157064; + } else { + result[0] += -0.03418844; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + result[0] += -0.01716392; + } else { + result[0] += -0.045232523; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { + result[0] += -0.01179669; + } else { + result[0] += 0.06294518; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 140))) { + result[0] += -0.011332896; + } else { + result[0] += 0.01226867; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { + result[0] += 0.0011395406; + } else { + result[0] += -0.0025050612; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 138))) { + result[0] += -0.037857305; + } else { + result[0] += -0.00218284; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 226))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 102))) { + result[0] += -0.0008166955; + } else { + result[0] += 0.004205488; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.0056326287; + } else { + result[0] += 0.0007912867; + } + } + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { + result[0] += 0.023435187; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 40))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 42))) { + result[0] += 0.03141637; + } else { + result[0] += -0.0015305742; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { + result[0] += -0.017991172; + } else { + result[0] += 0.002195324; + } + } else { + result[0] += 0.013269196; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 44))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 120))) { + result[0] += -0.01257918; + } else { + result[0] += 0.0010349855; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { + result[0] += -0.051812388; + } else { + result[0] += -0.010208193; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 30))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 278))) { + result[0] += 0.0073652193; + } else { + result[0] += -0.013559197; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 56))) { + result[0] += -0.02485094; + } else { + result[0] += -0.0037162665; + } + } + } + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 42))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 2))) { + result[0] += -0.034541044; + } else { + result[0] += 0.0027651105; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { + result[0] += -0.019249316; + } else { + result[0] += -0.0034680355; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 8))) { + result[0] += -0.02443984; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += 0.013286717; + } else { + result[0] += -0.0025256465; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 46))) { + result[0] += 0.0061984803; + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 48))) { + result[0] += 0.079341814; + } else { + result[0] += 0.025329694; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 126))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { + result[0] += 0.021308701; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 118))) { + result[0] += -0.006669204; + } else { + result[0] += -0.019334236; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 140))) { + result[0] += 0.04223214; + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 74))) { + result[0] += -0.01822163; + } else { + result[0] += 0.011897653; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 12))) { + result[0] += -0.03937181; + } else { + result[0] += -0.021595992; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { + result[0] += 0.008440125; + } else { + result[0] += -0.018780667; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 32))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { + result[0] += -0.0025710927; + } else { + result[0] += -0.015579129; + } + } else { + result[0] += 0.013569686; + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 118))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 10))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 102))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 90))) { + result[0] += -0.008451057; + } else { + result[0] += 0.010793014; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 244))) { + result[0] += -0.007276828; + } else { + result[0] += -0.00065764156; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { + result[0] += 0.00065685576; + } else { + result[0] += 0.025356445; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 142))) { + result[0] += -0.012041635; + } else { + result[0] += -0.05446145; + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { + result[0] += 0.044229705; + } else { + result[0] += 0.0006534785; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 30))) { + result[0] += 0.005015543; + } else { + result[0] += -0.01020183; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += 0.06759548; + } else { + result[0] += 0.007752572; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { + result[0] += 0.011832262; + } else { + result[0] += 0.0015743548; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 208))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 196))) { + result[0] += -0.0024539565; + } else { + result[0] += 0.001751725; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { + result[0] += -0; + } else { + result[0] += 0.021780172; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 272))) { + result[0] += 0.008948053; + } else { + result[0] += -0.0063331993; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { + result[0] += -0.0073422543; + } else { + result[0] += -0.026588783; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 210))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 104))) { + result[0] += 0.0029568898; + } else { + result[0] += -0.042533282; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { + result[0] += -0.0074031386; + } else { + result[0] += 0.014356777; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + result[0] += 0.00092436554; + } else { + result[0] += 0.024809679; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 326))) { + result[0] += -0.00329214; + } else { + result[0] += 0.00526762; + } + } + } + } + } + } + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 10))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 200))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 72))) { + result[0] += 0.0010467954; + } else { + result[0] += 0.030300766; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 62))) { + result[0] += 0.02035891; + } else { + result[0] += -0.010212629; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { + result[0] += -0.001989516; + } else { + result[0] += -0.013169396; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 346))) { + result[0] += 0.020909293; + } else { + result[0] += -0.0079391515; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 306))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 262))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 116))) { + result[0] += -0.0052688015; + } else { + result[0] += 0.0019646974; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 286))) { + result[0] += -0.031940565; + } else { + result[0] += -0; + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 322))) { + result[0] += 0.020321852; + } else { + result[0] += 0.034146465; + } + } else { + result[0] += -0.03401268; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { + result[0] += 0.0016832197; + } else { + result[0] += 0.027038325; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { + result[0] += -0.0071495273; + } else { + result[0] += -0.0014786319; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 258))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { + result[0] += 0.01780109; + } else { + result[0] += -0.01263422; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 228))) { + result[0] += 0.039167188; + } else { + result[0] += 0.018858656; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 26))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { + result[0] += -0.0019396268; + } else { + result[0] += 0.005724409; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 6))) { + result[0] += 0.027276058; + } else { + result[0] += 0.008047543; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { + result[0] += -0.012420956; + } else { + result[0] += -0.0021616125; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + result[0] += -0.0018222294; + } else { + result[0] += 0.0021800934; + } + } + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 150))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 92))) { + result[0] += -0.006505067; + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 220))) { + result[0] += 0.040462743; + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 46))) { + result[0] += 0.015701324; + } else { + result[0] += -0.0201608; + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 116))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 52))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 94))) { + result[0] += 0.0005646117; + } else { + result[0] += 0.010826596; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += -0.013457968; + } else { + result[0] += -1.2906838e-05; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += -0.0008355248; + } else { + result[0] += -0.0226526; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 92))) { + result[0] += 0.020389868; + } else { + result[0] += 0.0054127253; + } + } + } + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 14))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 76))) { + result[0] += -0.032427344; + } else { + result[0] += -0; + } + } else { + result[0] += 0.04492506; + } + } + } + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 40))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 8))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { + result[0] += -0.008939014; + } else { + result[0] += -0.031956512; + } + } else { + result[0] += 0.017092446; + } + } else { + result[0] += 0.022510538; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { + result[0] += 0.042899013; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + result[0] += -0.0038211767; + } else { + result[0] += 0.029483128; + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 72))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 160))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 82))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { + result[0] += -0.007246212; + } else { + result[0] += 0.0062661204; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 88))) { + result[0] += -0.02025948; + } else { + result[0] += -0.005702542; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 116))) { + result[0] += -0.030373631; + } else { + result[0] += -0.004761403; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 90))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { + result[0] += 0.010341472; + } else { + result[0] += 0.0385042; + } + } else { + result[0] += -0.0025437989; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 204))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += -0.0027436535; + } else { + result[0] += -0.020981843; + } + } else { + result[0] += 0.011424384; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { + result[0] += -0.026424287; + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 22))) { + result[0] += 0.015828108; + } else { + result[0] += -0.004946768; + } + } + } else { + result[0] += 0.036206927; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 0))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 46))) { + result[0] += -0.0018861439; + } else { + result[0] += 0.019979624; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 16))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 64))) { + result[0] += -0.012420944; + } else { + result[0] += -0.04097703; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 46))) { + result[0] += -0.014519716; + } else { + result[0] += 0.0064661787; + } + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 116))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { + result[0] += -0.0007507774; + } else { + result[0] += 0.011509613; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 74))) { + result[0] += 0.0015937341; + } else { + result[0] += 0.026918387; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 268))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { + result[0] += -0.001206148; + } else { + result[0] += -0.01964714; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 98))) { + result[0] += 0.008147176; + } else { + result[0] += -0.0026112716; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { + result[0] += -0.00044672118; + } else { + result[0] += -0.009276496; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 156))) { + result[0] += 0.039460566; + } else { + result[0] += -0.0033604186; + } + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 148))) { + result[0] += -0.0112617565; + } else { + result[0] += -0.0053185816; + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 16))) { + result[0] += -0.027565295; + } else { + result[0] += -0.004120046; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + result[0] += -0.0030114742; + } else { + result[0] += 0.010990894; + } + } + } else { + result[0] += -0.018377742; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + result[0] += 0.0032660365; + } else { + result[0] += 0.014772459; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 240))) { + result[0] += 0.01916596; + } else { + result[0] += 0.043604817; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 188))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { + result[0] += 0.020365; + } else { + result[0] += -0.004660358; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 182))) { + result[0] += -0.026131554; + } else { + result[0] += -0.012585315; + } + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 208))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 144))) { + result[0] += -0.009168918; + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 186))) { + result[0] += -0.017280573; + } else { + result[0] += 0.013135247; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { + result[0] += 0.027347783; + } else { + result[0] += 0.0058005466; + } + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 228))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 262))) { + result[0] += -0.01863461; + } else { + result[0] += -0.009977842; + } + } else { + result[0] += 0.0074623013; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 18))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 82))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 10))) { + result[0] += -0.027271813; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + result[0] += 0.03428016; + } else { + result[0] += 0.005898777; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 124))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 120))) { + result[0] += -0.0031157867; + } else { + result[0] += 0.012381009; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 40))) { + result[0] += -0.032176975; + } else { + result[0] += -0.0047460934; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 128))) { + result[0] += -0.011950021; + } else { + result[0] += -0.029780094; + } + } else { + result[0] += -0.038288817; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 14))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 58))) { + result[0] += -0.013251813; + } else { + result[0] += 0.009832563; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 40))) { + result[0] += -0.0028266981; + } else { + result[0] += 0.010589391; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 26))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 0))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 144))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 8))) { + result[0] += -0; + } else { + result[0] += 0.02299802; + } + } else { + result[0] += -0.0005336401; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 16))) { + result[0] += 0.024464216; + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 24))) { + result[0] += -0.02506329; + } else { + result[0] += -0.00617628; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 146))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 228))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 168))) { + result[0] += 0.000546928; + } else { + result[0] += -0.0015107197; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 244))) { + result[0] += 0.0052600866; + } else { + result[0] += 0.0005289404; + } + } + } else { + result[0] += -0.0067100944; + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 56))) { + result[0] += 0.000776489; + } else { + result[0] += -0.00053459004; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.0025825037; + } else { + result[0] += 0.033680957; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 94))) { + result[0] += -0.010514084; + } else { + result[0] += 0.00012713655; + } + } else { + result[0] += -8.464566e-05; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 192))) { + result[0] += -0.008038435; + } else { + result[0] += 0.03207838; + } + } else { + result[0] += -0.0059311604; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 196))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 104))) { + result[0] += -0.0027816568; + } else { + result[0] += 0.008232006; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + result[0] += 0.03486943; + } else { + result[0] += -0.009670003; + } + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 78))) { + result[0] += -0.021940826; + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 160))) { + result[0] += -0.007266288; + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { + result[0] += -0.004397231; + } else { + result[0] += 0.0011502573; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { + result[0] += -0.005171144; + } else { + result[0] += 0.009149543; + } + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 132))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 68))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += 0.033398908; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 146))) { + result[0] += -0.011877987; + } else { + result[0] += 0.009033947; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 32))) { + result[0] += 0.017914457; + } else { + result[0] += 0.00086382544; + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 114))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 106))) { + result[0] += -0; + } else { + result[0] += -0.025332725; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 136))) { + result[0] += 0.010888358; + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 132))) { + result[0] += -0.002672884; + } else { + result[0] += -0.0126517145; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { + result[0] += -0.01176235; + } else { + result[0] += -0.04480323; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 132))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 146))) { + result[0] += 0.022533778; + } else { + result[0] += 0.0015053398; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + result[0] += -0.029711738; + } else { + result[0] += -0.00018641823; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 194))) { + result[0] += -0.0029512055; + } else { + result[0] += -0.014446958; + } + } else { + result[0] += -0.04525297; + } + } + } + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 116))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 126))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 108))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 104))) { + result[0] += 0.0049565816; + } else { + result[0] += 0.063526; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 114))) { + result[0] += -0.019255767; + } else { + result[0] += 0.0012636579; + } + } + } else { + result[0] += 0.008695122; + } + } else { + result[0] += 0.019830158; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 190))) { + result[0] += -0.007866302; + } else { + result[0] += 0.012916532; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 30))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + result[0] += 0.06366762; + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 98))) { + result[0] += -0.004975738; + } else { + result[0] += -0.026275873; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 238))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 148))) { + result[0] += -0.009829768; + } else { + result[0] += -0.028741485; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { + result[0] += -0.0046568112; + } else { + result[0] += -0.028392253; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + result[0] += -0.01022596; + } else { + result[0] += 0.057439942; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 10))) { + result[0] += -0.005370389; + } else { + result[0] += -0.019819807; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 70))) { + result[0] += 0.011075004; + } else { + result[0] += -0.0060161785; + } + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + result[0] += 0.062947415; + } else { + result[0] += 0.0032754538; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 146))) { + result[0] += -0.0023154307; + } else { + result[0] += 0.013836692; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { + result[0] += -0.0051080496; + } else { + result[0] += -0.026022715; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 130))) { + result[0] += -0.021521274; + } else { + result[0] += 0.0008544709; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 44))) { + result[0] += 0.011564689; + } else { + result[0] += 0.039744; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 134))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 202))) { + result[0] += 0.00052881095; + } else { + result[0] += -0.0008797978; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 56))) { + result[0] += 0.002648249; + } else { + result[0] += 0.02056101; + } + } + } + } + } + } + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 160))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 142))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 114))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 92))) { + result[0] += -0.0069403; + } else { + result[0] += 0.0036496245; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { + result[0] += -0.041129064; + } else { + result[0] += -0.007193537; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 138))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { + result[0] += -0.016804745; + } else { + result[0] += -0.035601314; + } + } else { + result[0] += 0.0045456374; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 30))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 154))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { + result[0] += 0.051934537; + } else { + result[0] += -0.0017292331; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 18))) { + result[0] += -0.018062782; + } else { + result[0] += -0.0065657706; + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 36))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 12))) { + result[0] += -0.0018053079; + } else { + result[0] += 0.008867439; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + result[0] += 0.024381882; + } else { + result[0] += 0.0078328205; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 126))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 118))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { + result[0] += -0.0050182706; + } else { + result[0] += 0.0074505755; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 48))) { + result[0] += -0.027363313; + } else { + result[0] += -0.008215737; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 238))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 132))) { + result[0] += 0.0108621; + } else { + result[0] += 0.001027461; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { + result[0] += 0.0018521305; + } else { + result[0] += -0.019366777; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 196))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 66))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 42))) { + result[0] += -0.021509835; + } else { + result[0] += -0.0051732687; + } + } else { + result[0] += -0.036974095; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 10))) { + result[0] += 0.013641315; + } else { + result[0] += -0.0012665674; + } + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 168))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 110))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 382))) { + result[0] += -0.0007951208; + } else { + result[0] += -0.012272722; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { + result[0] += 0.013259816; + } else { + result[0] += 0.00076070236; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 124))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { + result[0] += -0.023466855; + } else { + result[0] += -0.0015092399; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + result[0] += 0.0067120204; + } else { + result[0] += -0.0027905267; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 180))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 132))) { + result[0] += 0.0012523715; + } else { + result[0] += 0.019288411; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 72))) { + result[0] += -0.0024032074; + } else { + result[0] += 0.020834908; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 100))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 302))) { + result[0] += -0.0045833127; + } else { + result[0] += 0.002748456; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + result[0] += -0.00035413975; + } else { + result[0] += 0.0021194927; + } + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 352))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 196))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 36))) { + result[0] += 0.012191023; + } else { + result[0] += -0; + } + } else { + result[0] += 0.026147142; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { + result[0] += -0.0044075255; + } else { + result[0] += -0.018723859; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 368))) { + result[0] += 0.02115663; + } else { + result[0] += -0.008576654; + } + } + } + } + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 116))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 312))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 78))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 146))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 108))) { + result[0] += -0.0011369804; + } else { + result[0] += 0.007111168; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { + result[0] += -0.0066426345; + } else { + result[0] += 0.004026822; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 166))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 126))) { + result[0] += -0.016191296; + } else { + result[0] += -0.008390819; + } + } else { + result[0] += 0.039027248; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 338))) { + result[0] += 0.011132075; + } else { + result[0] += 0.00045876933; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 108))) { + result[0] += 0.03038956; + } else { + result[0] += 0.005012113; + } + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 126))) { + result[0] += -0.002966301; + } else { + result[0] += -0.012031978; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { + result[0] += 0.0043450063; + } else { + result[0] += -0.0033799019; + } + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 74))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 72))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 172))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { + result[0] += 0.00018884889; + } else { + result[0] += 0.004433022; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 140))) { + result[0] += -0.0016364238; + } else { + result[0] += 0.011106547; + } + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { + result[0] += 0.010573897; + } else { + result[0] += -0.046158385; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { + result[0] += 0.025685733; + } else { + result[0] += -0.012269548; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 298))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 258))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 328))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 132))) { + result[0] += 0.012633822; + } else { + result[0] += -0.004591215; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 68))) { + result[0] += 0.027154177; + } else { + result[0] += -0.0073612966; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { + result[0] += -0.009671869; + } else { + result[0] += 0.034116954; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 82))) { + result[0] += 0.003360726; + } else { + result[0] += -0.0010045286; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 242))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 80))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 294))) { + result[0] += -0.0067084306; + } else { + result[0] += 0.017508617; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 52))) { + result[0] += -0.025245184; + } else { + result[0] += -0.009711; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 266))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { + result[0] += 0.0024178706; + } else { + result[0] += 0.02038781; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { + result[0] += -0.005333036; + } else { + result[0] += -0.021810295; + } + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 314))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 148))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 44))) { + result[0] += 0.002272234; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { + result[0] += 0.0017241904; + } else { + result[0] += 0.014623227; + } + } + } else { + result[0] += -0.0057367506; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 140))) { + result[0] += -0.029135648; + } else { + result[0] += -0.0059048724; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 80))) { + result[0] += 0.0016456817; + } else { + result[0] += 0.036199924; + } + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 312))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + result[0] += 5.137796e-05; + } else { + result[0] += 0.016802512; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { + result[0] += -0.007183667; + } else { + result[0] += 0.0086045405; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { + result[0] += -0.0025281047; + } else { + result[0] += -0.022966359; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 250))) { + result[0] += 0.023195243; + } else { + result[0] += 0.003728645; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 186))) { + result[0] += 0.008694565; + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + result[0] += -0.012267268; + } else { + result[0] += 0.012734731; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { + result[0] += 0.023913363; + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 228))) { + result[0] += -0.0029125137; + } else { + result[0] += -0.030048529; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 226))) { + result[0] += -0; + } else { + result[0] += 0.04022684; + } + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 296))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 2))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + result[0] += 0.010619841; + } else { + result[0] += -0.015812444; + } + } else { + result[0] += 0.036461692; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 10))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 92))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 10))) { + result[0] += 0.006464646; + } else { + result[0] += -0.03794737; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { + result[0] += -0.014366518; + } else { + result[0] += 0.00065602706; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += -0.036709867; + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 26))) { + result[0] += 0.009573085; + } else { + result[0] += -0.0042258967; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { + result[0] += 0.025289237; + } else { + result[0] += 0.0031290874; + } + } + } + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 32))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 314))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 176))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 38))) { + result[0] += 0.0070433277; + } else { + result[0] += -0.0021675983; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.00069358293; + } else { + result[0] += 0.014411396; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 224))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 182))) { + result[0] += -0.021365121; + } else { + result[0] += -0.009108902; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 258))) { + result[0] += -0.002070323; + } else { + result[0] += -0.0070032175; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 58))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { + result[0] += 0.015421606; + } else { + result[0] += 0.0065408074; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 68))) { + result[0] += -0.00017713972; + } else { + result[0] += 0.0036349527; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 112))) { + result[0] += -0.015431674; + } else { + result[0] += 0.0069513856; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { + result[0] += -0.0017071629; + } else { + result[0] += 0.0010942215; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 328))) { + result[0] += -0.018242605; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { + result[0] += 0.013435415; + } else { + result[0] += 0.000363245; + } + } else { + result[0] += -0.011918341; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { + result[0] += 0.057550073; + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 64))) { + result[0] += -0.010455087; + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { + result[0] += 0.025410349; + } else { + result[0] += 0.0067503005; + } + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 48))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { + result[0] += 0.0038494274; + } else { + result[0] += -0.012583888; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { + result[0] += 0.0052445154; + } else { + result[0] += 0.022994613; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 110))) { + result[0] += 0.010131297; + } else { + result[0] += 0.030005908; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 36))) { + result[0] += -0.015509938; + } else { + result[0] += 0.033259317; + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 256))) { + result[0] += -0.018772317; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 324))) { + result[0] += -0.0010802757; + } else { + result[0] += -0.019551003; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + result[0] += 0.015505517; + } else { + result[0] += -0.014615647; + } + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 228))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 266))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + result[0] += -0.0011533442; + } else { + result[0] += 0.0037619993; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 42))) { + result[0] += -0.0050083552; + } else { + result[0] += -0.020327281; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += -0.01845751; + } else { + result[0] += -0.0027648432; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 270))) { + result[0] += 0.014990672; + } else { + result[0] += -0.0010248877; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { + result[0] += -0.00588067; + } else { + result[0] += -0.0011169165; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + result[0] += 0.0015550206; + } else { + result[0] += -0.0028203435; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 10))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { + result[0] += 0.00057445857; + } else { + result[0] += -0.020817637; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { + result[0] += 0.004375774; + } else { + result[0] += -0.00017658187; + } + } + } + } + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 116))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 92))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 40))) { + result[0] += -0.029479165; + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { + result[0] += -0.0015629175; + } else { + result[0] += 0.014696643; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { + result[0] += -0.012695557; + } else { + result[0] += 0.002633101; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 38))) { + result[0] += 0.088580444; + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 18))) { + result[0] += -0.0013535247; + } else { + result[0] += -0.041627154; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { + result[0] += 0.010081653; + } else { + result[0] += 0.0011864647; + } + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 158))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { + result[0] += 0.024709804; + } else { + result[0] += 0.006805406; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { + result[0] += -0.020788606; + } else { + result[0] += 0.010531965; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 22))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { + result[0] += 0.029835364; + } else { + result[0] += 0.072482795; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 98))) { + result[0] += -0.0033422098; + } else { + result[0] += -0.027484313; + } + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 198))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 130))) { + result[0] += -0.0117129935; + } else { + result[0] += -0.027794067; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { + result[0] += -0.0058866264; + } else { + result[0] += -0.022589795; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 96))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { + result[0] += -0.007361448; + } else { + result[0] += 0.05226938; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 152))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { + result[0] += -0.0011431018; + } else { + result[0] += -0.013976167; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 34))) { + result[0] += -0; + } else { + result[0] += 0.0123240035; + } + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 16))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 42))) { + result[0] += 0.00010176472; + } else { + result[0] += 0.0130502; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + result[0] += -0.00884224; + } else { + result[0] += -0.0399912; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 102))) { + result[0] += -0.0070857047; + } else { + result[0] += 0.0050304467; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + result[0] += -0.018932706; + } else { + result[0] += -0.0011952891; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { + result[0] += 0.03236731; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 66))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { + result[0] += 0.00022758842; + } else { + result[0] += 0.0045965556; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { + result[0] += 0.0054974416; + } else { + result[0] += -0.00038611452; + } + } + } + } + } + } + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 118))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 140))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + result[0] += -0.030644778; + } else { + result[0] += -0.0012927776; + } + } else { + result[0] += -0.027898073; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 270))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 10))) { + result[0] += -0.0026217196; + } else { + result[0] += 0.00031906084; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { + result[0] += 0.053565513; + } else { + result[0] += 0.011517114; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 242))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 206))) { + result[0] += 0.0022491028; + } else { + result[0] += 0.017666435; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 48))) { + result[0] += -0.039555483; + } else { + result[0] += -0.007261213; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 24))) { + result[0] += -0.0013352408; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { + result[0] += 0.019903926; + } else { + result[0] += -0.0018211514; + } + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 98))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 260))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { + result[0] += -0.003761566; + } else { + result[0] += 5.4693035e-05; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 44))) { + result[0] += 0.066539966; + } else { + result[0] += 0.0024916714; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 282))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + result[0] += 0.0024568306; + } else { + result[0] += 0.013490746; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 144))) { + result[0] += 0.0017763426; + } else { + result[0] += -0.0017636567; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 66))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += -0.016284099; + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { + result[0] += 0.022716116; + } else { + result[0] += 0.0030027088; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 108))) { + result[0] += -0.008329183; + } else { + result[0] += -0.02752207; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { + result[0] += 0.024196664; + } else { + result[0] += -0.0069028423; + } + } + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 268))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 266))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 150))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { + result[0] += 0.03250914; + } else { + result[0] += 0.0055702133; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { + result[0] += 0.008322871; + } else { + result[0] += -0.015406762; + } + } + } else { + result[0] += 0.0395282; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 262))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 260))) { + result[0] += -0.018292183; + } else { + result[0] += -0; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 294))) { + result[0] += -0.0053772978; + } else { + result[0] += 0.0050149076; + } + } + } else { + result[0] += 0.006939339; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 88))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 282))) { + result[0] += 0.00481271; + } else { + result[0] += -0.0038877416; + } + } else { + result[0] += -0.0105433585; + } + } + } + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 320))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 284))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 252))) { + result[0] += 8.760832e-05; + } else { + result[0] += 0.0031985168; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { + result[0] += -0.0029068196; + } else { + result[0] += 0.0005925631; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 242))) { + result[0] += 0.019893859; + } else { + result[0] += 0.009958875; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 266))) { + result[0] += -0.017648466; + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 304))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 302))) { + result[0] += -0.011696982; + } else { + result[0] += 0.004698274; + } + } else { + result[0] += -0.0020677263; + } + } + } + } else { + result[0] += 0.04254042; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 162))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 122))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 76))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 98))) { + result[0] += -0.0029215065; + } else { + result[0] += 0.011221336; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 112))) { + result[0] += 0.020535378; + } else { + result[0] += 0.0062516048; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 302))) { + result[0] += -0.0063436194; + } else { + result[0] += -0.01713322; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 162))) { + result[0] += 0.010334019; + } else { + result[0] += -0.0069331317; + } + } + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 322))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 182))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { + result[0] += -0.0077398308; + } else { + result[0] += -0.03391137; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 190))) { + result[0] += 0.018701833; + } else { + result[0] += 0.005074762; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 192))) { + result[0] += 0.05592671; + } else { + result[0] += 0.012150154; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 138))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 44))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 84))) { + result[0] += -0.004530545; + } else { + result[0] += -0.014109983; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 34))) { + result[0] += 0.016917644; + } else { + result[0] += -0.0003956896; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { + result[0] += 0.018351296; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 222))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 326))) { + result[0] += 0.009391616; + } else { + result[0] += -0.012847346; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + result[0] += 0.0028710503; + } else { + result[0] += -0.014146092; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 24))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { + result[0] += 0.004130573; + } else { + result[0] += -0.02545332; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 136))) { + result[0] += -0.011545375; + } else { + result[0] += 0.0030030604; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 124))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 76))) { + result[0] += 0.0053757923; + } else { + result[0] += 0.026795724; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += 0.0073435195; + } else { + result[0] += -0.011538415; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { + result[0] += 0.010773177; + } else { + result[0] += -0.0014024094; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { + result[0] += -0.00650954; + } else { + result[0] += 0.017750045; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 280))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 256))) { + result[0] += -0.01760956; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 184))) { + result[0] += -0.017441498; + } else { + result[0] += -0.0037122124; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 216))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 182))) { + result[0] += -0.024908429; + } else { + result[0] += -0.012610437; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { + result[0] += 0.020257859; + } else { + result[0] += -0.0005201062; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 20))) { + result[0] += 0.031078367; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 338))) { + result[0] += -0.03464001; + } else { + result[0] += 0.020424169; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { + result[0] += 0.0028230688; + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 114))) { + result[0] += -0.003942101; + } else { + result[0] += -0.017917613; + } + } + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 12))) { + result[0] += -0.018274682; + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 88))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { + result[0] += -0.014930627; + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 22))) { + result[0] += 0.009653761; + } else { + result[0] += -0.005647427; + } + } + } else { + result[0] += -0.017578453; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 216))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 116))) { + result[0] += -0.004387402; + } else { + result[0] += 0.004307063; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 244))) { + result[0] += 0.0050197043; + } else { + result[0] += -0.009600091; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { + result[0] += -0.0004044637; + } else { + result[0] += 0.013372946; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 138))) { + result[0] += -0.030934876; + } else { + result[0] += -0.005925291; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { + result[0] += -0.018131623; + } else { + result[0] += 0.007491207; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 158))) { + result[0] += 0.00448369; + } else { + result[0] += -0.004200383; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 124))) { + result[0] += 0.00013818998; + } else { + result[0] += -0.0060928585; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 226))) { + result[0] += 0.003693558; + } else { + result[0] += -0.0001647349; + } + } + } + } + } + } + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 388))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { + result[0] += 0.00025481472; + } else { + result[0] += 0.015072713; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 56))) { + result[0] += -0.0068883398; + } else { + result[0] += 0.005798895; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + result[0] += -0.012064995; + } else { + result[0] += -0.00055150193; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 236))) { + result[0] += 0.025693614; + } else { + result[0] += -0.0082374755; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 108))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 4))) { + result[0] += 0.020123675; + } else { + result[0] += 0.00013938252; + } + } else { + result[0] += -0.009953358; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 196))) { + result[0] += 0.016889976; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { + result[0] += -0.010803269; + } else { + result[0] += 0.010580909; + } + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 198))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 154))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 144))) { + result[0] += -0; + } else { + result[0] += 0.05305763; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 74))) { + result[0] += -0.0071868575; + } else { + result[0] += 0.06390004; + } + } + } else { + result[0] += 0.057614993; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 176))) { + result[0] += -0.0067237015; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 52))) { + result[0] += 0.0020582944; + } else { + result[0] += 0.009856465; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 86))) { + result[0] += -0.0054150624; + } else { + result[0] += 0.0054157143; + } + } + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 90))) { + result[0] += -0.010108463; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + result[0] += 0.0012366887; + } else { + result[0] += -0.005106163; + } + } + } + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 162))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 150))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 52))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + result[0] += -0.0030484337; + } else { + result[0] += 0.007103072; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { + result[0] += -0.020119462; + } else { + result[0] += 0.0049716155; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 18))) { + result[0] += -0.020776983; + } else { + result[0] += 0.0001524161; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + result[0] += 0.009727257; + } else { + result[0] += 0.0006957262; + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 54))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 152))) { + result[0] += -0.011365656; + } else { + result[0] += -0.0006019767; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 154))) { + result[0] += 0.02111136; + } else { + result[0] += 0.001328925; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 216))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { + result[0] += -0.009145065; + } else { + result[0] += 0.011142318; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { + result[0] += -0.05126782; + } else { + result[0] += -0.02713782; + } + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 138))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 268))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 66))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 42))) { + result[0] += -0.003784838; + } else { + result[0] += 0.0068541127; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 30))) { + result[0] += -0.029539952; + } else { + result[0] += -0.0067226845; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 166))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 44))) { + result[0] += 0.0031095946; + } else { + result[0] += -0.031267606; + } + } else { + result[0] += 0.03802244; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + result[0] += -0.008942714; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 332))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + result[0] += 0.018265491; + } else { + result[0] += 0.01069651; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + result[0] += 0.0040097563; + } else { + result[0] += -0.03305195; + } + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 118))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 70))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 30))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { + result[0] += -0.019308351; + } else { + result[0] += 0.0002994246; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { + result[0] += 0.0010901977; + } else { + result[0] += 0.0058678756; + } + } + } else { + result[0] += 0.024462478; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 74))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + result[0] += -0.012439872; + } else { + result[0] += 0.017688526; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 242))) { + result[0] += -0.0040794015; + } else { + result[0] += -0.009616309; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { + result[0] += 0.0013879368; + } else { + result[0] += 0.017187012; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { + result[0] += -0.0060414565; + } else { + result[0] += 0.00086067413; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 210))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { + result[0] += -0.022543944; + } else { + result[0] += 0.0002652502; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { + result[0] += 0.010252799; + } else { + result[0] += -0.0059008473; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 68))) { + result[0] += -0.00482871; + } else { + result[0] += -0.036633775; + } + } else { + result[0] += -0.020131879; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += 0.019643709; + } else { + result[0] += -0.0055163004; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 110))) { + result[0] += -0.023596639; + } else { + result[0] += -0.005768484; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 214))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 282))) { + result[0] += 0.024731327; + } else { + result[0] += 0.0059899557; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 216))) { + result[0] += -0.012860804; + } else { + result[0] += 0.00026836878; + } + } + } + } + } + } + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 270))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 216))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 188))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { + result[0] += -0.015227991; + } else { + result[0] += 0.007824044; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { + result[0] += -0.0030827313; + } else { + result[0] += 0.04585096; + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 44))) { + result[0] += 0.0038916196; + } else { + result[0] += -0.00018074422; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += -0; + } else { + result[0] += 0.01431708; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 8))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 40))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 254))) { + result[0] += -0.03443868; + } else { + result[0] += -0.016926734; + } + } else { + result[0] += 0.016495222; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 318))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 150))) { + result[0] += -0.0018124201; + } else { + result[0] += -0.008941532; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + result[0] += 0.010480117; + } else { + result[0] += -0.0053374222; + } + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 144))) { + result[0] += -0.0025247212; + } else { + result[0] += -0.021172313; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 278))) { + result[0] += 0.0136889; + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 24))) { + result[0] += -0; + } else { + result[0] += -0.028621003; + } + } else { + result[0] += 0.008883018; + } + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 272))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 120))) { + result[0] += 0.004496847; + } else { + result[0] += -0.0019887805; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 60))) { + result[0] += 0.011425511; + } else { + result[0] += 0.001471782; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 164))) { + result[0] += 0.004373122; + } else { + result[0] += -0.0050846357; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 312))) { + result[0] += -0.01642145; + } else { + result[0] += -0.0041971803; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 240))) { + result[0] += -0.019498728; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 296))) { + result[0] += 0.016818725; + } else { + result[0] += 0.031616382; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 256))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 152))) { + result[0] += 0.0041652615; + } else { + result[0] += 0.024725467; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { + result[0] += -0.017824838; + } else { + result[0] += 0.00069643237; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 104))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 346))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 138))) { + result[0] += 0.057328846; + } else { + result[0] += -0.009282811; + } + } else { + result[0] += -0.027812624; + } + } else { + result[0] += 0.006232637; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 208))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 184))) { + result[0] += -0.0007241729; + } else { + result[0] += 0.01345086; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { + result[0] += -0.01243884; + } else { + result[0] += -0.0010584429; + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 48))) { + result[0] += 0.012493608; + } else { + result[0] += 0.0014906918; + } + } else { + result[0] += -0.011661606; + } + } + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 2))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 216))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 58))) { + result[0] += 0.0059683393; + } else { + result[0] += 0.047656022; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 76))) { + result[0] += -0.01823411; + } else { + result[0] += 0.011243396; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 88))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 36))) { + result[0] += -0.015964573; + } else { + result[0] += 0.018772287; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 84))) { + result[0] += -0.024348408; + } else { + result[0] += 0.0071454565; + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 118))) { + result[0] += 0.0038073042; + } else { + result[0] += -0.010677494; + } + } else { + result[0] += -0.010760325; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 166))) { + result[0] += -0.013037783; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 196))) { + result[0] += 0.04078368; + } else { + result[0] += -0; + } + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 110))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 24))) { + result[0] += -0.019799097; + } else { + result[0] += 0.0018352288; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 14))) { + result[0] += 0.016346006; + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 266))) { + result[0] += -0.018813994; + } else { + result[0] += -0.007913149; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 280))) { + result[0] += 0.000356507; + } else { + result[0] += 0.004809927; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 298))) { + result[0] += -0.015981428; + } else { + result[0] += -0.0013808893; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 84))) { + result[0] += -0.0013645035; + } else { + result[0] += -0.009478527; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { + result[0] += 0.01796195; + } else { + result[0] += -0.00023798608; + } + } + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 142))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 210))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 26))) { + result[0] += -0.0045832377; + } else { + result[0] += -0.03190832; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { + result[0] += 0.0066647097; + } else { + result[0] += -0.002318192; + } + } + } else { + result[0] += -0.040041517; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 264))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 270))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 54))) { + result[0] += -0.0019740702; + } else { + result[0] += -0.024438191; + } + } else { + result[0] += -0; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { + result[0] += 0.029889846; + } else { + result[0] += 0.0023792638; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { + result[0] += 0.0048653954; + } else { + result[0] += -0.013145282; + } + } + } + } + } else { + result[0] += 0.026439697; + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 50))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 92))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 154))) { + result[0] += -0.023412313; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 164))) { + result[0] += 0.020196555; + } else { + result[0] += -0.012161604; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 38))) { + result[0] += 0.08170753; + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 18))) { + result[0] += -0.002143487; + } else { + result[0] += -0.038541686; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 50))) { + result[0] += -0.006611839; + } else { + result[0] += 0.008542123; + } + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 200))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 132))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 110))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 46))) { + result[0] += 0.0018188714; + } else { + result[0] += -0.015560075; + } + } else { + result[0] += 0.021364069; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 112))) { + result[0] += -0.029760977; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 148))) { + result[0] += 0.0078332; + } else { + result[0] += -0.011854128; + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 64))) { + result[0] += 0.014748133; + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { + result[0] += -0.012884839; + } else { + result[0] += 0.0029220346; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 42))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 148))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + result[0] += 0.015425849; + } else { + result[0] += -0.004185417; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 98))) { + result[0] += -0.0011755283; + } else { + result[0] += -0.03457963; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 266))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { + result[0] += -0.009746331; + } else { + result[0] += -0.030245284; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { + result[0] += -0.006777007; + } else { + result[0] += -0.02345091; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 92))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { + result[0] += -0.008947103; + } else { + result[0] += 0.03920984; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 148))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 6))) { + result[0] += 0.009707036; + } else { + result[0] += -0.0060035028; + } + } else { + result[0] += 0.01016276; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 28))) { + result[0] += -0; + } else { + result[0] += 0.032831904; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { + result[0] += 6.658281e-06; + } else { + result[0] += 0.0031512368; + } + } else { + result[0] += 0.02200143; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { + result[0] += 0.033468585; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 82))) { + result[0] += -0.008753168; + } else { + result[0] += -0.0016586205; + } + } + } + } + } + } + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 26))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + result[0] += -0.033794925; + } else { + result[0] += 0.0085956175; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { + result[0] += 0.02062062; + } else { + result[0] += -0.0024983874; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 30))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { + result[0] += -0; + } else { + result[0] += 0.02111803; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 66))) { + result[0] += 0.048572876; + } else { + result[0] += 0.007911704; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 112))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 68))) { + result[0] += -0.0077246563; + } else { + result[0] += 0.005386382; + } + } else { + result[0] += -0.020841127; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 48))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { + result[0] += -0.014042695; + } else { + result[0] += 0.008498053; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 12))) { + result[0] += -0.024558064; + } else { + result[0] += -0.0025908693; + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 24))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { + result[0] += 2.548086e-05; + } else { + result[0] += 0.026205242; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 92))) { + result[0] += 0.009812611; + } else { + result[0] += 0.00306112; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { + result[0] += 0.027721763; + } else { + result[0] += 0.006687283; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 16))) { + result[0] += 0.035541125; + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 18))) { + result[0] += -0.017473036; + } else { + result[0] += -0.0030920196; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { + result[0] += -0.00029661623; + } else { + result[0] += -0.006844879; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += 0.009076519; + } else { + result[0] += 0.00021811183; + } + } + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 36))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { + result[0] += 0.026330112; + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 248))) { + result[0] += 0.01319269; + } else { + result[0] += -0.004578778; + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 62))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { + result[0] += -0; + } else { + result[0] += -0.017771253; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 68))) { + result[0] += 0.041357838; + } else { + result[0] += 0.0004609677; + } + } + } + } + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 116))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { + result[0] += -0.03449745; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 10))) { + result[0] += -0.000830644; + } else { + result[0] += 0.027823929; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { + result[0] += -0.014675349; + } else { + result[0] += 0.002744519; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { + result[0] += 0.00021282148; + } else { + result[0] += 0.012012251; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { + result[0] += -0.023646448; + } else { + result[0] += -0.0012802199; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 64))) { + result[0] += 0.005379937; + } else { + result[0] += 0.020074537; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { + result[0] += -0.017483069; + } else { + result[0] += 0.00045991183; + } + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 28))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { + result[0] += -0.010277932; + } else { + result[0] += 0.020262284; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { + result[0] += 0.026524289; + } else { + result[0] += 0.0005923277; + } + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 78))) { + result[0] += -0.023216326; + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 160))) { + result[0] += -0.0063289674; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 80))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 124))) { + result[0] += -0.026424408; + } else { + result[0] += -0.00650081; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { + result[0] += 0.035873644; + } else { + result[0] += -0.0067583695; + } + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 134))) { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { + result[0] += 0.019181004; + } else { + result[0] += 0.0012452018; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + result[0] += -0.006571722; + } else { + result[0] += -0.00113232; + } + } + } + } + } + } + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 248))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 270))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 184))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 104))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 312))) { + result[0] += -0.001673722; + } else { + result[0] += 0.0015081331; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { + result[0] += 0.019442735; + } else { + result[0] += -0.0041994313; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { + result[0] += 0.00018035778; + } else { + result[0] += -0.012916463; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { + result[0] += 0.003256688; + } else { + result[0] += -0.0061490457; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { + result[0] += -0.002768964; + } else { + result[0] += 0.036568955; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { + result[0] += 0.0035748954; + } else { + result[0] += -0.0018361468; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { + result[0] += 0.0008316287; + } else { + result[0] += -0.012621154; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { + result[0] += 0.0009690163; + } else { + result[0] += -0.009806303; + } + } + } + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 226))) { + result[0] += -0.008160834; + } else { + result[0] += 0.010674962; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 66))) { + result[0] += -0.018799648; + } else { + result[0] += -0.0010747229; + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 274))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 252))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { + result[0] += 0.013668454; + } else { + result[0] += 0.0015821367; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 254))) { + result[0] += 0.0013118708; + } else { + result[0] += -0.0027904937; + } + } + } else { + result[0] += 0.013429761; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 150))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 278))) { + result[0] += 0.0026331868; + } else { + result[0] += 0.02152044; + } + } else { + result[0] += 0.010367058; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + result[0] += -0.009165699; + } else { + result[0] += 0.012586451; + } + } else { + result[0] += 0.017478531; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 138))) { + result[0] += 0.03761896; + } else { + result[0] += -0.009919451; + } + } else { + result[0] += -0.024488965; + } + } else { + result[0] += 0.0063245073; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { + result[0] += 0.004199187; + } else { + result[0] += -0.0270401; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 12))) { + result[0] += -0.034475163; + } else { + result[0] += 0.03449012; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 204))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { + result[0] += -0.008276438; + } else { + result[0] += -0.021829268; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 348))) { + result[0] += -0.00024369739; + } else { + result[0] += 0.018283892; + } + } + } + } + } + } + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + result[0] += 0.015541151; + } else { + result[0] += -0.004526817; + } + } else { + result[0] += 0.04517481; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 44))) { + result[0] += -0.008011468; + } else { + result[0] += -0.0023013349; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 16))) { + result[0] += 0.027930424; + } else { + result[0] += 0.001679267; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 92))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 20))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 32))) { + result[0] += 0.022154698; + } else { + result[0] += -0.01756174; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 76))) { + result[0] += -0.045003; + } else { + result[0] += -0.010276439; + } + } + } else { + result[0] += 0.001000655; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 116))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { + result[0] += 0.0012434544; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 160))) { + result[0] += 0.018014176; + } else { + result[0] += -0.00070313603; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 126))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { + result[0] += 0.0062645376; + } else { + result[0] += -0.015609448; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { + result[0] += 0.01479733; + } else { + result[0] += 0.0014734569; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 134))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 304))) { + result[0] += -0; + } else { + result[0] += -0.012774549; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 40))) { + result[0] += 0.014705256; + } else { + result[0] += -0.004232665; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 314))) { + result[0] += 0.0029532865; + } else { + result[0] += -0.0029254516; + } + } else { + result[0] += 0.026304556; + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 128))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 256))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 150))) { + result[0] += 0.00030722877; + } else { + result[0] += -0.0051551643; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + result[0] += 0.016633255; + } else { + result[0] += 0.001668491; + } + } + } else { + result[0] += -0.014828484; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 236))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 254))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 286))) { + result[0] += 0.0071044452; + } else { + result[0] += -0.0076894383; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 54))) { + result[0] += 0.02084865; + } else { + result[0] += 0.007473866; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 278))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { + result[0] += 0.005741518; + } else { + result[0] += 0.03405652; + } + } else { + result[0] += 0.025841344; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 22))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 150))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 154))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 152))) { + result[0] += 0.00015940488; + } else { + result[0] += 0.05232349; + } + } else { + result[0] += -0.018910853; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { + result[0] += -0.051273484; + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 106))) { + result[0] += -0.02290248; + } else { + result[0] += -0.0016118204; + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { + result[0] += 0.0010482629; + } else { + result[0] += -0.016297817; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 196))) { + result[0] += 0.013967775; + } else { + result[0] += 0.027914885; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 74))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 2))) { + result[0] += 0.038794536; + } else { + result[0] += -0.0062946193; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + result[0] += -2.64479e-05; + } else { + result[0] += 0.021996183; + } + } + } + } + } + } + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 104))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 40))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 28))) { + result[0] += -0.005473042; + } else { + result[0] += 0.011005775; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 136))) { + result[0] += -0.011721667; + } else { + result[0] += 0.0078737205; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 46))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 38))) { + result[0] += 0.0025443584; + } else { + result[0] += -0.016291086; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += 0.00024633956; + } else { + result[0] += 0.027108392; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 26))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 10))) { + result[0] += 0.03744457; + } else { + result[0] += 0.0014647435; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 136))) { + result[0] += -0.0058734766; + } else { + result[0] += -0.02578007; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 50))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += -0.00944722; + } else { + result[0] += -0.03411841; + } + } else { + result[0] += -0.001750114; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 14))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 100))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 24))) { + result[0] += 0.0006245327; + } else { + result[0] += 0.02487868; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 18))) { + result[0] += 0.003864918; + } else { + result[0] += 0.04562244; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 94))) { + result[0] += -0; + } else { + result[0] += -0.037629705; + } + } else { + result[0] += 0.017797282; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { + result[0] += 0.03347585; + } else { + result[0] += -0.00015754919; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + result[0] += 0.042547952; + } else { + result[0] += 0.008394706; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 304))) { + result[0] += -0.0053476547; + } else { + result[0] += 0.019664986; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 274))) { + result[0] += 0.00061302836; + } else { + result[0] += -0.008260283; + } + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 112))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 164))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + result[0] += -0.0029065635; + } else { + result[0] += 0.012601085; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { + result[0] += 0.011005281; + } else { + result[0] += -0.010062504; + } + } + } else { + result[0] += 0.015878765; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 182))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 154))) { + result[0] += -0.012548672; + } else { + result[0] += -0.036353104; + } + } else { + result[0] += -0.0037594133; + } + } else { + result[0] += -0.026142243; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 110))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 108))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { + result[0] += 0.0049291016; + } else { + result[0] += 0.02395081; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 234))) { + result[0] += -0.0030472444; + } else { + result[0] += 0.0040821205; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 100))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 114))) { + result[0] += -0.020026503; + } else { + result[0] += -0.0037345327; + } + } else { + result[0] += -0.025079226; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 128))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 128))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { + result[0] += 0.006035849; + } else { + result[0] += -0.005563633; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 134))) { + result[0] += -0.017343946; + } else { + result[0] += 0.014413935; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { + result[0] += 0.007634467; + } else { + result[0] += -0.024592249; + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { + result[0] += 0.0005188165; + } else { + result[0] += -0.006757953; + } + } + } + } + } + } + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { + result[0] += 0.00393571; + } else { + result[0] += 0.02043746; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 262))) { + result[0] += -0.01545644; + } else { + result[0] += -0.002095156; + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 210))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { + result[0] += -0.00013221658; + } else { + result[0] += -0.002142299; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + result[0] += -0.0012591215; + } else { + result[0] += 0.0015919211; + } + } + } + } else { + result[0] += 0.011080801; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 300))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 142))) { + result[0] += -0.030891055; + } else { + result[0] += -0.011523842; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 296))) { + result[0] += -0.0094786985; + } else { + result[0] += 0.00014606201; + } + } + } else { + result[0] += 0.045800555; + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 18))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 0))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { + result[0] += 0.021174032; + } else { + result[0] += -0; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { + result[0] += -0; + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 170))) { + result[0] += -0.02313884; + } else { + result[0] += -0; + } + } + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { + result[0] += 0.0034402236; + } else { + result[0] += 0.03586924; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 150))) { + result[0] += -0.007850896; + } else { + result[0] += 0.0023356122; + } + } + } else { + result[0] += 0.019080771; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 340))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 216))) { + result[0] += -0.006365792; + } else { + result[0] += 0.012724089; + } + } else { + result[0] += -0.03172406; + } + } + } + } + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 350))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 142))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 116))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 138))) { + result[0] += -0.00062813837; + } else { + result[0] += 0.002671611; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 86))) { + result[0] += 0.00652712; + } else { + result[0] += 0.00037897687; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 286))) { + result[0] += -0.00025623475; + } else { + result[0] += -0.004133305; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 82))) { + result[0] += -0.01589259; + } else { + result[0] += 0.019172618; + } + } + } + } else { + result[0] += 0.009757864; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 250))) { + result[0] += -0.0065281326; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 254))) { + result[0] += 0.0007117824; + } else { + result[0] += 0.027270088; + } + } else { + result[0] += -0.0023996648; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 248))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 48))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { + result[0] += -0.0019857192; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 222))) { + result[0] += 0.025358735; + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { + result[0] += -0.0021736256; + } else { + result[0] += 0.016332442; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 52))) { + result[0] += -0.0129296705; + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 224))) { + result[0] += -0; + } else { + result[0] += 0.018950501; + } + } + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 68))) { + result[0] += -0.0070409672; + } else { + result[0] += 0.010926886; + } + } + } + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 312))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 248))) { + result[0] += -0.00022246773; + } else { + result[0] += 0.00076635665; + } + } else { + result[0] += 0.012082838; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 286))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 298))) { + result[0] += -0.015522775; + } else { + result[0] += -0.0064152265; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 348))) { + result[0] += -0.0013468252; + } else { + result[0] += 0.020515444; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 276))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 150))) { + result[0] += 0.0077754804; + } else { + result[0] += -0.0030992497; + } + } else { + result[0] += -0.017411573; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 250))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { + result[0] += 0.003438398; + } else { + result[0] += 0.032958325; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 30))) { + result[0] += 0.013451094; + } else { + result[0] += -0.006546424; + } + } + } + } + } else { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 188))) { + result[0] += 0.009157052; + } else { + result[0] += -0.010829896; + } + } else { + result[0] += 0.015605274; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { + result[0] += 0.01869001; + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 228))) { + result[0] += -0.0042858357; + } else { + result[0] += -0.02192712; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 226))) { + result[0] += -0.0021264374; + } else { + result[0] += 0.028851261; + } + } + } + } + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 150))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 282))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { + result[0] += 0.008089608; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 252))) { + result[0] += -0.02760528; + } else { + result[0] += -0.014671764; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { + result[0] += 0.0007742315; + } else { + result[0] += -0.014570135; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 310))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 264))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 52))) { + result[0] += 0.004098795; + } else { + result[0] += -0.0013311962; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 250))) { + result[0] += -0.016194416; + } else { + result[0] += -0.004321107; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 246))) { + result[0] += 0.012578972; + } else { + result[0] += 0.031848185; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { + result[0] += 0.002893864; + } else { + result[0] += -0.0014323759; + } + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 48))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 24))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += -0.0058673653; + } else { + result[0] += 0.014972388; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 32))) { + result[0] += -0.016248314; + } else { + result[0] += -0; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 42))) { + result[0] += 0.001427743; + } else { + result[0] += 0.013427376; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 218))) { + result[0] += 0.004056875; + } else { + result[0] += -0.0111101195; + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 142))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 108))) { + result[0] += 0.009302656; + } else { + result[0] += 0.0009259971; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { + result[0] += -0.0071252473; + } else { + result[0] += 0.0005085042; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 100))) { + result[0] += 0.0055279406; + } else { + result[0] += -0.015505721; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 152))) { + result[0] += 0.022024345; + } else { + result[0] += 0.013201664; + } + } + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 30))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 140))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 24))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 154))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 34))) { + result[0] += -0.0031792417; + } else { + result[0] += -0.024321148; + } + } else { + result[0] += 0.0074777827; + } + } else { + if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { + result[0] += -0.015280488; + } else { + result[0] += 0.004024551; + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 26))) { + result[0] += 0.013732604; + } else { + result[0] += -0; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 74))) { + result[0] += -0.028851384; + } else { + result[0] += -0.051958527; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { + result[0] += 0.010219454; + } else { + result[0] += -0.009982391; + } + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 162))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 256))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 158))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { + result[0] += 0.014080286; + } else { + result[0] += -0.004486435; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 136))) { + result[0] += 0.005933493; + } else { + result[0] += 0.02153199; + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + result[0] += -0.02032683; + } else { + result[0] += 0.002681443; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 214))) { + result[0] += -0.0042459033; + } else { + result[0] += 0.0068887584; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { + result[0] += 0.00047894806; + } else { + result[0] += -0.0061863232; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 230))) { + result[0] += 0.008450966; + } else { + result[0] += 0.0015639787; + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 200))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { + result[0] += -0.007651621; + } else { + result[0] += -0.0009728819; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { + result[0] += 0.0062414994; + } else { + result[0] += -0.0019789643; + } + } + } + } + } + } + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 312))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { + result[0] += -0.0028604511; + } else { + result[0] += 0.0004893718; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 224))) { + result[0] += 0.026684532; + } else { + result[0] += 0.0063786306; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 16))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 206))) { + result[0] += -0.009385272; + } else { + result[0] += 0.008824005; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 256))) { + result[0] += -0.00080077257; + } else { + result[0] += 0.0010740731; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 374))) { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 224))) { + result[0] += 0.014482776; + } else { + result[0] += -0.0036570956; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 308))) { + result[0] += 0.009366672; + } else { + result[0] += -0.00948626; + } + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { + result[0] += -0.005847118; + } else { + result[0] += 0.033575047; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 188))) { + result[0] += 0.0021236583; + } else { + result[0] += -0.01067119; + } + } else { + result[0] += 0.0088947555; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { + result[0] += 0.018932056; + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { + result[0] += -0.0033845974; + } else { + result[0] += -0.020902513; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 250))) { + result[0] += 0.0032020702; + } else { + result[0] += 0.042684905; + } + } + } + } + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 18))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 70))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { + result[0] += -0.008612228; + } else { + result[0] += 0.0040322477; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { + result[0] += -0.008511812; + } else { + result[0] += 0.011585411; + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 44))) { + result[0] += 1.8034565e-05; + } else { + result[0] += 0.027274786; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 64))) { + result[0] += 0.008447873; + } else { + result[0] += -0.025014887; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 58))) { + result[0] += -0.007062527; + } else { + result[0] += 0.014613422; + } + } else { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 24))) { + result[0] += 0.0011185434; + } else { + result[0] += 0.008517242; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { + result[0] += -0.025690308; + } else { + result[0] += -0.0029228963; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 120))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 46))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { + result[0] += -0.017073292; + } else { + result[0] += 0.0035216322; + } + } else { + result[0] += -0.023047855; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { + result[0] += -0.025554648; + } else { + result[0] += -0.0017376606; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { + result[0] += -0.0054964907; + } else { + result[0] += 0.037392512; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { + result[0] += -0.05761969; + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 98))) { + result[0] += -0.016015155; + } else { + result[0] += 0.0068704; + } + } else { + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 76))) { + result[0] += -0.015397488; + } else { + result[0] += -0.04052282; + } + } + } + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 86))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 4))) { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 52))) { + result[0] += 0.0003938696; + } else { + result[0] += 0.0045072534; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { + result[0] += -0.0024714952; + } else { + result[0] += 0.0049051414; + } + } + } else { + result[0] += 0.017899476; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 380))) { + result[0] += -0.008237792; + } else { + result[0] += -0.0031683035; + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 282))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 174))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 100))) { + result[0] += -0.0050061867; + } else { + result[0] += 0.00018127509; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { + result[0] += -0.0051624607; + } else { + result[0] += 0.00025673906; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 156))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { + result[0] += -0.0046179085; + } else { + result[0] += 0.026366374; + } + } else { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 8))) { + result[0] += -0; + } else { + result[0] += 0.018350547; + } + } + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 268))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 220))) { + result[0] += -0.0091535505; + } else { + result[0] += -0.042126443; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 248))) { + result[0] += -0.03904317; + } else { + result[0] += -0.0013817366; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 44))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 122))) { + result[0] += 0.0032674843; + } else { + result[0] += 0.038644753; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { + result[0] += -0.003548465; + } else { + result[0] += 0.044352487; + } + } + } + } + } + } + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 168))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 102))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { + result[0] += -0.0006659017; + } else { + result[0] += -0.017484738; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 124))) { + result[0] += 0.021091629; + } else { + result[0] += -0.0017686317; + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { + result[0] += -0.035111748; + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 46))) { + result[0] += -0.0162628; + } else { + result[0] += -0.004374349; + } + } + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 8))) { + result[0] += -0.031120077; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { + result[0] += -0.0055634947; + } else { + result[0] += -0.017814448; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 120))) { + result[0] += 0.042426206; + } else { + result[0] += -0.0013250493; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 58))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 132))) { + result[0] += 0.03963465; + } else { + result[0] += 0.018596584; + } + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 94))) { + result[0] += -0.0008957187; + } else { + result[0] += -0.03431357; + } + } else { + result[0] += 0.015205196; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 84))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 102))) { + result[0] += 0.012878765; + } else { + result[0] += -0.013091402; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 140))) { + result[0] += -0.022308733; + } else { + result[0] += 0.0014070682; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 38))) { + result[0] += -0.018751746; + } else { + result[0] += -0.041268926; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 52))) { + result[0] += -0.0035958923; + } else { + result[0] += 9.794186e-05; + } + } + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 352))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 198))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 120))) { + result[0] += 0.013142203; + } else { + result[0] += -0.0014631682; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += -0; + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 16))) { + result[0] += -0.01765822; + } else { + result[0] += 0.0049266764; + } + } + } + } else { + result[0] += -0.0039253565; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 368))) { + result[0] += 0.015282332; + } else { + result[0] += -0.0067160674; + } + } + } + if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 214))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 176))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 32))) { + result[0] += -0.0012096122; + } else { + result[0] += 0.004681909; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { + result[0] += -0.013531153; + } else { + result[0] += 0.004223753; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 72))) { + result[0] += -0.0026232149; + } else { + result[0] += -0.01515016; + } + } else { + if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 170))) { + result[0] += -0.00014745975; + } else { + result[0] += 0.0059427386; + } + } + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { + if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 322))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 146))) { + result[0] += 0.011954216; + } else { + result[0] += -0.00069962896; + } + } else { + result[0] += 0.03788197; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + result[0] += -0.0036799812; + } else { + result[0] += -0.014614584; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 194))) { + result[0] += -0; + } else { + result[0] += 0.020192096; + } + } + } + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 246))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { + if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 36))) { + result[0] += 0.010108634; + } else { + result[0] += 0.0014457417; + } + } else { + if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { + result[0] += -0.0062636524; + } else { + result[0] += 0.00020081793; + } + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { + result[0] += -0.011251283; + } else { + result[0] += 0.0056470656; + } + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { + result[0] += 0.0014956547; + } else { + result[0] += 0.0059374445; + } + } + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 266))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 114))) { + result[0] += -0.010740235; + } else { + result[0] += 0.00111787; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + result[0] += -0.001297196; + } else { + result[0] += 0.0012389937; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 240))) { + result[0] += 0.01125375; + } else { + result[0] += -0.03845505; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 292))) { + result[0] += -0.008091416; + } else { + result[0] += 0.0020921114; + } + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 8))) { + result[0] += -0.040072184; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 136))) { + result[0] += 0.013737966; + } else { + result[0] += -0.00681287; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 132))) { + if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { + result[0] += 0.028193597; + } else { + result[0] += -0.0037449375; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + result[0] += 0.015007503; + } else { + result[0] += -0.009241991; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 94))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { + result[0] += -0.003027987; + } else { + result[0] += -0.019359348; + } + } else { + result[0] += 0.0025615941; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + result[0] += 0.023024825; + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 218))) { + result[0] += -0.0007633574; + } else { + result[0] += -0.006595341; + } + } + } + } + } else { + if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { + result[0] += -0.039751314; + } else { + if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 108))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 308))) { + result[0] += 0.011828893; + } else { + result[0] += -0.009816745; + } + } else { + result[0] += -0.015453085; + } + } + } else { + result[0] += 0.0023617218; + } + } + } + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 322))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 282))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 272))) { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { + result[0] += -2.5651694e-05; + } else { + result[0] += 0.004297465; + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { + result[0] += -0.012060625; + } else { + result[0] += 0.027334878; + } + } + } else { + result[0] += 0.0077310125; + } + } else { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 112))) { + result[0] += -0.012176646; + } else { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 306))) { + result[0] += 0.0043146396; + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 278))) { + result[0] += -0.010458501; + } else { + result[0] += -0.0025602286; + } + } + } + } + } else { + result[0] += 0.026633803; + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { + if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 14))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 122))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { + result[0] += 0.009240702; + } else { + result[0] += 0.02637141; + } + } else { + result[0] += -0.0056706998; + } + } else { + if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 10))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 286))) { + result[0] += -0.003625507; + } else { + result[0] += 0.0070409435; + } + } else { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 280))) { + result[0] += 0.014894609; + } else { + result[0] += -0.003168697; + } + } + } + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 178))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + result[0] += -0.015371713; + } else { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { + result[0] += -0.009225684; + } else { + result[0] += 0.00080344983; + } + } + } else { + result[0] += -0.034244932; + } + } + } else { + if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 72))) { + if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { + if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 184))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += -0.013472222; + } else { + result[0] += 0.00748849; + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 304))) { + result[0] += 0.0054984465; + } else { + result[0] += 0.048147988; + } + } + } else { + result[0] += -0.010059855; + } + } else { + if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { + if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 250))) { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 278))) { + result[0] += 0.0179956; + } else { + result[0] += 0.00028234924; + } + } else { + if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 330))) { + result[0] += -0.005519641; + } else { + result[0] += -0.03736005; + } + } + } else { + if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 226))) { + result[0] += -0.0048794984; + } else { + result[0] += 0.007994923; + } + } + } + } + } + + // Apply base_scores + result[0] += -1.882233619689941406; + result[0] = std::exp(result[0]); + + // Apply postprocessor + if (!pred_margin) { postprocess(result); } +} + +void dualsimplex_predictor::postprocess(double* result) +{ + // Do nothing +} + +// Feature names array +const char* dualsimplex_predictor::feature_names[dualsimplex_predictor::NUM_FEATURES] = { + "m", + "n", + "nnz", + "density", + "avg_nnz_col", + "avg_nnz_row", + "bounded", + "free", + "refact_freq", + "num_refacts", + "num_updates", + "sparse_dz", + "dense_dz", + "bound_flips", + "num_infeas", + "dy_nz_pct", + "byte_loads", + "byte_stores"}; diff --git a/cpp/src/utilities/models/dualsimplex_predictor/quantize.cpp b/cpp/src/utilities/models/dualsimplex_predictor/quantize.cpp new file mode 100644 index 0000000000..ab62e5687e --- /dev/null +++ b/cpp/src/utilities/models/dualsimplex_predictor/quantize.cpp @@ -0,0 +1,1891 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "header.h" + +static const float threshold[] = { + 125, + 162, + 329, + 335, + 359, + 402, + 443, + 447, + 465, + 482, + 550, + 581, + 677, + 810, + 836, + 865, + 1004, + 1082, + 1388, + 1652, + 1690, + 1754, + 1820, + 1916, + 2387, + 2432, + 2471, + 2487, + 2676, + 3022, + 3128, + 3174, + 3472, + 3480, + 3897, + 4179, + 4193, + 4233, + 4240, + 4462, + 4480, + 4561, + 4661, + 4813, + 4934, + 5355, + 5593, + 6119, + 6332, + 6474, + 6504, + 6574, + 7260, + 8469, + 8488, + 8579, + 9001, + 9809, + 10400, + 10418, + 10711, + 10837, + 12702, + 13228, + 13552, + 14039, + 14653, + 15663, + 16026, + 16418, + 16488, + 16924, + 18083, + 18157, + 18525, + 18558, + 18584, + 18608, + 19134, + 20335, + 22063, + 24884, + 32736, + 33373, + 37617, + 39261, + 45220, + 48604, + 49951, + 53360, + 56116, + 57099, + 59576, + 67583, + 71393, + 88441, + 90924, + 99789, + 105209, + 105933, + 131865, + 163021, + 169576, + 224453, + 259962, + 265341, + 320520, + 349602, + 957446, + 1266, + 1314, + 2224, + 2246, + 2802, + 2825, + 2979, + 2984, + 4950, + 5769, + 5783, + 5970, + 6042, + 6519, + 6734, + 6742, + 7295, + 7784, + 8179, + 8393, + 8634, + 9240, + 9756, + 13075, + 13650, + 13802, + 14563, + 14619, + 15528, + 15655, + 15789, + 16634, + 16952, + 18099, + 18166, + 18178, + 18904, + 19457, + 19794, + 21196, + 22038, + 22645, + 24627, + 24951, + 25483, + 25720, + 30733, + 33499, + 33508, + 34436, + 37008, + 45637, + 47542, + 52526, + 56443, + 60016, + 60697, + 65771, + 65861, + 74900, + 75951, + 78534, + 88088, + 90196, + 114164, + 132419, + 141311, + 143793, + 152283, + 154622, + 159620, + 164010, + 164045, + 228960, + 233606, + 263018, + 266451, + 327865, + 408066, + 449518, + 471906, + 563256, + 1439711, + 1667610, + 2884312, + 2094, + 2530, + 2978, + 4192, + 5810, + 7766, + 8669, + 14451, + 15023, + 16817, + 19230, + 20237, + 20696, + 20930, + 21776, + 29160, + 36272, + 37026, + 40332, + 41996, + 43680, + 45193, + 50261, + 56378, + 77968, + 81543, + 81717, + 81884, + 84033, + 103839, + 109475, + 115299, + 120841, + 138700, + 141395, + 143805, + 144427, + 154634, + 171928, + 187719, + 188804, + 217438, + 217800, + 244409, + 260869, + 265420, + 280821, + 290749, + 292903, + 311318, + 352291, + 367831, + 373297, + 391677, + 394271, + 414508, + 435069, + 436515, + 466492, + 490740, + 580585, + 584200, + 819580, + 859035, + 1003742, + 1096149, + 1102562, + 1117965, + 1348523, + 1706240, + 1754504, + 1764516, + 1987895, + 2108293, + 2188365, + 4297708, + 5183486, + 7959863, + 11797396, + 1.01236e-05, + 1.146592e-05, + 1.7583379e-05, + 1.819257e-05, + 1.8350371e-05, + 2.181823e-05, + 2.1838039e-05, + 2.269307e-05, + 2.609327e-05, + 2.8569561e-05, + 3.116237e-05, + 3.2021151e-05, + 3.4602828e-05, + 3.5382109e-05, + 3.5911838e-05, + 3.710145e-05, + 3.8838469e-05, + 4.0873962e-05, + 4.824765e-05, + 5.1134e-05, + 5.5583841e-05, + 6.4015032e-05, + 7.5606709e-05, + 7.8921999e-05, + 8.1764643e-05, + 0.0001105412, + 0.0001109023, + 0.0001204482, + 0.0001509623, + 0.00016829019, + 0.0001782324, + 0.0001799585, + 0.0001880354, + 0.00019853171, + 0.0002269696, + 0.0002535714, + 0.0002745305, + 0.00028268999, + 0.00028290771, + 0.0002968506, + 0.0003150893, + 0.0003669617, + 0.00043851949, + 0.00044883709, + 0.00045417139, + 0.00049108238, + 0.00051668438, + 0.00067281991, + 0.00075661199, + 0.00083260372, + 0.0008424538, + 0.00090097258, + 0.001032864, + 0.001152342, + 0.001232837, + 0.001377049, + 0.001454633, + 0.001720742, + 0.001807331, + 0.0019736169, + 0.002305151, + 0.002327027, + 0.002512071, + 0.003719569, + 0.0038111249, + 0.0071778512, + 0.0079891831, + 0.0096212169, + 0.01194467, + 0.01507965, + 0.095635854, + 0.14575709, + 0.33324131, + 1.6, + 1.63, + 1.64, + 1.65, + 1.66, + 1.7, + 1.87, + 1.92, + 1.99, + 2, + 2.04, + 2.0899999, + 2.26, + 2.3199999, + 2.3299999, + 2.3399999, + 2.4000001, + 2.4100001, + 2.45, + 2.46, + 2.47, + 2.49, + 2.53, + 2.6900001, + 2.75, + 2.8099999, + 2.8199999, + 2.8299999, + 2.8900001, + 2.9000001, + 2.9400001, + 2.98, + 2.99, + 3, + 3.01, + 3.0599999, + 3.0999999, + 3.1500001, + 3.1700001, + 3.2, + 3.22, + 3.27, + 3.3099999, + 3.4200001, + 3.47, + 3.6199999, + 3.6400001, + 3.6900001, + 3.72, + 3.8499999, + 4.2800002, + 4.8200002, + 5.2600002, + 5.3200002, + 5.5, + 5.6599998, + 5.9099998, + 6.0300002, + 6.4000001, + 6.4200001, + 6.5300002, + 6.6799998, + 7.23, + 7.4899998, + 8.1599998, + 8.2799997, + 9.3400002, + 9.3900003, + 10.26, + 10.41, + 11.14, + 12.08, + 12.45, + 12.77, + 14.58, + 14.81, + 15.15, + 16.870001, + 17.219999, + 21.440001, + 25.24, + 34.73, + 37.759998, + 56.080002, + 69.279999, + 109.43, + 3.3299999, + 3.4200001, + 3.5, + 3.52, + 3.71, + 3.72, + 3.8, + 3.8599999, + 3.9400001, + 4.0100002, + 4.0300002, + 4.0900002, + 4.1100001, + 4.1300001, + 4.1999998, + 4.3099999, + 4.4200001, + 4.4299998, + 4.5700002, + 4.5999999, + 4.6300001, + 4.7800002, + 4.79, + 4.8200002, + 4.9099998, + 4.96, + 4.9699998, + 5, + 5.02, + 5.1500001, + 5.1799998, + 5.1900001, + 5.4099998, + 5.5900002, + 5.7399998, + 6.0700002, + 6.21, + 6.2800002, + 6.73, + 6.8200002, + 7.9400001, + 8.9200001, + 10.38, + 10.97, + 11, + 11.32, + 11.45, + 12.78, + 13.48, + 14.09, + 14.52, + 15.03, + 16.059999, + 18.5, + 20.07, + 20.99, + 25.85, + 42.599998, + 46.860001, + 50.740002, + 59.700001, + 67.800003, + 84.120003, + 89.410004, + 103.92, + 108.27, + 154.06, + 313.94, + 401.23999, + 742.96997, + 765.65997, + 214, + 363, + 630, + 792, + 955, + 1028, + 1110, + 1134, + 1143, + 1216, + 1302, + 1400, + 1414, + 1660, + 1683, + 1944, + 1946, + 2312, + 2376, + 2458, + 2595, + 2600, + 2795, + 2985, + 3034, + 3250, + 3252, + 3301, + 4136, + 4445, + 4914, + 5068, + 5376, + 5865, + 5936, + 5958, + 6730, + 7336, + 8214, + 8232, + 8450, + 8464, + 8955, + 8957, + 9520, + 10210, + 10716, + 11218, + 12414, + 12631, + 13265, + 13410, + 14099, + 15911, + 15977, + 16318, + 17187, + 18405, + 18865, + 20800, + 22480, + 24923, + 25096, + 26629, + 27542, + 29136, + 29435, + 30199, + 30731, + 31598, + 32428, + 40161, + 46727, + 53593, + 56994, + 65671, + 72468, + 73728, + 74860, + 78265, + 106260, + 122304, + 129171, + 129931, + 138134, + 167056, + 172094, + 185002, + 187879, + 242736, + 550539, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 19, + 20, + 21, + 22, + 23, + 25, + 27, + 29, + 32, + 33, + 35, + 36, + 38, + 39, + 41, + 43, + 45, + 48, + 50, + 51, + 53, + 54, + 56, + 58, + 60, + 64, + 68, + 70, + 72, + 74, + 76, + 78, + 83, + 85, + 87, + 89, + 91, + 94, + 96, + 98, + 100, + 102, + 104, + 107, + 114, + 117, + 119, + 122, + 124, + 127, + 129, + 132, + 137, + 142, + 145, + 148, + 150, + 155, + 157, + 160, + 163, + 165, + 168, + 171, + 182, + 185, + 188, + 191, + 194, + 197, + 200, + 202, + 205, + 208, + 211, + 214, + 217, + 220, + 223, + 226, + 229, + 233, + 236, + 243, + 246, + 249, + 252, + 255, + 263, + 266, + 270, + 274, + 278, + 281, + 285, + 294, + 298, + 306, + 315, + 320, + 324, + 329, + 338, + 343, + 347, + 351, + 360, + 365, + 370, + 374, + 379, + 388, + 393, + 398, + 402, + 407, + 412, + 422, + 427, + 438, + 443, + 449, + 453, + 459, + 470, + 476, + 482, + 489, + 495, + 501, + 520, + 526, + 533, + 540, + 547, + 553, + 560, + 567, + 574, + 582, + 589, + 597, + 604, + 612, + 637, + 645, + 653, + 661, + 669, + 678, + 687, + 696, + 704, + 742, + 751, + 762, + 772, + 783, + 796, + 808, + 821, + 835, + 850, + 864, + 881, + 897, + 913, + 1006, + 1025, + 1046, + 1098, + 1126, + 1154, + 1183, + 1240, + 1301, + 1340, + 1494, + 1550, + 1607, + 1722, + 2180, + 2294, + 2636, + 2750, + 3093, + 3, + 7, + 9, + 11, + 12, + 21, + 23, + 25, + 31, + 33, + 34, + 35, + 37, + 39, + 41, + 43, + 46, + 47, + 49, + 51, + 53, + 55, + 57, + 59, + 61, + 63, + 65, + 67, + 69, + 71, + 73, + 75, + 77, + 83, + 85, + 87, + 89, + 91, + 93, + 95, + 97, + 99, + 100, + 100, + 200, + 400, + 500, + 600, + 700, + 781, + 800, + 1100, + 1200, + 1300, + 1400, + 1500, + 1600, + 1800, + 1900, + 2000, + 2200, + 2631, + 2900, + 2991, + 3215, + 3229, + 3500, + 3600, + 3752, + 3900, + 4200, + 4334, + 4430, + 4993, + 5226, + 5400, + 5580, + 6800, + 7200, + 7600, + 8500, + 8700, + 9100, + 9389, + 9500, + 9700, + 9800, + 10088, + 10367, + 10621, + 10783, + 10900, + 11000, + 11189, + 11300, + 11467, + 11700, + 11853, + 11958, + 12068, + 12133, + 12200, + 12300, + 12700, + 12900, + 13100, + 13400, + 13900, + 14100, + 14400, + 14700, + 15700, + 15900, + 16200, + 16400, + 16700, + 16900, + 17400, + 17700, + 18500, + 19000, + 19500, + 20000, + 20300, + 20800, + 21600, + 21900, + 22100, + 22400, + 22700, + 22900, + 23401, + 23700, + 24000, + 24300, + 24600, + 24900, + 25100, + 25400, + 25700, + 26000, + 26900, + 27200, + 28100, + 28400, + 29000, + 29300, + 29900, + 30166, + 30800, + 31200, + 31859, + 32531, + 32900, + 33300, + 33638, + 34000, + 34348, + 34700, + 35031, + 35400, + 36100, + 37131, + 37700, + 39400, + 40500, + 42300, + 44300, + 45000, + 45700, + 48700, + 49531, + 50400, + 51200, + 52900, + 53700, + 54500, + 55300, + 56100, + 56900, + 57800, + 60550, + 61500, + 62600, + 65585, + 66455, + 67400, + 69531, + 70573, + 71700, + 75800, + 79153, + 83000, + 85200, + 87500, + 91100, + 98700, + 102620, + 106608, + 116538, + 128326, + 2, + 7, + 8, + 19, + 51, + 59, + 62, + 65, + 83, + 122, + 140, + 210, + 235, + 280, + 320, + 342, + 374, + 402, + 511, + 584, + 640, + 804, + 966, + 1055, + 1176, + 1365, + 1861, + 2032, + 2923, + 3175, + 3297, + 3691, + 5827, + 9936, + 10785, + 15388, + 15974, + 19297, + 20249, + 29707, + 32244, + 65988, + 75104, + 81627, + 102279, + 107471, + 115309, + 130401, + 136081, + 137992, + 139946, + 141867, + 147702, + 151565, + 157335, + 161066, + 163019, + 166929, + 189052, + 192643, + 196469, + 200185, + 203906, + 211277, + 218538, + 222118, + 225779, + 236984, + 244345, + 251930, + 255672, + 259464, + 263326, + 267093, + 1, + 2, + 4, + 6, + 7, + 8, + 11, + 13, + 16, + 20, + 24, + 29, + 33, + 35, + 40, + 45, + 50, + 55, + 58, + 61, + 65, + 68, + 74, + 79, + 86, + 92, + 99, + 103, + 110, + 114, + 120, + 123, + 134, + 140, + 142, + 159, + 217, + 238, + 257, + 262, + 271, + 305, + 340, + 351, + 384, + 445, + 525, + 626, + 685, + 766, + 881, + 950, + 1083, + 1116, + 1181, + 1221, + 1300, + 1371, + 1460, + 1526, + 1645, + 1748, + 1848, + 2023, + 2075, + 2130, + 2301, + 2553, + 2655, + 2825, + 2882, + 3055, + 3237, + 3664, + 4068, + 4554, + 4785, + 4844, + 4914, + 5244, + 5504, + 5741, + 5998, + 6427, + 6579, + 6711, + 6944, + 7199, + 7367, + 7614, + 7933, + 8753, + 9119, + 9522, + 9898, + 10181, + 11143, + 11318, + 11349, + 11401, + 11414, + 12040, + 12708, + 13433, + 14189, + 14430, + 14490, + 15578, + 16625, + 17045, + 17477, + 18091, + 18615, + 19381, + 19680, + 19741, + 21530, + 21730, + 22531, + 23178, + 23805, + 24531, + 25374, + 28481, + 28773, + 29314, + 36041, + 36592, + 41200, + 43679, + 48927, + 51948, + 54859, + 57680, + 59597, + 67687, + 69109, + 97630, + 121641, + 166257, + 188207, + 208963, + 231527, + 5, + 9, + 13, + 29, + 35, + 42, + 48, + 57, + 87, + 105, + 117, + 133, + 149, + 178, + 202, + 227, + 253, + 272, + 311, + 336, + 353, + 373, + 425, + 457, + 484, + 551, + 571, + 590, + 640, + 672, + 679, + 684, + 689, + 699, + 751, + 760, + 767, + 782, + 790, + 804, + 817, + 823, + 829, + 833, + 846, + 852, + 870, + 878, + 900, + 905, + 957, + 1001, + 1015, + 1029, + 1052, + 1070, + 1111, + 1209, + 1234, + 1256, + 1285, + 1307, + 1332, + 1354, + 1406, + 1433, + 1453, + 1536, + 1593, + 1617, + 1708, + 1740, + 1778, + 1815, + 2055, + 2100, + 2150, + 2207, + 2263, + 2312, + 2421, + 2472, + 2708, + 2790, + 2866, + 2904, + 2970, + 3041, + 3155, + 3448, + 3570, + 3678, + 3938, + 4009, + 4085, + 4220, + 4297, + 4470, + 4544, + 4631, + 4915, + 4992, + 5086, + 5175, + 5410, + 5515, + 5639, + 5871, + 6194, + 6322, + 6474, + 6674, + 7418, + 7842, + 8121, + 8513, + 8918, + 9387, + 9825, + 10305, + 10831, + 11319, + 11864, + 12390, + 12834, + 13338, + 13865, + 14407, + 14624, + 15346, + 17033, + 17329, + 17539, + 18028, + 18729, + 19229, + 19547, + 20016, + 20494, + 20821, + 21197, + 21552, + 22957, + 23882, + 24497, + 25451, + 26416, + 27670, + 29804, + 32934, + 36523, + 38379, + 39763, + 42089, + 45622, + 49111, + 55901, + 63904, + 0.0099999998, + 0.02, + 0.029999999, + 0.039999999, + 0.050000001, + 0.059999999, + 0.07, + 0.090000004, + 0.11, + 0.12, + 0.13, + 0.14, + 0.16, + 0.23, + 0.25, + 0.27000001, + 0.28999999, + 0.33000001, + 0.36000001, + 0.38999999, + 0.47999999, + 0.51999998, + 0.57999998, + 0.63999999, + 0.76999998, + 0.86000001, + 0.93000001, + 1, + 1.0599999, + 1.3200001, + 1.5, + 1.72, + 1.84, + 1.91, + 2.04, + 2.74, + 3.1900001, + 3.8, + 3.8800001, + 4.1500001, + 4.2800002, + 4.5799999, + 5.5700002, + 6.2399998, + 6.7199998, + 7.5599999, + 8.04, + 8.6300001, + 9.0100002, + 9.9899998, + 10.51, + 10.96, + 11.85, + 12.79, + 14.06, + 14.37, + 14.76, + 15.51, + 15.88, + 16.08, + 16.42, + 17.17, + 18.209999, + 19.51, + 20.52, + 20.82, + 21.1, + 22.299999, + 24.459999, + 26.08, + 53.470001, + 60.110001, + 61.09, + 61.849998, + 62.630001, + 62.919998, + 63.040001, + 63.169998, + 63.299999, + 63.389999, + 63.48, + 79.32, + 83.129997, + 83.849998, + 83.959999, + 100, + 1037650, + 1723504, + 2244653, + 2864825, + 3383987, + 3795480, + 4142277, + 4567104, + 5010256, + 5966615, + 6299370, + 7083277, + 7522126, + 8412871, + 8837549, + 9276948, + 9863821, + 10486369, + 11578704, + 12163923, + 13021873, + 13828478, + 14673878, + 15654338, + 16574689, + 17543426, + 18492992, + 20736132, + 21788372, + 22962064, + 23847060, + 24860524, + 25793028, + 26489768, + 27032812, + 27450752, + 27813360, + 28221620, + 29612440, + 30447920, + 31219590, + 31626088, + 32106968, + 32649468, + 33434304, + 34513800, + 35555364, + 38788752, + 39737744, + 40706704, + 41542668, + 43205512, + 44037980, + 46729864, + 50637396, + 53254524, + 54226704, + 55338660, + 59395504, + 60319136, + 61422976, + 63719232, + 64959840, + 68561256, + 70486456, + 71614280, + 72849216, + 74358072, + 75856336, + 77025160, + 78309984, + 79791192, + 83683880, + 85224608, + 86544872, + 87806552, + 88856952, + 91365568, + 92439280, + 93485088, + 94560704, + 1.0045851e+08, + 1.0065839e+08, + 1.0086424e+08, + 1.0106654e+08, + 1.0359994e+08, + 1.042553e+08, + 1.0508658e+08, + 1.0574689e+08, + 1.0658666e+08, + 1.0800078e+08, + 1.1096082e+08, + 1.1237282e+08, + 1.1344565e+08, + 1.1638846e+08, + 1.18122e+08, + 1.1955379e+08, + 1.2126572e+08, + 1.2230541e+08, + 1.2337086e+08, + 1.246013e+08, + 1.2752458e+08, + 1.2904266e+08, + 1.3026654e+08, + 1.3340886e+08, + 1.3488405e+08, + 1.3772952e+08, + 1.390135e+08, + 1.4265074e+08, + 1.457892e+08, + 1.4689109e+08, + 1.484849e+08, + 1.5004106e+08, + 1.5059768e+08, + 1.5118848e+08, + 1.5200856e+08, + 1.5250224e+08, + 1.5346341e+08, + 1.540247e+08, + 1.5532386e+08, + 1.567155e+08, + 1.5878211e+08, + 1.594644e+08, + 1.6018256e+08, + 1.6168109e+08, + 1.6223786e+08, + 1.6326845e+08, + 1.6384918e+08, + 1.649735e+08, + 1.656859e+08, + 1.7070971e+08, + 1.7694973e+08, + 1.7941701e+08, + 1.8222443e+08, + 1.8530262e+08, + 1.9251592e+08, + 2.0134139e+08, + 2.0541229e+08, + 2.1434315e+08, + 2.1878254e+08, + 2.2355106e+08, + 2.2831722e+08, + 2.3215955e+08, + 2.342463e+08, + 2.3843141e+08, + 2.4364762e+08, + 2.4820429e+08, + 2.5302923e+08, + 2.6208336e+08, + 2.6698949e+08, + 2.7137626e+08, + 2.8217254e+08, + 2.8691187e+08, + 2.9745187e+08, + 3.0660896e+08, + 3.1238829e+08, + 3.1902518e+08, + 3.229599e+08, + 3.329297e+08, + 3.5073504e+08, + 3.6027674e+08, + 3.7681318e+08, + 3.9339715e+08, + 4.0543194e+08, + 4.160361e+08, + 4.3077226e+08, + 4.523279e+08, + 4.8495926e+08, + 5.1632826e+08, + 5.5564128e+08, + 5.9271789e+08, + 6.411625e+08, + 7.372567e+08, + 8.6929702e+08, + 1.055401e+09, + 1.1658797e+09, + 240560, + 410076, + 711288, + 895492, + 1109492, + 1276528, + 1625728, + 1899768, + 2027364, + 2281644, + 2586332, + 2849488, + 3117988, + 3410364, + 3709888, + 3962256, + 4181556, + 4364328, + 4498032, + 4758348, + 4873952, + 5472904, + 5591664, + 5778728, + 6276504, + 6413356, + 6547728, + 6691128, + 6790740, + 7004992, + 7308272, + 7570344, + 8042524, + 8491408, + 8860504, + 9194476, + 9709736, + 10160616, + 10717016, + 11119504, + 11705964, + 12261312, + 12866664, + 13514312, + 14704776, + 15405152, + 16005068, + 16754080, + 17383300, + 18136364, + 19389944, + 20019916, + 20463948, + 21400196, + 21923612, + 22581916, + 22715788, + 23146444, + 23443700, + 23798364, + 24264244, + 24925016, + 25291860, + 25913100, + 26458836, + 27503132, + 28387432, + 29910644, + 31697792, + 32425572, + 33213116, + 34034288, + 35760708, + 38022176, + 38782176, + 40717416, + 41235180, + 41747264, + 43542308, + 43943804, + 44735816, + 45065864, + 46009976, + 46306288, + 46543792, + 47421788, + 48162160, + 48280112, + 48688764, + 48843128, + 49144836, + 49259852, + 49630964, + 51064148, + 51307316, + 51527992, + 53551800, + 53959488, + 54099240, + 54364560, + 57614644, + 58836712, + 60200060, + 61460216, + 64680100, + 65559320, + 66289320, + 66894620, + 72498960, + 73778352, + 75205760, + 76278448, + 77526400, + 78883072, + 81149424, + 82100688, + 83732032, + 85329040, + 86993952, + 87871808, + 88962104, + 90430704, + 92956960, + 96098048, + 97716312, + 99624112, + 1.0442499e+08, + 1.066803e+08, + 1.0837106e+08, + 1.105755e+08, + 1.1288506e+08, + 1.1562831e+08, + 1.1797418e+08, + 1.2039574e+08, + 1.2301378e+08, + 1.250115e+08, + 1.3212201e+08, + 1.3653286e+08, + 1.3967781e+08, + 1.4220454e+08, + 1.457472e+08, + 1.4944283e+08, + 1.5326578e+08, + 1.5793666e+08, + 1.625305e+08, + 1.6722179e+08, + 1.7208096e+08, + 1.7451046e+08, + 1.7936912e+08, + 1.8371443e+08, + 1.8765027e+08, + 1.9079603e+08, + 1.9395962e+08, + 2.0647309e+08, + 2.1130994e+08, + 2.1458229e+08, + 2.1853994e+08, + 2.3005197e+08, + 2.3600296e+08, + 2.4206338e+08, + 2.522703e+08, + 2.6565344e+08, + 2.7929763e+08, + 3.2687946e+08, + 3.9571389e+08, + 5.2705373e+08, +}; + +static const int th_begin[] = { + 0, + 109, + 194, + 273, + 346, + 432, + 503, + 594, + 594, + 594, + 797, + 840, + 998, + 1072, + 1215, + 1373, + 1459, + 1635, +}; + +static const int th_len[] = { + 109, + 85, + 79, + 73, + 86, + 71, + 91, + 0, + 0, + 203, + 43, + 158, + 74, + 143, + 158, + 86, + 176, + 166, +}; + +/* + * \brief Function to convert a feature value into bin index. + * \param val Feature value, in floating-point + * \param fid Feature identifier + * \return bin Index corresponding to given feature value + */ +int dualsimplex_predictor::quantize(float val, unsigned fid) +{ + const size_t offset = th_begin[fid]; + const float* array = &threshold[offset]; + int len = th_len[fid]; + int low = 0; + int high = len; + int mid; + float mval; + // It is possible th_begin[i] == [total_num_threshold]. This means that + // all features i, (i+1), ... are not used for any of the splits in the model. + // So in this case, just return something + if (offset == 1801 || val < array[0]) { return -10; } + while (low + 1 < high) { + mid = (low + high) / 2; + mval = array[mid]; + if (val == mval) { + return mid * 2; + } else if (val < mval) { + high = mid; + } else { + low = mid; + } + } + if (array[low] == val) { + return low * 2; + } else if (high == len) { + return len * 2; + } else { + return low * 2 + 1; + } +} diff --git a/cpp/src/utilities/models/fj.ubj.gz b/cpp/src/utilities/models/fj.ubj.gz deleted file mode 100644 index 08c4d7c1b062e4d73b8afb1e2d83c8d68c6246f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12153 zcmV-Wo#~WVrl^God;MHSNHHQy(o&Wu%lu`C5FTTHr$2<8+M~aF_vIQu`BxB znVp4oqtWCu{)5lEJm;BZ=iWPIcg~#OIp?04;14t}vzUm`gxH9L3EB@VLz9vcqQgcf zMI=^!Rx>d?dPHU0J`Ii!O$Z%Xxvoy^=#ktk36 zyOj;kT5LhgZ?~Xlx|pI)4M(MR70Bvb531ZWOeBiwkBko27A4d=N4?HuD&-M{Nx+dcFtaSy(0nJxRF#})AlI;xtKB-=8l%k zwHBHEwo4RuFG{pT>VazB>q+HDcNR4~9!i-XdIC)rc~axQwL`YcoKy}*(^Te$hm_rF z-d8@f%lGk^k*3_b_lj`w+uFp~vNxpNw#H;=pgXZR^;@z@Wn%q6>&wnx(=XQ1Y;F|ek~8q{gZUR68#Hfla^2z+w79~|+d1LaD$Cx1UTj#}4$ z5xRRO8r`qwLY;SLkG5>crL24Ipq89{Nm(9g4yUhOi#l#@jG}tFq9@OLRDMt4p-B;u zaS74*%X5f%&u_VEeQ+%PnZCbOU7c2m5s@SDZyz`G|LfrrqsK?Qzg5kl5yL`9$FSG% zu%sY|epNpi@bA7VFQVw7RX=Sjb}xd%qoQMmCPc*Q9zVM~23U;P3xL&6?3Ke_Tx>5a zwb|>KJv~_JV`+$`5tb%c?65S)(gI5>EUn+-7hbl-(hf_9xBLsg@5C=VW9fpW8x|KV zJ+bt{;*P}=i#HY@ECd$F_o1-(V)4Tw#=>BcV(EjWAK&I1EP+^p`2PpLtp=~kA4^PJ zVj})}LSrK%-qkOC-9`!eq?9N3F$xw1B-UvfFyo+cfNXbEz?98EqHN6*A`cdumB?nw)ul&0Lalv4nXx2}s?qm#}RlBVq;8XZt1omzQvTBpz!sKs9(( z-m>v6Ors5SE}QQ(KQryifEPnvjCe8T#e^4AUd(tg=f#2-OI~X5Qj-@eUTX1Dn-^hNO2OFdrd^U{EqMmm=-D@}Q^!=7;y7JOZM`gHZzl2%|><#&YYR!;8_onDZUtodM8*RKWIuvrBbe<=k|c82(ixD3BX0YO>}9@tCcDD6c%QK8 z~#pm%=O9Rh!Etm!0jDiBmB)5%Kn4PnbVI{aO`NNX1WUU6xaoUzXTfLC%_rDvw| zUbnq=98;b4+Ly7;IJ)Z%bS_|^w>>kRz9#(AkngWmxJFv#t+QX?J>&hS?>v^8uZM-S z{|ghcMj}SDC=8d;p|S5=x}wv41ftWPG6Jh?(Wyx#-#n#jF!Bmqzz1(2~nfLLXd8%h9afYnlZ1|Zhp?c>3T!!kfj)oh;~-v`?}KJmD%CH^c2 zZ&$6^4|cnP49)&mBOr|DDq+u-o_UEr?4vNY!Kh@^<2RjBR! z&T5|gt5&vV3=19;5uOy6pu_T$CVd>0gS_wUSgN#jZ%D~^ zn<~!<$D;28XNWwOL28+7q-y%GrDSo%S~&fPH#*p5plIH=Y1EsvG|Ki!H|prEU8>Fd z+Yt1t8sxSv_2818n^E1QRnV+|8f7uLDOxpF0UtIGqH^<|h-~Toyoo=rtR)4WxF+%n9RI62CNY!*L}I6XF7xZ}}aa^duuqwvoRdYFIb`Fv96Ms;N zQ?8(WV|K%v!E4ZvA@kwBJ{!pS`Msz*-*rZj!7V5klM=KjVSe>QHxaq-VOM zg}s0quQ+oCwV8S>c4L0d>CVc{sLo-^sv%c4`Yq>Fx5ckz3F?_106Kg zj$@#Mv&MQDY^L+Sq0VP!IttWKZ$C|S`c*e<4f;Ps)}X&e))yg^u|pNIviO;fwO9<6 zY`lKs0zmcY1JJz-?;r5qLqf4bc!vMlJjXptUstTIml^ z$L)YJ!?@~zFYg@~U8g7kd@TXwE;)eH(*apm4#<@mRj4ZGQMH9;JA{bG>~;8kDMnR{ zroz=4qf-}EIXpe2K~w>M%sz4+p)Y)KMD6mq5H(oP-pyQbdYd0LcQvJ4QkI7LF5W9L zr*f!)!5dXB?S_yWf{sA1Nq%UKGD6fS(S=#&0UM{+}qjBMSrG2H$`1$n;v9=Oafm&|UFi*61nMe7M~xHZ=YCC&7p zu8l*~%xOl{yt7M1ol{h3a_DF(Yf3p5y@8Z*HvW&_qJ6GVTVYCykk6i4u;;ybsHAlqyng2)d=R!2M!AQ>cAbjQ0GH_hMRPxft%hGXn#&?9 zZ2(G`G4$az8Da+oun*2$=4&ETG*T2Wf0r9qAN%xs+L0Bwe&8 zhk0 zWA6n?r@&PD%-F>o#*PV|M6XYjN$gfdNldBDk_LMK^G&!0TVESxNM2u0WxD1KqfKH2 zbm1dw@tqANpqjj@b2PWE&LyjhwYI$c*P^-lSo@)9?x$kzXN~5v$HJ?dO3c+F?&pr? z^6NOv)s5y>mq_kEh~^HzI>*azp&N^!0_V18xM*%HSioWLlru$I?4>#k2P|58O^aRN zS;H{^b&BGmzbIS|P_1~tnob;k3XeyzrjzT&f^4nn3X4RsWt@q2JocN~-VL|zT_6s%Ucn&@G$ zDgT+_2awrNXCJH8ou@iQb6M?1VAU#8tqno<4ppJ?1q}uYxAM{4tRXULU_*dT58%+4 zIHAEnEgEalki$nT!ik#yg%S-mic%`uKf58QRqbk83V+QSLBenAL%%};lDRw&(H(rzJf}ot zxA<_ulX}yUd}b4L&2BWjMO#slhOMc%pg*Vu-OSO^$!AqveFBuTA7&Bs;PXDGiXM?! z-)4{vi;7|C&It0#*>!Mk|6^o_(Sy;P=3CVDHjh9{A7;VQ^R-a>hUO$W%&4IC9+v`H zt36a_%K<1&wwp??*BRA*FaW(yJV5Os^N~xZ477UWDdo_%law9Y^L-W=eW$cuY%R3A z+?%lPU!-ij;dh_cXO|HvbuW=1wFlw9LZ--TIf(4sGXx6yJP{U{%_RG;90lEN77&f^ zkB51i{h`|eLY3Waw=m725&W*_Zdh337*TMj7(VMio{YF-2tz%h$mMba)%Jak>WuW4 z!pD}8$bQFqv~a~s_`TOFBwk&LZqJ&hJ{I*WoORoQ?6rP5+}CgdTIgv}FfpYU`eXPy zc(5%bXWRN$Pn3@Q!s?hsYNL-Mb>!zpYAsId*vegWY|n1|@}oG-YB#>^2P42NO7m?h zahkv7gkoXIF5+!9eti_VBLl8J@(VB;bwR3X{#_Eu4Q_x$zx>8 zotcdjGQ=Sjw#@M?`^pIN-KV7-dWRvA#8K?)|1wf0?!I_A-Aa%lmOmItr%)mEG~4UU zuKPK3sqH`xxouXf#B0Y0ddn>`m;eD_COx%gcKjp|tBRz457)3n0mLP?a;Dzv3lf`} z{r%56$(Seeb0i_@A#{oefolBvD%=O-Y0 zw+U(9RKZ@%ts`G6>7?EaCoo`c#>sYYO3Bs@p#WL(`P@8{=^pRSm zhr`Q&7U@|$_Rcpf_Y7VC*xTl`kNbw?Viv32PA0x$k=uLy?H+7I+tabf9XMSE(By2u zB0U?$hEvS}o1hKr&U^}OtpV#A(3*HGp0g&N_cHK|MSCv#J<|htp3OD$OkcL2i)^#~ z)`S4>;OBQ)6OTjsoH9V#Vb=Hwf6bR)op&96bq?zmfT|3@nt1M3gOXSmgQ(LI0lS}6 zqalDS$9K^U@zGy{iT7dTkJLeKBR%Xk)kAedou#Q>6x&!YsaiKtTiF zsQ_&f0peHOe?^EUYOa<6A-<*f2y_y@JIR4Co{v^*_eZ1?sXoLfe#dJ*6NPV;Q|VR! zzDWmYttCJ|%K%(^0iaGL0Bt@8psxhLQAGfyF9*;h9uW98PRzyIF~a-NO`vfRC}f;# z#7BD^8Jf%i0$bodC43Tl4(`)X&Lw!WiR@g40N|tO0^W5YsB#rlM*GvNlEv9f0QNoW z^7x{z1EVg^-9DG^AX?C=Os(*j45aF(O;Orh%z-CMT2qU1m!cbaCCZ;??IT;-Zz7cm zA;@B5IkhoZj>g}eK-pgEi~2^rP~LPZ5gt4ekXUrlVTZ}|#U1!zBC9_e>CN%jg{HQYSzOS;`lGTDoSq{pE zkL*?TY#fzM1EJ5UtAWCwv%V!-%SMv@zTc)SP4y+(h<_$6P4b~tjYedx<;AKi<#IS% z-ca~Fq5}GEnGes%*Q+L#$%TPCTae2Zw}Vr2is828ozSaxaX~BSM(+0-UtoHShAqQe z!V#fQQEvCwu#3FtK&zx+GGp5dBx@w6T8(jo2ai8SXA6hIR|`g{4_dc|4g;1IvdU z-h}9YID<0jlr!#9iBXg!qQy1In4{^ES&m~RYkqG^-|OkX%)1!Ipia}6i!NmnxF(+I zF*;T9`o(spmAybb^R>M=v;8tUKL9Z4qvYaSW{&jk>ARWm`58>qEiMmTvU{E*NBmb` zS4iv*lt}~c0&(^Z3DfeL%zyDo8Qn5xoB!eq7sWE;A#~x0Q}nVr0*3S+z!)V><@%|G zHN_XN7WEDwZ!l9=Na&`QN|=e~DwvcG=G^$*lvVleWd;g~Ma(71%;M*gty^D;x4pXP zf7AIc(_qihuhpv%UCq4;wmPKT82opqalbd8{X^aap4eS^Rn_YgybIC)rg!0=RHfHr|daTu`1AUiGGXV5)kK|o1kuZ2fDv0ed>7|$(mkUbFGc~^8xkkHTwv?I`Q$F`@m|ufZ7nu(u)l6B7*dN!~;+fk5BeV5Z}}Fw|M< zDr6JA&w-BeHq`Sr80jSt=;rb3dMnIy+Uh1Vn0&=vhc9nDf5`6;{r{Dcuw|<~<-PpR zeuqU^PgCGeDG6*gKbuX@`Vd&ZfQb16h|ER{V6S*UbvFX!T72Q3TMUS^czY|~<9ERJ zw6G8_@$Z9dE#N-$;Tc{V{;y0|aMke-IB4!4C@l<#Ja@~fHWh%{69Y&;Ie?}zKsD?I z;7ti2+Y10`D+lC$ydQ-t0fBE+gbeS?WV{@q*$$!B90%v)(2mbex__kcJFq?wZq~pB z&18JiUas+vlxX%<`(D8NDmZ@(>x;4H-)HFfE99DE`qKV~&o>iY(6qL>cS z+D=UFxxI+Ac=&`UUThA}gj|LZis=p>YS+riZ9S>yum`Q*4Q1Bj8y9pH$PHpIRm^-=9%O;j0Mb5VoT18~=r zSX6)2ZdGPpHzcWl7S%aEg__%aK4s+G9o4j(M>TsaQQW*(N0H`m(nqjoin77@AmPCD zDMX`=DXI=-t9@S8Y(%U$Q$n6HxKAj$bW=}%+Ftd*qY<$(&Yh^KoUCfUb0u_KVk6vI zXC|y&+Yhe&xw*=ndMs=uN`(V%&w_@hPLk7;1b)3#Kc&CgdjNu@Z&0cf^#57%@)t+u!MXzGfr~Sv=vGT|a!m?j zC|OBg9cWA+HwHS8lraS*$ZX z|H1DZ`OhzDApSnn+4rWP7rpa{c{ui=O%Y8vA5;rQ_1~9GtuA43;4HYqVsCvqg4J+Q}p?^@6fp}ne*!d z^i%Y|3bL-8i4Og&xtZYF_&qn%e7#^HGQ0~|vKF0MA=k$5Su=+vaZk7Umnb3xxRv=J zul2qISqkRj_bntqc+#dmYv#E9cA!zEnS&|YZHT{=o9#|#$k>_hUfHKQx!t+sXxer@;crZA{QT5H@+ZTsfJ&Lv)`Rz&#|>N9~VEQlScrm?@aW;f||K+X(n^^U_x_#imUeQqTQ&U3T z)(U3$*pM?V;}Qh zJrpam9EfR8QW@{Xq;jMlFYT>a@~}ez?HQ zoSI4>*pDQswGnf&#^mY@uY;>8ynZOD{oe{de+*`|5c|32nGgOqAyyk2*HJZpJGA^h zc-2#1f0}FlRlw}vcVKoknfngRE~q({o%7AXtn5lgCCp-jh=o~?!db6-S{mZ4rY0Shz&opbJIQg9i*TawzQQ|fGy`HYURK|D+|P2g8KQ6 zT*#T2iO0w(1A?oEw|cuioxmDI-nBx4bqyjmDc$w@()--J_tcw>d?p@WSzQdG4@2$X zPk>s3k3g+{61i?Nx;|{0=_RKd>9y70zHVi5Go3Mj-k2uf-STVI8E(IfLHHrO9sHMg zdo)*$T-6`O+nlvBD&!X+e1mmfk3&^>>m|TQD*G_rc1i-GZYKad`5aIvkwa7AFL(^Q z$ACPLp-ItJX)uvpN4A!6Gr8HYD6zV#uvsPMYD3BbO+9YpS^`8ZZUSns37}pykr8vS zNqJZV$Y433gpW0Kx-)o0#rp^QNB}$~0Aw#LeRYbb2{ftV+H`lWh#Ff%+*}TXH}U&2 z4c@Y~zKMl0Zgx92ZoE!NS;qIv<7d5pdCdK6nc(5x5A8^K%2+!za{3g7Z}eJN=iXs> zP~8J%<#blOZRJ9Q`^Ceb`kRn9dp{+Anlc1UzR?$L$!vobwr#EyUJv%#d#8qa`#p1F zyi;#jFeIhGc>4o&z;g@O@yINAH05%ED#Ql;I<79v%$opTsymV7{nG%wOzfwp#4{PxmL^~2eG zRkL}fiW#z#O6#6xKI2kK6^Gwcs19cjRo-!)qPl;|R^8!MwlL3%knc*!CbuNT5i9^iLC>j4kX~3S9Nqs26mBsgr!@Qq`rVBu>pd8&c5S~JHi_(A(4ea? zdFM)FwbR?i&}oVr-2UPcnsmBJL3?pu5*$~;jsf4GLA$#X))RsYF4y{zay&j38MWQ3 zUSFP$YD}!AHN(TdaIUx6$00cUb3?Fx{cxZ&bNufB@P}rLYr&Uaz7wt0&-MQ5a=OE% z4Ys^uUYTu@Sm#UVd(S0|O(|f4uGEtF9gv9W^@dp%cw}as9mOiyE_=9TQ{ahUCQ8q_1elDru}S0uTL02PYfg&m(kgbo#jhO2UAyR z-Sck#3)cd_Rc~DV-k6RRN9Nx4TW%E6dvl->kQzO71E7taxx!a!r)8U^RruFNS>*SjMEs*}u>~AeV^UHUl!TLGc zUu7Mv=gehAIO|8h3BvM2++JOc;{Ysw$w+>B{9 z)0Zo%hL<9lslCW#2_P^Uqi0Eg@YFY_W;BenFh`8SaN7ibmy@g?*}nm#$|DnVKNS-xq@k_RgcEvklDFvRx~#=TEJ(0 z4=I#W##TVc7G!&k$3IsSpl&IE%23Hr;$lFo;jx)VbhIuTAnUzAn2)!&8z1jW%{W4T z{!v?OM4WwuR}4Rc8=nJmkgT#cJeM^sfA`T{l@V#cuP>=uYTXN5U3u#-j_kcYTa9$@ zQ%#)Yp0!g@msyO$%5WBWEWsU(+Fk>WyDC%6aY`f>som7eDxQ)RnQu{s`!pnNyA7&W zDGIi`3{jkooh)3qXf7Gvt%7LUa(RJlNF(Um;WR8sbRkmlhDuo#=sWw{%EPa zr((ybT;}gP6B@uX@+5ng!)H0@CrsN%*2exw?30 z2z-*(3(n}}Nq(O*1T|TIy5O{EC?z-VfWEWrMs6B66piZo1ldl0iMj@_Q?$44kv}2Y zOEJpuh_Y4OQ$>d<^;Nz1WefL|)e&TO{7u#H?n-s-q%`8UR_BDZ-po?y{FF~uMgDBj44=HJNzF_u^e9ElGB6#_!Lfttk7{0JuL>8w`QeCLKw7{s< z8F)9l6C4`WMSbq}`NGWS2!_YB>qp$&)LR{P}Qhh=&6-8TxR z=foHEUh}<-g}*>z<2#HtJ#0=-xLrql;Z3zOU_OT5|8|XZ{VCI*J68Rr@cVN=5{$#~ z&s`&(S2O=8ehvPGDbb)B)BLSYZYu$->BOS3puBXh*P(CPllYdv#-DrU&CO&JrCCge1*IHLbIH>fgFWsvCD^BE z3odk-DNOHT#MWPDKWCTT+xyJ)eqg+p8afB~o_m(Zj>VczsK^qyc@~~zYpxTswoj(I zBv#@xERN@xfJ6RjY&!kD8tEo_h-#!4lQz;rZ{5Uc1HHs)BfWSv(Cep*#-=)$t^4RP z-Ra1tdNFR}>XMxO5AgZFStI?jrhr*xR2y4)Y~6AWNiS&dmyOq2!Q`qE4R-d`Ob%voQIr({D9c#@H8KY1S~?)s;dZy|0og_XP|c1&n2g(Y z*F?rE5q&c@B(XXsXWDC?I;}-xk!Dgeho#nh&Go)}0QI~X9y>k*IlvfoFrrjuX=4?0dVeo0|Fv9I z|4un+Ry08U&B;mRsYOc)hODT0z_}t52CMrcpLRpY%NbTEMAeq;>32yv#Cn0M?fNLi zb}u`hn#vifeO_6F?WR=W1ldVdNlzzY>5@^Xt++(_%i%Kh9KwLijh+vGIcw#UX?C&T zN3RwIAkeztw@$az8Rwfrc`}7MQnS%WpA84XyWB?&+e5|mhW8XbFJ>wilJ%4pZ(k@9 z4qTP*Dj6!cWtOWLFh7#E=u8Qm1ZdxKOrx`!(Z11(4+vr8(I zy;YKj&(vQl;Bc=eN>4UlUMZ zz0p9ua@Gs=BWe$EqG>2R{{2pJVY(Z+@KSS_d(E$)VvDWnbxB{iBY-GaU)%V9sQfWh zw)mGGC-#4p&aFjdo$=lq$^ME^*`ok3_4apXLfelOXC7}#U+kYnmzNJ<4g{t8`~P}M z{Bp2}u4_rqH@yt$qU>NgrW5xRYKGpJah&*!Cn?Rw<-otxK_xYsV9>SiPUl+ERxZvtT=i z)w@G2nE6X~{tuP^m)`v$RQ{w$^}qEvvHzAKYpv-du$mF;GkwS=b`CQ;7uqBFzyqRbsggtGe8Y0Vx*uAy{oH0B%8$E4q_wkU;O=hFkTGNRw4n&>>T$M7`bYknFvvZ@Xo9XmvsH~s%ZKRX# zt&JAzBCnYq`kLvr)2+Ov8<{o*e|{tw0Pn8^26V>Ltsf3PfbrF;vRAc2<&)X=P?S>@ zW!^nkPPx7U=&s@a1$%z}2(TuNf{} zsEO*5Y(jP?2@oxxU{8$-0kDP~P_?`PTwMfUSO_4y$~n9wOf-)ZBY%?t;a<(-xU@C7 zI26s%)a7DpkaLOG{8PONjK?Ha)8std_n19EFE0Sm>|+^}2gc}r<{0hqF=7=CCGmhV za0O%&Ie>Ep;C>jH#S%ay;xX>(Jkc8;D~rR#I=mn3*hRd{z=N*?4){7-GEGuA7fI&p zm9xmrM%TH|dEDlr%>P+bo}~T9I5u=-)!?R)VeD>JuC0svC&Wf11dqg4jmBo<(1?VDxP&T$B_cAIv-RIS va_hwK&=~w>#wGGjhWDRaGkmm(_&yp`c_kV}8%2X?lS%&%Cj1$&e4GFPbo<-0 diff --git a/cpp/src/utilities/models/pdlp_predictor/header.h b/cpp/src/utilities/models/pdlp_predictor/header.h new file mode 100644 index 0000000000..09d2fddc5e --- /dev/null +++ b/cpp/src/utilities/models/pdlp_predictor/header.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +class pdlp_predictor { + public: + union Entry { + int missing; + float fvalue; + int qvalue; + }; + + static int32_t get_num_target(void); + static void get_num_class(int32_t* out); + static int32_t get_num_feature(void); + static const char* get_threshold_type(void); + static const char* get_leaf_output_type(void); + static void predict(union Entry* data, int pred_margin, double* result); + static void postprocess(double* result); + static int quantize(float val, unsigned fid); + + // Feature names + static constexpr int NUM_FEATURES = 8; + static const char* feature_names[NUM_FEATURES]; +}; // class pdlp_predictor diff --git a/cpp/src/utilities/models/pdlp_predictor/main.cpp b/cpp/src/utilities/models/pdlp_predictor/main.cpp new file mode 100644 index 0000000000..c82153c91b --- /dev/null +++ b/cpp/src/utilities/models/pdlp_predictor/main.cpp @@ -0,0 +1,16893 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "header.h" + +#if defined(__clang__) || defined(__GNUC__) +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif +#define N_TARGET 1 +#define MAX_N_CLASS 1 + +const unsigned char is_categorical[] = { + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, +}; +static const int32_t num_class[] = { + 1, +}; + +int32_t pdlp_predictor::get_num_target(void) { return std::exp(N_TARGET); } +void pdlp_predictor::get_num_class(int32_t* out) +{ + for (int i = 0; i < N_TARGET; ++i) { + out[i] = num_class[i]; + } +} +int32_t pdlp_predictor::get_num_feature(void) { return std::exp(8); } +const char* pdlp_predictor::get_threshold_type(void) { return "float32"; } +const char* pdlp_predictor::get_leaf_output_type(void) { return "float32"; } + +void pdlp_predictor::predict(union Entry* data, int pred_margin, double* result) +{ + // Quantize data + for (int i = 0; i < 8; ++i) { + if (data[i].missing != -1 && !is_categorical[i]) { + data[i].qvalue = quantize(data[i].fvalue, i); + } + } + + unsigned int tmp; + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + result[0] += -0.014820111; + } else { + result[0] += 0.0105687855; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + result[0] += -0.053651925; + } else { + result[0] += -0.10176092; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 220))) { + result[0] += -0.012739944; + } else { + result[0] += -0.036262058; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { + result[0] += -0.0063560107; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { + result[0] += 0.08327574; + } else { + result[0] += 0.026058618; + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 230))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 164))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { + result[0] += 0.031961214; + } else { + result[0] += 0.04515064; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { + result[0] += 0.018566301; + } else { + result[0] += -0.009213644; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { + result[0] += 0.14206316; + } else { + result[0] += 0.07914438; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { + result[0] += 0.025240844; + } else { + result[0] += 0.058222026; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.26381668; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + result[0] += 0.085295476; + } else { + result[0] += 0.16802387; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { + result[0] += 0.19548021; + } else { + result[0] += 0.14702512; + } + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += 0.0039215386; + } else { + result[0] += -0.020411594; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { + result[0] += -0.030691355; + } else { + result[0] += -0.066723615; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { + result[0] += -0.039679535; + } else { + result[0] += -0.07033275; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 226))) { + result[0] += -0.017522344; + } else { + result[0] += 0.024236064; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { + result[0] += -0.01896934; + } else { + result[0] += 0.005805307; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += -0.05819216; + } else { + result[0] += -0.030700704; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 210))) { + result[0] += -0.016364044; + } else { + result[0] += -0.0079360735; + } + } else { + result[0] += 0.09915119; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 260))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { + result[0] += 0.0082174195; + } else { + result[0] += -0.0037611835; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { + result[0] += 0.05758765; + } else { + result[0] += 0.00845852; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 188))) { + result[0] += 0.08059614; + } else { + result[0] += 0.060384076; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { + result[0] += -0; + } else { + result[0] += 0.07759698; + } + } else { + result[0] += 0.12749861; + } + } else { + result[0] += 0.16187607; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 22))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += -0.0052639237; + } else { + result[0] += 0.022940176; + } + } else { + result[0] += -0.03480838; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { + result[0] += -0.04990984; + } else { + result[0] += -0.080560416; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + result[0] += -0.014295327; + } else { + result[0] += -0.03235691; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { + result[0] += -0; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { + result[0] += 0.07372898; + } else { + result[0] += 0.024104217; + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 230))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 176))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { + result[0] += 0.02912726; + } else { + result[0] += 0.04095058; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += 0.016825125; + } else { + result[0] += -0.007768286; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { + result[0] += 0.1265556; + } else { + result[0] += 0.07124209; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + result[0] += 0.040830098; + } else { + result[0] += 0.0559672; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.23753951; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + result[0] += 0.07685151; + } else { + result[0] += 0.1544579; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 260))) { + result[0] += 0.046362683; + } else { + result[0] += 0.17204122; + } + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { + result[0] += -0.022617795; + } else { + result[0] += -0.0122455275; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { + result[0] += -0.027298182; + } else { + result[0] += -0.059523176; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 248))) { + result[0] += -0.035259727; + } else { + result[0] += -0.061327398; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 236))) { + result[0] += -0.01425799; + } else { + result[0] += 0.017331526; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { + result[0] += 0.00193814; + } else { + result[0] += -0.017574918; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 196))) { + result[0] += -0.02869076; + } else { + result[0] += -0.065549426; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + result[0] += 0.0057360423; + } else { + result[0] += -0.013489055; + } + } else { + result[0] += 0.089799576; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 260))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { + result[0] += 0.007112731; + } else { + result[0] += -0.0027204938; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { + result[0] += 0.054216303; + } else { + result[0] += 0.007258077; + } + } + } else { + result[0] += 0.059579976; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { + result[0] += 0.0030776516; + } else { + result[0] += 0.05093806; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { + result[0] += 0.07593134; + } else { + result[0] += 0.11205458; + } + } + } else { + result[0] += 0.14210285; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 32))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += -0.014897979; + } else { + result[0] += 0.003548234; + } + } else { + result[0] += 0.018050188; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 88))) { + result[0] += -0.008003016; + } else { + result[0] += -0.029365538; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + result[0] += -0.008477488; + } else { + result[0] += -0.05125452; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { + result[0] += 0.02146909; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 8))) { + result[0] += -0.038308386; + } else { + result[0] += 0.0050441376; + } + } + } else { + result[0] += 0.045694016; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + result[0] += 0.010466478; + } else { + result[0] += 0.032060686; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + result[0] += 0.055632647; + } else { + result[0] += 0.10817957; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 244))) { + result[0] += 0.05290089; + } else { + result[0] += 0.15234004; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += 0.11994175; + } else { + result[0] += 0.16568963; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += 0.21408843; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { + result[0] += 0.109505884; + } else { + result[0] += 0.16057345; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += -0.021672187; + } else { + result[0] += -0.05343589; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += 0.0054487307; + } else { + result[0] += -0.017187914; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 92))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 188))) { + result[0] += -0.019816956; + } else { + result[0] += -0.0050099557; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 248))) { + result[0] += -0.03200326; + } else { + result[0] += -0.056725156; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + result[0] += -0.011511999; + } else { + result[0] += -0.028344637; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += 0.024851266; + } else { + result[0] += -0.05057622; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { + result[0] += -0.0015090223; + } else { + result[0] += 0.04174643; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 206))) { + result[0] += -0.014773312; + } else { + result[0] += -0.001539268; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 210))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + result[0] += 0.007884377; + } else { + result[0] += 0.061007004; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + result[0] += 0.021275358; + } else { + result[0] += 0.004744153; + } + } + } else { + result[0] += 0.053870168; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += 0.00918735; + } else { + result[0] += 0.06173278; + } + } else { + result[0] += 0.098605655; + } + } else { + result[0] += 0.13258949; + } + } + } + } + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 266))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { + result[0] += 0.0034634469; + } else { + result[0] += -0.01012047; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 202))) { + result[0] += 0.026168926; + } else { + result[0] += -0.003920808; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 34))) { + result[0] += 0.01599789; + } else { + result[0] += -0.02167696; + } + } else { + result[0] += -0.057240784; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 180))) { + result[0] += 0.016893527; + } else { + result[0] += -0.0182766; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { + result[0] += -0.027289895; + } else { + result[0] += -0.0045931367; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 236))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + result[0] += 0.06542163; + } else { + result[0] += 0.048862282; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 248))) { + result[0] += 0.012741444; + } else { + result[0] += -0.0075932243; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 250))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 64))) { + result[0] += 0.090353966; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 260))) { + result[0] += 0.042119607; + } else { + result[0] += 0.053020816; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { + result[0] += 0.03130157; + } else { + result[0] += 0.007205482; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + result[0] += 0.10881876; + } else { + result[0] += 0.054842055; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += 0.031206427; + } else { + result[0] += 0.093665555; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { + result[0] += 0.058885314; + } else { + result[0] += 0.009160067; + } + } + } else { + result[0] += 0.12904838; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 196))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 132))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 126))) { + result[0] += 0.009280808; + } else { + result[0] += 0.039755784; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 184))) { + result[0] += -0.0042881714; + } else { + result[0] += 0.02849459; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 176))) { + result[0] += 0.013534146; + } else { + result[0] += 0.042829912; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 196))) { + result[0] += 0.060934413; + } else { + result[0] += 0.04222716; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 166))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 252))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 242))) { + result[0] += -0.014791851; + } else { + result[0] += 0.03050575; + } + } else { + result[0] += -0.051454216; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 244))) { + result[0] += -0.00703993; + } else { + result[0] += 0.006275458; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 230))) { + result[0] += 0.05098787; + } else { + result[0] += 0.0045872494; + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 236))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 156))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { + result[0] += 0.010856108; + } else { + result[0] += -0.028078709; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 196))) { + result[0] += 0.059345823; + } else { + result[0] += 0.025444312; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 246))) { + result[0] += 0.1018459; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 226))) { + result[0] += 0.06307183; + } else { + result[0] += 0.049246445; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 192))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 248))) { + result[0] += -0.0057842466; + } else { + result[0] += 0.03709874; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 272))) { + result[0] += 0.10474528; + } else { + result[0] += 0.0065343017; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + result[0] += 0.14898759; + } else { + result[0] += 0.09902468; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 204))) { + result[0] += -0.020444617; + } else { + result[0] += 0.08464522; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + result[0] += -0.010948095; + } else { + result[0] += -0.023803001; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + result[0] += -0.011822949; + } else { + result[0] += 0.045119066; + } + } + } else { + result[0] += -0.06180344; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { + result[0] += -0.005668474; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { + result[0] += 0.06234616; + } else { + result[0] += 0.017185496; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { + result[0] += 0.024365185; + } else { + result[0] += 0.034411617; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + result[0] += 0.0037991663; + } else { + result[0] += 0.06540612; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + result[0] += 0.04412658; + } else { + result[0] += 0.09097751; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { + result[0] += 0.109255195; + } else { + result[0] += 0.15402773; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += 0.18228906; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { + result[0] += 0.091413066; + } else { + result[0] += 0.12995003; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 66))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + result[0] += -0.013535394; + } else { + result[0] += -0.030926574; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + result[0] += -0.02790393; + } else { + result[0] += -0.0042497306; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + result[0] += -0.016862048; + } else { + result[0] += -0.042121567; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += 0.043057855; + } else { + result[0] += -0.010520599; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 268))) { + result[0] += 0.0006321628; + } else { + result[0] += 0.043750294; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { + result[0] += 0.017152889; + } else { + result[0] += 0.0047487523; + } + } + } else { + result[0] += 0.047594197; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + result[0] += -0.0008511189; + } else { + result[0] += -0.015743343; + } + } else { + result[0] += 0.020423314; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { + result[0] += 0.071650326; + } else { + result[0] += 0.038795434; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.106261484; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { + result[0] += 0.05060423; + } else { + result[0] += 0.09152374; + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 32))) { + result[0] += -0.0061984994; + } else { + result[0] += 0.037181865; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { + result[0] += 0.05393646; + } else { + result[0] += 0.015753375; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 134))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + result[0] += -0.021209892; + } else { + result[0] += 0.0069300993; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + result[0] += -0.0077880546; + } else { + result[0] += -0.04197134; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 12))) { + result[0] += 0.040751807; + } else { + result[0] += -0.002328172; + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 254))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 198))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { + result[0] += 0.011177325; + } else { + result[0] += -0.0057386267; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { + result[0] += 0.022035722; + } else { + result[0] += 0.031592578; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { + result[0] += 0.046259467; + } else { + result[0] += 0.07964405; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.037900005; + } else { + result[0] += 0.006691271; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + result[0] += 0.045320172; + } else { + result[0] += 0.08683389; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + result[0] += 0.112302594; + } else { + result[0] += 0.15148906; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { + result[0] += 0.09804384; + } else { + result[0] += 0.15994717; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += -0.014513046; + } else { + result[0] += -0.04194841; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 60))) { + result[0] += -0.018427247; + } else { + result[0] += -0.009509578; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { + result[0] += -0.021712733; + } else { + result[0] += -0.033654116; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + result[0] += 0.017070105; + } else { + result[0] += -0.010606452; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { + result[0] += 0.003849056; + } else { + result[0] += -0.012776096; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += -0.01715595; + } else { + result[0] += -0.034953322; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + result[0] += 0.004928622; + } else { + result[0] += -0.009454594; + } + } else { + result[0] += 0.06586691; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + result[0] += 0.00017961742; + } else { + result[0] += -0.0068662055; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.034061044; + } else { + result[0] += 0.004285582; + } + } + } else { + result[0] += 0.041407876; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + result[0] += 0.0008129136; + } else { + result[0] += 0.046276525; + } + } else { + result[0] += 0.08599146; + } + } else { + result[0] += 0.092344016; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { + result[0] += 0.0022713204; + } else { + result[0] += -0.01581463; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + result[0] += -0.019155947; + } else { + result[0] += -0.031192351; + } + } + } else { + result[0] += -0.054840814; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += 0.034809362; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + result[0] += -0.03447778; + } else { + result[0] += -0.0010464619; + } + } + } else { + result[0] += 0.036184475; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { + result[0] += 0.011472473; + } else { + result[0] += -0.00536993; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { + result[0] += 0.017885301; + } else { + result[0] += 0.026684735; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 36))) { + result[0] += 0.050483853; + } else { + result[0] += 0.032498028; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { + result[0] += 0.107258014; + } else { + result[0] += 0.05922347; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += 0.14826694; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { + result[0] += 0.07493075; + } else { + result[0] += 0.105560794; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { + result[0] += -0.024180638; + } else { + result[0] += -0.010499556; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 218))) { + result[0] += 0.016562223; + } else { + result[0] += -0.013134924; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { + result[0] += -0.018523889; + } else { + result[0] += -0.030611722; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += -0.017535986; + } else { + result[0] += -0.004351136; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { + result[0] += -0.021926327; + } else { + result[0] += -0.008350995; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { + result[0] += -0.011140397; + } else { + result[0] += -0.04280148; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + result[0] += 0.03963171; + } else { + result[0] += 0.019383498; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 214))) { + result[0] += -0.00729556; + } else { + result[0] += -0.040553037; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.08876854; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.067758165; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { + result[0] += 0.03981258; + } else { + result[0] += 0.05752442; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { + result[0] += -0.00089354016; + } else { + result[0] += 0.0065446827; + } + } else { + result[0] += -0.014053066; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { + result[0] += 0.06474081; + } else { + result[0] += 0.017832294; + } + } + } + } + } + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 206))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 202))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 30))) { + result[0] += 0.0055920025; + } else { + result[0] += -0.013393702; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { + result[0] += 0.03213504; + } else { + result[0] += 0.004373153; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { + result[0] += -0.026502002; + } else { + result[0] += -0.0089536635; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 108))) { + result[0] += 0.023841746; + } else { + result[0] += -0.009630135; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 132))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 84))) { + result[0] += 0.03338425; + } else { + result[0] += -0.007993841; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 98))) { + result[0] += 0.039039984; + } else { + result[0] += 0.017829265; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + result[0] += -0.008180471; + } else { + result[0] += -0.017685762; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 172))) { + result[0] += 0.024588756; + } else { + result[0] += 0.00014103504; + } + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 120))) { + result[0] += -0.017513642; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 228))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 204))) { + result[0] += 0.053347778; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { + result[0] += 0.036463372; + } else { + result[0] += 0.019543491; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + result[0] += 0.06796604; + } else { + result[0] += 0.011818393; + } + } else { + result[0] += -0.003918262; + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 260))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 252))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 224))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 108))) { + result[0] += 0.055681136; + } else { + result[0] += 0.03390036; + } + } else { + result[0] += 0.014793222; + } + } else { + result[0] += 0.06464329; + } + } else { + result[0] += 0.1336614; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 214))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 200))) { + result[0] += -0.010694483; + } else { + result[0] += 0.0089774085; + } + } else { + result[0] += 0.09665075; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 214))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + result[0] += 0.005944577; + } else { + result[0] += 0.039748937; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { + result[0] += -0.036054015; + } else { + result[0] += -0.03007635; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { + result[0] += 0.07873906; + } else { + result[0] += 0.040747557; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 280))) { + result[0] += 0.10715278; + } else { + result[0] += 0.07093804; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 272))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 228))) { + result[0] += 0.0400591; + } else { + result[0] += 0.070340686; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { + result[0] += 0.006239193; + } else { + result[0] += 0.04050245; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + result[0] += 0.0143945515; + } else { + result[0] += -0.01724617; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { + result[0] += 0.06920057; + } else { + result[0] += 0.012906248; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { + result[0] += -0.028011957; + } else { + result[0] += -0.054441918; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 222))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += -0; + } else { + result[0] += -0.015584071; + } + } else { + result[0] += 0.04294205; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 176))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + result[0] += 0.0010215238; + } else { + result[0] += 0.018780788; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 36))) { + result[0] += 0.038763847; + } else { + result[0] += 0.023370415; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 224))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + result[0] += 0.032697503; + } else { + result[0] += 0.13195097; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += 0.074951015; + } else { + result[0] += 0.10605689; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += 0.12042389; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 6))) { + result[0] += 0.07597848; + } else { + result[0] += 0.059126675; + } + } else { + result[0] += 0.087247975; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + result[0] += -0.011189851; + } else { + result[0] += -0.023633793; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += -0.009847272; + } else { + result[0] += 0.0011721178; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + result[0] += -0.033823837; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + result[0] += -0.028593678; + } else { + result[0] += -0.023168823; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { + result[0] += 0.048958402; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 226))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { + result[0] += -0.0030530605; + } else { + result[0] += 0.009326304; + } + } else { + result[0] += 0.015771423; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + result[0] += -0.019048717; + } else { + result[0] += -0.00045981476; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + result[0] += 0.036297392; + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + result[0] += 0.004649827; + } else { + result[0] += 0.024824895; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { + result[0] += 0.0042260056; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + result[0] += 0.035964742; + } else { + result[0] += 0.05283689; + } + } + } else { + result[0] += 0.072465; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + result[0] += -0.012068967; + } else { + result[0] += -0.027094675; + } + } else { + result[0] += -0.066233106; + } + } else { + result[0] += 0.025606213; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { + result[0] += 0.0134207625; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + result[0] += -0.051151592; + } else { + result[0] += -0.013505876; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += 0.033789955; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 226))) { + result[0] += -0.0063351872; + } else { + result[0] += 0.01952872; + } + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { + result[0] += 0.017679365; + } else { + result[0] += 0.0012839021; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { + result[0] += 0.01832609; + } else { + result[0] += 0.031185871; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { + result[0] += 0.07488849; + } else { + result[0] += 0.046187967; + } + } else { + result[0] += 0.09955382; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { + result[0] += 0.069774024; + } else { + result[0] += 0.10838503; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { + result[0] += 0.05327609; + } else { + result[0] += 0.077984296; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + result[0] += 0.0047698705; + } else { + result[0] += 0.017100412; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + result[0] += -0.0088116955; + } else { + result[0] += 0.0046756053; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { + result[0] += -0.021546138; + } else { + result[0] += -0.06706627; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + result[0] += 0.0009891371; + } else { + result[0] += -0.015693253; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 76))) { + result[0] += -0.033967104; + } else { + result[0] += -0.010871164; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + result[0] += -0.01983557; + } else { + result[0] += -0.033842903; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += 3.485963e-05; + } else { + result[0] += 0.029934336; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { + result[0] += -0.009020819; + } else { + result[0] += -0.0016374525; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { + result[0] += -0.00035558367; + } else { + result[0] += 0.031120656; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { + result[0] += 0.0042784135; + } else { + result[0] += -0.003890027; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + result[0] += 0.03780892; + } else { + result[0] += 0.020871228; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { + result[0] += 0.032693; + } else { + result[0] += 0.016958345; + } + } else { + result[0] += 0.056264985; + } + } else { + result[0] += 0.061108418; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + result[0] += 0.016505312; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 58))) { + result[0] += -0.012738267; + } else { + result[0] += -0.020164208; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + result[0] += 0.0137371225; + } else { + result[0] += -0.010237254; + } + } + } else { + result[0] += -0.037308313; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + result[0] += 0.0016659134; + } else { + result[0] += 0.022526134; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { + result[0] += 0.02430934; + } else { + result[0] += 0.010316737; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + result[0] += 0.03054412; + } else { + result[0] += 0.013778204; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { + result[0] += 0.062106986; + } else { + result[0] += 0.08695674; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += 0.097699985; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { + result[0] += 0.048410773; + } else { + result[0] += 0.061695654; + } + } else { + result[0] += 0.0699871; + } + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { + result[0] += 0.017755916; + } else { + result[0] += -0.0076434105; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 80))) { + result[0] += -0.013907449; + } else { + result[0] += -0.028985953; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += -0.03517434; + } else { + result[0] += 0.02803059; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + result[0] += -0.005040298; + } else { + result[0] += 0.029063394; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 18))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 252))) { + result[0] += -0.0004860425; + } else { + result[0] += -0.00823578; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + result[0] += 0.020955933; + } else { + result[0] += -0.005362306; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + result[0] += -0.022735301; + } else { + result[0] += -0.006933318; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + result[0] += 0.008230773; + } else { + result[0] += -0.011880973; + } + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 232))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 192))) { + result[0] += 0.00536417; + } else { + result[0] += -0.020807503; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { + result[0] += 0.029748295; + } else { + result[0] += 0.041834693; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 196))) { + result[0] += -0.00087343634; + } else { + result[0] += -0.026689067; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 182))) { + result[0] += -0.0014600045; + } else { + result[0] += 0.013319497; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 256))) { + result[0] += 0.0012430011; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { + result[0] += -0.028792778; + } else { + result[0] += -0.024853468; + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { + result[0] += -0.009469046; + } else { + result[0] += 0.0108768735; + } + } else { + result[0] += -0.025166774; + } + } else { + result[0] += -0.061156314; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { + result[0] += -0.011232008; + } else { + result[0] += -0.025154117; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + result[0] += 0.008252529; + } else { + result[0] += 0.046288077; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 220))) { + result[0] += 0.040085025; + } else { + result[0] += -0.008784636; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + result[0] += -0.019954626; + } else { + result[0] += 0.0048681814; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += 0.007502618; + } else { + result[0] += -0.0053296923; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { + result[0] += 0.013098051; + } else { + result[0] += 0.022515437; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 222))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 234))) { + result[0] += 0.02392622; + } else { + result[0] += 0.006717711; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 92))) { + result[0] += 0.036931966; + } else { + result[0] += 0.024680862; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { + result[0] += 0.049618512; + } else { + result[0] += 0.030690536; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { + result[0] += 0.087034844; + } else { + result[0] += 0.06434413; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.076431125; + } else { + result[0] += 0.050094455; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.08487356; + } else { + result[0] += 0.016335858; + } + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { + result[0] += 0.0068731843; + } else { + result[0] += -0.027962524; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { + result[0] += -0.005616961; + } else { + result[0] += -0.020151896; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 160))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + result[0] += -0.011980721; + } else { + result[0] += -0.022607153; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + result[0] += 0.045720205; + } else { + result[0] += 0.011621847; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 58))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + result[0] += 0.02517539; + } else { + result[0] += -0.0003533575; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { + result[0] += 0.00938051; + } else { + result[0] += -0.011884357; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += -0.0057867505; + } else { + result[0] += -0.025664538; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { + result[0] += -0.008385401; + } else { + result[0] += -0.0017240072; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 266))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + result[0] += -0.000107964406; + } else { + result[0] += 0.0060515343; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 244))) { + result[0] += -0.005519016; + } else { + result[0] += 0.0014766084; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.02811285; + } else { + result[0] += 0.013522627; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { + result[0] += -0; + } else { + result[0] += 0.015318493; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { + result[0] += 0.02693863; + } else { + result[0] += 0.042594276; + } + } + } else { + result[0] += 0.05915534; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + result[0] += -0.015769325; + } else { + result[0] += 0.009167842; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + result[0] += -0.066482425; + } else { + result[0] += -0.029134443; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 58))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + result[0] += -0.010345658; + } else { + result[0] += -0.0004427325; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + result[0] += 0.00778659; + } else { + result[0] += -0.052467596; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += 0.025486384; + } else { + result[0] += 0.011895447; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + result[0] += 0.022794057; + } else { + result[0] += 0.048023786; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + result[0] += 0.024896143; + } else { + result[0] += 0.010721759; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += 0.04730309; + } else { + result[0] += 0.07061689; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += 0.079474784; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 6))) { + result[0] += 0.050025214; + } else { + result[0] += 0.03944496; + } + } else { + result[0] += 0.05712953; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 54))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += 0.0070698797; + } else { + result[0] += -0.009966139; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + result[0] += 0.015061869; + } else { + result[0] += -0.0023575744; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + result[0] += -0.021192541; + } else { + result[0] += -0.00409846; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 156))) { + result[0] += -0.01823308; + } else { + result[0] += 0.00014080759; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 206))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 100))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + result[0] += 0.02206879; + } else { + result[0] += -0.00015570642; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + result[0] += -0.015682366; + } else { + result[0] += -0.006868744; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { + result[0] += 0.0135331275; + } else { + result[0] += -0.023557609; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { + result[0] += 0.04654594; + } else { + result[0] += 0.0058658714; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + result[0] += -0.016888324; + } else { + result[0] += -0.00063805765; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + result[0] += 0.029826907; + } else { + result[0] += 0.007284993; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + result[0] += 0.001150948; + } else { + result[0] += 0.0069423555; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += -0.0042433054; + } else { + result[0] += 0.011705535; + } + } else { + result[0] += 0.025982574; + } + } else { + result[0] += 0.048386928; + } + } + } + } + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 206))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += -0.042859327; + } else { + result[0] += -0.0044231373; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { + result[0] += 0.049170107; + } else { + result[0] += 0.035520416; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { + result[0] += -0.0009743745; + } else { + result[0] += 0.009931285; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { + result[0] += -0.0017889539; + } else { + result[0] += -0.007930504; + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 226))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { + result[0] += 0.0063184327; + } else { + result[0] += -0.015213072; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += 0.014672543; + } else { + result[0] += 0.026392935; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { + result[0] += -0.037449803; + } else { + result[0] += -0.053623743; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { + result[0] += 0.002925502; + } else { + result[0] += -0.010573565; + } + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 264))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 152))) { + result[0] += 0.00568271; + } else { + result[0] += -0.004481957; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 212))) { + result[0] += 0.053150166; + } else { + result[0] += 0.014763187; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 194))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { + result[0] += -0.008710187; + } else { + result[0] += 0.075820915; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + result[0] += -0.023372917; + } else { + result[0] += -0.012099934; + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { + result[0] += 0.037964396; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 206))) { + result[0] += 0.0036865615; + } else { + result[0] += 0.021090202; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { + result[0] += -0.025623156; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 200))) { + result[0] += -0.004627872; + } else { + result[0] += 0.004686881; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 262))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { + result[0] += 0.0714253; + } else { + result[0] += 0.048554998; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 230))) { + result[0] += 0.02269533; + } else { + result[0] += 0.014301536; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 220))) { + result[0] += 0.0025220858; + } else { + result[0] += 0.011460328; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 56))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + result[0] += 0.019217266; + } else { + result[0] += 0.03249597; + } + } else { + result[0] += 0.035739582; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { + result[0] += -0.023269765; + } else { + result[0] += -0.01868113; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 180))) { + result[0] += -0.0065037594; + } else { + result[0] += -0.022253536; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { + result[0] += 0.0036995602; + } else { + result[0] += 0.0520568; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 146))) { + result[0] += 0.02432552; + } else { + result[0] += -0.01594592; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { + result[0] += 0.0497043; + } else { + result[0] += 0.022733098; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.055147793; + } else { + result[0] += 0.041854013; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + result[0] += 0.0019326921; + } else { + result[0] += -0.013451728; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 280))) { + result[0] += 0.029732933; + } else { + result[0] += 0.009648888; + } + } + } + } + } + } + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 206))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += -0.002109048; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 92))) { + result[0] += 0.04531033; + } else { + result[0] += 0.028058738; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { + result[0] += -0.0014130161; + } else { + result[0] += 0.009621711; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { + result[0] += -0.0014670437; + } else { + result[0] += -0.007695043; + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 226))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 156))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 192))) { + result[0] += 0.007604044; + } else { + result[0] += -0.0070991814; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 220))) { + result[0] += 0.020077487; + } else { + result[0] += 0.006828181; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { + result[0] += -0.03365531; + } else { + result[0] += -0.049779564; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { + result[0] += 0.0017921542; + } else { + result[0] += -0.009079668; + } + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 260))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 152))) { + result[0] += 0.005504231; + } else { + result[0] += -0.0039019831; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 212))) { + result[0] += 0.044337142; + } else { + result[0] += 0.0070573166; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 194))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { + result[0] += 0.028603448; + } else { + result[0] += -0.0078216465; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { + result[0] += 0.015729161; + } else { + result[0] += -0.016172249; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { + result[0] += 0.03005304; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { + result[0] += 0.016138185; + } else { + result[0] += 0.033333454; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { + result[0] += -0.02334422; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + result[0] += 0.0137236165; + } else { + result[0] += 0.003765919; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 260))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.06424896; + } else { + result[0] += 0.045115866; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { + result[0] += 0.020254442; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { + result[0] += 0.003927156; + } else { + result[0] += -0.00030379518; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 56))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + result[0] += 0.01626153; + } else { + result[0] += 0.030240763; + } + } else { + result[0] += 0.031179471; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { + result[0] += -0.019889602; + } else { + result[0] += -0.006115454; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { + result[0] += 0.02490984; + } else { + result[0] += 0.0096712075; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { + result[0] += -0.016214207; + } else { + result[0] += 0.0054753; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 162))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.04828751; + } else { + result[0] += 0.02183148; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + result[0] += 0.095508024; + } else { + result[0] += 0.037357938; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { + result[0] += 0.024429439; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += -0.032644305; + } else { + result[0] += -0.007228152; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 92))) { + result[0] += -0; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + result[0] += -0.0005319937; + } else { + result[0] += 0.04273183; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += -0; + } else { + result[0] += -0.015805844; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 220))) { + result[0] += 0.028671984; + } else { + result[0] += -0; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { + result[0] += -0.015004277; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { + result[0] += -0.043184254; + } else { + result[0] += -0.015434514; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { + result[0] += 0.027698422; + } else { + result[0] += 0.019098751; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { + result[0] += 0.0024283626; + } else { + result[0] += 0.019441115; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 58))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += 0.07004071; + } else { + result[0] += 0.022275856; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { + result[0] += -0.0013316347; + } else { + result[0] += 0.012960051; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 224))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.06203411; + } else { + result[0] += 0.035423633; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.057469275; + } else { + result[0] += 0.009981114; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 212))) { + result[0] += 0.023559755; + } else { + result[0] += 0.035965245; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { + result[0] += 0.058553632; + } else { + result[0] += 0.04478031; + } + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { + result[0] += 0.01235016; + } else { + result[0] += -0.0039201183; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 80))) { + result[0] += -0.007764698; + } else { + result[0] += -0.019835753; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += -0.006487711; + } else { + result[0] += 0.018991722; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + result[0] += -0.0039943634; + } else { + result[0] += 0.024546368; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { + result[0] += -0.0147830695; + } else { + result[0] += -0.008563632; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + result[0] += -0.030682612; + } else { + result[0] += 0.017968642; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 166))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + result[0] += 0.068739615; + } else { + result[0] += 0.00021970407; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + result[0] += -0.0028976346; + } else { + result[0] += -0.009871369; + } + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 236))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { + result[0] += -0.021564588; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { + result[0] += 0.0040140515; + } else { + result[0] += 0.016122099; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 210))) { + result[0] += -0.0033681232; + } else { + result[0] += 0.0073165386; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { + result[0] += -0.025544493; + } else { + result[0] += -0.01873306; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 230))) { + result[0] += -0.019029168; + } else { + result[0] += 0.004258202; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 254))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { + result[0] += -0.00011749373; + } else { + result[0] += -0.010187042; + } + } else { + result[0] += 0.037634984; + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + result[0] += -0.0064992644; + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 146))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { + result[0] += 0.022600017; + } else { + result[0] += -0.0016712642; + } + } else { + result[0] += 0.025729222; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 20))) { + result[0] += -0.007915151; + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += 0.0005776281; + } else { + result[0] += 0.031780552; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { + result[0] += -0.017479556; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 178))) { + result[0] += -0.04499294; + } else { + result[0] += -0.01988611; + } + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { + result[0] += 0.008722408; + } else { + result[0] += 0.032495603; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += -0.0015038172; + } else { + result[0] += 0.009706586; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.08136292; + } else { + result[0] += 0.015997507; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { + result[0] += 0.03901923; + } else { + result[0] += 0.0564677; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + result[0] += 0.0416153; + } else { + result[0] += 0.026928617; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { + result[0] += 0.041233912; + } else { + result[0] += 0.05209862; + } + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { + result[0] += 0.00972145; + } else { + result[0] += 0.04575323; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + result[0] += 0.02476694; + } else { + result[0] += -0.03629762; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.021036565; + } else { + result[0] += -0.045760524; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 224))) { + result[0] += 0.007162188; + } else { + result[0] += -0.018060943; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 22))) { + result[0] += -0.007815191; + } else { + result[0] += -0.017675815; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { + result[0] += -0.0029981257; + } else { + result[0] += 0.020084158; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { + result[0] += 0.02050746; + } else { + result[0] += -0.0026271623; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + result[0] += -0.011671978; + } else { + result[0] += -0.0055001257; + } + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 228))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { + result[0] += -0.019566214; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { + result[0] += 0.0054924428; + } else { + result[0] += -0.00093082106; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + result[0] += 0.0126124; + } else { + result[0] += 0.032813326; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 272))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + result[0] += -0.023916144; + } else { + result[0] += -0.017258545; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.05414641; + } else { + result[0] += 0.000113278526; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 254))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { + result[0] += -0.00055813155; + } else { + result[0] += -0.008351603; + } + } else { + result[0] += 0.032734703; + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + result[0] += -0.0139853405; + } else { + result[0] += 0.003684853; + } + } else { + result[0] += 0.008061817; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + result[0] += -0.06050358; + } else { + result[0] += -0.0023646136; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.028764948; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += -0.028082406; + } else { + result[0] += -0.0058664638; + } + } else { + result[0] += 0.023923343; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + result[0] += 0.009201097; + } else { + result[0] += -0.010257622; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + result[0] += 0.012351765; + } else { + result[0] += 0.08077949; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { + result[0] += 0.037493166; + } else { + result[0] += 0.052167382; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { + result[0] += 0.036415916; + } else { + result[0] += 0.046912543; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { + result[0] += 0.023727143; + } else { + result[0] += 0.036457557; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { + result[0] += -0.002685583; + } else { + result[0] += -0.006960124; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { + result[0] += 0.04494014; + } else { + result[0] += 0.0043056607; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 220))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + result[0] += -0.0039729676; + } else { + result[0] += -0.014122757; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { + result[0] += -0.025312701; + } else { + result[0] += -0.014867464; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + result[0] += 0.015924158; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 190))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 184))) { + result[0] += -0.0075506503; + } else { + result[0] += 0.010475333; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + result[0] += -0.027276382; + } else { + result[0] += -0.004030099; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 98))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { + result[0] += 0.014477782; + } else { + result[0] += -0.015422763; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { + result[0] += 0.03197563; + } else { + result[0] += 0.020037709; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { + result[0] += -0.019650823; + } else { + result[0] += 0.002236197; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 150))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { + result[0] += -0.02055483; + } else { + result[0] += -0.0014581191; + } + } else { + result[0] += -0.016465666; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 242))) { + result[0] += -0.05338929; + } else { + result[0] += -0.014811342; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { + result[0] += 0.01168551; + } else { + result[0] += 0.0010410798; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + result[0] += -0.005663588; + } else { + result[0] += 0.012237726; + } + } else { + result[0] += -0.015190408; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { + result[0] += -0.048400734; + } else { + result[0] += -0.01567449; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { + result[0] += 0.034811612; + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += -0.005241947; + } else { + result[0] += 0.0036905694; + } + } else { + result[0] += 0.027789101; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 172))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 168))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + result[0] += -0.0023323249; + } else { + result[0] += 0.008576729; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + result[0] += -0.02920859; + } else { + result[0] += -0.005879428; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 236))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { + result[0] += 0.01825313; + } else { + result[0] += 0.01146; + } + } else { + result[0] += -0.0015520846; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + result[0] += 0.050887883; + } else { + result[0] += 0.0008823246; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { + result[0] += 0.04027082; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { + result[0] += 0.02058533; + } else { + result[0] += 0.039607793; + } + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += 0.0065161586; + } else { + result[0] += -0.007997109; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { + result[0] += 0.005459733; + } else { + result[0] += -0.0032214918; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { + result[0] += -0.015889943; + } else { + result[0] += -0.054882165; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 160))) { + result[0] += 0.013258442; + } else { + result[0] += -0.008985414; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { + result[0] += 0.03941143; + } else { + result[0] += 0.01124495; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { + result[0] += -0.01534603; + } else { + result[0] += 0.0053413953; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { + result[0] += -0.017410088; + } else { + result[0] += 0.013132173; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { + result[0] += -0.0060942764; + } else { + result[0] += -0.0017843047; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { + result[0] += 0.035340164; + } else { + result[0] += 0.0032424498; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.02138096; + } else { + result[0] += 0.011583105; + } + } + } else { + result[0] += -0.0026775673; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { + result[0] += 0.0032441877; + } else { + result[0] += 0.013359073; + } + } else { + result[0] += 0.030153606; + } + } else { + result[0] += 0.036456186; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + result[0] += -0.0117252795; + } else { + result[0] += 0.0043645753; + } + } else { + result[0] += -0.041562933; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + result[0] += 0.0008401942; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + result[0] += -0.01901227; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { + result[0] += 0.014339037; + } else { + result[0] += -0.00501071; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 16))) { + result[0] += 0.012573126; + } else { + result[0] += 0.0028887743; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 38))) { + result[0] += 0.046177443; + } else { + result[0] += 0.015051349; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { + result[0] += 0.010199251; + } else { + result[0] += -0.039779864; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { + result[0] += -0.004799234; + } else { + result[0] += 0.008129753; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += 0.04275175; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + result[0] += -0.008011124; + } else { + result[0] += 0.002239405; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 268))) { + result[0] += 0.017378718; + } else { + result[0] += 0.03415826; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { + result[0] += 0.02640838; + } else { + result[0] += 0.039306883; + } + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += 0.0055097975; + } else { + result[0] += -0.007307568; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += 0.0022932815; + } else { + result[0] += -0.0064788023; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { + result[0] += -0.014033759; + } else { + result[0] += -0.049122956; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { + result[0] += 0.005159875; + } else { + result[0] += -0.009883177; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { + result[0] += -0.013826482; + } else { + result[0] += 0.022371221; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { + result[0] += -0.0141291395; + } else { + result[0] += 0.004760613; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { + result[0] += -0.007064409; + } else { + result[0] += 0.023824794; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + result[0] += -0.00016228954; + } else { + result[0] += -0.007653934; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 208))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { + result[0] += 0.004959669; + } else { + result[0] += -0.001015551; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { + result[0] += 0.027703479; + } else { + result[0] += 0.0036117423; + } + } + } else { + result[0] += -0.0026553113; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { + result[0] += 0.013676906; + } else { + result[0] += -0.0007326856; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + result[0] += -0; + } else { + result[0] += 0.030973986; + } + } + } else { + result[0] += 0.029461041; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 8))) { + result[0] += -0.009251536; + } else { + result[0] += 0.009845628; + } + } else { + result[0] += -0.04083568; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.029976163; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + result[0] += -0.027244702; + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { + result[0] += -0.004565258; + } else { + result[0] += 0.025788173; + } + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 254))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + result[0] += 0.011977387; + } else { + result[0] += 0.00021163402; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { + result[0] += 0.038581323; + } else { + result[0] += 0.013672948; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { + result[0] += 0.0043165404; + } else { + result[0] += -0.035845544; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { + result[0] += -0.0024726556; + } else { + result[0] += 0.008141091; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += 0.03638802; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { + result[0] += 0.0001506592; + } else { + result[0] += 0.009602265; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { + result[0] += 0.022467962; + } else { + result[0] += 0.03540038; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.06481004; + } else { + result[0] += 0.017584896; + } + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 140))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 104))) { + result[0] += 0.045290753; + } else { + result[0] += -0.01791346; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + result[0] += -0.058552768; + } else { + result[0] += 0.0349788; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 256))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 198))) { + result[0] += 0.0336964; + } else { + result[0] += 0.0018466607; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 176))) { + result[0] += -0.0049308436; + } else { + result[0] += -0.012199649; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 100))) { + result[0] += -0.007085; + } else { + result[0] += -0.016483508; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + result[0] += 0.006847045; + } else { + result[0] += -0.0070480793; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { + result[0] += -0.0041096415; + } else { + result[0] += 0.0021135767; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { + result[0] += -0.0060591055; + } else { + result[0] += -0.0011625281; + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 138))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + result[0] += 0.02211509; + } else { + result[0] += 0.060023166; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { + result[0] += -0.013461961; + } else { + result[0] += 0.04670935; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + result[0] += -0; + } else { + result[0] += 0.025248567; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 154))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 148))) { + result[0] += -0.008415373; + } else { + result[0] += 0.013447831; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 138))) { + result[0] += -0.016349984; + } else { + result[0] += -0.030504474; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += 0.00021573504; + } else { + result[0] += -0.013515745; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { + result[0] += 8.818205e-05; + } else { + result[0] += 0.010008925; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 18))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + result[0] += 0.0037289502; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { + result[0] += 0.054951753; + } else { + result[0] += -0.0039979555; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + result[0] += -0.00068969437; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + result[0] += -0.012617968; + } else { + result[0] += -0.027266933; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 160))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 48))) { + result[0] += 0.0068334453; + } else { + result[0] += -0.0039404687; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { + result[0] += -0; + } else { + result[0] += 0.039604023; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 36))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { + result[0] += -0.0074695237; + } else { + result[0] += 0.0059394534; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 218))) { + result[0] += 0.02302828; + } else { + result[0] += -0.047223628; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 156))) { + result[0] += -0.016071025; + } else { + result[0] += 0.0044617057; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + result[0] += -0.0016089712; + } else { + result[0] += 0.0069275023; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.030206159; + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { + result[0] += 0.010797313; + } else { + result[0] += -0.002882598; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { + result[0] += 0.020259693; + } else { + result[0] += 0.0355315; + } + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 192))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 78))) { + result[0] += -0.0044226176; + } else { + result[0] += -0.02530554; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + result[0] += 0.008079353; + } else { + result[0] += -0.003107704; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { + result[0] += -0.026262758; + } else { + result[0] += -0.013569482; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { + result[0] += -0.007361694; + } else { + result[0] += 0.01589146; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 186))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + result[0] += 0.0066897767; + } else { + result[0] += -0.03870764; + } + } else { + result[0] += 0.022245135; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 156))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 46))) { + result[0] += 0.0011395493; + } else { + result[0] += -0.0057673557; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { + result[0] += 0.022744471; + } else { + result[0] += 0.008478357; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.028410172; + } else { + result[0] += 0.014004901; + } + } else { + result[0] += 0.0010523595; + } + } else { + result[0] += 0.02651517; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 176))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { + result[0] += -0.0015872531; + } else { + result[0] += -0.017748317; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += -0.0032637853; + } else { + result[0] += 0.010829237; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 242))) { + result[0] += 0.034405578; + } else { + result[0] += 0.00891859; + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 254))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { + result[0] += 0.007226427; + } else { + result[0] += -0.012499331; + } + } else { + result[0] += 0.04246553; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 88))) { + result[0] += 0.002265221; + } else { + result[0] += -0.0032362868; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + result[0] += -0.011394487; + } else { + result[0] += -0.0008440514; + } + } + } else { + result[0] += -0.028067807; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { + result[0] += 0.08105516; + } else { + result[0] += 0.018269602; + } + } else { + result[0] += 0.015147194; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { + result[0] += 0.014643987; + } else { + result[0] += 0.00034420882; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { + result[0] += 0.019848578; + } else { + result[0] += 0.0055640773; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.0281445; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { + result[0] += 0.016479755; + } else { + result[0] += 0.023855714; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 252))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + result[0] += 0.0043632514; + } else { + result[0] += -0.0032153563; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += -0.0008953023; + } else { + result[0] += -0.012973676; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 224))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { + result[0] += 0.06142653; + } else { + result[0] += 0.014740917; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + result[0] += 0.036935415; + } else { + result[0] += -0.0027422463; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { + result[0] += -0.043081205; + } else { + result[0] += -0.020138515; + } + } else { + result[0] += -0.012991915; + } + } else { + result[0] += 0.005466835; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { + result[0] += -0.00065770803; + } else { + result[0] += 0.020950794; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 98))) { + result[0] += -0.0020690185; + } else { + result[0] += 0.0022426536; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.01779854; + } else { + result[0] += 0.007740788; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + result[0] += 0.010289395; + } else { + result[0] += 0.022736436; + } + } else { + result[0] += 0.02622193; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { + result[0] += -0.0030147363; + } else { + result[0] += -0.01984936; + } + } else { + result[0] += -0.014292531; + } + } else { + result[0] += 0.019834789; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { + result[0] += 0.012920295; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { + result[0] += 0.07497602; + } else { + result[0] += 0.01954972; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { + result[0] += 0.010222583; + } else { + result[0] += 0.00049927505; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + result[0] += -0.0032804606; + } else { + result[0] += 0.0070060194; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.024341205; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { + result[0] += 0.015133323; + } else { + result[0] += 0.021515321; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += 0.0021268248; + } else { + result[0] += -0.004917339; + } + } else { + result[0] += 0.020086113; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.023110032; + } else { + result[0] += -0.008982231; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 206))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + result[0] += -0.01757216; + } else { + result[0] += -5.0931576e-05; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + result[0] += 0.017951632; + } else { + result[0] += 0.0057268315; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 62))) { + result[0] += 0.0030992; + } else { + result[0] += -0.010930783; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + result[0] += -0.00038469338; + } else { + result[0] += -0.004318268; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { + result[0] += -0; + } else { + result[0] += 0.017621709; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + result[0] += 0.0036806122; + } else { + result[0] += -4.4085446e-05; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.021112496; + } else { + result[0] += 0.007883241; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + result[0] += 0.007880076; + } else { + result[0] += 0.02039106; + } + } else { + result[0] += 0.023736782; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 14))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + result[0] += -0; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { + result[0] += 0.022109; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { + result[0] += -0.011896599; + } else { + result[0] += -0.02695978; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 28))) { + result[0] += 0.017046606; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { + result[0] += -0.022864131; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { + result[0] += 0.01655387; + } else { + result[0] += -0.0029186176; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + result[0] += 0.006224805; + } else { + result[0] += -0.0006160491; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { + result[0] += 0.03912334; + } else { + result[0] += 0.0064465962; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + result[0] += 0.03801068; + } else { + result[0] += -0; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { + result[0] += -0.0056696637; + } else { + result[0] += 0.002394879; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { + result[0] += 0.0026905416; + } else { + result[0] += 0.014434603; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 18))) { + result[0] += -0.0066401684; + } else { + result[0] += -0.02399191; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + result[0] += 0.01420292; + } else { + result[0] += 0.007666865; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { + result[0] += 0.015625248; + } else { + result[0] += 0.035386283; + } + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 168))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 164))) { + result[0] += -0.0026949244; + } else { + result[0] += 0.010536998; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 172))) { + result[0] += -0.02333055; + } else { + result[0] += -0.004258865; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 190))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 256))) { + result[0] += 0.010322237; + } else { + result[0] += -0.004513755; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 194))) { + result[0] += -0.013793135; + } else { + result[0] += 0.00041879248; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { + result[0] += -0.0012282294; + } else { + result[0] += 0.012586914; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { + result[0] += -0.013370514; + } else { + result[0] += -0.009561422; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.020673957; + } else { + result[0] += 0.01021281; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += 0.029428396; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + result[0] += -0.0008754981; + } else { + result[0] += -0.0100570135; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { + result[0] += 0.016708953; + } else { + result[0] += 0.009689736; + } + } else { + result[0] += 0.00040766338; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { + result[0] += 0.024215741; + } else { + result[0] += 0.012051038; + } + } + } + } + } + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + result[0] += 0.0013062642; + } else { + result[0] += 0.019394014; + } + } else { + result[0] += -0.0104289735; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 48))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += -0.05682643; + } else { + result[0] += 0.026358023; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 126))) { + result[0] += 0.0067068855; + } else { + result[0] += -0.0033787123; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += -0.017422771; + } else { + result[0] += -0.013566106; + } + } else { + result[0] += 0.05098955; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 264))) { + result[0] += 0.0021389506; + } else { + result[0] += 0.022104675; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 196))) { + result[0] += -0.0034482577; + } else { + result[0] += -0.04897159; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += 0.04547104; + } else { + result[0] += -0.0036854; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.031185264; + } else { + result[0] += -0.00029240636; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + result[0] += 0.048903078; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { + result[0] += -0.012713486; + } else { + result[0] += -0.0045942473; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { + result[0] += -0.024384957; + } else { + result[0] += 0.010758282; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { + result[0] += 0.030021293; + } else { + result[0] += 0.017209774; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 220))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { + result[0] += -0.0025992983; + } else { + result[0] += 0.0014243874; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += -0.014005645; + } else { + result[0] += 0.00015922675; + } + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 184))) { + result[0] += -0.0023512202; + } else { + result[0] += 0.00030087968; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { + result[0] += 0.015333219; + } else { + result[0] += 0.050305735; + } + } else { + result[0] += 0.0038954068; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { + result[0] += 0.005740883; + } else { + result[0] += -0.011134521; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { + result[0] += -0.0102155125; + } else { + result[0] += -0.01861443; + } + } else { + result[0] += -0.008878123; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 36))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { + result[0] += 0.07084565; + } else { + result[0] += 0.019467566; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 106))) { + result[0] += -0.005714934; + } else { + result[0] += 0.0072955615; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { + result[0] += 0.0217909; + } else { + result[0] += 0.008551905; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + result[0] += 0.016447848; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { + result[0] += 0.028782597; + } else { + result[0] += -0.027478626; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { + result[0] += 0.007580962; + } else { + result[0] += 0.0014997128; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 214))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { + result[0] += -0.0022094585; + } else { + result[0] += -0.02451445; + } + } else { + result[0] += 0.024397848; + } + } else { + result[0] += -0.01616343; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + result[0] += 0.011657309; + } else { + result[0] += 0.0012069183; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += 0.040601656; + } else { + result[0] += 0.009451126; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + result[0] += -0.022806326; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + result[0] += 0.03292076; + } else { + result[0] += 0.001937497; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += 0.0035285496; + } else { + result[0] += -0.0058691064; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + result[0] += 0.012688639; + } else { + result[0] += 0.005491004; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { + result[0] += 0.0044394704; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 192))) { + result[0] += 0.012408857; + } else { + result[0] += 0.019069476; + } + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { + result[0] += 0.006046584; + } else { + result[0] += -0.015792418; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += 0.027976854; + } else { + result[0] += 0.012421743; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += 0.054596562; + } else { + result[0] += 0.01953054; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { + result[0] += -0.003245589; + } else { + result[0] += 0.013587843; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.021004315; + } else { + result[0] += -0.008656485; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + result[0] += 0.01555752; + } else { + result[0] += -0.0006588964; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { + result[0] += -0.0020559; + } else { + result[0] += -0.010479237; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += 0.021051032; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 220))) { + result[0] += 0.006872528; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { + result[0] += -0.00075457775; + } else { + result[0] += -0.008546843; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { + result[0] += 0.009004579; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 2))) { + result[0] += 0.013524537; + } else { + result[0] += 0.02246733; + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 202))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { + result[0] += -0.004806658; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + result[0] += -0.02193972; + } else { + result[0] += -0.0058797724; + } + } + } else { + result[0] += -0.023569103; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 264))) { + result[0] += 0.016511796; + } else { + result[0] += -0.0023302487; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += 0.004636476; + } else { + result[0] += 0.03744141; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { + result[0] += -0.0021920146; + } else { + result[0] += 0.0044151954; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += 0.013442422; + } else { + result[0] += 0.025949672; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 252))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { + result[0] += 0.012082203; + } else { + result[0] += 0.017159306; + } + } else { + result[0] += 0.018528607; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + result[0] += 0.012809316; + } else { + result[0] += 0.0020744025; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + result[0] += 0.02068119; + } else { + result[0] += 0.012666582; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.025207102; + } else { + result[0] += -0.0042135157; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + result[0] += 0.019998292; + } else { + result[0] += -0.011731009; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += 0.04935061; + } else { + result[0] += 0.00985283; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 2))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 10))) { + result[0] += -0.0114418; + } else { + result[0] += 0.001085467; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { + result[0] += 0.005336402; + } else { + result[0] += 0.040240493; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + result[0] += -0.0003519562; + } else { + result[0] += -0.016158544; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 22))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 186))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += -0.052682735; + } else { + result[0] += -0.024834817; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + result[0] += 0.0076021785; + } else { + result[0] += -0.009852809; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 24))) { + result[0] += 0.07371118; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + result[0] += -0.0062063127; + } else { + result[0] += -0.0016409116; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 138))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 96))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += -0.0056977295; + } else { + result[0] += -0.0020788263; + } + } else { + result[0] += 0.013674322; + } + } else { + result[0] += -0.012557744; + } + } else { + result[0] += 0.020858606; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { + result[0] += 0.004243218; + } else { + result[0] += -0.0012915577; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { + result[0] += 0.012041297; + } else { + result[0] += 0.0036917683; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { + result[0] += 0.021245888; + } else { + result[0] += 0.010770134; + } + } else { + result[0] += 0.026461909; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 252))) { + result[0] += 0.011941756; + } else { + result[0] += 0.01697006; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 190))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + result[0] += 0.0059637506; + } else { + result[0] += 0.0009332538; + } + } else { + result[0] += -0.01690738; + } + } else { + result[0] += 0.018049363; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { + result[0] += -0.003757357; + } else { + result[0] += -0.010965187; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 138))) { + result[0] += 0.054250848; + } else { + result[0] += -0.00039252196; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + result[0] += 0.01451879; + } else { + result[0] += 0.037112333; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 36))) { + result[0] += -0.017370671; + } else { + result[0] += -0.0013852807; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 222))) { + result[0] += 0.009491138; + } else { + result[0] += 0.03599734; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + result[0] += 0.0021581096; + } else { + result[0] += -0.0014275697; + } + } else { + result[0] += 0.009684578; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { + result[0] += 0.009782214; + } else { + result[0] += -0.0025362705; + } + } else { + result[0] += 0.015987474; + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { + result[0] += 0.031121805; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + result[0] += -0.0017203381; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { + result[0] += 0.030785752; + } else { + result[0] += -0.005102741; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + result[0] += -0.005013264; + } else { + result[0] += 0.027989835; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + result[0] += -0.024730321; + } else { + result[0] += -0.0032983057; + } + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += 0.009897011; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { + result[0] += -0.0021574632; + } else { + result[0] += 0.057048213; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { + result[0] += 0.010313834; + } else { + result[0] += 0.002621749; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.015189426; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { + result[0] += 0.009792722; + } else { + result[0] += 0.014417933; + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.0014002819; + } else { + result[0] += 0.006653227; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + result[0] += -0.023881285; + } else { + result[0] += -0; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + result[0] += 0.018696984; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.0133892; + } else { + result[0] += 0.008133677; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += 0.043852713; + } else { + result[0] += 0.0058281594; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 212))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 144))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 174))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { + result[0] += -0.0026800435; + } else { + result[0] += 0.0020921542; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + result[0] += 0.0018816863; + } else { + result[0] += -0.027228499; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + result[0] += -0.014789981; + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 150))) { + result[0] += -0.009289259; + } else { + result[0] += -0.0037157678; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 158))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + result[0] += -0.009539143; + } else { + result[0] += 0.006927042; + } + } else { + result[0] += 0.039872702; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 160))) { + result[0] += -0.039126202; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 166))) { + result[0] += -0.002670629; + } else { + result[0] += 0.001439321; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + result[0] += -5.299445e-05; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + result[0] += -0.019934397; + } else { + result[0] += -0.0012183964; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { + result[0] += -0.0013647849; + } else { + result[0] += 0.038733196; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + result[0] += 0.008902398; + } else { + result[0] += -0.00011572992; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 14))) { + result[0] += 3.4862278e-06; + } else { + result[0] += 0.012706413; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { + result[0] += 0.007615597; + } else { + result[0] += -0.0051007196; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 48))) { + result[0] += 0.015438475; + } else { + result[0] += 0.0018905745; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 246))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 230))) { + result[0] += 0.008593912; + } else { + result[0] += 0.014445068; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += -0.00014397338; + } else { + result[0] += 0.0083941175; + } + } + } else { + result[0] += -0.004308312; + } + } else { + result[0] += 0.046102088; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { + result[0] += -0.0013631996; + } else { + result[0] += -0.011290415; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 200))) { + result[0] += 0.00060525746; + } else { + result[0] += 0.013914026; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 248))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + result[0] += -0.023959834; + } else { + result[0] += -0.010296635; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { + result[0] += 0.008832153; + } else { + result[0] += 0.0023550882; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.015583684; + } else { + result[0] += 0.010421765; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { + result[0] += -0.00011256654; + } else { + result[0] += -0.02525649; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 232))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 230))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { + result[0] += 0.005643142; + } else { + result[0] += -0.00010354039; + } + } else { + result[0] += -0.015879504; + } + } else { + result[0] += 0.014167144; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 246))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.049543332; + } else { + result[0] += 0.0046836296; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { + result[0] += -0.017134476; + } else { + result[0] += -0.007789578; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += -0.008826947; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 250))) { + result[0] += -0.0007550689; + } else { + result[0] += 0.0071488456; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 238))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 106))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { + result[0] += -0.019138163; + } else { + result[0] += 0.008177842; + } + } else { + result[0] += 0.03202384; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { + result[0] += 0.03914841; + } else { + result[0] += 0.027094522; + } + } else { + result[0] += 0.0053101527; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += 0.0403822; + } else { + result[0] += 0.013740751; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { + result[0] += -0.030392349; + } else { + result[0] += 0.0028747516; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 80))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { + result[0] += -0.008877286; + } else { + result[0] += 0.012316696; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 118))) { + result[0] += 0.004029528; + } else { + result[0] += -0.00029411144; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { + result[0] += 0.011664437; + } else { + result[0] += 0.0006082279; + } + } else { + result[0] += -0.006005393; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + result[0] += 0.09773924; + } else { + result[0] += 0.02065113; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 200))) { + result[0] += 0.0035237612; + } else { + result[0] += 0.009829774; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 208))) { + result[0] += -0.007697291; + } else { + result[0] += 0.0015825549; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + result[0] += 0.0036124797; + } else { + result[0] += 0.014532133; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + result[0] += 0.0012666411; + } else { + result[0] += -0.03202522; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { + result[0] += 0.020200811; + } else { + result[0] += 0.004590917; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { + result[0] += 0.03727207; + } else { + result[0] += 0.016316103; + } + } else { + result[0] += -0.003320934; + } + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += 0.053239834; + } else { + result[0] += 0.015470129; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + result[0] += -0.0021254455; + } else { + result[0] += -0.009888625; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + result[0] += 0.062025126; + } else { + result[0] += -0.00554525; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { + result[0] += 0.057511933; + } else { + result[0] += -0.008578778; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { + result[0] += -0.046820056; + } else { + result[0] += -0.02672096; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 70))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + result[0] += -0.03445236; + } else { + result[0] += 0.01575507; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { + result[0] += -0.029678077; + } else { + result[0] += -0.0074849683; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + result[0] += 0.013723537; + } else { + result[0] += -0.0020312674; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { + result[0] += -0.004165123; + } else { + result[0] += -0.00078670814; + } + } + } + } + } + } + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += -0; + } else { + result[0] += -0.03194522; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { + result[0] += 0.025147652; + } else { + result[0] += 0.0062306984; + } + } + } else { + result[0] += -0.0008928859; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += 0.13331777; + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 206))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { + result[0] += 0.030818671; + } else { + result[0] += 0.011000312; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + result[0] += 0.0052034413; + } else { + result[0] += -0.002728651; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += 0.0031897593; + } else { + result[0] += 0.017273722; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { + result[0] += -0.02854721; + } else { + result[0] += 0.00037428603; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + result[0] += -0.0019407201; + } else { + result[0] += -0.007221583; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 62))) { + result[0] += 0.047634736; + } else { + result[0] += -0.017209576; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { + result[0] += 0.025280062; + } else { + result[0] += 0.012840076; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { + result[0] += 0.038194098; + } else { + result[0] += 0.008874618; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { + result[0] += -0.03347216; + } else { + result[0] += -0.0004954495; + } + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 202))) { + result[0] += -0.009764884; + } else { + result[0] += -0.027206961; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 202))) { + result[0] += 0.00784516; + } else { + result[0] += 0.0012178732; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 170))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 168))) { + result[0] += 0.024105137; + } else { + result[0] += -0.0019981433; + } + } else { + result[0] += 0.031363543; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + result[0] += -0.0072048977; + } else { + result[0] += -0.022911293; + } + } else { + result[0] += 0.010616104; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { + result[0] += 0.008845729; + } else { + result[0] += -0.0013868009; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 246))) { + result[0] += -0.0024586073; + } else { + result[0] += -0.0086732535; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 196))) { + result[0] += 0.01107409; + } else { + result[0] += -0.0011529119; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + result[0] += 0.013766219; + } else { + result[0] += 0.026060035; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 210))) { + result[0] += -0.004132199; + } else { + result[0] += -0.01266666; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 264))) { + result[0] += 0.011719443; + } else { + result[0] += -0.0067507224; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 90))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 188))) { + result[0] += 0.0155691; + } else { + result[0] += 0.010912938; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 198))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 188))) { + result[0] += 0.0051367334; + } else { + result[0] += -0.00038397807; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.044876117; + } else { + result[0] += 0.005380819; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 124))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 250))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + result[0] += -0.01634503; + } else { + result[0] += -0; + } + } else { + result[0] += -0.002735744; + } + } else { + result[0] += 0.012586943; + } + } else { + result[0] += -0.0009229357; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 106))) { + result[0] += -0.013541499; + } else { + result[0] += 0.016169539; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += -0; + } else { + result[0] += 0.026296902; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { + result[0] += 0.0010251206; + } else { + result[0] += 0.007284665; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 100))) { + result[0] += -0.0034584904; + } else { + result[0] += 0.0022243506; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { + result[0] += 0.008507351; + } else { + result[0] += 0.00037087366; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { + result[0] += -0.0015936692; + } else { + result[0] += -0.00584162; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.03250199; + } else { + result[0] += 0.014099632; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 174))) { + result[0] += 0.0007955474; + } else { + result[0] += 0.0064386777; + } + } + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { + result[0] += -0.032964613; + } else { + result[0] += 0.017446639; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { + result[0] += -0.015205093; + } else { + result[0] += -0; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + result[0] += 0.003139617; + } else { + result[0] += -0.014359586; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += -0.0014178219; + } else { + result[0] += 0.002840144; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 62))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + result[0] += 0.041497182; + } else { + result[0] += 0.001743556; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { + result[0] += -0.014737419; + } else { + result[0] += -0.0060345526; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 188))) { + result[0] += -0.0014039263; + } else { + result[0] += 0.009091877; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { + result[0] += -0.013735535; + } else { + result[0] += -0.0066661197; + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + result[0] += 0.05542008; + } else { + result[0] += 0.0056177597; + } + } else { + result[0] += 0.021063056; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += -0.008560927; + } else { + result[0] += 0.007372921; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + result[0] += 0.0033190027; + } else { + result[0] += 0.02229972; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 162))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { + result[0] += 0.0031475432; + } else { + result[0] += 0.01872689; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { + result[0] += -0.0027235162; + } else { + result[0] += -0.011199118; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { + result[0] += -0.013897911; + } else { + result[0] += -0.0017742496; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { + result[0] += 0.045032453; + } else { + result[0] += 0.0035365508; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { + result[0] += 0.015004215; + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += -0.02693589; + } else { + result[0] += 0.0030036382; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 236))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 220))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { + result[0] += -0.005196058; + } else { + result[0] += -0.0010462991; + } + } else { + result[0] += -0.036202352; + } + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += 0.009205636; + } else { + result[0] += -0.008914505; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + result[0] += 0.00077702984; + } else { + result[0] += -0.006057858; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 78))) { + result[0] += 0.011342842; + } else { + result[0] += 0.003530657; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + result[0] += 0.03748219; + } else { + result[0] += 0.014057839; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 102))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += -0.0016399727; + } else { + result[0] += -0.011802055; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += -0.0016713021; + } else { + result[0] += 0.0056822044; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + result[0] += 0.00386166; + } else { + result[0] += -0.0027170626; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 46))) { + result[0] += 0.019643234; + } else { + result[0] += 0.0028665168; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { + result[0] += 0.0005789458; + } else { + result[0] += 0.0143351955; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { + result[0] += 0.011239639; + } else { + result[0] += -0.016711237; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { + result[0] += 0.019091167; + } else { + result[0] += 0.0046307947; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.050568253; + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += 0.03797291; + } else { + result[0] += 0.007982335; + } + } + } else { + result[0] += 0.0035218038; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + result[0] += -0.016012253; + } else { + result[0] += 0.0023404376; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + result[0] += -0.001683798; + } else { + result[0] += 0.013443462; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 232))) { + result[0] += -0.0012087579; + } else { + result[0] += 0.009097977; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 192))) { + result[0] += -0.010690723; + } else { + result[0] += -0.001824428; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += 0.017058346; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 220))) { + result[0] += 0.004044805; + } else { + result[0] += -0.0013507138; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 242))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { + result[0] += 0.008160069; + } else { + result[0] += 0.015147629; + } + } else { + result[0] += 0.024462283; + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { + result[0] += -0.000107747815; + } else { + result[0] += 0.0067256675; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { + result[0] += -0.0076294737; + } else { + result[0] += -0.043650094; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 178))) { + result[0] += 0.034688678; + } else { + result[0] += -0.02312972; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { + result[0] += -0.002360337; + } else { + result[0] += 0.012530643; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + result[0] += -0.01826018; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { + result[0] += 0.0056892973; + } else { + result[0] += 0.03400869; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { + result[0] += -0.009709366; + } else { + result[0] += -0.00013415277; + } + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 156))) { + result[0] += 0.010154608; + } else { + result[0] += 0.001891119; + } + } else { + result[0] += 0.03333105; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { + result[0] += -0.022681896; + } else { + result[0] += -0.03837011; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { + result[0] += 0.007119199; + } else { + result[0] += -0.015321928; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 134))) { + result[0] += 0.08953688; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + result[0] += -0.022155154; + } else { + result[0] += 0.004006593; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + result[0] += 0.013605259; + } else { + result[0] += 0.027043974; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { + result[0] += -0.0115665; + } else { + result[0] += 0.007095266; + } + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += 0.035634317; + } else { + result[0] += 0.017916704; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + result[0] += -0.0043744547; + } else { + result[0] += -0.00026779916; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 236))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + result[0] += 0.0133640645; + } else { + result[0] += 0.004636778; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += 0.012312482; + } else { + result[0] += 0.06577807; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 132))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += 0.0068262727; + } else { + result[0] += -0.04068326; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { + result[0] += -0.0010328897; + } else { + result[0] += -0.005453972; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { + result[0] += -0.0008340759; + } else { + result[0] += -0.008702307; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 16))) { + result[0] += 0.040576342; + } else { + result[0] += 0.0024016355; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + result[0] += 0.029419197; + } else { + result[0] += 0.0010929945; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 156))) { + result[0] += -0.0012289739; + } else { + result[0] += -0.0070381216; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.0115409745; + } else { + result[0] += 0.0048038764; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 242))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { + result[0] += 0.023705222; + } else { + result[0] += 0.010082607; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.012382432; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 228))) { + result[0] += 0.004983134; + } else { + result[0] += -0; + } + } + } + } + } + } + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += 0.061853677; + } else { + result[0] += -0.0063631344; + } + } else { + result[0] += -0.036711987; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 28))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { + result[0] += 0.0031359557; + } else { + result[0] += -0.0016080218; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { + result[0] += 0.036603495; + } else { + result[0] += 0.015373263; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += -0.055269193; + } else { + result[0] += 0.019025035; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + result[0] += -0.022278577; + } else { + result[0] += 0.016397122; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.01897105; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { + result[0] += 0.00051737565; + } else { + result[0] += 0.0041289385; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += -0.036939483; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { + result[0] += 0.009198739; + } else { + result[0] += -0.013264345; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { + result[0] += 0.022775693; + } else { + result[0] += 0.007945242; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 156))) { + result[0] += -0.0018210205; + } else { + result[0] += 0.0037362184; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 128))) { + result[0] += -0.012762427; + } else { + result[0] += -0.0007420523; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + result[0] += 0.017958501; + } else { + result[0] += 0.0061608446; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { + result[0] += -0.0049462705; + } else { + result[0] += 0.0019659882; + } + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { + result[0] += 0.00022622973; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 250))) { + result[0] += 0.005413284; + } else { + result[0] += 0.030752031; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { + result[0] += 0.003555048; + } else { + result[0] += -0.0069270222; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { + result[0] += -0.008863601; + } else { + result[0] += -0.015783666; + } + } else { + result[0] += -0.0067091286; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 36))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { + result[0] += 0.062294442; + } else { + result[0] += 0.01808921; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { + result[0] += 0.08561305; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 190))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + result[0] += 0.0024828026; + } else { + result[0] += 0.018881498; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 204))) { + result[0] += -0.007572981; + } else { + result[0] += 0.001793948; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 126))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { + result[0] += -0.000932767; + } else { + result[0] += 0.006823505; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + result[0] += -0.036651295; + } else { + result[0] += -0.0058056363; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { + result[0] += -0.0055914307; + } else { + result[0] += 0.03341748; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { + result[0] += -0.00073259696; + } else { + result[0] += 0.010309592; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + result[0] += -0.019160004; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { + result[0] += 0.0046965308; + } else { + result[0] += 0.026486978; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 80))) { + result[0] += -0.008773739; + } else { + result[0] += -0.0007050743; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { + result[0] += 0.025857493; + } else { + result[0] += -0.0017104832; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { + result[0] += -0.04572242; + } else { + result[0] += -0.013417631; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 128))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + result[0] += 0.02735805; + } else { + result[0] += 0.008384274; + } + } else { + result[0] += 0.025471484; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 146))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 144))) { + result[0] += -0.0019103719; + } else { + result[0] += -0.015735812; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + result[0] += 0.003526498; + } else { + result[0] += 0.0004870697; + } + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 252))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 230))) { + result[0] += -0.0010278291; + } else { + result[0] += 0.0035127073; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + result[0] += 0.0007956819; + } else { + result[0] += -0.007078775; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { + result[0] += 0.06692124; + } else { + result[0] += 0.012191664; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { + result[0] += -0.0037893832; + } else { + result[0] += 0.0049554044; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.0026184532; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + result[0] += -0.03463073; + } else { + result[0] += -0.01464231; + } + } + } else { + result[0] += 0.01621621; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { + result[0] += -0.0019204362; + } else { + result[0] += 0.012864575; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += -0.008148334; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { + result[0] += 0.04631585; + } else { + result[0] += 0.008748725; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { + result[0] += -0.0012717935; + } else { + result[0] += 0.0031659238; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { + result[0] += 0.023349551; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + result[0] += 0.0070865126; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 220))) { + result[0] += -0.0010378936; + } else { + result[0] += -0.03451775; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 58))) { + result[0] += -0.0030549995; + } else { + result[0] += 0.042464044; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 196))) { + result[0] += -0.0068810317; + } else { + result[0] += -0.01983464; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + result[0] += 0.021494946; + } else { + result[0] += 0.046589624; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 246))) { + result[0] += 0.0016733175; + } else { + result[0] += -0.002286189; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { + result[0] += -0.023560746; + } else { + result[0] += 0.025176898; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + result[0] += -0.0032716715; + } else { + result[0] += -0.0107773235; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 158))) { + result[0] += 9.897955e-07; + } else { + result[0] += 0.0075915637; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 238))) { + result[0] += -0.018133821; + } else { + result[0] += -0.003638253; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { + result[0] += 0.013451889; + } else { + result[0] += 0.006885908; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 2))) { + result[0] += 0.016480822; + } else { + result[0] += 0.0028402929; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 182))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + result[0] += 0.009219227; + } else { + result[0] += -0.025390211; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += 0.025732726; + } else { + result[0] += -0.0023830726; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { + result[0] += 0.0035509118; + } else { + result[0] += 0.013229172; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.007854169; + } else { + result[0] += 0.0072692037; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.047711357; + } else { + result[0] += 0.025087982; + } + } else { + result[0] += 0.0016552185; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 36))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.0398016; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + result[0] += 0.0008522939; + } else { + result[0] += 0.022818817; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 8))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { + result[0] += -0.047148183; + } else { + result[0] += -0.014362188; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + result[0] += 0.033707537; + } else { + result[0] += -0.005045968; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 38))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 52))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + result[0] += -0.015981285; + } else { + result[0] += 0.061333533; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.011986034; + } else { + result[0] += 0.014650764; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 156))) { + result[0] += 0.03378779; + } else { + result[0] += 0.001349721; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + result[0] += -0.00046623495; + } else { + result[0] += -0.0032709301; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += 0.0026227154; + } else { + result[0] += -0.004871029; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += 0.024236446; + } else { + result[0] += 0.00875364; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 86))) { + result[0] += -0.010391722; + } else { + result[0] += -0.0003864034; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { + result[0] += 0.0091726575; + } else { + result[0] += 0.00034549323; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += -0.016140882; + } else { + result[0] += -0.0051438985; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { + result[0] += 0.0077548027; + } else { + result[0] += -0.0023856275; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 96))) { + result[0] += 0.0072452063; + } else { + result[0] += 0.021926673; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { + result[0] += 0.007803672; + } else { + result[0] += 0.07842522; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + result[0] += 0.0030498195; + } else { + result[0] += -0.030465296; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { + result[0] += 0.027338846; + } else { + result[0] += 0.018519042; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 104))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 150))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + result[0] += -0.0038131357; + } else { + result[0] += -0.014322984; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + result[0] += 0.011482879; + } else { + result[0] += -0.0018702493; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { + result[0] += 0.04208499; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 48))) { + result[0] += -0.013598467; + } else { + result[0] += 0.0022218376; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.014965734; + } else { + result[0] += 0.006420965; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + result[0] += 0.0035459124; + } else { + result[0] += -0.03692886; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.00581108; + } else { + result[0] += 0.0022818777; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += 0.02711085; + } else { + result[0] += 0.0037428073; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 232))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 230))) { + result[0] += -0.0006429512; + } else { + result[0] += -0.013558619; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + result[0] += 0.006967464; + } else { + result[0] += 0.020120773; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + result[0] += -0.0188818; + } else { + result[0] += -0.011276356; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { + result[0] += -0.0036909913; + } else { + result[0] += 0.0023398104; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 228))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 220))) { + result[0] += 0.0067386203; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += 0.009782809; + } else { + result[0] += -0.0003835666; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 242))) { + result[0] += 0.008386671; + } else { + result[0] += 0.01509656; + } + } + } + } + } + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 230))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += 0.0035304844; + } else { + result[0] += 0.00860195; + } + } else { + result[0] += 0.044866074; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += -0.0027610434; + } else { + result[0] += 0.032996777; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 30))) { + result[0] += 0.009394963; + } else { + result[0] += -0.0004232897; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { + result[0] += 0.019008147; + } else { + result[0] += 0.009637523; + } + } else { + result[0] += 0.041156612; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { + result[0] += 0.0076490478; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 182))) { + result[0] += 0.01141602; + } else { + result[0] += 0.0032564194; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + result[0] += 0.0054725604; + } else { + result[0] += -0.006845514; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + result[0] += -0; + } else { + result[0] += 0.04772381; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += -0.016645087; + } else { + result[0] += -0.032690536; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { + result[0] += 0.003037467; + } else { + result[0] += -0.009953394; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { + result[0] += 0.01011019; + } else { + result[0] += 0.017591367; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { + result[0] += 0.0020601144; + } else { + result[0] += -0.0052700583; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 172))) { + result[0] += -0.0001318003; + } else { + result[0] += -0.0058084927; + } + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 160))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 158))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { + result[0] += -0.021778304; + } else { + result[0] += -0.05481277; + } + } else { + result[0] += 0.01922178; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += -0.02884279; + } else { + result[0] += -0.05432384; + } + } else { + result[0] += -0.008790418; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += -0.0039755395; + } else { + result[0] += 0.0508219; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 128))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 28))) { + result[0] += -0.047565266; + } else { + result[0] += -0.034321543; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + result[0] += 0.011836532; + } else { + result[0] += -0.0025003483; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 58))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { + result[0] += 0.0057843057; + } else { + result[0] += 0.036940213; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + result[0] += -0.011010564; + } else { + result[0] += 0.014086082; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 226))) { + result[0] += -0.00865682; + } else { + result[0] += 0.018309247; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { + result[0] += 0.00883958; + } else { + result[0] += -0.0026359025; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 98))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { + result[0] += -0.0024397147; + } else { + result[0] += -0.023152852; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + result[0] += 0.0015076617; + } else { + result[0] += -0.009175034; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { + result[0] += 0.02444943; + } else { + result[0] += 0.0023199369; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + result[0] += -0.0028377601; + } else { + result[0] += 0.00046475232; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + result[0] += 0.00531006; + } else { + result[0] += -0.00012114655; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { + result[0] += -0.004693984; + } else { + result[0] += -0.032999605; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + result[0] += 0.029490167; + } else { + result[0] += 0.007921627; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 58))) { + result[0] += -0.0028598853; + } else { + result[0] += 0.0077966005; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 66))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 70))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + result[0] += 0.014692311; + } else { + result[0] += -0.021459384; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { + result[0] += -0.016421737; + } else { + result[0] += -0.0066403346; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += 0.0074493513; + } else { + result[0] += -0.0040894877; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + result[0] += 0.01759093; + } else { + result[0] += -0.00061179395; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += 0.0076502063; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { + result[0] += 0.00010772177; + } else { + result[0] += -0.003537047; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { + result[0] += -0.012459938; + } else { + result[0] += -0.0037218079; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 260))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + result[0] += 0.002977729; + } else { + result[0] += -0.019293662; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + result[0] += 0.015566354; + } else { + result[0] += 0.0037241564; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.03374357; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += 0.0041862135; + } else { + result[0] += 0.009111217; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { + result[0] += 0.001820308; + } else { + result[0] += 0.0113331815; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + result[0] += -0.01062336; + } else { + result[0] += 0.0023687228; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + result[0] += 0.0050820797; + } else { + result[0] += -0.010689573; + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.039545674; + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += 0.023330238; + } else { + result[0] += 0.0062547647; + } + } + } else { + result[0] += 0.00083993434; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { + result[0] += -0.0019025843; + } else { + result[0] += 0.0075600627; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + result[0] += 0.042078517; + } else { + result[0] += 0.0026668767; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.015797917; + } else { + result[0] += -0.006350769; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 96))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { + result[0] += 0.048628982; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { + result[0] += 0.021388667; + } else { + result[0] += 0.0020165527; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 110))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { + result[0] += -0.0051917243; + } else { + result[0] += 0.010212748; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += 0.0015873375; + } else { + result[0] += -0.0008199594; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 64))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { + result[0] += 0.01901365; + } else { + result[0] += -0.0028177812; + } + } else { + result[0] += 0.02409505; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 206))) { + result[0] += -0.0063365176; + } else { + result[0] += 0.00086170074; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + result[0] += -0.016780075; + } else { + result[0] += 0.012628958; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + result[0] += -0.009542297; + } else { + result[0] += 0.053097267; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 250))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 238))) { + result[0] += 0.003721561; + } else { + result[0] += 0.019598227; + } + } else { + result[0] += 0.03498596; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { + result[0] += 0.00025514522; + } else { + result[0] += 0.0040203123; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { + result[0] += -0.0074286456; + } else { + result[0] += 0.00093450863; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 166))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + result[0] += -0.00087522157; + } else { + result[0] += 0.007016174; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { + result[0] += -0.034118887; + } else { + result[0] += -0.011294309; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 260))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 256))) { + result[0] += 0.022198912; + } else { + result[0] += 0.012400496; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { + result[0] += -0.010423508; + } else { + result[0] += 0.004261658; + } + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 96))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 52))) { + result[0] += -0.001626932; + } else { + result[0] += 0.0061591933; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + result[0] += -0.0022012205; + } else { + result[0] += -0.0123000285; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 144))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { + result[0] += 0.007472606; + } else { + result[0] += 0.05678388; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 196))) { + result[0] += 3.3709428e-05; + } else { + result[0] += -0.0143476175; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { + result[0] += 0.03503921; + } else { + result[0] += -0.036016237; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { + result[0] += -0.0048608086; + } else { + result[0] += 0.022741511; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.035393443; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 88))) { + result[0] += -0.002815163; + } else { + result[0] += -2.7660133e-05; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 174))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 158))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 148))) { + result[0] += 0.0017332671; + } else { + result[0] += 0.026441107; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 222))) { + result[0] += -0.0073182597; + } else { + result[0] += 0.0006405428; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { + result[0] += 0.02126172; + } else { + result[0] += 0.011408723; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 68))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + result[0] += -0.014773398; + } else { + result[0] += 0.009008159; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += 0.0047272076; + } else { + result[0] += -0.01409996; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 160))) { + result[0] += 0.009959228; + } else { + result[0] += 0.04201186; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { + result[0] += -0.008849121; + } else { + result[0] += -0.00033711825; + } + } + } + } + } + } + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + result[0] += 0.002547614; + } else { + result[0] += -0.016982568; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + result[0] += 0.0087383045; + } else { + result[0] += -0.0019732136; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + result[0] += -0.027964113; + } else { + result[0] += -0.0070363963; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { + result[0] += 0.015301444; + } else { + result[0] += -0.0009437213; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 218))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 22))) { + result[0] += 0.011154254; + } else { + result[0] += 0.03460535; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { + result[0] += 0.006955503; + } else { + result[0] += 0.002656106; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 30))) { + result[0] += 0.008213889; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 234))) { + result[0] += -0.0014978464; + } else { + result[0] += 0.0018082943; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 166))) { + result[0] += -0.010998022; + } else { + result[0] += -0.028136352; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 28))) { + result[0] += 0.056712236; + } else { + result[0] += 0.00025265655; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += -0.032531813; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.012461627; + } else { + result[0] += -0.0026103533; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 62))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + result[0] += 0.003723057; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 44))) { + result[0] += 0.050172295; + } else { + result[0] += 0.03318845; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + result[0] += 0.038616996; + } else { + result[0] += -0.010049296; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { + result[0] += -0.0062160105; + } else { + result[0] += -4.625852e-05; + } + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 228))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 220))) { + result[0] += 0.027368566; + } else { + result[0] += -0.0007781659; + } + } else { + result[0] += -0.010964031; + } + } else { + result[0] += 0.0031334688; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 286))) { + result[0] += 0.023848334; + } else { + result[0] += 0.008595723; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + result[0] += 0.0113761; + } else { + result[0] += 0.0038027503; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + result[0] += 0.028975934; + } else { + result[0] += 0.01324735; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + result[0] += -0; + } else { + result[0] += 0.011437596; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { + result[0] += 0.01134281; + } else { + result[0] += 0.0018215694; + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 242))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 230))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 114))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { + result[0] += 0.0014854757; + } else { + result[0] += -0.0047508436; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { + result[0] += -0.012526001; + } else { + result[0] += -0.0042038257; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + result[0] += 0.027467722; + } else { + result[0] += 0.005847614; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + result[0] += -0.016319519; + } else { + result[0] += 0.0018143759; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { + result[0] += 0.0017114735; + } else { + result[0] += -0.04394586; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { + result[0] += 0.014061573; + } else { + result[0] += 0.0076026404; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { + result[0] += -0.0018655244; + } else { + result[0] += 0.011241974; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + result[0] += -0.013273408; + } else { + result[0] += 0.015772188; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 90))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { + result[0] += -0.00058818224; + } else { + result[0] += 0.008676103; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + result[0] += -0.010151858; + } else { + result[0] += 0.001280522; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + result[0] += -0.010351776; + } else { + result[0] += 0.012125977; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { + result[0] += -0.005976769; + } else { + result[0] += 0.0058461004; + } + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 22))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += 0.0014196447; + } else { + result[0] += -0.016857633; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 18))) { + result[0] += 0.0388976; + } else { + result[0] += 0.0022651523; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + result[0] += -0.0031505611; + } else { + result[0] += -0.02737777; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + result[0] += 0.027305475; + } else { + result[0] += -0.00032103845; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + result[0] += -0.033979844; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 44))) { + result[0] += -0.00036602697; + } else { + result[0] += -0.009034148; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { + result[0] += 0.02605836; + } else { + result[0] += 0.0035190487; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + result[0] += 0.0030745373; + } else { + result[0] += 0.021341039; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 112))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + result[0] += -0.00765642; + } else { + result[0] += 0.012637125; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 92))) { + result[0] += -0.031098261; + } else { + result[0] += -0.018325815; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 48))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 74))) { + result[0] += 0.007115057; + } else { + result[0] += 0.025569752; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 50))) { + result[0] += -0.030833412; + } else { + result[0] += -0.0027387291; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 118))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += -0.010184347; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { + result[0] += 0.009563519; + } else { + result[0] += 0.0019127353; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { + result[0] += -0.0016026145; + } else { + result[0] += -0.012827705; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { + result[0] += 0.006836459; + } else { + result[0] += -0.00047072658; + } + } + } + } + } + } + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { + result[0] += 0.016250825; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { + result[0] += -0.009732073; + } else { + result[0] += 0.017143272; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += -0.009671597; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 270))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 134))) { + result[0] += -0.00035089705; + } else { + result[0] += 0.004113893; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + result[0] += 0.01820572; + } else { + result[0] += 0.009231458; + } + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += 0.00027064016; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.0017144865; + } else { + result[0] += -0.0095933005; + } + } else { + result[0] += -0.012053235; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 194))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 250))) { + result[0] += -0.0034071447; + } else { + result[0] += 0.046097945; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 132))) { + result[0] += -0.0055225524; + } else { + result[0] += -0.0019266952; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 250))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 86))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + result[0] += 0.0048346436; + } else { + result[0] += 0.01435117; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { + result[0] += -0.019338546; + } else { + result[0] += -0.0010408615; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 178))) { + result[0] += 0.011773284; + } else { + result[0] += 0.002931762; + } + } else { + result[0] += 0.040974855; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + result[0] += 0.08329225; + } else { + result[0] += -0.0002780066; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { + result[0] += -0.0042001484; + } else { + result[0] += -0.02084507; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += -0.00010066136; + } else { + result[0] += 0.0027292925; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { + result[0] += -0.0122973155; + } else { + result[0] += 0.035760295; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += -0.001996606; + } else { + result[0] += 0.021067668; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 118))) { + result[0] += -0.013935153; + } else { + result[0] += 0.031183664; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += 0.061950255; + } else { + result[0] += 0.0058407323; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 142))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { + result[0] += 0.0829713; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.03793059; + } else { + result[0] += -0.00907617; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { + result[0] += 0.0025514578; + } else { + result[0] += -0.0012025639; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 16))) { + result[0] += 0.046701174; + } else { + result[0] += 0.0046325247; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { + result[0] += 0.027825123; + } else { + result[0] += -0; + } + } else { + result[0] += -0.021053893; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 82))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 152))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { + result[0] += -0.0013631139; + } else { + result[0] += -0.012569079; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + result[0] += -0.0008349316; + } else { + result[0] += 0.022382598; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 156))) { + result[0] += 0.013735485; + } else { + result[0] += 0.0033138485; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + result[0] += -0.0037839809; + } else { + result[0] += 0.002062925; + } + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += 0.0013772444; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { + result[0] += -0.011463809; + } else { + result[0] += -0.0047910134; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { + result[0] += 0.060537297; + } else { + result[0] += 0.010533939; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.016672423; + } else { + result[0] += -0.00052434875; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 250))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + result[0] += 0.0070519983; + } else { + result[0] += 0.016810419; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { + result[0] += -0.026641011; + } else { + result[0] += 0.00010854311; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { + result[0] += 0.03818845; + } else { + result[0] += 0.012029546; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { + result[0] += -0.007278714; + } else { + result[0] += -0.03333238; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 46))) { + result[0] += 0.007880951; + } else { + result[0] += -0.00031094736; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + result[0] += 0.013625564; + } else { + result[0] += 0.03885763; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 214))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + result[0] += -0.0029387611; + } else { + result[0] += 7.662349e-05; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += 0.029973779; + } else { + result[0] += 0.0043506036; + } + } + } + } + } + } + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 78))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + result[0] += -0.017686712; + } else { + result[0] += 0.015614047; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 242))) { + result[0] += 0.0010816348; + } else { + result[0] += 0.010603439; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += -0.013882413; + } else { + result[0] += -3.773386e-06; + } + } else { + result[0] += -0.029591149; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 52))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + result[0] += 0.0029416822; + } else { + result[0] += 0.060454708; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { + result[0] += -0.03178696; + } else { + result[0] += -0.011132187; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 164))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + result[0] += 0.06373953; + } else { + result[0] += -0.0049581574; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { + result[0] += -0.011388632; + } else { + result[0] += 0.0049218778; + } + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 14))) { + result[0] += 0.0012467568; + } else { + result[0] += -0.019324591; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { + result[0] += 0.001289832; + } else { + result[0] += -0.00096984406; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { + result[0] += -0.011180186; + } else { + result[0] += -0.0001193571; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { + result[0] += 0.017248346; + } else { + result[0] += 0.003171424; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 18))) { + result[0] += 0.04394267; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 176))) { + result[0] += -0.026358157; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.0084524695; + } else { + result[0] += -0.008720714; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 286))) { + result[0] += 0.02462693; + } else { + result[0] += 0.0102696465; + } + } + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + result[0] += -9.075111e-05; + } else { + result[0] += -0.0032513805; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { + result[0] += 0.0011170026; + } else { + result[0] += 0.023266837; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 204))) { + result[0] += -0.012011125; + } else { + result[0] += -0.0051451973; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 202))) { + result[0] += 0.023120182; + } else { + result[0] += -0.008411562; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 212))) { + result[0] += 0.017263098; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 230))) { + result[0] += 0.003063401; + } else { + result[0] += 0.021211077; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 234))) { + result[0] += -0.0117486585; + } else { + result[0] += -0.0008188842; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 248))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 40))) { + result[0] += -0.025011426; + } else { + result[0] += -0.01188937; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { + result[0] += 0.000581751; + } else { + result[0] += 0.035827655; + } + } + } else { + result[0] += -0.056811877; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { + result[0] += 0.03448122; + } else { + result[0] += 0.0075843707; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 120))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 150))) { + result[0] += 0.02317158; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { + result[0] += -0.011207158; + } else { + result[0] += 0.00603795; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + result[0] += 1.6565642e-05; + } else { + result[0] += 0.0018936057; + } + } else { + result[0] += -0.0073121726; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += 0.013665982; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + result[0] += 0.02345163; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 90))) { + result[0] += 0.031935133; + } else { + result[0] += 0.05211563; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 108))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 150))) { + result[0] += 0.006929166; + } else { + result[0] += 0.0018365175; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { + result[0] += -0.028940136; + } else { + result[0] += -0.0025783076; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.010843903; + } else { + result[0] += 0.006668359; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { + result[0] += -0.022552337; + } else { + result[0] += -0.0126125915; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 188))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { + result[0] += -0.0033015904; + } else { + result[0] += 0.0023345288; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 172))) { + result[0] += 0.027362315; + } else { + result[0] += 0.008473503; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 198))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += -0.0086487485; + } else { + result[0] += 0.0011204499; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + result[0] += -0.0040217172; + } else { + result[0] += 0.0013360606; + } + } + } + } + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { + result[0] += -0.0038944704; + } else { + result[0] += -0.024639545; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + result[0] += 0.010567961; + } else { + result[0] += -0.0008910143; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 222))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 208))) { + result[0] += 0.0022678128; + } else { + result[0] += 0.010532487; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { + result[0] += 0.0032915582; + } else { + result[0] += -0.0005411969; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + result[0] += -0.01407851; + } else { + result[0] += 0.026896363; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { + result[0] += -0.007195405; + } else { + result[0] += 0.004968326; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 174))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { + result[0] += 0.008754817; + } else { + result[0] += -0.0017400451; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { + result[0] += -0.00865253; + } else { + result[0] += 0.0009031243; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 244))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { + result[0] += 0.00038980006; + } else { + result[0] += -0.016266255; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 18))) { + result[0] += -0.011384182; + } else { + result[0] += -0.005449654; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + result[0] += 0.029928738; + } else { + result[0] += -0.005479242; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { + result[0] += -0.02587893; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + result[0] += -0.005359716; + } else { + result[0] += 0.06967212; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 236))) { + result[0] += 0.0018839792; + } else { + result[0] += 0.00916981; + } + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 104))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.0006954739; + } else { + result[0] += 0.048395343; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { + result[0] += 1.917278e-05; + } else { + result[0] += 0.03755577; + } + } + } else { + result[0] += -0.011912975; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 118))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { + result[0] += 0.00019196502; + } else { + result[0] += 0.014448823; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + result[0] += 0.019970976; + } else { + result[0] += -0.0011340084; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { + result[0] += -0.009018285; + } else { + result[0] += 0.020422988; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + result[0] += 0.02001476; + } else { + result[0] += 0.034766663; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 152))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + result[0] += -0.0060089673; + } else { + result[0] += -0.026388485; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 102))) { + result[0] += -0.0338698; + } else { + result[0] += -0.0058778613; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { + result[0] += 0.025475418; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + result[0] += 0.0019010755; + } else { + result[0] += 0.011219644; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + result[0] += -0.021041693; + } else { + result[0] += 0.008361343; + } + } else { + result[0] += 0.03766183; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + result[0] += -0.010491718; + } else { + result[0] += 0.003890305; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.011144113; + } else { + result[0] += -8.4675696e-05; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { + result[0] += 0.001330308; + } else { + result[0] += 0.02066918; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 130))) { + result[0] += 6.2536885e-05; + } else { + result[0] += -0.013168806; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { + result[0] += 0.019082962; + } else { + result[0] += 0.0053747944; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + result[0] += -0.0076252148; + } else { + result[0] += 0.0009393127; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.011447157; + } else { + result[0] += 0.0061047096; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { + result[0] += -0; + } else { + result[0] += -0.020285398; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += 0.01896581; + } else { + result[0] += 0.00095191033; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 172))) { + result[0] += 0.0154045075; + } else { + result[0] += -0.001370645; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += 0.006603177; + } else { + result[0] += 0.00034456272; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + result[0] += -0.0029918782; + } else { + result[0] += -0.012741557; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 164))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + result[0] += 0.0071618394; + } else { + result[0] += -0.012161553; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + result[0] += 0.03479741; + } else { + result[0] += 0.00893333; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 178))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 228))) { + result[0] += 0.0005804447; + } else { + result[0] += -0.006457042; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 186))) { + result[0] += 0.016656972; + } else { + result[0] += 0.002414147; + } + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 124))) { + result[0] += 0.022543216; + } else { + result[0] += 0.0024742563; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 50))) { + result[0] += -0.025850767; + } else { + result[0] += -0.0073662284; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { + result[0] += 0.0006718464; + } else { + result[0] += -0.0011177754; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { + result[0] += 0.003656535; + } else { + result[0] += -0.0034576224; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 122))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { + result[0] += 0.01316909; + } else { + result[0] += -0.00022220907; + } + } else { + result[0] += 0.0322718; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 132))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += 0.0029699726; + } else { + result[0] += -0.009400754; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + result[0] += 0.0157076; + } else { + result[0] += -0.0039490475; + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 136))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { + result[0] += -0.030938972; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { + result[0] += 0.027307421; + } else { + result[0] += -0.00040336605; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + result[0] += 0.029921725; + } else { + result[0] += 0.013266409; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 168))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 62))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 144))) { + result[0] += 0.044555373; + } else { + result[0] += 0.0020996393; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 90))) { + result[0] += -0.0010202875; + } else { + result[0] += -0.0097485995; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 170))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + result[0] += 0.024518076; + } else { + result[0] += -0.0032617298; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += -0.002727569; + } else { + result[0] += -0.00034776315; + } + } + } + } + } + } + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 188))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + result[0] += -0.00013192062; + } else { + result[0] += -0.007137733; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + result[0] += 0.0058402964; + } else { + result[0] += 0.00019643598; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 170))) { + result[0] += -0.015256891; + } else { + result[0] += 0.027069787; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { + result[0] += 0.016238226; + } else { + result[0] += 0.045753922; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { + result[0] += -0.019134747; + } else { + result[0] += 0.010413033; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { + result[0] += -0.0016630454; + } else { + result[0] += -0.024183424; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + result[0] += 0.012589683; + } else { + result[0] += 0.0009457501; + } + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 210))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 208))) { + result[0] += -0.0066352724; + } else { + result[0] += -0.017913656; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.0011060528; + } else { + result[0] += 0.0039828527; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 200))) { + result[0] += -0.0031051391; + } else { + result[0] += -0.016724786; + } + } else { + result[0] += -0.024875712; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 162))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 116))) { + result[0] += -0.016162679; + } else { + result[0] += 0.0026748257; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 160))) { + result[0] += 0.01718258; + } else { + result[0] += 0.0035327424; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 114))) { + result[0] += 0.0027622434; + } else { + result[0] += -0.019732043; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { + result[0] += -0.009568975; + } else { + result[0] += -0.0024784; + } + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 212))) { + result[0] += 0.01407246; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + result[0] += -0.04716774; + } else { + result[0] += -0.008298879; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { + result[0] += -0.012182956; + } else { + result[0] += -0.0051582577; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 128))) { + result[0] += -0.0067259143; + } else { + result[0] += 0.013526896; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { + result[0] += 0.002809637; + } else { + result[0] += -0.0016421535; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 82))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + result[0] += -0.0011062829; + } else { + result[0] += 0.014348181; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { + result[0] += 0.0076159174; + } else { + result[0] += -0.0151206255; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 124))) { + result[0] += 0.0045484654; + } else { + result[0] += 0.015772928; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + result[0] += -0.0059127775; + } else { + result[0] += 0.0025872302; + } + } + } + } + } + } + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 206))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += 0.115350716; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.030523414; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { + result[0] += 0.00818327; + } else { + result[0] += 0.042636707; + } + } else { + result[0] += 0.002394322; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += -0; + } else { + result[0] += 0.022605298; + } + } else { + result[0] += -0.018712113; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + result[0] += -0.018390236; + } else { + result[0] += 0.038224217; + } + } else { + result[0] += 0.0023876673; + } + } + } else { + result[0] += -0.0001913241; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 182))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + result[0] += 0.002910438; + } else { + result[0] += -0.036367122; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += 0.0029667527; + } else { + result[0] += 0.04206534; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.012119245; + } else { + result[0] += -0.023951335; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 178))) { + result[0] += -0.034575075; + } else { + result[0] += 0.007937021; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { + result[0] += -0.0042928117; + } else { + result[0] += -0.0012923562; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 184))) { + result[0] += -0.028960615; + } else { + result[0] += -0.016311336; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += -0.053593513; + } else { + result[0] += 0.009981692; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 78))) { + result[0] += -0.017819297; + } else { + result[0] += -0.00070253096; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 44))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 32))) { + result[0] += 0.004783728; + } else { + result[0] += 0.016091399; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { + result[0] += 0.029074514; + } else { + result[0] += 0.002160437; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 250))) { + result[0] += 0.055964243; + } else { + result[0] += 0.01622332; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 250))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { + result[0] += -0.008321747; + } else { + result[0] += -0.0040490143; + } + } else { + result[0] += -0.018693618; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 250))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + result[0] += 6.4203756e-05; + } else { + result[0] += -0.0021725388; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { + result[0] += 0.0055606547; + } else { + result[0] += -0.0023256661; + } + } + } + } + } + } + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 182))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + result[0] += -6.4768305e-06; + } else { + result[0] += -0.013116406; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += 0.019466847; + } else { + result[0] += 0.006013643; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + result[0] += -0.026702961; + } else { + result[0] += 0.0020189236; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { + result[0] += -0.0006026015; + } else { + result[0] += 0.01105925; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + result[0] += 0.029843587; + } else { + result[0] += 0.0661096; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 100))) { + result[0] += 0.00522026; + } else { + result[0] += -0.0006855772; + } + } else { + result[0] += 0.013178887; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 124))) { + result[0] += -0.020214727; + } else { + result[0] += 0.0038588562; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 200))) { + result[0] += -0.00505647; + } else { + result[0] += -0.026435608; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { + result[0] += -0.0028923058; + } else { + result[0] += -0.008900782; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { + result[0] += 0.050368883; + } else { + result[0] += 0.0024831446; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { + result[0] += 0.011402026; + } else { + result[0] += -0.031253975; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { + result[0] += -0.012896495; + } else { + result[0] += -0.0015336399; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 68))) { + result[0] += 0.011413305; + } else { + result[0] += 0.02147497; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 212))) { + result[0] += 0.013494618; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 46))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 260))) { + result[0] += 0.0066242903; + } else { + result[0] += 0.0001638933; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + result[0] += -0.009525633; + } else { + result[0] += 0.0022819052; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 176))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 70))) { + result[0] += 0.0022807613; + } else { + result[0] += -0.0062286295; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + result[0] += -0.01628782; + } else { + result[0] += -0.004168268; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 104))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { + result[0] += 0.011599141; + } else { + result[0] += 0.0024420898; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 162))) { + result[0] += 0.013803795; + } else { + result[0] += -0.0013663528; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 146))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { + result[0] += -0.005095913; + } else { + result[0] += 0.00041055848; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 78))) { + result[0] += -0.004392452; + } else { + result[0] += 0.0040476876; + } + } + } + } + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { + result[0] += -0.00044016525; + } else { + result[0] += 0.0027606068; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 180))) { + result[0] += -0.0027404772; + } else { + result[0] += -0.012484104; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += 0.1066999; + } else { + result[0] += 0.005611764; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { + result[0] += -0.0044804937; + } else { + result[0] += -0.00094216457; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 20))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += -0.016515603; + } else { + result[0] += -0.0019510689; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += 0.04879087; + } else { + result[0] += 0.019489786; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 174))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + result[0] += 0.028882284; + } else { + result[0] += 0.0029704724; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { + result[0] += -0.008776897; + } else { + result[0] += 0.00076872477; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 22))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 246))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += -0.00024617615; + } else { + result[0] += -0.013363178; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.012836638; + } else { + result[0] += -0.00578899; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { + result[0] += 0.02552943; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 74))) { + result[0] += 0.014522381; + } else { + result[0] += -0.0055740518; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { + result[0] += -0.024675723; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + result[0] += 0.036256976; + } else { + result[0] += -0.010159162; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 186))) { + result[0] += -0.0034239579; + } else { + result[0] += 0.0019339178; + } + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { + result[0] += 0.0021977962; + } else { + result[0] += -0.0026835771; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { + result[0] += 0.014414914; + } else { + result[0] += 0.010211523; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + result[0] += -0.008403643; + } else { + result[0] += -0.024761902; + } + } else { + result[0] += 0.0032713003; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + result[0] += -0.02039499; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.009458961; + } else { + result[0] += -0.0018666508; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + result[0] += 0.0327911; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.036110453; + } else { + result[0] += 0.0033109114; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 48))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 48))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 104))) { + result[0] += 0.010583217; + } else { + result[0] += -0.01392099; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + result[0] += 0.032042317; + } else { + result[0] += 0.011768063; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { + result[0] += -0.04997726; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + result[0] += -0.008824881; + } else { + result[0] += 0.0048329984; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + result[0] += 0.00096224155; + } else { + result[0] += -0.009902506; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 146))) { + result[0] += 0.02890099; + } else { + result[0] += 5.074874e-05; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += -0.0082296785; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 96))) { + result[0] += 0.0020531386; + } else { + result[0] += -0.001512293; + } + } + } + } + } + } + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { + result[0] += -0.009371125; + } else { + result[0] += 0.016960299; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += -0.008832243; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 254))) { + result[0] += 0.008071503; + } else { + result[0] += 0.013252865; + } + } else { + result[0] += -0.0039770114; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { + result[0] += -0.0074452083; + } else { + result[0] += 0.0010280218; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + result[0] += 0.020828962; + } else { + result[0] += 0.006464574; + } + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 244))) { + result[0] += 0.044012897; + } else { + result[0] += -0.01173665; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + result[0] += 0.013455394; + } else { + result[0] += -0.014296198; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + result[0] += 0.00890102; + } else { + result[0] += -0.00038118512; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { + result[0] += 0.0076601445; + } else { + result[0] += 0.037503462; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { + result[0] += 0.00028127801; + } else { + result[0] += -0.0077372934; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { + result[0] += -0.0058348128; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + result[0] += -0.0035285298; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += 0.0027004466; + } else { + result[0] += 0.009092315; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { + result[0] += 0.009767297; + } else { + result[0] += -0.008808373; + } + } else { + result[0] += 0.009121577; + } + } else { + result[0] += -0.0003377227; + } + } else { + result[0] += -0.0081705395; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 110))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + result[0] += 0.012397877; + } else { + result[0] += 0.003961118; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { + result[0] += -0.003340742; + } else { + result[0] += 0.001318198; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { + result[0] += 0.04594936; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + result[0] += -0.012992064; + } else { + result[0] += 0.013072202; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 146))) { + result[0] += -0.016053863; + } else { + result[0] += -0.0013210791; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 120))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { + result[0] += -0.0011712526; + } else { + result[0] += 0.010021153; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 124))) { + result[0] += -0.007040714; + } else { + result[0] += 0.0005415445; + } + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + result[0] += 0.0068953778; + } else { + result[0] += -0.0090838885; + } + } else { + result[0] += 0.024001935; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + result[0] += -0.009051267; + } else { + result[0] += -0.02782002; + } + } else { + result[0] += 0.007848546; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 8))) { + result[0] += 0.0028921093; + } else { + result[0] += 0.023708088; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + result[0] += 0.0013097594; + } else { + result[0] += -0.001009873; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + result[0] += 0.017365938; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += -0.026752142; + } else { + result[0] += -0.00315145; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + result[0] += -0.0013150068; + } else { + result[0] += 0.05037552; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + result[0] += -0.02626193; + } else { + result[0] += 0.030513177; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 8))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.0053047254; + } else { + result[0] += -0.027583335; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 54))) { + result[0] += 0.043980025; + } else { + result[0] += -0.006328375; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { + result[0] += 0.0048042852; + } else { + result[0] += 0.042036824; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.012095607; + } else { + result[0] += -0.00045656387; + } + } + } + } + } + } + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 112))) { + result[0] += -0.0005225084; + } else { + result[0] += 0.0013189358; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + result[0] += -0.01325873; + } else { + result[0] += 0.008976769; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { + result[0] += -0.004084352; + } else { + result[0] += 0.0038890864; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 204))) { + result[0] += -0.010790448; + } else { + result[0] += -0.0041690473; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 214))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += 0.00092866377; + } else { + result[0] += -0.021240106; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { + result[0] += 0.016957764; + } else { + result[0] += 0.009066544; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 56))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 120))) { + result[0] += -0.008545898; + } else { + result[0] += -0.0013856884; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 216))) { + result[0] += -0.0058118394; + } else { + result[0] += 0.0030270133; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.004897514; + } else { + result[0] += 0.012878601; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 136))) { + result[0] += -0.009626532; + } else { + result[0] += -0.005689178; + } + } + } else { + result[0] += -0.04376392; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { + result[0] += 0.03378177; + } else { + result[0] += 0.01186064; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 120))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { + result[0] += -0.0032555745; + } else { + result[0] += 0.0158323; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { + result[0] += 0.00046772003; + } else { + result[0] += 0.0077147195; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + result[0] += 0.0011735325; + } else { + result[0] += -0.006349434; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 146))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.023203839; + } else { + result[0] += 0.00827275; + } + } else { + result[0] += 0.039015923; + } + } else { + result[0] += 0.016087564; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 156))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 206))) { + result[0] += -0.0005247008; + } else { + result[0] += -0.0068671047; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + result[0] += -0.00041580052; + } else { + result[0] += 0.005611544; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + result[0] += -0.015564476; + } else { + result[0] += -0.005183963; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 172))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 218))) { + result[0] += 0.023443257; + } else { + result[0] += -0.002142666; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { + result[0] += 0.05721947; + } else { + result[0] += 0.000846235; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 8))) { + result[0] += -0.010377513; + } else { + result[0] += 0.002864465; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { + result[0] += -0.041931864; + } else { + result[0] += -0.01965585; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { + result[0] += -0.0147984475; + } else { + result[0] += 0.031754512; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { + result[0] += 0.0027872298; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { + result[0] += -0.0018927314; + } else { + result[0] += 0.00035012572; + } + } + } else { + result[0] += 0.011900772; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += 0.02755776; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { + result[0] += -0.03298561; + } else { + result[0] += -0.008792006; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 260))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { + result[0] += -0.00033489856; + } else { + result[0] += 0.006294869; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 216))) { + result[0] += -0.00025057443; + } else { + result[0] += 0.005475367; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 22))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 194))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += 0.034542117; + } else { + result[0] += 0.0051070247; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { + result[0] += -0.011878603; + } else { + result[0] += 0.015853336; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 12))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 204))) { + result[0] += -0.017843468; + } else { + result[0] += 0.0025024624; + } + } else { + result[0] += -0.038026553; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { + result[0] += 0.0004767517; + } else { + result[0] += -0.005413917; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { + result[0] += -0.00014004558; + } else { + result[0] += 0.0038309437; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { + result[0] += -0.025853286; + } else { + result[0] += 0.016463885; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { + result[0] += 0.008300542; + } else { + result[0] += -0.0022710604; + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 26))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { + result[0] += 0.06824603; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + result[0] += 0.0344267; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 24))) { + result[0] += -0.02413011; + } else { + result[0] += 0.009757657; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 240))) { + result[0] += 0.04187591; + } else { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += 0.021772258; + } else { + result[0] += 0.00023637361; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + result[0] += -0.00018605802; + } else { + result[0] += 0.00892628; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + result[0] += -0.0029304621; + } else { + result[0] += 0.005322406; + } + } + } + } + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { + result[0] += 0.058786936; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { + result[0] += -0.001987611; + } else { + result[0] += 0.0012095956; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += -0.013887591; + } else { + result[0] += -0.005378675; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + result[0] += 0.004469014; + } else { + result[0] += -0.0019160692; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + result[0] += -0.052244443; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { + result[0] += 0.034024667; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 184))) { + result[0] += -0.028885717; + } else { + result[0] += -0.016080601; + } + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 28))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 196))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 206))) { + result[0] += 0.004683802; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + result[0] += 0.0038840864; + } else { + result[0] += -0.0010689719; + } + } + } else { + result[0] += 0.040237945; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 174))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { + result[0] += 0.009488398; + } else { + result[0] += 0.0063879974; + } + } else { + result[0] += 0.040055256; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { + result[0] += 0.0063549983; + } else { + result[0] += 0.0503232; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { + result[0] += -0.0018827593; + } else { + result[0] += 0.0051954878; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { + result[0] += 0.032296978; + } else { + result[0] += 0.0139222685; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { + result[0] += 0.04671686; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + result[0] += 0.0009886044; + } else { + result[0] += 0.006239516; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { + result[0] += 0.031537697; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { + result[0] += -0.019081848; + } else { + result[0] += -0.0026591418; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + result[0] += 0.026619418; + } else { + result[0] += 0.0041615735; + } + } else { + result[0] += 0.047726057; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + result[0] += 0.001571185; + } else { + result[0] += -0.00050108694; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 140))) { + result[0] += 0.009636164; + } else { + result[0] += 0.0010100096; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + result[0] += -0.0026403468; + } else { + result[0] += 0.0114326365; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { + result[0] += -0.021934291; + } else { + result[0] += -0.0050015096; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 238))) { + result[0] += -0.007851736; + } else { + result[0] += 0.03168834; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + result[0] += 0.033044577; + } else { + result[0] += 9.3673785e-05; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 188))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 156))) { + result[0] += 0.00030188911; + } else { + result[0] += 0.002446159; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + result[0] += -0.0055051325; + } else { + result[0] += 0.0012621379; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + result[0] += 0.014215629; + } else { + result[0] += 0.005867033; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { + result[0] += -0.006319243; + } else { + result[0] += 0.0017076422; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 166))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { + result[0] += -0.0017003525; + } else { + result[0] += 0.010260985; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 236))) { + result[0] += -0.015775895; + } else { + result[0] += -0.007966612; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + result[0] += 0.0038435606; + } else { + result[0] += 0.0005556598; + } + } else { + result[0] += 0.0073271217; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 230))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 196))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + result[0] += -0.00010884991; + } else { + result[0] += -0.0013555246; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + result[0] += 0.00045378748; + } else { + result[0] += 0.00696068; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + result[0] += 0.0034289937; + } else { + result[0] += -0.005728068; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { + result[0] += -0.009315341; + } else { + result[0] += -0.004357322; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { + result[0] += -0.024190784; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { + result[0] += -0; + } else { + result[0] += -0.019713525; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 64))) { + result[0] += -0.0078073195; + } else { + result[0] += 5.9448754e-05; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 186))) { + result[0] += -0.006281379; + } else { + result[0] += -0.010314364; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + result[0] += 0.001542045; + } else { + result[0] += -0.0030066923; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { + result[0] += 0.045451414; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 250))) { + result[0] += -0.0048594624; + } else { + result[0] += 0.002339422; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 236))) { + result[0] += 0.0038202107; + } else { + result[0] += -0.0043049804; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 222))) { + result[0] += 0.035619907; + } else { + result[0] += 0.011654694; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + result[0] += 0.011329205; + } else { + result[0] += -0.0026530891; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + result[0] += 0.00091164187; + } else { + result[0] += 0.0065599764; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 180))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + result[0] += 0.0003863835; + } else { + result[0] += 0.012511316; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { + result[0] += -0.012533942; + } else { + result[0] += 0.000940961; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + result[0] += 0.02408296; + } else { + result[0] += 0.006255468; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 188))) { + result[0] += 0.01437206; + } else { + result[0] += 0.005517607; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 156))) { + result[0] += -0.004864832; + } else { + result[0] += 0.004785055; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 206))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + result[0] += -0.008446374; + } else { + result[0] += 3.6487032e-05; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { + result[0] += -0.013399954; + } else { + result[0] += 0.0013180381; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 56))) { + result[0] += -0.01676119; + } else { + result[0] += 0.019005135; + } + } else { + result[0] += 0.026536051; + } + } else { + result[0] += -0.017303595; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 234))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { + result[0] += -0.005325236; + } else { + result[0] += 0.0026667507; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { + result[0] += 0.006688422; + } else { + result[0] += 0.0130848605; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 200))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { + result[0] += -6.232897e-05; + } else { + result[0] += -0.011703821; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { + result[0] += 0.01631069; + } else { + result[0] += 0.0024924437; + } + } + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 118))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.020456076; + } else { + result[0] += 1.7096074e-05; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += -0.0043996517; + } else { + result[0] += 0.037505116; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { + result[0] += 0.03947706; + } else { + result[0] += 0.00013893798; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { + result[0] += -0.00020466889; + } else { + result[0] += -0.0056823995; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { + result[0] += 0.0015122422; + } else { + result[0] += 0.014326178; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 98))) { + result[0] += -0.03659178; + } else { + result[0] += -0.00897372; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 234))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { + result[0] += -0.0012049631; + } else { + result[0] += -0.010048345; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + result[0] += 0.023757316; + } else { + result[0] += -0.00096084067; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + result[0] += 0.007543098; + } else { + result[0] += 0.05037853; + } + } else { + result[0] += -0.020506142; + } + } else { + result[0] += 0.041784402; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 168))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + result[0] += 0.022379946; + } else { + result[0] += 0.001123256; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 176))) { + result[0] += -0.0040573548; + } else { + result[0] += -0.00018302095; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { + result[0] += 0.026662258; + } else { + result[0] += 0.001635097; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { + result[0] += 0.0157876; + } else { + result[0] += 0.00078219484; + } + } + } + } + } + } + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 168))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { + result[0] += -0.003070421; + } else { + result[0] += 0.003986802; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + result[0] += -0.016073924; + } else { + result[0] += -0.00033199994; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 82))) { + result[0] += -0.0026495587; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 180))) { + result[0] += 0.01088603; + } else { + result[0] += 0.0028157744; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { + result[0] += 0.035645917; + } else { + result[0] += 0.011242066; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 158))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + result[0] += -0.004172708; + } else { + result[0] += -0.013315861; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + result[0] += 0.0016847762; + } else { + result[0] += -0.019520441; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 116))) { + result[0] += -0.0012042717; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + result[0] += 0.010144642; + } else { + result[0] += 0.021312661; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { + result[0] += -0.0060045626; + } else { + result[0] += -0.019477958; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { + result[0] += -0.010557742; + } else { + result[0] += -0.019895343; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 164))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { + result[0] += 0.0023980096; + } else { + result[0] += -0.0058308984; + } + } else { + result[0] += 0.0054435167; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 170))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 144))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 52))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + result[0] += 0.01054426; + } else { + result[0] += -0.013723351; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 190))) { + result[0] += 0.0035272748; + } else { + result[0] += -0.0014124735; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 128))) { + result[0] += -0.020531978; + } else { + result[0] += -0.008468418; + } + } + } else { + result[0] += 0.027421832; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 136))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { + result[0] += -0.0014708912; + } else { + result[0] += 0.010910965; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { + result[0] += 0.02383196; + } else { + result[0] += -0.005580553; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { + result[0] += -0.015012925; + } else { + result[0] += -0.038420197; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 102))) { + result[0] += 0.027166018; + } else { + result[0] += -0.0051050577; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 158))) { + result[0] += 0.005120454; + } else { + result[0] += -0.0066694873; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + result[0] += 0.040612247; + } else { + result[0] += 0.010608687; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 206))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 186))) { + result[0] += 9.48829e-05; + } else { + result[0] += -0.0028726817; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 222))) { + result[0] += 0.0036865217; + } else { + result[0] += -0.0017621756; + } + } + } + } + } + } + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { + result[0] += 0.010326921; + } else { + result[0] += -6.580556e-05; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 14))) { + result[0] += 0.035937645; + } else { + result[0] += 0.0061092353; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += 0.09306122; + } else { + result[0] += 0.004483877; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { + result[0] += -0.0021131127; + } else { + result[0] += -0.0002122157; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 16))) { + result[0] += -0.0010388399; + } else { + result[0] += 0.04628381; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 170))) { + result[0] += -0.012791668; + } else { + result[0] += 0.012744421; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + result[0] += 0.001258762; + } else { + result[0] += -0.010475395; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 18))) { + result[0] += 0.025982514; + } else { + result[0] += 0.0025854723; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 26))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { + result[0] += -0.020725088; + } else { + result[0] += 0.014970714; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 176))) { + result[0] += -0.0017299574; + } else { + result[0] += 0.026476989; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 158))) { + result[0] += -0.004242038; + } else { + result[0] += -0.029603764; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 100))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 126))) { + result[0] += 0.0020087094; + } else { + result[0] += -0.001871386; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 44))) { + result[0] += 0.014256491; + } else { + result[0] += 0.007524089; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 94))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 206))) { + result[0] += -0.014824388; + } else { + result[0] += -0.0017853121; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + result[0] += -0.004817825; + } else { + result[0] += 0.00016148947; + } + } + } + } + } + } else { + result[0] += 0.0088046715; + } + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 168))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + result[0] += 9.3958115e-05; + } else { + result[0] += 0.0067572976; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 222))) { + result[0] += -0.0049092458; + } else { + result[0] += -0.0005594916; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 178))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 108))) { + result[0] += 0.00977688; + } else { + result[0] += 0.0015517873; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 204))) { + result[0] += -0.008679122; + } else { + result[0] += -0.002352368; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 158))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + result[0] += 0.009798234; + } else { + result[0] += 0.0032348556; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 182))) { + result[0] += 0.014364332; + } else { + result[0] += 0.025256773; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 118))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { + result[0] += -0.019178228; + } else { + result[0] += -0.0023992255; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { + result[0] += 0.026579231; + } else { + result[0] += 0.0040463707; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 172))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 170))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + result[0] += -0.012679273; + } else { + result[0] += -0.03488743; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { + result[0] += 0.012128734; + } else { + result[0] += -0.0036993285; + } + } + } else { + result[0] += -0.026699487; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 164))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 46))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { + result[0] += 0.008520793; + } else { + result[0] += -0.0012307116; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 174))) { + result[0] += -0.0020589442; + } else { + result[0] += -0.0074132094; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 258))) { + result[0] += -0.00428136; + } else { + result[0] += 0.042908087; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { + result[0] += 0.034873586; + } else { + result[0] += 0.0018836738; + } + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 190))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { + result[0] += -0.0036578246; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 208))) { + result[0] += 0.020423703; + } else { + result[0] += 0.030946225; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += -0.006327176; + } else { + result[0] += -0.015637185; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 258))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { + result[0] += 0.0020563873; + } else { + result[0] += 0.009788949; + } + } else { + result[0] += -0.0091839805; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 194))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + result[0] += 0.051798742; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 252))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += -0.006234445; + } else { + result[0] += 0.008611916; + } + } else { + result[0] += 0.0055716326; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 196))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { + result[0] += 0.003886501; + } else { + result[0] += 0.008659224; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 198))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { + result[0] += -0.03154314; + } else { + result[0] += -0.0031137222; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + result[0] += 0.00038270376; + } else { + result[0] += 0.0020157003; + } + } + } + } + } + } + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + result[0] += 0.026274556; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + result[0] += -0.014014624; + } else { + result[0] += 0.0017416707; + } + } else { + result[0] += -0.020999283; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { + result[0] += 0.019021433; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 2))) { + result[0] += 0.00134224; + } else { + result[0] += 0.0040796655; + } + } else { + result[0] += -0.0030546128; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + result[0] += -0.0019856154; + } else { + result[0] += -0.0002087965; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + result[0] += 0.013158979; + } else { + result[0] += -0.0030576333; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + result[0] += 0.003060523; + } else { + result[0] += 0.0005930412; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { + result[0] += 0.03627108; + } else { + result[0] += -0.0035451804; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + result[0] += -0.0046488056; + } else { + result[0] += 0.00013075671; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 120))) { + result[0] += 0.012213302; + } else { + result[0] += 0.04960065; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + result[0] += -0.0069404887; + } else { + result[0] += -0.01752409; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + result[0] += 0.0142224645; + } else { + result[0] += -0.0028541964; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + result[0] += -0.00048694783; + } else { + result[0] += -0.01978219; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + result[0] += 0.023798453; + } else { + result[0] += 0.0028695017; + } + } + } else { + result[0] += 0.049870964; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { + result[0] += -0.011660507; + } else { + result[0] += 0.020355195; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + result[0] += -0.010959279; + } else { + result[0] += -0.022352552; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 104))) { + result[0] += 0.0019975018; + } else { + result[0] += 0.02552997; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.005593583; + } else { + result[0] += 0.0001567727; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 36))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + result[0] += 0.0025115781; + } else { + result[0] += 0.01276709; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += 0.0009829837; + } else { + result[0] += -0.0020405638; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 176))) { + result[0] += -0.0027318567; + } else { + result[0] += 0.00036724083; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { + result[0] += 0.00952085; + } else { + result[0] += 0.00021925311; + } + } + } + } else { + result[0] += 0.0028065902; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 264))) { + result[0] += -0.010198384; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 68))) { + result[0] += 0.008980814; + } else { + result[0] += -0.00083386357; + } + } + } else { + result[0] += 0.034579825; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + result[0] += -0.0005227964; + } else { + result[0] += 0.00078901247; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 264))) { + result[0] += -0.0031115639; + } else { + result[0] += -0.008557741; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { + result[0] += 0.044274148; + } else { + result[0] += 0.004722073; + } + } else { + result[0] += -0.020457005; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 208))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 214))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { + result[0] += -0.011751657; + } else { + result[0] += -0.004513866; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 174))) { + result[0] += 0.0004462137; + } else { + result[0] += 0.0044212313; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 202))) { + result[0] += -0.015617072; + } else { + result[0] += -0.0095054805; + } + } else { + result[0] += -3.9380015e-05; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 160))) { + result[0] += 0.002189336; + } else { + result[0] += 0.011331045; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 230))) { + result[0] += 0.0016538227; + } else { + result[0] += -0.0024343396; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 186))) { + result[0] += 0.030128485; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + result[0] += 0.0012932423; + } else { + result[0] += 0.004705872; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += 0.001994251; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 252))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 138))) { + result[0] += -0.00304999; + } else { + result[0] += 0.011378044; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { + result[0] += 0.004555874; + } else { + result[0] += -0.00066039886; + } + } + } + } + } + } + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + result[0] += 0.020459097; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 56))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + result[0] += -0.006080057; + } else { + result[0] += 0.0015269167; + } + } else { + result[0] += 0.004850093; + } + } else { + result[0] += 0.0372449; + } + } else { + result[0] += -0.0033630708; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + result[0] += 0.0020560354; + } else { + result[0] += -0.01611949; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { + result[0] += 0.02037514; + } else { + result[0] += 0.0024216073; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { + result[0] += -1.3009435e-06; + } else { + result[0] += -0.010717098; + } + } else { + result[0] += -0.026966467; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { + result[0] += -0.035333917; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { + result[0] += -0.0039970325; + } else { + result[0] += -0.018680593; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { + result[0] += 0.060415726; + } else { + result[0] += -0.0035995846; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { + result[0] += 0.079136275; + } else { + result[0] += 0.006364348; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + result[0] += 0.009026817; + } else { + result[0] += -0.00017536264; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { + result[0] += 0.0025470634; + } else { + result[0] += -0.0051941304; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 78))) { + result[0] += -0.0008757123; + } else { + result[0] += -0.008570026; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 52))) { + result[0] += 0.005017677; + } else { + result[0] += -7.974629e-05; + } + } + } + } + } + } + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 190))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 174))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + result[0] += -8.178638e-05; + } else { + result[0] += 0.0078121508; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { + result[0] += -0.005830926; + } else { + result[0] += -0.0010021594; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 262))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 244))) { + result[0] += 0.0016010083; + } else { + result[0] += 0.00825313; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 206))) { + result[0] += -0.003538803; + } else { + result[0] += 0.0063129514; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 198))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 170))) { + result[0] += -0.005545571; + } else { + result[0] += -0.0232496; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { + result[0] += 0.012393202; + } else { + result[0] += -4.4827804e-05; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 208))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 200))) { + result[0] += 0.003246121; + } else { + result[0] += -0.00059273635; + } + } else { + result[0] += -0.013444054; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 180))) { + result[0] += 0.00011026966; + } else { + result[0] += -0.012288004; + } + } else { + result[0] += 0.009097963; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 210))) { + result[0] += -0.004248265; + } else { + result[0] += -0.008213413; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.041847315; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 214))) { + result[0] += 0.008025631; + } else { + result[0] += 0.0023118325; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { + result[0] += -0.004090266; + } else { + result[0] += 0.001676759; + } + } + } + } + } + } else { + result[0] += 0.0124376435; + } + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 130))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + result[0] += 8.788087e-05; + } else { + result[0] += -0.0007693504; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 62))) { + result[0] += 0.028994996; + } else { + result[0] += 0.0019165861; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 82))) { + result[0] += 0.0018343481; + } else { + result[0] += -0.001085312; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + result[0] += -0.00844089; + } else { + result[0] += -0.0010173704; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 118))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { + result[0] += 0.03216304; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.019660346; + } else { + result[0] += 0.010661392; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + result[0] += -0.011746697; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { + result[0] += 0.040727697; + } else { + result[0] += 0.007775008; + } + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 146))) { + result[0] += -0.017095553; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += -0.010490341; + } else { + result[0] += -0.0029303418; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + result[0] += 0.0037032042; + } else { + result[0] += 0.019236496; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 88))) { + result[0] += -0.00065161986; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 154))) { + result[0] += 0.013047007; + } else { + result[0] += 0.018410644; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { + result[0] += -0.0025795808; + } else { + result[0] += 0.0025427365; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 44))) { + result[0] += 0.0063687847; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 158))) { + result[0] += 0.021263849; + } else { + result[0] += 0.037114166; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { + result[0] += -0.007888737; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.0043152072; + } else { + result[0] += -0.00065306626; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { + result[0] += 0.0002832889; + } else { + result[0] += 0.023496693; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 164))) { + result[0] += -0.004027024; + } else { + result[0] += 0.0036689022; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 52))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 168))) { + result[0] += -0.006764188; + } else { + result[0] += -0.015382841; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 188))) { + result[0] += -0.0006603152; + } else { + result[0] += -0.0053936313; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { + result[0] += 0.0016898311; + } else { + result[0] += 0.010389189; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { + result[0] += -0.0190918; + } else { + result[0] += 6.013682e-05; + } + } + } + } + } + } + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 30))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += 0.0022224819; + } else { + result[0] += -9.1448324e-05; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { + result[0] += 0.008647813; + } else { + result[0] += 0.0013861794; + } + } + } else { + result[0] += -0.0051472313; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { + result[0] += 0.022298444; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { + result[0] += 0.014823428; + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 78))) { + result[0] += 0.0028555428; + } else { + result[0] += -0.0060178605; + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 8))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 204))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { + result[0] += -0.011365654; + } else { + result[0] += 0.008808569; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 166))) { + result[0] += -0.024276486; + } else { + result[0] += -0.0131767215; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + result[0] += 0.00949425; + } else { + result[0] += -0.013411551; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + result[0] += -0; + } else { + result[0] += 0.012317988; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + result[0] += 0.0853606; + } else { + result[0] += 0.0014590746; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 42))) { + result[0] += -0.04024057; + } else { + result[0] += 0.025248704; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { + result[0] += -0.00020014495; + } else { + result[0] += -0.008825987; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { + result[0] += -0.0026628354; + } else { + result[0] += 0.00010321446; + } + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { + result[0] += -0.002153416; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + result[0] += -0.0011085857; + } else { + result[0] += 0.00558898; + } + } + } + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + result[0] += -0.0072398344; + } else { + result[0] += -0.00012400204; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { + result[0] += 0.025649467; + } else { + result[0] += -0.0001327928; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 94))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { + result[0] += -0.0015077029; + } else { + result[0] += -0.014433789; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += 0.026124759; + } else { + result[0] += 0.0094175115; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { + result[0] += -0.009028183; + } else { + result[0] += -0.05055972; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + result[0] += 0.008288766; + } else { + result[0] += -0.03171199; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 74))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += 0.029759515; + } else { + result[0] += -0.019024173; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 86))) { + result[0] += 0.0045653116; + } else { + result[0] += 6.555731e-05; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 106))) { + result[0] += 0.029950727; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.0046233633; + } else { + result[0] += 0.020635402; + } + } else { + result[0] += 0.0056214184; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += -0.03250946; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + result[0] += 0.013577218; + } else { + result[0] += -0.031636387; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { + result[0] += -0.005065638; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.004201682; + } else { + result[0] += 0.0023832265; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 84))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 152))) { + result[0] += -0.023720464; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + result[0] += -0.0023072253; + } else { + result[0] += 0.0019047937; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 158))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { + result[0] += -0.0026599832; + } else { + result[0] += 0.03371409; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 98))) { + result[0] += -0.010501557; + } else { + result[0] += 0.03556975; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 92))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + result[0] += 0.005952064; + } else { + result[0] += 0.024354918; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + result[0] += 0.0024803034; + } else { + result[0] += -0.007694894; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { + result[0] += 0.016107908; + } else { + result[0] += -0.0016387564; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { + result[0] += 0.003588716; + } else { + result[0] += -1.9828594e-05; + } + } + } + } + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 206))) { + result[0] += 0.0046148184; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { + result[0] += 0.0024027901; + } else { + result[0] += 0.00039783234; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += 0.044661757; + } else { + result[0] += 0.0023485457; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + result[0] += -0.04294611; + } else { + result[0] += -0.02216121; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += 0.0008537049; + } else { + result[0] += -0.020757614; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 192))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { + result[0] += -0.0010473432; + } else { + result[0] += -0.012557432; + } + } else { + result[0] += 0.0345631; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 10))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { + result[0] += 0.009390167; + } else { + result[0] += 0.0029766764; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { + result[0] += -0.00874328; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + result[0] += -0.011705816; + } else { + result[0] += 0.0072610932; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { + result[0] += -0.00043213202; + } else { + result[0] += 0.0003012484; + } + } + } + } + } + } + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + result[0] += -0.0078075933; + } else { + result[0] += -0.03296116; + } + } else { + result[0] += 0.0045325574; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + result[0] += 0.0027738758; + } else { + result[0] += -0.0002652021; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.017156854; + } else { + result[0] += 0.0061222934; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + result[0] += 0.009275707; + } else { + result[0] += -0.008091964; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + result[0] += 0.02927331; + } else { + result[0] += 0.0036204413; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + result[0] += -0.009472274; + } else { + result[0] += -0.036276244; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { + result[0] += -0.0055947257; + } else { + result[0] += -0.016915802; + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + result[0] += 0.0031531143; + } else { + result[0] += -0.020101098; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 10))) { + result[0] += -0.019649826; + } else { + result[0] += -0.0027359962; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { + result[0] += 0.02526692; + } else { + result[0] += -0; + } + } else { + result[0] += 0.0012190904; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { + result[0] += 0.079478934; + } else { + result[0] += 0.004041801; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.05100611; + } else { + result[0] += 0.013841884; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + result[0] += -0.0001244545; + } else { + result[0] += 0.0013780014; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + result[0] += 0.004001136; + } else { + result[0] += -0.011721842; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + result[0] += 0.015507097; + } else { + result[0] += -0.002207271; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 68))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 174))) { + result[0] += -0.0029817559; + } else { + result[0] += 0.0030468712; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { + result[0] += 0.008323856; + } else { + result[0] += 0.0378311; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + result[0] += 0.019646537; + } else { + result[0] += -0.009434113; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { + result[0] += 0.015092474; + } else { + result[0] += 0.00023066562; + } + } + } + } + } + } + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { + result[0] += 0.00044216277; + } else { + result[0] += 0.0030401708; + } + } else { + result[0] += -0.053345073; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + result[0] += 0.024727738; + } else { + result[0] += -0.008567747; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { + result[0] += 0.036883164; + } else { + result[0] += 0.014384936; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.004501282; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 14))) { + result[0] += -0.023340857; + } else { + result[0] += 0.0061771907; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.024513956; + } else { + result[0] += -0.0015961519; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { + result[0] += 0.0052294037; + } else { + result[0] += 0.00016616772; + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 218))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 62))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { + result[0] += 0.0042944904; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { + result[0] += -0.035668056; + } else { + result[0] += 0.033606566; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { + result[0] += 0.013674865; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { + result[0] += 0.0019538465; + } else { + result[0] += -0.001950983; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 6))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += 0.0020924758; + } else { + result[0] += 0.00730929; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 78))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 10))) { + result[0] += 0.00075499614; + } else { + result[0] += 0.0019689128; + } + } else { + result[0] += -0.0016815666; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 226))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { + result[0] += -0.0115894945; + } else { + result[0] += -0.051073868; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + result[0] += 0.011092906; + } else { + result[0] += 0.00051702786; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { + result[0] += -0.013087346; + } else { + result[0] += -0.0062652132; + } + } else { + result[0] += 0.009260368; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 92))) { + result[0] += -0.013211399; + } else { + result[0] += -0.005936895; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += 0.018809779; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { + result[0] += -0.00044671824; + } else { + result[0] += 0.0025783246; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { + result[0] += -0.008156588; + } else { + result[0] += 0.011215605; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + result[0] += 0.009001787; + } else { + result[0] += 0.027359499; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += 0.03523052; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { + result[0] += -0.016149698; + } else { + result[0] += -0.0014326718; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { + result[0] += 0.009203473; + } else { + result[0] += -0.01101023; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { + result[0] += 0.007282652; + } else { + result[0] += -3.0036234e-05; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { + result[0] += 0.00011574587; + } else { + result[0] += 0.021244911; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { + result[0] += 0.0015066937; + } else { + result[0] += 0.01799863; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 116))) { + result[0] += 0.002711843; + } else { + result[0] += 0.01596604; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 178))) { + result[0] += -0.0002499475; + } else { + result[0] += 0.0038768928; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + result[0] += 0.022616291; + } else { + result[0] += -0; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { + result[0] += -0.018833179; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { + result[0] += -0.007184063; + } else { + result[0] += -0.00079069706; + } + } + } + } + } else { + result[0] += 0.0025515545; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 254))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 208))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 230))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 218))) { + result[0] += -9.1387315e-05; + } else { + result[0] += -0.0014537438; + } + } else { + result[0] += 0.0155031625; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 140))) { + result[0] += -0.003084584; + } else { + result[0] += -0.01318902; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 160))) { + result[0] += 0.0037594668; + } else { + result[0] += -0.0014401844; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { + result[0] += 0.0035882175; + } else { + result[0] += -0.0048747966; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 162))) { + result[0] += 0.0041894084; + } else { + result[0] += 0.0011686251; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 210))) { + result[0] += -0.0026981125; + } else { + result[0] += -0.009141969; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { + result[0] += 0.0005261341; + } else { + result[0] += -0.0018949058; + } + } + } + } + } else { + result[0] += 0.018716332; + } + } + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { + result[0] += 2.3203478e-05; + } else { + result[0] += -0.0077802497; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + result[0] += 0.031958427; + } else { + result[0] += 0.0035587456; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { + result[0] += 0.002486235; + } else { + result[0] += 0.011185441; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 204))) { + result[0] += -0.0068403906; + } else { + result[0] += -0.001759151; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 140))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.0073994272; + } else { + result[0] += -0.016759716; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 108))) { + result[0] += 0.028997958; + } else { + result[0] += -0.00054327224; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { + result[0] += 0.02114836; + } else { + result[0] += -0.011946307; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 264))) { + result[0] += 0.002376872; + } else { + result[0] += -0.0016341202; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + result[0] += -0.03481881; + } else { + result[0] += -0.007797651; + } + } else { + result[0] += -0.00034481628; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + result[0] += 0.0057917973; + } else { + result[0] += 0.0218565; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { + result[0] += -0.001166375; + } else { + result[0] += 0.0030204598; + } + } + } + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { + result[0] += -8.485738e-05; + } else { + result[0] += 0.0070774257; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 148))) { + result[0] += -0.0021743942; + } else { + result[0] += -0.029421; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 172))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + result[0] += 0.0006834134; + } else { + result[0] += 0.010325692; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { + result[0] += -0.001693792; + } else { + result[0] += 0.0011259892; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + result[0] += -0.0077881524; + } else { + result[0] += 0.0026733195; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 166))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { + result[0] += 0.028013662; + } else { + result[0] += 0.0070042475; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { + result[0] += 0.004207076; + } else { + result[0] += -0.016646465; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 182))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + result[0] += 0.014890812; + } else { + result[0] += -0.0032379036; + } + } else { + result[0] += 0.0058797766; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { + result[0] += -0.004874174; + } else { + result[0] += -0.014910466; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 194))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { + result[0] += -0.0024306828; + } else { + result[0] += -0.021217367; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 232))) { + result[0] += 0.0033338335; + } else { + result[0] += -0.00023477618; + } + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 194))) { + result[0] += -0.037188523; + } else { + result[0] += -0.012556768; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { + result[0] += 0.0015031213; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + result[0] += 0.038557947; + } else { + result[0] += 0.018226711; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { + result[0] += -0.02505053; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 204))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { + result[0] += -0.0005712052; + } else { + result[0] += -0.0054953173; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 216))) { + result[0] += 0.0013088783; + } else { + result[0] += -0.0013262478; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 214))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 92))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 246))) { + result[0] += -0.0016769763; + } else { + result[0] += 0.004949599; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + result[0] += -0.022474889; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + result[0] += 0.011304743; + } else { + result[0] += 0.0049235076; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 82))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + result[0] += -0.0013205075; + } else { + result[0] += 0.005631945; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { + result[0] += -0.013254325; + } else { + result[0] += -0.0048218477; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 234))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + result[0] += 0.0018386835; + } else { + result[0] += -0.010448969; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { + result[0] += -0.0026523883; + } else { + result[0] += 0.00015089083; + } + } + } + } + } + } + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { + result[0] += 0.0024612967; + } else { + result[0] += -0.00267146; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { + result[0] += 0.0072369473; + } else { + result[0] += 0.03716041; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { + result[0] += 0.016798072; + } else { + result[0] += -0.020668425; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + result[0] += -0.0026954925; + } else { + result[0] += 0.00023960511; + } + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 140))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.009252391; + } else { + result[0] += 0.0032986738; + } + } else { + result[0] += -0.01665429; + } + } else { + result[0] += 0.0028180957; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { + result[0] += 0.02164252; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + result[0] += -0.012067605; + } else { + result[0] += 0.0026951; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { + result[0] += 0.015092296; + } else { + result[0] += -0.038058575; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { + result[0] += 0.00892017; + } else { + result[0] += 0.031467702; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 216))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 154))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + result[0] += 0.0022178197; + } else { + result[0] += -0.005610294; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + result[0] += 0.011955344; + } else { + result[0] += -0.0074412758; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + result[0] += 0.0014451804; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.0032450047; + } else { + result[0] += 0.00080591295; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { + result[0] += 0.010952067; + } else { + result[0] += -0.003597115; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 144))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 230))) { + result[0] += 0.0010651236; + } else { + result[0] += 0.019118952; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { + result[0] += -0.0010132345; + } else { + result[0] += 0.0005361346; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + result[0] += 0.0063631246; + } else { + result[0] += -0.005013407; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 216))) { + result[0] += -0.0018120991; + } else { + result[0] += 0.0013921553; + } + } + } + } + } + } + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { + result[0] += -0.0027856524; + } else { + result[0] += 0.011518478; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 52))) { + result[0] += -0.024774041; + } else { + result[0] += -0.0019057058; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 10))) { + result[0] += -0.01394673; + } else { + result[0] += 0.006964297; + } + } else { + result[0] += 0.05194057; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 152))) { + result[0] += -0.028471593; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 186))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { + result[0] += -0.0014699342; + } else { + result[0] += 0.024541676; + } + } else { + result[0] += -0.010517646; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 210))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + result[0] += -0.006757573; + } else { + result[0] += 0.0052805874; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { + result[0] += -0.01105545; + } else { + result[0] += 0.001222587; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 72))) { + result[0] += -0.011149263; + } else { + result[0] += -0.031825926; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 80))) { + result[0] += -6.1052204e-05; + } else { + result[0] += -0.0018558489; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += -0; + } else { + result[0] += 0.037658174; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 184))) { + result[0] += -0.022644173; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { + result[0] += -0.011395801; + } else { + result[0] += -0.004704497; + } + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 116))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { + result[0] += 0.018175911; + } else { + result[0] += 0.0022316524; + } + } else { + result[0] += -0.01448056; + } + } else { + result[0] += 0.03850323; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 10))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { + result[0] += -0.0056385025; + } else { + result[0] += -0.001911032; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 142))) { + result[0] += 0.006841138; + } else { + result[0] += 0.0015013752; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + result[0] += -0.003987257; + } else { + result[0] += 0.0014403629; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 140))) { + result[0] += 0.0001458845; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 168))) { + result[0] += -0.018207422; + } else { + result[0] += 0.0007071493; + } + } + } else { + result[0] += -0.014048204; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { + result[0] += 0.024706252; + } else { + result[0] += 0.012115999; + } + } else { + result[0] += -0.010074422; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + result[0] += 0.00012816093; + } else { + result[0] += -0.0048219212; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + result[0] += 0.00042065704; + } else { + result[0] += -0.00044052277; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 2))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + result[0] += 0.012054322; + } else { + result[0] += -0.012439433; + } + } else { + result[0] += 0.004142578; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 246))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { + result[0] += 0.0005554498; + } else { + result[0] += 0.0034215685; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + result[0] += 0.00583103; + } else { + result[0] += 0.00022540719; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { + result[0] += 0.003226608; + } else { + result[0] += 0.029922882; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 20))) { + result[0] += -0.006730991; + } else { + result[0] += -0.001442519; + } + } + } + } + } else { + result[0] += 0.020426724; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 254))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + result[0] += 0.0003546644; + } else { + result[0] += -0.0044249105; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 132))) { + result[0] += 0.0020044944; + } else { + result[0] += -0.0026672701; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { + result[0] += -0.007007061; + } else { + result[0] += -0.0024277617; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 156))) { + result[0] += 0.0035193504; + } else { + result[0] += 0.016300239; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + result[0] += -0.002883124; + } else { + result[0] += 0.0034831774; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 222))) { + result[0] += -0.004244165; + } else { + result[0] += -0.02417766; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 36))) { + result[0] += -0.0009843176; + } else { + result[0] += -0.00013388977; + } + } + } + } + } else { + result[0] += 0.013616696; + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { + result[0] += 0.00050606584; + } else { + result[0] += -0.0017093392; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + result[0] += -0.0051450143; + } else { + result[0] += 0.0035812582; + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.010360922; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { + result[0] += 0.0013417737; + } else { + result[0] += -0.0003085478; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += 0.06123736; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { + result[0] += -0.010781719; + } else { + result[0] += -0.002857905; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 52))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + result[0] += 0.013251043; + } else { + result[0] += -0.020714538; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 76))) { + result[0] += -0.0037599222; + } else { + result[0] += -0.0004986952; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { + result[0] += 0.08917741; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { + result[0] += -0.030496597; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 252))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { + result[0] += -0.0025217582; + } else { + result[0] += 0.00018528964; + } + } else { + result[0] += -0.014542065; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 222))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.009864878; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { + result[0] += 0.017176276; + } else { + result[0] += 0.030590722; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 92))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { + result[0] += 0.0058373446; + } else { + result[0] += -0.012375738; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += 0.047506116; + } else { + result[0] += 0.011469667; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { + result[0] += 0.004630673; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 252))) { + result[0] += -0.004851036; + } else { + result[0] += -0.023372188; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { + result[0] += 0.036052324; + } else { + result[0] += -0.002185328; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 224))) { + result[0] += -0.00040355555; + } else { + result[0] += -0.0124766445; + } + } else { + result[0] += 0.008253347; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 92))) { + result[0] += -0.011534996; + } else { + result[0] += -0.0021382947; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { + result[0] += -0.0014395117; + } else { + result[0] += 0.019073278; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + result[0] += 0.019316873; + } else { + result[0] += 0.0020871195; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + result[0] += 0.004829779; + } else { + result[0] += 0.021349184; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += -0.0045497594; + } else { + result[0] += 0.015120694; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { + result[0] += 0.0015102568; + } else { + result[0] += -3.608707e-06; + } + } + } + } + } + } + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + result[0] += 0.00032929177; + } else { + result[0] += 0.029328126; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { + result[0] += -0.00073194504; + } else { + result[0] += -0.009144339; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += -0.032370485; + } else { + result[0] += 0.0061941766; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 50))) { + result[0] += 0.053673845; + } else { + result[0] += 0.018453414; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 178))) { + result[0] += -0.010862867; + } else { + result[0] += 0.0031142721; + } + } else { + result[0] += 0.04961296; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 28))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { + result[0] += -0.035367988; + } else { + result[0] += -0.014287199; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { + result[0] += 0.0022301266; + } else { + result[0] += -0.0076341094; + } + } + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 74))) { + result[0] += 0.009890891; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { + result[0] += -0.034998115; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { + result[0] += 0.003447539; + } else { + result[0] += 0.0015428506; + } + } + } + } else { + result[0] += 0.021317383; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { + result[0] += 0.06219096; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { + result[0] += 0.0083704125; + } else { + result[0] += 0.0011106033; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += -0.006979663; + } else { + result[0] += -0.004151942; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + result[0] += 0.017166223; + } else { + result[0] += 0.0019738798; + } + } else { + result[0] += -0.025185201; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 66))) { + result[0] += 0.0008634852; + } else { + result[0] += -0.008104631; + } + } else { + result[0] += 0.0040236074; + } + } else { + result[0] += 0.010268874; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 74))) { + result[0] += -0.029302115; + } else { + result[0] += 0.027285008; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { + result[0] += -0.004545824; + } else { + result[0] += -0.00053453137; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + result[0] += 0.0019230042; + } else { + result[0] += -4.2119464e-06; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { + result[0] += -0.010225962; + } else { + result[0] += -0.00024979832; + } + } + } + } + } + } + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 36))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 26))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { + result[0] += -0.0014482096; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 196))) { + result[0] += 0.0071594575; + } else { + result[0] += 0.027249644; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + result[0] += 0.011023153; + } else { + result[0] += -0.010220974; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + result[0] += 0.0010131482; + } else { + result[0] += -0.00073193485; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 208))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + result[0] += -0.049486402; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + result[0] += -0.00315987; + } else { + result[0] += -0.014758741; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 26))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 22))) { + result[0] += 0.004376946; + } else { + result[0] += 0.040119316; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { + result[0] += -0.010627906; + } else { + result[0] += 0.0038438165; + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { + result[0] += 0.0069011683; + } else { + result[0] += 0.016726343; + } + } else { + result[0] += 0.0049917004; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 186))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + result[0] += 0.01811142; + } else { + result[0] += 0.0030194859; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.002317766; + } else { + result[0] += 0.00087052287; + } + } + } else { + result[0] += -0.0039971955; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 40))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 162))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { + result[0] += -0.0014173501; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 152))) { + result[0] += -0.0050792135; + } else { + result[0] += 0.006009587; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { + result[0] += -0.00039048417; + } else { + result[0] += -0.009521704; + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 182))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 182))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { + result[0] += -0.0080691455; + } else { + result[0] += 0.016010879; + } + } else { + result[0] += -0.01479948; + } + } else { + result[0] += -0.02853345; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 82))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { + result[0] += -0.015648594; + } else { + result[0] += 0.005108214; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + result[0] += 0.011618692; + } else { + result[0] += 0.0042830543; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 200))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { + result[0] += 0.005279971; + } else { + result[0] += -0.009966015; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { + result[0] += -0.0035748226; + } else { + result[0] += 0.0021337403; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { + result[0] += -0.025904989; + } else { + result[0] += 0.0019193542; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + result[0] += -0.00022157452; + } else { + result[0] += -0.0015951125; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { + result[0] += -0.00094731955; + } else { + result[0] += 0.029716676; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + result[0] += 0.00062934245; + } else { + result[0] += -0.00041988047; + } + } + } + } + } + } + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 110))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.0063841576; + } else { + result[0] += 0.0017673693; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + result[0] += -0.0013868456; + } else { + result[0] += 0.00015485496; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 72))) { + result[0] += -0.009897975; + } else { + result[0] += -0.030677944; + } + } else { + result[0] += 0.021359773; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + result[0] += 0.021139013; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.0034971682; + } else { + result[0] += -0.0043157833; + } + } else { + result[0] += -0.010502246; + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { + result[0] += 0.02943166; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.005999833; + } else { + result[0] += -0.0019856528; + } + } else { + result[0] += 0.013738169; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { + result[0] += 0.008470384; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + result[0] += -0.003441482; + } else { + result[0] += -0.011388111; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 110))) { + result[0] += 0.0024586066; + } else { + result[0] += -0.021055488; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { + result[0] += -0.026231218; + } else { + result[0] += -0.0044914987; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + result[0] += 0.027324816; + } else { + result[0] += 0.0072242687; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { + result[0] += -0.0015850182; + } else { + result[0] += -0.010035633; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { + result[0] += 0.054534066; + } else { + result[0] += 0.0008897401; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 166))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += 0.075174846; + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += -0.0014329397; + } else { + result[0] += 0.002409567; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + result[0] += -0.007922634; + } else { + result[0] += -0.00042070524; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 158))) { + result[0] += -0.00044648108; + } else { + result[0] += 0.0013399239; + } + } + } + } + } + } + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 168))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += 0.0013971846; + } else { + result[0] += -6.5496147e-06; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + result[0] += -0.02558857; + } else { + result[0] += -0.0035198168; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 68))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { + result[0] += -0.0007052002; + } else { + result[0] += 0.011330684; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 110))) { + result[0] += 0.008754961; + } else { + result[0] += 0.017457347; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { + result[0] += 0.021995483; + } else { + result[0] += 0.0037829764; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.013063647; + } else { + result[0] += -0.008626144; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 218))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 210))) { + result[0] += -0.00048324605; + } else { + result[0] += 0.014737451; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { + result[0] += 0.03996015; + } else { + result[0] += -0.004091793; + } + } + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 170))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 144))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + result[0] += 0.0020200752; + } else { + result[0] += 0.016987968; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { + result[0] += -0.013594501; + } else { + result[0] += 0.0012423365; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + result[0] += -0.0187645; + } else { + result[0] += -0.007347082; + } + } + } else { + result[0] += 0.021339634; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 198))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { + result[0] += 0.06586228; + } else { + result[0] += 0.021907127; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 94))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 142))) { + result[0] += 0.0030923286; + } else { + result[0] += 0.00036826293; + } + } else { + result[0] += 0.04356821; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += 0.01220308; + } else { + result[0] += -0.007081172; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { + result[0] += 3.0494935e-05; + } else { + result[0] += -0.006945651; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 16))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 10))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + result[0] += 0.00072253105; + } else { + result[0] += -0.020929243; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 194))) { + result[0] += 0.057745833; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { + result[0] += -0.0037926536; + } else { + result[0] += 0.042418174; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.034033656; + } else { + result[0] += -0.021374384; + } + } else { + result[0] += -0; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + result[0] += -0.0036783994; + } else { + result[0] += -0.012444709; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 156))) { + result[0] += -0.00028157423; + } else { + result[0] += -0.0030722509; + } + } + } + } + } + } + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 248))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + result[0] += -0.018552605; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 212))) { + result[0] += 0.013609043; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + result[0] += -0.0035600197; + } else { + result[0] += 0.005957694; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 254))) { + result[0] += -0.0039241104; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { + result[0] += -0.0053416244; + } else { + result[0] += 0.0076100454; + } + } else { + result[0] += 0.0010679305; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { + result[0] += -0.005295289; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { + result[0] += -1.6435115e-05; + } else { + result[0] += -0.0010428902; + } + } + } else { + result[0] += -0.0050287596; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 228))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { + result[0] += 0.025240282; + } else { + result[0] += 0.006482261; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 264))) { + result[0] += 0.0052149496; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { + result[0] += 0.006406661; + } else { + result[0] += -0.0011082895; + } + } + } + } + } + } else { + result[0] += 0.010802027; + } + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 168))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { + result[0] += 2.1751774e-05; + } else { + result[0] += -0.0076580383; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 68))) { + result[0] += -0.00075408455; + } else { + result[0] += 0.015074461; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + result[0] += -0.0018395454; + } else { + result[0] += 0.015883565; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + result[0] += -0.0091815125; + } else { + result[0] += 0.0014608675; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 172))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + result[0] += 0.0026474108; + } else { + result[0] += 0.01692606; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 140))) { + result[0] += 0.02202346; + } else { + result[0] += -0.012683655; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { + result[0] += -0.0071168602; + } else { + result[0] += -0.00039252793; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + result[0] += 0.001928371; + } else { + result[0] += -0.0002336656; + } + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 148))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { + result[0] += -0.025342045; + } else { + result[0] += 0.020604817; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { + result[0] += -0.013844515; + } else { + result[0] += -0.0027904052; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 142))) { + result[0] += 0.019259404; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { + result[0] += 0.0053436733; + } else { + result[0] += 0.0006968994; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { + result[0] += -0.013023679; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { + result[0] += -0.0089388685; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 140))) { + result[0] += -0.013734943; + } else { + result[0] += 0.0016584283; + } + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + result[0] += -0.00055690337; + } else { + result[0] += -0.016434593; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + result[0] += 0.076187894; + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 144))) { + result[0] += 0.0040504658; + } else { + result[0] += 0.010756901; + } + } + } + } else { + result[0] += 0.023556888; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { + result[0] += 0.06953845; + } else { + result[0] += -0.0023770647; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 186))) { + result[0] += 0.021977352; + } else { + result[0] += 0.0001357905; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 44))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { + result[0] += 0.0026280086; + } else { + result[0] += 0.015929218; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { + result[0] += 0.0037350792; + } else { + result[0] += 0.00039957697; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 18))) { + result[0] += 0.03222883; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 176))) { + result[0] += -0.011439302; + } else { + result[0] += -0.0039858185; + } + } + } + } + } + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + result[0] += -0.0012402734; + } else { + result[0] += -0.019989545; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 8))) { + result[0] += 0.0026303197; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.040178392; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.0065984237; + } else { + result[0] += 0.02927506; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.020329256; + } else { + result[0] += -0; + } + } else { + result[0] += 0.012915528; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + result[0] += 0.009343422; + } else { + result[0] += -0.012758999; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + result[0] += -0.027498607; + } else { + result[0] += -0.012787706; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 24))) { + result[0] += 0.013563978; + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + result[0] += -0; + } else { + result[0] += 0.017173748; + } + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { + result[0] += -0; + } else { + result[0] += -0.018669667; + } + } else { + result[0] += -0; + } + } + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + result[0] += 0.0013438625; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { + result[0] += 0.0075284163; + } else { + result[0] += 0.020848578; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 224))) { + result[0] += 0.0053101867; + } else { + result[0] += -0.005638622; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { + result[0] += 0.00015324964; + } else { + result[0] += 0.0012270033; + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 238))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { + result[0] += -0.0004296944; + } else { + result[0] += 0.00052584225; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 174))) { + result[0] += 0.0010627261; + } else { + result[0] += -0.00823095; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 256))) { + result[0] += 0.0063353493; + } else { + result[0] += 0.00052647333; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { + result[0] += 0.0017266994; + } else { + result[0] += -0.0006555208; + } + } + } + } + } + } + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 34))) { + result[0] += 0.0004357934; + } else { + result[0] += -0.0016803583; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + result[0] += 0.013963197; + } else { + result[0] += 0.0010920282; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { + result[0] += -0.0020354139; + } else { + result[0] += -0.0084240325; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { + result[0] += 0.00018836466; + } else { + result[0] += -0.001789179; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 98))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { + result[0] += -0.0021141667; + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { + result[0] += 0.0008162449; + } else { + result[0] += -0.0007128279; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { + result[0] += 0.005020843; + } else { + result[0] += 0.015004709; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + result[0] += 0.004781536; + } else { + result[0] += 0.0005883411; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 130))) { + result[0] += -0.035336528; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + result[0] += 0.023583809; + } else { + result[0] += 0.0007455337; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { + result[0] += 0.0023794405; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + result[0] += 0.023832815; + } else { + result[0] += 0.010761608; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 196))) { + result[0] += -0.010108376; + } else { + result[0] += -0.0007176217; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { + result[0] += 0.0009595863; + } else { + result[0] += -0.004075228; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { + result[0] += -0; + } else { + result[0] += 0.01168954; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { + result[0] += -0.0033294098; + } else { + result[0] += -6.6527915e-05; + } + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 118))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { + result[0] += 0.033734445; + } else { + result[0] += 0.0013792046; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { + result[0] += 0.011527828; + } else { + result[0] += 0.0049809148; + } + } + } else { + result[0] += 0.013943759; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + result[0] += -0.008578103; + } else { + result[0] += 0.0029740678; + } + } else { + result[0] += -0.018524783; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 140))) { + result[0] += -0.00042957798; + } else { + result[0] += -0.009110951; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 132))) { + result[0] += 0.021989485; + } else { + result[0] += 0.0028953014; + } + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { + result[0] += -0.0064930986; + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 188))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { + result[0] += 0.00023702074; + } else { + result[0] += 0.0032349098; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { + result[0] += -0.0035499786; + } else { + result[0] += -0; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { + result[0] += -0.008582824; + } else { + result[0] += 0.0003269389; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { + result[0] += 0.006198867; + } else { + result[0] += 0.0029896083; + } + } + } + } + } + } + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 194))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { + result[0] += -0.008292492; + } else { + result[0] += 0.048605617; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { + result[0] += -0.011937882; + } else { + result[0] += -0.0048466916; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 36))) { + result[0] += -0.0032074524; + } else { + result[0] += 6.964723e-05; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 52))) { + result[0] += 0.024765873; + } else { + result[0] += -0.0021545556; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { + result[0] += -1.2665853e-05; + } else { + result[0] += -0.023850983; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { + result[0] += 0.0025796972; + } else { + result[0] += 0.00040402517; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 160))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.0170327; + } else { + result[0] += -0.00829741; + } + } else { + result[0] += -0.0026483634; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 188))) { + result[0] += 0.0010909947; + } else { + result[0] += 0.0053282953; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { + result[0] += 0.026514; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { + result[0] += 0.037232116; + } else { + result[0] += 0.004267938; + } + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 248))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 126))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { + result[0] += 0.026404385; + } else { + result[0] += -0.001118703; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + result[0] += 0.0017804791; + } else { + result[0] += -0.007409166; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 130))) { + result[0] += -0.004484429; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 222))) { + result[0] += 0.031472586; + } else { + result[0] += 0.0001007054; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 252))) { + result[0] += -0.003407167; + } else { + result[0] += 0.001687183; + } + } else { + result[0] += 0.014353302; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 28))) { + result[0] += -0.0015060778; + } else { + result[0] += -0.0003457068; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 10))) { + result[0] += -0.013887319; + } else { + result[0] += -0.0035536517; + } + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 238))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 160))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 74))) { + result[0] += -0.0055618854; + } else { + result[0] += -0.0014347414; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 224))) { + result[0] += -0.00070218346; + } else { + result[0] += 0.0014398324; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 8))) { + result[0] += 0.023142483; + } else { + result[0] += -0.000307219; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { + result[0] += 0.0010922215; + } else { + result[0] += 0.017112693; + } + } + } + } + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + result[0] += -0.010769006; + } else { + result[0] += 0.0069697066; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { + result[0] += 0.02430601; + } else { + result[0] += 0.00639099; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { + result[0] += 0.015791424; + } else { + result[0] += -0.0046518813; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { + result[0] += -0.03668975; + } else { + result[0] += -0.012895368; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { + result[0] += 0.03604609; + } else { + result[0] += 0.012836625; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 50))) { + result[0] += -0.010758377; + } else { + result[0] += -0.001967916; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 220))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { + result[0] += 0.034449853; + } else { + result[0] += 0.0030996487; + } + } else { + result[0] += -0.015721142; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 254))) { + result[0] += 0.039080422; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 222))) { + result[0] += 0.020187657; + } else { + result[0] += 0.0016309306; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 224))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { + result[0] += -0.01591835; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 64))) { + result[0] += -0.006518031; + } else { + result[0] += -0.0026554007; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + result[0] += 0.00103551; + } else { + result[0] += 0.00901093; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + result[0] += -0.0012550136; + } else { + result[0] += -1.612309e-05; + } + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 116))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 130))) { + result[0] += 0.00788109; + } else { + result[0] += -0.0018598955; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 60))) { + result[0] += 0.005137967; + } else { + result[0] += -0.0032950975; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 98))) { + result[0] += -0.042013966; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { + result[0] += -0.0046039354; + } else { + result[0] += 0.011869277; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 172))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + result[0] += -0.026546886; + } else { + result[0] += 0.009092043; + } + } else { + result[0] += 0.017844815; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { + result[0] += 0.0068409294; + } else { + result[0] += 0.01327271; + } + } else { + result[0] += 0.000659167; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { + result[0] += 0.004663289; + } else { + result[0] += -0.006825484; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 56))) { + result[0] += 0.008568982; + } else { + result[0] += 0.0030761992; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { + result[0] += -0.014622196; + } else { + result[0] += 0.00041277093; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 42))) { + result[0] += 0.0016062713; + } else { + result[0] += 0.046605274; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { + result[0] += -0.004711694; + } else { + result[0] += -0.0015061334; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 222))) { + result[0] += 0.012894141; + } else { + result[0] += -0.0020437974; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { + result[0] += -0.0009532698; + } else { + result[0] += 0.00017820772; + } + } + } + } + } + } + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 120))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += 0.0047425376; + } else { + result[0] += 0.009442864; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { + result[0] += -0.016419962; + } else { + result[0] += 0.00078140077; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { + result[0] += -0.016464185; + } else { + result[0] += -0.0012191174; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { + result[0] += 0.00046863809; + } else { + result[0] += 0.0035006986; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 124))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { + result[0] += -0.0128822; + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 170))) { + result[0] += -0.0021174157; + } else { + result[0] += -0.01799887; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { + result[0] += 0.043405965; + } else { + result[0] += 0.0019986776; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += -0.00859444; + } else { + result[0] += 0.00027344722; + } + } + } + } + } else { + result[0] += 0.013666634; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { + result[0] += 0.0035516606; + } else { + result[0] += -1.4989321e-05; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 230))) { + result[0] += -0.003034349; + } else { + result[0] += -0.0006231525; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 244))) { + result[0] += 0.0052153706; + } else { + result[0] += -0.0051266435; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { + result[0] += -0.0033818122; + } else { + result[0] += -0.0005771067; + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 244))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { + result[0] += 0.042181622; + } else { + result[0] += 0.004492736; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { + result[0] += -0.01460122; + } else { + result[0] += 0.00014612076; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { + result[0] += -0.001969084; + } else { + result[0] += -6.331263e-05; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 160))) { + result[0] += 0.0028343403; + } else { + result[0] += 0.00031744837; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { + result[0] += -0.006944129; + } else { + result[0] += -0.0027726921; + } + } + } + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 278))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { + result[0] += 0.0005400666; + } else { + result[0] += -0.0019886708; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { + result[0] += -0.009072294; + } else { + result[0] += -0.0026447035; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + result[0] += -0.0027691973; + } else { + result[0] += 0.00404422; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 276))) { + result[0] += -2.3040471e-05; + } else { + result[0] += 0.012937434; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += -0.015312892; + } else { + result[0] += -0.004679353; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { + result[0] += 0.006205307; + } else { + result[0] += 0.0010604822; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { + result[0] += -0.0003646382; + } else { + result[0] += 0.0056393673; + } + } + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 98))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + result[0] += 0.0009229826; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { + result[0] += -0.002368063; + } else { + result[0] += -0.00024388141; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 250))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + result[0] += -0.012024042; + } else { + result[0] += -0.0031967615; + } + } else { + result[0] += -0.00085695874; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 260))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { + result[0] += -0.00046735056; + } else { + result[0] += 0.0040739644; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { + result[0] += -0.0032260884; + } else { + result[0] += 0.0010031442; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + result[0] += -0.009778283; + } else { + result[0] += 0.019902207; + } + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 282))) { + result[0] += -0.0005751638; + } else { + result[0] += -0.01039412; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 230))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { + result[0] += -0.01584022; + } else { + result[0] += 0.0018113088; + } + } else { + result[0] += 0.0046630637; + } + } + } + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 250))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 36))) { + result[0] += -0.00047560144; + } else { + result[0] += 0.0013354685; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { + result[0] += -0.0026048406; + } else { + result[0] += 0.00038235518; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { + result[0] += 0.0044472557; + } else { + result[0] += 0.03397901; + } + } else { + result[0] += -0.0003922035; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { + result[0] += 0.031751767; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { + result[0] += -0.0036668777; + } else { + result[0] += 0.00452039; + } + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { + result[0] += -0.002395912; + } else { + result[0] += 0.030751167; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { + result[0] += -0.015407211; + } else { + result[0] += 0.011755302; + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { + result[0] += -0.03115373; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { + result[0] += 0.0073042098; + } else { + result[0] += 0.033008788; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { + result[0] += -0.0064700614; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { + result[0] += -0.007395641; + } else { + result[0] += -0.02954722; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { + result[0] += -0.01630216; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 184))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { + result[0] += 0.008391211; + } else { + result[0] += -0.0021036936; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { + result[0] += -0.00060320273; + } else { + result[0] += 0.0022427232; + } + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { + if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { + result[0] += -0.000107325846; + } else { + result[0] += 0.029023886; + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 50))) { + result[0] += -0.003524834; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { + result[0] += 0.002008538; + } else { + result[0] += 0.00040226732; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 132))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { + result[0] += 0.0028264725; + } else { + result[0] += 0.0078762965; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 190))) { + result[0] += -0.0036010318; + } else { + result[0] += -0.020989217; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { + result[0] += -0.025969082; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 76))) { + result[0] += -0.0037539073; + } else { + result[0] += -0.015046534; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 52))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + result[0] += 0.008154599; + } else { + result[0] += -0.00034012404; + } + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.00019781369; + } else { + result[0] += -0.0046178605; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { + result[0] += 0.0022514695; + } else { + result[0] += 0.02182677; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { + result[0] += -0.002138548; + } else { + result[0] += 0.00019392448; + } + } + } + } + } + } + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { + result[0] += -0.018533966; + } else { + result[0] += 0.009372647; + } + } else { + result[0] += -0.013326846; + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { + result[0] += 0.017056702; + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 250))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 36))) { + result[0] += 0.0010617184; + } else { + result[0] += 0.003547955; + } + } else { + result[0] += -0.002409021; + } + } else { + result[0] += 0.022888973; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 218))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { + result[0] += -6.381352e-05; + } else { + result[0] += 0.01608517; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + result[0] += 0.031114805; + } else { + result[0] += 0.0031871856; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { + result[0] += 0.0010918784; + } else { + result[0] += 0.010223559; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + result[0] += -0.0053569055; + } else { + result[0] += -0.00082102185; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { + result[0] += 0.017271513; + } else { + result[0] += -0.0010735758; + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 210))) { + result[0] += -0.0022431507; + } else { + result[0] += -0.0044196337; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { + result[0] += 0.03313246; + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 166))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 116))) { + result[0] += 0.0011855942; + } else { + result[0] += 0.0060151634; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { + result[0] += -0.007734927; + } else { + result[0] += 0.0012392174; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { + result[0] += -0.0054051043; + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { + result[0] += -0.00018920467; + } else { + result[0] += 0.0010806855; + } + } + } + } + } + } + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 184))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { + result[0] += -5.627934e-05; + } else { + result[0] += 0.004937595; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 176))) { + result[0] += -0.0081005385; + } else { + result[0] += -0.00034580208; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 112))) { + result[0] += 0.0017502941; + } else { + result[0] += 0.0067032403; + } + } else { + result[0] += 0.05561105; + } + } + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 104))) { + result[0] += -0.017684242; + } else { + result[0] += -0.008623506; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 210))) { + result[0] += -0.0030512868; + } else { + result[0] += 0.00268173; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 116))) { + result[0] += -0.008280813; + } else { + result[0] += -0.0006436382; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 68))) { + result[0] += 0.005869223; + } else { + result[0] += 0.0157161; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { + result[0] += -0.040014755; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { + result[0] += 0.0034462882; + } else { + result[0] += 0.012366778; + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { + result[0] += 0.0029618521; + } else { + result[0] += -0.0031078772; + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 78))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 156))) { + result[0] += -0.0013473729; + } else { + result[0] += 0.0062149167; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { + result[0] += 0.017057097; + } else { + result[0] += -0.010900224; + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 144))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { + result[0] += 0.0012661046; + } else { + result[0] += 0.0069153532; + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { + result[0] += -0.016097846; + } else { + result[0] += -0.00017735653; + } + } + } + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { + result[0] += 0.010808589; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { + result[0] += -0.009866092; + } else { + result[0] += 0.0047606938; + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { + result[0] += -0; + } else { + result[0] += 0.014268956; + } + } else { + result[0] += 0.0023136109; + } + } else { + result[0] += -0.0024249533; + } + } + } + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 96))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { + result[0] += -0.0002939115; + } else { + result[0] += 0.03095895; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { + result[0] += -0.016124493; + } else { + result[0] += 0.0012605732; + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { + result[0] += 0.00055091607; + } else { + result[0] += -0.0038312187; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + result[0] += 0.020991528; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { + result[0] += 0.00425994; + } else { + result[0] += -0.012923174; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 112))) { + result[0] += -0.008987924; + } else { + result[0] += 0.0027231926; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 136))) { + result[0] += 0.00033489117; + } else { + result[0] += 0.0031028683; + } + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { + result[0] += -0.023087258; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { + result[0] += 0.040452894; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { + result[0] += -0.004355786; + } else { + result[0] += 0.00052141724; + } + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 98))) { + result[0] += -0.026337389; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { + result[0] += -0.011440942; + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { + result[0] += 0.0051072245; + } else { + result[0] += -0.0016136405; + } + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 94))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { + result[0] += -0.016175192; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 90))) { + result[0] += -0.003506745; + } else { + result[0] += -0.0008637124; + } + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { + result[0] += 0.039352115; + } else { + result[0] += 0.009527031; + } + } else { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { + result[0] += -0.014133029; + } else { + result[0] += 0.0009379142; + } + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 124))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + result[0] += 0.0014655776; + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { + result[0] += 0.00799122; + } else { + result[0] += 0.02205122; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { + result[0] += 0.00084181456; + } else { + result[0] += 0.03656875; + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + result[0] += -0.0324972; + } else { + result[0] += -0.0043041403; + } + } + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 106))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { + result[0] += -0.014786902; + } else { + result[0] += -0.027247995; + } + } else { + result[0] += -0.013442961; + } + } else { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { + result[0] += 0.014649215; + } else { + result[0] += 0.00018173472; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { + result[0] += -0.003195444; + } else { + result[0] += -0.017632857; + } + } + } + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 172))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { + result[0] += 0.0011816436; + } else { + result[0] += 0.006784923; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 174))) { + result[0] += -0.0025611892; + } else { + result[0] += 0.00039800836; + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 222))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { + result[0] += -0.015747108; + } else { + result[0] += -0.0025709863; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 222))) { + result[0] += 0.00016036122; + } else { + result[0] += -0.004456304; + } + } + } + } + } + } + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { + result[0] += -0.043082695; + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { + result[0] += -0.015321686; + } else { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { + result[0] += 0.009543039; + } else { + result[0] += -0.00492233; + } + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 60))) { + result[0] += -0.018702324; + } else { + result[0] += 0.0030421054; + } + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 96))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 104))) { + result[0] += 0.023666665; + } else { + result[0] += -0.003085631; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { + result[0] += 0.0025167426; + } else { + result[0] += -0.0006228736; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { + result[0] += 0.025037153; + } else { + result[0] += 0.00086976105; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { + result[0] += -0.00833582; + } else { + result[0] += 6.457727e-05; + } + } + } + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { + result[0] += -0.034489177; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { + result[0] += 0.059084423; + } else { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { + result[0] += -0.008554864; + } else { + result[0] += -0.000933964; + } + } + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 98))) { + if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { + result[0] += -0.005567677; + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 74))) { + result[0] += 0.026860246; + } else { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { + if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 238))) { + result[0] += 0.003906974; + } else { + result[0] += 0.016403912; + } + } else { + result[0] += -0.0005779523; + } + } + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 102))) { + if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 68))) { + result[0] += -0.021588845; + } else { + result[0] += -0.0072186044; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 56))) { + result[0] += 0.00058102736; + } else { + result[0] += 0.0042956616; + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { + result[0] += -0.0021127523; + } else { + result[0] += 0.0012841078; + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 186))) { + result[0] += -0.004427323; + } else { + result[0] += -0.0004450692; + } + } else { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 188))) { + result[0] += 0.0005094443; + } else { + result[0] += -0.00016178952; + } + } + } + } + } + } + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { + if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 230))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { + result[0] += -0.00010068305; + } else { + result[0] += 0.0004361026; + } + } else { + result[0] += 0.027371911; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 130))) { + result[0] += 0.023174508; + } else { + result[0] += 0.011960031; + } + } + } else { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { + if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { + result[0] += -0.015924754; + } else { + result[0] += -0.0012669711; + } + } else { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 188))) { + result[0] += 0.018523319; + } else { + result[0] += -0.00022792198; + } + } + } else { + result[0] += 0.039226815; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 146))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 90))) { + result[0] += 0.022155268; + } else { + result[0] += -0.0030770514; + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { + result[0] += 0.03438976; + } else { + result[0] += 0.016168883; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { + result[0] += 0.023656188; + } else { + result[0] += 0.009587833; + } + } else { + if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 118))) { + result[0] += 0.003995668; + } else { + result[0] += -0.00010666515; + } + } + } + } else { + if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 108))) { + if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 150))) { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 136))) { + result[0] += 0.004109849; + } else { + result[0] += 0.027041653; + } + } else { + result[0] += -9.42766e-05; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { + result[0] += 0.0130727235; + } else { + if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 78))) { + result[0] += -0.0015822932; + } else { + result[0] += -0.008224895; + } + } + } + } + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { + result[0] += -0.012683782; + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { + result[0] += -0.0033396825; + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { + result[0] += 0.01649657; + } else { + result[0] += 0.0024240483; + } + } + } else { + if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { + result[0] += -0.004931916; + } else { + result[0] += 4.3331627e-05; + } + } + } else { + if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 52))) { + if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 202))) { + if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { + result[0] += -0.0077444026; + } else { + result[0] += -0.0008859424; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 178))) { + result[0] += 0.0073036547; + } else { + result[0] += -0.007702441; + } + } + } else { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { + if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { + result[0] += -0.00019439656; + } else { + result[0] += -0.0056553436; + } + } else { + if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 180))) { + result[0] += 0.0066337893; + } else { + result[0] += 0.00068436674; + } + } + } + } + } + } + + // Apply base_scores + result[0] += 3.356329679489135742; + + // Apply postprocessor + if (!pred_margin) { postprocess(result); } +} + +void pdlp_predictor::postprocess(double* result) +{ + // Do nothing +} + +// Feature names array +const char* pdlp_predictor::feature_names[pdlp_predictor::NUM_FEATURES] = { + "n_vars", "n_cstrs", "nnz", "sparsity", "nnz_stddev", "unbalancedness", "spmv_ops", "total_nnz"}; diff --git a/cpp/src/utilities/models/pdlp_predictor/quantize.cpp b/cpp/src/utilities/models/pdlp_predictor/quantize.cpp new file mode 100644 index 0000000000..b0c1356158 --- /dev/null +++ b/cpp/src/utilities/models/pdlp_predictor/quantize.cpp @@ -0,0 +1,1006 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "header.h" + +static const float threshold[] = { + 17, + 26, + 31, + 52, + 56, + 58, + 60, + 69, + 86, + 145, + 156, + 160, + 210, + 214, + 219, + 253, + 269, + 272, + 365, + 420, + 479, + 504, + 511, + 649, + 655, + 770, + 780, + 784, + 875, + 888, + 955, + 1024, + 1106, + 1184, + 1187, + 1216, + 1344, + 1400, + 1455, + 1635, + 2025, + 2036, + 2164, + 2272, + 2303, + 2376, + 2442, + 2595, + 2600, + 3034, + 3060, + 3073, + 3079, + 3261, + 3338, + 3472, + 3510, + 3561, + 3654, + 4009, + 4171, + 4272, + 4462, + 4484, + 4704, + 4745, + 4768, + 4798, + 5068, + 5376, + 5662, + 5942, + 5951, + 5956, + 5958, + 6260, + 6481, + 6483, + 6730, + 6868, + 7389, + 7418, + 7745, + 8538, + 10218, + 10720, + 11185, + 11928, + 12414, + 12631, + 12927, + 13410, + 13688, + 13839, + 13869, + 14060, + 14099, + 14370, + 14636, + 15977, + 17187, + 17188, + 18405, + 18865, + 20602, + 20849, + 22480, + 23530, + 23836, + 24923, + 27542, + 29397, + 29435, + 30199, + 32450, + 33154, + 35734, + 37616, + 37648, + 37816, + 40272, + 53593, + 62171, + 63708, + 72468, + 74873, + 74884, + 74918, + 78287, + 86262, + 94595, + 129171, + 129931, + 138056, + 167056, + 184865, + 268358, + 1428996, + 8, + 12, + 13, + 40, + 41, + 45, + 46, + 49, + 61, + 108, + 112, + 126, + 130, + 158, + 162, + 225, + 234, + 241, + 242, + 260, + 270, + 296, + 322, + 332, + 359, + 360, + 397, + 403, + 443, + 444, + 447, + 448, + 482, + 483, + 484, + 487, + 566, + 569, + 577, + 582, + 643, + 659, + 711, + 721, + 723, + 764, + 811, + 867, + 879, + 900, + 1005, + 1025, + 1299, + 1389, + 1500, + 1513, + 1573, + 1634, + 1653, + 1691, + 1759, + 1863, + 1898, + 1917, + 1947, + 2225, + 2301, + 2344, + 2362, + 2366, + 2372, + 2376, + 2433, + 2472, + 2522, + 2526, + 2898, + 3174, + 3249, + 3250, + 3480, + 3491, + 3492, + 3760, + 3897, + 3925, + 4234, + 4241, + 4424, + 4454, + 4463, + 4634, + 4660, + 4811, + 4813, + 5190, + 5594, + 6120, + 6323, + 6333, + 6654, + 6754, + 6894, + 6956, + 8469, + 8620, + 10427, + 10479, + 11334, + 13361, + 14040, + 14766, + 16027, + 16488, + 16925, + 16927, + 18304, + 18609, + 22191, + 24885, + 24971, + 30499, + 32737, + 32933, + 37617, + 43882, + 45221, + 48604, + 53361, + 59577, + 67584, + 99790, + 131866, + 146326, + 169577, + 259962, + 314413, + 2881228, + 11, + 90, + 130, + 300, + 403, + 463, + 480, + 840, + 916, + 1011, + 1023, + 1035, + 1602, + 2221, + 2298, + 2710, + 3120, + 3382, + 3456, + 3718, + 4448, + 4496, + 4875, + 5096, + 5849, + 5940, + 6208, + 6498, + 7023, + 7490, + 7592, + 7755, + 7875, + 8379, + 8991, + 9168, + 9180, + 9714, + 10300, + 10530, + 12612, + 12613, + 14030, + 14576, + 15688, + 17774, + 19143, + 20810, + 22736, + 23965, + 26394, + 29040, + 30477, + 30489, + 31720, + 39920, + 40392, + 43256, + 43305, + 43345, + 43357, + 47777, + 48695, + 50150, + 58368, + 59790, + 65145, + 78260, + 83617, + 94254, + 101208, + 102473, + 110022, + 113048, + 118141, + 119459, + 127416, + 132112, + 147024, + 152533, + 154570, + 178965, + 182574, + 185099, + 187236, + 187968, + 188780, + 203770, + 210725, + 228952, + 228988, + 229044, + 247107, + 271063, + 283189, + 283914, + 293410, + 295358, + 302709, + 319705, + 346211, + 409850, + 433829, + 518476, + 648112, + 648715, + 834722, + 858766, + 1024230, + 1632480, + 1697945, + 2087795, + 2192958, + 4313301, + 2.4999999e-05, + 2.9000001e-05, + 3.7999998e-05, + 4.7000001e-05, + 5.3e-05, + 5.6000001e-05, + 5.9000002e-05, + 7.8999998e-05, + 7.9999998e-05, + 0.000109, + 0.00012899999, + 0.000157, + 0.00017499999, + 0.000181, + 0.000199, + 0.000256, + 0.00028000001, + 0.000283, + 0.00028400001, + 0.000287, + 0.000397, + 0.000401, + 0.00045200001, + 0.00047299999, + 0.00050600001, + 0.00053899997, + 0.00056000001, + 0.00062599999, + 0.00067400001, + 0.00072399998, + 0.00079299998, + 0.00090599997, + 0.001052, + 0.001054, + 0.001064, + 0.00107, + 0.001084, + 0.00122, + 0.001233, + 0.001236, + 0.001297, + 0.001321, + 0.001404, + 0.001618, + 0.001706, + 0.001785, + 0.001862, + 0.0020570001, + 0.0021569999, + 0.002267, + 0.0025269999, + 0.002664, + 0.00278, + 0.0029440001, + 0.003064, + 0.0030789999, + 0.0031069999, + 0.003267, + 0.0032800001, + 0.003491, + 0.0038409999, + 0.0039599999, + 0.0040600002, + 0.0042429999, + 0.004373, + 0.0047650002, + 0.0049749999, + 0.0053010001, + 0.005316, + 0.0054930001, + 0.0055149999, + 0.005903, + 0.0061130002, + 0.0071859998, + 0.007557, + 0.0079190005, + 0.0082029998, + 0.0083109997, + 0.0085450001, + 0.0088409996, + 0.008986, + 0.0096119996, + 0.010158, + 0.010454, + 0.010491, + 0.010588, + 0.011062, + 0.011189, + 0.01136, + 0.011947, + 0.012388, + 0.01285, + 0.013316, + 0.014028, + 0.014862, + 0.017344, + 0.017529, + 0.018440999, + 0.019753, + 0.020732, + 0.021379, + 0.024308, + 0.028986, + 0.029084001, + 0.029973, + 0.031771, + 0.032212, + 0.032758001, + 0.036036, + 0.036471002, + 0.048811998, + 0.048967998, + 0.057353001, + 0.064516, + 0.087884001, + 0.101821, + 0.13953499, + 0.14706101, + 0.229167, + 0.231547, + 0.258656, + 0.26689899, + 0.329083, + 0.34957099, + 0.35977599, + 0.42875499, + 0.91632003, + 0.073445998, + 0.233594, + 0.27638501, + 0.38584599, + 0.39129001, + 0.39430001, + 0.47627199, + 0.49999601, + 0.64261901, + 0.75658399, + 0.767389, + 0.84350997, + 1.015707, + 1.1055419, + 1.15354, + 1.4087991, + 1.722888, + 1.833473, + 1.891134, + 1.944816, + 2.2556751, + 2.4810159, + 2.66401, + 3.046876, + 3.5996871, + 3.6403749, + 4.273489, + 4.50634, + 5.4227982, + 5.6764622, + 6.6741581, + 6.9359412, + 6.9459491, + 9.0884533, + 9.3204279, + 11.047834, + 11.05003, + 11.34829, + 11.977816, + 12.56616, + 12.896968, + 13.082605, + 13.121695, + 15.218937, + 15.219805, + 15.422595, + 15.908748, + 16.333834, + 16.442045, + 17.222109, + 18.255583, + 18.337471, + 18.482592, + 19.853386, + 20.08359, + 20.493109, + 20.575439, + 21.781368, + 21.850649, + 23.025219, + 23.394861, + 23.543165, + 24.943169, + 26.903715, + 29.262121, + 30.220488, + 30.346951, + 30.993811, + 31.029835, + 32.219078, + 34.479328, + 39.028454, + 39.205608, + 39.3573, + 43.941006, + 44.133533, + 49.547768, + 52.339855, + 53.529537, + 56.239407, + 57.230946, + 60.911472, + 61.411575, + 66.006073, + 75.513832, + 76.959404, + 79.399498, + 94.297729, + 101.50806, + 104.90943, + 108.45773, + 125.96421, + 131.78522, + 149.06885, + 149.10501, + 159.80826, + 166.47375, + 190.44113, + 193.85234, + 214.411, + 228.99097, + 243.77342, + 251.07321, + 265.90009, + 277.75555, + 290.5871, + 321.94229, + 349.17996, + 435.58398, + 461.15714, + 480.75259, + 555.98761, + 556.20483, + 632.65863, + 828.06183, + 836.67456, + 857.39771, + 1038.6504, + 1445.1395, + 2359.3203, + 2750.1714, + 3077.2048, + 3270.0989, + 3929.8506, + 3954.1118, + 5246.2725, + 7567.3979, + 14218.984, + 0.0041490002, + 0.036821999, + 0.049214002, + 0.130913, + 0.177389, + 0.18044201, + 0.183018, + 0.19227199, + 0.244839, + 0.30151099, + 0.33716401, + 0.33797899, + 0.33922401, + 0.376284, + 0.436288, + 0.488325, + 0.50635898, + 0.51155603, + 0.53070199, + 0.55729002, + 0.576186, + 0.60621798, + 0.62735999, + 0.63285798, + 0.64827198, + 0.69477397, + 0.727198, + 0.74622601, + 0.76021701, + 0.77570999, + 0.817267, + 0.84039903, + 0.874892, + 0.88309401, + 0.91301, + 0.914644, + 0.92933702, + 0.94280899, + 1.017756, + 1.11876, + 1.1953419, + 1.205152, + 1.207976, + 1.208634, + 1.295553, + 1.2959141, + 1.326723, + 1.347288, + 1.3574491, + 1.390246, + 1.390783, + 1.428421, + 1.4407949, + 1.45542, + 1.502932, + 1.5636179, + 1.581139, + 1.78348, + 1.78504, + 1.801465, + 1.873001, + 2.0050731, + 2.062494, + 2.1306751, + 2.1410699, + 2.142792, + 2.1604609, + 2.301403, + 2.3202839, + 2.320334, + 2.342221, + 2.3494289, + 2.392698, + 2.4694431, + 2.4709809, + 2.548645, + 2.7524309, + 2.8433869, + 2.8817151, + 2.9853411, + 3.0516059, + 3.0590241, + 3.1028869, + 3.2358611, + 3.3461559, + 3.357758, + 3.364897, + 3.3847311, + 3.4308331, + 3.538095, + 3.712795, + 3.7464709, + 3.7710979, + 4.0210929, + 4.3894181, + 4.4247661, + 4.4356871, + 4.64078, + 4.7781639, + 5.100903, + 5.298172, + 5.2985978, + 5.4433179, + 5.4726572, + 5.5151229, + 5.6058269, + 5.6186318, + 5.738615, + 5.7462459, + 5.792984, + 5.9997821, + 6.0248361, + 6.1309428, + 6.1651001, + 6.6284609, + 7.162158, + 7.2263432, + 7.5340571, + 7.7937908, + 8.3784914, + 9.5885639, + 9.5996771, + 9.8454266, + 9.853548, + 10.502564, + 12.32793, + 13.122874, + 14.426889, + 15.470113, + 16.773743, + 19.316851, + 19.475534, + 22.584642, + 28.079082, + 46.668407, + 9, + 12, + 15, + 18, + 21, + 24, + 27, + 30, + 60, + 90, + 120, + 150, + 1620, + 7800, + 13500, + 24200, + 25700, + 28800, + 45400, + 55000, + 60400, + 61900, + 72000, + 96100, + 104000, + 106000, + 133000, + 137000, + 138000, + 152000, + 155000, + 177000, + 191000, + 222000, + 240000, + 264000, + 288000, + 304000, + 333000, + 345000, + 391000, + 408000, + 425000, + 449000, + 468000, + 518000, + 554000, + 583000, + 665000, + 719000, + 731000, + 756000, + 757000, + 772000, + 837000, + 931000, + 975000, + 1050000, + 1060000, + 1120000, + 1150000, + 1160000, + 1210000, + 1350000, + 1440000, + 1740000, + 1820000, + 1830000, + 1890000, + 2020000, + 2400000, + 2430000, + 2550000, + 2600000, + 2680000, + 2760000, + 2870000, + 2920000, + 3050000, + 3310000, + 3410000, + 3500000, + 3590000, + 3630000, + 3740000, + 3850000, + 3910000, + 4360000, + 4570000, + 4760000, + 5080000, + 5660000, + 6060000, + 6070000, + 6150000, + 6490000, + 6500000, + 6600000, + 6780000, + 7090000, + 7170000, + 8100000, + 8760000, + 9040000, + 9270000, + 10700000, + 11300000, + 11600000, + 12100000, + 12600000, + 12700000, + 13700000, + 14100000, + 15200000, + 15900000, + 16500000, + 17100000, + 17600000, + 17900000, + 19200000, + 20800000, + 22500000, + 23200000, + 26000000, + 26800000, + 27800000, + 31100000, + 31400000, + 31600000, + 31700000, + 34300000, + 34400000, + 37100000, + 39800000, + 42800000, + 44300000, + 45400000, + 51500000, + 51900000, + 56600000, + 61500000, + 65100000, + 77500000, + 78400000, + 97200000, + 1.07e+08, + 1.24e+08, + 1.29e+08, + 1.34e+08, + 1.54e+08, + 2.14e+08, + 2.49e+08, + 3.13e+08, + 3.34e+08, + 5.06e+08, + 1.34e+09, +}; + +static const int th_begin[] = { + 0, + 138, + 276, + 390, + 517, + 645, + 780, + 792, +}; + +static const int th_len[] = { + 138, + 138, + 114, + 127, + 128, + 135, + 12, + 144, +}; + +/* + * \brief Function to convert a feature value into bin index. + * \param val Feature value, in floating-point + * \param fid Feature identifier + * \return bin Index corresponding to given feature value + */ +int pdlp_predictor::quantize(float val, unsigned fid) +{ + const size_t offset = th_begin[fid]; + const float* array = &threshold[offset]; + int len = th_len[fid]; + int low = 0; + int high = len; + int mid; + float mval; + // It is possible th_begin[i] == [total_num_threshold]. This means that + // all features i, (i+1), ... are not used for any of the splits in the model. + // So in this case, just return something + if (offset == 936 || val < array[0]) { return -10; } + while (low + 1 < high) { + mid = (low + high) / 2; + mval = array[mid]; + if (val == mval) { + return mid * 2; + } else if (val < mval) { + high = mid; + } else { + low = mid; + } + } + if (array[low] == val) { + return low * 2; + } else if (high == len) { + return len * 2; + } else { + return low * 2 + 1; + } +} diff --git a/cpp/src/utilities/version_info.cpp b/cpp/src/utilities/version_info.cpp index ec9db5130b..a14c9fa274 100644 --- a/cpp/src/utilities/version_info.cpp +++ b/cpp/src/utilities/version_info.cpp @@ -163,6 +163,36 @@ static double get_available_memory_gb() return kb / (1024.0 * 1024.0); // Convert KB to GB } +double get_cpu_max_clock_mhz() +{ + // Try sysfs cpufreq interface first (returns frequency in KHz) + // FIXME: assumes all available CPUs have the same max clock as CPU0 + std::ifstream freq_file("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"); + if (freq_file.is_open()) { + long khz = 0; + freq_file >> khz; + if (khz > 0) { return khz / 1e3; } + } + + // Fallback: parse /proc/cpuinfo for "cpu MHz" + std::ifstream cpuinfo("/proc/cpuinfo"); + if (!cpuinfo.is_open()) return 0.0; + + std::string line; + double max_mhz = 0.0; + while (std::getline(cpuinfo, line)) { + if (line.find("cpu MHz") != std::string::npos) { + std::size_t colon = line.find(':'); + if (colon != std::string::npos) { + double mhz = std::stod(line.substr(colon + 1)); + if (mhz > max_mhz) { max_mhz = mhz; } + } + } + } + + return max_mhz; +} + void print_version_info() { int device_id = 0; diff --git a/cpp/src/utilities/version_info.hpp b/cpp/src/utilities/version_info.hpp index dbadea8ecd..ea909e7c19 100644 --- a/cpp/src/utilities/version_info.hpp +++ b/cpp/src/utilities/version_info.hpp @@ -8,4 +8,5 @@ namespace cuopt { void print_version_info(); -} +double get_cpu_max_clock_mhz(); +} // namespace cuopt diff --git a/cpp/src/utilities/work_unit_predictor.cpp b/cpp/src/utilities/work_unit_predictor.cpp index 68d2a8f4d7..71ed55c94a 100644 --- a/cpp/src/utilities/work_unit_predictor.cpp +++ b/cpp/src/utilities/work_unit_predictor.cpp @@ -22,10 +22,13 @@ #include #include #include +#include #include #include "models/cpufj_predictor/header.h" +#include "models/dualsimplex_predictor/header.h" #include "models/fj_predictor/header.h" +#include "models/pdlp_predictor/header.h" namespace cuopt { @@ -48,6 +51,8 @@ template float work_unit_predictor_t::predict_scalar( const std::map& features) const { + raft::common::nvtx::range range("work_unit_predictor_t::predict_scalar"); + typename model_t::Entry data[model_t::NUM_FEATURES]; for (int i = 0; i < model_t::NUM_FEATURES; ++i) { if (features.find(std::string(model_t::feature_names[i])) == features.end()) { @@ -78,10 +83,12 @@ float work_unit_predictor_t::predict_scalar( std::chrono::duration elapsed = end - start; CUOPT_LOG_DEBUG("Prediction time: %f ms", elapsed.count()); CUOPT_LOG_DEBUG("Result: %f", result); - return std::abs(result); + return result; } template class work_unit_predictor_t; template class work_unit_predictor_t; +template class work_unit_predictor_t; +template class work_unit_predictor_t; } // namespace cuopt From ecf9f25e1d70a9d7ef4b7213312e870bdc0d59de Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 27 Nov 2025 18:18:29 +0000 Subject: [PATCH 094/225] improved dualsimplex instrumentation --- cpp/src/dual_simplex/barrier.cu | 2 +- cpp/src/dual_simplex/cusparse_view.cu | 6 +- cpp/src/dual_simplex/phase2.cpp | 171 ++++++++++--------- cpp/src/dual_simplex/phase2.hpp | 1 + cpp/src/dual_simplex/singletons.cpp | 8 +- cpp/src/dual_simplex/sparse_vector.cpp | 6 +- cpp/src/mip/relaxed_lp/relaxed_lp.cu | 10 +- cpp/src/utilities/memory_instrumentation.hpp | 150 +++++++++------- 8 files changed, 197 insertions(+), 157 deletions(-) diff --git a/cpp/src/dual_simplex/barrier.cu b/cpp/src/dual_simplex/barrier.cu index d194b9be1b..5af03759d7 100644 --- a/cpp/src/dual_simplex/barrier.cu +++ b/cpp/src/dual_simplex/barrier.cu @@ -1260,7 +1260,7 @@ class iteration_data_t { // v = alpha * A * w + beta * v = alpha * A * Dinv * A^T * y + beta * v matrix_vector_multiply(A, alpha, w, beta, v); if (debug) { - printf("||A|| = %.16e\n", vector_norm2(A.x.array)); + printf("||A|| = %.16e\n", vector_norm2(A.x.underlying())); printf("||w|| = %.16e\n", vector_norm2(w)); printf("||v|| = %.16e\n", vector_norm2(v)); } diff --git a/cpp/src/dual_simplex/cusparse_view.cu b/cpp/src/dual_simplex/cusparse_view.cu index d9bae07392..282c6ca2b0 100644 --- a/cpp/src/dual_simplex/cusparse_view.cu +++ b/cpp/src/dual_simplex/cusparse_view.cu @@ -148,9 +148,9 @@ cusparse_view_t::cusparse_view_t(raft::handle_t const* handle_ptr, A_indices_ = device_copy(indices, handle_ptr->get_stream()); A_data_ = device_copy(data, handle_ptr->get_stream()); - A_T_offsets_ = device_copy(A.col_start.array, handle_ptr->get_stream()); - A_T_indices_ = device_copy(A.i.array, handle_ptr->get_stream()); - A_T_data_ = device_copy(A.x.array, handle_ptr->get_stream()); + A_T_offsets_ = device_copy(A.col_start.underlying(), handle_ptr->get_stream()); + A_T_indices_ = device_copy(A.i.underlying(), handle_ptr->get_stream()); + A_T_data_ = device_copy(A.x.underlying(), handle_ptr->get_stream()); cusparseCreateCsr(&A_, rows, diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 7cd1c420fd..1403f9cbf1 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -663,8 +663,8 @@ void update_single_primal_infeasibility(const std::vector& lower, const std::vector& upper, const std::vector& x, f_t primal_tol, - ins_vector& squared_infeasibilities, - ins_vector& infeasibility_indices, + std::vector& squared_infeasibilities, + std::vector& infeasibility_indices, i_t j, f_t& primal_inf) { @@ -705,9 +705,9 @@ void update_primal_infeasibilities(const lp_problem_t& lp, const std::vector& x, i_t entering_index, i_t leaving_index, - ins_vector& basic_change_list, - ins_vector& squared_infeasibilities, - ins_vector& infeasibility_indices, + std::vector& basic_change_list, + std::vector& squared_infeasibilities, + std::vector& infeasibility_indices, f_t& primal_inf) { const f_t primal_tol = settings.primal_tol; @@ -764,7 +764,7 @@ void clean_up_infeasibilities(ins_vector& squared_infeasibilities, template i_t steepest_edge_pricing_with_infeasibilities(const lp_problem_t& lp, const simplex_solver_settings_t& settings, - const std::vector& x, + const ins_vector& x, const std::vector& dy_steepest_edge, const ins_vector& basic_mark, ins_vector& squared_infeasibilities, @@ -866,7 +866,7 @@ i_t steepest_edge_pricing(const lp_problem_t& lp, template i_t phase2_pricing(const lp_problem_t& lp, const simplex_solver_settings_t& settings, - const std::vector& x, + const ins_vector& x, const std::vector& basic_list, i_t& direction, i_t& basic_leaving, @@ -911,7 +911,7 @@ template f_t first_stage_harris(const lp_problem_t& lp, const std::vector& vstatus, const std::vector& nonbasic_list, - std::vector& z, + ins_vector& z, ins_vector& delta_z) { const i_t n = lp.num_cols; @@ -945,7 +945,7 @@ template i_t second_stage_harris(const lp_problem_t& lp, const std::vector& vstatus, const std::vector& nonbasic_list, - const std::vector& z, + const ins_vector& z, const ins_vector& delta_z, f_t max_step_length, f_t& step_length, @@ -986,9 +986,9 @@ i_t second_stage_harris(const lp_problem_t& lp, template i_t phase2_ratio_test(const lp_problem_t& lp, const simplex_solver_settings_t& settings, - const std::vector& vstatus, - const std::vector& nonbasic_list, - std::vector& z, + std::vector& vstatus, + std::vector& nonbasic_list, + ins_vector& z, ins_vector& delta_z, f_t& step_length, i_t& nonbasic_entering) @@ -1040,8 +1040,8 @@ template i_t flip_bounds(const lp_problem_t& lp, const simplex_solver_settings_t& settings, const ins_vector& bounded_variables, - const std::vector& objective, - const std::vector& z, + const ins_vector& objective, + const ins_vector& z, const ins_vector& delta_z_indices, const std::vector& nonbasic_list, i_t entering_index, @@ -1377,8 +1377,8 @@ template i_t compute_perturbation(const lp_problem_t& lp, const simplex_solver_settings_t& settings, const ins_vector& delta_z_indices, - std::vector& z, - std::vector& objective, + ins_vector& z, + ins_vector& objective, f_t& sum_perturb) { const i_t n = lp.num_cols; @@ -1498,8 +1498,8 @@ i_t update_dual_variables(const sparse_vector_t& delta_y_sparse, const ins_vector& delta_z, f_t step_length, i_t leaving_index, - std::vector& y, - std::vector& z) + ins_vector& y, + ins_vector& z) { // Update dual variables // y <- y + steplength * delta_y @@ -1528,7 +1528,7 @@ void adjust_for_flips(const basis_update_mpf_t& ft, sparse_vector_t& atilde_sparse, sparse_vector_t& delta_xB_0_sparse, ins_vector& delta_x_flip, - std::vector& x) + ins_vector& x) { const i_t atilde_nz = atilde_index.size(); // B*delta_xB_0 = atilde @@ -1573,7 +1573,7 @@ i_t compute_delta_x(const lp_problem_t& lp, const std::vector& basic_list, const ins_vector& delta_x_flip, const sparse_vector_t& rhs_sparse, - const std::vector& x, + const ins_vector& x, sparse_vector_t& utilde_sparse, sparse_vector_t& scaled_delta_xB_sparse, ins_vector& delta_x) @@ -1636,7 +1636,7 @@ void update_primal_variables(const sparse_vector_t& scaled_delta_xB_sp const std::vector& basic_list, const ins_vector& delta_x, i_t entering_index, - std::vector& x) + ins_vector& x) { // x <- x + delta_x const i_t scaled_delta_xB_nz = scaled_delta_xB_sparse.i.size(); @@ -2008,7 +2008,7 @@ void set_primal_variables_on_bounds(const lp_problem_t& lp, } template -f_t compute_perturbed_objective(const std::vector& objective, const std::vector& x) +f_t compute_perturbed_objective(const ins_vector& objective, const ins_vector& x) { const size_t n = objective.size(); f_t obj_val = 0.0; @@ -2019,7 +2019,7 @@ f_t compute_perturbed_objective(const std::vector& objective, const std::ve } template -f_t amount_of_perturbation(const lp_problem_t& lp, const std::vector& objective) +f_t amount_of_perturbation(const lp_problem_t& lp, const ins_vector& objective) { f_t perturbation = 0.0; const i_t n = lp.num_cols; @@ -2033,7 +2033,7 @@ template void prepare_optimality(const lp_problem_t& lp, const simplex_solver_settings_t& settings, basis_update_mpf_t& ft, - const std::vector& objective, + const ins_vector& objective, const std::vector& basic_list, const std::vector& nonbasic_list, const std::vector& vstatus, @@ -2041,9 +2041,9 @@ void prepare_optimality(const lp_problem_t& lp, f_t start_time, f_t max_val, i_t iter, - const std::vector& x, - std::vector& y, - std::vector& z, + const ins_vector& x, + ins_vector& y, + ins_vector& z, lp_solution_t& sol) { const i_t m = lp.num_rows; @@ -2074,8 +2074,8 @@ void prepare_optimality(const lp_problem_t& lp, sol.l2_primal_residual = l2_primal_residual(lp, sol); sol.l2_dual_residual = l2_dual_residual(lp, sol); - const f_t dual_infeas = phase2::dual_infeasibility(lp, settings, vstatus, z, 0.0, 0.0); - const f_t primal_infeas = phase2::primal_infeasibility(lp, settings, vstatus, x); + const f_t dual_infeas = phase2::dual_infeasibility(lp, settings, vstatus, sol.z, 0.0, 0.0); + const f_t primal_infeas = phase2::primal_infeasibility(lp, settings, vstatus, sol.x); if (phase == 1 && iter > 0) { settings.log.printf("Dual phase I complete. Iterations %d. Time %.2f\n", iter, toc(start_time)); } @@ -2139,20 +2139,20 @@ class phase2_timers_t { delta_z_time + lu_update_time + se_norms_time + se_entering_time + perturb_time + vector_time + objective_time + update_infeasibility_time; // clang-format off - settings.log.printf("BFRT time %.2fs %4.1f%\n", bfrt_time, 100.0 * bfrt_time / total_time); - settings.log.printf("Pricing time %.2fs %4.1f%\n", pricing_time, 100.0 * pricing_time / total_time); - settings.log.printf("BTran time %.2fs %4.1f%\n", btran_time, 100.0 * btran_time / total_time); - settings.log.printf("FTran time %.2fs %4.1f%\n", ftran_time, 100.0 * ftran_time / total_time); - settings.log.printf("Flip time %.2fs %4.1f%\n", flip_time, 100.0 * flip_time / total_time); - settings.log.printf("Delta_z time %.2fs %4.1f%\n", delta_z_time, 100.0 * delta_z_time / total_time); - settings.log.printf("LU update time %.2fs %4.1f%\n", lu_update_time, 100.0 * lu_update_time / total_time); - settings.log.printf("SE norms time %.2fs %4.1f%\n", se_norms_time, 100.0 * se_norms_time / total_time); - settings.log.printf("SE enter time %.2fs %4.1f%\n", se_entering_time, 100.0 * se_entering_time / total_time); - settings.log.printf("Perturb time %.2fs %4.1f%\n", perturb_time, 100.0 * perturb_time / total_time); - settings.log.printf("Vector time %.2fs %4.1f%\n", vector_time, 100.0 * vector_time / total_time); - settings.log.printf("Objective time %.2fs %4.1f%\n", objective_time, 100.0 * objective_time / total_time); - settings.log.printf("Inf update time %.2fs %4.1f%\n", update_infeasibility_time, 100.0 * update_infeasibility_time / total_time); - settings.log.printf("Sum %.2fs\n", total_time); + printf("BFRT time %.2fs %4.1f%\n", bfrt_time, 100.0 * bfrt_time / total_time); + printf("Pricing time %.2fs %4.1f%\n", pricing_time, 100.0 * pricing_time / total_time); + printf("BTran time %.2fs %4.1f%\n", btran_time, 100.0 * btran_time / total_time); + printf("FTran time %.2fs %4.1f%\n", ftran_time, 100.0 * ftran_time / total_time); + printf("Flip time %.2fs %4.1f%\n", flip_time, 100.0 * flip_time / total_time); + printf("Delta_z time %.2fs %4.1f%\n", delta_z_time, 100.0 * delta_z_time / total_time); + printf("LU update time %.2fs %4.1f%\n", lu_update_time, 100.0 * lu_update_time / total_time); + printf("SE norms time %.2fs %4.1f%\n", se_norms_time, 100.0 * se_norms_time / total_time); + printf("SE enter time %.2fs %4.1f%\n", se_entering_time, 100.0 * se_entering_time / total_time); + printf("Perturb time %.2fs %4.1f%\n", perturb_time, 100.0 * perturb_time / total_time); + printf("Vector time %.2fs %4.1f%\n", vector_time, 100.0 * vector_time / total_time); + printf("Objective time %.2fs %4.1f%\n", objective_time, 100.0 * objective_time / total_time); + printf("Inf update time %.2fs %4.1f%\n", update_infeasibility_time, 100.0 * update_infeasibility_time / total_time); + printf("Sum %.2fs\n", total_time); // clang-format on } f_t bfrt_time; @@ -2237,16 +2237,18 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, assert(lp.upper.size() == n); assert(lp.rhs.size() == m); - std::vector& x = sol.x; - std::vector& y = sol.y; - std::vector& z = sol.z; + // Create instrumented wrappers around sol vectors (no ownership transfer) + ins_vector x, y, z; + x.wrap(sol.x); + y.wrap(sol.y); + z.wrap(sol.z); dual::status_t status = dual::status_t::UNSET; raft::common::nvtx::push_range("DualSimplex::phase2_advanced_init"); - // Perturbed objective - std::vector objective = lp.objective; + // Perturbed objective (instrumented for main loop tracking) + ins_vector objective(lp.objective); settings.log.printf("Dual Simplex Phase %d\n", phase); std::vector vstatus_old = vstatus; @@ -2275,7 +2277,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } // Solve B'*y = cB - ft.b_transpose_solve(c_basic, y); + ft.b_transpose_solve(c_basic, sol.y); if (toc(start_time) > settings.time_limit) { return dual::status_t::TIME_LIMIT; } constexpr bool print_norms = false; if constexpr (print_norms) { @@ -2283,7 +2285,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, "|| y || %e || cB || %e\n", vector_norm_inf(y), vector_norm_inf(c_basic)); } - phase2::compute_reduced_costs(objective, lp.A, y, basic_list, nonbasic_list, z); + phase2::compute_reduced_costs( + objective.underlying(), lp.A, sol.y, basic_list, nonbasic_list, sol.z); if constexpr (print_norms) { settings.log.printf("|| z || %e\n", vector_norm_inf(z)); } #ifdef COMPUTE_DUAL_RESIDUAL @@ -2296,7 +2299,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, assert(dual_res_norm < 1e-3); #endif - phase2::set_primal_variables_on_bounds(lp, settings, z, vstatus, x); + phase2::set_primal_variables_on_bounds(lp, settings, sol.z, vstatus, sol.x); #ifdef PRINT_VSTATUS_CHANGES i_t num_vstatus_changes; @@ -2307,7 +2310,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, #endif const f_t init_dual_inf = - phase2::dual_infeasibility(lp, settings, vstatus, z, settings.tight_tol, settings.dual_tol); + phase2::dual_infeasibility(lp, settings, vstatus, sol.z, settings.tight_tol, settings.dual_tol); if (init_dual_inf > settings.dual_tol) { settings.log.printf("Initial dual infeasibility %e\n", init_dual_inf); } @@ -2319,10 +2322,10 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } phase2::compute_primal_variables( - ft, lp.rhs, lp.A, basic_list, nonbasic_list, settings.tight_tol, x); + ft, lp.rhs, lp.A, basic_list, nonbasic_list, settings.tight_tol, sol.x); if (toc(start_time) > settings.time_limit) { return dual::status_t::TIME_LIMIT; } - if (print_norms) { settings.log.printf("|| x || %e\n", vector_norm2(x)); } + if (print_norms) { settings.log.printf("|| x || %e\n", vector_norm2(sol.x)); } #ifdef COMPUTE_PRIMAL_RESIDUAL std::vector residual = lp.rhs; @@ -2380,7 +2383,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, phase2::compute_bounded_info(lp.lower, lp.upper, bounded_variables); f_t primal_infeasibility = phase2::compute_initial_primal_infeasibilities( - lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); + lp, settings, basic_list, sol.x, squared_infeasibilities, infeasibility_indices); #ifdef CHECK_BASIC_INFEASIBILITIES phase2::check_basic_infeasibilities(basic_list, basic_mark, infeasibility_indices, 0); @@ -2389,7 +2392,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, csc_matrix_t A_transpose(1, 1, 0); lp.A.transpose(A_transpose); - f_t obj = compute_objective(lp, x); + f_t obj = compute_objective(lp, sol.x); raft::common::nvtx::pop_range(); @@ -2400,7 +2403,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, i_t num_refactors = 0; i_t total_bound_flips = 0; f_t delta_y_nz_percentage = 0.0; - phase2::phase2_timers_t timers(false); + phase2::phase2_timers_t timers(true); // Feature collection for regression training dual_simplex_features_t features; @@ -2434,6 +2437,10 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, manifold.add("squared_infeasibilities", squared_infeasibilities); manifold.add("infeasibility_indices", infeasibility_indices); manifold.add("bounded_variables", bounded_variables); + // Ratio test vectors + // manifold.add("vstatus", vstatus); + // manifold.add("nonbasic_list", nonbasic_list); + manifold.add("z", z); // Add sparse vector internal arrays to manifold manifold.add("delta_y_sparse.i", delta_y_sparse.i); @@ -2614,12 +2621,12 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, slope, lp.lower, lp.upper, - bounded_variables, + bounded_variables.underlying(), vstatus, nonbasic_list, - z, - delta_z, - delta_z_indices, + sol.z, + delta_z.underlying(), + delta_z_indices.underlying(), nonbasic_mark); entering_index = bfrt.compute_step_length(step_length, nonbasic_entering_index); if (entering_index == -4) { @@ -2658,9 +2665,9 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, std::vector unperturbed_x(n); phase2::compute_primal_solution_from_basis( lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); - x = unperturbed_x; + sol.x = unperturbed_x; primal_infeasibility = phase2::compute_initial_primal_infeasibilities( - lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); + lp, settings, basic_list, sol.x, squared_infeasibilities, infeasibility_indices); settings.log.printf("Updated primal infeasibility: %e\n", primal_infeasibility); objective = lp.objective; @@ -2695,12 +2702,12 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, std::vector unperturbed_x(n); phase2::compute_primal_solution_from_basis( lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); - x = unperturbed_x; + sol.x = unperturbed_x; primal_infeasibility = phase2::compute_initial_primal_infeasibilities( - lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); + lp, settings, basic_list, sol.x, squared_infeasibilities, infeasibility_indices); const f_t orig_dual_infeas = phase2::dual_infeasibility( - lp, settings, vstatus, z, settings.tight_tol, settings.dual_tol); + lp, settings, vstatus, sol.z, settings.tight_tol, settings.dual_tol); if (primal_infeasibility <= settings.primal_tol && orig_dual_infeas <= settings.dual_tol) { @@ -2757,10 +2764,10 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } } - const f_t dual_infeas = - phase2::dual_infeasibility(lp, settings, vstatus, z, settings.tight_tol, settings.dual_tol); + const f_t dual_infeas = phase2::dual_infeasibility( + lp, settings, vstatus, sol.z, settings.tight_tol, settings.dual_tol); settings.log.printf("Dual infeasibility %e\n", dual_infeas); - const f_t primal_inf = phase2::primal_infeasibility(lp, settings, vstatus, x); + const f_t primal_inf = phase2::primal_infeasibility(lp, settings, vstatus, sol.x); settings.log.printf("Primal infeasibility %e\n", primal_inf); settings.log.printf("Updates %d\n", ft.num_updates()); settings.log.printf("Steepest edge %e\n", max_val); @@ -2912,32 +2919,32 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, phase2::update_primal_infeasibilities(lp, settings, basic_list, - x, + sol.x, entering_index, leaving_index, - delta_xB_0_sparse.i, - squared_infeasibilities, - infeasibility_indices, + delta_xB_0_sparse.i.underlying(), + squared_infeasibilities.underlying(), + infeasibility_indices.underlying(), primal_infeasibility); // Update primal infeasibilities due to changes in basic variables // from the leaving and entering variables phase2::update_primal_infeasibilities(lp, settings, basic_list, - x, + sol.x, entering_index, leaving_index, - scaled_delta_xB_sparse.i, - squared_infeasibilities, - infeasibility_indices, + scaled_delta_xB_sparse.i.underlying(), + squared_infeasibilities.underlying(), + infeasibility_indices.underlying(), primal_infeasibility); // Update the entering variable phase2::update_single_primal_infeasibility(lp.lower, lp.upper, - x, + sol.x, settings.primal_tol, - squared_infeasibilities, - infeasibility_indices, + squared_infeasibilities.underlying(), + infeasibility_indices.underlying(), entering_index, primal_infeasibility); @@ -3022,10 +3029,10 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, std::vector unperturbed_x(n); phase2::compute_primal_solution_from_basis( lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); - x = unperturbed_x; + sol.x = unperturbed_x; } phase2::compute_initial_primal_infeasibilities( - lp, settings, basic_list, x, squared_infeasibilities, infeasibility_indices); + lp, settings, basic_list, sol.x, squared_infeasibilities, infeasibility_indices); } #ifdef CHECK_BASIC_INFEASIBILITIES phase2::check_basic_infeasibilities(basic_list, basic_mark, infeasibility_indices, 7); diff --git a/cpp/src/dual_simplex/phase2.hpp b/cpp/src/dual_simplex/phase2.hpp index caeae82e1c..c6ed03c84c 100644 --- a/cpp/src/dual_simplex/phase2.hpp +++ b/cpp/src/dual_simplex/phase2.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include diff --git a/cpp/src/dual_simplex/singletons.cpp b/cpp/src/dual_simplex/singletons.cpp index c4656889b9..71603306ca 100644 --- a/cpp/src/dual_simplex/singletons.cpp +++ b/cpp/src/dual_simplex/singletons.cpp @@ -200,8 +200,8 @@ i_t find_singletons(const csc_matrix_t& A, // Find column singletons row_col_graph_t graph{Cdeg.begin(), col_perm.begin(), - A.col_start.array.cbegin(), - A.i.array.cbegin(), + A.col_start.underlying().cbegin(), + A.i.underlying().cbegin(), Rdeg.begin(), row_perm.begin(), Rp.cbegin(), @@ -235,8 +235,8 @@ i_t find_singletons(const csc_matrix_t& A, Rj.cbegin(), Cdeg.begin(), col_perm.begin(), - A.col_start.array.cbegin(), - A.i.array.cbegin()}; + A.col_start.underlying().cbegin(), + A.i.underlying().cbegin()}; #ifdef SINGLETON_DEBUG printf("Searching for row singletons %ld\n", singleton_queue.size()); #endif diff --git a/cpp/src/dual_simplex/sparse_vector.cpp b/cpp/src/dual_simplex/sparse_vector.cpp index 111d3ce829..79d323d310 100644 --- a/cpp/src/dual_simplex/sparse_vector.cpp +++ b/cpp/src/dual_simplex/sparse_vector.cpp @@ -72,8 +72,8 @@ void sparse_vector_t::to_csc(csc_matrix_t& A) const A.col_start.resize(2); A.col_start[0] = 0; A.col_start[1] = i.size(); - A.i = i.array; - A.x = x.array; + A.i = i.underlying(); + A.x = x.underlying(); } template @@ -201,7 +201,7 @@ void sparse_vector_t::sort() perm[k] = k; } // Need to capture the underlying array for the lambda - auto& iunsorted = i.array; + auto& iunsorted = i; std::sort( perm.begin(), perm.end(), [&iunsorted](i_t a, i_t b) { return iunsorted[a] < iunsorted[b]; }); for (i_t k = 0; k < nz; ++k) { diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index 6a336e7c00..d9c6f45b61 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -188,11 +188,11 @@ optimization_problem_solution_t get_relaxed_lp_solution( : 0.0; // Compute sparsity metrics - const double sparsity = (n_cstrs > 0 && n_vars > 0) - ? static_cast(nnz) / (static_cast(n_cstrs) * n_vars) - : 0.0; - double nnz_stddev = 0.0; - double unbalancedness = 0.0; + const double sparsity = (n_cstrs > 0 && n_vars > 0) + ? static_cast(nnz) / (static_cast(n_cstrs) * n_vars) + : 0.0; + double nnz_stddev = 0.0; + [[maybe_unused]] double unbalancedness = 0.0; if (op_problem.offsets.size() == static_cast(n_cstrs + 1) && n_cstrs > 0) { std::vector h_offsets(n_cstrs + 1); raft::copy(h_offsets.data(), diff --git a/cpp/src/utilities/memory_instrumentation.hpp b/cpp/src/utilities/memory_instrumentation.hpp index 52c07be3db..ca8771abf8 100644 --- a/cpp/src/utilities/memory_instrumentation.hpp +++ b/cpp/src/utilities/memory_instrumentation.hpp @@ -491,28 +491,28 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { using const_reverse_iterator = std::reverse_iterator; // Constructors - memop_instrumentation_wrapper_t() : array() + memop_instrumentation_wrapper_t() : array_(), wrapped_ptr(nullptr) { if constexpr (type_traits_utils::has_data::value) { - data_ptr = array.data(); + data_ptr = array_.data(); } else { data_ptr = nullptr; } } // Copy/move from underlying type - memop_instrumentation_wrapper_t(const T& array) : array(array) + memop_instrumentation_wrapper_t(const T& arr) : array_(arr) { if constexpr (type_traits_utils::has_data::value) { - data_ptr = const_cast(array.data()); + data_ptr = const_cast(array_.data()); } else { data_ptr = nullptr; } } - memop_instrumentation_wrapper_t(T&& array) : array(std::move(array)) + memop_instrumentation_wrapper_t(T&& arr) : array_(std::move(arr)) { if constexpr (type_traits_utils::has_data::value) { - data_ptr = array.data(); + data_ptr = array_.data(); } else { data_ptr = nullptr; } @@ -527,10 +527,10 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { !std::is_same_v, T> && (sizeof...(Args) > 0 || !std::is_convertible_v)>> explicit memop_instrumentation_wrapper_t(Arg&& arg, Args&&... args) - : array(std::forward(arg), std::forward(args)...) + : array_(std::forward(arg), std::forward(args)...) { if constexpr (type_traits_utils::has_data::value) { - data_ptr = array.data(); + data_ptr = array_.data(); } else { data_ptr = nullptr; } @@ -538,10 +538,10 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { // Copy constructor - must update data_ptr to point to our own array memop_instrumentation_wrapper_t(const memop_instrumentation_wrapper_t& other) - : memory_instrumentation_base_t(other), array(other.array) + : memory_instrumentation_base_t(other), array_(other.array_) { if constexpr (type_traits_utils::has_data::value) { - data_ptr = array.data(); + data_ptr = array_.data(); } else { data_ptr = nullptr; } @@ -549,10 +549,10 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { // Move constructor - must update data_ptr to point to our own array memop_instrumentation_wrapper_t(memop_instrumentation_wrapper_t&& other) noexcept - : memory_instrumentation_base_t(std::move(other)), array(std::move(other.array)) + : memory_instrumentation_base_t(std::move(other)), array_(std::move(other.array_)) { if constexpr (type_traits_utils::has_data::value) { - data_ptr = array.data(); + data_ptr = array_.data(); } else { data_ptr = nullptr; } @@ -563,9 +563,9 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { { if (this != &other) { memory_instrumentation_base_t::operator=(other); - array = other.array; + array_ = other.array_; if constexpr (type_traits_utils::has_data::value) { - data_ptr = array.data(); + data_ptr = array_.data(); } else { data_ptr = nullptr; } @@ -578,9 +578,9 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { { if (this != &other) { memory_instrumentation_base_t::operator=(std::move(other)); - array = std::move(other.array); + array_ = std::move(other.array_); if constexpr (type_traits_utils::has_data::value) { - data_ptr = array.data(); + data_ptr = array_.data(); } else { data_ptr = nullptr; } @@ -588,7 +588,10 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { return *this; } - element_proxy_t operator[](size_type index) { return element_proxy_t(array[index], *this); } + element_proxy_t operator[](size_type index) + { + return element_proxy_t(underlying()[index], *this); + } HDI value_type operator[](size_type index) const { @@ -597,47 +600,50 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { if constexpr (type_traits_utils::has_data::value) { return data_ptr[index]; } else { - return array[index]; + return underlying()[index]; } } template std::enable_if_t::value, element_proxy_t> front() { - return element_proxy_t(array.front(), *this); + return element_proxy_t(underlying().front(), *this); } template std::enable_if_t::value, value_type> front() const { this->template record_load(); - return array.front(); + return underlying().front(); } template std::enable_if_t::value, element_proxy_t> back() { - return element_proxy_t(array.back(), *this); + return element_proxy_t(underlying().back(), *this); } template std::enable_if_t::value, value_type> back() const { this->template record_load(); - return array.back(); + return underlying().back(); } // Iterators - iterator begin() noexcept { return iterator(std::begin(array), this); } - const_iterator begin() const noexcept { return const_iterator(std::begin(array), this); } - const_iterator cbegin() const noexcept { return const_iterator(std::begin(array), this); } + iterator begin() noexcept { return iterator(std::begin(underlying()), this); } + const_iterator begin() const noexcept { return const_iterator(std::begin(underlying()), this); } + const_iterator cbegin() const noexcept { return const_iterator(std::begin(underlying()), this); } - iterator end() noexcept { return iterator(std::end(array), this); } - const_iterator end() const noexcept { return const_iterator(std::end(array), this); } - const_iterator cend() const noexcept { return const_iterator(std::end(array), this); } + iterator end() noexcept { return iterator(std::end(underlying()), this); } + const_iterator end() const noexcept { return const_iterator(std::end(underlying()), this); } + const_iterator cend() const noexcept { return const_iterator(std::end(underlying()), this); } reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(std::end(array)); } + const_reverse_iterator rbegin() const noexcept + { + return const_reverse_iterator(std::end(underlying())); + } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } reverse_iterator rend() noexcept { return reverse_iterator(begin()); } @@ -645,41 +651,44 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } // Capacity - bool empty() const noexcept { return std::begin(array) == std::end(array); } - size_type size() const noexcept { return std::distance(std::begin(array), std::end(array)); } + bool empty() const noexcept { return std::begin(underlying()) == std::end(underlying()); } + size_type size() const noexcept + { + return std::distance(std::begin(underlying()), std::end(underlying())); + } // Conditional methods - only available if underlying type supports them template std::enable_if_t::value, size_type> max_size() const noexcept { - return array.max_size(); + return underlying().max_size(); } template std::enable_if_t::value, size_type> capacity() const noexcept { - return array.capacity(); + return underlying().capacity(); } template std::enable_if_t::value> reserve(size_type new_cap) { - array.reserve(new_cap); - if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + underlying().reserve(new_cap); + if constexpr (type_traits_utils::has_data::value) { data_ptr = underlying().data(); } } template std::enable_if_t::value> shrink_to_fit() { - array.shrink_to_fit(); - if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + underlying().shrink_to_fit(); + if constexpr (type_traits_utils::has_data::value) { data_ptr = underlying().data(); } } template std::enable_if_t::value> clear() noexcept { - array.clear(); - if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + underlying().clear(); + if constexpr (type_traits_utils::has_data::value) { data_ptr = underlying().data(); } } template @@ -688,74 +697,97 @@ struct memop_instrumentation_wrapper_t : public memory_instrumentation_base_t { // we should probably take into account possible copies done by std::vector. oh well. // hot loops shouldn't be doing such operations anyway this->template record_store(); - array.push_back(value); - if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + underlying().push_back(value); + if constexpr (type_traits_utils::has_data::value) { data_ptr = underlying().data(); } } template std::enable_if_t::value> push_back(value_type&& value) { this->template record_store(); - array.push_back(std::move(value)); - if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + underlying().push_back(std::move(value)); + if constexpr (type_traits_utils::has_data::value) { data_ptr = underlying().data(); } } template std::enable_if_t::value> emplace_back(Args&&... args) { this->template record_store(); - array.emplace_back(std::forward(args)...); - if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + underlying().emplace_back(std::forward(args)...); + if constexpr (type_traits_utils::has_data::value) { data_ptr = underlying().data(); } } template std::enable_if_t::value> pop_back() { this->template record_load(); // Reading the element before removal - array.pop_back(); - if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + underlying().pop_back(); + if constexpr (type_traits_utils::has_data::value) { data_ptr = underlying().data(); } } template std::enable_if_t::value> resize(size_type count) { - size_type old_size = array.size(); - array.resize(count); + size_type old_size = underlying().size(); + underlying().resize(count); if (count > old_size) { this->byte_stores += (count - old_size) * type_size; // New elements initialized } - if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + if constexpr (type_traits_utils::has_data::value) { data_ptr = underlying().data(); } } template std::enable_if_t::value> resize(size_type count, const value_type& value) { - size_type old_size = array.size(); - array.resize(count, value); + size_type old_size = underlying().size(); + underlying().resize(count, value); if (count > old_size) { this->byte_stores += (count - old_size) * type_size; } - if constexpr (type_traits_utils::has_data::value) { data_ptr = array.data(); } + if constexpr (type_traits_utils::has_data::value) { data_ptr = underlying().data(); } } template std::enable_if_t::value, value_type*> data() noexcept { - return array.data(); + return underlying().data(); } template std::enable_if_t::value, const value_type*> data() const noexcept { - return array.data(); + return underlying().data(); } // Access to underlying array - operator T&() { return array; } - operator const T&() const { return array; } + operator T&() { return underlying(); } + operator const T&() const { return underlying(); } - T&& release_array() { return std::move(array); } + T&& release_array() { return std::move(array_); } - T array; + // Wrap an external vector without taking ownership + void wrap(T& external_array) + { + wrapped_ptr = &external_array; + if constexpr (type_traits_utils::has_data::value) { data_ptr = external_array.data(); } + } + + // Stop wrapping and return to using the owned array + void unwrap() + { + wrapped_ptr = nullptr; + if constexpr (type_traits_utils::has_data::value) { data_ptr = array_.data(); } + } + + // Check if currently wrapping an external array + bool is_wrapping() const { return wrapped_ptr != nullptr; } + + // Get the underlying container (wrapped or owned) + T& underlying() { return wrapped_ptr ? *wrapped_ptr : array_; } + const T& underlying() const { return wrapped_ptr ? *wrapped_ptr : array_; } + + private: + T array_; + T* wrapped_ptr{nullptr}; value_type* data_ptr{nullptr}; }; From fb9c782968bb8db4b10a9540af887a4135a265dd Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 1 Dec 2025 13:55:37 +0000 Subject: [PATCH 095/225] integrate work unit limiting with branch_and_bound --- .../linear_programming/cuopt/run_mip.cpp | 10 +- .../cuopt/linear_programming/constants.h | 4 + .../mip/solver_settings.hpp | 15 ++- cpp/src/dual_simplex/branch_and_bound.cpp | 62 +++++++--- cpp/src/dual_simplex/branch_and_bound.hpp | 12 +- cpp/src/dual_simplex/phase2.cpp | 110 ++++++++++-------- cpp/src/dual_simplex/phase2.hpp | 11 +- .../dual_simplex/simplex_solver_settings.hpp | 5 +- cpp/src/dual_simplex/solve.cpp | 2 + cpp/src/dual_simplex/solve.hpp | 3 +- cpp/src/math_optimization/solver_settings.cu | 6 +- cpp/src/mip/diversity/diversity_manager.cu | 26 ++--- .../diversity/recombiners/fp_recombiner.cuh | 2 +- .../mip/diversity/recombiners/recombiner.cuh | 4 +- .../mip/feasibility_jump/feasibility_jump.cu | 8 +- .../feasibility_jump_kernels.cu | 12 +- .../feasibility_pump/feasibility_pump.cu | 8 +- .../line_segment_search.cu | 4 +- cpp/src/mip/local_search/local_search.cu | 16 ++- .../local_search/rounding/constraint_prop.cu | 6 +- cpp/src/mip/presolve/bounds_presolve.cu | 2 +- cpp/src/mip/presolve/utils.cuh | 1 + cpp/src/mip/relaxed_lp/relaxed_lp.cu | 22 ++-- cpp/src/mip/solver.cu | 55 +++++---- cpp/src/mip/solver_context.cuh | 10 +- .../models/dualsimplex_predictor/main.cpp | 4 +- .../utilities/models/pdlp_predictor/main.cpp | 4 +- cpp/src/utilities/work_limit_timer.hpp | 4 +- cpp/src/utilities/work_unit_predictor.cpp | 28 ++--- cpp/src/utilities/work_unit_predictor.hpp | 29 ++++- cpp/tests/mip/diversity_test.cu | 32 ++--- cpp/tests/mip/feasibility_jump_tests.cu | 19 +-- cpp/tests/mip/local_search_test.cu | 8 +- datasets/mip/download_miplib_test_dataset.sh | 1 + 34 files changed, 336 insertions(+), 209 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index fe06f1638c..7db53358bd 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -198,11 +198,11 @@ int run_single_file(std::string file_path, } } - settings.time_limit = time_limit; - settings.heuristics_only = heuristics_only; - settings.num_cpu_threads = num_cpu_threads; - settings.log_to_console = log_to_console; - settings.deterministic = deterministic; + settings.time_limit = time_limit; + settings.heuristics_only = heuristics_only; + settings.num_cpu_threads = num_cpu_threads; + settings.log_to_console = log_to_console; + settings.determinism_mode = deterministic ? CUOPT_MODE_DETERMINISTIC : CUOPT_MODE_OPPORTUNISTIC; settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; settings.presolve = true; diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index eeb9f17758..dd380e0d1f 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -62,6 +62,10 @@ #define CUOPT_NUM_CPU_THREADS "num_cpu_threads" #define CUOPT_USER_PROBLEM_FILE "user_problem_file" +/* @brief MIP determinism mode constants */ +#define CUOPT_MODE_OPPORTUNISTIC 0 +#define CUOPT_MODE_DETERMINISTIC 1 + /* @brief LP/MIP termination status constants */ #define CUOPT_TERIMINATION_STATUS_NO_TERMINATION 0 #define CUOPT_TERIMINATION_STATUS_OPTIMAL 1 diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 412f878b1f..6e48dcfcfb 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -89,9 +89,18 @@ class mip_solver_settings_t { /** Initial primal solutions */ std::vector>> initial_solutions; - bool mip_scaling = true; - bool presolve = true; - bool deterministic = false; + bool mip_scaling = true; + bool presolve = true; + /** + * @brief Determinism mode for MIP solver. + * + * Controls the determinism behavior of the MIP solver: + * - CUOPT_MODE_OPPORTUNISTIC (0): Default mode, allows non-deterministic + * parallelism for better performance + * - CUOPT_MODE_DETERMINISTIC (1): Ensures deterministic results across runs + * at potential cost of performance + */ + int determinism_mode = CUOPT_MODE_OPPORTUNISTIC; // this is for extracting info from different places of the solver during // benchmarks benchmark_info_t* benchmark_info_ptr = nullptr; diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 9284520b63..2f1510c334 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -140,6 +140,8 @@ dual::status_t convert_lp_status_to_dual_status(lp_status_t status) return dual::status_t::ITERATION_LIMIT; } else if (status == lp_status_t::TIME_LIMIT) { return dual::status_t::TIME_LIMIT; + } else if (status == lp_status_t::WORK_LIMIT) { + return dual::status_t::WORK_LIMIT; } else if (status == lp_status_t::NUMERICAL_ISSUES) { return dual::status_t::NUMERICAL; } else if (status == lp_status_t::CUTOFF) { @@ -619,6 +621,10 @@ mip_status_t branch_and_bound_t::set_final_solution(mip_solution_t::solve_node( lp_settings.cut_off = upper_bound + settings_.dual_tol; lp_settings.inside_mip = 2; lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time); + lp_settings.work_limit = settings_.work_limit; lp_settings.scale_columns = false; #ifdef LOG_NODE_SIMPLEX @@ -836,20 +843,29 @@ node_solve_info_t branch_and_bound_t::solve_node( { raft::common::nvtx::range scope_lp("BB::node_lp_solve"); - lp_status = dual_phase2_with_advanced_basis(2, - 0, - recompute_bounds_and_basis, - lp_start_time, - leaf_problem, - lp_settings, - leaf_vstatus, - basis_factors, - basic_list, - nonbasic_list, - leaf_solution, - node_iter, - leaf_edge_norms); + lp_status = + dual_phase2_with_advanced_basis(2, + 0, + recompute_bounds_and_basis, + lp_start_time, + leaf_problem, + lp_settings, + leaf_vstatus, + basis_factors, + basic_list, + nonbasic_list, + leaf_solution, + node_iter, + leaf_edge_norms, + settings_.deterministic ? &work_unit_context_ : nullptr); + } + if (settings_.deterministic && + work_unit_context_.global_work_units_elapsed >= settings_.work_limit) { + lp_status = dual::status_t::WORK_LIMIT; } + printf("------ Total work unit progress B&B: %f / %f\n", + work_unit_context_.global_work_units_elapsed, + settings_.work_limit); if (lp_status == dual::status_t::NUMERICAL) { log.printf("Numerical issue node %d. Resolving from scratch.\n", node_ptr->node_id); @@ -983,6 +999,9 @@ node_solve_info_t branch_and_bound_t::solve_node( search_tree.graphviz_node(log, node_ptr, "timeout", 0.0); return node_solve_info_t::TIME_LIMIT; + } else if (lp_status == dual::status_t::WORK_LIMIT) { + search_tree.graphviz_node(log, node_ptr, "work limit", 0.0); + return node_solve_info_t::WORK_LIMIT; } else { if (thread_type == thread_type_t::EXPLORATION) { fetch_min(lower_bound_ceiling_, node_ptr->lower_bound); @@ -1098,6 +1117,10 @@ void branch_and_bound_t::exploration_ramp_up(mip_node_t* nod solver_status_ = mip_exploration_status_t::TIME_LIMIT; return; + } else if (status == node_solve_info_t::WORK_LIMIT) { + solver_status_ = mip_exploration_status_t::WORK_LIMIT; + return; + } else if (has_children(status)) { exploration_stats_.nodes_unexplored += 2; @@ -1220,6 +1243,10 @@ void branch_and_bound_t::explore_subtree(i_t task_id, solver_status_ = mip_exploration_status_t::TIME_LIMIT; return; + } else if (status == node_solve_info_t::WORK_LIMIT) { + solver_status_ = mip_exploration_status_t::WORK_LIMIT; + return; + } else if (has_children(status)) { // The stack should only contain the children of the current parent. // If the stack size is greater than 0, @@ -1403,6 +1430,9 @@ void branch_and_bound_t::diving_thread(const csr_matrix_t& A if (status == node_solve_info_t::TIME_LIMIT) { solver_status_ = mip_exploration_status_t::TIME_LIMIT; return; + } else if (status == node_solve_info_t::WORK_LIMIT) { + solver_status_ = mip_exploration_status_t::WORK_LIMIT; + return; } else if (has_children(status)) { if (status == node_solve_info_t::UP_CHILD_FIRST) { @@ -1505,6 +1535,11 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut return set_final_solution(solution, -inf); } + if (root_status == lp_status_t::WORK_LIMIT) { + solver_status_ = mip_exploration_status_t::WORK_LIMIT; + return set_final_solution(solution, -inf); + } + assert(root_vstatus_.size() == original_lp_.num_cols); set_uninitialized_steepest_edge_norms(edge_norms_); @@ -1606,6 +1641,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut min_diving_queue_size_ = 4 * settings_.num_diving_threads; solver_status_ = mip_exploration_status_t::RUNNING; lower_bound_ceiling_ = inf; + work_unit_context_.deterministic = settings_.deterministic; #pragma omp parallel num_threads(settings_.num_threads) { diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index e07b18da9d..6f8d8a239a 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -15,7 +15,10 @@ #include #include #include + #include +#include +#include #include #include @@ -31,6 +34,7 @@ enum class mip_status_t { NODE_LIMIT = 4, // The maximum number of nodes was reached (not implemented) NUMERICAL = 5, // The solver encountered a numerical error UNSET = 6, // The status is not set + WORK_LIMIT = 7, // The solver reached a deterministic work limit }; enum class mip_exploration_status_t { @@ -40,6 +44,7 @@ enum class mip_exploration_status_t { NUMERICAL = 3, // The solver encountered a numerical error RUNNING = 4, // The solver is currently exploring the tree COMPLETED = 5, // The solver finished exploring the tree + WORK_LIMIT = 6, // The solver reached a deterministic work limit }; enum class node_solve_info_t { @@ -48,7 +53,8 @@ enum class node_solve_info_t { DOWN_CHILD_FIRST = 2, // The down child should be explored first TIME_LIMIT = 3, // The solver reached a time limit ITERATION_LIMIT = 4, // The solver reached a iteration limit - NUMERICAL = 5 // The solver encounter a numerical error when solving the node + NUMERICAL = 5, // The solver encounter a numerical error when solving the node + WORK_LIMIT = 6, // The solver reached a deterministic work limit }; // Indicate the search and variable selection algorithms used by the thread (See [1]). @@ -143,6 +149,10 @@ class branch_and_bound_t { const user_problem_t& original_problem_; const simplex_solver_settings_t settings_; + // Work unit contexts for each worker + // TODO: only one for now, sequential B&B for now + work_limit_context_t work_unit_context_; + // Initial guess. std::vector guess_; diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 1403f9cbf1..37d729fbaa 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include @@ -2223,7 +2225,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, std::vector& nonbasic_list, lp_solution_t& sol, i_t& iter, - std::vector& delta_y_steepest_edge) + std::vector& delta_y_steepest_edge, + work_limit_context_t* work_unit_context) { raft::common::nvtx::range scope("DualSimplex::phase2_advanced"); const i_t m = lp.num_rows; @@ -2403,7 +2406,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, i_t num_refactors = 0; i_t total_bound_flips = 0; f_t delta_y_nz_percentage = 0.0; - phase2::phase2_timers_t timers(true); + phase2::phase2_timers_t timers(false); // Feature collection for regression training dual_simplex_features_t features; @@ -2466,9 +2469,44 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, manifold.add("A_transpose.x", A_transpose.x); // Track iteration interval start time for runtime measurement - f_t interval_start_time = toc(start_time); - - // Note: Features are logged inline with DS_FEATURES: prefix + f_t interval_start_time = toc(start_time); + i_t last_feature_log_iter = iter; + + // Helper to compute scaled work units for a given number of iterations + cuopt::work_unit_predictor_t work_predictor{}; + auto predict_work_units = [&](i_t num_iters) -> f_t { + std::map features_map; + features_map["m"] = static_cast(features.num_rows); + features_map["n"] = static_cast(features.num_cols); + features_map["nnz"] = static_cast(features.num_nonzeros); + features_map["density"] = static_cast(features.matrix_density); + features_map["avg_nnz_col"] = static_cast(features.avg_nnz_per_col); + features_map["avg_nnz_row"] = static_cast(features.avg_nnz_per_row); + features_map["bounded"] = static_cast(features.num_bounded_vars); + features_map["free"] = static_cast(features.num_free_vars); + features_map["refact_freq"] = static_cast(features.refactor_frequency); + features_map["num_refacts"] = static_cast(features.num_refactors); + features_map["num_updates"] = static_cast(features.num_basis_updates); + features_map["sparse_dz"] = static_cast(features.sparse_delta_z_count); + features_map["dense_dz"] = static_cast(features.dense_delta_z_count); + features_map["bound_flips"] = static_cast(features.total_bound_flips); + features_map["num_infeas"] = static_cast(features.num_infeasibilities); + features_map["dy_nz_pct"] = static_cast(features.delta_y_nz_percentage); + features_map["byte_loads"] = static_cast(features.byte_loads); + features_map["byte_stores"] = static_cast(features.byte_stores); + + f_t base_prediction = std::max((f_t)0.0, (f_t)work_predictor.predict_scalar(features_map)); + return base_prediction * static_cast(num_iters) / FEATURE_LOG_INTERVAL; + }; + + cuopt::scope_guard work_unit_guard([&]() { + if (!work_unit_context) return; + i_t remaining_iters = iter - last_feature_log_iter; + if (remaining_iters <= 0) return; + f_t prediction = predict_work_units(remaining_iters); + printf("DualSimplex determ (final): %d iters, predicted %.4f\n", remaining_iters, prediction); + work_unit_context->record_work(prediction); + }); while (iter < iter_limit) { raft::common::nvtx::range scope_main("DualSimplex::phase2_main_loop"); @@ -3065,16 +3103,15 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, // Feature logging for regression training (every FEATURE_LOG_INTERVAL iterations) if ((iter % FEATURE_LOG_INTERVAL) == 0) { - // Collect aggregated memory access statistics + i_t iters_elapsed = iter - last_feature_log_iter; + auto [total_loads, total_stores] = manifold.collect_and_flush(); features.byte_loads = total_loads; features.byte_stores = total_stores; - // Compute interval runtime features.interval_runtime = now - interval_start_time; interval_start_time = now; - // Update dynamic features features.iteration = iter; features.num_refactors = num_refactors; features.num_basis_updates = ft.num_updates(); @@ -3084,45 +3121,17 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, features.num_infeasibilities = infeasibility_indices.size(); features.delta_y_nz_percentage = delta_y_nz_percentage; - // Log all features on a single line - features.log_features(settings); - - // Populate features map for predictor - cuopt::work_unit_predictor_t dualsimplex_predictor{}; - std::map features_map; - features_map["m"] = static_cast(features.num_rows); - features_map["n"] = static_cast(features.num_cols); - features_map["nnz"] = static_cast(features.num_nonzeros); - features_map["density"] = static_cast(features.matrix_density); - features_map["avg_nnz_col"] = static_cast(features.avg_nnz_per_col); - features_map["avg_nnz_row"] = static_cast(features.avg_nnz_per_row); - features_map["bounded"] = static_cast(features.num_bounded_vars); - features_map["free"] = static_cast(features.num_free_vars); - features_map["refact_freq"] = static_cast(features.refactor_frequency); - features_map["num_refacts"] = static_cast(features.num_refactors); - features_map["num_updates"] = static_cast(features.num_basis_updates); - features_map["sparse_dz"] = static_cast(features.sparse_delta_z_count); - features_map["dense_dz"] = static_cast(features.dense_delta_z_count); - features_map["bound_flips"] = static_cast(features.total_bound_flips); - features_map["num_infeas"] = static_cast(features.num_infeasibilities); - features_map["dy_nz_pct"] = static_cast(features.delta_y_nz_percentage); - features_map["byte_loads"] = static_cast(features.byte_loads); - features_map["byte_stores"] = static_cast(features.byte_stores); - auto time_prediction = - std::max((f_t)0.0, (f_t)dualsimplex_predictor.predict_scalar(features_map)); - f_t baseline_max_clock = 3800; // 3.8Ghz - f_t max_clock = cuopt::get_cpu_max_clock_mhz(); - f_t scaling_factor = baseline_max_clock / max_clock; - f_t adjusted_time_prediction = time_prediction * scaling_factor; - printf( - "DualSimplex determ: Estimated time for %d iters: %f, actual time: %f, error %f, CPU max " - "clock: %f MHz, scaling factor: %f\n", - FEATURE_LOG_INTERVAL, - adjusted_time_prediction, - features.interval_runtime, - adjusted_time_prediction - features.interval_runtime, - max_clock, - scaling_factor); + if (work_unit_context) { + f_t prediction = predict_work_units(iters_elapsed); + printf("DualSimplex determ: %d iters, predicted %.4f, actual %.4f, error %.4f\n", + iters_elapsed, + prediction, + features.interval_runtime, + prediction - features.interval_runtime); + work_unit_context->record_work(prediction); + } + + last_feature_log_iter = iter; } if ((iter - start_iter) < settings.first_iteration_log || @@ -3144,6 +3153,10 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, return dual::status_t::CUTOFF; } + if (work_unit_context && work_unit_context->global_work_units_elapsed >= settings.work_limit) { + return dual::status_t::WORK_LIMIT; + } + if (now > settings.time_limit) { return dual::status_t::TIME_LIMIT; } if (settings.concurrent_halt != nullptr && *settings.concurrent_halt == 1) { @@ -3196,7 +3209,8 @@ template dual::status_t dual_phase2_with_advanced_basis( std::vector& nonbasic_list, lp_solution_t& sol, int& iter, - std::vector& steepest_edge_norms); + std::vector& steepest_edge_norms, + work_limit_context_t* work_unit_context); #endif } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/phase2.hpp b/cpp/src/dual_simplex/phase2.hpp index c6ed03c84c..54e2b4087d 100644 --- a/cpp/src/dual_simplex/phase2.hpp +++ b/cpp/src/dual_simplex/phase2.hpp @@ -17,6 +17,10 @@ #include +namespace cuopt { +struct work_limit_context_t; +} + namespace cuopt::linear_programming::dual_simplex { namespace dual { @@ -28,7 +32,8 @@ enum class status_t { TIME_LIMIT = 4, ITERATION_LIMIT = 5, CONCURRENT_LIMIT = 6, - UNSET = 7 + UNSET = 7, + WORK_LIMIT = 8 }; static std::string status_to_string(status_t status) @@ -41,6 +46,7 @@ static std::string status_to_string(status_t status) case status_t::TIME_LIMIT: return "TIME_LIMIT"; case status_t::ITERATION_LIMIT: return "ITERATION_LIMIT"; case status_t::CONCURRENT_LIMIT: return "CONCURRENT_LIMIT"; + case status_t::WORK_LIMIT: return "WORK_LIMIT"; case status_t::UNSET: return "UNSET"; } return "UNKNOWN"; @@ -71,6 +77,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, std::vector& nonbasic_list, lp_solution_t& sol, i_t& iter, - std::vector& delta_y_steepest_edge); + std::vector& delta_y_steepest_edge, + work_limit_context_t* work_unit_context = nullptr); } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 8e54c40bb2..8d85581f8b 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -25,6 +25,7 @@ struct simplex_solver_settings_t { : iteration_limit(std::numeric_limits::max()), node_limit(std::numeric_limits::max()), time_limit(std::numeric_limits::infinity()), + work_limit(std::numeric_limits::infinity()), absolute_mip_gap_tol(0.0), relative_mip_gap_tol(1e-3), integer_tol(1e-5), @@ -84,6 +85,7 @@ struct simplex_solver_settings_t { i_t iteration_limit; i_t node_limit; f_t time_limit; + f_t work_limit; f_t absolute_mip_gap_tol; // Tolerance on mip gap to declare optimal f_t relative_mip_gap_tol; // Tolerance on mip gap to declare optimal f_t integer_tol; // Tolerance on integralitiy violation @@ -118,7 +120,8 @@ struct simplex_solver_settings_t { bool print_presolve_stats; // true to print presolve stats bool barrier_presolve; // true to use barrier presolve bool cudss_deterministic; // true to use cuDSS deterministic mode, false for non-deterministic - bool barrier; // true to use barrier method, false to use dual simplex method + bool deterministic; // true to use B&B deterministic mode, false to use non-deterministic mode + bool barrier; // true to use barrier method, false to use dual simplex method bool eliminate_dense_columns; // true to eliminate dense columns from A*D*A^T i_t folding; // -1 automatic, 0 don't fold, 1 fold i_t augmented; // -1 automatic, 0 to solve with ADAT, 1 to solve with augmented system diff --git a/cpp/src/dual_simplex/solve.cpp b/cpp/src/dual_simplex/solve.cpp index 7e59e83c37..271a31aedb 100644 --- a/cpp/src/dual_simplex/solve.cpp +++ b/cpp/src/dual_simplex/solve.cpp @@ -209,6 +209,7 @@ lp_status_t solve_linear_program_with_advanced_basis( return lp_status_t::NUMERICAL_ISSUES; } if (phase1_status == dual::status_t::TIME_LIMIT) { return lp_status_t::TIME_LIMIT; } + if (phase1_status == dual::status_t::WORK_LIMIT) { return lp_status_t::WORK_LIMIT; } if (phase1_status == dual::status_t::ITERATION_LIMIT) { return lp_status_t::ITERATION_LIMIT; } if (phase1_status == dual::status_t::CONCURRENT_LIMIT) { return lp_status_t::CONCURRENT_LIMIT; } phase1_obj = phase1_solution.objective; @@ -292,6 +293,7 @@ lp_status_t solve_linear_program_with_advanced_basis( } if (status == dual::status_t::DUAL_UNBOUNDED) { lp_status = lp_status_t::INFEASIBLE; } if (status == dual::status_t::TIME_LIMIT) { lp_status = lp_status_t::TIME_LIMIT; } + if (status == dual::status_t::WORK_LIMIT) { lp_status = lp_status_t::WORK_LIMIT; } if (status == dual::status_t::ITERATION_LIMIT) { lp_status = lp_status_t::ITERATION_LIMIT; } if (status == dual::status_t::CONCURRENT_LIMIT) { lp_status = lp_status_t::CONCURRENT_LIMIT; } if (status == dual::status_t::NUMERICAL) { lp_status = lp_status_t::NUMERICAL_ISSUES; } diff --git a/cpp/src/dual_simplex/solve.hpp b/cpp/src/dual_simplex/solve.hpp index e962297843..ecfba6f501 100644 --- a/cpp/src/dual_simplex/solve.hpp +++ b/cpp/src/dual_simplex/solve.hpp @@ -27,7 +27,8 @@ enum class lp_status_t { NUMERICAL_ISSUES = 5, CUTOFF = 6, CONCURRENT_LIMIT = 7, - UNSET = 8 + UNSET = 8, + WORK_LIMIT = 9 }; template diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index 12d38e7201..d8d18d6c44 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -86,7 +86,8 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_FOLDING, &pdlp_settings.folding, -1, 1, -1}, {CUOPT_DUALIZE, &pdlp_settings.dualize, -1, 1, -1}, {CUOPT_ORDERING, &pdlp_settings.ordering, -1, 1, -1}, - {CUOPT_BARRIER_DUAL_INITIAL_POINT, &pdlp_settings.barrier_dual_initial_point, -1, 1, -1} + {CUOPT_BARRIER_DUAL_INITIAL_POINT, &pdlp_settings.barrier_dual_initial_point, -1, 1, -1}, + {CUOPT_MIP_DETERMINISTIC, &mip_settings.determinism_mode, CUOPT_MODE_OPPORTUNISTIC, CUOPT_MODE_DETERMINISTIC, CUOPT_MODE_OPPORTUNISTIC} }; // Bool parameters @@ -105,8 +106,7 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_CUDSS_DETERMINISTIC, &pdlp_settings.cudss_deterministic, false}, {CUOPT_PRESOLVE, &pdlp_settings.presolve, false}, {CUOPT_PRESOLVE, &mip_settings.presolve, true}, - {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true}, - {CUOPT_MIP_DETERMINISTIC, &mip_settings.deterministic, false} + {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true} }; // String parameters string_parameters = { diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 96f93d267a..e562f1e3cf 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -104,7 +104,8 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::run_presolve(f_t time_limit) CUOPT_LOG_INFO("Running presolve!"); work_limit_timer_t presolve_timer(context.gpu_heur_loop, time_limit); auto term_crit = ls.constraint_prop.bounds_update.solve(*problem_ptr); - presolve_timer.record_work(0); if (ls.constraint_prop.bounds_update.infeas_constraints_count > 0) { stats.presolve_time = timer.elapsed_time(); return false; @@ -283,7 +283,9 @@ void diversity_manager_t::run_fj_alone(solution_t& solution) ls.fj.settings.iteration_limit = iter_dist(rng); ls.fj.settings.time_limit = std::numeric_limits::infinity(); - if (context.settings.deterministic) { ls.fj.settings.iteration_limit = iter_dist(rng); } + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + ls.fj.settings.iteration_limit = iter_dist(rng); + } CUOPT_LOG_INFO( "FJ benchmark run %d/%d: iteration_limit=%d", run + 1, 1000, ls.fj.settings.iteration_limit); @@ -349,19 +351,11 @@ solution_t diversity_manager_t::run_solver() add_user_given_solutions(initial_sol_vector); // Run CPUFJ early to find quick initial solutions ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop - if (!context.settings.deterministic) { -#if 0 + + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { ls.start_cpufj_scratch_threads(population); - // 30'000 iters - ls.scratch_cpu_fj[0].wait_for_cpu_solver(); - // std::this_thread::sleep_for(std::chrono::seconds(30)); - ls.stop_cpufj_scratch_threads(); - exit(0); -#endif } - if (!context.settings.deterministic) { ls.start_cpufj_scratch_threads(population); } - // before probing cache or LP, run FJ to generate initial primal feasible solution const f_t time_ratio_of_probing_cache = diversity_config.time_ratio_of_probing_cache; const f_t max_time_on_probing = diversity_config.max_time_on_probing; @@ -456,7 +450,9 @@ solution_t diversity_manager_t::run_solver() lp_rounded_sol.round_nearest(); lp_rounded_sol.compute_feasibility(); population.add_solution(std::move(lp_rounded_sol)); - if (!context.settings.deterministic) { ls.start_cpufj_lptopt_scratch_threads(population); } + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + ls.start_cpufj_lptopt_scratch_threads(population); + } } population.add_solutions_from_vec(std::move(initial_sol_vector)); @@ -474,7 +470,7 @@ solution_t diversity_manager_t::run_solver() run_fj_alone(sol); return sol; } - if (!context.settings.deterministic) { rins.enable(); } + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { rins.enable(); } generate_solution(timer.remaining_time(), false); printf("=======================================================\n"); diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index d84e24254f..f7451edfc9 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -85,7 +85,7 @@ class fp_recombiner_t : public recombiner_t { CUOPT_LOG_DEBUG("FP rec: running LP with infeasibility detection"); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; - if (this->context.settings.deterministic) { + if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { lp_settings.time_limit = std::numeric_limits::max(); // TODO should be global time limit lp_settings.work_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; diff --git a/cpp/src/mip/diversity/recombiners/recombiner.cuh b/cpp/src/mip/diversity/recombiners/recombiner.cuh index 2454f545d9..925d9a7c2f 100644 --- a/cpp/src/mip/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/recombiner.cuh @@ -223,7 +223,9 @@ class recombiner_t { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } // submip not supported in deterministic mode yet - if (context.settings.deterministic) { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); + } recombiner_t::enabled_recombiners = std::vector(enabled_recombiners.begin(), enabled_recombiners.end()); } diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index b037390684..927eb15753 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -1317,7 +1317,9 @@ i_t fj_t::solve(solution_t& solution) // settings.load_balancing_mode = fj_load_balancing_mode_t::ALWAYS_OFF; - if (context.settings.deterministic) { settings.work_limit = settings.time_limit; } + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + settings.work_limit = settings.time_limit; + } // if work_limit is set: compute an estimate of the number of iterations required if (settings.work_limit != std::numeric_limits::infinity()) { std::map features_map = get_feature_vector(0); @@ -1423,7 +1425,8 @@ i_t fj_t::solve(solution_t& solution) "FJ early exit at %d iterations (limit: %d)", iterations, settings.iteration_limit); // Compute the work unit corresponding to the number of iterations elapsed // by incrementally guessing work units until the model predicts >= actual iterations - if (context.settings.deterministic && iterations > 0) { + // TODO: awfully ugly, change + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC && iterations > 0) { double guessed_work = 0.0; const double work_increment = 0.1; const double max_work = settings.work_limit * 2.0; // Safety limit @@ -1449,6 +1452,7 @@ i_t fj_t::solve(solution_t& solution) } } + CUOPT_LOG_DEBUG("FJ: recording work %fwu for %d iterations", work_to_record, iterations); timer.record_work(work_to_record); CUOPT_LOG_DEBUG("FJ sol hash %x", solution.get_hash()); diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 7c8fb0433c..0332b41e41 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -897,10 +897,14 @@ DI void update_changed_constraints(typename fj_t::climber_data_t::view if (blockIdx.x == 0) { if (threadIdx.x == 0) { // sort changed constraints to guarantee determinism - // TODO: horribly slow as it is - thrust::sort(thrust::seq, - fj.constraints_changed.begin(), - fj.constraints_changed.begin() + *fj.constraints_changed_count); + // TODO: horribly slow as it is... block-parallelize at least? but not trivial for arbitrary + // sizes w/ CUB + // TODO(2): tsk.. ... just bucket sort... + if (fj.settings->work_limit != std::numeric_limits::infinity()) { + thrust::sort(thrust::seq, + fj.constraints_changed.begin(), + fj.constraints_changed.begin() + *fj.constraints_changed_count); + } for (i_t i = 0; i < *fj.constraints_changed_count; ++i) { i_t idx = fj.constraints_changed[i]; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index 205292f7f0..7e981a8657 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -231,7 +231,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t::infinity(); lp_settings.work_limit = time_limit; } @@ -327,7 +327,9 @@ bool feasibility_pump_t::test_fj_feasible(solution_t& soluti fj.settings.feasibility_run = true; fj.settings.n_of_minimums_for_exit = 5000; fj.settings.time_limit = std::min(time_limit, timer.remaining_time()); - if (context.settings.deterministic) { fj.settings.work_limit = fj.settings.time_limit; } + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + fj.settings.work_limit = fj.settings.time_limit; + } cuopt_func_call(solution.test_variable_bounds(true)); is_feasible = fj.solve(solution); cuopt_func_call(solution.test_variable_bounds(true)); @@ -341,7 +343,7 @@ bool feasibility_pump_t::test_fj_feasible(solution_t& soluti } else { CUOPT_LOG_DEBUG("20%% FJ run found feasible!"); } - timer.record_work(fj.settings.time_limit); + timer.record_work(fj.settings.work_limit); CUOPT_LOG_DEBUG("20%% FJ run finished, elapsed %fs remaining %fwu", fj.settings.time_limit, timer.remaining_time()); diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu index 578c328c54..766cb8ee21 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu @@ -189,7 +189,7 @@ bool line_segment_search_t::search_line_segment( best_feasible_cost, curr_cost); } - if (!context.settings.deterministic) { + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { if (timer.check_time_limit()) { break; } } i_t number_of_integer_var_diff = compute_number_of_integer_var_diff( @@ -230,7 +230,7 @@ bool line_segment_search_t::search_line_segment( curr_cost); } // cuopt_assert(context.gpu_heur_loop, ""); - if (!context.settings.deterministic) { + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { if (timer.check_time_limit()) { break; } } } diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 28be6dd4f4..115b9b7751 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -123,7 +123,7 @@ void local_search_t::start_cpufj_lptopt_scratch_threads( scratch_cpu_fj_on_lp_opt.fj_cpu = fj.create_cpu_climber(solution_lp, default_weights, default_weights, 0., cpu_fj_settings, true); scratch_cpu_fj_on_lp_opt.fj_cpu->log_prefix = "******* scratch on LP optimal: "; - if (!context.settings.deterministic) { + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { scratch_cpu_fj_on_lp_opt.fj_cpu->improvement_callback = [this, &population](f_t obj, const std::vector& h_vec) { population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); @@ -167,7 +167,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, // for now: always assign the CPUFJs to perform 1000 iters per s fj_settings_t cpu_fj_settings{}; - if (!context.settings.deterministic) { + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { cpu_fj_settings.iteration_limit = std::numeric_limits::max(); } else { // TODO: CHANGE @@ -192,7 +192,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, in_fj.solve(solution); // Stop CPU solver - if (!context.settings.deterministic) { + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { for (auto& cpu_fj : ls_cpu_fj) { cpu_fj.stop_cpu_solver(); } @@ -264,14 +264,11 @@ void local_search_t::generate_fast_solution(solution_t& solu fj.settings.update_weights = true; fj.settings.feasibility_run = true; fj.settings.time_limit = std::min(30., timer.remaining_time()); - // CHANGE - fj.settings.time_limit = std::numeric_limits::infinity(); while (!timer.check_time_limit()) { work_limit_timer_t constr_prop_timer = work_limit_timer_t(context.gpu_heur_loop, std::min(timer.remaining_time(), 2.)); // do constraint prop on lp optimal solution constraint_prop.apply_round(solution, 1., constr_prop_timer); - constr_prop_timer.record_work(0); if (solution.compute_feasibility()) { return; } if (timer.check_time_limit()) { return; }; f_t time_limit = std::min(3., timer.remaining_time()); @@ -423,11 +420,12 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu work_limit_timer_t(context.gpu_heur_loop, std::min(timer.remaining_time(), 10.)); bool is_feasible = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); - bounds_prop_timer.record_work(0); if (!is_feasible) { f_t lp_run_time = 2.; // CHANGE - if (context.settings.deterministic) { lp_run_time = std::numeric_limits::infinity(); } + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + lp_run_time = std::numeric_limits::infinity(); + } relaxed_lp_settings_t lp_settings; lp_settings.time_limit = std::min(lp_run_time, timer.remaining_time()); lp_settings.work_limit = lp_settings.time_limit; @@ -444,7 +442,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu fj.settings.update_weights = true; fj.settings.feasibility_run = false; f_t fj_time_limit = 30.; - if (context.settings.deterministic) { + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { fj_time_limit = std::numeric_limits::infinity(); fj.settings.iteration_limit = 100'000; } diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index dd9221dbf2..77ceb37758 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -775,7 +775,7 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob f_t repair_start_time = timer.remaining_time(); i_t n_of_repairs_needed_for_feasible = 0; i_t iter_limit = std::numeric_limits::max(); - if (this->context.settings.deterministic) { + if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); iter_limit = 100; } @@ -865,7 +865,7 @@ bool constraint_prop_t::find_integer( std::mt19937 rng(seed); // CHANGE - if (this->context.settings.deterministic) { + if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); } @@ -1133,7 +1133,7 @@ bool constraint_prop_t::apply_round( // === CONSTRAINT PROP PREDICTOR FEATURES - END === max_timer = work_limit_timer_t{context.gpu_heur_loop, max_time_for_bounds_prop}; - if (this->context.settings.deterministic) { + if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { max_timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); } if (check_brute_force_rounding(sol)) { diff --git a/cpp/src/mip/presolve/bounds_presolve.cu b/cpp/src/mip/presolve/bounds_presolve.cu index 532ac099c7..a820fb0804 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cu +++ b/cpp/src/mip/presolve/bounds_presolve.cu @@ -172,7 +172,7 @@ termination_criterion_t bound_presolve_t::bound_update_loop(problem_t< termination_criterion_t criteria = termination_criterion_t::ITERATION_LIMIT; // CHANGE - if (context.settings.deterministic) { + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { timer = timer_t(std::numeric_limits::infinity()); settings.iteration_limit = std::min(settings.iteration_limit, 50); } diff --git a/cpp/src/mip/presolve/utils.cuh b/cpp/src/mip/presolve/utils.cuh index 4870b3180c..2d354504d2 100644 --- a/cpp/src/mip/presolve/utils.cuh +++ b/cpp/src/mip/presolve/utils.cuh @@ -12,6 +12,7 @@ namespace cuopt::linear_programming::detail { enum class termination_criterion_t { TIME_LIMIT, ITERATION_LIMIT, + WORK_LIMIT, CONVERGENCE, INFEASIBLE, NO_UPDATE diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index d9c6f45b61..9a0492e666 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -79,9 +79,13 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.iteration_limit = settings.iteration_limit; // CHANGE - i_t work_limit = pdlp_settings.time_limit; // settings.work_limit - pdlp_settings.time_limit = std::numeric_limits::infinity(); - if (work_limit != std::numeric_limits::infinity()) { + i_t work_limit = settings.work_limit; + bool determinism_mode = work_limit != std::numeric_limits::infinity(); + pdlp_settings.concurrent_halt = settings.concurrent_halt; + pdlp_settings.per_constraint_residual = settings.per_constraint_residual; + pdlp_settings.first_primal_feasible = settings.return_first_feasible; + pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; + if (determinism_mode) { // try to estimate the iteration count based on the requested work limit int estim_iters = 100; do { @@ -93,16 +97,8 @@ optimization_problem_solution_t get_relaxed_lp_solution( estim_iters += 100; } while (true); CUOPT_LOG_DEBUG("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); - pdlp_settings.iteration_limit = estim_iters; - pdlp_settings.time_limit = std::numeric_limits::infinity(); - } - pdlp_settings.concurrent_halt = settings.concurrent_halt; - pdlp_settings.per_constraint_residual = settings.per_constraint_residual; - pdlp_settings.first_primal_feasible = settings.return_first_feasible; - pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; - // Stable3 has a simpler and more predictable codepath - // if (work_limit != std::numeric_limits::infinity()) - { + pdlp_settings.iteration_limit = estim_iters; + pdlp_settings.time_limit = std::numeric_limits::infinity(); pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable3; } set_pdlp_solver_mode(pdlp_settings); diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index 55d71306eb..f3fd67a523 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -155,8 +155,7 @@ solution_t mip_solver_t::run_solver() branch_and_bound_solution_helper_t solution_helper(&dm, branch_and_bound_settings); dual_simplex::mip_solution_t branch_and_bound_solution(1); - // for now, disable B&B in deterministic mode - bool run_bb = !context.settings.deterministic && !context.settings.heuristics_only; + bool run_bb = !context.settings.heuristics_only; if (run_bb) { // Convert the presolved problem to dual_simplex::user_problem_t op_problem_.get_host_user_problem(branch_and_bound_problem); @@ -169,6 +168,12 @@ solution_t mip_solver_t::run_solver() branch_and_bound_settings.absolute_mip_gap_tol = context.settings.tolerances.absolute_mip_gap; branch_and_bound_settings.relative_mip_gap_tol = context.settings.tolerances.relative_mip_gap; branch_and_bound_settings.integer_tol = context.settings.tolerances.integrality_tolerance; + branch_and_bound_settings.deterministic = + context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; + branch_and_bound_settings.work_limit = + context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC + ? context.settings.time_limit + : std::numeric_limits::infinity(); if (context.settings.num_cpu_threads < 0) { branch_and_bound_settings.num_threads = omp_get_max_threads() - 1; @@ -177,13 +182,11 @@ solution_t mip_solver_t::run_solver() } CUOPT_LOG_INFO("Using %d CPU threads for B&B", branch_and_bound_settings.num_threads); - CUOPT_LOG_ERROR("AAAAAA\n"); - i_t num_threads = branch_and_bound_settings.num_threads; i_t num_bfs_threads = std::max(1, num_threads / 4); i_t num_diving_threads = num_threads - num_bfs_threads; // deterministic mode: no diving for now - if (context.settings.deterministic) { + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { num_threads = 1; num_bfs_threads = 1; num_diving_threads = 0; @@ -192,26 +195,28 @@ solution_t mip_solver_t::run_solver() branch_and_bound_settings.num_diving_threads = num_diving_threads; // Set the branch and bound -> primal heuristics callback - branch_and_bound_settings.solution_callback = - std::bind(&branch_and_bound_solution_helper_t::solution_callback, - &solution_helper, - std::placeholders::_1, - std::placeholders::_2); - branch_and_bound_settings.heuristic_preemption_callback = std::bind( - &branch_and_bound_solution_helper_t::preempt_heuristic_solver, &solution_helper); - - branch_and_bound_settings.set_simplex_solution_callback = - std::bind(&branch_and_bound_solution_helper_t::set_simplex_solution, - &solution_helper, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3); - - branch_and_bound_settings.node_processed_callback = - std::bind(&branch_and_bound_solution_helper_t::node_processed_callback, - &solution_helper, - std::placeholders::_1, - std::placeholders::_2); + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + branch_and_bound_settings.solution_callback = + std::bind(&branch_and_bound_solution_helper_t::solution_callback, + &solution_helper, + std::placeholders::_1, + std::placeholders::_2); + branch_and_bound_settings.heuristic_preemption_callback = std::bind( + &branch_and_bound_solution_helper_t::preempt_heuristic_solver, &solution_helper); + + branch_and_bound_settings.set_simplex_solution_callback = + std::bind(&branch_and_bound_solution_helper_t::set_simplex_solution, + &solution_helper, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3); + + branch_and_bound_settings.node_processed_callback = + std::bind(&branch_and_bound_solution_helper_t::node_processed_callback, + &solution_helper, + std::placeholders::_1, + std::placeholders::_2); + } // Create the branch and bound object branch_and_bound = std::make_unique>( diff --git a/cpp/src/mip/solver_context.cuh b/cpp/src/mip/solver_context.cuh index b4df2578e9..4cd990519e 100644 --- a/cpp/src/mip/solver_context.cuh +++ b/cpp/src/mip/solver_context.cuh @@ -28,9 +28,9 @@ class branch_and_bound_t; namespace cuopt::linear_programming::detail { struct mip_solver_work_unit_predictors_t { - work_unit_predictor_t fj_predictor{}; - work_unit_predictor_t cpufj_predictor{}; - work_unit_predictor_t pdlp_predictor{}; + work_unit_predictor_t fj_predictor{}; + work_unit_predictor_t cpufj_predictor{}; + work_unit_predictor_t pdlp_predictor{}; }; // Aggregate structure containing the global context of the solving process for convenience: @@ -46,7 +46,7 @@ struct mip_solver_context_t { cuopt_assert(problem_ptr != nullptr, "problem_ptr is nullptr"); stats.solution_bound = problem_ptr->maximize ? std::numeric_limits::infinity() : -std::numeric_limits::infinity(); - gpu_heur_loop.deterministic = settings.deterministic; + gpu_heur_loop.deterministic = settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; } raft::handle_t const* const handle_ptr; @@ -60,6 +60,8 @@ struct mip_solver_context_t { // Work limit context for tracking work units in deterministic mode (shared across all timers in // GPU heuristic loop) cuopt::work_limit_context_t gpu_heur_loop; + cuopt::work_limit_context_t cpu_branch_and_bound; // TODO: should be each per worker thread. + // Works for now since threads_b&b = 1 }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/utilities/models/dualsimplex_predictor/main.cpp b/cpp/src/utilities/models/dualsimplex_predictor/main.cpp index 571527421c..211e2d0979 100644 --- a/cpp/src/utilities/models/dualsimplex_predictor/main.cpp +++ b/cpp/src/utilities/models/dualsimplex_predictor/main.cpp @@ -40,14 +40,14 @@ static const int32_t num_class[] = { 1, }; -int32_t dualsimplex_predictor::get_num_target(void) { return std::exp(N_TARGET); } +int32_t dualsimplex_predictor::get_num_target(void) { return N_TARGET; } void dualsimplex_predictor::get_num_class(int32_t* out) { for (int i = 0; i < N_TARGET; ++i) { out[i] = num_class[i]; } } -int32_t dualsimplex_predictor::get_num_feature(void) { return std::exp(18); } +int32_t dualsimplex_predictor::get_num_feature(void) { return 18; } const char* dualsimplex_predictor::get_threshold_type(void) { return "float32"; } const char* dualsimplex_predictor::get_leaf_output_type(void) { return "float32"; } diff --git a/cpp/src/utilities/models/pdlp_predictor/main.cpp b/cpp/src/utilities/models/pdlp_predictor/main.cpp index c82153c91b..6ae3486409 100644 --- a/cpp/src/utilities/models/pdlp_predictor/main.cpp +++ b/cpp/src/utilities/models/pdlp_predictor/main.cpp @@ -30,14 +30,14 @@ static const int32_t num_class[] = { 1, }; -int32_t pdlp_predictor::get_num_target(void) { return std::exp(N_TARGET); } +int32_t pdlp_predictor::get_num_target(void) { return N_TARGET; } void pdlp_predictor::get_num_class(int32_t* out) { for (int i = 0; i < N_TARGET; ++i) { out[i] = num_class[i]; } } -int32_t pdlp_predictor::get_num_feature(void) { return std::exp(8); } +int32_t pdlp_predictor::get_num_feature(void) { return 8; } const char* pdlp_predictor::get_threshold_type(void) { return "float32"; } const char* pdlp_predictor::get_leaf_output_type(void) { return "float32"; } diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp index 2cd644786a..c259abbc29 100644 --- a/cpp/src/utilities/work_limit_timer.hpp +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -22,8 +22,8 @@ namespace cuopt { -// Context for tracking global work units across multiple timers in deterministic mode -// This allows different subsystems to have independent work unit tracking +// Context for tracking global work units across multiple timers in a single thread in deterministic +// mode struct work_limit_context_t { double global_work_units_elapsed{0.0}; bool deterministic{false}; diff --git a/cpp/src/utilities/work_unit_predictor.cpp b/cpp/src/utilities/work_unit_predictor.cpp index 71ed55c94a..0aebf953de 100644 --- a/cpp/src/utilities/work_unit_predictor.cpp +++ b/cpp/src/utilities/work_unit_predictor.cpp @@ -47,8 +47,8 @@ static inline uint32_t compute_hash(std::vector h_contents) return hash; } -template -float work_unit_predictor_t::predict_scalar( +template +float work_unit_predictor_t::predict_scalar( const std::map& features) const { raft::common::nvtx::range range("work_unit_predictor_t::predict_scalar"); @@ -57,13 +57,12 @@ float work_unit_predictor_t::predict_scalar( for (int i = 0; i < model_t::NUM_FEATURES; ++i) { if (features.find(std::string(model_t::feature_names[i])) == features.end()) { data[i].missing = -1; - printf("Feature %s: missing\n", model_t::feature_names[i]); + CUOPT_LOG_WARN("Feature %s: missing\n", model_t::feature_names[i]); } else { data[i].fvalue = features.at(std::string(model_t::feature_names[i])); - // printf("Feature %s: %f\n", model_t::feature_names[i], data[i].fvalue); } } - // Compute a hash key for the relevant inputs + std::vector cache_vec; cache_vec.reserve(model_t::NUM_FEATURES); for (int i = 0; i < model_t::NUM_FEATURES; ++i) { @@ -75,20 +74,23 @@ float work_unit_predictor_t::predict_scalar( auto cached_it = prediction_cache.find(key); if (cached_it != prediction_cache.end()) { return cached_it->second; } - // run predictor double result = 0.0; auto start = std::chrono::high_resolution_clock::now(); model_t::predict(data, 0, &result); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = end - start; - CUOPT_LOG_DEBUG("Prediction time: %f ms", elapsed.count()); - CUOPT_LOG_DEBUG("Result: %f", result); - return result; + if (debug) CUOPT_LOG_DEBUG("Prediction time: %f ms", elapsed.count()); + + float scaled_result = scaler_.scale_work_units(result); + prediction_cache[key] = scaled_result; + if (debug) CUOPT_LOG_DEBUG("Result: %f (scaled: %f)", result, scaled_result); + + return scaled_result; } -template class work_unit_predictor_t; -template class work_unit_predictor_t; -template class work_unit_predictor_t; -template class work_unit_predictor_t; +template class work_unit_predictor_t; +template class work_unit_predictor_t; +template class work_unit_predictor_t; +template class work_unit_predictor_t; } // namespace cuopt diff --git a/cpp/src/utilities/work_unit_predictor.hpp b/cpp/src/utilities/work_unit_predictor.hpp index 0a7c63e33d..45bc6b97bc 100644 --- a/cpp/src/utilities/work_unit_predictor.hpp +++ b/cpp/src/utilities/work_unit_predictor.hpp @@ -22,15 +22,42 @@ #include #include +#include + namespace cuopt { -template +// Temporary scaling classes until I figure out better ways to do this +// to account for performance differences between the regression learning machine and the user +// machine. (e.g. integrate memory latency/bandwidth, cache topology, user-provided tuning...) +struct cpu_work_unit_scaler_t { + cpu_work_unit_scaler_t() + { + constexpr double baseline_max_clock = 3800.0; + double max_clock = get_cpu_max_clock_mhz(); + scaling_factor_ = baseline_max_clock / max_clock; + } + + double scale_work_units(double work_units) const { return work_units * scaling_factor_; } + + private: + double scaling_factor_; +}; + +struct gpu_work_unit_scaler_t { + double scale_work_units(double work_units) const { return work_units; } +}; + +template class work_unit_predictor_t { public: float predict_scalar(const std::map& features) const; + public: + bool debug{false}; + private: mutable std::unordered_map prediction_cache; + scaler_t scaler_; }; } // namespace cuopt diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index 1f96348a26..6b74581fc2 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -100,12 +100,12 @@ static uint32_t test_full_run_determinism(std::string path, nullptr, true); - auto settings = mip_solver_settings_t{}; - settings.time_limit = 3000.; - settings.work_limit = 10; // about 10 seconds of runtime - settings.deterministic = true; - settings.heuristics_only = true; - auto timer = cuopt::timer_t(3000); + auto settings = mip_solver_settings_t{}; + settings.time_limit = 3000.; + settings.work_limit = 10; // about 10 seconds of runtime + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.heuristics_only = true; + auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); @@ -156,11 +156,11 @@ static uint32_t test_initial_solution_determinism(std::string path, nullptr, true); - auto settings = mip_solver_settings_t{}; - settings.time_limit = 3000.; - settings.deterministic = true; - settings.heuristics_only = true; - auto timer = cuopt::timer_t(3000); + auto settings = mip_solver_settings_t{}; + settings.time_limit = 3000.; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.heuristics_only = true; + auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); @@ -212,11 +212,11 @@ static uint32_t test_recombiners_determinism(std::string path, nullptr, true); - auto settings = mip_solver_settings_t{}; - settings.time_limit = 3000.; - settings.deterministic = true; - settings.heuristics_only = true; - auto timer = cuopt::timer_t(3000); + auto settings = mip_solver_settings_t{}; + settings.time_limit = 3000.; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.heuristics_only = true; + auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index 8c14494f4b..19390e5681 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -261,18 +261,19 @@ TEST(mip_solve, feasibility_jump_determinism) int seed = std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); - for (const auto& instance : {"thor50dday.mps", - "gen-ip054.mps", - "50v-10.mps", - "seymour1.mps", - "rmatr200-p5.mps", - "tr12-30.mps", - "sct2.mps", - "uccase9.mps"}) { + for (const auto& [instance, iter_limit] : {std::make_pair("thor50dday.mps", 1000), + std::make_pair("gen-ip054.mps", 1000), + std::make_pair("50v-10.mps", 1000), + std::make_pair("seymour1.mps", 1000), + std::make_pair("rmatr200-p5.mps", 1000), + std::make_pair("tr12-30.mps", 1000), + std::make_pair("sct2.mps", 1000), + std::make_pair("uccase9.mps", 1000), + std::make_pair("supportcase42.mps", 25000)}) { for (int i = 0; i < 10; i++) { // while (true) { cuopt::seed_generator::set_seed(seed); - run_fj_check_determinism(instance, 1000); + run_fj_check_determinism(instance, iter_limit); } } } diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index 0393c2def6..f616bda9a8 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -110,10 +110,10 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) nullptr, true); - auto settings = mip_solver_settings_t{}; - settings.time_limit = 30.; - settings.deterministic = true; - auto timer = cuopt::timer_t(30); + auto settings = mip_solver_settings_t{}; + settings.time_limit = 30.; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + auto timer = cuopt::timer_t(30); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); diff --git a/datasets/mip/download_miplib_test_dataset.sh b/datasets/mip/download_miplib_test_dataset.sh index d0ffe45d25..99c9661e59 100755 --- a/datasets/mip/download_miplib_test_dataset.sh +++ b/datasets/mip/download_miplib_test_dataset.sh @@ -24,6 +24,7 @@ INSTANCES=( "enlight_hard" "enlight11" "supportcase22" + "supportcase42" ) BASE_URL="https://miplib.zib.de/WebData/instances" From 1a4ac76231e92518b167877450bef6d8dc7485ee Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 1 Dec 2025 15:57:45 +0000 Subject: [PATCH 096/225] add work unit scheduler for parallel threads --- cpp/src/CMakeLists.txt | 1 + cpp/src/dual_simplex/branch_and_bound.hpp | 4 +- .../recombiners/line_segment_recombiner.cuh | 2 +- cpp/src/mip/solver.cu | 14 +- cpp/src/mip/solver_context.cuh | 10 +- cpp/src/utilities/work_limit_timer.hpp | 16 +- cpp/src/utilities/work_unit_scheduler.cpp | 183 ++++++++++++++++++ cpp/src/utilities/work_unit_scheduler.hpp | 80 ++++++++ cpp/tests/mip/diversity_test.cu | 6 +- cpp/tests/mip/local_search_test.cu | 2 +- cpp/tests/mip/presolve_test.cu | 2 +- 11 files changed, 302 insertions(+), 18 deletions(-) create mode 100644 cpp/src/utilities/work_unit_scheduler.cpp create mode 100644 cpp/src/utilities/work_unit_scheduler.hpp diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 3267f3e802..c35c6d2b58 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -8,6 +8,7 @@ set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_predictor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_scheduler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/quantize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/main.cpp diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index 3375ddf29b..452ba9c43c 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -145,13 +145,15 @@ class branch_and_bound_t { // The main entry routine. Returns the solver status and populates solution with the incumbent. mip_status_t solve(mip_solution_t& solution); + work_limit_context_t& get_work_unit_context() { return work_unit_context_; } + private: const user_problem_t& original_problem_; const simplex_solver_settings_t settings_; // Work unit contexts for each worker // TODO: only one for now, sequential B&B for now - work_limit_context_t work_unit_context_; + work_limit_context_t work_unit_context_{"B&B"}; // Initial guess. std::vector guess_; diff --git a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh index f666544836..afa77092df 100644 --- a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh @@ -19,7 +19,7 @@ namespace cuopt::linear_programming::detail { template class line_segment_recombiner_t : public recombiner_t { public: - line_segment_recombiner_t(mip_solver_context_t context, + line_segment_recombiner_t(mip_solver_context_t& context, i_t n_vars, line_segment_search_t& line_segment_search_, const raft::handle_t* handle_ptr) diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index f3fd67a523..383965d529 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -147,6 +147,8 @@ solution_t mip_solver_t::run_solver() return sol; } + context.work_unit_scheduler_.register_context(context.gpu_heur_loop); + namespace dual_simplex = cuopt::linear_programming::dual_simplex; std::future branch_and_bound_status_future; dual_simplex::user_problem_t branch_and_bound_problem(context.problem_ptr->handle_ptr); @@ -224,10 +226,14 @@ solution_t mip_solver_t::run_solver() context.branch_and_bound_ptr = branch_and_bound.get(); // Set the primal heuristics -> branch and bound callback - context.problem_ptr->branch_and_bound_callback = - std::bind(&dual_simplex::branch_and_bound_t::set_new_solution, - branch_and_bound.get(), - std::placeholders::_1); + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + context.problem_ptr->branch_and_bound_callback = + std::bind(&dual_simplex::branch_and_bound_t::set_new_solution, + branch_and_bound.get(), + std::placeholders::_1); + } + + context.work_unit_scheduler_.register_context(branch_and_bound->get_work_unit_context()); // Fork a thread for branch and bound // std::async and std::future allow us to get the return value of bb::solve() diff --git a/cpp/src/mip/solver_context.cuh b/cpp/src/mip/solver_context.cuh index 4cd990519e..52202e6c11 100644 --- a/cpp/src/mip/solver_context.cuh +++ b/cpp/src/mip/solver_context.cuh @@ -49,6 +49,9 @@ struct mip_solver_context_t { gpu_heur_loop.deterministic = settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; } + mip_solver_context_t(const mip_solver_context_t&) = delete; + mip_solver_context_t& operator=(const mip_solver_context_t&) = delete; + raft::handle_t const* const handle_ptr; problem_t* problem_ptr; dual_simplex::branch_and_bound_t* branch_and_bound_ptr{nullptr}; @@ -59,9 +62,10 @@ struct mip_solver_context_t { mip_solver_work_unit_predictors_t work_unit_predictors; // Work limit context for tracking work units in deterministic mode (shared across all timers in // GPU heuristic loop) - cuopt::work_limit_context_t gpu_heur_loop; - cuopt::work_limit_context_t cpu_branch_and_bound; // TODO: should be each per worker thread. - // Works for now since threads_b&b = 1 + work_limit_context_t gpu_heur_loop{"GPUHeur"}; + + // synchronization every 5 seconds for deterministic mode + work_unit_scheduler_t work_unit_scheduler_{5.0}; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp index c259abbc29..d6e726dcb8 100644 --- a/cpp/src/utilities/work_limit_timer.hpp +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -18,19 +18,27 @@ #include #include + +#include + #include "timer.hpp" +#include "work_unit_scheduler.hpp" namespace cuopt { -// Context for tracking global work units across multiple timers in a single thread in deterministic -// mode struct work_limit_context_t { double global_work_units_elapsed{0.0}; bool deterministic{false}; + work_unit_scheduler_t* scheduler{nullptr}; + std::string name; + + work_limit_context_t(const std::string& name) : name(name) {} void record_work(double work) { - if (deterministic) global_work_units_elapsed += work; + if (!deterministic) return; + global_work_units_elapsed += work; + if (scheduler) { scheduler->on_work_recorded(*this, global_work_units_elapsed); } } }; @@ -95,7 +103,6 @@ class work_limit_timer_t { int line = __builtin_LINE()) { if (deterministic && work_context) { - work_context->global_work_units_elapsed += work_units; CUOPT_LOG_DEBUG("%s:%d: %s(): Recorded %f work units in %fs, total %f", file, line, @@ -103,6 +110,7 @@ class work_limit_timer_t { work_units, timer.elapsed_time(), work_context->global_work_units_elapsed); + work_context->record_work(work_units); } } diff --git a/cpp/src/utilities/work_unit_scheduler.cpp b/cpp/src/utilities/work_unit_scheduler.cpp new file mode 100644 index 0000000000..0fe5197d37 --- /dev/null +++ b/cpp/src/utilities/work_unit_scheduler.cpp @@ -0,0 +1,183 @@ +/* + * 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 "work_unit_scheduler.hpp" + +#include "work_limit_timer.hpp" + +#include +#include +#include +#include + +#include + +namespace cuopt { + +work_unit_scheduler_t::work_unit_scheduler_t(double sync_interval) : sync_interval_(sync_interval) +{ +} + +void work_unit_scheduler_t::register_context(work_limit_context_t& ctx) +{ + std::lock_guard lock(mutex_); + contexts_.push_back(ctx); + callback_queues_[&ctx] = callback_queue_t{}; + last_sync_target_[&ctx] = 0.0; + ctx.scheduler = this; +} + +void work_unit_scheduler_t::deregister_context(work_limit_context_t& ctx) +{ + std::lock_guard lock(mutex_); + ctx.scheduler = nullptr; + contexts_.erase(std::remove_if(contexts_.begin(), + contexts_.end(), + [&ctx](const std::reference_wrapper& ref) { + return &ref.get() == &ctx; + }), + contexts_.end()); + callback_queues_.erase(&ctx); + last_sync_target_.erase(&ctx); + cv_.notify_all(); +} + +void work_unit_scheduler_t::on_work_recorded(work_limit_context_t& ctx, double total_work) +{ + if (verbose) { + double sync_target = current_sync_target(); + CUOPT_LOG_DEBUG("[%s] Work recorded: %f, sync_target: %f (gen %zu)", + ctx.name.c_str(), + total_work, + sync_target, + barrier_generation_); + } + + // Loop to handle large work increments that cross multiple sync points + while (total_work >= current_sync_target()) { + wait_at_sync_point(ctx, current_sync_target()); + } +} + +void work_unit_scheduler_t::queue_callback(work_limit_context_t& source, + work_limit_context_t& destination, + callback_t callback) +{ + std::lock_guard lock(mutex_); + double tag = source.global_work_units_elapsed; + auto it = callback_queues_.find(&destination); + if (it != callback_queues_.end()) { it->second.push({tag, std::move(callback)}); } +} + +double work_unit_scheduler_t::current_sync_target() const +{ + if (sync_interval_ <= 0) return std::numeric_limits::infinity(); + return (barrier_generation_ + 1) * sync_interval_; +} + +void work_unit_scheduler_t::wait_at_sync_point(work_limit_context_t& ctx, double sync_target) +{ + auto wait_start = std::chrono::high_resolution_clock::now(); + + std::unique_lock lock(mutex_); + + last_sync_target_[&ctx] = sync_target; + size_t my_generation = barrier_generation_; + contexts_at_barrier_++; + + if (verbose) { + CUOPT_LOG_DEBUG("[%s] Waiting at sync point %.2f (gen %zu, %zu/%zu contexts)", + ctx.name.c_str(), + sync_target, + my_generation, + contexts_at_barrier_, + contexts_.size()); + } + + if (contexts_at_barrier_ == contexts_.size()) { + current_sync_target_ = sync_target; + barrier_generation_++; + if (verbose) { + CUOPT_LOG_DEBUG("[%s] All contexts arrived, new generation %zu, notifying", + ctx.name.c_str(), + barrier_generation_); + } + cv_.notify_all(); + } else { + cv_.wait(lock, [&] { return barrier_generation_ != my_generation; }); + if (verbose) { CUOPT_LOG_DEBUG("[%s] Woke up from first wait", ctx.name.c_str()); } + } + + size_t my_exit_generation = exit_generation_; + + if (verbose) { CUOPT_LOG_DEBUG("[%s] Processing callbacks", ctx.name.c_str()); } + lock.unlock(); + process_callbacks_for_context(ctx, sync_target); + lock.lock(); + if (verbose) { CUOPT_LOG_DEBUG("[%s] Done processing callbacks", ctx.name.c_str()); } + + contexts_at_barrier_--; + if (contexts_at_barrier_ == 0) { + exit_generation_++; + if (verbose) { + CUOPT_LOG_DEBUG("[%s] All contexts finished callbacks at sync point %.2f (exit gen %zu)", + ctx.name.c_str(), + sync_target, + exit_generation_); + } + cv_.notify_all(); + } else { + if (verbose) { + CUOPT_LOG_DEBUG("[%s] Waiting for other contexts to finish callbacks (%zu remaining)", + ctx.name.c_str(), + contexts_at_barrier_); + } + cv_.wait(lock, [&] { return exit_generation_ != my_exit_generation; }); + if (verbose) { CUOPT_LOG_DEBUG("[%s] Woke up from second wait", ctx.name.c_str()); } + } + + if (verbose) { + auto wait_end = std::chrono::high_resolution_clock::now(); + double wait_ms = std::chrono::duration(wait_end - wait_start).count(); + CUOPT_LOG_DEBUG( + "[%s] Sync complete at %.2f, waited %.2f ms", ctx.name.c_str(), sync_target, wait_ms); + } +} + +void work_unit_scheduler_t::process_callbacks_for_context(work_limit_context_t& ctx, + double up_to_work_units) +{ + std::vector to_execute; + + { + std::lock_guard lock(mutex_); + auto it = callback_queues_.find(&ctx); + if (it == callback_queues_.end()) return; + + auto& queue = it->second; + while (!queue.empty() && queue.top().work_unit_tag <= up_to_work_units) { + to_execute.push_back(std::move(const_cast(queue.top()).callback)); + queue.pop(); + } + } + + for (auto& cb : to_execute) { + cb(); + } +} + +} // namespace cuopt diff --git a/cpp/src/utilities/work_unit_scheduler.hpp b/cpp/src/utilities/work_unit_scheduler.hpp new file mode 100644 index 0000000000..57f7d3181b --- /dev/null +++ b/cpp/src/utilities/work_unit_scheduler.hpp @@ -0,0 +1,80 @@ +/* + * 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 +#include +#include +#include +#include +#include + +namespace cuopt { + +struct work_limit_context_t; + +class work_unit_scheduler_t { + public: + using callback_t = std::function; + + explicit work_unit_scheduler_t(double sync_interval = 5.0); + + void set_sync_interval(double interval); + double get_sync_interval() const; + + void register_context(work_limit_context_t& ctx); + void deregister_context(work_limit_context_t& ctx); + void on_work_recorded(work_limit_context_t& ctx, double total_work); + void queue_callback(work_limit_context_t& source, + work_limit_context_t& destination, + callback_t callback); + + public: + bool verbose{false}; + double sync_interval_; + + private: + struct tagged_callback_t { + double work_unit_tag; + callback_t callback; + + bool operator>(const tagged_callback_t& other) const + { + return work_unit_tag > other.work_unit_tag; + } + }; + + using callback_queue_t = + std::priority_queue, std::greater<>>; + + double current_sync_target() const; + void wait_at_sync_point(work_limit_context_t& ctx, double sync_target); + void process_callbacks_for_context(work_limit_context_t& ctx, double up_to_work_units); + + std::vector> contexts_; + std::unordered_map callback_queues_; + std::unordered_map last_sync_target_; + + std::mutex mutex_; + std::condition_variable cv_; + size_t contexts_at_barrier_{0}; + double current_sync_target_{0}; + size_t barrier_generation_{0}; + size_t exit_generation_{0}; +}; + +} // namespace cuopt diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index 6b74581fc2..ddbb3286af 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -110,7 +110,7 @@ static uint32_t test_full_run_determinism(std::string path, problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - work_limit_context_t work_limit_context; + work_limit_context_t work_limit_context("DiversityManager"); diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); diversity_manager.run_solver(); @@ -165,7 +165,7 @@ static uint32_t test_initial_solution_determinism(std::string path, problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - work_limit_context_t work_limit_context; + work_limit_context_t work_limit_context("DiversityManager"); diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); diversity_manager.diversity_config.initial_solution_only = true; diversity_manager.run_solver(); @@ -221,7 +221,7 @@ static uint32_t test_recombiners_determinism(std::string path, problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - work_limit_context_t work_limit_context; + work_limit_context_t work_limit_context("DiversityManager"); diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); diversity_manager.diversity_config.dry_run = true; diversity_manager.run_solver(); diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index f616bda9a8..8b541958bd 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -149,7 +149,7 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) printf("LP optimal hash: 0x%x\n", detail::compute_hash(lp_optimal_solution)); printf("running mode: %d\n", mode); - work_limit_context_t work_limit_context; + work_limit_context_t work_limit_context("LocalSearch"); local_search.fp.timer = work_limit_timer_t(work_limit_context, 6000); detail::ls_config_t ls_config{}; diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index 392f3e8ddd..bc62fee2a0 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -124,7 +124,7 @@ uint32_t test_probing_cache_determinism(std::string path, detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); detail::bound_presolve_t bnd_prb(solver.context); - work_limit_context_t work_limit_context; + work_limit_context_t work_limit_context("ProbingCache"); // rely on the iteration limit compute_probing_cache( bnd_prb, problem, work_limit_timer_t(work_limit_context, std::numeric_limits::max())); From ce44aff3698a00edfa17a5abb67dc6b9aec89c9b Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 1 Dec 2025 16:43:03 +0000 Subject: [PATCH 097/225] cleanup --- .../linear_programming/cuopt/run_mip.cpp | 3 +- cpp/CMakeLists.txt | 20 +- .../restart_strategy/pdlp_restart_strategy.cu | 3 +- cpp/src/mip/diversity/diversity_manager.cu | 18 - .../recombiners/bound_prop_recombiner.cuh | 7 - .../mip/feasibility_jump/feasibility_jump.cu | 58 +-- .../feasibility_pump/feasibility_pump.cu | 30 +- .../line_segment_search.cu | 1 - cpp/src/mip/local_search/local_search.cu | 82 ++-- cpp/src/mip/local_search/local_search.cuh | 2 +- .../local_search/rounding/bounds_repair.cu | 3 +- .../local_search/rounding/constraint_prop.cu | 21 +- cpp/src/mip/presolve/bounds_presolve.cu | 2 +- cpp/src/mip/problem/problem.cu | 29 +- cpp/src/mip/problem/problem.cuh | 4 +- cpp/src/mip/solve.cu | 22 +- cpp/src/mip/utils.cuh | 3 - cpp/src/utilities/logger.cpp | 1 + cpp/src/utilities/seed_generator.cuh | 16 +- cpp/src/utilities/work_limit_timer.hpp | 2 + cpp/tests/CMakeLists.txt | 4 - cpp/tests/utilities/CMakeLists.txt | 1 - cpp/tests/utilities/Eval.h | 274 ------------- cpp/tests/utilities/Metric.h | 245 ------------ cpp/tests/utilities/helper_cupti.h | 165 -------- cpp/tests/utilities/test_cupti.cu | 361 ------------------ scripts/requirements.txt | 10 - 27 files changed, 98 insertions(+), 1289 deletions(-) delete mode 100644 cpp/tests/utilities/Eval.h delete mode 100644 cpp/tests/utilities/Metric.h delete mode 100644 cpp/tests/utilities/helper_cupti.h delete mode 100644 cpp/tests/utilities/test_cupti.cu delete mode 100644 scripts/requirements.txt diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 7db53358bd..323dac706f 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -206,8 +206,6 @@ int run_single_file(std::string file_path, settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; settings.presolve = true; - // settings.heuristics_only = true; - cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); @@ -406,6 +404,7 @@ int main(int argc, char* argv[]) if (program.is_used("--initial-solution-path")) { initial_solution_file = program.get("--initial-solution-path"); } + if (run_dir) { std::queue task_queue; std::queue gpu_queue; diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 7178c61ff7..97a8c62037 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -198,7 +198,6 @@ rapids_cpm_rapids_logger(BUILD_EXPORT_SET cuopt-exports INSTALL_EXPORT_SET cuopt create_logger_macros(CUOPT "cuopt::default_logger()" include/cuopt) find_package(CUDSS REQUIRED) -find_package(xgboost REQUIRED) if(BUILD_TESTS) include(cmake/thirdparty/get_gtest.cmake) @@ -316,23 +315,22 @@ target_link_libraries(cuopt rapids_logger::rapids_logger CCCL::CCCL raft::raft - xgboost::xgboost cuopt::mps_parser ${CUDSS_LIB_FILE} PRIVATE ${CUOPT_PRIVATE_CUDA_LIBS} ) -find_path(PAPI_INCLUDE_DIR papi.h) -find_library(PAPI_LIBRARY papi) +# find_path(PAPI_INCLUDE_DIR papi.h) +# find_library(PAPI_LIBRARY papi) -if (PAPI_INCLUDE_DIR AND PAPI_LIBRARY) - message(STATUS "Found PAPI in ${PAPI_INCLUDE_DIR}") - target_include_directories(cuopt PRIVATE ${PAPI_INCLUDE_DIR}) - target_link_libraries(cuopt PRIVATE ${PAPI_LIBRARY}) -else() - message(FATAL_ERROR "Could not find PAPI") -endif() +# if (PAPI_INCLUDE_DIR AND PAPI_LIBRARY) +# message(STATUS "Found PAPI in ${PAPI_INCLUDE_DIR}") +# target_include_directories(cuopt PRIVATE ${PAPI_INCLUDE_DIR}) +# target_link_libraries(cuopt PRIVATE ${PAPI_LIBRARY}) +# else() +# message(FATAL_ERROR "Could not find PAPI") +# endif() # ################################################################################################## diff --git a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu index 691d1c0545..5462785487 100644 --- a/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu +++ b/cpp/src/linear_programming/restart_strategy/pdlp_restart_strategy.cu @@ -1720,7 +1720,8 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( // Perform the reduction // Convert raw pointer to thrust::device_ptr to write directly device side through reduce // TODO - // use a guaranteed-deterministic reduce instead of thrust::reduce + // use a guaranteed-deterministic reduce instead of thrust::reduce which I'm not sure actually + // guarantees determinism (but in practice it does) thrust::device_ptr thrust_hrsp(high_radius_squared_.data()); *thrust_hrsp = thrust::reduce(handle_ptr_->get_thrust_policy(), transformed_begin, diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index e562f1e3cf..68f828229c 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -427,23 +427,6 @@ solution_t diversity_manager_t::run_solver() clamp_within_var_bounds(lp_optimal_solution, problem_ptr, problem_ptr->handle_ptr); } - // Run this 100 times with varying iteration limits - // for (int i = 0; i < 100; i++) { - // relaxed_lp_settings_t lp_settings; - // lp_settings.time_limit = lp_time_limit; - // lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; - // lp_settings.return_first_feasible = false; - // lp_settings.save_state = true; - // lp_settings.concurrent_halt = &global_concurrent_halt; - // lp_settings.has_initial_primal = false; - // lp_settings.iteration_limit = 100 + i * 100; - // rmm::device_uvector lp_optimal_solution_copy(lp_optimal_solution.size(), - // problem_ptr->handle_ptr->get_stream()); - // auto lp_result = - // get_relaxed_lp_solution(*problem_ptr, lp_optimal_solution_copy, lp_state, lp_settings); - // } - // exit(0); - if (ls.lp_optimal_exists) { solution_t lp_rounded_sol(*problem_ptr); lp_rounded_sol.copy_new_assignment(lp_optimal_solution); @@ -473,7 +456,6 @@ solution_t diversity_manager_t::run_solver() if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { rins.enable(); } generate_solution(timer.remaining_time(), false); - printf("=======================================================\n"); if (diversity_config.initial_solution_only) { return population.best_feasible(); } if (work_limit_reached()) { population.add_external_solutions_to_population(); diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index 2c85520f0e..a9844606f9 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -228,13 +228,6 @@ class bound_prop_recombiner_t : public recombiner_t { constraint_prop.single_rounding_only = false; offspring.compute_feasibility(); bool feasible_after_bounds_prop = offspring.get_feasible(); - cuopt_func_call(f_t excess_before = offspring.get_total_excess()); - CUOPT_LOG_ERROR("Excess before: %g, %g, %g, %g, feas %d", - offspring.get_total_excess(), - offspring.compute_max_constraint_violation(), - offspring.compute_max_int_violation(), - offspring.compute_max_variable_violation(), - feasible_after_bounds_prop); offspring.handle_ptr->sync_stream(); offspring.problem_ptr = a.problem_ptr; fixed_assignment = std::move(offspring.assignment); diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 927eb15753..da4f2edce1 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -651,19 +651,15 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream auto [grid_setval, blocks_setval] = setval_launch_dims; auto [grid_update_changed_constraints, blocks_update_changed_constraints] = update_changed_constraints_launch_dims; - auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; - auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; - auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; - auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; - - // use_graph = false; + auto [grid_resetmoves, blocks_resetmoves] = resetmoves_launch_dims; + auto [grid_resetmoves_bin, blocks_resetmoves_bin] = resetmoves_bin_launch_dims; + [[maybe_unused]] auto [grid_update_weights, blocks_update_weights] = update_weights_launch_dims; + [[maybe_unused]] auto [grid_lift_move, blocks_lift_move] = lift_move_launch_dims; auto& data = *climbers[climber_idx]; auto v = data.view(); settings.seed = cuopt::seed_generator::get_seed(); - // settings.iteration_limit = 10000; - // CUOPT_LOG_DEBUG("FJ: settings seed %d", settings.seed); - // ensure an updated copy of the settings is used device-side + // ensure an updated copy of the settings is used device-side raft::copy(v.settings, &settings, 1, climber_stream); bool is_binary_pb = pb_ptr->n_variables == thrust::count(handle_ptr->get_thrust_policy(), @@ -788,7 +784,6 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream 0, climber_stream); - (void)grid_update_weights; cudaLaunchCooperativeKernel((void*)handle_local_minimum_kernel, grid_update_weights, blocks_update_weights, @@ -802,39 +797,12 @@ void fj_t::run_step_device(const rmm::cuda_stream_view& climber_stream update_assignment_args, 0, climber_stream); - - // { - // printf("Changed constraints hash: %x, size: %d\n", compute_hash( - // make_span(data.constraints_changed, 0, - // data.constraints_changed_count.value(climber_stream)), climber_stream), - // data.constraints_changed_count.value(climber_stream)); - - // printf("before update: Violated constraints hash: %x, size: %d\n", compute_hash( - // make_span(data.violated_constraints.contents, 0, - // data.violated_constraints.set_size.value(climber_stream)), climber_stream), - // data.violated_constraints.set_size.value(climber_stream)); - // } - cudaLaunchKernel((void*)update_changed_constraints_kernel, 1, blocks_update_changed_constraints, kernel_args, 0, climber_stream); - - // TODO: figure out why the ordering of the violated constraints is ruined - // data.violated_constraints.sort(climber_stream); - // printf("[%d] iter: Violated constraints hash: %x\n", data.iterations.value(climber_stream), - // compute_hash( - // make_span(data.violated_constraints.contents, 0, - // data.violated_constraints.set_size.value(climber_stream)), climber_stream)); - - // { - // printf("Violated constraints hash: %x, size: %d\n", compute_hash( - // make_span(data.violated_constraints.contents, 0, - // data.violated_constraints.set_size.value(climber_stream)), climber_stream), - // data.violated_constraints.set_size.value(climber_stream)); - // } } if (use_graph) { @@ -1315,8 +1283,6 @@ i_t fj_t::solve(solution_t& solution) detail::compute_hash(cstr_left_weights), detail::compute_hash(cstr_right_weights)); - // settings.load_balancing_mode = fj_load_balancing_mode_t::ALWAYS_OFF; - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { settings.work_limit = settings.time_limit; } @@ -1457,20 +1423,6 @@ i_t fj_t::solve(solution_t& solution) CUOPT_LOG_DEBUG("FJ sol hash %x", solution.get_hash()); - // // Print compact feature vector summary - // char logbuf[4096]; - // int offset = 0; - // offset += snprintf(logbuf + offset, - // sizeof(logbuf) - offset, - // "FJ: iter=%d time=%g", - // iterations, - // timer.elapsed_time()); - // for (const auto& [name, value] : feature_vector) { - // offset += snprintf(logbuf + offset, sizeof(logbuf) - offset, " %s=%g", name.c_str(), value); - // if (offset >= (int)(sizeof(logbuf) - 32)) break; - // } - // CUOPT_LOG_INFO("%s", logbuf); - return is_new_feasible; } diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index 7e981a8657..837c1e5a67 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -137,7 +137,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tvariable_bounds, solution.handle_ptr->get_stream()); @@ -151,10 +151,6 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tinteger_indices, solution.handle_ptr->get_stream()); f_t obj_offset = 0; - // CUOPT_LOG_DEBUG("FP: h_integer_indices hash 0x%x", detail::compute_hash(h_integer_indices)); - // CUOPT_LOG_DEBUG("FP: h_assignment hash 0x%x", detail::compute_hash(h_assignment)); - // CUOPT_LOG_DEBUG("FP: h_variable_bounds hash 0x%x", detail::compute_hash(h_variable_bounds)); - // CUOPT_LOG_DEBUG("FP: h_last_projection hash 0x%x", detail::compute_hash(h_last_projection)); // for each integer add the variable and the distance constraints for (auto i : h_integer_indices) { auto h_var_bounds = h_variable_bounds[i]; @@ -192,8 +188,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t 0) { temp_p.insert_variables(h_variables); } @@ -229,8 +224,6 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t::infinity(); lp_settings.work_limit = time_limit; @@ -343,7 +336,6 @@ bool feasibility_pump_t::test_fj_feasible(solution_t& soluti } else { CUOPT_LOG_DEBUG("20%% FJ run found feasible!"); } - timer.record_work(fj.settings.work_limit); CUOPT_LOG_DEBUG("20%% FJ run finished, elapsed %fs remaining %fwu", fj.settings.time_limit, timer.remaining_time()); @@ -636,11 +628,6 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s n_integers = solution.compute_number_of_integers(); if (is_feasible && n_integers == solution.problem_ptr->n_integer_vars) { CUOPT_LOG_DEBUG("Feasible solution verified with LP!"); - f_t time_taken = start_time - timer.remaining_time(); - // CUOPT_LOG_INFO( - // "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_LP_VERIFIED", - // fp_iterations, - // time_taken); return true; } } @@ -655,20 +642,11 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s } if (timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); - f_t time_taken = start_time - timer.remaining_time(); - // CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f - // termination=TIME_LIMIT_AFTER_ROUND", - // fp_iterations, - // time_taken); return false; } if (is_feasible) { bool res = solution.compute_feasibility(); cuopt_assert(res, "Feasibility issue"); - f_t time_taken = start_time - timer.remaining_time(); - // CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_AFTER_ROUND", - // fp_iterations, - // time_taken); return true; } // do the cycle check if alpha diff is small enough @@ -686,10 +664,6 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s remaining_time_end_fp, fp_fj_cycle_time_begin, total_fp_time_until_cycle); - f_t time_taken = start_time - timer.remaining_time(); - // CUOPT_LOG_INFO("FP_RESULT: iterations=%d time_taken=%.6f termination=ASSIGNMENT_CYCLE", - // fp_iterations, - // time_taken); return false; } cycle_queue.n_iterations_without_cycle++; diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu index 766cb8ee21..8454daa575 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu @@ -229,7 +229,6 @@ bool line_segment_search_t::search_line_segment( best_feasible_cost, curr_cost); } - // cuopt_assert(context.gpu_heur_loop, ""); if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { if (timer.check_time_limit()) { break; } } diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 115b9b7751..8e4dc250e8 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -63,10 +63,6 @@ void local_search_t::start_cpufj_scratch_threads(population_t default_weights(context.problem_ptr->n_constraints, 1.); - fj_settings_t cpu_fj_settings{}; - cpu_fj_settings.time_limit = std::numeric_limits::infinity(); - cpu_fj_settings.iteration_limit = 10000; // would like 10 samples per instance - solution_t solution(*context.problem_ptr); thrust::fill(solution.handle_ptr->get_thrust_policy(), solution.assignment.begin(), @@ -80,24 +76,22 @@ void local_search_t::start_cpufj_scratch_threads(population_t 0); - cpu_fj.fj_cpu->log_prefix = "******* scratch " + std::to_string(counter) + ": "; - // if (!context.settings.deterministic) { - // cpu_fj.fj_cpu->improvement_callback = [this, &population, &cpu_fj]( - // f_t obj, const std::vector& h_vec) { - // population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); - // if (obj < local_search_best_obj) { - // CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", - // context.problem_ptr->get_user_obj_from_solver_obj(obj), - // context.problem_ptr->get_user_obj_from_solver_obj( - // population.is_feasible() ? population.best_feasible().get_objective() - // : std::numeric_limits::max())); - // local_search_best_obj = obj; - // } - // }; - // } + cpu_fj.fj_cpu->log_prefix = "******* scratch " + std::to_string(counter) + ": "; + cpu_fj.fj_cpu->improvement_callback = [this, &population, &cpu_fj]( + f_t obj, const std::vector& h_vec) { + population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); + if (obj < local_search_best_obj) { + CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", + context.problem_ptr->get_user_obj_from_solver_obj(obj), + context.problem_ptr->get_user_obj_from_solver_obj( + population.is_feasible() ? population.best_feasible().get_objective() + : std::numeric_limits::max())); + local_search_best_obj = obj; + } + }; counter++; }; @@ -114,29 +108,24 @@ void local_search_t::start_cpufj_lptopt_scratch_threads( std::vector default_weights(context.problem_ptr->n_constraints, 1.); - fj_settings_t cpu_fj_settings{}; - cpu_fj_settings.time_limit = std::numeric_limits::infinity(); - solution_t solution_lp(*context.problem_ptr); solution_lp.copy_new_assignment(host_copy(lp_optimal_solution)); solution_lp.round_random_nearest(500); scratch_cpu_fj_on_lp_opt.fj_cpu = - fj.create_cpu_climber(solution_lp, default_weights, default_weights, 0., cpu_fj_settings, true); + fj.create_cpu_climber(solution_lp, default_weights, default_weights, 0.); scratch_cpu_fj_on_lp_opt.fj_cpu->log_prefix = "******* scratch on LP optimal: "; - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { - scratch_cpu_fj_on_lp_opt.fj_cpu->improvement_callback = - [this, &population](f_t obj, const std::vector& h_vec) { - population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); - if (obj < local_search_best_obj) { - CUOPT_LOG_DEBUG("******* New local search best obj %g, best overall %g", - context.problem_ptr->get_user_obj_from_solver_obj(obj), - context.problem_ptr->get_user_obj_from_solver_obj( - population.is_feasible() ? population.best_feasible().get_objective() - : std::numeric_limits::max())); - local_search_best_obj = obj; - } - }; - } + scratch_cpu_fj_on_lp_opt.fj_cpu->improvement_callback = + [this, &population](f_t obj, const std::vector& h_vec) { + population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); + if (obj < local_search_best_obj) { + CUOPT_LOG_DEBUG("******* New local search best obj %g, best overall %g", + context.problem_ptr->get_user_obj_from_solver_obj(obj), + context.problem_ptr->get_user_obj_from_solver_obj( + population.is_feasible() ? population.best_feasible().get_objective() + : std::numeric_limits::max())); + local_search_best_obj = obj; + } + }; // default weights cudaDeviceSynchronize(); @@ -182,8 +171,10 @@ bool local_search_t::do_fj_solve(solution_t& solution, auto solution_copy = solution; // Start CPU solver in background thread - for (auto& cpu_fj : ls_cpu_fj) { - cpu_fj.start_cpu_solver(); + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + for (auto& cpu_fj : ls_cpu_fj) { + cpu_fj.start_cpu_solver(); + } } // Run GPU solver and measure execution time @@ -220,6 +211,9 @@ bool local_search_t::do_fj_solve(solution_t& solution, bool gpu_feasible = solution.get_feasible(); bool cpu_feasible = cpu_sol_found && solution_cpu.get_feasible(); + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + cpu_feasible = false; // ignore CPUFJ in deterministic mode + } static std::unordered_map total_calls; static std::unordered_map cpu_better; @@ -441,12 +435,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu fj.settings.n_of_minimums_for_exit = 20000; fj.settings.update_weights = true; fj.settings.feasibility_run = false; - f_t fj_time_limit = 30.; - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - fj_time_limit = std::numeric_limits::infinity(); - fj.settings.iteration_limit = 100'000; - } - f_t time_limit = std::min(fj_time_limit, timer.remaining_time()); + f_t time_limit = std::min(30., timer.remaining_time()); do_fj_solve(solution, fj, time_limit, "on_lp_optimal"); return solution.get_feasible(); } @@ -700,7 +689,6 @@ bool local_search_t::run_fp(solution_t& solution, is_feasible = false; break; } - printf("------------------------------------------------------------------------\n"); CUOPT_LOG_DEBUG("fp_loop it %d last_improved_iteration %d", i, last_improved_iteration); population_ptr->add_external_solutions_to_population(); if (population_ptr->preempt_heuristic_solver_.load()) { diff --git a/cpp/src/mip/local_search/local_search.cuh b/cpp/src/mip/local_search/local_search.cuh index 9e688988f5..3316160aba 100644 --- a/cpp/src/mip/local_search/local_search.cuh +++ b/cpp/src/mip/local_search/local_search.cuh @@ -120,7 +120,7 @@ class local_search_t { feasibility_pump_t fp; std::mt19937 rng; - std::array, 0> ls_cpu_fj; + std::array, 8> ls_cpu_fj; std::array, 1> scratch_cpu_fj; cpu_fj_thread_t scratch_cpu_fj_on_lp_opt; problem_t problem_with_objective_cut; diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index 507c978753..03f2c47ed5 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -405,7 +405,8 @@ bool bounds_repair_t::repair_problem(problem_t& problem, curr_violation = best_violation; best_bounds.update_from(problem, handle_ptr); i_t no_candidate_in_a_row = 0; - i_t iter_limit = 20; + i_t iter_limit = std::numeric_limits::max(); + if (problem.deterministic) { iter_limit = 20; } while (h_n_violated_cstr > 0 && iter_limit-- > 0) { CUOPT_LOG_TRACE("Bounds repair loop: n_violated %d best_violation %f curr_violation %f", h_n_violated_cstr, diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index 77ceb37758..a037886c26 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -218,12 +218,10 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t return bounds_interval_1 < bounds_interval_2; } }); - - // CUOPT_LOG_DEBUG("hash vars 0x%x", detail::compute_hash(vars)); - // now do the suffling, for that we need to assign some random values to rnd array - // we will sort this rnd array and the vars in subsections, so that each subsection will be - // shuffled in total we will have 3(binary, ternary and rest) x 7 intervals = 21 subsections. - // first extract these subsections from the data + // now do the suffling, for that we need to assign some random values to rnd array + // we will sort this rnd array and the vars in subsections, so that each subsection will be + // shuffled in total we will have 3(binary, ternary and rest) x 7 intervals = 21 subsections. + // first extract these subsections from the data rmm::device_uvector subsection_offsets(size_of_subsections, sol.handle_ptr->get_stream()); thrust::fill( sol.handle_ptr->get_thrust_policy(), subsection_offsets.begin(), subsection_offsets.end(), -1); @@ -273,10 +271,7 @@ void constraint_prop_t::sort_by_interval_and_frac(solution_t } } }); - - // CUOPT_LOG_DEBUG("hash subsection_offsets 0x%x", detail::compute_hash(subsection_offsets)); auto random_vector = get_random_uniform_vector((i_t)vars.size(), rng); - // CUOPT_LOG_DEBUG("hash random_vector 0x%x", detail::compute_hash(random_vector)); rmm::device_uvector device_random_vector(random_vector.size(), sol.handle_ptr->get_stream()); raft::copy(device_random_vector.data(), random_vector.data(), @@ -422,7 +417,6 @@ void constraint_prop_t::collapse_crossing_bounds(problem_t& template void constraint_prop_t::set_bounds_on_fixed_vars(solution_t& sol) { - CUOPT_LOG_DEBUG("Bound hash before 0x%x", detail::compute_hash(sol.problem_ptr->variable_bounds)); auto assgn = make_span(sol.assignment); auto var_bounds = make_span(sol.problem_ptr->variable_bounds); thrust::for_each(sol.handle_ptr->get_thrust_policy(), @@ -434,7 +428,6 @@ void constraint_prop_t::set_bounds_on_fixed_vars(solution_t& var_bounds[idx] = typename type_2::type{var_val, var_val}; } }); - CUOPT_LOG_DEBUG("Bound hash after 0x%x", detail::compute_hash(sol.problem_ptr->variable_bounds)); } template @@ -944,15 +937,9 @@ bool constraint_prop_t::find_integer( } // do the sort if the problem is not ii. crossing bounds might cause some issues on the sort order else { - // CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x, sol 0x%x before sort\n", - // detail::compute_hash(unset_integer_vars), - // sol.get_hash()); // this is a sort to have initial shuffling, so that stable sort within will keep the order and // some randomness will be achieved sort_by_interval_and_frac(sol, make_span(unset_integer_vars), rng); - // CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x sol 0x%x after sort\n", - // detail::compute_hash(unset_integer_vars), - // sol.get_hash()); } set_host_bounds(sol); size_t set_count = 0; diff --git a/cpp/src/mip/presolve/bounds_presolve.cu b/cpp/src/mip/presolve/bounds_presolve.cu index a820fb0804..06c924430f 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cu +++ b/cpp/src/mip/presolve/bounds_presolve.cu @@ -171,7 +171,7 @@ termination_criterion_t bound_presolve_t::bound_update_loop(problem_t< { termination_criterion_t criteria = termination_criterion_t::ITERATION_LIMIT; - // CHANGE + // CHANGE once we have a work predictor if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { timer = timer_t(std::numeric_limits::infinity()); settings.iteration_limit = std::min(settings.iteration_limit, 50); diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 1832ee38e9..cdac5523a5 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -100,11 +100,13 @@ void problem_t::op_problem_cstr_body(const optimization_problem_t problem_t::problem_t( const optimization_problem_t& problem_, - const typename mip_solver_settings_t::tolerances_t tolerances_) + const typename mip_solver_settings_t::tolerances_t tolerances_, + bool deterministic_) : original_problem_ptr(&problem_), handle_ptr(problem_.get_handle_ptr()), integer_fixed_variable_map(problem_.get_n_variables(), problem_.get_handle_ptr()->get_stream()), tolerances(tolerances_), + deterministic(deterministic_), n_variables(problem_.get_n_variables()), n_constraints(problem_.get_n_constraints()), n_binary_vars(0), @@ -148,6 +150,7 @@ template problem_t::problem_t(const problem_t& problem_) : original_problem_ptr(problem_.original_problem_ptr), tolerances(problem_.tolerances), + deterministic(problem_.deterministic), handle_ptr(problem_.handle_ptr), integer_fixed_problem(problem_.integer_fixed_problem), integer_fixed_variable_map(problem_.integer_fixed_variable_map, handle_ptr->get_stream()), @@ -198,6 +201,7 @@ template problem_t::problem_t(const problem_t& problem_, bool no_deep_copy) : original_problem_ptr(problem_.original_problem_ptr), tolerances(problem_.tolerances), + deterministic(problem_.deterministic), handle_ptr(problem_.handle_ptr), integer_fixed_problem(problem_.integer_fixed_problem), integer_fixed_variable_map(problem_.n_variables, handle_ptr->get_stream()), @@ -859,6 +863,9 @@ void problem_t::compute_related_variables(double time_limit) handle_ptr->sync_stream(); + // CHANGE + if (deterministic) { time_limit = std::numeric_limits::infinity(); } + // previously used constants were based on 40GB of memory. Scale accordingly on smaller GPUs // We can't rely on querying free memory or allocation try/catch // since this would break determinism guarantees (GPU may be shared by other processes) @@ -885,7 +892,7 @@ void problem_t::compute_related_variables(double time_limit) i_t output_offset = 0; i_t related_var_offset = 0; - // auto start_time = std::chrono::high_resolution_clock::now(); + auto start_time = std::chrono::high_resolution_clock::now(); for (i_t i = 0;; ++i) { i_t slice_size = std::min(max_slice_size, n_variables - i * max_slice_size); if (slice_size <= 0) break; @@ -914,13 +921,12 @@ void problem_t::compute_related_variables(double time_limit) i_t related_var_base = related_variables.size(); related_variables.resize(related_variables.size() + array_size, handle_ptr->get_stream()); - // auto current_time = std::chrono::high_resolution_clock::now(); + auto current_time = std::chrono::high_resolution_clock::now(); // if the related variable array would wind up being too large for available memory, abort // TODO this used to be 1e9 - if (related_variables.size() > 1e9 * size_factor) { - // || - // std::chrono::duration_cast(current_time - start_time).count() > - // time_limit) { + if (related_variables.size() > 1e9 * size_factor || + std::chrono::duration_cast(current_time - start_time).count() > + time_limit) { CUOPT_LOG_DEBUG( "Computing the related variable array would use too much memory or time, aborting\n"); related_variables.resize(0, handle_ptr->get_stream()); @@ -1288,13 +1294,8 @@ problem_t problem_t::get_problem_after_fixing_vars( // total_time_taken); // if the fixing is greater than 150, mark this as expensive. // this way we can avoid frequent fixings for this problem - - // TODO: CHANGE - // constexpr double expensive_time_threshold = 150; - // if (time_taken > expensive_time_threshold) { expensive_to_fix_vars = true; } - CUOPT_LOG_DEBUG("Model fingerprint after fixing: 0x%x, expensive? %d", - problem.get_fingerprint(), - expensive_to_fix_vars); + constexpr double expensive_time_threshold = 150; + if (time_taken > expensive_time_threshold && !deterministic) { expensive_to_fix_vars = true; } return problem; } diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index 7e9a4f93a6..c90bb5577c 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -48,7 +48,8 @@ template class problem_t { public: problem_t(const optimization_problem_t& problem, - const typename mip_solver_settings_t::tolerances_t tolerances_ = {}); + const typename mip_solver_settings_t::tolerances_t tolerances_ = {}, + bool deterministic = false); problem_t() = delete; // copy constructor problem_t(const problem_t& problem); @@ -220,6 +221,7 @@ class problem_t { bool maximize{false}; bool is_binary_pb{false}; bool empty{false}; + bool deterministic{false}; // Auxiliary problem statistics double sparsity{0.0}; diff --git a/cpp/src/mip/solve.cu b/cpp/src/mip/solve.cu index 37628cd8a6..44ad5fce98 100644 --- a/cpp/src/mip/solve.cu +++ b/cpp/src/mip/solve.cu @@ -37,7 +37,6 @@ #include #include -#include namespace cuopt::linear_programming { @@ -140,6 +139,9 @@ mip_solution_t run_mip(detail::problem_t& problem, auto sol = scaled_sol.get_solution( is_feasible_before_scaling || is_feasible_after_unscaling, solver.get_solver_stats(), false); + + // TODO: RESTORE THIS + // detail::print_solution(scaled_problem.handle_ptr, sol.get_solution()); return sol; } @@ -162,15 +164,6 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, // This needs to be called before pdlp is initialized init_handler(op_problem.get_handle_ptr()); - auto retval = PAPI_library_init(PAPI_VER_CURRENT); - if (retval != PAPI_VER_CURRENT && retval > 0) { - fprintf(stderr, "PAPI library version mismatch!\n"); - exit(1); - } else if (retval < 0) { - fprintf(stderr, "PAPI initialization error!\n"); - exit(1); - } - print_version_info(); raft::common::nvtx::range fun_scope("Running solver"); @@ -198,7 +191,8 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, double presolve_time = 0.0; std::unique_ptr> presolver; - detail::problem_t problem(op_problem, settings.get_tolerances()); + detail::problem_t problem( + op_problem, settings.get_tolerances(), settings.determinism_mode == CUOPT_MODE_DETERMINISTIC); auto run_presolve = settings.presolve; run_presolve = run_presolve && settings.get_mip_callbacks().empty(); @@ -211,9 +205,9 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, // allocate not more than 10% of the time limit to presolve. // Note that this is not the presolve time, but the time limit for presolve. double presolve_time_limit = std::min(0.1 * time_limit, 60.0); - - // TODO FIX - presolve_time_limit = std::numeric_limits::infinity(); + if (settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + presolve_time_limit = std::numeric_limits::infinity(); + } const bool dual_postsolve = false; presolver = std::make_unique>(); auto result = presolver->apply(op_problem, diff --git a/cpp/src/mip/utils.cuh b/cpp/src/mip/utils.cuh index 1550c1caa3..ba5de9bb42 100644 --- a/cpp/src/mip/utils.cuh +++ b/cpp/src/mip/utils.cuh @@ -265,9 +265,6 @@ f_t compute_objective_from_vec(const rmm::device_uvector& assignment, assignment.end(), objective_coefficients.begin(), 0.); - // CUOPT_LOG_DEBUG("compute_objective_from_vec: input %x coefs %x obj %x", - // detail::compute_hash(assignment), detail::compute_hash(objective_coefficients), - // detail::compute_hash(computed_obj)); return computed_obj; } diff --git a/cpp/src/utilities/logger.cpp b/cpp/src/utilities/logger.cpp index 6a99eb7bc2..a16c49c113 100644 --- a/cpp/src/utilities/logger.cpp +++ b/cpp/src/utilities/logger.cpp @@ -81,6 +81,7 @@ rapids_logger::sink_ptr default_sink() * @return std::string The default log pattern. */ inline std::string default_pattern() { return "[%Y-%m-%d %H:%M:%S:%f] [%n] [%-6l] %v"; } + /** * @brief Returns the default log level for the global logger. * diff --git a/cpp/src/utilities/seed_generator.cuh b/cpp/src/utilities/seed_generator.cuh index 50bd381636..7692ff4676 100644 --- a/cpp/src/utilities/seed_generator.cuh +++ b/cpp/src/utilities/seed_generator.cuh @@ -31,16 +31,14 @@ class seed_generator { set_seed(seed1 + ((seed0 + seed1) * (seed0 + seed1 + 1) / 2), seeds...); } -#if 0 +#if SEED_GENERATOR_DEBUG static int64_t get_seed(const char* caller = __builtin_FUNCTION(), - const char* file = __builtin_FILE(), - int line = __builtin_LINE()) { - - printf("&&&&&&& SEED CALLED BY %s:%d: %s() ***\n", - file, - line, - caller); - return seed_++; } + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) + { + printf("&&&&&&& SEED CALLED BY %s:%d: %s() ***\n", file, line, caller); + return seed_++; + } #else static int64_t get_seed() { return seed_++; } #endif diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp index d6e726dcb8..862d276b7d 100644 --- a/cpp/src/utilities/work_limit_timer.hpp +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -68,6 +68,8 @@ class work_limit_timer_t { const char* file = __builtin_FILE(), int line = __builtin_LINE()) const noexcept { + // TODO check against global timer + if (deterministic) { if (!work_context) { return false; } // Check if global work has exceeded our budget (snapshot + limit) diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 77e83e6972..aeaf656f3b 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -30,10 +30,6 @@ endif() set(CUOPT_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -if (NOT TARGET CUDA::toolkit) - find_package(CUDAToolkit REQUIRED) -endif() - if (EXISTS "${CUDAToolkit_LIBRARY_ROOT}/extras/CUPTI/lib64") # NVIDIA installer layout: set(cuopt_cupti_root "${CUDAToolkit_LIBRARY_ROOT}/extras/CUPTI") diff --git a/cpp/tests/utilities/CMakeLists.txt b/cpp/tests/utilities/CMakeLists.txt index c95d048c05..5f9e6d5e82 100644 --- a/cpp/tests/utilities/CMakeLists.txt +++ b/cpp/tests/utilities/CMakeLists.txt @@ -5,4 +5,3 @@ # Add CLI end-to-end test ConfigureTest(CLI_TEST test_cli.cpp) -#ConfigureTest(CUPTI_TEST test_cupti.cu) diff --git a/cpp/tests/utilities/Eval.h b/cpp/tests/utilities/Eval.h deleted file mode 100644 index ccafcd1aea..0000000000 --- a/cpp/tests/utilities/Eval.h +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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 -#include -#include - -template - -class ScopeExit { - public: - ScopeExit(T t) : t(t) {} - ~ScopeExit() { t(); } - T t; -}; - -template -ScopeExit MoveScopeExit(T t) -{ - return ScopeExit(t); -}; - -#define NV_ANONYMOUS_VARIABLE_DIRECT(name, line) name##line -#define NV_ANONYMOUS_VARIABLE_INDIRECT(name, line) NV_ANONYMOUS_VARIABLE_DIRECT(name, line) - -#define SCOPE_EXIT(func) \ - const auto NV_ANONYMOUS_VARIABLE_INDIRECT(EXIT, __LINE__) = MoveScopeExit([=]() { func; }) - -namespace NV { -namespace Metric { -namespace Eval { -std::string GetHwUnit(const std::string& metricName) -{ - return metricName.substr(0, metricName.find("__", 0)); -} - -bool GetMetricGpuValue(std::string chipName, - const std::vector& counterDataImage, - const std::vector& metricNames, - std::vector& metricNameValueMap, - const uint8_t* pCounterAvailabilityImage) -{ - if (!counterDataImage.size()) { - std::cout << "Counter Data Image is empty!\n"; - return false; - } - - NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params calculateScratchBufferSizeParam = { - NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params_STRUCT_SIZE}; - calculateScratchBufferSizeParam.pChipName = chipName.c_str(); - calculateScratchBufferSizeParam.pCounterAvailabilityImage = pCounterAvailabilityImage; - RETURN_IF_NVPW_ERROR( - false, NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize(&calculateScratchBufferSizeParam)); - - std::vector scratchBuffer(calculateScratchBufferSizeParam.scratchBufferSize); - NVPW_CUDA_MetricsEvaluator_Initialize_Params metricEvaluatorInitializeParams = { - NVPW_CUDA_MetricsEvaluator_Initialize_Params_STRUCT_SIZE}; - metricEvaluatorInitializeParams.scratchBufferSize = scratchBuffer.size(); - metricEvaluatorInitializeParams.pScratchBuffer = scratchBuffer.data(); - metricEvaluatorInitializeParams.pChipName = chipName.c_str(); - metricEvaluatorInitializeParams.pCounterAvailabilityImage = pCounterAvailabilityImage; - RETURN_IF_NVPW_ERROR(false, - NVPW_CUDA_MetricsEvaluator_Initialize(&metricEvaluatorInitializeParams)); - NVPW_MetricsEvaluator* metricEvaluator = metricEvaluatorInitializeParams.pMetricsEvaluator; - - NVPW_CounterData_GetNumRanges_Params getNumRangesParams = { - NVPW_CounterData_GetNumRanges_Params_STRUCT_SIZE}; - getNumRangesParams.pCounterDataImage = counterDataImage.data(); - RETURN_IF_NVPW_ERROR(false, NVPW_CounterData_GetNumRanges(&getNumRangesParams)); - - bool isolated = true; - bool keepInstances = true; - for (size_t metricIndex = 0; metricIndex < metricNames.size(); ++metricIndex) { - std::string reqName; - NV::Metric::Parser::ParseMetricNameString( - metricNames[metricIndex], &reqName, &isolated, &keepInstances); - NVPW_MetricEvalRequest metricEvalRequest; - NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params convertMetricToEvalRequest = { - NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params_STRUCT_SIZE}; - convertMetricToEvalRequest.pMetricsEvaluator = metricEvaluator; - convertMetricToEvalRequest.pMetricName = reqName.c_str(); - convertMetricToEvalRequest.pMetricEvalRequest = &metricEvalRequest; - convertMetricToEvalRequest.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; - RETURN_IF_NVPW_ERROR( - false, - NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest(&convertMetricToEvalRequest)); - - MetricNameValue metricNameValue; - metricNameValue.numRanges = getNumRangesParams.numRanges; - metricNameValue.metricName = metricNames[metricIndex]; - for (size_t rangeIndex = 0; rangeIndex < getNumRangesParams.numRanges; ++rangeIndex) { - NVPW_Profiler_CounterData_GetRangeDescriptions_Params getRangeDescParams = { - NVPW_Profiler_CounterData_GetRangeDescriptions_Params_STRUCT_SIZE}; - getRangeDescParams.pCounterDataImage = counterDataImage.data(); - getRangeDescParams.rangeIndex = rangeIndex; - RETURN_IF_NVPW_ERROR(false, - NVPW_Profiler_CounterData_GetRangeDescriptions(&getRangeDescParams)); - std::vector descriptionPtrs(getRangeDescParams.numDescriptions); - getRangeDescParams.ppDescriptions = descriptionPtrs.data(); - RETURN_IF_NVPW_ERROR(false, - NVPW_Profiler_CounterData_GetRangeDescriptions(&getRangeDescParams)); - - std::string rangeName; - for (size_t descriptionIndex = 0; descriptionIndex < getRangeDescParams.numDescriptions; - ++descriptionIndex) { - if (descriptionIndex) { rangeName += "/"; } - rangeName += descriptionPtrs[descriptionIndex]; - } - - NVPW_MetricsEvaluator_SetDeviceAttributes_Params setDeviceAttribParams = { - NVPW_MetricsEvaluator_SetDeviceAttributes_Params_STRUCT_SIZE}; - setDeviceAttribParams.pMetricsEvaluator = metricEvaluator; - setDeviceAttribParams.pCounterDataImage = counterDataImage.data(); - setDeviceAttribParams.counterDataImageSize = counterDataImage.size(); - RETURN_IF_NVPW_ERROR(false, - NVPW_MetricsEvaluator_SetDeviceAttributes(&setDeviceAttribParams)); - - double metricValue = 0.0; - NVPW_MetricsEvaluator_EvaluateToGpuValues_Params evaluateToGpuValuesParams = { - NVPW_MetricsEvaluator_EvaluateToGpuValues_Params_STRUCT_SIZE}; - evaluateToGpuValuesParams.pMetricsEvaluator = metricEvaluator; - evaluateToGpuValuesParams.pMetricEvalRequests = &metricEvalRequest; - evaluateToGpuValuesParams.numMetricEvalRequests = 1; - evaluateToGpuValuesParams.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; - evaluateToGpuValuesParams.metricEvalRequestStrideSize = sizeof(NVPW_MetricEvalRequest); - evaluateToGpuValuesParams.pCounterDataImage = counterDataImage.data(); - evaluateToGpuValuesParams.counterDataImageSize = counterDataImage.size(); - evaluateToGpuValuesParams.rangeIndex = rangeIndex; - evaluateToGpuValuesParams.isolated = true; - evaluateToGpuValuesParams.pMetricValues = &metricValue; - RETURN_IF_NVPW_ERROR(false, - NVPW_MetricsEvaluator_EvaluateToGpuValues(&evaluateToGpuValuesParams)); - metricNameValue.rangeNameMetricValueMap.push_back(std::make_pair(rangeName, metricValue)); - } - metricNameValueMap.push_back(metricNameValue); - } - - NVPW_MetricsEvaluator_Destroy_Params metricEvaluatorDestroyParams = { - NVPW_MetricsEvaluator_Destroy_Params_STRUCT_SIZE}; - metricEvaluatorDestroyParams.pMetricsEvaluator = metricEvaluator; - RETURN_IF_NVPW_ERROR(false, NVPW_MetricsEvaluator_Destroy(&metricEvaluatorDestroyParams)); - return true; -} - -bool PrintMetricValues(std::string chipName, - const std::vector& counterDataImage, - const std::vector& metricNames, - const uint8_t* pCounterAvailabilityImage) -{ - if (!counterDataImage.size()) { - std::cout << "Counter Data Image is empty!\n"; - return false; - } - - NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params calculateScratchBufferSizeParam = { - NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params_STRUCT_SIZE}; - calculateScratchBufferSizeParam.pChipName = chipName.c_str(); - calculateScratchBufferSizeParam.pCounterAvailabilityImage = pCounterAvailabilityImage; - RETURN_IF_NVPW_ERROR( - false, NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize(&calculateScratchBufferSizeParam)); - - std::vector scratchBuffer(calculateScratchBufferSizeParam.scratchBufferSize); - NVPW_CUDA_MetricsEvaluator_Initialize_Params metricEvaluatorInitializeParams = { - NVPW_CUDA_MetricsEvaluator_Initialize_Params_STRUCT_SIZE}; - metricEvaluatorInitializeParams.scratchBufferSize = scratchBuffer.size(); - metricEvaluatorInitializeParams.pScratchBuffer = scratchBuffer.data(); - metricEvaluatorInitializeParams.pChipName = chipName.c_str(); - metricEvaluatorInitializeParams.pCounterAvailabilityImage = pCounterAvailabilityImage; - metricEvaluatorInitializeParams.pCounterDataImage = counterDataImage.data(); - metricEvaluatorInitializeParams.counterDataImageSize = counterDataImage.size(); - RETURN_IF_NVPW_ERROR(false, - NVPW_CUDA_MetricsEvaluator_Initialize(&metricEvaluatorInitializeParams)); - NVPW_MetricsEvaluator* metricEvaluator = metricEvaluatorInitializeParams.pMetricsEvaluator; - - NVPW_CounterData_GetNumRanges_Params getNumRangesParams = { - NVPW_CounterData_GetNumRanges_Params_STRUCT_SIZE}; - getNumRangesParams.pCounterDataImage = counterDataImage.data(); - RETURN_IF_NVPW_ERROR(false, NVPW_CounterData_GetNumRanges(&getNumRangesParams)); - - std::cout << "\n" - << std::setw(40) << std::left << "Range Name" << std::setw(100) << std::left - << "Metric Name" - << "Metric Value" << std::endl; - std::cout << std::setfill('-') << std::setw(160) << "" << std::setfill(' ') << std::endl; - - std::string reqName; - bool isolated = true; - bool keepInstances = true; - for (std::string metricName : metricNames) { - NV::Metric::Parser::ParseMetricNameString(metricName, &reqName, &isolated, &keepInstances); - NVPW_MetricEvalRequest metricEvalRequest; - NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params convertMetricToEvalRequest = { - NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params_STRUCT_SIZE}; - convertMetricToEvalRequest.pMetricsEvaluator = metricEvaluator; - convertMetricToEvalRequest.pMetricName = reqName.c_str(); - convertMetricToEvalRequest.pMetricEvalRequest = &metricEvalRequest; - convertMetricToEvalRequest.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; - RETURN_IF_NVPW_ERROR( - false, - NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest(&convertMetricToEvalRequest)); - - for (size_t rangeIndex = 0; rangeIndex < getNumRangesParams.numRanges; ++rangeIndex) { - NVPW_Profiler_CounterData_GetRangeDescriptions_Params getRangeDescParams = { - NVPW_Profiler_CounterData_GetRangeDescriptions_Params_STRUCT_SIZE}; - getRangeDescParams.pCounterDataImage = counterDataImage.data(); - getRangeDescParams.rangeIndex = rangeIndex; - RETURN_IF_NVPW_ERROR(false, - NVPW_Profiler_CounterData_GetRangeDescriptions(&getRangeDescParams)); - std::vector descriptionPtrs(getRangeDescParams.numDescriptions); - getRangeDescParams.ppDescriptions = descriptionPtrs.data(); - RETURN_IF_NVPW_ERROR(false, - NVPW_Profiler_CounterData_GetRangeDescriptions(&getRangeDescParams)); - - std::string rangeName; - for (size_t descriptionIndex = 0; descriptionIndex < getRangeDescParams.numDescriptions; - ++descriptionIndex) { - if (descriptionIndex) { rangeName += "/"; } - rangeName += descriptionPtrs[descriptionIndex]; - } - - NVPW_MetricsEvaluator_SetDeviceAttributes_Params setDeviceAttribParams = { - NVPW_MetricsEvaluator_SetDeviceAttributes_Params_STRUCT_SIZE}; - setDeviceAttribParams.pMetricsEvaluator = metricEvaluator; - setDeviceAttribParams.pCounterDataImage = counterDataImage.data(); - setDeviceAttribParams.counterDataImageSize = counterDataImage.size(); - RETURN_IF_NVPW_ERROR(false, - NVPW_MetricsEvaluator_SetDeviceAttributes(&setDeviceAttribParams)); - - double metricValue; - NVPW_MetricsEvaluator_EvaluateToGpuValues_Params evaluateToGpuValuesParams = { - NVPW_MetricsEvaluator_EvaluateToGpuValues_Params_STRUCT_SIZE}; - evaluateToGpuValuesParams.pMetricsEvaluator = metricEvaluator; - evaluateToGpuValuesParams.pMetricEvalRequests = &metricEvalRequest; - evaluateToGpuValuesParams.numMetricEvalRequests = 1; - evaluateToGpuValuesParams.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; - evaluateToGpuValuesParams.metricEvalRequestStrideSize = sizeof(NVPW_MetricEvalRequest); - evaluateToGpuValuesParams.pCounterDataImage = counterDataImage.data(); - evaluateToGpuValuesParams.counterDataImageSize = counterDataImage.size(); - evaluateToGpuValuesParams.rangeIndex = rangeIndex; - evaluateToGpuValuesParams.isolated = true; - evaluateToGpuValuesParams.pMetricValues = &metricValue; - RETURN_IF_NVPW_ERROR(false, - NVPW_MetricsEvaluator_EvaluateToGpuValues(&evaluateToGpuValuesParams)); - - std::cout << std::setw(40) << std::left << rangeName << std::setw(100) << std::left - << metricName << metricValue << std::endl; - } - } - - NVPW_MetricsEvaluator_Destroy_Params metricEvaluatorDestroyParams = { - NVPW_MetricsEvaluator_Destroy_Params_STRUCT_SIZE}; - metricEvaluatorDestroyParams.pMetricsEvaluator = metricEvaluator; - RETURN_IF_NVPW_ERROR(false, NVPW_MetricsEvaluator_Destroy(&metricEvaluatorDestroyParams)); - return true; -} -} // namespace Eval -} // namespace Metric -} // namespace NV diff --git a/cpp/tests/utilities/Metric.h b/cpp/tests/utilities/Metric.h deleted file mode 100644 index 05261c8e63..0000000000 --- a/cpp/tests/utilities/Metric.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * 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 -#include -#include - -class ScopeExit { - public: - ScopeExit(T t) : t(t) {} - ~ScopeExit() { t(); } - T t; -}; - -template -ScopeExit MoveScopeExit(T t) -{ - return ScopeExit(t); -}; - -#define NV_ANONYMOUS_VARIABLE_DIRECT(name, line) name##line -#define NV_ANONYMOUS_VARIABLE_INDIRECT(name, line) NV_ANONYMOUS_VARIABLE_DIRECT(name, line) - -#define SCOPE_EXIT(func) \ - const auto NV_ANONYMOUS_VARIABLE_INDIRECT(EXIT, __LINE__) = MoveScopeExit([=]() { func; }) - -namespace NV { -namespace Metric { -namespace Config { - -bool GetRawMetricRequests(std::string chipName, - const std::vector& metricNames, - std::vector& rawMetricRequests, - const uint8_t* pCounterAvailabilityImage) -{ - NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params calculateScratchBufferSizeParam = { - NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize_Params_STRUCT_SIZE}; - calculateScratchBufferSizeParam.pChipName = chipName.c_str(); - calculateScratchBufferSizeParam.pCounterAvailabilityImage = pCounterAvailabilityImage; - RETURN_IF_NVPW_ERROR( - false, NVPW_CUDA_MetricsEvaluator_CalculateScratchBufferSize(&calculateScratchBufferSizeParam)); - - std::vector scratchBuffer(calculateScratchBufferSizeParam.scratchBufferSize); - NVPW_CUDA_MetricsEvaluator_Initialize_Params metricEvaluatorInitializeParams = { - NVPW_CUDA_MetricsEvaluator_Initialize_Params_STRUCT_SIZE}; - metricEvaluatorInitializeParams.scratchBufferSize = scratchBuffer.size(); - metricEvaluatorInitializeParams.pScratchBuffer = scratchBuffer.data(); - metricEvaluatorInitializeParams.pChipName = chipName.c_str(); - metricEvaluatorInitializeParams.pCounterAvailabilityImage = pCounterAvailabilityImage; - RETURN_IF_NVPW_ERROR(false, - NVPW_CUDA_MetricsEvaluator_Initialize(&metricEvaluatorInitializeParams)); - NVPW_MetricsEvaluator* metricEvaluator = metricEvaluatorInitializeParams.pMetricsEvaluator; - - bool isolated = true; - bool keepInstances = true; - std::vector rawMetricNames; - for (auto& metricName : metricNames) { - std::string reqName; - NV::Metric::Parser::ParseMetricNameString(metricName, &reqName, &isolated, &keepInstances); - keepInstances = true; - NVPW_MetricEvalRequest metricEvalRequest; - NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params convertMetricToEvalRequest = { - NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest_Params_STRUCT_SIZE}; - convertMetricToEvalRequest.pMetricsEvaluator = metricEvaluator; - convertMetricToEvalRequest.pMetricName = reqName.c_str(); - convertMetricToEvalRequest.pMetricEvalRequest = &metricEvalRequest; - convertMetricToEvalRequest.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; - RETURN_IF_NVPW_ERROR( - false, - NVPW_MetricsEvaluator_ConvertMetricNameToMetricEvalRequest(&convertMetricToEvalRequest)); - - std::vector rawDependencies; - NVPW_MetricsEvaluator_GetMetricRawDependencies_Params getMetricRawDependenciesParms = { - NVPW_MetricsEvaluator_GetMetricRawDependencies_Params_STRUCT_SIZE}; - getMetricRawDependenciesParms.pMetricsEvaluator = metricEvaluator; - getMetricRawDependenciesParms.pMetricEvalRequests = &metricEvalRequest; - getMetricRawDependenciesParms.numMetricEvalRequests = 1; - getMetricRawDependenciesParms.metricEvalRequestStructSize = NVPW_MetricEvalRequest_STRUCT_SIZE; - getMetricRawDependenciesParms.metricEvalRequestStrideSize = sizeof(NVPW_MetricEvalRequest); - RETURN_IF_NVPW_ERROR( - false, NVPW_MetricsEvaluator_GetMetricRawDependencies(&getMetricRawDependenciesParms)); - rawDependencies.resize(getMetricRawDependenciesParms.numRawDependencies); - getMetricRawDependenciesParms.ppRawDependencies = rawDependencies.data(); - RETURN_IF_NVPW_ERROR( - false, NVPW_MetricsEvaluator_GetMetricRawDependencies(&getMetricRawDependenciesParms)); - - for (size_t i = 0; i < rawDependencies.size(); ++i) { - rawMetricNames.push_back(rawDependencies[i]); - } - } - - for (auto& rawMetricName : rawMetricNames) { - NVPA_RawMetricRequest metricRequest = {NVPA_RAW_METRIC_REQUEST_STRUCT_SIZE}; - metricRequest.pMetricName = rawMetricName; - metricRequest.isolated = isolated; - metricRequest.keepInstances = keepInstances; - rawMetricRequests.push_back(metricRequest); - } - - NVPW_MetricsEvaluator_Destroy_Params metricEvaluatorDestroyParams = { - NVPW_MetricsEvaluator_Destroy_Params_STRUCT_SIZE}; - metricEvaluatorDestroyParams.pMetricsEvaluator = metricEvaluator; - RETURN_IF_NVPW_ERROR(false, NVPW_MetricsEvaluator_Destroy(&metricEvaluatorDestroyParams)); - return true; -} - -bool GetConfigImage(std::string chipName, - const std::vector& metricNames, - std::vector& configImage, - const uint8_t* pCounterAvailabilityImage) -{ - std::vector rawMetricRequests; - GetRawMetricRequests(chipName, metricNames, rawMetricRequests, pCounterAvailabilityImage); - - NVPW_CUDA_RawMetricsConfig_Create_V2_Params rawMetricsConfigCreateParams = { - NVPW_CUDA_RawMetricsConfig_Create_V2_Params_STRUCT_SIZE}; - rawMetricsConfigCreateParams.activityKind = NVPA_ACTIVITY_KIND_PROFILER; - rawMetricsConfigCreateParams.pChipName = chipName.c_str(); - rawMetricsConfigCreateParams.pCounterAvailabilityImage = pCounterAvailabilityImage; - RETURN_IF_NVPW_ERROR(false, NVPW_CUDA_RawMetricsConfig_Create_V2(&rawMetricsConfigCreateParams)); - NVPA_RawMetricsConfig* pRawMetricsConfig = rawMetricsConfigCreateParams.pRawMetricsConfig; - - if (pCounterAvailabilityImage) { - NVPW_RawMetricsConfig_SetCounterAvailability_Params setCounterAvailabilityParams = { - NVPW_RawMetricsConfig_SetCounterAvailability_Params_STRUCT_SIZE}; - setCounterAvailabilityParams.pRawMetricsConfig = pRawMetricsConfig; - setCounterAvailabilityParams.pCounterAvailabilityImage = pCounterAvailabilityImage; - RETURN_IF_NVPW_ERROR( - false, NVPW_RawMetricsConfig_SetCounterAvailability(&setCounterAvailabilityParams)); - } - - NVPW_RawMetricsConfig_Destroy_Params rawMetricsConfigDestroyParams = { - NVPW_RawMetricsConfig_Destroy_Params_STRUCT_SIZE}; - rawMetricsConfigDestroyParams.pRawMetricsConfig = pRawMetricsConfig; - SCOPE_EXIT([&]() { - NVPW_RawMetricsConfig_Destroy( - (NVPW_RawMetricsConfig_Destroy_Params*)&rawMetricsConfigDestroyParams); - }); - - NVPW_RawMetricsConfig_BeginPassGroup_Params beginPassGroupParams = { - NVPW_RawMetricsConfig_BeginPassGroup_Params_STRUCT_SIZE}; - beginPassGroupParams.pRawMetricsConfig = pRawMetricsConfig; - RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_BeginPassGroup(&beginPassGroupParams)); - - NVPW_RawMetricsConfig_AddMetrics_Params addMetricsParams = { - NVPW_RawMetricsConfig_AddMetrics_Params_STRUCT_SIZE}; - addMetricsParams.pRawMetricsConfig = pRawMetricsConfig; - addMetricsParams.pRawMetricRequests = rawMetricRequests.data(); - addMetricsParams.numMetricRequests = rawMetricRequests.size(); - RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_AddMetrics(&addMetricsParams)); - - NVPW_RawMetricsConfig_EndPassGroup_Params endPassGroupParams = { - NVPW_RawMetricsConfig_EndPassGroup_Params_STRUCT_SIZE}; - endPassGroupParams.pRawMetricsConfig = pRawMetricsConfig; - RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_EndPassGroup(&endPassGroupParams)); - - NVPW_RawMetricsConfig_GenerateConfigImage_Params generateConfigImageParams = { - NVPW_RawMetricsConfig_GenerateConfigImage_Params_STRUCT_SIZE}; - generateConfigImageParams.pRawMetricsConfig = pRawMetricsConfig; - RETURN_IF_NVPW_ERROR(false, - NVPW_RawMetricsConfig_GenerateConfigImage(&generateConfigImageParams)); - - NVPW_RawMetricsConfig_GetConfigImage_Params getConfigImageParams = { - NVPW_RawMetricsConfig_GetConfigImage_Params_STRUCT_SIZE}; - getConfigImageParams.pRawMetricsConfig = pRawMetricsConfig; - getConfigImageParams.bytesAllocated = 0; - getConfigImageParams.pBuffer = NULL; - RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_GetConfigImage(&getConfigImageParams)); - - configImage.resize(getConfigImageParams.bytesCopied); - getConfigImageParams.bytesAllocated = configImage.size(); - getConfigImageParams.pBuffer = configImage.data(); - RETURN_IF_NVPW_ERROR(false, NVPW_RawMetricsConfig_GetConfigImage(&getConfigImageParams)); - - return true; -} - -bool GetCounterDataPrefixImage(std::string chipName, - const std::vector& metricNames, - std::vector& counterDataImagePrefix, - const uint8_t* pCounterAvailabilityImage) -{ - std::vector rawMetricRequests; - GetRawMetricRequests(chipName, metricNames, rawMetricRequests, pCounterAvailabilityImage); - - NVPW_CUDA_CounterDataBuilder_Create_Params counterDataBuilderCreateParams = { - NVPW_CUDA_CounterDataBuilder_Create_Params_STRUCT_SIZE}; - counterDataBuilderCreateParams.pChipName = chipName.c_str(); - counterDataBuilderCreateParams.pCounterAvailabilityImage = pCounterAvailabilityImage; - RETURN_IF_NVPW_ERROR(false, NVPW_CUDA_CounterDataBuilder_Create(&counterDataBuilderCreateParams)); - - NVPW_CounterDataBuilder_Destroy_Params counterDataBuilderDestroyParams = { - NVPW_CounterDataBuilder_Destroy_Params_STRUCT_SIZE}; - counterDataBuilderDestroyParams.pCounterDataBuilder = - counterDataBuilderCreateParams.pCounterDataBuilder; - SCOPE_EXIT([&]() { - NVPW_CounterDataBuilder_Destroy( - (NVPW_CounterDataBuilder_Destroy_Params*)&counterDataBuilderDestroyParams); - }); - - NVPW_CounterDataBuilder_AddMetrics_Params addMetricsParams = { - NVPW_CounterDataBuilder_AddMetrics_Params_STRUCT_SIZE}; - addMetricsParams.pCounterDataBuilder = counterDataBuilderCreateParams.pCounterDataBuilder; - addMetricsParams.pRawMetricRequests = rawMetricRequests.data(); - addMetricsParams.numMetricRequests = rawMetricRequests.size(); - RETURN_IF_NVPW_ERROR(false, NVPW_CounterDataBuilder_AddMetrics(&addMetricsParams)); - - size_t counterDataPrefixSize = 0; - NVPW_CounterDataBuilder_GetCounterDataPrefix_Params getCounterDataPrefixParams = { - NVPW_CounterDataBuilder_GetCounterDataPrefix_Params_STRUCT_SIZE}; - getCounterDataPrefixParams.pCounterDataBuilder = - counterDataBuilderCreateParams.pCounterDataBuilder; - getCounterDataPrefixParams.bytesAllocated = 0; - getCounterDataPrefixParams.pBuffer = NULL; - RETURN_IF_NVPW_ERROR(false, - NVPW_CounterDataBuilder_GetCounterDataPrefix(&getCounterDataPrefixParams)); - - counterDataImagePrefix.resize(getCounterDataPrefixParams.bytesCopied); - getCounterDataPrefixParams.bytesAllocated = counterDataImagePrefix.size(); - getCounterDataPrefixParams.pBuffer = counterDataImagePrefix.data(); - RETURN_IF_NVPW_ERROR(false, - NVPW_CounterDataBuilder_GetCounterDataPrefix(&getCounterDataPrefixParams)); - - return true; -} - -} // namespace Config -} // namespace Metric -} // namespace NV diff --git a/cpp/tests/utilities/helper_cupti.h b/cpp/tests/utilities/helper_cupti.h deleted file mode 100644 index 0b5f2c9982..0000000000 --- a/cpp/tests/utilities/helper_cupti.h +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved. - * - * Please refer to the NVIDIA end user license agreement (EULA) associated - * with this source code for terms and conditions that govern your use of - * this software. Any use, reproduction, disclosure, or distribution of - * this software and related documentation outside the terms of the EULA - * is strictly prohibited. - * - */ - -//////////////////////////////////////////////////////////////////////////////// - -#ifndef HELPER_CUPTI_H_ -#define HELPER_CUPTI_H_ - -#pragma once - -#include - -#ifndef EXIT_WAIVED -#define EXIT_WAIVED 2 -#endif - -#if defined(WIN32) || defined(_WIN32) -#define stricmp _stricmp -#else -#define stricmp strcasecmp -#endif - -#define CUDA_MAX_DEVICES 256 // consider theoretical max devices as 256 - -#ifndef DRIVER_API_CALL -#define DRIVER_API_CALL(apiFunctionCall) \ - do { \ - CUresult _status = apiFunctionCall; \ - if (_status != CUDA_SUCCESS) { \ - const char* pErrorString; \ - cuGetErrorString(_status, &pErrorString); \ - \ - std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ - << #apiFunctionCall << " failed with error(" << _status << "): " << pErrorString \ - << ".\n\n"; \ - \ - exit(EXIT_FAILURE); \ - } \ - } while (0) -#endif - -#ifndef RUNTIME_API_CALL -#define RUNTIME_API_CALL(apiFunctionCall) \ - do { \ - cudaError_t _status = apiFunctionCall; \ - if (_status != cudaSuccess) { \ - std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ - << #apiFunctionCall << " failed with error(" << _status \ - << "): " << cudaGetErrorString(_status) << ".\n\n"; \ - \ - exit(EXIT_FAILURE); \ - } \ - } while (0) -#endif - -#ifndef CUPTI_API_CALL -#define CUPTI_API_CALL(apiFunctionCall) \ - do { \ - CUptiResult _status = apiFunctionCall; \ - if (_status != CUPTI_SUCCESS) { \ - const char* pErrorString; \ - cuptiGetResultString(_status, &pErrorString); \ - \ - std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ - << #apiFunctionCall << " failed with error(" << _status << "): " << pErrorString \ - << ".\n\n"; \ - \ - exit(EXIT_FAILURE); \ - } \ - } while (0) -#endif - -#ifndef CUPTI_API_CALL_VERBOSE -#define CUPTI_API_CALL_VERBOSE(apiFunctionCall) \ - do { \ - std::cout << "Calling CUPTI API: " << #apiFunctionCall << "\n"; \ - \ - CUptiResult _status = apiFunctionCall; \ - if (_status != CUPTI_SUCCESS) { \ - const char* pErrorString; \ - cuptiGetResultString(_status, &pErrorString); \ - \ - std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ - << #apiFunctionCall << " failed with error(" << _status << "): " << pErrorString \ - << ".\n\n"; \ - \ - exit(EXIT_FAILURE); \ - } \ - } while (0) -#endif - -#ifndef CUPTI_UTIL_CALL -#define CUPTI_UTIL_CALL(apiFunctionCall) \ - do { \ - CUptiUtilResult _status = apiFunctionCall; \ - if (_status != CUPTI_UTIL_SUCCESS) { \ - std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ - << #apiFunctionCall << " failed with error: " << _status << "\n\n"; \ - \ - exit(EXIT_FAILURE); \ - } \ - } while (0) -#endif - -#ifndef NVPW_API_CALL -#define NVPW_API_CALL(apiFunctionCall) \ - do { \ - NVPA_Status _status = apiFunctionCall; \ - if (_status != NVPA_STATUS_SUCCESS) { \ - std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Function " \ - << #apiFunctionCall << " failed with error: " << _status << "\n\n"; \ - \ - exit(EXIT_FAILURE); \ - } \ - } while (0) -#endif - -#ifndef MEMORY_ALLOCATION_CALL -#define MEMORY_ALLOCATION_CALL(variable) \ - do { \ - if (variable == NULL) { \ - std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ \ - << " Memory allocation failed.\n\n"; \ - \ - exit(EXIT_FAILURE); \ - } \ - } while (0) -#endif - -#ifndef CHECK_CONDITION -#define CHECK_CONDITION(condition) \ - do { \ - if (!(condition)) { \ - std::cerr << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Condition " << #condition \ - << " failed.\n\n"; \ - \ - exit(EXIT_FAILURE); \ - } \ - } while (0) -#endif - -#ifndef CHECK_INTEGER_CONDITION -#define CHECK_INTEGER_CONDITION(argument1, operator, argument2) \ - do { \ - if (!(argument1 operator argument2)) { \ - std::cerr \ - << "\n\nError: " << __FILE__ << ":" << __LINE__ << ": Condition " << #argument1 << " " \ - << # \ - operator<< " " << #argument2 << " fails. " << #argument1 << " = " << argument1 << "," \ - " " << #argument2 << " = " << argument2 << "\n\n"; \ - \ - exit(EXIT_FAILURE); \ - } \ - } while (0) -#endif - -#endif // HELPER_CUPTI_H_ diff --git a/cpp/tests/utilities/test_cupti.cu b/cpp/tests/utilities/test_cupti.cu deleted file mode 100644 index de1778541a..0000000000 --- a/cpp/tests/utilities/test_cupti.cu +++ /dev/null @@ -1,361 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include - -#include "../linear_programming/utilities/pdlp_test_utilities.cuh" -#include "../mip/mip_utils.cuh" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cuopt::linear_programming::test { - -void init_handler(const raft::handle_t* handle_ptr) -{ - // Init cuBlas / cuSparse context here to avoid having it during solving time - RAFT_CUBLAS_TRY(raft::linalg::detail::cublassetpointermode( - handle_ptr->get_cublas_handle(), CUBLAS_POINTER_MODE_DEVICE, handle_ptr->get_stream())); - RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsesetpointermode( - handle_ptr->get_cusparse_handle(), CUSPARSE_POINTER_MODE_DEVICE, handle_ptr->get_stream())); -} - -__global__ void VectorAdd(const float* A, const float* B, float* C, int N) -{ - int idx = blockIdx.x * blockDim.x + threadIdx.x; - if (idx < N) C[idx] = A[idx] + B[idx]; -} - -class cupti_profiler_t { - public: - cupti_profiler_t(const std::vector& metrics) - : metricNames_(metrics), pRangeProfilerObject_(nullptr) - { - } - - cupti_profiler_t(const cupti_profiler_t&) = delete; - cupti_profiler_t& operator=(const cupti_profiler_t&) = delete; - - ~cupti_profiler_t() - { - if (!pRangeProfilerObject_) return; - CUpti_RangeProfiler_Disable_Params disableRangeProfiler = { - .structSize = CUpti_RangeProfiler_Disable_Params_STRUCT_SIZE}; - disableRangeProfiler.pRangeProfilerObject = pRangeProfilerObject_; - cuptiRangeProfilerDisable(&disableRangeProfiler); - CUpti_Profiler_DeInitialize_Params profilerDeInitializeParams = { - .structSize = CUpti_Profiler_DeInitialize_Params_STRUCT_SIZE}; - cuptiProfilerDeInitialize(&profilerDeInitializeParams); - } - - void initialize_and_enable(CUcontext cuContext) - { - CUpti_Profiler_Initialize_Params profilerInitializeParams = { - .structSize = CUpti_Profiler_Initialize_Params_STRUCT_SIZE}; - cuptiProfilerInitialize(&profilerInitializeParams); - CUdevice device; - cuCtxGetDevice(&device); - CUpti_Device_GetChipName_Params getChipNameParams = { - .structSize = CUpti_Device_GetChipName_Params_STRUCT_SIZE}; - getChipNameParams.deviceIndex = (size_t)device; - cuptiDeviceGetChipName(&getChipNameParams); - chipName_ = getChipNameParams.pChipName; - CUpti_RangeProfiler_Enable_Params enableRange = { - .structSize = CUpti_RangeProfiler_Enable_Params_STRUCT_SIZE}; - enableRange.ctx = cuContext; - cuptiRangeProfilerEnable(&enableRange); - pRangeProfilerObject_ = enableRange.pRangeProfilerObject; - } - - void configure(CUpti_ProfilerRange range, CUpti_ProfilerReplayMode replayMode, size_t numOfRanges) - { - create_config_image(); - create_counter_data_image(numOfRanges); - - CUpti_RangeProfiler_SetConfig_Params setConfig = { - .structSize = CUpti_RangeProfiler_SetConfig_Params_STRUCT_SIZE}; - setConfig.pRangeProfilerObject = pRangeProfilerObject_; - setConfig.configSize = configImage_.size(); - setConfig.pConfig = configImage_.data(); - setConfig.counterDataImageSize = counterDataImage_.size(); - setConfig.pCounterDataImage = counterDataImage_.data(); - setConfig.range = range; - setConfig.replayMode = replayMode; - setConfig.maxRangesPerPass = numOfRanges; - setConfig.numNestingLevels = 1; - setConfig.minNestingLevel = 1; - setConfig.passIndex = 0; - setConfig.targetNestingLevel = 0; - cuptiRangeProfilerSetConfig(&setConfig); - } - - void start() - { - CUpti_RangeProfiler_Start_Params p = {.structSize = - CUpti_RangeProfiler_Start_Params_STRUCT_SIZE}; - p.pRangeProfilerObject = pRangeProfilerObject_; - cuptiRangeProfilerStart(&p); - } - - void stop() - { - CUpti_RangeProfiler_Stop_Params p = {.structSize = CUpti_RangeProfiler_Stop_Params_STRUCT_SIZE}; - p.pRangeProfilerObject = pRangeProfilerObject_; - cuptiRangeProfilerStop(&p); - } - - std::unordered_map decode() - { - CUpti_RangeProfiler_DecodeData_Params decodeData = { - .structSize = CUpti_RangeProfiler_DecodeData_Params_STRUCT_SIZE}; - decodeData.pRangeProfilerObject = pRangeProfilerObject_; - cuptiRangeProfilerDecodeData(&decodeData); - CUpti_RangeProfiler_GetCounterDataInfo_Params cdiParams = { - .structSize = CUpti_RangeProfiler_GetCounterDataInfo_Params_STRUCT_SIZE}; - cdiParams.pCounterDataImage = counterDataImage_.data(); - cdiParams.counterDataImageSize = counterDataImage_.size(); - cuptiRangeProfilerGetCounterDataInfo(&cdiParams); - evaluate_all_ranges(cdiParams.numTotalRanges > 10 ? 10 : cdiParams.numTotalRanges); - return metric_values; - } - - private: - void create_config_image() - { - CUpti_Profiler_Host_Initialize_Params hostInitializeParams = { - .structSize = CUpti_Profiler_Host_Initialize_Params_STRUCT_SIZE}; - hostInitializeParams.profilerType = CUPTI_PROFILER_TYPE_RANGE_PROFILER; - hostInitializeParams.pChipName = chipName_.c_str(); - hostInitializeParams.pCounterAvailabilityImage = nullptr; - cuptiProfilerHostInitialize(&hostInitializeParams); - CUpti_Profiler_Host_Object* pHostObject = hostInitializeParams.pHostObject; - - CUpti_Profiler_Host_ConfigAddMetrics_Params configAddMetricsParams = { - .structSize = CUpti_Profiler_Host_ConfigAddMetrics_Params_STRUCT_SIZE}; - configAddMetricsParams.pHostObject = pHostObject; - configAddMetricsParams.ppMetricNames = metricNames_.data(); - configAddMetricsParams.numMetrics = metricNames_.size(); - cuptiProfilerHostConfigAddMetrics(&configAddMetricsParams); - - CUpti_Profiler_Host_GetConfigImageSize_Params getConfigImageSizeParams = { - .structSize = CUpti_Profiler_Host_GetConfigImageSize_Params_STRUCT_SIZE}; - getConfigImageSizeParams.pHostObject = pHostObject; - cuptiProfilerHostGetConfigImageSize(&getConfigImageSizeParams); - configImage_.resize(getConfigImageSizeParams.configImageSize); - - CUpti_Profiler_Host_GetConfigImage_Params getConfigImageParams = { - .structSize = CUpti_Profiler_Host_GetConfigImage_Params_STRUCT_SIZE}; - getConfigImageParams.pHostObject = pHostObject; - getConfigImageParams.pConfigImage = configImage_.data(); - getConfigImageParams.configImageSize = configImage_.size(); - cuptiProfilerHostGetConfigImage(&getConfigImageParams); - - CUpti_Profiler_Host_GetNumOfPasses_Params getNumOfPassesParam = { - .structSize = CUpti_Profiler_Host_GetNumOfPasses_Params_STRUCT_SIZE}; - getNumOfPassesParam.pConfigImage = configImage_.data(); - getNumOfPassesParam.configImageSize = configImage_.size(); - cuptiProfilerHostGetNumOfPasses(&getNumOfPassesParam); - printf("Num of Passes: %d\n", (int)getNumOfPassesParam.numOfPasses); - CUpti_Profiler_Host_Deinitialize_Params deinitializeParams = { - .structSize = CUpti_Profiler_Host_Deinitialize_Params_STRUCT_SIZE}; - deinitializeParams.pHostObject = pHostObject; - cuptiProfilerHostDeinitialize(&deinitializeParams); - } - - void create_counter_data_image(size_t maxNumOfRangesInCounterDataImage) - { - CUpti_RangeProfiler_GetCounterDataSize_Params ctDataSize = { - .structSize = CUpti_RangeProfiler_GetCounterDataSize_Params_STRUCT_SIZE}; - ctDataSize.pRangeProfilerObject = pRangeProfilerObject_; - ctDataSize.pMetricNames = metricNames_.data(); - ctDataSize.numMetrics = metricNames_.size(); - ctDataSize.maxNumOfRanges = maxNumOfRangesInCounterDataImage; - ctDataSize.maxNumRangeTreeNodes = maxNumOfRangesInCounterDataImage; - cuptiRangeProfilerGetCounterDataSize(&ctDataSize); - counterDataImage_.resize(ctDataSize.counterDataSize); - CUpti_RangeProfiler_CounterDataImage_Initialize_Params initCtImg = { - .structSize = CUpti_RangeProfiler_CounterDataImage_Initialize_Params_STRUCT_SIZE}; - initCtImg.pRangeProfilerObject = pRangeProfilerObject_; - initCtImg.pCounterData = counterDataImage_.data(); - initCtImg.counterDataSize = counterDataImage_.size(); - cuptiRangeProfilerCounterDataImageInitialize(&initCtImg); - } - - void evaluate_range(size_t rangeIndex, CUpti_Profiler_Host_Object* pHostObject) - { - std::vector metricValues(metricNames_.size()); - CUpti_Profiler_Host_EvaluateToGpuValues_Params p = { - .structSize = CUpti_Profiler_Host_EvaluateToGpuValues_Params_STRUCT_SIZE}; - p.pHostObject = pHostObject; - p.pCounterDataImage = counterDataImage_.data(); - p.counterDataImageSize = counterDataImage_.size(); - p.ppMetricNames = metricNames_.data(); - p.numMetrics = metricNames_.size(); - p.rangeIndex = rangeIndex; - p.pMetricValues = metricValues.data(); - cuptiProfilerHostEvaluateToGpuValues(&p); - for (size_t i = 0; i < metricNames_.size(); ++i) - metric_values[metricNames_[i]] += (size_t)metricValues[i]; - } - - void evaluate_all_ranges(size_t numOfRanges) - { - CUpti_Profiler_Host_Initialize_Params hostInitializeParams = { - .structSize = CUpti_Profiler_Host_Initialize_Params_STRUCT_SIZE}; - hostInitializeParams.profilerType = CUPTI_PROFILER_TYPE_RANGE_PROFILER; - hostInitializeParams.pChipName = chipName_.c_str(); - hostInitializeParams.pCounterAvailabilityImage = nullptr; - cuptiProfilerHostInitialize(&hostInitializeParams); - for (size_t i = 0; i < numOfRanges; ++i) - evaluate_range(i, hostInitializeParams.pHostObject); - CUpti_Profiler_Host_Deinitialize_Params deinitializeParams = { - .structSize = CUpti_Profiler_Host_Deinitialize_Params_STRUCT_SIZE}; - deinitializeParams.pHostObject = hostInitializeParams.pHostObject; - cuptiProfilerHostDeinitialize(&deinitializeParams); - } - - std::unordered_map metric_values; - std::vector metricNames_; - CUpti_RangeProfiler_Object* pRangeProfilerObject_; - std::vector counterDataImage_, configImage_; - std::string chipName_; -}; - -void test_cupti() -{ - rmm::mr::cuda_memory_resource cuda_mr; - rmm::mr::set_current_device_resource(&cuda_mr); - - const raft::handle_t handle_{}; - auto stream = handle_.get_stream(); - std::string test_instance = "pk1.mps"; - - auto path = cuopt::test::get_rapids_dataset_root_dir() + ("/mip/" + test_instance); - cuopt::mps_parser::mps_data_model_t mps_problem = - cuopt::mps_parser::parse_mps(path, false); - handle_.sync_stream(); - auto op_problem = mps_data_model_to_optimization_problem(&handle_, mps_problem); - problem_checking_t::check_problem_representation(op_problem); - - init_handler(op_problem.get_handle_ptr()); - // run the problem constructor of MIP, so that we do bounds standardization - detail::problem_t problem(op_problem); - problem.preprocess_problem(); - - const int vectorLen = 100000; - rmm::device_uvector d_A(vectorLen, stream), d_B(vectorLen, stream), d_C(vectorLen, stream); - - auto random_generator = [](unsigned int seed) { - return [=] __device__(int idx) { - thrust::default_random_engine rng(seed); - thrust::uniform_real_distribution dist(0.0f, 1.0f); - rng.discard(idx); - return dist(rng); - }; - }; - - thrust::transform(rmm::exec_policy(stream), - thrust::counting_iterator(0), - thrust::counting_iterator(vectorLen), - d_A.begin(), - random_generator(1234)); - thrust::transform(rmm::exec_policy(stream), - thrust::counting_iterator(0), - thrust::counting_iterator(vectorLen), - d_B.begin(), - random_generator(5678)); - - // Get CUDA context after CUDA operations have initialized it - CUcontext cuContext; - cuCtxGetCurrent(&cuContext); - - // cupti_profiler_t profiler({ - // "sm__warps_launched.sum", "l1tex__t_sectors.sum", "l1tex__data_bank_reads.sum", - // "l1tex__data_bank_writes.sum", "l1tex__m_xbar2l1tex_read_bytes.sum", - // "l1tex__m_l1tex2xbar_write_bytes.sum", "lts__t_sectors_op_read.sum", - // "lts__t_sectors_op_write.sum" - // }); - cupti_profiler_t profiler({"l1tex__t_sectors.sum"}); - profiler.initialize_and_enable(cuContext); - profiler.configure(CUPTI_AutoRange, CUPTI_KernelReplay, 10); - profiler.start(); - - detail::fj_settings_t fj_settings; - fj_settings.feasibility_run = false; - fj_settings.iteration_limit = 10; - fj_settings.seed = 42; - printf("Running FJ\n"); - auto solution = run_fj(problem, fj_settings).solution; - - // VectorAdd<<<(vectorLen + 127) / 128, 128, 0, stream.value()>>>(d_A.data(), d_B.data(), - // d_C.data(), vectorLen); - - profiler.stop(); - - stream.synchronize(); - - auto metric_values = profiler.decode(); - for (const auto& [k, v] : metric_values) - printf("%s: %zu\n", k.c_str(), v); - - const size_t reads = 2 * vectorLen * sizeof(float); - const size_t writes = vectorLen * sizeof(float); - const size_t sector_reads = reads / 32; - const size_t sector_writes = writes / 32; - const size_t total_sectors = sector_reads + sector_writes; - EXPECT_EQ(metric_values["l1tex__t_sectors.sum"], total_sectors); - // EXPECT_EQ(metric_values["l1tex__m_xbar2l1tex_read_bytes.sum"], reads); - // EXPECT_EQ(metric_values["l1tex__m_l1tex2xbar_write_bytes.sum"], writes); -} - -TEST(cupti, test_cupti) { test_cupti(); } - -} // namespace cuopt::linear_programming::test diff --git a/scripts/requirements.txt b/scripts/requirements.txt deleted file mode 100644 index bcce7a7882..0000000000 --- a/scripts/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -numpy>=1.20.0 -pandas>=1.3.0 -scikit-learn>=1.0.0 -xgboost>=1.5.0 -lightgbm>=3.0.0 -joblib>=1.0.0 - -# Optional: For exporting models to C source code -# treelite>=4.0.0 -# tl2cgen>=0.1.0 From 9b881d813287f315c4bccd7dca629c343e4f3ccf Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 1 Dec 2025 17:54:39 +0000 Subject: [PATCH 098/225] fix build --- cpp/src/dual_simplex/branch_and_bound.cpp | 8 +- cpp/src/dual_simplex/phase2.cpp | 2 +- cpp/src/mip/feasibility_jump/fj_cpu.cu | 185 ---------------------- 3 files changed, 6 insertions(+), 189 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index ba8353b14e..d6d7108841 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -863,9 +863,11 @@ node_solve_info_t branch_and_bound_t::solve_node( work_unit_context_.global_work_units_elapsed >= settings_.work_limit) { lp_status = dual::status_t::WORK_LIMIT; } - printf("------ Total work unit progress B&B: %f / %f\n", - work_unit_context_.global_work_units_elapsed, - settings_.work_limit); + if (settings_.deterministic) { + printf("------ Total work unit progress B&B: %f / %f\n", + work_unit_context_.global_work_units_elapsed, + settings_.work_limit); + } if (lp_status == dual::status_t::NUMERICAL) { log.printf("Numerical issue node %d. Resolving from scratch.\n", node_ptr->node_id); diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 37d729fbaa..284c628877 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -3179,7 +3179,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } } - printf("Iterations: %d, time taken: %f\n", iter, toc(start_time)); + // printf("Iterations: %d, time taken: %f\n", iter, toc(start_time)); return status; } diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index 43f747d862..5cdab97957 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -22,12 +22,6 @@ #include #include -#ifdef __linux__ -#include -#include -#include -#endif - #define CPUFJ_TIMING_TRACE 0 namespace cuopt::linear_programming::detail { @@ -116,144 +110,6 @@ static void print_timing_stats(fj_cpu_climber_t& fj_cpu) CUOPT_LOG_TRACE("========================================"); } -#ifdef __linux__ -template -static void initialize_papi(fj_cpu_climber_t& fj_cpu) -{ - int retval = PAPI_library_init(PAPI_VER_CURRENT); - if (retval != PAPI_VER_CURRENT && retval > 0) { - CUOPT_LOG_TRACE("%sPAPI library version mismatch", fj_cpu.log_prefix.c_str()); - return; - } - if (retval < 0) { - CUOPT_LOG_TRACE("%sPAPI library initialization failed", fj_cpu.log_prefix.c_str()); - return; - } - - fj_cpu.papi_event_set = PAPI_NULL; - retval = PAPI_create_eventset(&fj_cpu.papi_event_set); - if (retval != PAPI_OK) { - CUOPT_LOG_TRACE("%sPAPI eventset creation failed", fj_cpu.log_prefix.c_str()); - return; - } - - // Define the events we want to track - int candidate_events[] = { - PAPI_L1_DCA, // L1 data cache accesses - PAPI_L1_DCM, // L1 data cache misses - PAPI_L2_DCA, // L2 data cache accesses - PAPI_L2_DCM, // L2 data cache misses - PAPI_L3_TCA, // L3 total cache accesses - PAPI_L3_TCM, // L3 total cache misses - PAPI_LD_INS, // Load instructions - PAPI_SR_INS // Store instructions - }; - - const char* event_names[] = {"PAPI_L1_DCA", - "PAPI_L1_DCM", - "PAPI_L2_DCA", - "PAPI_L2_DCM", - "PAPI_L3_TCA", - "PAPI_L3_TCM", - "PAPI_LD_INS", - "PAPI_SR_INS"}; - - int num_candidate_events = sizeof(candidate_events) / sizeof(candidate_events[0]); - - // Try to add each event, store -1 for unavailable ones - for (int i = 0; i < num_candidate_events; i++) { - retval = PAPI_add_event(fj_cpu.papi_event_set, candidate_events[i]); - if (retval == PAPI_OK) { - fj_cpu.papi_events.push_back(candidate_events[i]); - } else { - fj_cpu.papi_events.push_back(-1); - CUOPT_LOG_TRACE( - "%sPAPI event %s not available on this system", fj_cpu.log_prefix.c_str(), event_names[i]); - } - } - (void)event_names; // Suppress unused warning when logging is disabled - - // Start counting - retval = PAPI_start(fj_cpu.papi_event_set); - if (retval != PAPI_OK) { - CUOPT_LOG_TRACE("%sPAPI start failed", fj_cpu.log_prefix.c_str()); - return; - } - - fj_cpu.papi_initialized = true; - CUOPT_LOG_TRACE("%sPAPI initialized successfully", fj_cpu.log_prefix.c_str()); -} - -// template -// static void collect_and_print_papi_metrics(fj_cpu_climber_t& fj_cpu) -// { -// if (!fj_cpu.papi_initialized) return; - -// std::vector values(fj_cpu.papi_events.size(), 0); -// int retval = PAPI_read(fj_cpu.papi_event_set, values.data()); -// if (retval != PAPI_OK) { -// CUOPT_LOG_TRACE("%sPAPI read failed", fj_cpu.log_prefix.c_str()); -// return; -// } - -// // Get thread ID -// pid_t tid = syscall(SYS_gettid); - -// // Build map of actual values indexed by event position -// std::vector all_values(8, -1); -// int value_idx = 0; -// for (size_t i = 0; i < fj_cpu.papi_events.size(); i++) { -// if (fj_cpu.papi_events[i] != -1) { all_values[i] = values[value_idx++]; } -// } - -// // Compute derived metrics -// double l1_miss_rate = -1.0; -// if (all_values[0] > 0 && all_values[1] != -1) { -// l1_miss_rate = (double)all_values[1] / all_values[0] * 100.0; -// } - -// double l2_miss_rate = -1.0; -// if (all_values[2] > 0 && all_values[3] != -1) { -// l2_miss_rate = (double)all_values[3] / all_values[2] * 100.0; -// } - -// // Lock to ensure thread-safe printing -// std::lock_guard lock(papi_print_mutex); - -// // Print everything on a single compact line -// CUOPT_LOG_DEBUG( -// "%sPAPI iter=%d tid=%d L1_DCA=%lld L1_DCM=%lld L2_DCA=%lld L2_DCM=%lld L3_TCA=%lld " -// "L3_TCM=%lld LD_INS=%lld SR_INS=%lld L1_miss=%.2f%% L2_miss=%.2f%%", -// fj_cpu.log_prefix.c_str(), -// fj_cpu.iterations, -// tid, -// all_values[0], -// all_values[1], -// all_values[2], -// all_values[3], -// all_values[4], -// all_values[5], -// all_values[6], -// all_values[7], -// l1_miss_rate, -// l2_miss_rate); - -// // Reset counters for the next 1000 iterations -// retval = PAPI_reset(fj_cpu.papi_event_set); -// if (retval != PAPI_OK) { CUOPT_LOG_TRACE("%sPAPI reset failed", fj_cpu.log_prefix.c_str()); } -// } - -template -static void cleanup_papi(fj_cpu_climber_t& fj_cpu) -{ - if (fj_cpu.papi_initialized) { - PAPI_stop(fj_cpu.papi_event_set, nullptr); - PAPI_cleanup_eventset(fj_cpu.papi_event_set); - PAPI_destroy_eventset(&fj_cpu.papi_event_set); - } -} -#endif - template static void precompute_problem_features(fj_cpu_climber_t& fj_cpu) { @@ -389,33 +245,6 @@ static void log_regression_features(fj_cpu_climber_t& fj_cpu, double l1_miss = -1.0; double l3_miss = -1.0; -#ifdef __linux__ - // Get PAPI metrics if available - if (fj_cpu.papi_initialized) { - std::vector values(fj_cpu.papi_events.size(), 0); - int retval = PAPI_read(fj_cpu.papi_event_set, values.data()); - if (retval == PAPI_OK) { - std::vector all_values(8, -1); - int value_idx = 0; - for (size_t i = 0; i < fj_cpu.papi_events.size(); i++) { - if (fj_cpu.papi_events[i] != -1) { all_values[i] = values[value_idx++]; } - } - - // Compute cache miss rates - if (all_values[0] > 0 && all_values[1] != -1) { - l1_miss = (double)all_values[1] / all_values[0] * 100.0; - } - if (all_values[4] > 0 && all_values[5] != -1) { - l3_miss = (double)all_values[5] / all_values[4] * 100.0; - } - - // Loads and stores per iteration - if (all_values[6] != -1) { loads_per_iter = (double)all_values[6] / 1000.0; } - if (all_values[7] != -1) { stores_per_iter = (double)all_values[7] / 1000.0; } - } - } -#endif - // Compute memory statistics double mem_loads_mb = mem_loads_bytes / 1e6; double mem_stores_mb = mem_stores_bytes / 1e6; @@ -1544,11 +1373,6 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l auto time_limit = std::chrono::milliseconds((int)(in_time_limit * 1000)); auto loop_time_start = std::chrono::high_resolution_clock::now(); -#ifdef __linux__ - // Initialize PAPI for performance monitoring - initialize_papi(fj_cpu); -#endif - // Initialize feature tracking fj_cpu.last_feature_log_time = loop_start; fj_cpu.prev_best_objective = fj_cpu.h_best_objective; @@ -1686,10 +1510,6 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l std::chrono::duration_cast>(now - loop_start).count() * 1000.0; - // #ifdef __linux__ - // collect_and_print_papi_metrics(fj_cpu); - // #endif - // Collect memory statistics auto [loads, stores] = fj_cpu.memory_manifold.collect(); @@ -1730,11 +1550,6 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l print_timing_stats(fj_cpu); #endif -#ifdef __linux__ - // Cleanup PAPI - cleanup_papi(fj_cpu); -#endif - return fj_cpu.feasible_found; } From b293e947796856888b320700bce3e4c5b906d51e Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 1 Dec 2025 17:55:15 +0000 Subject: [PATCH 099/225] cleanup --- cpp/src/mip/feasibility_jump/fj_cpu.cu | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cu b/cpp/src/mip/feasibility_jump/fj_cpu.cu index 5cdab97957..f93cff9f65 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cu @@ -28,11 +28,6 @@ namespace cuopt::linear_programming::detail { static constexpr double BIGVAL_THRESHOLD = 1e20; -// #ifdef __linux__ -// // Global mutex to protect PAPI metric printing across multiple threads -// static std::mutex papi_print_mutex; -// #endif - template class timing_raii_t { public: From ee7037fff7bcd8e007e5f31e503d0472ca0d0f1e Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 2 Dec 2025 12:53:25 +0000 Subject: [PATCH 100/225] cleanup --- cpp/src/mip/diversity/diversity_manager.cu | 70 +++++++---------- cpp/src/mip/diversity/multi_armed_bandit.cuh | 15 +++- .../recombiners/recombiner_stats.hpp | 15 ++++ .../mip/feasibility_jump/feasibility_jump.cu | 78 +++++++++---------- .../feasibility_jump_kernels.cu | 1 - cpp/src/mip/feasibility_jump/fj_cpu.cuh | 7 +- .../feasibility_pump/feasibility_pump.cu | 1 - .../line_segment_search.cu | 8 +- cpp/src/mip/local_search/local_search.cu | 39 +++------- .../local_search/rounding/bounds_repair.cu | 3 +- .../local_search/rounding/constraint_prop.cu | 15 +--- cpp/src/mip/presolve/multi_probe.cu | 8 +- 12 files changed, 119 insertions(+), 141 deletions(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 68f828229c..958c4a00d8 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -263,38 +263,14 @@ template void diversity_manager_t::run_fj_alone(solution_t& solution) { CUOPT_LOG_INFO("Running FJ alone!"); - // Benchmark FJ with 1000 different random starting solutions and varying iteration limits - CUOPT_LOG_INFO("Starting FJ benchmark: 1000 runs with random starting solutions"); - - std::mt19937 rng(cuopt::seed_generator::get_seed()); - std::uniform_int_distribution iter_dist(100, 50000); - - for (i_t run = 0; run < 1000; ++run) { - // Generate random starting solution within bounds - solution.assign_random_within_bounds(1.0, false); - solution.round_nearest(); - - // Configure FJ settings with random iteration limit - ls.fj.settings = fj_settings_t{}; - ls.fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; - ls.fj.settings.n_of_minimums_for_exit = 20000 * 1000; - ls.fj.settings.update_weights = true; - ls.fj.settings.feasibility_run = false; - ls.fj.settings.iteration_limit = iter_dist(rng); - ls.fj.settings.time_limit = std::numeric_limits::infinity(); - - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - ls.fj.settings.iteration_limit = iter_dist(rng); - } - - CUOPT_LOG_INFO( - "FJ benchmark run %d/%d: iteration_limit=%d", run + 1, 1000, ls.fj.settings.iteration_limit); - - ls.fj.solve(solution); - } - - CUOPT_LOG_INFO("FJ benchmark finished: 1000 runs completed"); - exit(0); + solution.round_nearest(); + ls.fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; + ls.fj.settings.n_of_minimums_for_exit = 20000 * 1000; + ls.fj.settings.update_weights = true; + ls.fj.settings.feasibility_run = false; + ls.fj.settings.time_limit = timer.remaining_time(); + ls.fj.solve(solution); + CUOPT_LOG_INFO("FJ alone finished!"); } // returns the best feasible solution @@ -320,7 +296,9 @@ solution_t diversity_manager_t::run_solver() { raft::common::nvtx::range fun_scope("run_solver"); - diversity_config.fj_only_run = false; + CUOPT_LOG_DEBUG("Determinism mode: %s", + context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC ? "deterministic" + : "opportunistic"); population.timer = timer; const f_t time_limit = timer.remaining_time(); @@ -606,6 +584,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& sol1.get_feasible(), sol2.get_quality(population.weights), sol2.get_feasible()); + bool deterministic = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; double best_objective_of_parents = std::min(sol1.get_objective(), sol2.get_objective()); bool at_least_one_parent_feasible = sol1.get_feasible() || sol2.get_feasible(); // randomly choose among 3 recombiners @@ -616,7 +595,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::max(), - recombiner_work_normalized_reward_t(0.0)); + recombiner_work_normalized_reward_t(deterministic, 0.0)); return std::make_pair(solution_t(sol1), solution_t(sol2)); } cuopt_assert(population.test_invariant(), ""); @@ -636,7 +615,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::max(), - recombiner_work_normalized_reward_t(0.0)); + recombiner_work_normalized_reward_t(deterministic, 0.0)); return std::make_pair(solution_t(sol1), solution_t(sol2)); } cuopt_assert(offspring.test_number_all_integer(), "All must be integers after LS"); @@ -676,12 +655,15 @@ diversity_manager_t::recombine_and_local_search(solution_t& offspring_qual, sol1.get_quality(population.weights), sol2.get_quality(population.weights)); f_t best_quality_of_parents = std::min(sol1.get_quality(population.weights), sol2.get_quality(population.weights)); - mab_recombiner.add_mab_reward( - mab_recombiner.last_chosen_option, - best_quality_of_parents, - population.best().get_quality(population.weights), - offspring_qual, - recombiner_work_normalized_reward_t(recombine_stats.get_last_recombiner_work())); + mab_recombiner.add_mab_reward(mab_recombiner.last_chosen_option, + best_quality_of_parents, + population.best().get_quality(population.weights), + offspring_qual, + !deterministic + ? recombiner_work_normalized_reward_t( + deterministic, recombine_stats.get_last_recombiner_time()) + : recombiner_work_normalized_reward_t( + deterministic, recombine_stats.get_last_recombiner_work())); mab_ls.add_mab_reward(mab_ls_config_t::last_ls_mab_option, best_quality_of_parents, population.best_feasible().get_quality(population.weights), @@ -728,34 +710,40 @@ std::pair, bool> diversity_manager_t::recombine( } mab_recombiner.set_last_chosen_option(selected_index); recombine_stats.add_attempt((recombiner_enum_t)recombiner); + recombine_stats.start_recombiner_time(); CUOPT_LOG_DEBUG("Recombining sol %x and %x with recombiner %d, weights %x", a.get_hash(), b.get_hash(), recombiner, population.weights.get_hash()); + // Refactored code using a switch statement switch (recombiner) { case recombiner_enum_t::BOUND_PROP: { auto [sol, success, work] = bound_prop_recombiner.recombine(a, b, population.weights); recombine_stats.set_recombiner_work(work); + recombine_stats.stop_recombiner_time(); if (success) { recombine_stats.add_success(); } return std::make_pair(sol, success); } case recombiner_enum_t::FP: { auto [sol, success, work] = fp_recombiner.recombine(a, b, population.weights); recombine_stats.set_recombiner_work(work); + recombine_stats.stop_recombiner_time(); if (success) { recombine_stats.add_success(); } return std::make_pair(sol, success); } case recombiner_enum_t::LINE_SEGMENT: { auto [sol, success, work] = line_segment_recombiner.recombine(a, b, population.weights); recombine_stats.set_recombiner_work(work); + recombine_stats.stop_recombiner_time(); if (success) { recombine_stats.add_success(); } return std::make_pair(sol, success); } case recombiner_enum_t::SUB_MIP: { auto [sol, success, work] = sub_mip_recombiner.recombine(a, b, population.weights); recombine_stats.set_recombiner_work(work); + recombine_stats.stop_recombiner_time(); if (success) { recombine_stats.add_success(); } return std::make_pair(sol, success); } diff --git a/cpp/src/mip/diversity/multi_armed_bandit.cuh b/cpp/src/mip/diversity/multi_armed_bandit.cuh index 571939ae63..abcd233fa6 100644 --- a/cpp/src/mip/diversity/multi_armed_bandit.cuh +++ b/cpp/src/mip/diversity/multi_armed_bandit.cuh @@ -45,13 +45,22 @@ struct ls_work_normalized_reward_t { }; struct recombiner_work_normalized_reward_t { + bool deterministic; double work; - recombiner_work_normalized_reward_t(double work) : work(work) {} + recombiner_work_normalized_reward_t(bool deterministic, double work) + : deterministic(deterministic), work(work) + { + } double operator()(double factor) const { - // normal recombiners process 200 variables - return factor * (std::max(0.1, 4.0 - (work / 200))); + // normal recombiners take 2000 ms + if (deterministic) { + double time_in_miliseconds = work; + return factor * (std::max(0.1, 4.0 - (time_in_miliseconds / 2000))); + } else { + return factor * (std::max(0.1, 4.0 - (work / 200))); + } } }; diff --git a/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp b/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp index 9986a4d940..de0c314058 100644 --- a/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp +++ b/cpp/src/mip/diversity/recombiners/recombiner_stats.hpp @@ -75,12 +75,27 @@ struct all_recombine_stats { // enum of the last attempted recombiner std::optional last_attempt; + double last_recombiner_time; + std::chrono::high_resolution_clock::time_point last_recombiner_start_time; double last_recombiner_work; void set_recombiner_work(double work) { last_recombiner_work = work; } double get_last_recombiner_work() { return last_recombiner_work; } + void start_recombiner_time() + { + last_recombiner_start_time = std::chrono::high_resolution_clock::now(); + } + void stop_recombiner_time() + { + last_recombiner_time = std::chrono::duration_cast( + std::chrono::high_resolution_clock::now() - last_recombiner_start_time) + .count(); + } + + double get_last_recombiner_time() { return last_recombiner_time; } + void reset() { for (size_t i = 0; i < recombiner_count; ++i) { diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index da4f2edce1..5051d540c6 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -1079,11 +1079,10 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) if (timer.check_time_limit() || steps >= settings.iteration_limit) { limit_reached = true; } #if !FJ_SINGLE_STEP - // if (steps % 500 == 0) - if (false) + if (steps % 500 == 0) #endif { - CUOPT_LOG_DEBUG( + CUOPT_LOG_TRACE( "FJ " "step %d viol %.2g [%d], obj %.8g, best %.8g, mins %d, maxw %g, " "objw %g, sol %x, delta %x, inc %x, lhs %x, lhscomp %x, viol %x, weights %x", @@ -1283,9 +1282,8 @@ i_t fj_t::solve(solution_t& solution) detail::compute_hash(cstr_left_weights), detail::compute_hash(cstr_right_weights)); - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - settings.work_limit = settings.time_limit; - } + bool deterministic = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; + if (deterministic) { settings.work_limit = settings.time_limit; } // if work_limit is set: compute an estimate of the number of iterations required if (settings.work_limit != std::numeric_limits::infinity()) { std::map features_map = get_feature_vector(0); @@ -1333,7 +1331,7 @@ i_t fj_t::solve(solution_t& solution) handle_ptr->sync_stream(); // Compute and store feature vector for later logging - feature_vector = get_feature_vector(0); + if (deterministic) { feature_vector = get_feature_vector(0); } i_t iterations = host_loop(solution); RAFT_CHECK_CUDA(handle_ptr->get_stream()); @@ -1384,42 +1382,44 @@ i_t fj_t::solve(solution_t& solution) cuopt_func_call(solution.test_variable_bounds()); - double work_to_record = settings.work_limit; - - if (iterations < settings.iteration_limit) { - CUOPT_LOG_DEBUG( - "FJ early exit at %d iterations (limit: %d)", iterations, settings.iteration_limit); - // Compute the work unit corresponding to the number of iterations elapsed - // by incrementally guessing work units until the model predicts >= actual iterations - // TODO: awfully ugly, change - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC && iterations > 0) { - double guessed_work = 0.0; - const double work_increment = 0.1; - const double max_work = settings.work_limit * 2.0; // Safety limit - float predicted_iters = 0.0f; - - // Make a copy of the feature vector and modify the time/work_limit field - std::map features_for_prediction = feature_vector; - - while (guessed_work <= max_work) { - features_for_prediction["time"] = (float)guessed_work; - predicted_iters = std::max( - 0.0f, - (float)ceil( - context.work_unit_predictors.fj_predictor.predict_scalar(features_for_prediction))); - - if (predicted_iters >= (float)iterations) { - work_to_record = guessed_work; - break; - } + if (deterministic) { + double work_to_record = settings.work_limit; + + if (iterations < settings.iteration_limit) { + CUOPT_LOG_DEBUG( + "FJ early exit at %d iterations (limit: %d)", iterations, settings.iteration_limit); + // Compute the work unit corresponding to the number of iterations elapsed + // by incrementally guessing work units until the model predicts >= actual iterations + // TODO: awfully ugly, change + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC && iterations > 0) { + double guessed_work = 0.0; + const double work_increment = 0.1; + const double max_work = settings.work_limit * 2.0; // Safety limit + float predicted_iters = 0.0f; + + // Make a copy of the feature vector and modify the time/work_limit field + std::map features_for_prediction = feature_vector; + + while (guessed_work <= max_work) { + features_for_prediction["time"] = (float)guessed_work; + predicted_iters = std::max( + 0.0f, + (float)ceil( + context.work_unit_predictors.fj_predictor.predict_scalar(features_for_prediction))); + + if (predicted_iters >= (float)iterations) { + work_to_record = guessed_work; + break; + } - guessed_work += work_increment; + guessed_work += work_increment; + } } } - } - CUOPT_LOG_DEBUG("FJ: recording work %fwu for %d iterations", work_to_record, iterations); - timer.record_work(work_to_record); + CUOPT_LOG_DEBUG("FJ: recording work %fwu for %d iterations", work_to_record, iterations); + timer.record_work(work_to_record); + } CUOPT_LOG_DEBUG("FJ sol hash %x", solution.get_hash()); diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index 0332b41e41..803f2cf5aa 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -899,7 +899,6 @@ DI void update_changed_constraints(typename fj_t::climber_data_t::view // sort changed constraints to guarantee determinism // TODO: horribly slow as it is... block-parallelize at least? but not trivial for arbitrary // sizes w/ CUB - // TODO(2): tsk.. ... just bucket sort... if (fj.settings->work_limit != std::numeric_limits::infinity()) { thrust::sort(thrust::seq, fj.constraints_changed.begin(), diff --git a/cpp/src/mip/feasibility_jump/fj_cpu.cuh b/cpp/src/mip/feasibility_jump/fj_cpu.cuh index 27f9d79115..00a2cd224d 100644 --- a/cpp/src/mip/feasibility_jump/fj_cpu.cuh +++ b/cpp/src/mip/feasibility_jump/fj_cpu.cuh @@ -125,7 +125,7 @@ struct fj_cpu_climber_t { // vector is actually likely beneficial here since we're memory bound std::vector flip_move_computed; - + ; // CSR nnz offset -> (delta, score) std::vector> cached_mtm_moves; @@ -151,11 +151,6 @@ struct fj_cpu_climber_t { std::atomic halted{false}; - // PAPI performance counters - int papi_event_set{-1}; - bool papi_initialized{false}; - std::vector papi_events; - // Feature tracking for regression model (last 1000 iterations) i_t nnz_processed_window{0}; i_t n_lift_moves_window{0}; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index 837c1e5a67..ecd93c2e27 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -188,7 +188,6 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t 0) { temp_p.insert_variables(h_variables); } diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu index 8454daa575..5f6676e775 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu @@ -189,9 +189,7 @@ bool line_segment_search_t::search_line_segment( best_feasible_cost, curr_cost); } - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { - if (timer.check_time_limit()) { break; } - } + if (timer.check_time_limit()) { break; } i_t number_of_integer_var_diff = compute_number_of_integer_var_diff( solution.problem_ptr->integer_indices, solution.assignment, @@ -229,9 +227,7 @@ bool line_segment_search_t::search_line_segment( best_feasible_cost, curr_cost); } - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { - if (timer.check_time_limit()) { break; } - } + if (timer.check_time_limit()) { break; } } // if not recombiner mode but local search mode if (!settings.recombiner_mode) { diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 8e4dc250e8..4d5e8f919e 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -153,19 +153,9 @@ bool local_search_t::do_fj_solve(solution_t& solution, auto h_weights = cuopt::host_copy(in_fj.cstr_weights, solution.handle_ptr->get_stream()); auto h_objective_weight = in_fj.objective_weight.value(solution.handle_ptr->get_stream()); - - // for now: always assign the CPUFJs to perform 1000 iters per s - fj_settings_t cpu_fj_settings{}; - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { - cpu_fj_settings.iteration_limit = std::numeric_limits::max(); - } else { - // TODO: CHANGE - cpu_fj_settings.iteration_limit = 1000 * time_limit; - } - for (auto& cpu_fj : ls_cpu_fj) { cpu_fj.fj_cpu = cpu_fj.fj_ptr->create_cpu_climber( - solution, h_weights, h_weights, h_objective_weight, cpu_fj_settings, true); + solution, h_weights, h_weights, h_objective_weight, fj_settings_t{}, true); } auto solution_copy = solution; @@ -196,14 +186,16 @@ bool local_search_t::do_fj_solve(solution_t& solution, f_t best_cpu_obj = std::numeric_limits::max(); // // Wait for CPU solver to finish - for (auto& cpu_fj : ls_cpu_fj) { - bool cpu_sol_found = cpu_fj.wait_for_cpu_solver(); - if (cpu_sol_found) { - f_t cpu_obj = cpu_fj.fj_cpu->h_best_objective; - if (cpu_obj < best_cpu_obj) { - best_cpu_obj = cpu_obj; - solution_cpu.copy_new_assignment(cpu_fj.fj_cpu->h_best_assignment); - solution_cpu.compute_feasibility(); + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + for (auto& cpu_fj : ls_cpu_fj) { + bool cpu_sol_found = cpu_fj.wait_for_cpu_solver(); + if (cpu_sol_found) { + f_t cpu_obj = cpu_fj.fj_cpu->h_best_objective; + if (cpu_obj < best_cpu_obj) { + best_cpu_obj = cpu_obj; + solution_cpu.copy_new_assignment(cpu_fj.fj_cpu->h_best_assignment); + solution_cpu.compute_feasibility(); + } } } } @@ -211,9 +203,6 @@ bool local_search_t::do_fj_solve(solution_t& solution, bool gpu_feasible = solution.get_feasible(); bool cpu_feasible = cpu_sol_found && solution_cpu.get_feasible(); - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - cpu_feasible = false; // ignore CPUFJ in deterministic mode - } static std::unordered_map total_calls; static std::unordered_map cpu_better; @@ -415,11 +404,7 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu bool is_feasible = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); if (!is_feasible) { - f_t lp_run_time = 2.; - // CHANGE - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - lp_run_time = std::numeric_limits::infinity(); - } + const f_t lp_run_time = 2.; relaxed_lp_settings_t lp_settings; lp_settings.time_limit = std::min(lp_run_time, timer.remaining_time()); lp_settings.work_limit = lp_settings.time_limit; diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index 03f2c47ed5..036db25bbe 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -405,7 +405,8 @@ bool bounds_repair_t::repair_problem(problem_t& problem, curr_violation = best_violation; best_bounds.update_from(problem, handle_ptr); i_t no_candidate_in_a_row = 0; - i_t iter_limit = std::numeric_limits::max(); + // TODO: do this better + i_t iter_limit = std::numeric_limits::max(); if (problem.deterministic) { iter_limit = 20; } while (h_n_violated_cstr > 0 && iter_limit-- > 0) { CUOPT_LOG_TRACE("Bounds repair loop: n_violated %d best_violation %f curr_violation %f", diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index a037886c26..11c6f988ef 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -767,7 +767,8 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob repair_stats.repair_attempts++; f_t repair_start_time = timer.remaining_time(); i_t n_of_repairs_needed_for_feasible = 0; - i_t iter_limit = std::numeric_limits::max(); + // TODO: do this better + i_t iter_limit = std::numeric_limits::max(); if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); iter_limit = 100; @@ -854,14 +855,7 @@ bool constraint_prop_t::find_integer( { using crit_t = termination_criterion_t; auto& unset_integer_vars = unset_vars; - i_t seed = cuopt::seed_generator::get_seed(); - std::mt19937 rng(seed); - - // CHANGE - if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); - } - + std::mt19937 rng(cuopt::seed_generator::get_seed()); lb_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); ub_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); assignment_restore.resize(sol.problem_ptr->n_variables, sol.handle_ptr->get_stream()); @@ -1120,9 +1114,6 @@ bool constraint_prop_t::apply_round( // === CONSTRAINT PROP PREDICTOR FEATURES - END === max_timer = work_limit_timer_t{context.gpu_heur_loop, max_time_for_bounds_prop}; - if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - max_timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); - } if (check_brute_force_rounding(sol)) { auto cp_end_time = std::chrono::high_resolution_clock::now(); auto cp_elapsed_ms = diff --git a/cpp/src/mip/presolve/multi_probe.cu b/cpp/src/mip/presolve/multi_probe.cu index 248ba0b49c..5a11164e43 100644 --- a/cpp/src/mip/presolve/multi_probe.cu +++ b/cpp/src/mip/presolve/multi_probe.cu @@ -148,7 +148,7 @@ bool multi_probe_t::calculate_bounds_update(problem_t& pb, <<get_stream()>>>(pb.view(), upd_1.view()); RAFT_CHECK_CUDA(handle_ptr->get_stream()); i_t h_bounds_changed_1 = upd_1.bounds_changed.value(handle_ptr->get_stream()); - // CUOPT_LOG_TRACE("Bounds changed upd 1 %d", h_bounds_changed_1); + CUOPT_LOG_TRACE("Bounds changed upd 1 %d", h_bounds_changed_1); skip_1 = (h_bounds_changed_1 == zero); } else if (skip_1) { upd_0.bounds_changed.set_value_async(zero, handle_ptr->get_stream()); @@ -156,7 +156,7 @@ bool multi_probe_t::calculate_bounds_update(problem_t& pb, <<get_stream()>>>(pb.view(), upd_0.view()); RAFT_CHECK_CUDA(handle_ptr->get_stream()); i_t h_bounds_changed_0 = upd_0.bounds_changed.value(handle_ptr->get_stream()); - // CUOPT_LOG_TRACE("Bounds changed upd 0 %d", h_bounds_changed_0); + CUOPT_LOG_TRACE("Bounds changed upd 0 %d", h_bounds_changed_0); skip_0 = (h_bounds_changed_0 == zero); } else { upd_0.bounds_changed.set_value_async(zero, handle_ptr->get_stream()); @@ -166,9 +166,9 @@ bool multi_probe_t::calculate_bounds_update(problem_t& pb, pb.view(), upd_0.view(), upd_1.view()); RAFT_CHECK_CUDA(handle_ptr->get_stream()); i_t h_bounds_changed_0 = upd_0.bounds_changed.value(handle_ptr->get_stream()); - // CUOPT_LOG_TRACE("Bounds changed upd 0 %d", h_bounds_changed_0); + CUOPT_LOG_TRACE("Bounds changed upd 0 %d", h_bounds_changed_0); i_t h_bounds_changed_1 = upd_1.bounds_changed.value(handle_ptr->get_stream()); - // CUOPT_LOG_TRACE("Bounds changed upd 1 %d", h_bounds_changed_1); + CUOPT_LOG_TRACE("Bounds changed upd 1 %d", h_bounds_changed_1); skip_0 = (h_bounds_changed_0 == zero); skip_1 = (h_bounds_changed_1 == zero); From fb0c0725e8cd6fe3c8cc1b25ace127ee78943da7 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 18 Nov 2025 11:18:49 +0000 Subject: [PATCH 101/225] add support for building with clang --- build.sh | 16 ++++++++++++++-- ci/tsan_suppressions.txt | 6 ++++++ cpp/CMakeLists.txt | 19 ++++++++++++++++++- cpp/include/cuopt/error.hpp | 2 -- .../utilities/internals.hpp | 1 + cpp/src/linear_programming/pdlp.cu | 2 +- .../utilities/cython_solve.cu | 3 +-- cpp/src/linear_programming/utils.cuh | 6 +++--- cpp/src/mip/diversity/lns/rins.cu | 4 ++-- cpp/src/mip/local_search/local_search.cu | 3 +-- cpp/src/mip/presolve/gf2_presolve.hpp | 4 ++++ cpp/src/mip/presolve/third_party_presolve.cpp | 4 ++++ cpp/src/mip/utilities/cpu_worker_thread.cuh | 8 +++++--- cpp/src/routing/crossovers/ox_recombiner.cuh | 4 ++-- .../distance_engine/waypoint_matrix_test.cpp | 6 +++--- .../c_api_tests/c_api_test.c | 1 + cpp/tests/routing/level0/l0_ges_test.cu | 4 ++-- .../level0/l0_objective_function_test.cu | 2 +- cpp/tests/routing/level0/l0_routing_test.cu | 2 +- .../routing/level0/l0_vehicle_order_match.cu | 2 +- .../routing/level0/l0_vehicle_types_test.cu | 2 +- 21 files changed, 72 insertions(+), 29 deletions(-) create mode 100644 ci/tsan_suppressions.txt diff --git a/build.sh b/build.sh index e129ee4efa..07a435352e 100755 --- a/build.sh +++ b/build.sh @@ -15,7 +15,7 @@ REPODIR=$(cd "$(dirname "$0")"; pwd) LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build} LIBMPS_PARSER_BUILD_DIR=${LIBMPS_PARSER_BUILD_DIR:=${REPODIR}/cpp/libmps_parser/build} -VALIDARGS="clean libcuopt libmps_parser cuopt_mps_parser cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" +VALIDARGS="clean libcuopt libmps_parser cuopt_mps_parser cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" HELP="$0 [ ...] [ ...] where is: clean - remove all existing build artifacts and configuration (start over) @@ -32,7 +32,8 @@ HELP="$0 [ ...] [ ...] -g - build for debug -a - Enable assertion (by default in debug mode) -b - Build with benchmark settings - -fsanitize - Build with sanitizer + -fsanitize - Build with AddressSanitizer and UndefinedBehaviorSanitizer + -tsan - Build with ThreadSanitizer (cannot be used with -fsanitize) -n - no install step --no-fetch-rapids - don't fetch rapids dependencies -l= - log level. Options are: TRACE | DEBUG | INFO | WARN | ERROR | CRITICAL | OFF. Default=INFO @@ -76,6 +77,7 @@ BUILD_ALL_GPU_ARCH=0 BUILD_CI_ONLY=0 BUILD_LP_ONLY=0 BUILD_SANITIZER=0 +BUILD_TSAN=0 SKIP_C_PYTHON_ADAPTERS=0 SKIP_TESTS_BUILD=0 SKIP_ROUTING_BUILD=0 @@ -230,6 +232,9 @@ fi if hasArg -fsanitize; then BUILD_SANITIZER=1 fi +if hasArg -tsan; then + BUILD_TSAN=1 +fi if hasArg --skip-c-python-adapters; then SKIP_C_PYTHON_ADAPTERS=1 fi @@ -298,6 +303,12 @@ if [ ${BUILD_LP_ONLY} -eq 1 ] && [ ${SKIP_C_PYTHON_ADAPTERS} -eq 0 ]; then exit 1 fi +if [ ${BUILD_SANITIZER} -eq 1 ] && [ ${BUILD_TSAN} -eq 1 ]; then + echo "ERROR: -fsanitize and -tsan cannot be used together" + echo "AddressSanitizer and ThreadSanitizer are mutually exclusive" + exit 1 +fi + if [ ${BUILD_ALL_GPU_ARCH} -eq 1 ]; then CUOPT_CMAKE_CUDA_ARCHITECTURES="RAPIDS" echo "Building for *ALL* supported GPU architectures..." @@ -344,6 +355,7 @@ if buildAll || hasArg libcuopt; then -DFETCH_RAPIDS=${FETCH_RAPIDS} \ -DBUILD_LP_ONLY=${BUILD_LP_ONLY} \ -DBUILD_SANITIZER=${BUILD_SANITIZER} \ + -DBUILD_TSAN=${BUILD_TSAN} \ -DSKIP_C_PYTHON_ADAPTERS=${SKIP_C_PYTHON_ADAPTERS} \ -DBUILD_TESTS=$((1 - ${SKIP_TESTS_BUILD})) \ -DSKIP_ROUTING_BUILD=${SKIP_ROUTING_BUILD} \ diff --git a/ci/tsan_suppressions.txt b/ci/tsan_suppressions.txt new file mode 100644 index 0000000000..b6f413e370 --- /dev/null +++ b/ci/tsan_suppressions.txt @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. +# SPDX-License-Identifier: Apache-2.0 + +# Ignore races in external header-only libraries +race:tbb +race:Papilo diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 9c26451a71..c2c2be2bb4 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -81,9 +81,22 @@ endif(CMAKE_COMPILER_IS_GNUCXX) # 2. (Optional) To run with a debugger (gdb or cuda-gdb) use the additional ASAN option alloc_dealloc_mismatch=0 if(BUILD_SANITIZER) list(APPEND CUOPT_CXX_FLAGS -fsanitize=address,undefined -fno-omit-frame-pointer -g -Wno-error=maybe-uninitialized) + if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + list(APPEND CUOPT_CXX_FLAGS -Wno-error=maybe-uninitialized) + endif() add_link_options(-fsanitize=address,undefined) endif(BUILD_SANITIZER) +# To use ThreadSanitizer: +# 1. Build with clang and the -tsan flag +# 2. Run the binary with env var set: OMP_TOOL_LIBRARIES=/usr/lib/llvm-17/lib/libarcher.so ARCHER_OPTIONS='verbose=1' TSAN_OPTIONS='suppresions=ci/tsan_suppressions.txt:ignore_noninstrumented_modules=1:halt_on_error=1' +# Replace with local llvm install path. libarcher.so must be presetn +if(BUILD_TSAN) + message(STATUS "Building with ThreadSanitizer enabled") + list(APPEND CUOPT_CXX_FLAGS -fsanitize=thread -fno-omit-frame-pointer -g) + add_link_options(-fsanitize=thread) +endif(BUILD_TSAN) + if(DEFINE_ASSERT) add_definitions(-DASSERT_MODE) endif(DEFINE_ASSERT) @@ -117,7 +130,11 @@ if(${CMAKE_CUDA_COMPILER_VERSION} VERSION_GREATER_EQUAL 12.9) set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -static-global-template-stub=false") endif() list(APPEND CUOPT_CUDA_FLAGS -Werror=cross-execution-space-call -Wno-deprecated-declarations -Xcompiler=-Werror --default-stream=per-thread) -list(APPEND CUOPT_CUDA_FLAGS -Xcompiler=-Wall -Wno-error=non-template-friend) +if("${CMAKE_CUDA_HOST_COMPILER}" MATCHES "clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + list(APPEND CUOPT_CUDA_FLAGS -Xcompiler=-Wall) +else() + list(APPEND CUOPT_CUDA_FLAGS -Xcompiler=-Wall -Wno-error=non-template-friend) +endif() list(APPEND CUOPT_CUDA_FLAGS -Xfatbin=-compress-all) if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 12.9 AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 13.0) list(APPEND CUOPT_CUDA_FLAGS -Xfatbin=--compress-level=3) diff --git a/cpp/include/cuopt/error.hpp b/cpp/include/cuopt/error.hpp index b6086245db..a83413515e 100644 --- a/cpp/include/cuopt/error.hpp +++ b/cpp/include/cuopt/error.hpp @@ -33,8 +33,6 @@ enum class error_type_t { */ struct logic_error : public std::logic_error { - explicit logic_error() = default; - logic_error(const logic_error& exception) = default; // Move constructor diff --git a/cpp/include/cuopt/linear_programming/utilities/internals.hpp b/cpp/include/cuopt/linear_programming/utilities/internals.hpp index 84c96a7164..86e7246faf 100644 --- a/cpp/include/cuopt/linear_programming/utilities/internals.hpp +++ b/cpp/include/cuopt/linear_programming/utilities/internals.hpp @@ -62,6 +62,7 @@ namespace linear_programming { class base_solution_t { public: + virtual ~base_solution_t() = default; virtual bool is_mip() const = 0; }; diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index 5d982bcc96..d78f1d1f48 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -1510,7 +1510,7 @@ void pdlp_solver_t::compute_initial_step_size() const auto& cusparse_view_ = pdhg_solver_.get_cusparse_view(); - int sing_iters = 0; + [[maybe_unused]] int sing_iters = 0; for (int i = 0; i < max_iterations; ++i) { ++sing_iters; // d_q = d_z diff --git a/cpp/src/linear_programming/utilities/cython_solve.cu b/cpp/src/linear_programming/utilities/cython_solve.cu index 111f3caf04..38968c0508 100644 --- a/cpp/src/linear_programming/utilities/cython_solve.cu +++ b/cpp/src/linear_programming/utilities/cython_solve.cu @@ -295,8 +295,7 @@ std::pair>, double> call_batch_solve( #pragma omp parallel for num_threads(max_thread) for (std::size_t i = 0; i < size; ++i) - list[i] = - std::move(call_solve(data_models[i], solver_settings, cudaStreamNonBlocking, is_batch_mode)); + list[i] = call_solve(data_models[i], solver_settings, cudaStreamNonBlocking, is_batch_mode); auto end = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end - start_solver); diff --git a/cpp/src/linear_programming/utils.cuh b/cpp/src/linear_programming/utils.cuh index 0da5d25ceb..2333283f39 100644 --- a/cpp/src/linear_programming/utils.cuh +++ b/cpp/src/linear_programming/utils.cuh @@ -62,9 +62,9 @@ struct max_abs_value { template i_t conditional_major(uint64_t total_pdlp_iterations) { - uint64_t step = 10; - uint64_t threshold = 1000; - uint64_t iteration = 0; + uint64_t step = 10; + uint64_t threshold = 1000; + [[maybe_unused]] uint64_t iteration = 0; [[maybe_unused]] constexpr uint64_t max_u64 = std::numeric_limits::max(); diff --git a/cpp/src/mip/diversity/lns/rins.cu b/cpp/src/mip/diversity/lns/rins.cu index c2e46da4d5..e09982391a 100644 --- a/cpp/src/mip/diversity/lns/rins.cu +++ b/cpp/src/mip/diversity/lns/rins.cu @@ -259,8 +259,8 @@ void rins_t::run_rins() branch_and_bound_settings.num_diving_threads = 1; branch_and_bound_settings.log.log = false; branch_and_bound_settings.log.log_prefix = "[RINS] "; - branch_and_bound_settings.solution_callback = [this, &rins_solution_queue]( - std::vector& solution, f_t objective) { + branch_and_bound_settings.solution_callback = [&rins_solution_queue](std::vector& solution, + f_t objective) { rins_solution_queue.push_back(solution); }; dual_simplex::branch_and_bound_t branch_and_bound(branch_and_bound_problem, diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index a3353e72fe..212c282ef6 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -81,8 +81,7 @@ void local_search_t::start_cpufj_scratch_threads(population_t 0); cpu_fj.fj_cpu->log_prefix = "******* scratch " + std::to_string(counter) + ": "; - cpu_fj.fj_cpu->improvement_callback = [this, &population, &cpu_fj]( - f_t obj, const std::vector& h_vec) { + cpu_fj.fj_cpu->improvement_callback = [&population](f_t obj, const std::vector& h_vec) { population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); if (obj < local_search_best_obj) { CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", diff --git a/cpp/src/mip/presolve/gf2_presolve.hpp b/cpp/src/mip/presolve/gf2_presolve.hpp index 19d4e7d813..53e79d2c93 100644 --- a/cpp/src/mip/presolve/gf2_presolve.hpp +++ b/cpp/src/mip/presolve/gf2_presolve.hpp @@ -7,13 +7,17 @@ #pragma once +#if !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overflow" // ignore boost error for pip wheel build +#endif #include #include #include #include +#if !defined(__clang__) #pragma GCC diagnostic pop +#endif namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index 22827c6e28..f3faf3dd7d 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -14,11 +14,15 @@ #include +#if !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overflow" // ignore boost error for pip wheel build +#endif #include #include +#if !defined(__clang__) #pragma GCC diagnostic pop +#endif namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip/utilities/cpu_worker_thread.cuh b/cpp/src/mip/utilities/cpu_worker_thread.cuh index 0f1671c94a..8d0b6d71ee 100644 --- a/cpp/src/mip/utilities/cpu_worker_thread.cuh +++ b/cpp/src/mip/utilities/cpu_worker_thread.cuh @@ -60,6 +60,7 @@ template cpu_worker_thread_base_t::~cpu_worker_thread_base_t() { // Note: We don't call on_terminate() here since the derived object is already destroyed. + CUOPT_LOG_DEBUG("Destroying CPU worker thread"); join_worker(); } @@ -83,12 +84,14 @@ void cpu_worker_thread_base_t::cpu_worker_thread() std::lock_guard lock(cpu_mutex); cpu_thread_done = true; } + cpu_cv.notify_all(); } } template void cpu_worker_thread_base_t::request_termination() { + CUOPT_LOG_DEBUG("Requesting termination of CPU worker thread"); bool should_terminate = false; { std::lock_guard lock(cpu_mutex); @@ -131,9 +134,8 @@ void cpu_worker_thread_base_t::start_cpu_solver() template bool cpu_worker_thread_base_t::wait_for_cpu_solver() { - while (!cpu_thread_done && !cpu_thread_terminate) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } + std::unique_lock lock(cpu_mutex); + cpu_cv.wait(lock, [this] { return cpu_thread_done || cpu_thread_terminate; }); return static_cast(this)->get_result(); } diff --git a/cpp/src/routing/crossovers/ox_recombiner.cuh b/cpp/src/routing/crossovers/ox_recombiner.cuh index 17823c28b8..7f965de2ff 100644 --- a/cpp/src/routing/crossovers/ox_recombiner.cuh +++ b/cpp/src/routing/crossovers/ox_recombiner.cuh @@ -336,7 +336,7 @@ struct OX { int i = routes_number; if (optimal_routes_search) { i = optimal_routes_number; } int end_index = offspring.size() - 1; - double cost_n, cost_p, total_delta = 0.; + [[maybe_unused]] double cost_n, cost_p, total_delta = 0.; std::vector>>> routes_to_add; std::vector tmp_route; @@ -530,7 +530,7 @@ struct OX { "Mismatch number of edges"); for (size_t j = 0; j < h_transpose_graph[i].size(); ++j) { auto [ref_edge, ref_weight, ref_veh] = h_transpose_graph[i][j]; - bool found = false; + [[maybe_unused]] bool found = false; for (int x = 0; x < tmp_transpose.row_sizes[i]; ++x) { auto edge = tmp_transpose.indices[transpose_offset + x]; auto veh = tmp_transpose.buckets[transpose_offset + x]; diff --git a/cpp/tests/distance_engine/waypoint_matrix_test.cpp b/cpp/tests/distance_engine/waypoint_matrix_test.cpp index 80288bc6fa..1f9bacf0ec 100644 --- a/cpp/tests/distance_engine/waypoint_matrix_test.cpp +++ b/cpp/tests/distance_engine/waypoint_matrix_test.cpp @@ -44,7 +44,7 @@ class waypoint_matrix_waypoints_sequence_test_t this->expected_sequence_offsets = param.sequence_offsets; } - void TearDown() {} + void TearDown() override {} void test_compute_waypoint_sequence() { @@ -131,7 +131,7 @@ class waypoint_matrix_shortest_path_cost_t this->weights.data()); } - void TearDown() {} + void TearDown() override {} void test_compute_shortest_path_costs() { @@ -192,7 +192,7 @@ class waypoint_matrix_cost_matrix_test_t this->weights.data()); } - void TearDown() {} + void TearDown() override {} void test_compute_cost_matrix() { diff --git a/cpp/tests/linear_programming/c_api_tests/c_api_test.c b/cpp/tests/linear_programming/c_api_tests/c_api_test.c index 25aef6d258..3b3032176b 100644 --- a/cpp/tests/linear_programming/c_api_tests/c_api_test.c +++ b/cpp/tests/linear_programming/c_api_tests/c_api_test.c @@ -53,6 +53,7 @@ const char* termination_status_to_string(cuopt_int_t termination_status) case CUOPT_TERIMINATION_STATUS_FEASIBLE_FOUND: return "Feasible found"; } + return "Unknown"; } diff --git a/cpp/tests/routing/level0/l0_ges_test.cu b/cpp/tests/routing/level0/l0_ges_test.cu index 22373f7045..afd0a26276 100644 --- a/cpp/tests/routing/level0/l0_ges_test.cu +++ b/cpp/tests/routing/level0/l0_ges_test.cu @@ -55,7 +55,7 @@ class routing_ges_test_t : public ::testing::TestWithParam>, this->populate_device_vectors(); } - void TearDown() {} + void TearDown() override {} assignment_t solve(const cuopt::routing::data_model_view_t& data_model, const cuopt::routing::solver_settings_t& solver_settings, @@ -163,7 +163,7 @@ class simple_routes_ges_test_t : public ::testing::TestWithParampopulate_device_vectors(); } - void TearDown() {} + void TearDown() override {} assignment_t solve(const cuopt::routing::data_model_view_t& data_model, const cuopt::routing::solver_settings_t& solver_settings, diff --git a/cpp/tests/routing/level0/l0_objective_function_test.cu b/cpp/tests/routing/level0/l0_objective_function_test.cu index 9355750269..4491398497 100644 --- a/cpp/tests/routing/level0/l0_objective_function_test.cu +++ b/cpp/tests/routing/level0/l0_objective_function_test.cu @@ -25,7 +25,7 @@ template class objective_function_test_t : public base_test_t, public ::testing::TestWithParam> { public: - objective_function_test_t() : base_test_t(512, 5E-2, 0) {} + objective_function_test_t() : base_test_t(512, 0, 0) {} void SetUp() override { auto p = GetParam(); diff --git a/cpp/tests/routing/level0/l0_routing_test.cu b/cpp/tests/routing/level0/l0_routing_test.cu index 4d7bbad024..735b6e4bc1 100644 --- a/cpp/tests/routing/level0/l0_routing_test.cu +++ b/cpp/tests/routing/level0/l0_routing_test.cu @@ -320,7 +320,7 @@ class routing_retail_test_t : public base_test_t, this->populate_device_vectors(); } - void TearDown() {} + void TearDown() override {} void test_cvrptw() { diff --git a/cpp/tests/routing/level0/l0_vehicle_order_match.cu b/cpp/tests/routing/level0/l0_vehicle_order_match.cu index 6c4d40ab79..4b1b9fdd37 100644 --- a/cpp/tests/routing/level0/l0_vehicle_order_match.cu +++ b/cpp/tests/routing/level0/l0_vehicle_order_match.cu @@ -24,7 +24,7 @@ namespace test { template class vehicle_order_test_t : public base_test_t, public ::testing::TestWithParam { public: - vehicle_order_test_t() : base_test_t(512, 5E-2, 0) {} + vehicle_order_test_t() : base_test_t(512, 0, 0) {} void SetUp() override { this->not_matching_constraints_fraction = GetParam(); diff --git a/cpp/tests/routing/level0/l0_vehicle_types_test.cu b/cpp/tests/routing/level0/l0_vehicle_types_test.cu index f7f2476836..4e46d31d69 100644 --- a/cpp/tests/routing/level0/l0_vehicle_types_test.cu +++ b/cpp/tests/routing/level0/l0_vehicle_types_test.cu @@ -23,7 +23,7 @@ namespace test { template class vehicle_types_test_t : public base_test_t, public ::testing::Test { public: - vehicle_types_test_t() : base_test_t(512, 5E-2, 0) {} + vehicle_types_test_t() : base_test_t(512, 0, 0) {} void SetUp() override { this->n_locations = input_.n_locations; From e3f2a79339f4d06e664339521eb79631b7977530 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 18 Nov 2025 12:34:27 +0000 Subject: [PATCH 102/225] remove debug calls --- cpp/src/mip/utilities/cpu_worker_thread.cuh | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpp/src/mip/utilities/cpu_worker_thread.cuh b/cpp/src/mip/utilities/cpu_worker_thread.cuh index 8d0b6d71ee..e437486cda 100644 --- a/cpp/src/mip/utilities/cpu_worker_thread.cuh +++ b/cpp/src/mip/utilities/cpu_worker_thread.cuh @@ -60,7 +60,6 @@ template cpu_worker_thread_base_t::~cpu_worker_thread_base_t() { // Note: We don't call on_terminate() here since the derived object is already destroyed. - CUOPT_LOG_DEBUG("Destroying CPU worker thread"); join_worker(); } @@ -91,7 +90,6 @@ void cpu_worker_thread_base_t::cpu_worker_thread() template void cpu_worker_thread_base_t::request_termination() { - CUOPT_LOG_DEBUG("Requesting termination of CPU worker thread"); bool should_terminate = false; { std::lock_guard lock(cpu_mutex); From 057ecc789514a8efa72f55b6a4b85156e8eb6d75 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 18 Nov 2025 14:02:20 +0000 Subject: [PATCH 103/225] fix cmakelists --- cpp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index c2c2be2bb4..899a5ed387 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -80,7 +80,7 @@ endif(CMAKE_COMPILER_IS_GNUCXX) # 1. Run the binary with env var set: LD_PRELOAD="$(gcc -print-file-name=libasan.so)" ASAN_OPTIONS='protect_shadow_gap=0:replace_intrin=0' # 2. (Optional) To run with a debugger (gdb or cuda-gdb) use the additional ASAN option alloc_dealloc_mismatch=0 if(BUILD_SANITIZER) - list(APPEND CUOPT_CXX_FLAGS -fsanitize=address,undefined -fno-omit-frame-pointer -g -Wno-error=maybe-uninitialized) + list(APPEND CUOPT_CXX_FLAGS -fsanitize=address,undefined -fno-omit-frame-pointer -g) if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") list(APPEND CUOPT_CXX_FLAGS -Wno-error=maybe-uninitialized) endif() From 2463de65518a7791499f9f1b3cb237fccbfbd268 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 25 Nov 2025 16:46:38 +0000 Subject: [PATCH 104/225] move suppressiosn --- cpp/CMakeLists.txt | 2 +- {ci => cpp}/tsan_suppressions.txt | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {ci => cpp}/tsan_suppressions.txt (100%) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 899a5ed387..eda143c964 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -89,7 +89,7 @@ endif(BUILD_SANITIZER) # To use ThreadSanitizer: # 1. Build with clang and the -tsan flag -# 2. Run the binary with env var set: OMP_TOOL_LIBRARIES=/usr/lib/llvm-17/lib/libarcher.so ARCHER_OPTIONS='verbose=1' TSAN_OPTIONS='suppresions=ci/tsan_suppressions.txt:ignore_noninstrumented_modules=1:halt_on_error=1' +# 2. Run the binary with env var set: OMP_TOOL_LIBRARIES=/usr/lib/llvm-17/lib/libarcher.so ARCHER_OPTIONS='verbose=1' TSAN_OPTIONS='suppresions=cpp/tsan_suppressions.txt:ignore_noninstrumented_modules=1:halt_on_error=1' # Replace with local llvm install path. libarcher.so must be presetn if(BUILD_TSAN) message(STATUS "Building with ThreadSanitizer enabled") diff --git a/ci/tsan_suppressions.txt b/cpp/tsan_suppressions.txt similarity index 100% rename from ci/tsan_suppressions.txt rename to cpp/tsan_suppressions.txt From 8356f1d2ec6d01d053aad54b376bc856ca32f1d3 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 4 Dec 2025 16:16:17 +0000 Subject: [PATCH 105/225] address warnings, add msan setting --- build.sh | 22 +++++++++++++++++-- cpp/CMakeLists.txt | 10 +++++++++ cpp/src/routing/local_search/local_search.cu | 4 ++-- cpp/src/routing/local_search/sliding_tsp.cu | 4 ++-- .../routing/local_search/sliding_window.cu | 5 ++--- cpp/src/routing/local_search/two_opt.cu | 5 ++--- .../routing/local_search/vrp/vrp_execute.cu | 4 ++-- 7 files changed, 40 insertions(+), 14 deletions(-) diff --git a/build.sh b/build.sh index 07a435352e..3f1a2c1f5b 100755 --- a/build.sh +++ b/build.sh @@ -15,7 +15,7 @@ REPODIR=$(cd "$(dirname "$0")"; pwd) LIBCUOPT_BUILD_DIR=${LIBCUOPT_BUILD_DIR:=${REPODIR}/cpp/build} LIBMPS_PARSER_BUILD_DIR=${LIBMPS_PARSER_BUILD_DIR:=${REPODIR}/cpp/libmps_parser/build} -VALIDARGS="clean libcuopt libmps_parser cuopt_mps_parser cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" +VALIDARGS="clean libcuopt libmps_parser cuopt_mps_parser cuopt cuopt_server cuopt_sh_client docs deb -a -b -g -fsanitize -tsan -msan -v -l= --verbose-pdlp --build-lp-only --no-fetch-rapids --skip-c-python-adapters --skip-tests-build --skip-routing-build --skip-fatbin-write --host-lineinfo [--cmake-args=\\\"\\\"] [--cache-tool=] -n --allgpuarch --ci-only-arch --show_depr_warn -h --help" HELP="$0 [ ...] [ ...] where is: clean - remove all existing build artifacts and configuration (start over) @@ -33,7 +33,8 @@ HELP="$0 [ ...] [ ...] -a - Enable assertion (by default in debug mode) -b - Build with benchmark settings -fsanitize - Build with AddressSanitizer and UndefinedBehaviorSanitizer - -tsan - Build with ThreadSanitizer (cannot be used with -fsanitize) + -tsan - Build with ThreadSanitizer (cannot be used with -fsanitize or -msan) + -msan - Build with MemorySanitizer (cannot be used with -fsanitize or -tsan) -n - no install step --no-fetch-rapids - don't fetch rapids dependencies -l= - log level. Options are: TRACE | DEBUG | INFO | WARN | ERROR | CRITICAL | OFF. Default=INFO @@ -78,6 +79,7 @@ BUILD_CI_ONLY=0 BUILD_LP_ONLY=0 BUILD_SANITIZER=0 BUILD_TSAN=0 +BUILD_MSAN=0 SKIP_C_PYTHON_ADAPTERS=0 SKIP_TESTS_BUILD=0 SKIP_ROUTING_BUILD=0 @@ -235,6 +237,9 @@ fi if hasArg -tsan; then BUILD_TSAN=1 fi +if hasArg -msan; then + BUILD_MSAN=1 +fi if hasArg --skip-c-python-adapters; then SKIP_C_PYTHON_ADAPTERS=1 fi @@ -309,6 +314,18 @@ if [ ${BUILD_SANITIZER} -eq 1 ] && [ ${BUILD_TSAN} -eq 1 ]; then exit 1 fi +if [ ${BUILD_SANITIZER} -eq 1 ] && [ ${BUILD_MSAN} -eq 1 ]; then + echo "ERROR: -fsanitize and -msan cannot be used together" + echo "AddressSanitizer and MemorySanitizer are mutually exclusive" + exit 1 +fi + +if [ ${BUILD_TSAN} -eq 1 ] && [ ${BUILD_MSAN} -eq 1 ]; then + echo "ERROR: -tsan and -msan cannot be used together" + echo "ThreadSanitizer and MemorySanitizer are mutually exclusive" + exit 1 +fi + if [ ${BUILD_ALL_GPU_ARCH} -eq 1 ]; then CUOPT_CMAKE_CUDA_ARCHITECTURES="RAPIDS" echo "Building for *ALL* supported GPU architectures..." @@ -356,6 +373,7 @@ if buildAll || hasArg libcuopt; then -DBUILD_LP_ONLY=${BUILD_LP_ONLY} \ -DBUILD_SANITIZER=${BUILD_SANITIZER} \ -DBUILD_TSAN=${BUILD_TSAN} \ + -DBUILD_MSAN=${BUILD_MSAN} \ -DSKIP_C_PYTHON_ADAPTERS=${SKIP_C_PYTHON_ADAPTERS} \ -DBUILD_TESTS=$((1 - ${SKIP_TESTS_BUILD})) \ -DSKIP_ROUTING_BUILD=${SKIP_ROUTING_BUILD} \ diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index eda143c964..a2467f45c8 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -97,6 +97,16 @@ if(BUILD_TSAN) add_link_options(-fsanitize=thread) endif(BUILD_TSAN) +# To use MemorySanitizer: +# 1. Build with clang and the -msan flag (MemorySanitizer requires clang) +# 2. Run the binary with env var set: MSAN_OPTIONS='halt_on_error=1' +# Note: MemorySanitizer requires all code (including libraries) to be instrumented for accurate results +if(BUILD_MSAN) + message(STATUS "Building with MemorySanitizer enabled") + list(APPEND CUOPT_CXX_FLAGS -fsanitize=memory -fno-omit-frame-pointer -g -fsanitize-memory-track-origins=1) + add_link_options(-fsanitize=memory) +endif(BUILD_MSAN) + if(DEFINE_ASSERT) add_definitions(-DASSERT_MODE) endif(DEFINE_ASSERT) diff --git a/cpp/src/routing/local_search/local_search.cu b/cpp/src/routing/local_search/local_search.cu index d26ec1ab93..795d1fb35c 100644 --- a/cpp/src/routing/local_search/local_search.cu +++ b/cpp/src/routing/local_search/local_search.cu @@ -126,8 +126,8 @@ bool local_search_t::run_cross_search(solution_t EPSILON, "Cost should improve!"); cuopt_assert(abs((cost_before - cost_after) - - move_candidates.debug_delta.value(sol.sol_handle->get_stream()) < - EPSILON * (1 + abs(cost_before))), + move_candidates.debug_delta.value(sol.sol_handle->get_stream())) < + EPSILON * (1 + abs(cost_before)), "Cost mismatch on cross costs!"); return true; } diff --git a/cpp/src/routing/local_search/sliding_tsp.cu b/cpp/src/routing/local_search/sliding_tsp.cu index 61869a2b71..f0b43542bc 100644 --- a/cpp/src/routing/local_search/sliding_tsp.cu +++ b/cpp/src/routing/local_search/sliding_tsp.cu @@ -565,8 +565,8 @@ bool local_search_t::perform_sliding_tsp( sol.get_cost(false, move_candidates.weights)); cuopt_assert(abs((cost_before - cost_after) + - move_candidates.debug_delta.value(sol.sol_handle->get_stream()) < - EPSILON * (1 + abs(cost_before))), + move_candidates.debug_delta.value(sol.sol_handle->get_stream())) < + EPSILON * (1 + abs(cost_before)), "Cost mismatch on sliding_tsp costs!"); cuopt_assert(cost_before - cost_after >= EPSILON, "Cost should improve!"); diff --git a/cpp/src/routing/local_search/sliding_window.cu b/cpp/src/routing/local_search/sliding_window.cu index 5c19d22a2a..fd5ef8500e 100644 --- a/cpp/src/routing/local_search/sliding_window.cu +++ b/cpp/src/routing/local_search/sliding_window.cu @@ -1116,9 +1116,8 @@ bool local_search_t::perform_sliding_window( cuopt_assert(cost_before - cost_after >= EPSILON, "Cost should improve!"); cuopt_assert(abs((cost_before - cost_after) - - move_candidates.debug_delta.value(solution.sol_handle->get_stream()) < - EPSILON), - "Cost mismatch on cross costs!"); + move_candidates.debug_delta.value(solution.sol_handle->get_stream())) < EPSILON, + "Cost mismatch on sliding_window costs!"); return true; } diff --git a/cpp/src/routing/local_search/two_opt.cu b/cpp/src/routing/local_search/two_opt.cu index 966917b980..ff345312af 100644 --- a/cpp/src/routing/local_search/two_opt.cu +++ b/cpp/src/routing/local_search/two_opt.cu @@ -458,9 +458,8 @@ bool local_search_t::perform_two_opt( : sol.get_cost(move_candidates.include_objective, move_candidates.weights)); cuopt_assert(abs((cost_before - cost_after) + - - move_candidates.debug_delta.value(sol.sol_handle->get_stream()) < - EPSILON * (1 + abs(cost_before))), + move_candidates.debug_delta.value(sol.sol_handle->get_stream())) < + EPSILON * (1 + abs(cost_before)), "Cost mismatch on two_opt costs!"); cuopt_assert(cost_before - cost_after >= EPSILON, "Cost should improve!"); sol.global_runtime_checks(false, false, "two_opt_end"); diff --git a/cpp/src/routing/local_search/vrp/vrp_execute.cu b/cpp/src/routing/local_search/vrp/vrp_execute.cu index cafe0e5ef3..ff560e1cca 100644 --- a/cpp/src/routing/local_search/vrp/vrp_execute.cu +++ b/cpp/src/routing/local_search/vrp/vrp_execute.cu @@ -465,8 +465,8 @@ bool execute_vrp_moves(solution_t& sol, sol.get_cost(move_candidates.include_objective, move_candidates.weights)); cuopt_assert(cost_before - cost_after > EPSILON, "Cost should improve!"); cuopt_assert(abs((cost_before - cost_after) + - move_candidates.debug_delta.value(sol.sol_handle->get_stream()) < - EPSILON * (1 + abs(cost_before))), + move_candidates.debug_delta.value(sol.sol_handle->get_stream())) < + EPSILON * (1 + abs(cost_before)), "Cost mismatch on vrp costs!"); return true; } From 3cced0159196011c8460d1ff1b8b77158c562943 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 17 Dec 2025 17:34:02 +0000 Subject: [PATCH 106/225] clang compiler bug workaround --- cpp/src/dual_simplex/barrier.cu | 56 ++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/cpp/src/dual_simplex/barrier.cu b/cpp/src/dual_simplex/barrier.cu index 8c0a32fad3..4bf3e3ed70 100644 --- a/cpp/src/dual_simplex/barrier.cu +++ b/cpp/src/dual_simplex/barrier.cu @@ -44,6 +44,48 @@ namespace cuopt::linear_programming::dual_simplex { auto constexpr use_gpu = true; +// non-template wrappers to work around clang compiler bug +[[maybe_unused]] static void pairwise_multiply( + float* a, float* b, float* out, int size, rmm::cuda_stream_view stream) +{ + cub::DeviceTransform::Transform( + cuda::std::make_tuple(a, b), out, size, cuda::std::multiplies<>{}, stream); +} + +[[maybe_unused]] static void pairwise_multiply( + double* a, double* b, double* out, int size, rmm::cuda_stream_view stream) +{ + cub::DeviceTransform::Transform( + cuda::std::make_tuple(a, b), out, size, cuda::std::multiplies<>{}, stream); +} + +[[maybe_unused]] static void axpy( + float alpha, float* x, float beta, float* y, float* out, int size, rmm::cuda_stream_view stream) +{ + cub::DeviceTransform::Transform( + cuda::std::make_tuple(x, y), + out, + size, + [alpha, beta] __host__ __device__(float a, float b) { return alpha * a + beta * b; }, + stream); +} + +[[maybe_unused]] static void axpy(double alpha, + double* x, + double beta, + double* y, + double* out, + int size, + rmm::cuda_stream_view stream) +{ + cub::DeviceTransform::Transform( + cuda::std::make_tuple(x, y), + out, + size, + [alpha, beta] __host__ __device__(double a, double b) { return alpha * a + beta * b; }, + stream); +} + template class iteration_data_t { public: @@ -1404,12 +1446,7 @@ class iteration_data_t { // diag.pairwise_product(x1, r1); // r1 <- D * x_1 - thrust::transform(handle_ptr->get_thrust_policy(), - d_x1.data(), - d_x1.data() + n, - d_diag_.data(), - d_r1.data(), - thrust::multiplies()); + pairwise_multiply(d_x1.data(), d_diag_.data(), d_r1.data(), n, stream_view_); // r1 <- Q x1 + D x1 if (Q.n > 0) { @@ -1419,12 +1456,7 @@ class iteration_data_t { // y1 <- - alpha * r1 + beta * y1 // y1.axpy(-alpha, r1, beta); - thrust::transform(handle_ptr->get_thrust_policy(), - d_r1.data(), - d_r1.data() + n, - d_y1.data(), - d_y1.data(), - axpy_op{-alpha, beta}); + axpy(-alpha, d_r1.data(), beta, d_y1.data(), d_y1.data(), n, stream_view_); // matrix_transpose_vector_multiply(A, alpha, x2, 1.0, y1); cusparse_view_.transpose_spmv(alpha, d_x2, 1.0, d_y1); From 741f373b8ea728afbf726c5ea89d51f351ef3f0b Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 7 Jan 2026 16:15:21 +0100 Subject: [PATCH 107/225] initial bb debug impl --- cpp/CMakeLists.txt | 3 + cpp/src/dual_simplex/bb_event.hpp | 228 ++++++ cpp/src/dual_simplex/bb_worker_state.hpp | 291 ++++++++ cpp/src/dual_simplex/branch_and_bound.cpp | 781 +++++++++++++++++++- cpp/src/dual_simplex/branch_and_bound.hpp | 64 ++ cpp/src/dual_simplex/bsp_debug.hpp | 843 ++++++++++++++++++++++ cpp/src/dual_simplex/mip_node.hpp | 30 + cpp/src/mip/solver.cu | 18 +- cpp/tests/mip/presolve_test.cu | 2 +- 9 files changed, 2235 insertions(+), 25 deletions(-) create mode 100644 cpp/src/dual_simplex/bb_event.hpp create mode 100644 cpp/src/dual_simplex/bb_worker_state.hpp create mode 100644 cpp/src/dual_simplex/bsp_debug.hpp diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 39d4ed78b2..d8b3928521 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -62,6 +62,9 @@ message(VERBOSE "cuOpt: fatbin: ${WRITE_FATBIN}") # CUDA runtime rapids_cuda_init_runtime(USE_STATIC ON) +message(STATUS "HEY") +find_package(CUDAToolkit) +message(STATUS "HEY") rapids_find_package(CUDAToolkit REQUIRED BUILD_EXPORT_SET cuopt-exports diff --git a/cpp/src/dual_simplex/bb_event.hpp b/cpp/src/dual_simplex/bb_event.hpp new file mode 100644 index 0000000000..280863f6a2 --- /dev/null +++ b/cpp/src/dual_simplex/bb_event.hpp @@ -0,0 +1,228 @@ +/* clang-format off */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* clang-format on */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace cuopt::linear_programming::dual_simplex { + +// Event types generated by B&B workers during BSP execution +enum class bb_event_type_t : int8_t { + NODE_BRANCHED = 0, // Node was solved and branched into two children + NODE_FATHOMED = 1, // Node was fathomed (cutoff or infeasible) + NODE_INTEGER = 2, // Node has an integer feasible solution + NODE_PAUSED = 3, // Node processing paused at horizon boundary + NODE_INFEASIBLE = 4, // Node LP relaxation is infeasible + NODE_NUMERICAL = 5, // Numerical issue encountered + HEURISTIC_SOLUTION = 6 // Solution received from heuristics +}; + +// Payload for NODE_BRANCHED events +template +struct branched_payload_t { + i_t down_child_id; + i_t up_child_id; + f_t node_lower_bound; + i_t branch_var; + f_t branch_value; +}; + +// Payload for NODE_INTEGER events (integer feasible solution found) +template +struct integer_solution_payload_t { + f_t objective_value; + // Note: the full solution is stored separately, not in the event +}; + +// Payload for NODE_FATHOMED events +template +struct fathomed_payload_t { + f_t lower_bound; // Bound at which node was fathomed +}; + +// Payload for NODE_PAUSED events +template +struct paused_payload_t { + f_t accumulated_vt; // How much virtual time spent on this node so far +}; + +// Payload for HEURISTIC_SOLUTION events +template +struct heuristic_solution_payload_t { + f_t objective_value; + size_t solution_index; // Index into a solution storage vector +}; + +// A single event generated during BSP execution +template +struct bb_event_t { + bb_event_type_t type; + double vt_timestamp; // Virtual time when this event occurred + int worker_id; // Which worker generated this event + i_t node_id; // Node that generated this event + int event_sequence; // Sequence number within worker for tie-breaking + + // Payload union - only one is valid based on type + union { + branched_payload_t branched; + integer_solution_payload_t integer_solution; + fathomed_payload_t fathomed; + paused_payload_t paused; + heuristic_solution_payload_t heuristic; + } payload; + + // Default constructor + bb_event_t() + : type(bb_event_type_t::NODE_FATHOMED), + vt_timestamp(0.0), + worker_id(0), + node_id(0), + event_sequence(0) + { + payload.fathomed = {0.0}; + } + + // Comparison for deterministic sorting: (vt_timestamp, worker_id, event_sequence) + bool operator<(const bb_event_t& other) const + { + return std::tie(vt_timestamp, worker_id, event_sequence) < + std::tie(other.vt_timestamp, other.worker_id, other.event_sequence); + } + + // Factory methods for creating events + + static bb_event_t make_branched(double vt, + int worker, + i_t node, + int seq, + i_t down_id, + i_t up_id, + f_t lower_bound, + i_t branch_var, + f_t branch_val) + { + bb_event_t e; + e.type = bb_event_type_t::NODE_BRANCHED; + e.vt_timestamp = vt; + e.worker_id = worker; + e.node_id = node; + e.event_sequence = seq; + e.payload.branched = {down_id, up_id, lower_bound, branch_var, branch_val}; + return e; + } + + static bb_event_t make_integer_solution(double vt, int worker, i_t node, int seq, f_t objective) + { + bb_event_t e; + e.type = bb_event_type_t::NODE_INTEGER; + e.vt_timestamp = vt; + e.worker_id = worker; + e.node_id = node; + e.event_sequence = seq; + e.payload.integer_solution = {objective}; + return e; + } + + static bb_event_t make_fathomed(double vt, int worker, i_t node, int seq, f_t lower_bound) + { + bb_event_t e; + e.type = bb_event_type_t::NODE_FATHOMED; + e.vt_timestamp = vt; + e.worker_id = worker; + e.node_id = node; + e.event_sequence = seq; + e.payload.fathomed = {lower_bound}; + return e; + } + + static bb_event_t make_infeasible(double vt, int worker, i_t node, int seq) + { + bb_event_t e; + e.type = bb_event_type_t::NODE_INFEASIBLE; + e.vt_timestamp = vt; + e.worker_id = worker; + e.node_id = node; + e.event_sequence = seq; + e.payload.fathomed = {std::numeric_limits::infinity()}; + return e; + } + + static bb_event_t make_paused(double vt, int worker, i_t node, int seq, f_t accumulated) + { + bb_event_t e; + e.type = bb_event_type_t::NODE_PAUSED; + e.vt_timestamp = vt; + e.worker_id = worker; + e.node_id = node; + e.event_sequence = seq; + e.payload.paused = {accumulated}; + return e; + } + + static bb_event_t make_numerical(double vt, int worker, i_t node, int seq) + { + bb_event_t e; + e.type = bb_event_type_t::NODE_NUMERICAL; + e.vt_timestamp = vt; + e.worker_id = worker; + e.node_id = node; + e.event_sequence = seq; + e.payload.fathomed = {std::numeric_limits::infinity()}; + return e; + } + + static bb_event_t make_heuristic_solution(double vt, + int worker, + int seq, + f_t objective, + size_t sol_idx) + { + bb_event_t e; + e.type = bb_event_type_t::HEURISTIC_SOLUTION; + e.vt_timestamp = vt; + e.worker_id = worker; + e.node_id = -1; // Not associated with a B&B node + e.event_sequence = seq; + e.payload.heuristic = {objective, sol_idx}; + return e; + } +}; + +// Batch of events from a single horizon period +template +struct bb_event_batch_t { + std::vector> events; + double horizon_start; + double horizon_end; + + void clear() + { + events.clear(); + horizon_start = 0.0; + horizon_end = 0.0; + } + + void add(bb_event_t event) { events.push_back(std::move(event)); } + + // Sort events for deterministic replay + void sort_for_replay() { std::sort(events.begin(), events.end()); } + + size_t size() const { return events.size(); } + bool empty() const { return events.empty(); } +}; + +} // namespace cuopt::linear_programming::dual_simplex + + + + + diff --git a/cpp/src/dual_simplex/bb_worker_state.hpp b/cpp/src/dual_simplex/bb_worker_state.hpp new file mode 100644 index 0000000000..e759e5bee1 --- /dev/null +++ b/cpp/src/dual_simplex/bb_worker_state.hpp @@ -0,0 +1,291 @@ +/* clang-format off */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* clang-format on */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace cuopt::linear_programming::dual_simplex { + +// Per-worker state for BSP (Bulk Synchronous Parallel) branch-and-bound +template +struct bb_worker_state_t { + int worker_id{0}; + + // Local node queue - buffer of nodes assigned to this worker for the current horizon + std::deque*> local_queue; + + // Current node being processed (may be paused at horizon boundary) + mip_node_t* current_node{nullptr}; + + // Worker's virtual time clock (cumulative work units) + double clock{0.0}; + + // Events generated during this horizon + bb_event_batch_t events; + + // Event sequence counter for deterministic tie-breaking + int event_sequence{0}; + + // LP problem copy for this worker (bounds modified per node) + std::unique_ptr> leaf_problem; + + // Basis factorization state + std::unique_ptr> basis_factors; + + // Bounds strengthening (node presolver) + std::unique_ptr> node_presolver; + + // Working vectors for basis + std::vector basic_list; + std::vector nonbasic_list; + + // Work unit context for this worker + work_limit_context_t work_context; + + // Whether basis needs recomputation for next node + bool recompute_bounds_and_basis{true}; + + // Statistics + i_t nodes_processed_this_horizon{0}; + double work_units_this_horizon{0.0}; + + // Constructor + explicit bb_worker_state_t(int id) : worker_id(id), work_context("BB_Worker_" + std::to_string(id)) + { + } + + // Initialize worker with problem data + void initialize(const lp_problem_t& original_lp, + const csr_matrix_t& Arow, + const std::vector& var_types, + i_t refactor_frequency, + bool deterministic) + { + // Create copy of LP problem for this worker + leaf_problem = std::make_unique>(original_lp); + + // Initialize basis factors + const i_t m = leaf_problem->num_rows; + basis_factors = std::make_unique>(m, refactor_frequency); + + // Initialize bounds strengthening + std::vector row_sense; + node_presolver = + std::make_unique>(*leaf_problem, Arow, row_sense, var_types); + + // Initialize working vectors + basic_list.resize(m); + nonbasic_list.clear(); + + // Configure work context + work_context.deterministic = deterministic; + } + + // Reset for new horizon + void reset_for_horizon(double horizon_start, double horizon_end) + { + // Reset clock to horizon_start for consistent VT timestamps across workers + clock = horizon_start; + events.clear(); + events.horizon_start = horizon_start; + events.horizon_end = horizon_end; + event_sequence = 0; + nodes_processed_this_horizon = 0; + work_units_this_horizon = 0.0; + // Also sync work_context to match clock for consistent tracking + work_context.global_work_units_elapsed = horizon_start; + } + + // Add a node to the local queue + void enqueue_node(mip_node_t* node) { local_queue.push_back(node); } + + // Get next node to process + mip_node_t* dequeue_node() + { + if (current_node != nullptr) { + // Resume paused node + mip_node_t* node = current_node; + current_node = nullptr; + return node; + } + if (local_queue.empty()) { return nullptr; } + mip_node_t* node = local_queue.front(); + local_queue.pop_front(); + return node; + } + + // Check if worker has work available + bool has_work() const { return current_node != nullptr || !local_queue.empty(); } + + // Get number of nodes in local queue (including paused node) + size_t queue_size() const + { + return local_queue.size() + (current_node != nullptr ? 1 : 0); + } + + // Record an event + void record_event(bb_event_t event) + { + event.event_sequence = event_sequence++; + events.add(std::move(event)); + } + + // Pause current node processing at horizon boundary + void pause_current_node(mip_node_t* node, double accumulated_vt) + { + node->accumulated_vt = accumulated_vt; + node->bsp_state = bsp_node_state_t::PAUSED; + current_node = node; + + record_event(bb_event_t::make_paused(clock, worker_id, node->node_id, 0, accumulated_vt)); + } + + // Record node branching event + void record_branched(mip_node_t* node, + i_t down_child_id, + i_t up_child_id, + i_t branch_var, + f_t branch_val) + { + record_event(bb_event_t::make_branched(clock, + worker_id, + node->node_id, + 0, + down_child_id, + up_child_id, + node->lower_bound, + branch_var, + branch_val)); + } + + // Record integer solution found + void record_integer_solution(mip_node_t* node, f_t objective) + { + record_event(bb_event_t::make_integer_solution(clock, worker_id, node->node_id, 0, objective)); + } + + // Record node fathomed + void record_fathomed(mip_node_t* node, f_t lower_bound) + { + record_event(bb_event_t::make_fathomed(clock, worker_id, node->node_id, 0, lower_bound)); + } + + // Record node infeasible + void record_infeasible(mip_node_t* node) + { + record_event(bb_event_t::make_infeasible(clock, worker_id, node->node_id, 0)); + } + + // Record numerical error + void record_numerical(mip_node_t* node) + { + record_event(bb_event_t::make_numerical(clock, worker_id, node->node_id, 0)); + } + + // Update clock with work units + void advance_clock(double work_units) + { + clock += work_units; + work_units_this_horizon += work_units; + work_context.record_work(work_units); + } +}; + +// Container for all worker states in BSP B&B +template +class bb_worker_pool_t { + public: + bb_worker_pool_t() = default; + + // Initialize pool with specified number of workers + void initialize(int num_workers, + const lp_problem_t& original_lp, + const csr_matrix_t& Arow, + const std::vector& var_types, + i_t refactor_frequency, + bool deterministic) + { + workers_.clear(); + workers_.reserve(num_workers); + for (int i = 0; i < num_workers; ++i) { + workers_.emplace_back(i); + workers_.back().initialize(original_lp, Arow, var_types, refactor_frequency, deterministic); + } + } + + // Get worker by ID + bb_worker_state_t& operator[](int worker_id) { return workers_[worker_id]; } + + const bb_worker_state_t& operator[](int worker_id) const { return workers_[worker_id]; } + + // Get number of workers + int size() const { return static_cast(workers_.size()); } + + // Reset all workers for new horizon + void reset_for_horizon(double horizon_start, double horizon_end) + { + for (auto& worker : workers_) { + worker.reset_for_horizon(horizon_start, horizon_end); + } + } + + // Collect all events from all workers into a single sorted batch + bb_event_batch_t collect_and_sort_events() + { + bb_event_batch_t all_events; + for (auto& worker : workers_) { + for (auto& event : worker.events.events) { + all_events.add(std::move(event)); + } + worker.events.clear(); + } + all_events.sort_for_replay(); + return all_events; + } + + // Check if any worker has work + bool any_has_work() const + { + for (const auto& worker : workers_) { + if (worker.has_work()) return true; + } + return false; + } + + // Get total queue size across all workers + size_t total_queue_size() const + { + size_t total = 0; + for (const auto& worker : workers_) { + total += worker.queue_size(); + } + return total; + } + + // Iterator support + auto begin() { return workers_.begin(); } + auto end() { return workers_.end(); } + auto begin() const { return workers_.begin(); } + auto end() const { return workers_.end(); } + + private: + std::vector> workers_; +}; + +} // namespace cuopt::linear_programming::dual_simplex + diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index bdb646c7d4..c19a9656d3 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -226,7 +227,8 @@ branch_and_bound_t::branch_and_bound_t( root_relax_soln_(1, 1), root_crossover_soln_(1, 1), pc_(1), - solver_status_(mip_exploration_status_t::UNSET) + solver_status_(mip_exploration_status_t::UNSET), + bsp_debug_settings_(bsp_debug_settings_t::from_environment()) { exploration_stats_.start_time = tic(); dualize_info_t dualize_info; @@ -494,6 +496,45 @@ void branch_and_bound_t::set_new_solution(const std::vector& solu } } +template +void branch_and_bound_t::set_new_solution_deterministic(const std::vector& solution, + double vt_timestamp) +{ + // In BSP mode, queue the solution to be processed at the correct virtual time + // This ensures deterministic ordering of solution events + + if (solution.size() != original_problem_.num_cols) { + settings_.log.printf( + "Solution size mismatch %ld %d\n", solution.size(), original_problem_.num_cols); + return; + } + + std::vector crushed_solution; + crush_primal_solution( + original_problem_, original_lp_, solution, new_slacks_, crushed_solution); + f_t obj = compute_objective(original_lp_, crushed_solution); + + // Validate solution before queueing + f_t primal_err; + f_t bound_err; + i_t num_fractional; + bool is_feasible = check_guess( + original_lp_, settings_, var_types_, crushed_solution, primal_err, bound_err, num_fractional); + + if (!is_feasible) { + // Queue for repair + mutex_repair_.lock(); + repair_queue_.push_back(crushed_solution); + mutex_repair_.unlock(); + return; + } + + // Queue the solution with its VT timestamp + mutex_heuristic_queue_.lock(); + heuristic_solution_queue_.push_back({std::move(crushed_solution), obj, vt_timestamp}); + mutex_heuristic_queue_.unlock(); +} + template bool branch_and_bound_t::repair_solution(const std::vector& edge_norms, const std::vector& potential_solution, @@ -693,9 +734,10 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, i_t leaf_depth, thread_type_t thread_type) { - bool send_solution = false; - i_t nodes_explored = exploration_stats_.nodes_explored; - i_t nodes_unexplored = exploration_stats_.nodes_unexplored; + bool send_solution = false; + bool improved_incumbent = false; + i_t nodes_explored = exploration_stats_.nodes_explored; + i_t nodes_unexplored = exploration_stats_.nodes_unexplored; mutex_upper_.lock(); if (leaf_objective < upper_bound_) { @@ -716,7 +758,8 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, user_mip_gap(obj, lower).c_str(), toc(exploration_stats_.start_time)); - send_solution = true; + send_solution = true; + improved_incumbent = true; } if (send_solution && settings_.solution_callback != nullptr) { @@ -725,6 +768,12 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, settings_.solution_callback(original_x, upper_bound_); } mutex_upper_.unlock(); + + // Debug: Log incumbent update (after releasing mutex to avoid potential issues) + if (improved_incumbent && bsp_debug_settings_.any_enabled() && bsp_mode_enabled_) { + std::string source = (thread_type == thread_type_t::EXPLORATION) ? "bb_integer" : "diving"; + bsp_debug_logger_.log_incumbent_update(bsp_current_horizon_, leaf_objective, source); + } } template @@ -1748,34 +1797,42 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut work_unit_context_.deterministic = settings_.deterministic; should_report_ = true; + // Choose between BSP coordinator (deterministic) and opportunistic exploration + if (settings_.deterministic && settings_.num_bfs_threads > 0) { + // Use deterministic BSP coordinator for parallel execution + settings_.log.printf("Using BSP coordinator for deterministic parallel B&B\n"); + run_bsp_coordinator(Arow); + } else { + // Use traditional opportunistic parallel exploration #pragma omp parallel num_threads(settings_.num_threads) - { - raft::common::nvtx::range scope_tree("BB::tree_exploration"); -#pragma omp master { - auto down_child = search_tree_.root.get_down_child(); - auto up_child = search_tree_.root.get_up_child(); - i_t initial_size = 2 * settings_.num_threads; + raft::common::nvtx::range scope_tree("BB::tree_exploration"); +#pragma omp master + { + auto down_child = search_tree_.root.get_down_child(); + auto up_child = search_tree_.root.get_up_child(); + i_t initial_size = 2 * settings_.num_threads; #pragma omp task - exploration_ramp_up(down_child, &search_tree_, Arow, initial_size); + exploration_ramp_up(down_child, &search_tree_, Arow, initial_size); #pragma omp task - exploration_ramp_up(up_child, &search_tree_, Arow, initial_size); - } + exploration_ramp_up(up_child, &search_tree_, Arow, initial_size); + } #pragma omp barrier #pragma omp master - { - for (i_t i = 0; i < settings_.num_bfs_threads; i++) { + { + for (i_t i = 0; i < settings_.num_bfs_threads; i++) { #pragma omp task - best_first_thread(i, search_tree_, Arow); - } + best_first_thread(i, search_tree_, Arow); + } - for (i_t i = 0; i < settings_.num_diving_threads; i++) { + for (i_t i = 0; i < settings_.num_diving_threads; i++) { #pragma omp task - diving_thread(Arow); + diving_thread(Arow); + } } } } @@ -1789,6 +1846,690 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut return set_final_solution(solution, lower_bound); } +// ============================================================================ +// BSP (Bulk Synchronous Parallel) Implementation +// ============================================================================ + +template +void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t& Arow) +{ + raft::common::nvtx::range scope("BB::bsp_coordinator"); + + const int num_workers = settings_.num_bfs_threads; + bsp_mode_enabled_ = true; + bsp_current_horizon_ = bsp_horizon_step_; + bsp_horizon_number_ = 0; + + // Initialize worker pool + bsp_workers_ = std::make_unique>(); + bsp_workers_->initialize(num_workers, + original_lp_, + Arow, + var_types_, + settings_.refactor_frequency, + settings_.deterministic); + + // Initialize debug logger + bsp_debug_logger_.set_settings(bsp_debug_settings_); + bsp_debug_logger_.set_num_workers(num_workers); + bsp_debug_logger_.set_horizon_step(bsp_horizon_step_); + + settings_.log.printf("BSP Mode: %d workers, horizon step = %.2f work units\n", + num_workers, + bsp_horizon_step_); + + // Push initial children to the global heap + // Set deterministic final_ids for root children (1 and 2) + search_tree_.root.get_down_child()->final_id = 1; + search_tree_.root.get_up_child()->final_id = 2; + bsp_next_final_id_ = 3; // Next ID to assign + + heap_.push(search_tree_.root.get_down_child()); + heap_.push(search_tree_.root.get_up_child()); + + const f_t inf = std::numeric_limits::infinity(); + f_t lower_bound = get_lower_bound(); + f_t upper_bound = get_upper_bound(); + f_t abs_gap = upper_bound - lower_bound; + f_t rel_gap = user_relative_gap(original_lp_, upper_bound, lower_bound); + + constexpr i_t target_queue_size = 5; // Target nodes per worker + + // Main BSP coordinator loop + while (solver_status_ == mip_exploration_status_t::RUNNING && + abs_gap > settings_.absolute_mip_gap_tol && rel_gap > settings_.relative_mip_gap_tol && + (heap_.size() > 0 || bsp_workers_->any_has_work())) { + + ++bsp_horizon_number_; + double horizon_start = bsp_current_horizon_ - bsp_horizon_step_; + double horizon_end = bsp_current_horizon_; + + // Debug: Log horizon start + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_horizon_start(bsp_horizon_number_, horizon_start, horizon_end); + } + + // PHASE 1: ASSIGNMENT - Fill worker queues + refill_worker_queues(target_queue_size); + + // Flush assignment trace after all assignments + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.flush_assign_trace(); + } + + // Reset workers for new horizon + bsp_workers_->reset_for_horizon(horizon_start, horizon_end); + + // PHASE 2: PARALLEL EXECUTION - Workers run until horizon +#pragma omp parallel num_threads(num_workers) + { + int worker_id = omp_get_thread_num(); + auto& worker = (*bsp_workers_)[worker_id]; + + // Run worker until horizon + run_worker_until_horizon(worker, search_tree_, bsp_current_horizon_); + } + + // PHASE 3: SYNCHRONIZATION - The Barrier + // Collect and sort all events deterministically + bb_event_batch_t all_events = bsp_workers_->collect_and_sort_events(); + + // Debug: Log sync phase + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_sync_phase_start(horizon_end, all_events.size()); + } + + // Process history and sync + process_history_and_sync(all_events); + + // Debug: Flush final IDs trace and log sync end + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.flush_final_ids_trace(); + bsp_debug_logger_.log_sync_phase_end(horizon_end); + } + + // Prune paused nodes that are now dominated by new incumbent + prune_worker_nodes_vs_incumbent(); + + // Debug: Log horizon end, emit tree state and JSON state + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_horizon_end(bsp_horizon_number_, horizon_end); + + // Emit tree state (DOT format) + bsp_debug_logger_.emit_tree_state(bsp_horizon_number_, search_tree_.root, get_upper_bound()); + + // Collect heap nodes for JSON state + std::vector*> heap_snapshot; + // Note: We can't easily iterate the heap, so just log the size + bsp_debug_logger_.emit_state_json(bsp_horizon_number_, horizon_start, horizon_end, + bsp_next_final_id_, get_upper_bound(), get_lower_bound(), + exploration_stats_.nodes_explored, + exploration_stats_.nodes_unexplored, *bsp_workers_, + heap_snapshot, all_events); + } + + // Advance the horizon + bsp_current_horizon_ += bsp_horizon_step_; + + // Update gap info + lower_bound = get_lower_bound(); + upper_bound = get_upper_bound(); + abs_gap = upper_bound - lower_bound; + rel_gap = user_relative_gap(original_lp_, upper_bound, lower_bound); + + // Check time/work limits + if (toc(exploration_stats_.start_time) > settings_.time_limit) { + solver_status_ = mip_exploration_status_t::TIME_LIMIT; + } + if (settings_.deterministic && + work_unit_context_.global_work_units_elapsed >= settings_.work_limit) { + solver_status_ = mip_exploration_status_t::WORK_LIMIT; + } + + // Progress logging + f_t obj = compute_user_objective(original_lp_, upper_bound); + f_t user_lower = compute_user_objective(original_lp_, lower_bound); + std::string gap_user = user_mip_gap(obj, user_lower); + i_t nodes_explored = exploration_stats_.nodes_explored; + i_t nodes_unexplored = exploration_stats_.nodes_unexplored; + + settings_.log.printf(" %10d %10lu %+13.6e %+10.6e %s %9.2f\n", + nodes_explored, + nodes_unexplored, + obj, + user_lower, + gap_user.c_str(), + toc(exploration_stats_.start_time)); + } + + // Finalize debug logger + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.finalize(); + } + + // Mark completed if we finished exploring + if (solver_status_ == mip_exploration_status_t::RUNNING) { + solver_status_ = mip_exploration_status_t::COMPLETED; + } +} + +template +void branch_and_bound_t::refill_worker_queues(i_t target_queue_size) +{ + // Distribute nodes from global pool to workers in round-robin fashion + // This ensures deterministic assignment based on node ordering in the heap + + std::vector*> nodes_to_assign; + + // Pop nodes from heap while respecting incumbent bound + mutex_heap_.lock(); + while (!heap_.empty()) { + mip_node_t* node = heap_.top(); + + // Skip pruned nodes + if (node->lower_bound >= get_upper_bound()) { + heap_.pop(); + search_tree_.update(node, node_status_t::FATHOMED); + --exploration_stats_.nodes_unexplored; + continue; + } + + // Check if we have enough nodes + if (nodes_to_assign.size() >= static_cast(target_queue_size * bsp_workers_->size())) { + break; + } + + heap_.pop(); + nodes_to_assign.push_back(node); + } + mutex_heap_.unlock(); + + // Assign nodes to workers deterministically (round-robin by deterministic ID order) + // Use get_deterministic_id() which returns final_id if set, else provisional_id, else node_id + std::sort(nodes_to_assign.begin(), nodes_to_assign.end(), + [](const mip_node_t* a, const mip_node_t* b) { + return a->get_deterministic_id() < b->get_deterministic_id(); + }); + + for (size_t i = 0; i < nodes_to_assign.size(); ++i) { + int worker_id = i % bsp_workers_->size(); + auto* node = nodes_to_assign[i]; + (*bsp_workers_)[worker_id].enqueue_node(node); + + // Debug: Log node assignment + if (bsp_debug_settings_.any_enabled()) { + double vt = bsp_current_horizon_ - bsp_horizon_step_; // Start of current horizon + bsp_debug_logger_.log_node_assigned(vt, worker_id, node->node_id, node->final_id, + node->lower_bound); + } + } +} + +template +void branch_and_bound_t::run_worker_until_horizon(bb_worker_state_t& worker, + search_tree_t& search_tree, + double current_horizon) +{ + raft::common::nvtx::range scope("BB::worker_run"); + + while (worker.clock < current_horizon && worker.has_work() && + solver_status_ == mip_exploration_status_t::RUNNING) { + + mip_node_t* node = worker.dequeue_node(); + if (node == nullptr) break; + + // Check if node should be pruned + f_t upper_bound = get_upper_bound(); + if (node->lower_bound >= upper_bound) { + worker.record_fathomed(node, node->lower_bound); + search_tree.update(node, node_status_t::FATHOMED); + --exploration_stats_.nodes_unexplored; + continue; + } + + // Solve the node (this records events) + node_solve_info_t status = solve_node_bsp(worker, node, search_tree, current_horizon); + + // Handle result + if (status == node_solve_info_t::TIME_LIMIT) { + solver_status_ = mip_exploration_status_t::TIME_LIMIT; + break; + } else if (status == node_solve_info_t::WORK_LIMIT) { + // Node paused at horizon - already handled in solve_node_bsp + break; + } + } +} + +template +node_solve_info_t branch_and_bound_t::solve_node_bsp( + bb_worker_state_t& worker, + mip_node_t* node_ptr, + search_tree_t& search_tree, + double current_horizon) +{ + raft::common::nvtx::range scope("BB::solve_node_bsp"); + + // Track work units at start (from work_context, which simplex solver updates) + double work_units_at_start = worker.work_context.global_work_units_elapsed; + double clock_at_start = worker.clock; + bool is_resumed = (node_ptr->bsp_state == bsp_node_state_t::PAUSED); + + // Debug: Log solve start + if (bsp_debug_settings_.any_enabled()) { + double work_limit = current_horizon - worker.clock; + bsp_debug_logger_.log_solve_start(worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->final_id, work_limit, is_resumed); + } + + // Setup leaf problem bounds + std::fill(worker.node_presolver->bounds_changed.begin(), + worker.node_presolver->bounds_changed.end(), + false); + + if (worker.recompute_bounds_and_basis) { + worker.leaf_problem->lower = original_lp_.lower; + worker.leaf_problem->upper = original_lp_.upper; + node_ptr->get_variable_bounds(worker.leaf_problem->lower, + worker.leaf_problem->upper, + worker.node_presolver->bounds_changed); + } else { + node_ptr->update_branched_variable_bounds(worker.leaf_problem->lower, + worker.leaf_problem->upper, + worker.node_presolver->bounds_changed); + } + + // Bounds strengthening + simplex_solver_settings_t lp_settings = settings_; + lp_settings.set_log(false); + lp_settings.cut_off = get_upper_bound() + settings_.dual_tol; + lp_settings.inside_mip = 2; + lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time); + // Work limit is the remaining VT budget in this horizon + // Note: accumulated_vt tracks work already spent (for statistics), but doesn't increase budget + lp_settings.work_limit = current_horizon - worker.clock; + lp_settings.scale_columns = false; + + bool feasible = worker.node_presolver->bounds_strengthening( + worker.leaf_problem->lower, worker.leaf_problem->upper, lp_settings); + + if (!feasible) { + node_ptr->lower_bound = std::numeric_limits::infinity(); + search_tree.update(node_ptr, node_status_t::INFEASIBLE); + worker.record_infeasible(node_ptr); + --exploration_stats_.nodes_unexplored; + ++exploration_stats_.nodes_explored; + worker.recompute_bounds_and_basis = true; + return node_solve_info_t::NO_CHILDREN; + } + + // Solve LP relaxation + lp_solution_t leaf_solution(worker.leaf_problem->num_rows, worker.leaf_problem->num_cols); + std::vector& leaf_vstatus = node_ptr->vstatus; + i_t node_iter = 0; + f_t lp_start_time = tic(); + std::vector leaf_edge_norms = edge_norms_; + + dual::status_t lp_status = dual_phase2_with_advanced_basis( + 2, + 0, + worker.recompute_bounds_and_basis, + lp_start_time, + *worker.leaf_problem, + lp_settings, + leaf_vstatus, + *worker.basis_factors, + worker.basic_list, + worker.nonbasic_list, + leaf_solution, + node_iter, + leaf_edge_norms, + &worker.work_context); + + // Update worker clock with work performed + // The simplex solver recorded work to work_context.global_work_units_elapsed + // Compute the delta and advance the worker clock (but don't double-record to work_context) + double work_performed = worker.work_context.global_work_units_elapsed - work_units_at_start; + worker.clock += work_performed; + worker.work_units_this_horizon += work_performed; + // Note: don't call advance_clock() as work_context was already updated by simplex solver + + // Check if we hit the horizon mid-solve + if (lp_status == dual::status_t::WORK_LIMIT || worker.clock >= current_horizon) { + // Pause this node - accumulated_vt is the total work spent on this node + double accumulated_vt = (worker.clock - clock_at_start) + node_ptr->accumulated_vt; + worker.pause_current_node(node_ptr, accumulated_vt); + + // Debug: Log solve end (paused) + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->final_id, "PAUSED", node_ptr->lower_bound); + bsp_debug_logger_.log_paused(worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->final_id, static_cast(accumulated_vt)); + } + return node_solve_info_t::WORK_LIMIT; + } + + exploration_stats_.total_lp_solve_time += toc(lp_start_time); + exploration_stats_.total_lp_iters += node_iter; + ++exploration_stats_.nodes_explored; + --exploration_stats_.nodes_unexplored; + + // Process LP result + if (lp_status == dual::status_t::DUAL_UNBOUNDED) { + node_ptr->lower_bound = std::numeric_limits::infinity(); + search_tree.update(node_ptr, node_status_t::INFEASIBLE); + worker.record_infeasible(node_ptr); + worker.recompute_bounds_and_basis = true; + + // Debug: Log solve end (infeasible) + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->final_id, "INFEASIBLE", node_ptr->lower_bound); + bsp_debug_logger_.log_infeasible(worker.clock, worker.worker_id, node_ptr->node_id); + } + return node_solve_info_t::NO_CHILDREN; + + } else if (lp_status == dual::status_t::CUTOFF) { + node_ptr->lower_bound = get_upper_bound(); + search_tree.update(node_ptr, node_status_t::FATHOMED); + worker.record_fathomed(node_ptr, node_ptr->lower_bound); + worker.recompute_bounds_and_basis = true; + + // Debug: Log solve end (fathomed - cutoff) + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->final_id, "FATHOMED", node_ptr->lower_bound); + bsp_debug_logger_.log_fathomed(worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->lower_bound); + } + return node_solve_info_t::NO_CHILDREN; + + } else if (lp_status == dual::status_t::OPTIMAL) { + std::vector leaf_fractional; + i_t leaf_num_fractional = + fractional_variables(settings_, leaf_solution.x, var_types_, leaf_fractional); + + f_t leaf_objective = compute_objective(*worker.leaf_problem, leaf_solution.x); + node_ptr->lower_bound = leaf_objective; + pc_.update_pseudo_costs(node_ptr, leaf_objective); + + if (leaf_num_fractional == 0) { + // Integer feasible + add_feasible_solution(leaf_objective, leaf_solution.x, node_ptr->depth, thread_type_t::EXPLORATION); + search_tree.update(node_ptr, node_status_t::INTEGER_FEASIBLE); + worker.record_integer_solution(node_ptr, leaf_objective); + worker.recompute_bounds_and_basis = true; + + // Debug: Log solve end (integer) + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->final_id, "INTEGER", leaf_objective); + bsp_debug_logger_.log_integer(worker.clock, worker.worker_id, node_ptr->node_id, + leaf_objective); + } + return node_solve_info_t::NO_CHILDREN; + + } else if (leaf_objective <= get_upper_bound() + settings_.absolute_mip_gap_tol / 10) { + // Branch + logger_t log; + log.log = false; + const i_t branch_var = pc_.variable_selection(leaf_fractional, leaf_solution.x, log); + + search_tree.branch(node_ptr, branch_var, leaf_solution.x[branch_var], leaf_vstatus, *worker.leaf_problem, log); + search_tree.update(node_ptr, node_status_t::HAS_CHILDREN); + + i_t down_child_id = node_ptr->get_down_child()->node_id; + i_t up_child_id = node_ptr->get_up_child()->node_id; + worker.record_branched(node_ptr, down_child_id, up_child_id, branch_var, leaf_solution.x[branch_var]); + + // Debug: Log solve end (branched) and branched event + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->final_id, "BRANCH", leaf_objective); + bsp_debug_logger_.log_branched(worker.clock, worker.worker_id, node_ptr->node_id, + down_child_id, up_child_id); + } + + exploration_stats_.nodes_unexplored += 2; + worker.recompute_bounds_and_basis = false; + + // Add children to worker's local queue (deterministic order: down first) + worker.enqueue_node(node_ptr->get_down_child()); + worker.enqueue_node(node_ptr->get_up_child()); + + return rounding_direction_t::DOWN == child_selection(node_ptr) + ? node_solve_info_t::DOWN_CHILD_FIRST + : node_solve_info_t::UP_CHILD_FIRST; + + } else { + search_tree.update(node_ptr, node_status_t::FATHOMED); + worker.record_fathomed(node_ptr, leaf_objective); + worker.recompute_bounds_and_basis = true; + + // Debug: Log solve end (fathomed by bound) + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->final_id, "FATHOMED", leaf_objective); + bsp_debug_logger_.log_fathomed(worker.clock, worker.worker_id, node_ptr->node_id, + leaf_objective); + } + return node_solve_info_t::NO_CHILDREN; + } + + } else if (lp_status == dual::status_t::TIME_LIMIT) { + return node_solve_info_t::TIME_LIMIT; + + } else { + // Numerical issue + search_tree.update(node_ptr, node_status_t::NUMERICAL); + worker.record_numerical(node_ptr); + worker.recompute_bounds_and_basis = true; + return node_solve_info_t::NUMERICAL; + } +} + +template +void branch_and_bound_t::process_history_and_sync(const bb_event_batch_t& events) +{ + // Collect queued heuristic solutions + std::vector heuristic_solutions; + mutex_heuristic_queue_.lock(); + heuristic_solutions = std::move(heuristic_solution_queue_); + heuristic_solution_queue_.clear(); + mutex_heuristic_queue_.unlock(); + + // Sort heuristic solutions by VT + std::sort(heuristic_solutions.begin(), heuristic_solutions.end(), + [](const queued_heuristic_solution_t& a, const queued_heuristic_solution_t& b) { + return a.vt_timestamp < b.vt_timestamp; + }); + + // Build mapping from provisional_id -> final_id for deterministic node ordering + // This is populated during event processing and applied to nodes afterward + std::unordered_map provisional_to_final_id; + + // Merge B&B events and heuristic solutions for unified timeline replay + // Both are sorted by VT, so we can do a merge-style iteration + size_t event_idx = 0; + size_t heuristic_idx = 0; + + while (event_idx < events.events.size() || heuristic_idx < heuristic_solutions.size()) { + bool process_event = false; + bool process_heuristic = false; + + if (event_idx >= events.events.size()) { + process_heuristic = true; + } else if (heuristic_idx >= heuristic_solutions.size()) { + process_event = true; + } else { + // Both have items - pick the one with smaller VT + if (events.events[event_idx].vt_timestamp <= heuristic_solutions[heuristic_idx].vt_timestamp) { + process_event = true; + } else { + process_heuristic = true; + } + } + + if (process_event) { + const auto& event = events.events[event_idx++]; + switch (event.type) { + case bb_event_type_t::NODE_INTEGER: { + // Check if this solution beats the incumbent KNOWN AT THAT VIRTUAL TIME + f_t obj = event.payload.integer_solution.objective_value; + mutex_upper_.lock(); + if (obj < upper_bound_) { + // Note: The actual solution was already stored during solve_node_bsp + // Here we just acknowledge the event order + } + mutex_upper_.unlock(); + break; + } + + case bb_event_type_t::NODE_BRANCHED: { + // Assign deterministic final IDs to the children + // Events are processed in sorted (deterministic) order, so this assignment is deterministic + i_t down_provisional = event.payload.branched.down_child_id; + i_t up_provisional = event.payload.branched.up_child_id; + + i_t down_final_id = bsp_next_final_id_++; + i_t up_final_id = bsp_next_final_id_++; + provisional_to_final_id[down_provisional] = down_final_id; + provisional_to_final_id[up_provisional] = up_final_id; + + // Debug: Log final ID assignments + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_final_id_assigned(down_provisional, down_final_id); + bsp_debug_logger_.log_final_id_assigned(up_provisional, up_final_id); + } + break; + } + + case bb_event_type_t::NODE_FATHOMED: + case bb_event_type_t::NODE_INFEASIBLE: + case bb_event_type_t::NODE_NUMERICAL: + case bb_event_type_t::NODE_PAUSED: + case bb_event_type_t::HEURISTIC_SOLUTION: + // These events don't need additional processing during replay + break; + } + } + + if (process_heuristic) { + const auto& hsol = heuristic_solutions[heuristic_idx++]; + + // Debug: Log heuristic received + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_heuristic_received(hsol.vt_timestamp, hsol.objective); + } + + // Process heuristic solution at its correct VT position + // FIX: Release mutex before calling get_lower_bound() to avoid deadlock + f_t new_upper = std::numeric_limits::infinity(); + + mutex_upper_.lock(); + if (hsol.objective < upper_bound_) { + upper_bound_ = hsol.objective; + incumbent_.set_incumbent_solution(hsol.objective, hsol.solution); + new_upper = hsol.objective; + + // Debug: Log incumbent update + if (bsp_debug_settings_.any_enabled()) { + bsp_debug_logger_.log_incumbent_update(hsol.vt_timestamp, hsol.objective, "heuristic"); + } + } + mutex_upper_.unlock(); + + // Log after releasing mutex to avoid holding mutex while calling get_lower_bound() + if (new_upper < std::numeric_limits::infinity()) { + f_t user_obj = compute_user_objective(original_lp_, new_upper); + f_t user_lower = compute_user_objective(original_lp_, get_lower_bound()); + std::string gap = user_mip_gap(user_obj, user_lower); + + settings_.log.printf( + "H %+13.6e %+10.6e %s %9.2f\n", + user_obj, + user_lower, + gap.c_str(), + toc(exploration_stats_.start_time)); + } + } + } + + // Apply final IDs to all nodes in worker queues and global heap + // Helper lambda to apply final_id mapping to a node + auto apply_final_id = [&provisional_to_final_id](mip_node_t* node) { + if (node->final_id < 0 && node->node_id > 0) { + // Use node_id as provisional_id (they are the same during BSP) + auto it = provisional_to_final_id.find(node->node_id); + if (it != provisional_to_final_id.end()) { + node->final_id = it->second; + } + } + }; + + // Apply to worker local queues + for (auto& worker : *bsp_workers_) { + for (auto* node : worker.local_queue) { + apply_final_id(node); + } + if (worker.current_node != nullptr) { + apply_final_id(worker.current_node); + } + } + + // Get current upper bound for pruning decisions + f_t upper_bound = get_upper_bound(); + + // Move ALL nodes from worker local queues to global heap for redistribution + // This ensures proper work distribution across workers each horizon + mutex_heap_.lock(); + for (auto& worker : *bsp_workers_) { + while (!worker.local_queue.empty()) { + mip_node_t* node = worker.local_queue.front(); + worker.local_queue.pop_front(); + // Only push non-pruned nodes + if (node->lower_bound < upper_bound) { + heap_.push(node); + } else { + search_tree_.update(node, node_status_t::FATHOMED); + --exploration_stats_.nodes_unexplored; + } + } + } + mutex_heap_.unlock(); +} + +template +void branch_and_bound_t::prune_worker_nodes_vs_incumbent() +{ + f_t upper_bound = get_upper_bound(); + + for (auto& worker : *bsp_workers_) { + // Check paused node + if (worker.current_node != nullptr) { + if (worker.current_node->lower_bound >= upper_bound) { + // Prune the paused node + search_tree_.update(worker.current_node, node_status_t::FATHOMED); + --exploration_stats_.nodes_unexplored; + worker.current_node = nullptr; + } + } + + // Check nodes in local queue + auto it = worker.local_queue.begin(); + while (it != worker.local_queue.end()) { + if ((*it)->lower_bound >= upper_bound) { + search_tree_.update(*it, node_status_t::FATHOMED); + --exploration_stats_.nodes_unexplored; + it = worker.local_queue.erase(it); + } else { + ++it; + } + } + } +} + #ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE template class branch_and_bound_t; diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index 86b2c80a96..b021e029fa 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -7,6 +7,9 @@ #pragma once +#include +#include +#include #include #include #include @@ -152,6 +155,10 @@ class branch_and_bound_t { // Set a solution based on the user problem during the course of the solve void set_new_solution(const std::vector& solution); + // BSP-aware solution injection for deterministic mode + // This queues the solution to be processed at the correct virtual time + void set_new_solution_deterministic(const std::vector& solution, double vt_timestamp); + void set_concurrent_lp_root_solve(bool enable) { enable_concurrent_lp_root_solve_ = enable; } // Repair a low-quality solution from the heuristics. @@ -328,6 +335,63 @@ class branch_and_bound_t { // Sort the children based on the Martin's criteria. rounding_direction_t child_selection(mip_node_t* node_ptr); + + // ============================================================================ + // BSP (Bulk Synchronous Parallel) methods for deterministic parallel B&B + // ============================================================================ + + // Main BSP coordinator loop - runs in deterministic mode + void run_bsp_coordinator(const csr_matrix_t& Arow); + + // Refill worker queues from the global pool + void refill_worker_queues(i_t target_queue_size); + + // Run a single worker until it reaches the horizon + void run_worker_until_horizon(bb_worker_state_t& worker, + search_tree_t& search_tree, + double current_horizon); + + // Process history and synchronize - the "brain" of BSP + void process_history_and_sync(const bb_event_batch_t& events); + + // Prune nodes held by workers based on new incumbent + void prune_worker_nodes_vs_incumbent(); + + // BSP-specific node solving that records events + node_solve_info_t solve_node_bsp(bb_worker_state_t& worker, + mip_node_t* node_ptr, + search_tree_t& search_tree, + double current_horizon); + + private: + // BSP state + std::unique_ptr> bsp_workers_; + double bsp_horizon_step_{5.0}; // Virtual time step per horizon (tunable) + double bsp_current_horizon_{0.0}; // Current horizon target + bool bsp_mode_enabled_{false}; // Whether BSP mode is active + int bsp_horizon_number_{0}; // Current horizon number (for debugging) + + // Counter for deterministic final_id assignment during sync phase + // Starts at 2 (root's children are 1 and 2), incremented by 2 for each branch + i_t bsp_next_final_id_{3}; + + // BSP heuristic solution queue - solutions received from GPU heuristics + // Stored with VT timestamp for deterministic ordering + struct queued_heuristic_solution_t { + std::vector solution; + f_t objective; + double vt_timestamp; + }; + omp_mutex_t mutex_heuristic_queue_; + std::vector heuristic_solution_queue_; + + // BSP debug logger (settings read from environment variables) + bsp_debug_settings_t bsp_debug_settings_; + bsp_debug_logger_t bsp_debug_logger_; + + public: + // Accessor for GPU heuristics to get the current BSP horizon + double get_current_bsp_horizon() const { return bsp_current_horizon_; } }; } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/bsp_debug.hpp b/cpp/src/dual_simplex/bsp_debug.hpp new file mode 100644 index 0000000000..1b3833b06c --- /dev/null +++ b/cpp/src/dual_simplex/bsp_debug.hpp @@ -0,0 +1,843 @@ +/* clang-format off */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* clang-format on */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cuopt::linear_programming::dual_simplex { + +// ============================================================================ +// BSP Debug Event Types +// ============================================================================ + +enum class bsp_log_event_t { + HORIZON_START, // New horizon begins + HORIZON_END, // Horizon completed + NODE_ASSIGNED, // Node assigned to worker + NODE_SOLVE_START, // Worker starts solving node + NODE_SOLVE_END, // Worker finishes node (with result type) + NODE_PAUSED, // Node paused at horizon + NODE_RESUMED, // Paused node resumed + NODE_BRANCHED, // Node branched into children + NODE_PRUNED, // Node pruned (cutoff) + NODE_INTEGER, // Integer solution found + NODE_INFEASIBLE, // Node infeasible + NODE_FATHOMED, // Node fathomed + FINAL_ID_ASSIGNED, // Final ID assigned during sync + HEURISTIC_RECEIVED, // Heuristic solution received + INCUMBENT_UPDATE, // New best solution found + SYNC_PHASE_START, // Sync phase begins + SYNC_PHASE_END, // Sync phase ends + WORKER_IDLE, // Worker has no work +}; + +inline const char* bsp_log_event_name(bsp_log_event_t event) +{ + switch (event) { + case bsp_log_event_t::HORIZON_START: return "HORIZON_START"; + case bsp_log_event_t::HORIZON_END: return "HORIZON_END"; + case bsp_log_event_t::NODE_ASSIGNED: return "NODE_ASSIGNED"; + case bsp_log_event_t::NODE_SOLVE_START: return "NODE_SOLVE_START"; + case bsp_log_event_t::NODE_SOLVE_END: return "NODE_SOLVE_END"; + case bsp_log_event_t::NODE_PAUSED: return "NODE_PAUSED"; + case bsp_log_event_t::NODE_RESUMED: return "NODE_RESUMED"; + case bsp_log_event_t::NODE_BRANCHED: return "NODE_BRANCHED"; + case bsp_log_event_t::NODE_PRUNED: return "NODE_PRUNED"; + case bsp_log_event_t::NODE_INTEGER: return "NODE_INTEGER"; + case bsp_log_event_t::NODE_INFEASIBLE: return "NODE_INFEASIBLE"; + case bsp_log_event_t::NODE_FATHOMED: return "NODE_FATHOMED"; + case bsp_log_event_t::FINAL_ID_ASSIGNED: return "FINAL_ID_ASSIGNED"; + case bsp_log_event_t::HEURISTIC_RECEIVED: return "HEURISTIC_RECEIVED"; + case bsp_log_event_t::INCUMBENT_UPDATE: return "INCUMBENT_UPDATE"; + case bsp_log_event_t::SYNC_PHASE_START: return "SYNC_PHASE_START"; + case bsp_log_event_t::SYNC_PHASE_END: return "SYNC_PHASE_END"; + case bsp_log_event_t::WORKER_IDLE: return "WORKER_IDLE"; + default: return "UNKNOWN"; + } +} + +// ============================================================================ +// BSP Debug Settings +// ============================================================================ + +/** + * @brief BSP debug settings controlled via environment variables. + * + * Environment variables: + * CUOPT_BSP_DEBUG_LOG=1 Enable structured event log + * CUOPT_BSP_DEBUG_TIMELINE=1 Emit ASCII timeline visualization + * CUOPT_BSP_DEBUG_TREE=1 Emit DOT tree state files + * CUOPT_BSP_DEBUG_JSON=1 Emit JSON state dumps + * CUOPT_BSP_DEBUG_TRACE=1 Emit determinism trace file + * CUOPT_BSP_DEBUG_ALL=1 Enable all debug output + * CUOPT_BSP_DEBUG_DIR=path Output directory (default: ./bsp_debug/) + * CUOPT_BSP_DEBUG_LEVEL=N Log level: 0=off, 1=major events, 2=all events + */ +struct bsp_debug_settings_t { + bool enable_event_log{false}; + bool enable_timeline{false}; + bool enable_tree_dot{false}; + bool enable_state_json{false}; + bool enable_determinism_trace{false}; + std::string output_dir{"./bsp_debug/"}; + int log_level{1}; // 0=off, 1=major events, 2=all events + + bool any_enabled() const + { + return enable_event_log || enable_timeline || enable_tree_dot || enable_state_json || + enable_determinism_trace; + } + + void enable_all() + { + enable_event_log = true; + enable_timeline = true; + enable_tree_dot = true; + enable_state_json = true; + enable_determinism_trace = true; + } + + /** + * @brief Initialize settings from environment variables. + * @return A bsp_debug_settings_t populated from environment variables. + */ + static bsp_debug_settings_t from_environment() + { + bsp_debug_settings_t settings; + + auto get_env_bool = [](const char* name) -> bool { + const char* val = std::getenv(name); + if (val == nullptr) return false; + return std::string(val) == "1" || std::string(val) == "true" || std::string(val) == "TRUE"; + }; + + auto get_env_int = [](const char* name, int default_val) -> int { + const char* val = std::getenv(name); + if (val == nullptr) return default_val; + try { + return std::stoi(val); + } catch (...) { + return default_val; + } + }; + + auto get_env_string = [](const char* name, const std::string& default_val) -> std::string { + const char* val = std::getenv(name); + if (val == nullptr) return default_val; + return std::string(val); + }; + + // Check for CUOPT_BSP_DEBUG_ALL first + if (get_env_bool("CUOPT_BSP_DEBUG_ALL")) { + settings.enable_all(); + } else { + settings.enable_event_log = get_env_bool("CUOPT_BSP_DEBUG_LOG"); + settings.enable_timeline = get_env_bool("CUOPT_BSP_DEBUG_TIMELINE"); + settings.enable_tree_dot = get_env_bool("CUOPT_BSP_DEBUG_TREE"); + settings.enable_state_json = get_env_bool("CUOPT_BSP_DEBUG_JSON"); + settings.enable_determinism_trace = get_env_bool("CUOPT_BSP_DEBUG_TRACE"); + } + + settings.output_dir = get_env_string("CUOPT_BSP_DEBUG_DIR", "./bsp_debug/"); + settings.log_level = get_env_int("CUOPT_BSP_DEBUG_LEVEL", 1); + + return settings; + } +}; + +// ============================================================================ +// Timeline Event (for ASCII visualization) +// ============================================================================ + +struct timeline_event_t { + double vt_start; + double vt_end; + int worker_id; + int node_id; + int final_id; + std::string result; // "BRANCH", "INTEGER", "FATHOMED", "PAUSED", etc. +}; + +// ============================================================================ +// BSP Debug Logger Class +// ============================================================================ + +template +class bsp_debug_logger_t { + public: + bsp_debug_logger_t() : start_time_(std::chrono::steady_clock::now()) {} + + void set_settings(const bsp_debug_settings_t& settings) + { + settings_ = settings; + if (settings_.any_enabled()) { + // Create output directory if it doesn't exist + try { + std::filesystem::create_directories(settings_.output_dir); + } catch (const std::filesystem::filesystem_error& e) { + // Silently disable debug output if we can't create the directory + settings_.enable_event_log = false; + settings_.enable_timeline = false; + settings_.enable_tree_dot = false; + settings_.enable_state_json = false; + settings_.enable_determinism_trace = false; + } + + // Clear timeline file at start (it uses append mode for each horizon) + if (settings_.enable_timeline) { + std::string filename = settings_.output_dir + "bsp_timeline.txt"; + std::ofstream file(filename, std::ios::trunc); + if (file.is_open()) { + file << "BSP Timeline Visualization\n"; + file << "==========================\n"; + file.close(); + } + } + } + } + + void set_num_workers(int num_workers) { num_workers_ = num_workers; } + + void set_horizon_step(double step) { horizon_step_ = step; } + + // ======================================================================== + // Event Logging + // ======================================================================== + + void log_horizon_start(int horizon_num, double vt_start, double vt_end) + { + current_horizon_ = horizon_num; + horizon_vt_start_ = vt_start; + horizon_vt_end_ = vt_end; + + if (settings_.enable_event_log) { + log_event(vt_start, -1, bsp_log_event_t::HORIZON_START, -1, -1, + "horizon=[" + std::to_string(vt_start) + "," + std::to_string(vt_end) + ")"); + } + + if (settings_.enable_determinism_trace) { + trace_ss_ << "H" << horizon_num << ":START:vt=[" << vt_start << "," << vt_end << ")\n"; + } + + // Clear timeline events for new horizon + timeline_events_.clear(); + } + + void log_horizon_end(int horizon_num, double vt) + { + if (settings_.enable_event_log) { + log_event(vt, -1, bsp_log_event_t::HORIZON_END, -1, -1, ""); + } + + if (settings_.enable_timeline) { + emit_timeline_for_horizon(horizon_num); + } + } + + void log_node_assigned(double vt, int worker_id, i_t node_id, i_t final_id, f_t lower_bound) + { + if (settings_.enable_event_log) { + log_event(vt, worker_id, bsp_log_event_t::NODE_ASSIGNED, node_id, final_id, + "lb=" + std::to_string(lower_bound)); + } + + if (settings_.enable_determinism_trace) { + assign_trace_ss_ << "W" << worker_id << "=" << final_id << ","; + } + } + + void flush_assign_trace() + { + if (settings_.enable_determinism_trace && assign_trace_ss_.str().length() > 0) { + std::string s = assign_trace_ss_.str(); + if (!s.empty() && s.back() == ',') s.pop_back(); + trace_ss_ << "H" << current_horizon_ << ":ASSIGN:" << s << "\n"; + assign_trace_ss_.str(""); + assign_trace_ss_.clear(); + } + } + + void log_solve_start(double vt, int worker_id, i_t node_id, i_t final_id, double work_limit, + bool is_resumed) + { + std::lock_guard lock(mutex_); + + if (settings_.enable_event_log && settings_.log_level >= 2) { + log_event_unlocked(vt, worker_id, + is_resumed ? bsp_log_event_t::NODE_RESUMED : bsp_log_event_t::NODE_SOLVE_START, + node_id, final_id, "work_limit=" + std::to_string(work_limit)); + } + + // Start timeline event + current_solve_start_[worker_id] = vt; + current_solve_node_[worker_id] = node_id; + current_solve_fid_[worker_id] = final_id; + } + + void log_solve_end(double vt, int worker_id, i_t node_id, i_t final_id, const std::string& result, + f_t lower_bound) + { + std::lock_guard lock(mutex_); + + if (settings_.enable_event_log) { + log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_SOLVE_END, node_id, final_id, + "result=" + result + ",lb=" + std::to_string(lower_bound)); + } + + // Complete timeline event + if (settings_.enable_timeline) { + timeline_event_t te; + te.vt_start = current_solve_start_[worker_id]; + te.vt_end = vt; + te.worker_id = worker_id; + te.node_id = node_id; + te.final_id = final_id; + te.result = result; + timeline_events_.push_back(te); + } + } + + void log_branched(double vt, int worker_id, i_t parent_id, i_t down_id, i_t up_id) + { + std::lock_guard lock(mutex_); + + if (settings_.enable_event_log) { + log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_BRANCHED, parent_id, -1, + "children=" + std::to_string(down_id) + "," + std::to_string(up_id)); + } + + if (settings_.enable_determinism_trace) { + events_trace_ss_ << "BRANCH(" << vt << ",W" << worker_id << "," << parent_id << "->" + << down_id << "," << up_id << "),"; + } + } + + void log_paused(double vt, int worker_id, i_t node_id, i_t final_id, f_t accumulated_vt) + { + std::lock_guard lock(mutex_); + + if (settings_.enable_event_log) { + log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_PAUSED, node_id, final_id, + "acc_vt=" + std::to_string(accumulated_vt)); + } + + if (settings_.enable_determinism_trace) { + events_trace_ss_ << "PAUSE(" << vt << ",W" << worker_id << "," << node_id << "),"; + } + } + + void log_integer(double vt, int worker_id, i_t node_id, f_t objective) + { + std::lock_guard lock(mutex_); + + if (settings_.enable_event_log) { + log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_INTEGER, node_id, -1, + "obj=" + std::to_string(objective)); + } + + if (settings_.enable_determinism_trace) { + events_trace_ss_ << "INT(" << vt << ",W" << worker_id << "," << objective << "),"; + } + } + + void log_fathomed(double vt, int worker_id, i_t node_id, f_t lower_bound) + { + std::lock_guard lock(mutex_); + + if (settings_.enable_event_log && settings_.log_level >= 2) { + log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_FATHOMED, node_id, -1, + "lb=" + std::to_string(lower_bound)); + } + } + + void log_infeasible(double vt, int worker_id, i_t node_id) + { + std::lock_guard lock(mutex_); + + if (settings_.enable_event_log && settings_.log_level >= 2) { + log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_INFEASIBLE, node_id, -1, ""); + } + } + + void log_pruned(double vt, i_t node_id, i_t final_id, f_t lower_bound, f_t upper_bound) + { + std::lock_guard lock(mutex_); + + if (settings_.enable_event_log && settings_.log_level >= 2) { + log_event_unlocked(vt, -1, bsp_log_event_t::NODE_PRUNED, node_id, final_id, + "lb=" + std::to_string(lower_bound) + ",ub=" + std::to_string(upper_bound)); + } + } + + void log_sync_phase_start(double vt, size_t num_events) + { + if (settings_.enable_event_log) { + log_event(vt, -1, bsp_log_event_t::SYNC_PHASE_START, -1, -1, + "num_events=" + std::to_string(num_events)); + } + } + + void log_sync_phase_end(double vt) + { + if (settings_.enable_event_log) { + log_event(vt, -1, bsp_log_event_t::SYNC_PHASE_END, -1, -1, ""); + } + + // Flush event trace + if (settings_.enable_determinism_trace) { + std::string s = events_trace_ss_.str(); + if (!s.empty() && s.back() == ',') s.pop_back(); + trace_ss_ << "H" << current_horizon_ << ":EVENTS:" << s << "\n"; + events_trace_ss_.str(""); + events_trace_ss_.clear(); + } + } + + void log_final_id_assigned(i_t provisional_id, i_t final_id) + { + if (settings_.enable_determinism_trace) { + final_ids_trace_ss_ << provisional_id << "->" << final_id << ","; + } + } + + void flush_final_ids_trace() + { + if (settings_.enable_determinism_trace && final_ids_trace_ss_.str().length() > 0) { + std::string s = final_ids_trace_ss_.str(); + if (!s.empty() && s.back() == ',') s.pop_back(); + trace_ss_ << "H" << current_horizon_ << ":FINAL_IDS:" << s << "\n"; + final_ids_trace_ss_.str(""); + final_ids_trace_ss_.clear(); + } + } + + void log_heuristic_received(double vt, f_t objective) + { + if (settings_.enable_event_log) { + log_event(vt, -1, bsp_log_event_t::HEURISTIC_RECEIVED, -1, -1, + "obj=" + std::to_string(objective)); + } + + if (settings_.enable_determinism_trace) { + trace_ss_ << "H" << current_horizon_ << ":HEURISTIC:" << objective << "@" << vt << "\n"; + } + } + + void log_incumbent_update(double vt, f_t objective, const std::string& source) + { + if (settings_.enable_event_log) { + log_event(vt, -1, bsp_log_event_t::INCUMBENT_UPDATE, -1, -1, + "obj=" + std::to_string(objective) + ",source=" + source); + } + + if (settings_.enable_determinism_trace) { + trace_ss_ << "H" << current_horizon_ << ":INCUMBENT:" << objective << "@" << vt << "(" + << source << ")\n"; + } + } + + void log_heap_order(const std::vector& final_ids) + { + if (settings_.enable_determinism_trace) { + trace_ss_ << "H" << current_horizon_ << ":HEAP_ORDER:"; + for (size_t i = 0; i < final_ids.size(); ++i) { + trace_ss_ << final_ids[i]; + if (i < final_ids.size() - 1) trace_ss_ << ","; + } + trace_ss_ << "\n"; + } + } + + // ======================================================================== + // Tree State (DOT format) + // ======================================================================== + + void emit_tree_state(int horizon_num, const mip_node_t& root, f_t upper_bound) + { + if (!settings_.enable_tree_dot) return; + + std::string filename = + settings_.output_dir + "bsp_tree_h" + std::to_string(horizon_num) + ".dot"; + std::ofstream file(filename); + if (!file.is_open()) return; + + file << "digraph BSPTree_Horizon" << horizon_num << " {\n"; + file << " rankdir=TB;\n"; + file << " node [shape=box];\n\n"; + + // Traverse tree and emit nodes + emit_tree_node_recursive(file, &root, upper_bound); + + file << "}\n"; + file.close(); + } + + // ======================================================================== + // JSON State Dump + // ======================================================================== + + template + void emit_state_json(int horizon_num, double vt_start, double vt_end, i_t next_final_id, + f_t upper_bound, f_t lower_bound, i_t nodes_explored, i_t nodes_unexplored, + const WorkerPool& workers, const std::vector*>& heap_nodes, + const bb_event_batch_t& events) + { + if (!settings_.enable_state_json) return; + + std::string filename = + settings_.output_dir + "bsp_state_h" + std::to_string(horizon_num) + ".json"; + std::ofstream file(filename); + if (!file.is_open()) return; + + file << "{\n"; + file << " \"horizon\": " << horizon_num << ",\n"; + file << " \"vt_range\": [" << vt_start << ", " << vt_end << "],\n"; + file << " \"bsp_next_final_id\": " << next_final_id << ",\n"; + file << " \"upper_bound\": " << (upper_bound >= 1e30 ? "\"inf\"" : std::to_string(upper_bound)) + << ",\n"; + file << " \"lower_bound\": " << lower_bound << ",\n"; + file << " \"nodes_explored\": " << nodes_explored << ",\n"; + file << " \"nodes_unexplored\": " << nodes_unexplored << ",\n"; + + // Workers + file << " \"workers\": [\n"; + for (int i = 0; i < workers.size(); ++i) { + const auto& w = workers[i]; + file << " {\n"; + file << " \"id\": " << i << ",\n"; + file << " \"clock\": " << w.clock << ",\n"; + if (w.current_node != nullptr) { + file << " \"current_node\": {\"id\": " << w.current_node->node_id + << ", \"final_id\": " << w.current_node->final_id + << ", \"acc_vt\": " << w.current_node->accumulated_vt << "},\n"; + } else { + file << " \"current_node\": null,\n"; + } + file << " \"local_queue_size\": " << w.local_queue.size() << "\n"; + file << " }" << (i < workers.size() - 1 ? "," : "") << "\n"; + } + file << " ],\n"; + + // Heap + file << " \"heap\": [\n"; + for (size_t i = 0; i < heap_nodes.size(); ++i) { + const auto* n = heap_nodes[i]; + file << " {\"id\": " << n->node_id << ", \"final_id\": " << n->final_id + << ", \"lb\": " << n->lower_bound << "}" << (i < heap_nodes.size() - 1 ? "," : "") + << "\n"; + } + file << " ],\n"; + + // Events this horizon + file << " \"events_count\": " << events.events.size() << "\n"; + + file << "}\n"; + file.close(); + } + + // ======================================================================== + // Finalization + // ======================================================================== + + void finalize() + { + if (settings_.enable_event_log) { flush_event_log(); } + + if (settings_.enable_determinism_trace) { flush_trace(); } + } + + private: + bsp_debug_settings_t settings_; + std::chrono::steady_clock::time_point start_time_; + int num_workers_{0}; + double horizon_step_{5.0}; + int current_horizon_{0}; + double horizon_vt_start_{0.0}; + double horizon_vt_end_{0.0}; + + std::mutex mutex_; + std::stringstream log_ss_; + std::stringstream trace_ss_; + std::stringstream assign_trace_ss_; + std::stringstream events_trace_ss_; + std::stringstream final_ids_trace_ss_; + + // Timeline tracking + std::vector timeline_events_; + std::map current_solve_start_; + std::map current_solve_node_; + std::map current_solve_fid_; + + double get_real_time() const + { + auto now = std::chrono::steady_clock::now(); + return std::chrono::duration(now - start_time_).count(); + } + + void log_event(double vt, int worker_id, bsp_log_event_t event, i_t node_id, i_t final_id, + const std::string& details) + { + std::lock_guard lock(mutex_); + log_event_unlocked(vt, worker_id, event, node_id, final_id, details); + } + + void log_event_unlocked(double vt, int worker_id, bsp_log_event_t event, i_t node_id, + i_t final_id, const std::string& details) + { + log_ss_ << std::fixed << std::setprecision(3) << vt << "\t" << get_real_time() << "\t" + << current_horizon_ << "\t" << (worker_id < 0 ? "COORD" : std::to_string(worker_id)) + << "\t" << bsp_log_event_name(event) << "\t" + << (node_id < 0 ? "-" : std::to_string(node_id)) << "\t" + << (final_id < 0 ? "-" : std::to_string(final_id)) << "\t" << details << "\n"; + } + + void flush_event_log() + { + std::string filename = settings_.output_dir + "bsp_events.log"; + std::ofstream file(filename); + if (file.is_open()) { + file << "VT\tREAL_TIME\tHORIZON\tWORKER\tEVENT\tNODE_ID\tFINAL_ID\tDETAILS\n"; + file << log_ss_.str(); + file.close(); + } + } + + void flush_trace() + { + std::string filename = settings_.output_dir + "bsp_trace.txt"; + std::ofstream file(filename); + if (file.is_open()) { + file << "# BSP Determinism Trace\n"; + file << "# Compare this file across runs - must be identical for determinism\n\n"; + file << trace_ss_.str(); + file.close(); + } + } + + void emit_timeline_for_horizon(int horizon_num) + { + std::string filename = settings_.output_dir + "bsp_timeline.txt"; + std::ofstream file(filename, std::ios::app); + if (!file.is_open()) return; + + const int width = 80; + const double vt_min = horizon_vt_start_; + const double vt_max = horizon_vt_end_; + const double vt_range = vt_max - vt_min; + + // Avoid division by zero + if (vt_range <= 0.0) { + file << "\nHorizon " << horizon_num << ": Empty or invalid VT range\n"; + return; + } + + file << "\n"; + file << std::string(width, '=') << "\n"; + file << "Horizon " << horizon_num << ": VT [" << vt_min << ", " << vt_max << ")\n"; + file << std::string(width, '-') << "\n"; + + // VT scale + file << " VT: "; + for (int i = 0; i <= 5; ++i) { + double vt = vt_min + (vt_range * i / 5.0); + file << std::setw(10) << std::fixed << std::setprecision(1) << vt; + } + file << "\n"; + + // Worker rows + for (int w = 0; w < num_workers_; ++w) { + file << " W" << w << ": "; + + std::string row(60, '-'); + + for (const auto& te : timeline_events_) { + if (te.worker_id != w) continue; + + int start_col = static_cast((te.vt_start - vt_min) / vt_range * 60); + int end_col = static_cast((te.vt_end - vt_min) / vt_range * 60); + start_col = std::max(0, std::min(59, start_col)); + end_col = std::max(0, std::min(59, end_col)); + + char fill_char = '#'; + if (te.result == "PAUSED") + fill_char = '%'; + else if (te.result == "INTEGER") + fill_char = '*'; + else if (te.result == "FATHOMED" || te.result == "INFEASIBLE") + fill_char = 'x'; + + for (int c = start_col; c <= end_col; ++c) { + row[c] = fill_char; + } + } + + file << row << "\n"; + + // Labels row + file << " "; + std::string labels(60, ' '); + for (const auto& te : timeline_events_) { + if (te.worker_id != w) continue; + int mid_col = static_cast(((te.vt_start + te.vt_end) / 2 - vt_min) / vt_range * 60); + mid_col = std::max(0, std::min(55, mid_col)); + + std::string label = "N" + std::to_string(te.final_id); + for (size_t i = 0; i < label.size() && mid_col + i < 60; ++i) { + labels[mid_col + i] = label[i]; + } + } + file << labels << "\n"; + } + + file << std::string(width, '-') << "\n"; + file << "Legend: ### solving %%% paused *** integer xxx fathomed/infeasible\n"; + file << std::string(width, '=') << "\n"; + } + + void emit_tree_node_recursive(std::ofstream& file, const mip_node_t* node, + f_t upper_bound) + { + if (node == nullptr) return; + + // Determine fill color based on status + std::string color = "white"; + std::string status_str; + switch (node->status) { + case node_status_t::PENDING: + color = "lightgray"; + status_str = "PENDING"; + break; + case node_status_t::INTEGER_FEASIBLE: + color = "lightgreen"; + status_str = "INTEGER"; + break; + case node_status_t::INFEASIBLE: + color = "lightcoral"; + status_str = "INFEASIBLE"; + break; + case node_status_t::FATHOMED: + color = "lightsalmon"; + status_str = "FATHOMED"; + break; + case node_status_t::HAS_CHILDREN: + color = "lightblue"; + status_str = "BRANCHED"; + break; + case node_status_t::NUMERICAL: + color = "orange"; + status_str = "NUMERICAL"; + break; + } + + if (node->bsp_state == bsp_node_state_t::PAUSED) { + color = "yellow"; + status_str = "PAUSED"; + } + + file << " N" << node->node_id << " [label=\"N" << node->node_id; + if (node->final_id >= 0) file << " (fid=" << node->final_id << ")"; + file << "\\nlb=" << std::fixed << std::setprecision(1) << node->lower_bound; + file << "\\n" << status_str; + if (node->bsp_state == bsp_node_state_t::PAUSED) { + file << "\\nacc_vt=" << node->accumulated_vt; + } + file << "\" style=filled fillcolor=" << color << "];\n"; + + // Emit edges to children + if (node->get_down_child() != nullptr) { + file << " N" << node->node_id << " -> N" << node->get_down_child()->node_id + << " [label=\"x" << node->branch_var << "<=" << std::floor(node->fractional_val) + << "\"];\n"; + emit_tree_node_recursive(file, node->get_down_child(), upper_bound); + } + + if (node->get_up_child() != nullptr) { + file << " N" << node->node_id << " -> N" << node->get_up_child()->node_id << " [label=\"x" + << node->branch_var << ">=" << std::ceil(node->fractional_val) << "\"];\n"; + emit_tree_node_recursive(file, node->get_up_child(), upper_bound); + } + } +}; + +// ============================================================================ +// Convenience Macros +// ============================================================================ + +#ifdef BSP_DEBUG_ENABLED + +#define BSP_DEBUG_LOG_HORIZON_START(logger, h, vs, ve) (logger).log_horizon_start(h, vs, ve) +#define BSP_DEBUG_LOG_HORIZON_END(logger, h, vt) (logger).log_horizon_end(h, vt) +#define BSP_DEBUG_LOG_NODE_ASSIGNED(logger, vt, w, nid, fid, lb) \ + (logger).log_node_assigned(vt, w, nid, fid, lb) +#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(logger) (logger).flush_assign_trace() +#define BSP_DEBUG_LOG_SOLVE_START(logger, vt, w, nid, fid, wl, resumed) \ + (logger).log_solve_start(vt, w, nid, fid, wl, resumed) +#define BSP_DEBUG_LOG_SOLVE_END(logger, vt, w, nid, fid, result, lb) \ + (logger).log_solve_end(vt, w, nid, fid, result, lb) +#define BSP_DEBUG_LOG_BRANCHED(logger, vt, w, pid, did, uid) \ + (logger).log_branched(vt, w, pid, did, uid) +#define BSP_DEBUG_LOG_PAUSED(logger, vt, w, nid, fid, acc) (logger).log_paused(vt, w, nid, fid, acc) +#define BSP_DEBUG_LOG_INTEGER(logger, vt, w, nid, obj) (logger).log_integer(vt, w, nid, obj) +#define BSP_DEBUG_LOG_FATHOMED(logger, vt, w, nid, lb) (logger).log_fathomed(vt, w, nid, lb) +#define BSP_DEBUG_LOG_INFEASIBLE(logger, vt, w, nid) (logger).log_infeasible(vt, w, nid) +#define BSP_DEBUG_LOG_SYNC_PHASE_START(logger, vt, ne) (logger).log_sync_phase_start(vt, ne) +#define BSP_DEBUG_LOG_SYNC_PHASE_END(logger, vt) (logger).log_sync_phase_end(vt) +#define BSP_DEBUG_LOG_FINAL_ID_ASSIGNED(logger, pid, fid) (logger).log_final_id_assigned(pid, fid) +#define BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(logger) (logger).flush_final_ids_trace() +#define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(logger, vt, obj) (logger).log_heuristic_received(vt, obj) +#define BSP_DEBUG_LOG_INCUMBENT_UPDATE(logger, vt, obj, src) \ + (logger).log_incumbent_update(vt, obj, src) +#define BSP_DEBUG_LOG_HEAP_ORDER(logger, fids) (logger).log_heap_order(fids) +#define BSP_DEBUG_EMIT_TREE_STATE(logger, h, root, ub) (logger).emit_tree_state(h, root, ub) +#define BSP_DEBUG_EMIT_STATE_JSON(logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ + (logger).emit_state_json(h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) +#define BSP_DEBUG_FINALIZE(logger) (logger).finalize() + +#else + +#define BSP_DEBUG_LOG_HORIZON_START(logger, h, vs, ve) ((void)0) +#define BSP_DEBUG_LOG_HORIZON_END(logger, h, vt) ((void)0) +#define BSP_DEBUG_LOG_NODE_ASSIGNED(logger, vt, w, nid, fid, lb) ((void)0) +#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(logger) ((void)0) +#define BSP_DEBUG_LOG_SOLVE_START(logger, vt, w, nid, fid, wl, resumed) ((void)0) +#define BSP_DEBUG_LOG_SOLVE_END(logger, vt, w, nid, fid, result, lb) ((void)0) +#define BSP_DEBUG_LOG_BRANCHED(logger, vt, w, pid, did, uid) ((void)0) +#define BSP_DEBUG_LOG_PAUSED(logger, vt, w, nid, fid, acc) ((void)0) +#define BSP_DEBUG_LOG_INTEGER(logger, vt, w, nid, obj) ((void)0) +#define BSP_DEBUG_LOG_FATHOMED(logger, vt, w, nid, lb) ((void)0) +#define BSP_DEBUG_LOG_INFEASIBLE(logger, vt, w, nid) ((void)0) +#define BSP_DEBUG_LOG_SYNC_PHASE_START(logger, vt, ne) ((void)0) +#define BSP_DEBUG_LOG_SYNC_PHASE_END(logger, vt) ((void)0) +#define BSP_DEBUG_LOG_FINAL_ID_ASSIGNED(logger, pid, fid) ((void)0) +#define BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(logger) ((void)0) +#define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(logger, vt, obj) ((void)0) +#define BSP_DEBUG_LOG_INCUMBENT_UPDATE(logger, vt, obj, src) ((void)0) +#define BSP_DEBUG_LOG_HEAP_ORDER(logger, fids) ((void)0) +#define BSP_DEBUG_EMIT_TREE_STATE(logger, h, root, ub) ((void)0) +#define BSP_DEBUG_EMIT_STATE_JSON(logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ + ((void)0) +#define BSP_DEBUG_FINALIZE(logger) ((void)0) + +#endif + +} // namespace cuopt::linear_programming::dual_simplex + diff --git a/cpp/src/dual_simplex/mip_node.hpp b/cpp/src/dual_simplex/mip_node.hpp index 1d66a21f73..b65ec2edd7 100644 --- a/cpp/src/dual_simplex/mip_node.hpp +++ b/cpp/src/dual_simplex/mip_node.hpp @@ -29,6 +29,12 @@ enum class node_status_t : int { enum class rounding_direction_t : int8_t { NONE = -1, DOWN = 0, UP = 1 }; +// BSP state for deterministic parallel B&B +enum class bsp_node_state_t : int8_t { + READY = 0, // Node is ready to be processed + PAUSED = 1 // Node processing was paused at horizon boundary +}; + bool inactive_status(node_status_t status); template @@ -233,6 +239,11 @@ class mip_node_t { copy.branch_var_upper = branch_var_upper; copy.fractional_val = fractional_val; copy.node_id = node_id; + // Copy BSP fields + copy.accumulated_vt = accumulated_vt; + copy.bsp_state = bsp_state; + copy.provisional_id = provisional_id; + copy.final_id = final_id; return copy; } @@ -250,6 +261,25 @@ class mip_node_t { std::unique_ptr children[2]; std::vector vstatus; + + // BSP fields for deterministic parallel B&B + f_t accumulated_vt{0.0}; // Virtual time spent on this node so far + bsp_node_state_t bsp_state{bsp_node_state_t::READY}; // BSP processing state + + // For deterministic node ID assignment in BSP mode: + // - provisional_id is assigned atomically during parallel execution + // - final_id is assigned deterministically during sync phase based on event order + // - Use final_id for sorting if set (>= 0), otherwise fall back to provisional_id/node_id + i_t provisional_id{-1}; + i_t final_id{-1}; + + // Get the ID to use for deterministic ordering + i_t get_deterministic_id() const + { + if (final_id >= 0) return final_id; + if (provisional_id >= 0) return provisional_id; + return node_id; + } }; template diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index 644c906e9d..f121861641 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -191,11 +191,11 @@ solution_t mip_solver_t::run_solver() i_t num_threads = branch_and_bound_settings.num_threads; i_t num_bfs_threads = std::max(1, num_threads / 4); i_t num_diving_threads = num_threads - num_bfs_threads; - // deterministic mode: no diving for now + // deterministic mode: use BSP coordinator with multiple workers, no diving if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - num_threads = 1; - num_bfs_threads = 1; - num_diving_threads = 0; + // BSP mode can use multiple workers deterministically + num_bfs_threads = std::max(1, num_threads); + num_diving_threads = 0; // No diving in deterministic mode } branch_and_bound_settings.num_bfs_threads = num_bfs_threads; branch_and_bound_settings.num_diving_threads = num_diving_threads; @@ -236,6 +236,16 @@ solution_t mip_solver_t::run_solver() std::bind(&dual_simplex::branch_and_bound_t::set_new_solution, branch_and_bound.get(), std::placeholders::_1); + } else if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + // In deterministic mode, use VT-aware solution injection + // Use the B&B's current horizon as the VT timestamp + // This ensures heuristic solutions are processed at the END of the current horizon, + // maintaining determinism regardless of when the GPU heuristic actually finds the solution + context.problem_ptr->branch_and_bound_callback = + [bb = branch_and_bound.get()](const std::vector& solution) { + double vt = bb->get_current_bsp_horizon(); + bb->set_new_solution_deterministic(solution, vt); + }; } context.work_unit_scheduler_.register_context(branch_and_bound->get_work_unit_context()); diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index 7bdec72973..57cd93fa2f 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -59,7 +59,7 @@ std::tuple, std::vector, std::vector> select_k_ std::cerr << "Tested with seed " << seed << "\n"; problem.compute_n_integer_vars(); auto [v_lb, v_ub] = extract_host_bounds(problem.variable_bounds, problem.handle_ptr); - auto int_var_id = host_copy(problem.integer_indices); + auto int_var_id = host_copy(problem.integer_indices, problem.handle_ptr->get_stream()); int_var_id.erase( std::remove_if(int_var_id.begin(), int_var_id.end(), From 0ea2c1f8da4b5cfde2b0619869de7ac0c2047604 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 7 Jan 2026 16:50:57 +0000 Subject: [PATCH 108/225] some debug tweaks --- .../linear_programming/cuopt/run_mip.cpp | 5 +- cpp/src/dual_simplex/branch_and_bound.cpp | 378 ++++++++++-------- cpp/src/dual_simplex/bsp_debug.hpp | 329 ++++++++++----- cpp/src/dual_simplex/mip_node.hpp | 29 +- 4 files changed, 465 insertions(+), 276 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 323dac706f..66435da2b3 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -206,6 +206,9 @@ int run_single_file(std::string file_path, settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; settings.presolve = true; + + settings.presolve = false; + cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index c19a9656d3..80d239f2d2 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -28,9 +28,9 @@ #include #include #include -#include #include #include +#include #include namespace cuopt::linear_programming::dual_simplex { @@ -230,6 +230,8 @@ branch_and_bound_t::branch_and_bound_t( solver_status_(mip_exploration_status_t::UNSET), bsp_debug_settings_(bsp_debug_settings_t::from_environment()) { + bsp_debug_settings_.enable_all(); + exploration_stats_.start_time = tic(); dualize_info_t dualize_info; convert_user_problem(original_problem_, settings_, original_lp_, new_slacks_, dualize_info); @@ -498,7 +500,7 @@ void branch_and_bound_t::set_new_solution(const std::vector& solu template void branch_and_bound_t::set_new_solution_deterministic(const std::vector& solution, - double vt_timestamp) + double vt_timestamp) { // In BSP mode, queue the solution to be processed at the correct virtual time // This ensures deterministic ordering of solution events @@ -734,10 +736,10 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, i_t leaf_depth, thread_type_t thread_type) { - bool send_solution = false; + bool send_solution = false; bool improved_incumbent = false; - i_t nodes_explored = exploration_stats_.nodes_explored; - i_t nodes_unexplored = exploration_stats_.nodes_unexplored; + i_t nodes_explored = exploration_stats_.nodes_explored; + i_t nodes_unexplored = exploration_stats_.nodes_unexplored; mutex_upper_.lock(); if (leaf_objective < upper_bound_) { @@ -770,9 +772,10 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, mutex_upper_.unlock(); // Debug: Log incumbent update (after releasing mutex to avoid potential issues) - if (improved_incumbent && bsp_debug_settings_.any_enabled() && bsp_mode_enabled_) { + if (improved_incumbent && bsp_mode_enabled_) { std::string source = (thread_type == thread_type_t::EXPLORATION) ? "bb_integer" : "diving"; - bsp_debug_logger_.log_incumbent_update(bsp_current_horizon_, leaf_objective, source); + BSP_DEBUG_LOG_INCUMBENT_UPDATE( + bsp_debug_settings_, bsp_debug_logger_, bsp_current_horizon_, leaf_objective, source); } } @@ -914,11 +917,6 @@ node_solve_info_t branch_and_bound_t::solve_node( work_unit_context_.global_work_units_elapsed >= settings_.work_limit) { lp_status = dual::status_t::WORK_LIMIT; } - if (settings_.deterministic) { - printf("------ Total work unit progress B&B: %f / %f\n", - work_unit_context_.global_work_units_elapsed, - settings_.work_limit); - } if (lp_status == dual::status_t::NUMERICAL) { log.printf("Numerical issue node %d. Resolving from scratch.\n", node_ptr->node_id); @@ -1874,24 +1872,23 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_tfinal_id = 1; - search_tree_.root.get_up_child()->final_id = 2; - bsp_next_final_id_ = 3; // Next ID to assign + search_tree_.root.get_up_child()->final_id = 2; + bsp_next_final_id_ = 3; // Next ID to assign heap_.push(search_tree_.root.get_down_child()); heap_.push(search_tree_.root.get_up_child()); - const f_t inf = std::numeric_limits::infinity(); - f_t lower_bound = get_lower_bound(); - f_t upper_bound = get_upper_bound(); - f_t abs_gap = upper_bound - lower_bound; - f_t rel_gap = user_relative_gap(original_lp_, upper_bound, lower_bound); + const f_t inf = std::numeric_limits::infinity(); + f_t lower_bound = get_lower_bound(); + f_t upper_bound = get_upper_bound(); + f_t abs_gap = upper_bound - lower_bound; + f_t rel_gap = user_relative_gap(original_lp_, upper_bound, lower_bound); constexpr i_t target_queue_size = 5; // Target nodes per worker @@ -1899,23 +1896,19 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t settings_.absolute_mip_gap_tol && rel_gap > settings_.relative_mip_gap_tol && (heap_.size() > 0 || bsp_workers_->any_has_work())) { - ++bsp_horizon_number_; double horizon_start = bsp_current_horizon_ - bsp_horizon_step_; double horizon_end = bsp_current_horizon_; // Debug: Log horizon start - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_horizon_start(bsp_horizon_number_, horizon_start, horizon_end); - } + BSP_DEBUG_LOG_HORIZON_START( + bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_start, horizon_end); // PHASE 1: ASSIGNMENT - Fill worker queues refill_worker_queues(target_queue_size); // Flush assignment trace after all assignments - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.flush_assign_trace(); - } + BSP_DEBUG_FLUSH_ASSIGN_TRACE(bsp_debug_settings_, bsp_debug_logger_); // Reset workers for new horizon bsp_workers_->reset_for_horizon(horizon_start, horizon_end); @@ -1930,43 +1923,52 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t all_events = bsp_workers_->collect_and_sort_events(); // Debug: Log sync phase - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_sync_phase_start(horizon_end, all_events.size()); - } + BSP_DEBUG_LOG_SYNC_PHASE_START( + bsp_debug_settings_, bsp_debug_logger_, horizon_end, all_events.size()); // Process history and sync process_history_and_sync(all_events); // Debug: Flush final IDs trace and log sync end - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.flush_final_ids_trace(); - bsp_debug_logger_.log_sync_phase_end(horizon_end); - } + BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(bsp_debug_settings_, bsp_debug_logger_); + BSP_DEBUG_LOG_SYNC_PHASE_END(bsp_debug_settings_, bsp_debug_logger_, horizon_end); // Prune paused nodes that are now dominated by new incumbent prune_worker_nodes_vs_incumbent(); // Debug: Log horizon end, emit tree state and JSON state - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_horizon_end(bsp_horizon_number_, horizon_end); - - // Emit tree state (DOT format) - bsp_debug_logger_.emit_tree_state(bsp_horizon_number_, search_tree_.root, get_upper_bound()); - - // Collect heap nodes for JSON state - std::vector*> heap_snapshot; - // Note: We can't easily iterate the heap, so just log the size - bsp_debug_logger_.emit_state_json(bsp_horizon_number_, horizon_start, horizon_end, - bsp_next_final_id_, get_upper_bound(), get_lower_bound(), - exploration_stats_.nodes_explored, - exploration_stats_.nodes_unexplored, *bsp_workers_, - heap_snapshot, all_events); - } + BSP_DEBUG_LOG_HORIZON_END( + bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_end); + BSP_DEBUG_EMIT_TREE_STATE(bsp_debug_settings_, + bsp_debug_logger_, + bsp_horizon_number_, + search_tree_.root, + get_upper_bound()); + // Collect heap nodes for JSON state (Note: We can't easily iterate the heap, so just log the + // size) + std::vector*> heap_snapshot; + BSP_DEBUG_EMIT_STATE_JSON(bsp_debug_settings_, + bsp_debug_logger_, + bsp_horizon_number_, + horizon_start, + horizon_end, + bsp_next_final_id_, + get_upper_bound(), + get_lower_bound(), + exploration_stats_.nodes_explored, + exploration_stats_.nodes_unexplored, + *bsp_workers_, + heap_snapshot, + all_events); // Advance the horizon bsp_current_horizon_ += bsp_horizon_step_; @@ -2003,9 +2005,7 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t::refill_worker_queues(i_t target_queue_size) mutex_heap_.unlock(); // Assign nodes to workers deterministically (round-robin by deterministic ID order) - // Use get_deterministic_id() which returns final_id if set, else provisional_id, else node_id - std::sort(nodes_to_assign.begin(), nodes_to_assign.end(), + // Use get_deterministic_id() which returns final_id if set, else node_id + std::sort(nodes_to_assign.begin(), + nodes_to_assign.end(), [](const mip_node_t* a, const mip_node_t* b) { return a->get_deterministic_id() < b->get_deterministic_id(); }); @@ -2057,11 +2058,14 @@ void branch_and_bound_t::refill_worker_queues(i_t target_queue_size) (*bsp_workers_)[worker_id].enqueue_node(node); // Debug: Log node assignment - if (bsp_debug_settings_.any_enabled()) { - double vt = bsp_current_horizon_ - bsp_horizon_step_; // Start of current horizon - bsp_debug_logger_.log_node_assigned(vt, worker_id, node->node_id, node->final_id, - node->lower_bound); - } + double vt = bsp_current_horizon_ - bsp_horizon_step_; // Start of current horizon + BSP_DEBUG_LOG_NODE_ASSIGNED(bsp_debug_settings_, + bsp_debug_logger_, + vt, + worker_id, + node->node_id, + node->final_id, + node->lower_bound); } } @@ -2074,7 +2078,6 @@ void branch_and_bound_t::run_worker_until_horizon(bb_worker_state_t* node = worker.dequeue_node(); if (node == nullptr) break; @@ -2102,11 +2105,10 @@ void branch_and_bound_t::run_worker_until_horizon(bb_worker_state_t -node_solve_info_t branch_and_bound_t::solve_node_bsp( - bb_worker_state_t& worker, - mip_node_t* node_ptr, - search_tree_t& search_tree, - double current_horizon) +node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t& worker, + mip_node_t* node_ptr, + search_tree_t& search_tree, + double current_horizon) { raft::common::nvtx::range scope("BB::solve_node_bsp"); @@ -2116,11 +2118,15 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp( bool is_resumed = (node_ptr->bsp_state == bsp_node_state_t::PAUSED); // Debug: Log solve start - if (bsp_debug_settings_.any_enabled()) { - double work_limit = current_horizon - worker.clock; - bsp_debug_logger_.log_solve_start(worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, work_limit, is_resumed); - } + double work_limit = current_horizon - worker.clock; + BSP_DEBUG_LOG_SOLVE_START(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + node_ptr->final_id, + work_limit, + is_resumed); // Setup leaf problem bounds std::fill(worker.node_presolver->bounds_changed.begin(), @@ -2142,9 +2148,9 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp( // Bounds strengthening simplex_solver_settings_t lp_settings = settings_; lp_settings.set_log(false); - lp_settings.cut_off = get_upper_bound() + settings_.dual_tol; - lp_settings.inside_mip = 2; - lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time); + lp_settings.cut_off = get_upper_bound() + settings_.dual_tol; + lp_settings.inside_mip = 2; + lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time); // Work limit is the remaining VT budget in this horizon // Note: accumulated_vt tracks work already spent (for statistics), but doesn't increase budget lp_settings.work_limit = current_horizon - worker.clock; @@ -2164,27 +2170,27 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp( } // Solve LP relaxation - lp_solution_t leaf_solution(worker.leaf_problem->num_rows, worker.leaf_problem->num_cols); + lp_solution_t leaf_solution(worker.leaf_problem->num_rows, + worker.leaf_problem->num_cols); std::vector& leaf_vstatus = node_ptr->vstatus; i_t node_iter = 0; f_t lp_start_time = tic(); std::vector leaf_edge_norms = edge_norms_; - dual::status_t lp_status = dual_phase2_with_advanced_basis( - 2, - 0, - worker.recompute_bounds_and_basis, - lp_start_time, - *worker.leaf_problem, - lp_settings, - leaf_vstatus, - *worker.basis_factors, - worker.basic_list, - worker.nonbasic_list, - leaf_solution, - node_iter, - leaf_edge_norms, - &worker.work_context); + dual::status_t lp_status = dual_phase2_with_advanced_basis(2, + 0, + worker.recompute_bounds_and_basis, + lp_start_time, + *worker.leaf_problem, + lp_settings, + leaf_vstatus, + *worker.basis_factors, + worker.basic_list, + worker.nonbasic_list, + leaf_solution, + node_iter, + leaf_edge_norms, + &worker.work_context); // Update worker clock with work performed // The simplex solver recorded work to work_context.global_work_units_elapsed @@ -2201,12 +2207,21 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp( worker.pause_current_node(node_ptr, accumulated_vt); // Debug: Log solve end (paused) - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, "PAUSED", node_ptr->lower_bound); - bsp_debug_logger_.log_paused(worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, static_cast(accumulated_vt)); - } + BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + node_ptr->final_id, + "PAUSED", + node_ptr->lower_bound); + BSP_DEBUG_LOG_PAUSED(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + node_ptr->final_id, + static_cast(accumulated_vt)); return node_solve_info_t::WORK_LIMIT; } @@ -2223,11 +2238,16 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp( worker.recompute_bounds_and_basis = true; // Debug: Log solve end (infeasible) - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, "INFEASIBLE", node_ptr->lower_bound); - bsp_debug_logger_.log_infeasible(worker.clock, worker.worker_id, node_ptr->node_id); - } + BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + node_ptr->final_id, + "INFEASIBLE", + node_ptr->lower_bound); + BSP_DEBUG_LOG_INFEASIBLE( + bsp_debug_settings_, bsp_debug_logger_, worker.clock, worker.worker_id, node_ptr->node_id); return node_solve_info_t::NO_CHILDREN; } else if (lp_status == dual::status_t::CUTOFF) { @@ -2237,12 +2257,20 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp( worker.recompute_bounds_and_basis = true; // Debug: Log solve end (fathomed - cutoff) - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, "FATHOMED", node_ptr->lower_bound); - bsp_debug_logger_.log_fathomed(worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->lower_bound); - } + BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + node_ptr->final_id, + "FATHOMED", + node_ptr->lower_bound); + BSP_DEBUG_LOG_FATHOMED(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + node_ptr->lower_bound); return node_solve_info_t::NO_CHILDREN; } else if (lp_status == dual::status_t::OPTIMAL) { @@ -2256,40 +2284,60 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp( if (leaf_num_fractional == 0) { // Integer feasible - add_feasible_solution(leaf_objective, leaf_solution.x, node_ptr->depth, thread_type_t::EXPLORATION); + add_feasible_solution( + leaf_objective, leaf_solution.x, node_ptr->depth, thread_type_t::EXPLORATION); search_tree.update(node_ptr, node_status_t::INTEGER_FEASIBLE); worker.record_integer_solution(node_ptr, leaf_objective); worker.recompute_bounds_and_basis = true; // Debug: Log solve end (integer) - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, "INTEGER", leaf_objective); - bsp_debug_logger_.log_integer(worker.clock, worker.worker_id, node_ptr->node_id, - leaf_objective); - } + BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + node_ptr->final_id, + "INTEGER", + leaf_objective); + BSP_DEBUG_LOG_INTEGER(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + leaf_objective); return node_solve_info_t::NO_CHILDREN; } else if (leaf_objective <= get_upper_bound() + settings_.absolute_mip_gap_tol / 10) { // Branch logger_t log; - log.log = false; + log.log = false; const i_t branch_var = pc_.variable_selection(leaf_fractional, leaf_solution.x, log); - search_tree.branch(node_ptr, branch_var, leaf_solution.x[branch_var], leaf_vstatus, *worker.leaf_problem, log); + search_tree.branch( + node_ptr, branch_var, leaf_solution.x[branch_var], leaf_vstatus, *worker.leaf_problem, log); search_tree.update(node_ptr, node_status_t::HAS_CHILDREN); i_t down_child_id = node_ptr->get_down_child()->node_id; i_t up_child_id = node_ptr->get_up_child()->node_id; - worker.record_branched(node_ptr, down_child_id, up_child_id, branch_var, leaf_solution.x[branch_var]); + worker.record_branched( + node_ptr, down_child_id, up_child_id, branch_var, leaf_solution.x[branch_var]); // Debug: Log solve end (branched) and branched event - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, "BRANCH", leaf_objective); - bsp_debug_logger_.log_branched(worker.clock, worker.worker_id, node_ptr->node_id, - down_child_id, up_child_id); - } + BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + node_ptr->final_id, + "BRANCH", + leaf_objective); + BSP_DEBUG_LOG_BRANCHED(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + down_child_id, + up_child_id); exploration_stats_.nodes_unexplored += 2; worker.recompute_bounds_and_basis = false; @@ -2308,12 +2356,20 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp( worker.recompute_bounds_and_basis = true; // Debug: Log solve end (fathomed by bound) - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_solve_end(worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, "FATHOMED", leaf_objective); - bsp_debug_logger_.log_fathomed(worker.clock, worker.worker_id, node_ptr->node_id, - leaf_objective); - } + BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + node_ptr->final_id, + "FATHOMED", + leaf_objective); + BSP_DEBUG_LOG_FATHOMED(bsp_debug_settings_, + bsp_debug_logger_, + worker.clock, + worker.worker_id, + node_ptr->node_id, + leaf_objective); return node_solve_info_t::NO_CHILDREN; } @@ -2330,7 +2386,8 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp( } template -void branch_and_bound_t::process_history_and_sync(const bb_event_batch_t& events) +void branch_and_bound_t::process_history_and_sync( + const bb_event_batch_t& events) { // Collect queued heuristic solutions std::vector heuristic_solutions; @@ -2340,14 +2397,16 @@ void branch_and_bound_t::process_history_and_sync(const bb_event_batch mutex_heuristic_queue_.unlock(); // Sort heuristic solutions by VT - std::sort(heuristic_solutions.begin(), heuristic_solutions.end(), + std::sort(heuristic_solutions.begin(), + heuristic_solutions.end(), [](const queued_heuristic_solution_t& a, const queued_heuristic_solution_t& b) { return a.vt_timestamp < b.vt_timestamp; }); - // Build mapping from provisional_id -> final_id for deterministic node ordering - // This is populated during event processing and applied to nodes afterward - std::unordered_map provisional_to_final_id; + // Build mapping from node_id -> final_id for deterministic node ordering + // node_id is used as the provisional identifier during parallel execution; + // final_id is assigned deterministically during this sync phase based on event order. + std::unordered_map node_id_to_final_id; // Merge B&B events and heuristic solutions for unified timeline replay // Both are sorted by VT, so we can do a merge-style iteration @@ -2355,7 +2414,7 @@ void branch_and_bound_t::process_history_and_sync(const bb_event_batch size_t heuristic_idx = 0; while (event_idx < events.events.size() || heuristic_idx < heuristic_solutions.size()) { - bool process_event = false; + bool process_event = false; bool process_heuristic = false; if (event_idx >= events.events.size()) { @@ -2364,7 +2423,8 @@ void branch_and_bound_t::process_history_and_sync(const bb_event_batch process_event = true; } else { // Both have items - pick the one with smaller VT - if (events.events[event_idx].vt_timestamp <= heuristic_solutions[heuristic_idx].vt_timestamp) { + if (events.events[event_idx].vt_timestamp <= + heuristic_solutions[heuristic_idx].vt_timestamp) { process_event = true; } else { process_heuristic = true; @@ -2388,20 +2448,21 @@ void branch_and_bound_t::process_history_and_sync(const bb_event_batch case bb_event_type_t::NODE_BRANCHED: { // Assign deterministic final IDs to the children - // Events are processed in sorted (deterministic) order, so this assignment is deterministic + // Events are processed in sorted (deterministic) order, so this assignment is + // deterministic i_t down_provisional = event.payload.branched.down_child_id; i_t up_provisional = event.payload.branched.up_child_id; - i_t down_final_id = bsp_next_final_id_++; - i_t up_final_id = bsp_next_final_id_++; - provisional_to_final_id[down_provisional] = down_final_id; - provisional_to_final_id[up_provisional] = up_final_id; + i_t down_final_id = bsp_next_final_id_++; + i_t up_final_id = bsp_next_final_id_++; + node_id_to_final_id[down_provisional] = down_final_id; + node_id_to_final_id[up_provisional] = up_final_id; // Debug: Log final ID assignments - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_final_id_assigned(down_provisional, down_final_id); - bsp_debug_logger_.log_final_id_assigned(up_provisional, up_final_id); - } + BSP_DEBUG_LOG_FINAL_ID_ASSIGNED( + bsp_debug_settings_, bsp_debug_logger_, down_provisional, down_final_id); + BSP_DEBUG_LOG_FINAL_ID_ASSIGNED( + bsp_debug_settings_, bsp_debug_logger_, up_provisional, up_final_id); break; } @@ -2419,9 +2480,8 @@ void branch_and_bound_t::process_history_and_sync(const bb_event_batch const auto& hsol = heuristic_solutions[heuristic_idx++]; // Debug: Log heuristic received - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_heuristic_received(hsol.vt_timestamp, hsol.objective); - } + BSP_DEBUG_LOG_HEURISTIC_RECEIVED( + bsp_debug_settings_, bsp_debug_logger_, hsol.vt_timestamp, hsol.objective); // Process heuristic solution at its correct VT position // FIX: Release mutex before calling get_lower_bound() to avoid deadlock @@ -2434,9 +2494,8 @@ void branch_and_bound_t::process_history_and_sync(const bb_event_batch new_upper = hsol.objective; // Debug: Log incumbent update - if (bsp_debug_settings_.any_enabled()) { - bsp_debug_logger_.log_incumbent_update(hsol.vt_timestamp, hsol.objective, "heuristic"); - } + BSP_DEBUG_LOG_INCUMBENT_UPDATE( + bsp_debug_settings_, bsp_debug_logger_, hsol.vt_timestamp, hsol.objective, "heuristic"); } mutex_upper_.unlock(); @@ -2458,13 +2517,10 @@ void branch_and_bound_t::process_history_and_sync(const bb_event_batch // Apply final IDs to all nodes in worker queues and global heap // Helper lambda to apply final_id mapping to a node - auto apply_final_id = [&provisional_to_final_id](mip_node_t* node) { + auto apply_final_id = [&node_id_to_final_id](mip_node_t* node) { if (node->final_id < 0 && node->node_id > 0) { - // Use node_id as provisional_id (they are the same during BSP) - auto it = provisional_to_final_id.find(node->node_id); - if (it != provisional_to_final_id.end()) { - node->final_id = it->second; - } + auto it = node_id_to_final_id.find(node->node_id); + if (it != node_id_to_final_id.end()) { node->final_id = it->second; } } }; @@ -2473,9 +2529,7 @@ void branch_and_bound_t::process_history_and_sync(const bb_event_batch for (auto* node : worker.local_queue) { apply_final_id(node); } - if (worker.current_node != nullptr) { - apply_final_id(worker.current_node); - } + if (worker.current_node != nullptr) { apply_final_id(worker.current_node); } } // Get current upper bound for pruning decisions diff --git a/cpp/src/dual_simplex/bsp_debug.hpp b/cpp/src/dual_simplex/bsp_debug.hpp index 1b3833b06c..a9bdb80689 100644 --- a/cpp/src/dual_simplex/bsp_debug.hpp +++ b/cpp/src/dual_simplex/bsp_debug.hpp @@ -1,14 +1,19 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ #pragma once -#include +// Enable BSP debug macros. The actual logging is controlled at runtime via +// environment variables (CUOPT_BSP_DEBUG_*). This define enables the macro +// infrastructure; without it, all BSP_DEBUG_* macros become complete no-ops. +#define BSP_DEBUG_ENABLED + #include +#include #include #include @@ -150,10 +155,10 @@ struct bsp_debug_settings_t { if (get_env_bool("CUOPT_BSP_DEBUG_ALL")) { settings.enable_all(); } else { - settings.enable_event_log = get_env_bool("CUOPT_BSP_DEBUG_LOG"); - settings.enable_timeline = get_env_bool("CUOPT_BSP_DEBUG_TIMELINE"); - settings.enable_tree_dot = get_env_bool("CUOPT_BSP_DEBUG_TREE"); - settings.enable_state_json = get_env_bool("CUOPT_BSP_DEBUG_JSON"); + settings.enable_event_log = get_env_bool("CUOPT_BSP_DEBUG_LOG"); + settings.enable_timeline = get_env_bool("CUOPT_BSP_DEBUG_TIMELINE"); + settings.enable_tree_dot = get_env_bool("CUOPT_BSP_DEBUG_TREE"); + settings.enable_state_json = get_env_bool("CUOPT_BSP_DEBUG_JSON"); settings.enable_determinism_trace = get_env_bool("CUOPT_BSP_DEBUG_TRACE"); } @@ -195,10 +200,10 @@ class bsp_debug_logger_t { std::filesystem::create_directories(settings_.output_dir); } catch (const std::filesystem::filesystem_error& e) { // Silently disable debug output if we can't create the directory - settings_.enable_event_log = false; - settings_.enable_timeline = false; - settings_.enable_tree_dot = false; - settings_.enable_state_json = false; + settings_.enable_event_log = false; + settings_.enable_timeline = false; + settings_.enable_tree_dot = false; + settings_.enable_state_json = false; settings_.enable_determinism_trace = false; } @@ -225,12 +230,16 @@ class bsp_debug_logger_t { void log_horizon_start(int horizon_num, double vt_start, double vt_end) { - current_horizon_ = horizon_num; + current_horizon_ = horizon_num; horizon_vt_start_ = vt_start; horizon_vt_end_ = vt_end; if (settings_.enable_event_log) { - log_event(vt_start, -1, bsp_log_event_t::HORIZON_START, -1, -1, + log_event(vt_start, + -1, + bsp_log_event_t::HORIZON_START, + -1, + -1, "horizon=[" + std::to_string(vt_start) + "," + std::to_string(vt_end) + ")"); } @@ -244,19 +253,19 @@ class bsp_debug_logger_t { void log_horizon_end(int horizon_num, double vt) { - if (settings_.enable_event_log) { - log_event(vt, -1, bsp_log_event_t::HORIZON_END, -1, -1, ""); - } + if (settings_.enable_event_log) { log_event(vt, -1, bsp_log_event_t::HORIZON_END, -1, -1, ""); } - if (settings_.enable_timeline) { - emit_timeline_for_horizon(horizon_num); - } + if (settings_.enable_timeline) { emit_timeline_for_horizon(horizon_num); } } void log_node_assigned(double vt, int worker_id, i_t node_id, i_t final_id, f_t lower_bound) { if (settings_.enable_event_log) { - log_event(vt, worker_id, bsp_log_event_t::NODE_ASSIGNED, node_id, final_id, + log_event(vt, + worker_id, + bsp_log_event_t::NODE_ASSIGNED, + node_id, + final_id, "lb=" + std::to_string(lower_bound)); } @@ -276,15 +285,19 @@ class bsp_debug_logger_t { } } - void log_solve_start(double vt, int worker_id, i_t node_id, i_t final_id, double work_limit, - bool is_resumed) + void log_solve_start( + double vt, int worker_id, i_t node_id, i_t final_id, double work_limit, bool is_resumed) { std::lock_guard lock(mutex_); if (settings_.enable_event_log && settings_.log_level >= 2) { - log_event_unlocked(vt, worker_id, - is_resumed ? bsp_log_event_t::NODE_RESUMED : bsp_log_event_t::NODE_SOLVE_START, - node_id, final_id, "work_limit=" + std::to_string(work_limit)); + log_event_unlocked( + vt, + worker_id, + is_resumed ? bsp_log_event_t::NODE_RESUMED : bsp_log_event_t::NODE_SOLVE_START, + node_id, + final_id, + "work_limit=" + std::to_string(work_limit)); } // Start timeline event @@ -293,13 +306,17 @@ class bsp_debug_logger_t { current_solve_fid_[worker_id] = final_id; } - void log_solve_end(double vt, int worker_id, i_t node_id, i_t final_id, const std::string& result, - f_t lower_bound) + void log_solve_end( + double vt, int worker_id, i_t node_id, i_t final_id, const std::string& result, f_t lower_bound) { std::lock_guard lock(mutex_); if (settings_.enable_event_log) { - log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_SOLVE_END, node_id, final_id, + log_event_unlocked(vt, + worker_id, + bsp_log_event_t::NODE_SOLVE_END, + node_id, + final_id, "result=" + result + ",lb=" + std::to_string(lower_bound)); } @@ -321,7 +338,11 @@ class bsp_debug_logger_t { std::lock_guard lock(mutex_); if (settings_.enable_event_log) { - log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_BRANCHED, parent_id, -1, + log_event_unlocked(vt, + worker_id, + bsp_log_event_t::NODE_BRANCHED, + parent_id, + -1, "children=" + std::to_string(down_id) + "," + std::to_string(up_id)); } @@ -336,7 +357,11 @@ class bsp_debug_logger_t { std::lock_guard lock(mutex_); if (settings_.enable_event_log) { - log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_PAUSED, node_id, final_id, + log_event_unlocked(vt, + worker_id, + bsp_log_event_t::NODE_PAUSED, + node_id, + final_id, "acc_vt=" + std::to_string(accumulated_vt)); } @@ -350,7 +375,11 @@ class bsp_debug_logger_t { std::lock_guard lock(mutex_); if (settings_.enable_event_log) { - log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_INTEGER, node_id, -1, + log_event_unlocked(vt, + worker_id, + bsp_log_event_t::NODE_INTEGER, + node_id, + -1, "obj=" + std::to_string(objective)); } @@ -364,7 +393,11 @@ class bsp_debug_logger_t { std::lock_guard lock(mutex_); if (settings_.enable_event_log && settings_.log_level >= 2) { - log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_FATHOMED, node_id, -1, + log_event_unlocked(vt, + worker_id, + bsp_log_event_t::NODE_FATHOMED, + node_id, + -1, "lb=" + std::to_string(lower_bound)); } } @@ -383,15 +416,24 @@ class bsp_debug_logger_t { std::lock_guard lock(mutex_); if (settings_.enable_event_log && settings_.log_level >= 2) { - log_event_unlocked(vt, -1, bsp_log_event_t::NODE_PRUNED, node_id, final_id, - "lb=" + std::to_string(lower_bound) + ",ub=" + std::to_string(upper_bound)); + log_event_unlocked( + vt, + -1, + bsp_log_event_t::NODE_PRUNED, + node_id, + final_id, + "lb=" + std::to_string(lower_bound) + ",ub=" + std::to_string(upper_bound)); } } void log_sync_phase_start(double vt, size_t num_events) { if (settings_.enable_event_log) { - log_event(vt, -1, bsp_log_event_t::SYNC_PHASE_START, -1, -1, + log_event(vt, + -1, + bsp_log_event_t::SYNC_PHASE_START, + -1, + -1, "num_events=" + std::to_string(num_events)); } } @@ -433,8 +475,8 @@ class bsp_debug_logger_t { void log_heuristic_received(double vt, f_t objective) { if (settings_.enable_event_log) { - log_event(vt, -1, bsp_log_event_t::HEURISTIC_RECEIVED, -1, -1, - "obj=" + std::to_string(objective)); + log_event( + vt, -1, bsp_log_event_t::HEURISTIC_RECEIVED, -1, -1, "obj=" + std::to_string(objective)); } if (settings_.enable_determinism_trace) { @@ -445,7 +487,11 @@ class bsp_debug_logger_t { void log_incumbent_update(double vt, f_t objective, const std::string& source) { if (settings_.enable_event_log) { - log_event(vt, -1, bsp_log_event_t::INCUMBENT_UPDATE, -1, -1, + log_event(vt, + -1, + bsp_log_event_t::INCUMBENT_UPDATE, + -1, + -1, "obj=" + std::to_string(objective) + ",source=" + source); } @@ -496,9 +542,16 @@ class bsp_debug_logger_t { // ======================================================================== template - void emit_state_json(int horizon_num, double vt_start, double vt_end, i_t next_final_id, - f_t upper_bound, f_t lower_bound, i_t nodes_explored, i_t nodes_unexplored, - const WorkerPool& workers, const std::vector*>& heap_nodes, + void emit_state_json(int horizon_num, + double vt_start, + double vt_end, + i_t next_final_id, + f_t upper_bound, + f_t lower_bound, + i_t nodes_explored, + i_t nodes_unexplored, + const WorkerPool& workers, + const std::vector*>& heap_nodes, const bb_event_batch_t& events) { if (!settings_.enable_state_json) return; @@ -593,15 +646,23 @@ class bsp_debug_logger_t { return std::chrono::duration(now - start_time_).count(); } - void log_event(double vt, int worker_id, bsp_log_event_t event, i_t node_id, i_t final_id, + void log_event(double vt, + int worker_id, + bsp_log_event_t event, + i_t node_id, + i_t final_id, const std::string& details) { std::lock_guard lock(mutex_); log_event_unlocked(vt, worker_id, event, node_id, final_id, details); } - void log_event_unlocked(double vt, int worker_id, bsp_log_event_t event, i_t node_id, - i_t final_id, const std::string& details) + void log_event_unlocked(double vt, + int worker_id, + bsp_log_event_t event, + i_t node_id, + i_t final_id, + const std::string& details) { log_ss_ << std::fixed << std::setprecision(3) << vt << "\t" << get_real_time() << "\t" << current_horizon_ << "\t" << (worker_id < 0 ? "COORD" : std::to_string(worker_id)) @@ -639,9 +700,9 @@ class bsp_debug_logger_t { std::ofstream file(filename, std::ios::app); if (!file.is_open()) return; - const int width = 80; - const double vt_min = horizon_vt_start_; - const double vt_max = horizon_vt_end_; + const int width = 80; + const double vt_min = horizon_vt_start_; + const double vt_max = horizon_vt_end_; const double vt_range = vt_max - vt_min; // Avoid division by zero @@ -713,7 +774,8 @@ class bsp_debug_logger_t { file << std::string(width, '=') << "\n"; } - void emit_tree_node_recursive(std::ofstream& file, const mip_node_t* node, + void emit_tree_node_recursive(std::ofstream& file, + const mip_node_t* node, f_t upper_bound) { if (node == nullptr) return; @@ -764,9 +826,8 @@ class bsp_debug_logger_t { // Emit edges to children if (node->get_down_child() != nullptr) { - file << " N" << node->node_id << " -> N" << node->get_down_child()->node_id - << " [label=\"x" << node->branch_var << "<=" << std::floor(node->fractional_val) - << "\"];\n"; + file << " N" << node->node_id << " -> N" << node->get_down_child()->node_id << " [label=\"x" + << node->branch_var << "<=" << std::floor(node->fractional_val) << "\"];\n"; emit_tree_node_recursive(file, node->get_down_child(), upper_bound); } @@ -781,63 +842,133 @@ class bsp_debug_logger_t { // ============================================================================ // Convenience Macros // ============================================================================ +// +// These macros provide a clean interface for BSP debug logging. +// - When BSP_DEBUG_ENABLED is defined: macros are active (controlled by settings at runtime) +// - When BSP_DEBUG_ENABLED is not defined: macros are no-ops (zero overhead) +// +// The 'settings' parameter is a bsp_debug_settings_t& that controls which features are enabled. #ifdef BSP_DEBUG_ENABLED -#define BSP_DEBUG_LOG_HORIZON_START(logger, h, vs, ve) (logger).log_horizon_start(h, vs, ve) -#define BSP_DEBUG_LOG_HORIZON_END(logger, h, vt) (logger).log_horizon_end(h, vt) -#define BSP_DEBUG_LOG_NODE_ASSIGNED(logger, vt, w, nid, fid, lb) \ - (logger).log_node_assigned(vt, w, nid, fid, lb) -#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(logger) (logger).flush_assign_trace() -#define BSP_DEBUG_LOG_SOLVE_START(logger, vt, w, nid, fid, wl, resumed) \ - (logger).log_solve_start(vt, w, nid, fid, wl, resumed) -#define BSP_DEBUG_LOG_SOLVE_END(logger, vt, w, nid, fid, result, lb) \ - (logger).log_solve_end(vt, w, nid, fid, result, lb) -#define BSP_DEBUG_LOG_BRANCHED(logger, vt, w, pid, did, uid) \ - (logger).log_branched(vt, w, pid, did, uid) -#define BSP_DEBUG_LOG_PAUSED(logger, vt, w, nid, fid, acc) (logger).log_paused(vt, w, nid, fid, acc) -#define BSP_DEBUG_LOG_INTEGER(logger, vt, w, nid, obj) (logger).log_integer(vt, w, nid, obj) -#define BSP_DEBUG_LOG_FATHOMED(logger, vt, w, nid, lb) (logger).log_fathomed(vt, w, nid, lb) -#define BSP_DEBUG_LOG_INFEASIBLE(logger, vt, w, nid) (logger).log_infeasible(vt, w, nid) -#define BSP_DEBUG_LOG_SYNC_PHASE_START(logger, vt, ne) (logger).log_sync_phase_start(vt, ne) -#define BSP_DEBUG_LOG_SYNC_PHASE_END(logger, vt) (logger).log_sync_phase_end(vt) -#define BSP_DEBUG_LOG_FINAL_ID_ASSIGNED(logger, pid, fid) (logger).log_final_id_assigned(pid, fid) -#define BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(logger) (logger).flush_final_ids_trace() -#define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(logger, vt, obj) (logger).log_heuristic_received(vt, obj) -#define BSP_DEBUG_LOG_INCUMBENT_UPDATE(logger, vt, obj, src) \ - (logger).log_incumbent_update(vt, obj, src) -#define BSP_DEBUG_LOG_HEAP_ORDER(logger, fids) (logger).log_heap_order(fids) -#define BSP_DEBUG_EMIT_TREE_STATE(logger, h, root, ub) (logger).emit_tree_state(h, root, ub) -#define BSP_DEBUG_EMIT_STATE_JSON(logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ - (logger).emit_state_json(h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) -#define BSP_DEBUG_FINALIZE(logger) (logger).finalize() +#define BSP_DEBUG_LOG_HORIZON_START(settings, logger, h, vs, ve) \ + do { \ + if ((settings).any_enabled()) (logger).log_horizon_start(h, vs, ve); \ + } while (0) +#define BSP_DEBUG_LOG_HORIZON_END(settings, logger, h, vt) \ + do { \ + if ((settings).any_enabled()) (logger).log_horizon_end(h, vt); \ + } while (0) +#define BSP_DEBUG_LOG_NODE_ASSIGNED(settings, logger, vt, w, nid, fid, lb) \ + do { \ + if ((settings).any_enabled()) (logger).log_node_assigned(vt, w, nid, fid, lb); \ + } while (0) +#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(settings, logger) \ + do { \ + if ((settings).any_enabled()) (logger).flush_assign_trace(); \ + } while (0) +#define BSP_DEBUG_LOG_SOLVE_START(settings, logger, vt, w, nid, fid, wl, resumed) \ + do { \ + if ((settings).any_enabled()) (logger).log_solve_start(vt, w, nid, fid, wl, resumed); \ + } while (0) +#define BSP_DEBUG_LOG_SOLVE_END(settings, logger, vt, w, nid, fid, result, lb) \ + do { \ + if ((settings).any_enabled()) (logger).log_solve_end(vt, w, nid, fid, result, lb); \ + } while (0) +#define BSP_DEBUG_LOG_BRANCHED(settings, logger, vt, w, pid, did, uid) \ + do { \ + if ((settings).any_enabled()) (logger).log_branched(vt, w, pid, did, uid); \ + } while (0) +#define BSP_DEBUG_LOG_PAUSED(settings, logger, vt, w, nid, fid, acc) \ + do { \ + if ((settings).any_enabled()) (logger).log_paused(vt, w, nid, fid, acc); \ + } while (0) +#define BSP_DEBUG_LOG_INTEGER(settings, logger, vt, w, nid, obj) \ + do { \ + if ((settings).any_enabled()) (logger).log_integer(vt, w, nid, obj); \ + } while (0) +#define BSP_DEBUG_LOG_FATHOMED(settings, logger, vt, w, nid, lb) \ + do { \ + if ((settings).any_enabled()) (logger).log_fathomed(vt, w, nid, lb); \ + } while (0) +#define BSP_DEBUG_LOG_INFEASIBLE(settings, logger, vt, w, nid) \ + do { \ + if ((settings).any_enabled()) (logger).log_infeasible(vt, w, nid); \ + } while (0) +#define BSP_DEBUG_LOG_PRUNED(settings, logger, vt, nid, fid, lb, ub) \ + do { \ + if ((settings).any_enabled()) (logger).log_pruned(vt, nid, fid, lb, ub); \ + } while (0) +#define BSP_DEBUG_LOG_SYNC_PHASE_START(settings, logger, vt, ne) \ + do { \ + if ((settings).any_enabled()) (logger).log_sync_phase_start(vt, ne); \ + } while (0) +#define BSP_DEBUG_LOG_SYNC_PHASE_END(settings, logger, vt) \ + do { \ + if ((settings).any_enabled()) (logger).log_sync_phase_end(vt); \ + } while (0) +#define BSP_DEBUG_LOG_FINAL_ID_ASSIGNED(settings, logger, pid, fid) \ + do { \ + if ((settings).any_enabled()) (logger).log_final_id_assigned(pid, fid); \ + } while (0) +#define BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(settings, logger) \ + do { \ + if ((settings).any_enabled()) (logger).flush_final_ids_trace(); \ + } while (0) +#define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(settings, logger, vt, obj) \ + do { \ + if ((settings).any_enabled()) (logger).log_heuristic_received(vt, obj); \ + } while (0) +#define BSP_DEBUG_LOG_INCUMBENT_UPDATE(settings, logger, vt, obj, src) \ + do { \ + if ((settings).any_enabled()) (logger).log_incumbent_update(vt, obj, src); \ + } while (0) +#define BSP_DEBUG_LOG_HEAP_ORDER(settings, logger, fids) \ + do { \ + if ((settings).any_enabled()) (logger).log_heap_order(fids); \ + } while (0) +#define BSP_DEBUG_EMIT_TREE_STATE(settings, logger, h, root, ub) \ + do { \ + if ((settings).any_enabled()) (logger).emit_tree_state(h, root, ub); \ + } while (0) +#define BSP_DEBUG_EMIT_STATE_JSON( \ + settings, logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ + do { \ + if ((settings).any_enabled()) \ + (logger).emit_state_json(h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events); \ + } while (0) +#define BSP_DEBUG_FINALIZE(settings, logger) \ + do { \ + if ((settings).any_enabled()) (logger).finalize(); \ + } while (0) #else -#define BSP_DEBUG_LOG_HORIZON_START(logger, h, vs, ve) ((void)0) -#define BSP_DEBUG_LOG_HORIZON_END(logger, h, vt) ((void)0) -#define BSP_DEBUG_LOG_NODE_ASSIGNED(logger, vt, w, nid, fid, lb) ((void)0) -#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(logger) ((void)0) -#define BSP_DEBUG_LOG_SOLVE_START(logger, vt, w, nid, fid, wl, resumed) ((void)0) -#define BSP_DEBUG_LOG_SOLVE_END(logger, vt, w, nid, fid, result, lb) ((void)0) -#define BSP_DEBUG_LOG_BRANCHED(logger, vt, w, pid, did, uid) ((void)0) -#define BSP_DEBUG_LOG_PAUSED(logger, vt, w, nid, fid, acc) ((void)0) -#define BSP_DEBUG_LOG_INTEGER(logger, vt, w, nid, obj) ((void)0) -#define BSP_DEBUG_LOG_FATHOMED(logger, vt, w, nid, lb) ((void)0) -#define BSP_DEBUG_LOG_INFEASIBLE(logger, vt, w, nid) ((void)0) -#define BSP_DEBUG_LOG_SYNC_PHASE_START(logger, vt, ne) ((void)0) -#define BSP_DEBUG_LOG_SYNC_PHASE_END(logger, vt) ((void)0) -#define BSP_DEBUG_LOG_FINAL_ID_ASSIGNED(logger, pid, fid) ((void)0) -#define BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(logger) ((void)0) -#define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(logger, vt, obj) ((void)0) -#define BSP_DEBUG_LOG_INCUMBENT_UPDATE(logger, vt, obj, src) ((void)0) -#define BSP_DEBUG_LOG_HEAP_ORDER(logger, fids) ((void)0) -#define BSP_DEBUG_EMIT_TREE_STATE(logger, h, root, ub) ((void)0) -#define BSP_DEBUG_EMIT_STATE_JSON(logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ +#define BSP_DEBUG_LOG_HORIZON_START(settings, logger, h, vs, ve) ((void)0) +#define BSP_DEBUG_LOG_HORIZON_END(settings, logger, h, vt) ((void)0) +#define BSP_DEBUG_LOG_NODE_ASSIGNED(settings, logger, vt, w, nid, fid, lb) ((void)0) +#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(settings, logger) ((void)0) +#define BSP_DEBUG_LOG_SOLVE_START(settings, logger, vt, w, nid, fid, wl, resumed) ((void)0) +#define BSP_DEBUG_LOG_SOLVE_END(settings, logger, vt, w, nid, fid, result, lb) ((void)0) +#define BSP_DEBUG_LOG_BRANCHED(settings, logger, vt, w, pid, did, uid) ((void)0) +#define BSP_DEBUG_LOG_PAUSED(settings, logger, vt, w, nid, fid, acc) ((void)0) +#define BSP_DEBUG_LOG_INTEGER(settings, logger, vt, w, nid, obj) ((void)0) +#define BSP_DEBUG_LOG_FATHOMED(settings, logger, vt, w, nid, lb) ((void)0) +#define BSP_DEBUG_LOG_INFEASIBLE(settings, logger, vt, w, nid) ((void)0) +#define BSP_DEBUG_LOG_PRUNED(settings, logger, vt, nid, fid, lb, ub) ((void)0) +#define BSP_DEBUG_LOG_SYNC_PHASE_START(settings, logger, vt, ne) ((void)0) +#define BSP_DEBUG_LOG_SYNC_PHASE_END(settings, logger, vt) ((void)0) +#define BSP_DEBUG_LOG_FINAL_ID_ASSIGNED(settings, logger, pid, fid) ((void)0) +#define BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(settings, logger) ((void)0) +#define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(settings, logger, vt, obj) ((void)0) +#define BSP_DEBUG_LOG_INCUMBENT_UPDATE(settings, logger, vt, obj, src) ((void)0) +#define BSP_DEBUG_LOG_HEAP_ORDER(settings, logger, fids) ((void)0) +#define BSP_DEBUG_EMIT_TREE_STATE(settings, logger, h, root, ub) ((void)0) +#define BSP_DEBUG_EMIT_STATE_JSON( \ + settings, logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ ((void)0) -#define BSP_DEBUG_FINALIZE(logger) ((void)0) +#define BSP_DEBUG_FINALIZE(settings, logger) ((void)0) #endif } // namespace cuopt::linear_programming::dual_simplex - diff --git a/cpp/src/dual_simplex/mip_node.hpp b/cpp/src/dual_simplex/mip_node.hpp index b65ec2edd7..bfea830e92 100644 --- a/cpp/src/dual_simplex/mip_node.hpp +++ b/cpp/src/dual_simplex/mip_node.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -240,10 +240,9 @@ class mip_node_t { copy.fractional_val = fractional_val; copy.node_id = node_id; // Copy BSP fields - copy.accumulated_vt = accumulated_vt; - copy.bsp_state = bsp_state; - copy.provisional_id = provisional_id; - copy.final_id = final_id; + copy.accumulated_vt = accumulated_vt; + copy.bsp_state = bsp_state; + copy.final_id = final_id; return copy; } @@ -263,21 +262,19 @@ class mip_node_t { std::vector vstatus; // BSP fields for deterministic parallel B&B - f_t accumulated_vt{0.0}; // Virtual time spent on this node so far + f_t accumulated_vt{0.0}; // Virtual time spent on this node so far bsp_node_state_t bsp_state{bsp_node_state_t::READY}; // BSP processing state // For deterministic node ID assignment in BSP mode: - // - provisional_id is assigned atomically during parallel execution + // - node_id is assigned non-deterministically during parallel execution (via atomic counter) // - final_id is assigned deterministically during sync phase based on event order - // - Use final_id for sorting if set (>= 0), otherwise fall back to provisional_id/node_id - i_t provisional_id{-1}; + // - Use final_id for sorting if set (>= 0), otherwise fall back to node_id i_t final_id{-1}; // Get the ID to use for deterministic ordering i_t get_deterministic_id() const { if (final_id >= 0) return final_id; - if (provisional_id >= 0) return provisional_id; return node_id; } }; @@ -295,16 +292,20 @@ void remove_fathomed_nodes(std::vector*>& stack) template class node_compare_t { public: + // Comparison for priority queue: returns true if 'a' has lower priority than 'b' + // (elements with lower priority are output last from the heap). + // Primary: prefer lower bound (best-first search) + // Tie-breaker: use deterministic ID for reproducibility across runs bool operator()(const mip_node_t& a, const mip_node_t& b) const { - return a.lower_bound > - b.lower_bound; // True if a comes before b, elements that come before are output last + if (a.lower_bound != b.lower_bound) { return a.lower_bound > b.lower_bound; } + return a.get_deterministic_id() > b.get_deterministic_id(); } bool operator()(const mip_node_t* a, const mip_node_t* b) const { - return a->lower_bound > - b->lower_bound; // True if a comes before b, elements that come before are output last + if (a->lower_bound != b->lower_bound) { return a->lower_bound > b->lower_bound; } + return a->get_deterministic_id() > b->get_deterministic_id(); } }; From c374f69bf43400ba649456199c9f186b0750bfe8 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 7 Jan 2026 16:55:24 +0000 Subject: [PATCH 109/225] fix copyright --- build.sh | 2 +- cpp/include/cuopt/error.hpp | 2 +- cpp/include/cuopt/linear_programming/utilities/internals.hpp | 2 +- cpp/src/dual_simplex/barrier.cu | 2 +- cpp/src/linear_programming/pdlp.cu | 2 +- cpp/src/linear_programming/utilities/cython_solve.cu | 2 +- cpp/src/linear_programming/utils.cuh | 2 +- cpp/src/mip/presolve/gf2_presolve.hpp | 2 +- cpp/src/mip/presolve/third_party_presolve.cpp | 2 +- cpp/src/mip/utilities/cpu_worker_thread.cuh | 2 +- cpp/src/routing/local_search/local_search.cu | 2 +- cpp/src/routing/local_search/sliding_tsp.cu | 2 +- cpp/src/routing/local_search/sliding_window.cu | 2 +- cpp/src/routing/local_search/two_opt.cu | 2 +- cpp/src/routing/local_search/vrp/vrp_execute.cu | 2 +- cpp/tests/distance_engine/waypoint_matrix_test.cpp | 2 +- cpp/tests/linear_programming/c_api_tests/c_api_test.c | 2 +- cpp/tests/routing/level0/l0_ges_test.cu | 2 +- cpp/tests/routing/level0/l0_objective_function_test.cu | 2 +- cpp/tests/routing/level0/l0_routing_test.cu | 2 +- cpp/tests/routing/level0/l0_vehicle_order_match.cu | 2 +- cpp/tests/routing/level0/l0_vehicle_types_test.cu | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/build.sh b/build.sh index 3f1a2c1f5b..1ee8e87fc3 100755 --- a/build.sh +++ b/build.sh @@ -1,6 +1,6 @@ #!/bin/bash -# 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 diff --git a/cpp/include/cuopt/error.hpp b/cpp/include/cuopt/error.hpp index a83413515e..9dd547adbb 100644 --- a/cpp/include/cuopt/error.hpp +++ b/cpp/include/cuopt/error.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * 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 */ /* clang-format on */ diff --git a/cpp/include/cuopt/linear_programming/utilities/internals.hpp b/cpp/include/cuopt/linear_programming/utilities/internals.hpp index 86e7246faf..90d856b237 100644 --- a/cpp/include/cuopt/linear_programming/utilities/internals.hpp +++ b/cpp/include/cuopt/linear_programming/utilities/internals.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/dual_simplex/barrier.cu b/cpp/src/dual_simplex/barrier.cu index 4bf3e3ed70..43334a2659 100644 --- a/cpp/src/dual_simplex/barrier.cu +++ b/cpp/src/dual_simplex/barrier.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/linear_programming/pdlp.cu b/cpp/src/linear_programming/pdlp.cu index 545791e2e2..ae298310c7 100644 --- a/cpp/src/linear_programming/pdlp.cu +++ b/cpp/src/linear_programming/pdlp.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/linear_programming/utilities/cython_solve.cu b/cpp/src/linear_programming/utilities/cython_solve.cu index 3370ac663e..0e1dbc6aff 100644 --- a/cpp/src/linear_programming/utilities/cython_solve.cu +++ b/cpp/src/linear_programming/utilities/cython_solve.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/linear_programming/utils.cuh b/cpp/src/linear_programming/utils.cuh index 2333283f39..18c023e5db 100644 --- a/cpp/src/linear_programming/utils.cuh +++ b/cpp/src/linear_programming/utils.cuh @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/mip/presolve/gf2_presolve.hpp b/cpp/src/mip/presolve/gf2_presolve.hpp index 53e79d2c93..623de4be4a 100644 --- a/cpp/src/mip/presolve/gf2_presolve.hpp +++ b/cpp/src/mip/presolve/gf2_presolve.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/mip/presolve/third_party_presolve.cpp b/cpp/src/mip/presolve/third_party_presolve.cpp index faba99ca18..366967de3e 100644 --- a/cpp/src/mip/presolve/third_party_presolve.cpp +++ b/cpp/src/mip/presolve/third_party_presolve.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/mip/utilities/cpu_worker_thread.cuh b/cpp/src/mip/utilities/cpu_worker_thread.cuh index e437486cda..60bd5685b9 100644 --- a/cpp/src/mip/utilities/cpu_worker_thread.cuh +++ b/cpp/src/mip/utilities/cpu_worker_thread.cuh @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights * reserved. SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/cpp/src/routing/local_search/local_search.cu b/cpp/src/routing/local_search/local_search.cu index 795d1fb35c..c889d9963c 100644 --- a/cpp/src/routing/local_search/local_search.cu +++ b/cpp/src/routing/local_search/local_search.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/routing/local_search/sliding_tsp.cu b/cpp/src/routing/local_search/sliding_tsp.cu index 6da1169a38..c7923c361a 100644 --- a/cpp/src/routing/local_search/sliding_tsp.cu +++ b/cpp/src/routing/local_search/sliding_tsp.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/routing/local_search/sliding_window.cu b/cpp/src/routing/local_search/sliding_window.cu index fd5ef8500e..2d676d9b38 100644 --- a/cpp/src/routing/local_search/sliding_window.cu +++ b/cpp/src/routing/local_search/sliding_window.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/routing/local_search/two_opt.cu b/cpp/src/routing/local_search/two_opt.cu index ff345312af..abe6e8a928 100644 --- a/cpp/src/routing/local_search/two_opt.cu +++ b/cpp/src/routing/local_search/two_opt.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/src/routing/local_search/vrp/vrp_execute.cu b/cpp/src/routing/local_search/vrp/vrp_execute.cu index ff560e1cca..5e417a9345 100644 --- a/cpp/src/routing/local_search/vrp/vrp_execute.cu +++ b/cpp/src/routing/local_search/vrp/vrp_execute.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/tests/distance_engine/waypoint_matrix_test.cpp b/cpp/tests/distance_engine/waypoint_matrix_test.cpp index 1f9bacf0ec..2db3953c2f 100644 --- a/cpp/tests/distance_engine/waypoint_matrix_test.cpp +++ b/cpp/tests/distance_engine/waypoint_matrix_test.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/tests/linear_programming/c_api_tests/c_api_test.c b/cpp/tests/linear_programming/c_api_tests/c_api_test.c index 7f2dd8322b..52be9e16f1 100644 --- a/cpp/tests/linear_programming/c_api_tests/c_api_test.c +++ b/cpp/tests/linear_programming/c_api_tests/c_api_test.c @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/tests/routing/level0/l0_ges_test.cu b/cpp/tests/routing/level0/l0_ges_test.cu index afd0a26276..b3e72c56e6 100644 --- a/cpp/tests/routing/level0/l0_ges_test.cu +++ b/cpp/tests/routing/level0/l0_ges_test.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * 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 */ /* clang-format on */ diff --git a/cpp/tests/routing/level0/l0_objective_function_test.cu b/cpp/tests/routing/level0/l0_objective_function_test.cu index 4491398497..53217807c9 100644 --- a/cpp/tests/routing/level0/l0_objective_function_test.cu +++ b/cpp/tests/routing/level0/l0_objective_function_test.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/tests/routing/level0/l0_routing_test.cu b/cpp/tests/routing/level0/l0_routing_test.cu index 735b6e4bc1..e078a34995 100644 --- a/cpp/tests/routing/level0/l0_routing_test.cu +++ b/cpp/tests/routing/level0/l0_routing_test.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * 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 */ /* clang-format on */ diff --git a/cpp/tests/routing/level0/l0_vehicle_order_match.cu b/cpp/tests/routing/level0/l0_vehicle_order_match.cu index 4b1b9fdd37..782f93edcf 100644 --- a/cpp/tests/routing/level0/l0_vehicle_order_match.cu +++ b/cpp/tests/routing/level0/l0_vehicle_order_match.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ diff --git a/cpp/tests/routing/level0/l0_vehicle_types_test.cu b/cpp/tests/routing/level0/l0_vehicle_types_test.cu index 4e46d31d69..2fa3c559fe 100644 --- a/cpp/tests/routing/level0/l0_vehicle_types_test.cu +++ b/cpp/tests/routing/level0/l0_vehicle_types_test.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ From d89c50ab998faf498297aea4f2498e1c8d74e900 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 8 Jan 2026 09:29:58 +0000 Subject: [PATCH 110/225] flag to disable gpu heuristics --- cpp/src/dual_simplex/branch_and_bound.cpp | 69 +++++++++++++++++++++- cpp/src/mip/diversity/diversity_manager.cu | 18 +++++- cpp/src/mip/problem/problem.cu | 4 +- 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 80d239f2d2..75ac669302 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -1691,6 +1691,24 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } assert(root_vstatus_.size() == original_lp_.num_cols); + + // Validate root_vstatus_ has correct BASIC count + // This catches bugs where the root LP solve produces an invalid vstatus + { + const i_t expected_basic_count = original_lp_.num_rows; + i_t actual_basic_count = 0; + for (const auto& status : root_vstatus_) { + if (status == variable_status_t::BASIC) { actual_basic_count++; } + } + if (actual_basic_count != expected_basic_count) { + settings_.log.printf("ERROR: root_vstatus_ has %d BASIC entries, expected %d (num_rows)\n", + actual_basic_count, + expected_basic_count); + assert(actual_basic_count == expected_basic_count && + "root_vstatus_ BASIC count mismatch - LP solver returned invalid basis"); + } + } + set_uninitialized_steepest_edge_norms(edge_norms_); root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); @@ -2112,6 +2130,29 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t { raft::common::nvtx::range scope("BB::solve_node_bsp"); + // Validate vstatus has correct BASIC count before processing + // This helps diagnose heap overflow bugs where vstatus has too many BASIC entries + { + const i_t expected_basic_count = original_lp_.num_rows; + i_t actual_basic_count = 0; + for (const auto& status : node_ptr->vstatus) { + if (status == variable_status_t::BASIC) { actual_basic_count++; } + } + if (actual_basic_count != expected_basic_count) { + settings_.log.printf( + "ERROR: Node %d (final_id %d) vstatus has %d BASIC entries, expected %d (num_rows)\n", + node_ptr->node_id, + node_ptr->final_id, + actual_basic_count, + expected_basic_count); + settings_.log.printf(" vstatus.size() = %zu, num_cols = %d\n", + node_ptr->vstatus.size(), + original_lp_.num_cols); + assert(actual_basic_count == expected_basic_count && + "vstatus BASIC count mismatch - this indicates vstatus corruption"); + } + } + // Track work units at start (from work_context, which simplex solver updates) double work_units_at_start = worker.work_context.global_work_units_elapsed; double clock_at_start = worker.clock; @@ -2192,6 +2233,26 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t leaf_edge_norms, &worker.work_context); + // Validate vstatus after LP solve - check for corruption during simplex + { + const i_t expected_basic_count = original_lp_.num_rows; + i_t actual_basic_count = 0; + for (const auto& status : leaf_vstatus) { + if (status == variable_status_t::BASIC) { actual_basic_count++; } + } + if (actual_basic_count != expected_basic_count) { + settings_.log.printf( + "ERROR: After LP solve, node %d vstatus has %d BASIC entries, expected %d\n", + node_ptr->node_id, + actual_basic_count, + expected_basic_count); + settings_.log.printf(" lp_status = %d, recompute_basis = %d\n", + static_cast(lp_status), + worker.recompute_bounds_and_basis ? 1 : 0); + assert(actual_basic_count == expected_basic_count && "vstatus corrupted during LP solve"); + } + } + // Update worker clock with work performed // The simplex solver recorded work to work_context.global_work_units_elapsed // Compute the delta and advance the worker clock (but don't double-record to work_context) @@ -2340,7 +2401,13 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t up_child_id); exploration_stats_.nodes_unexplored += 2; - worker.recompute_bounds_and_basis = false; + + // In BSP mode, always recompute basis to ensure basic_list matches the node's vstatus. + // When recompute_bounds_and_basis = false, the simplex reuses basic_list from the previous + // solve, but each node has its own vstatus (copied at branch time). If basic_list and vstatus + // diverge, pivoting corrupts vstatus by adding BASIC entries without properly removing + // others. This is safe because the child's vstatus is a copy of the parent's final vstatus. + worker.recompute_bounds_and_basis = true; // Add children to worker's local queue (deterministic order: down first) worker.enqueue_node(node_ptr->get_down_child()); diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index 05a5ce0471..ef11de6495 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -17,6 +17,8 @@ #include +#include // for std::this_thread::sleep_for + constexpr bool fj_only_run = false; namespace cuopt::linear_programming::detail { @@ -303,6 +305,20 @@ solution_t diversity_manager_t::run_solver() context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC ? "deterministic" : "opportunistic"); + // Debug: Allow disabling GPU heuristics to test B&B tree determinism in isolation + const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); + if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { + CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); + // Initialize population minimally and wait for B&B to finish + population.initialize_population(); + population.allocate_solutions(); + // Wait for B&B to signal completion + while (!check_b_b_preemption()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return population.best_feasible(); + } + population.timer = timer; const f_t time_limit = timer.remaining_time(); const f_t lp_time_limit = diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 51fb8452a3..c8ee111760 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -1418,8 +1418,8 @@ problem_t problem_t::get_problem_after_fixing_vars( auto end_time = std::chrono::high_resolution_clock::now(); double time_taken = std::chrono::duration_cast(end_time - start_time).count(); - static double total_time_taken = 0.; - static int total_calls = 0; + [[maybe_unused]] static double total_time_taken = 0.; + [[maybe_unused]] static int total_calls = 0; total_time_taken += time_taken; total_calls++; // CUOPT_LOG_DEBUG( From d67f2301dbb189eccbae73ca58c61ae0cc750ceb Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 9 Jan 2026 08:31:51 +0000 Subject: [PATCH 111/225] BSP b&b progress --- cpp/src/dual_simplex/branch_and_bound.cpp | 174 +++++++++++++++--- cpp/src/dual_simplex/branch_and_bound.hpp | 13 +- cpp/src/dual_simplex/bsp_debug.hpp | 25 +++ cpp/src/dual_simplex/phase2.cpp | 15 +- cpp/src/mip/diversity/diversity_manager.cu | 1 + .../recombiners/bound_prop_recombiner.cuh | 4 +- cpp/src/mip/solver.cu | 6 +- 7 files changed, 193 insertions(+), 45 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 75ac669302..e6d71db4b0 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -1871,6 +1872,8 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t::run_bsp_coordinator(const csr_matrix_t settings_.absolute_mip_gap_tol && rel_gap > settings_.relative_mip_gap_tol && @@ -1922,12 +1929,6 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_treset_for_horizon(horizon_start, horizon_end); @@ -1963,9 +1964,54 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t state_data; + + // Global state + state_data.push_back(static_cast(bsp_next_final_id_)); + state_data.push_back(static_cast(exploration_stats_.nodes_explored)); + state_data.push_back(static_cast(exploration_stats_.nodes_unexplored)); + + // Upper/lower bounds (convert to fixed-point for exact comparison) + f_t ub = get_upper_bound(); + f_t lb = get_lower_bound(); + state_data.push_back(static_cast(ub * 1000000)); // 6 decimal places + state_data.push_back(static_cast(lb * 1000000)); + + // Worker queue contents (sorted by final_id for determinism) + for (const auto& worker : *bsp_workers_) { + // Hash paused node if any + if (worker.current_node != nullptr) { + state_data.push_back(worker.current_node->final_id >= 0 ? worker.current_node->final_id + : worker.current_node->node_id); + } + // Hash local queue + std::vector queue_fids; + for (const auto* node : worker.local_queue) { + queue_fids.push_back(node->final_id >= 0 ? node->final_id : node->node_id); + } + std::sort(queue_fids.begin(), queue_fids.end()); + for (int fid : queue_fids) { + state_data.push_back(fid); + } + } + + uint32_t hash = cuopt::mip::compute_hash(state_data); + BSP_DEBUG_LOG_HORIZON_HASH( + bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_end, hash); + } + BSP_DEBUG_EMIT_TREE_STATE(bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, @@ -2192,9 +2238,8 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t lp_settings.cut_off = get_upper_bound() + settings_.dual_tol; lp_settings.inside_mip = 2; lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time); - // Work limit is the remaining VT budget in this horizon - // Note: accumulated_vt tracks work already spent (for statistics), but doesn't increase budget - lp_settings.work_limit = current_horizon - worker.clock; + // Work limit is the ABSOLUTE VT at which to pause (LP solver compares against absolute elapsed) + lp_settings.work_limit = current_horizon; lp_settings.scale_columns = false; bool feasible = worker.node_presolver->bounds_strengthening( @@ -2599,26 +2644,8 @@ void branch_and_bound_t::process_history_and_sync( if (worker.current_node != nullptr) { apply_final_id(worker.current_node); } } - // Get current upper bound for pruning decisions - f_t upper_bound = get_upper_bound(); - - // Move ALL nodes from worker local queues to global heap for redistribution - // This ensures proper work distribution across workers each horizon - mutex_heap_.lock(); - for (auto& worker : *bsp_workers_) { - while (!worker.local_queue.empty()) { - mip_node_t* node = worker.local_queue.front(); - worker.local_queue.pop_front(); - // Only push non-pruned nodes - if (node->lower_bound < upper_bound) { - heap_.push(node); - } else { - search_tree_.update(node, node_status_t::FATHOMED); - --exploration_stats_.nodes_unexplored; - } - } - } - mutex_heap_.unlock(); + // Workers keep their local queues across horizons - no redistribution here. + // Load balancing is handled separately in balance_worker_loads() if needed. } template @@ -2651,6 +2678,95 @@ void branch_and_bound_t::prune_worker_nodes_vs_incumbent() } } +template +void branch_and_bound_t::balance_worker_loads() +{ + const size_t num_workers = bsp_workers_->size(); + if (num_workers <= 1) return; + + // Count work for each worker: current_node (if any) + local_queue size + std::vector work_counts(num_workers); + size_t total_work = 0; + size_t max_work = 0; + size_t min_work = std::numeric_limits::max(); + + for (size_t w = 0; w < num_workers; ++w) { + auto& worker = (*bsp_workers_)[w]; + work_counts[w] = worker.local_queue.size(); + if (worker.current_node != nullptr) { work_counts[w]++; } + total_work += work_counts[w]; + max_work = std::max(max_work, work_counts[w]); + min_work = std::min(min_work, work_counts[w]); + } + + // Check if we need to balance: significant imbalance = some worker has 0 work while others have + // 2+ Or max/min ratio is very high + bool needs_balance = + (min_work == 0 && max_work >= 2) || (min_work > 0 && max_work > 4 * min_work); + + if (!needs_balance) return; + + // Collect all redistributable nodes (from local queues only, not current_node which is paused) + std::vector*> all_nodes; + for (auto& worker : *bsp_workers_) { + while (!worker.local_queue.empty()) { + all_nodes.push_back(worker.local_queue.front()); + worker.local_queue.pop_front(); + } + } + + // Also pull nodes from global heap if workers need work + mutex_heap_.lock(); + f_t upper_bound = get_upper_bound(); + while (!heap_.empty() && all_nodes.size() < num_workers * 5) { + mip_node_t* node = heap_.top(); + heap_.pop(); + if (node->lower_bound < upper_bound) { + all_nodes.push_back(node); + } else { + search_tree_.update(node, node_status_t::FATHOMED); + --exploration_stats_.nodes_unexplored; + } + } + mutex_heap_.unlock(); + + if (all_nodes.empty()) return; + + // Sort by deterministic ID for consistent distribution + std::sort(all_nodes.begin(), + all_nodes.end(), + [](const mip_node_t* a, const mip_node_t* b) { + return a->get_deterministic_id() < b->get_deterministic_id(); + }); + + // Redistribute round-robin, but skip workers that have a paused current_node + // (they already have work and will resume that node first) + std::vector worker_order; + for (size_t w = 0; w < num_workers; ++w) { + // Prioritize workers without a paused node + if ((*bsp_workers_)[w].current_node == nullptr) { worker_order.push_back(w); } + } + for (size_t w = 0; w < num_workers; ++w) { + if ((*bsp_workers_)[w].current_node != nullptr) { worker_order.push_back(w); } + } + + // Distribute nodes + for (size_t i = 0; i < all_nodes.size(); ++i) { + size_t worker_idx = worker_order[i % num_workers]; + (*bsp_workers_)[worker_idx].enqueue_node(all_nodes[i]); + + // Debug: Log redistribution (happens at horizon END, at the sync point) + double vt = bsp_current_horizon_; + BSP_DEBUG_LOG_NODE_ASSIGNED(bsp_debug_settings_, + bsp_debug_logger_, + vt, + static_cast(worker_idx), + all_nodes[i]->node_id, + all_nodes[i]->final_id, + all_nodes[i]->lower_bound); + } +} + #ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE template class branch_and_bound_t; diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index b021e029fa..537a9cced9 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -357,6 +357,9 @@ class branch_and_bound_t { // Prune nodes held by workers based on new incumbent void prune_worker_nodes_vs_incumbent(); + // Balance worker loads - redistribute nodes only if significant imbalance detected + void balance_worker_loads(); + // BSP-specific node solving that records events node_solve_info_t solve_node_bsp(bb_worker_state_t& worker, mip_node_t* node_ptr, @@ -366,10 +369,10 @@ class branch_and_bound_t { private: // BSP state std::unique_ptr> bsp_workers_; - double bsp_horizon_step_{5.0}; // Virtual time step per horizon (tunable) - double bsp_current_horizon_{0.0}; // Current horizon target - bool bsp_mode_enabled_{false}; // Whether BSP mode is active - int bsp_horizon_number_{0}; // Current horizon number (for debugging) + double bsp_horizon_step_{5.0}; // Virtual time step per horizon (tunable) + double bsp_current_horizon_{0.0}; // Current horizon target + bool bsp_mode_enabled_{false}; // Whether BSP mode is active + int bsp_horizon_number_{0}; // Current horizon number (for debugging) // Counter for deterministic final_id assignment during sync phase // Starts at 2 (root's children are 1 and 2), incremented by 2 for each branch diff --git a/cpp/src/dual_simplex/bsp_debug.hpp b/cpp/src/dual_simplex/bsp_debug.hpp index a9bdb80689..bbab5070ed 100644 --- a/cpp/src/dual_simplex/bsp_debug.hpp +++ b/cpp/src/dual_simplex/bsp_debug.hpp @@ -37,6 +37,7 @@ namespace cuopt::linear_programming::dual_simplex { enum class bsp_log_event_t { HORIZON_START, // New horizon begins HORIZON_END, // Horizon completed + HORIZON_HASH, // Determinism fingerprint for horizon NODE_ASSIGNED, // Node assigned to worker NODE_SOLVE_START, // Worker starts solving node NODE_SOLVE_END, // Worker finishes node (with result type) @@ -60,6 +61,7 @@ inline const char* bsp_log_event_name(bsp_log_event_t event) switch (event) { case bsp_log_event_t::HORIZON_START: return "HORIZON_START"; case bsp_log_event_t::HORIZON_END: return "HORIZON_END"; + case bsp_log_event_t::HORIZON_HASH: return "HORIZON_HASH"; case bsp_log_event_t::NODE_ASSIGNED: return "NODE_ASSIGNED"; case bsp_log_event_t::NODE_SOLVE_START: return "NODE_SOLVE_START"; case bsp_log_event_t::NODE_SOLVE_END: return "NODE_SOLVE_END"; @@ -258,6 +260,24 @@ class bsp_debug_logger_t { if (settings_.enable_timeline) { emit_timeline_for_horizon(horizon_num); } } + // Log determinism fingerprint hash for the horizon + // This hash captures all state that should be identical across deterministic runs + void log_horizon_hash(int horizon_num, double vt, uint32_t hash) + { + std::lock_guard lock(mutex_); + + if (settings_.enable_event_log) { + std::stringstream ss; + ss << "hash=0x" << std::hex << std::setfill('0') << std::setw(8) << hash; + log_event_unlocked(vt, -1, bsp_log_event_t::HORIZON_HASH, -1, -1, ss.str()); + } + + if (settings_.enable_determinism_trace) { + trace_ss_ << "H" << horizon_num << ":HASH:0x" << std::hex << std::setfill('0') << std::setw(8) + << hash << std::dec << "\n"; + } + } + void log_node_assigned(double vt, int worker_id, i_t node_id, i_t final_id, f_t lower_bound) { if (settings_.enable_event_log) { @@ -859,6 +879,10 @@ class bsp_debug_logger_t { do { \ if ((settings).any_enabled()) (logger).log_horizon_end(h, vt); \ } while (0) +#define BSP_DEBUG_LOG_HORIZON_HASH(settings, logger, h, vt, hash) \ + do { \ + if ((settings).any_enabled()) (logger).log_horizon_hash(h, vt, hash); \ + } while (0) #define BSP_DEBUG_LOG_NODE_ASSIGNED(settings, logger, vt, w, nid, fid, lb) \ do { \ if ((settings).any_enabled()) (logger).log_node_assigned(vt, w, nid, fid, lb); \ @@ -946,6 +970,7 @@ class bsp_debug_logger_t { #define BSP_DEBUG_LOG_HORIZON_START(settings, logger, h, vs, ve) ((void)0) #define BSP_DEBUG_LOG_HORIZON_END(settings, logger, h, vt) ((void)0) +#define BSP_DEBUG_LOG_HORIZON_HASH(settings, logger, h, vt, hash) ((void)0) #define BSP_DEBUG_LOG_NODE_ASSIGNED(settings, logger, vt, w, nid, fid, lb) ((void)0) #define BSP_DEBUG_FLUSH_ASSIGN_TRACE(settings, logger) ((void)0) #define BSP_DEBUG_LOG_SOLVE_START(settings, logger, vt, w, nid, fid, wl, resumed) ((void)0) diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 876d905335..b6bffacc5b 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -2504,7 +2504,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, i_t remaining_iters = iter - last_feature_log_iter; if (remaining_iters <= 0) return; f_t prediction = predict_work_units(remaining_iters); - printf("DualSimplex determ (final): %d iters, predicted %.4f\n", remaining_iters, prediction); + // printf("DualSimplex determ (final): %d iters, predicted %.4f\n", remaining_iters, + // prediction); work_unit_context->record_work(prediction); }); @@ -3123,11 +3124,11 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, if (work_unit_context) { f_t prediction = predict_work_units(iters_elapsed); - printf("DualSimplex determ: %d iters, predicted %.4f, actual %.4f, error %.4f\n", - iters_elapsed, - prediction, - features.interval_runtime, - prediction - features.interval_runtime); + // printf("DualSimplex determ: %d iters, predicted %.4f, actual %.4f, error %.4f\n", + // iters_elapsed, + // prediction, + // features.interval_runtime, + // prediction - features.interval_runtime); work_unit_context->record_work(prediction); } diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index ef11de6495..bc6baa1dce 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -307,6 +307,7 @@ solution_t diversity_manager_t::run_solver() // Debug: Allow disabling GPU heuristics to test B&B tree determinism in isolation const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); + disable_heuristics_env = "1"; if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); // Initialize population minimally and wait for B&B to finish diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index 65a9550ccd..3d217a2a53 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -245,8 +245,8 @@ class bound_prop_recombiner_t : public recombiner_t { // CUOPT_LOG_ERROR("Excess: %g, %g, %g, %g, feas %d", offspring.get_total_excess(), // offspring.compute_max_constraint_violation(), offspring.compute_max_int_violation(), // offspring.compute_max_variable_violation(), feasible_after_unfix); - cuopt_assert(fabs(excess_after_unfix - excess_before) < 1e-6, - "Excess after unfix should be same as before unfix!"); + // cuopt_assert(fabs(excess_after_unfix - excess_before) < 1e-6, + // "Excess after unfix should be same as before unfix!"); } a.handle_ptr->sync_stream(); } else { diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index f121861641..9ba8fdbcf2 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -201,14 +201,16 @@ solution_t mip_solver_t::run_solver() branch_and_bound_settings.num_diving_threads = num_diving_threads; // Set the branch and bound -> primal heuristics callback + // heuristic_preemption_callback is needed in both modes to properly stop the heuristic thread + branch_and_bound_settings.heuristic_preemption_callback = std::bind( + &branch_and_bound_solution_helper_t::preempt_heuristic_solver, &solution_helper); + if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { branch_and_bound_settings.solution_callback = std::bind(&branch_and_bound_solution_helper_t::solution_callback, &solution_helper, std::placeholders::_1, std::placeholders::_2); - branch_and_bound_settings.heuristic_preemption_callback = std::bind( - &branch_and_bound_solution_helper_t::preempt_heuristic_solver, &solution_helper); branch_and_bound_settings.set_simplex_solution_callback = std::bind(&branch_and_bound_solution_helper_t::set_simplex_solution, From eb6bc28ef6900c5813529ef861763e322825f39f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 9 Jan 2026 16:51:22 +0000 Subject: [PATCH 112/225] initial working impl on small problens --- cpp/src/dual_simplex/bb_worker_state.hpp | 289 +++++++-- cpp/src/dual_simplex/bounds_strengthening.cpp | 6 +- cpp/src/dual_simplex/branch_and_bound.cpp | 566 ++++++++++++++---- cpp/src/dual_simplex/branch_and_bound.hpp | 4 - cpp/src/dual_simplex/bsp_debug.hpp | 180 +++++- cpp/src/dual_simplex/mip_node.hpp | 116 +++- cpp/src/dual_simplex/phase2.cpp | 2 - cpp/src/dual_simplex/pseudo_costs.hpp | 57 +- cpp/src/mip/diversity/diversity_manager.cu | 12 +- cpp/src/mip/problem/problem.cu | 4 + cpp/src/mip/utils.cuh | 29 +- cpp/src/utilities/hashing.hpp | 45 ++ 12 files changed, 1069 insertions(+), 241 deletions(-) create mode 100644 cpp/src/utilities/hashing.hpp diff --git a/cpp/src/dual_simplex/bb_worker_state.hpp b/cpp/src/dual_simplex/bb_worker_state.hpp index e759e5bee1..a8ea9476e6 100644 --- a/cpp/src/dual_simplex/bb_worker_state.hpp +++ b/cpp/src/dual_simplex/bb_worker_state.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -15,19 +15,38 @@ #include #include -#include +#include +#include #include +#include #include namespace cuopt::linear_programming::dual_simplex { +// Queued pseudo-cost update for BSP determinism +// Updates are collected during horizon, then applied in deterministic order at sync +template +struct pseudo_cost_update_t { + i_t variable; + rounding_direction_t direction; + f_t delta; // change_in_obj / frac + double vt; // virtual time when update occurred (for deterministic ordering) + int worker_id; // for tie-breaking in sort +}; + // Per-worker state for BSP (Bulk Synchronous Parallel) branch-and-bound template struct bb_worker_state_t { int worker_id{0}; - // Local node queue - buffer of nodes assigned to this worker for the current horizon - std::deque*> local_queue; + // Type alias for the BSP priority queue + using bsp_queue_t = std::priority_queue*, + std::vector*>, + bsp_node_compare_t>; + + // Local node queue - priority queue ordered by (lower_bound, origin_worker_id, creation_seq) + // Nodes are assigned BSP identity (origin_worker_id, creation_seq) when enqueued + bsp_queue_t local_queue; // Current node being processed (may be paused at horizon boundary) mip_node_t* current_node{nullptr}; @@ -35,6 +54,10 @@ struct bb_worker_state_t { // Worker's virtual time clock (cumulative work units) double clock{0.0}; + // Creation sequence counter - cumulative across horizons for unique identity + // Each node created by this worker gets (worker_id, next_creation_seq++) + int32_t next_creation_seq{0}; + // Events generated during this horizon bb_event_batch_t events; @@ -60,12 +83,49 @@ struct bb_worker_state_t { // Whether basis needs recomputation for next node bool recompute_bounds_and_basis{true}; - // Statistics + // Per-horizon statistics (reset each horizon) i_t nodes_processed_this_horizon{0}; double work_units_this_horizon{0.0}; + // Cumulative statistics (across all horizons) + i_t total_nodes_processed{0}; + i_t total_nodes_pruned{0}; + i_t total_nodes_branched{0}; + i_t total_nodes_infeasible{0}; + i_t total_integer_solutions{0}; + i_t total_nodes_assigned{0}; // via load balancing + double total_work_units{0.0}; + + // Timing statistics (in seconds) + double total_runtime{0.0}; // Total time spent doing actual work + double total_barrier_wait{0.0}; // Total time spent waiting at horizon sync barriers + double horizon_finish_time{ + 0.0}; // Timestamp when worker finished current horizon (for barrier wait calc) + + // Worker-local upper bound for BSP determinism (prevents cross-worker pruning races) + f_t local_upper_bound{std::numeric_limits::infinity()}; + + // Queued integer solutions found during this horizon (merged at sync) + struct queued_integer_solution_t { + f_t objective; + std::vector solution; + i_t depth; + }; + std::vector integer_solutions; + + // Queued pseudo-cost updates (applied in deterministic order at sync) + std::vector> pseudo_cost_updates; + + // Pseudo-cost snapshot for deterministic variable selection + // These are copied from global pseudo-costs at horizon start + std::vector pc_sum_up_snapshot; + std::vector pc_sum_down_snapshot; + std::vector pc_num_up_snapshot; + std::vector pc_num_down_snapshot; + // Constructor - explicit bb_worker_state_t(int id) : worker_id(id), work_context("BB_Worker_" + std::to_string(id)) + explicit bb_worker_state_t(int id) + : worker_id(id), work_context("BB_Worker_" + std::to_string(id)) { } @@ -80,7 +140,7 @@ struct bb_worker_state_t { leaf_problem = std::make_unique>(original_lp); // Initialize basis factors - const i_t m = leaf_problem->num_rows; + const i_t m = leaf_problem->num_rows; basis_factors = std::make_unique>(m, refactor_frequency); // Initialize bounds strengthening @@ -97,35 +157,136 @@ struct bb_worker_state_t { } // Reset for new horizon - void reset_for_horizon(double horizon_start, double horizon_end) + void reset_for_horizon(double horizon_start, double horizon_end, f_t global_upper_bound) { // Reset clock to horizon_start for consistent VT timestamps across workers clock = horizon_start; events.clear(); - events.horizon_start = horizon_start; - events.horizon_end = horizon_end; - event_sequence = 0; + events.horizon_start = horizon_start; + events.horizon_end = horizon_end; + event_sequence = 0; nodes_processed_this_horizon = 0; - work_units_this_horizon = 0.0; + work_units_this_horizon = 0.0; // Also sync work_context to match clock for consistent tracking work_context.global_work_units_elapsed = horizon_start; + // Note: next_creation_seq is NOT reset - it's cumulative for unique identity + + // Initialize worker-local upper bound from global (for BSP determinism) + local_upper_bound = global_upper_bound; + + // Clear queued updates from previous horizon + integer_solutions.clear(); + pseudo_cost_updates.clear(); } - // Add a node to the local queue - void enqueue_node(mip_node_t* node) { local_queue.push_back(node); } + // Queue a pseudo-cost update (to be applied at sync in deterministic order) + void queue_pseudo_cost_update(i_t variable, rounding_direction_t direction, f_t delta) + { + pseudo_cost_updates.push_back({variable, direction, delta, clock, worker_id}); + } - // Get next node to process + // Variable selection using snapshot (for BSP determinism) + // Returns the best variable to branch on based on pseudo-cost scores + i_t variable_selection_from_snapshot(const std::vector& fractional, + const std::vector& solution) const + { + const i_t num_fractional = fractional.size(); + if (num_fractional == 0) return -1; + + // Compute averages from snapshot + i_t num_initialized_down = 0; + i_t num_initialized_up = 0; + f_t pseudo_cost_down_avg = 0; + f_t pseudo_cost_up_avg = 0; + + const i_t n = pc_sum_down_snapshot.size(); + for (i_t j = 0; j < n; ++j) { + if (pc_num_down_snapshot[j] > 0) { + ++num_initialized_down; + if (std::isfinite(pc_sum_down_snapshot[j])) { + pseudo_cost_down_avg += pc_sum_down_snapshot[j] / pc_num_down_snapshot[j]; + } + } + if (pc_num_up_snapshot[j] > 0) { + ++num_initialized_up; + if (std::isfinite(pc_sum_up_snapshot[j])) { + pseudo_cost_up_avg += pc_sum_up_snapshot[j] / pc_num_up_snapshot[j]; + } + } + } + if (num_initialized_down > 0) { + pseudo_cost_down_avg /= num_initialized_down; + } else { + pseudo_cost_down_avg = 1.0; + } + if (num_initialized_up > 0) { + pseudo_cost_up_avg /= num_initialized_up; + } else { + pseudo_cost_up_avg = 1.0; + } + + // Compute scores + std::vector score(num_fractional); + for (i_t k = 0; k < num_fractional; ++k) { + const i_t j = fractional[k]; + f_t pc_down = (pc_num_down_snapshot[j] != 0) + ? pc_sum_down_snapshot[j] / pc_num_down_snapshot[j] + : pseudo_cost_down_avg; + f_t pc_up = (pc_num_up_snapshot[j] != 0) ? pc_sum_up_snapshot[j] / pc_num_up_snapshot[j] + : pseudo_cost_up_avg; + constexpr f_t eps = 1e-6; + const f_t f_down = solution[j] - std::floor(solution[j]); + const f_t f_up = std::ceil(solution[j]) - solution[j]; + score[k] = std::max(f_down * pc_down, eps) * std::max(f_up * pc_up, eps); + } + + // Select variable with maximum score + i_t branch_var = fractional[0]; + f_t max_score = score[0]; + for (i_t k = 1; k < num_fractional; ++k) { + if (score[k] > max_score) { + max_score = score[k]; + branch_var = fractional[k]; + } + } + + return branch_var; + } + + // Add a node to the local queue, assigning BSP identity if not already set + // The tuple (origin_worker_id, creation_seq) uniquely identifies the node + void enqueue_node(mip_node_t* node) + { + // Assign BSP identity if not already set + // Nodes from load balancing keep their original identity + if (!node->has_bsp_identity()) { + node->origin_worker_id = worker_id; + node->creation_seq = next_creation_seq++; + } + local_queue.push(node); + } + + // Add a node that already has BSP identity (from load balancing or initial distribution) + // Does NOT modify the node's identity + void enqueue_node_with_identity(mip_node_t* node) + { + assert(node->has_bsp_identity() && + "Node must have BSP identity for enqueue_node_with_identity"); + local_queue.push(node); + } + + // Get next node to process (highest priority = lowest lower_bound) mip_node_t* dequeue_node() { if (current_node != nullptr) { // Resume paused node mip_node_t* node = current_node; - current_node = nullptr; + current_node = nullptr; return node; } if (local_queue.empty()) { return nullptr; } - mip_node_t* node = local_queue.front(); - local_queue.pop_front(); + mip_node_t* node = local_queue.top(); + local_queue.pop(); return node; } @@ -133,9 +294,37 @@ struct bb_worker_state_t { bool has_work() const { return current_node != nullptr || !local_queue.empty(); } // Get number of nodes in local queue (including paused node) - size_t queue_size() const + size_t queue_size() const { return local_queue.size() + (current_node != nullptr ? 1 : 0); } + + // Extract all nodes from queue (for load balancing) + // Returns nodes in arbitrary order - caller should sort if deterministic order needed + std::vector*> extract_all_nodes() + { + std::vector*> nodes; + nodes.reserve(queue_size()); + + // Include paused node if any + if (current_node != nullptr) { + nodes.push_back(current_node); + current_node = nullptr; + } + + // Extract all nodes from priority queue + while (!local_queue.empty()) { + nodes.push_back(local_queue.top()); + local_queue.pop(); + } + + return nodes; + } + + // Clear the queue without returning nodes (use with caution) + void clear_queue() { - return local_queue.size() + (current_node != nullptr ? 1 : 0); + current_node = nullptr; + while (!local_queue.empty()) { + local_queue.pop(); + } } // Record an event @@ -152,37 +341,37 @@ struct bb_worker_state_t { node->bsp_state = bsp_node_state_t::PAUSED; current_node = node; - record_event(bb_event_t::make_paused(clock, worker_id, node->node_id, 0, accumulated_vt)); + record_event( + bb_event_t::make_paused(clock, worker_id, node->node_id, 0, accumulated_vt)); } // Record node branching event - void record_branched(mip_node_t* node, - i_t down_child_id, - i_t up_child_id, - i_t branch_var, - f_t branch_val) + void record_branched( + mip_node_t* node, i_t down_child_id, i_t up_child_id, i_t branch_var, f_t branch_val) { record_event(bb_event_t::make_branched(clock, - worker_id, - node->node_id, - 0, - down_child_id, - up_child_id, - node->lower_bound, - branch_var, - branch_val)); + worker_id, + node->node_id, + 0, + down_child_id, + up_child_id, + node->lower_bound, + branch_var, + branch_val)); } // Record integer solution found void record_integer_solution(mip_node_t* node, f_t objective) { - record_event(bb_event_t::make_integer_solution(clock, worker_id, node->node_id, 0, objective)); + record_event( + bb_event_t::make_integer_solution(clock, worker_id, node->node_id, 0, objective)); } // Record node fathomed void record_fathomed(mip_node_t* node, f_t lower_bound) { - record_event(bb_event_t::make_fathomed(clock, worker_id, node->node_id, 0, lower_bound)); + record_event( + bb_event_t::make_fathomed(clock, worker_id, node->node_id, 0, lower_bound)); } // Record node infeasible @@ -202,8 +391,31 @@ struct bb_worker_state_t { { clock += work_units; work_units_this_horizon += work_units; + total_work_units += work_units; work_context.record_work(work_units); } + + // Track node processed (called when a node LP solve completes) + void track_node_processed() + { + ++nodes_processed_this_horizon; + ++total_nodes_processed; + } + + // Track node branched + void track_node_branched() { ++total_nodes_branched; } + + // Track node pruned (fathomed due to bound) + void track_node_pruned() { ++total_nodes_pruned; } + + // Track node infeasible + void track_node_infeasible() { ++total_nodes_infeasible; } + + // Track integer solution found + void track_integer_solution() { ++total_integer_solutions; } + + // Track node assigned via load balancing + void track_node_assigned() { ++total_nodes_assigned; } }; // Container for all worker states in BSP B&B @@ -237,10 +449,10 @@ class bb_worker_pool_t { int size() const { return static_cast(workers_.size()); } // Reset all workers for new horizon - void reset_for_horizon(double horizon_start, double horizon_end) + void reset_for_horizon(double horizon_start, double horizon_end, f_t global_upper_bound) { for (auto& worker : workers_) { - worker.reset_for_horizon(horizon_start, horizon_end); + worker.reset_for_horizon(horizon_start, horizon_end, global_upper_bound); } } @@ -288,4 +500,3 @@ class bb_worker_pool_t { }; } // namespace cuopt::linear_programming::dual_simplex - diff --git a/cpp/src/dual_simplex/bounds_strengthening.cpp b/cpp/src/dual_simplex/bounds_strengthening.cpp index f1bf52c1e3..d857d2f450 100644 --- a/cpp/src/dual_simplex/bounds_strengthening.cpp +++ b/cpp/src/dual_simplex/bounds_strengthening.cpp @@ -1,12 +1,14 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ #include +#include + #include #include @@ -98,6 +100,8 @@ bool bounds_strengthening_t::bounds_strengthening( const i_t m = A.m; const i_t n = A.n; + raft::common::nvtx::range fun_scope("bounds_strengthening"); + std::vector constraint_changed(m, true); std::vector variable_changed(n, false); std::vector constraint_changed_next(m, false); diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index e6d71db4b0..3d839e7a89 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include @@ -232,6 +232,9 @@ branch_and_bound_t::branch_and_bound_t( bsp_debug_settings_(bsp_debug_settings_t::from_environment()) { bsp_debug_settings_.enable_all(); + bsp_debug_settings_.output_dir = "/home/scratch.yboucher_gpu_1/bsp_debug/"; + bsp_debug_settings_.flush_every_horizon = false; + bsp_debug_settings_.disable_all(); exploration_stats_.start_time = tic(); dualize_info_t dualize_info; @@ -676,6 +679,14 @@ mip_status_t branch_and_bound_t::set_final_solution(mip_solution_t::solve(mip_solution_t& solut pc_); } + // Log strong branching results for determinism debugging + { + uint32_t sb_hash = pc_.compute_strong_branch_hash(); + uint32_t pc_hash = pc_.compute_state_hash(); + CUOPT_LOG_DEBUG("Strong branching completed: %zu variables, SB hash=0x%08x, PC hash=0x%08x", + fractional.size(), + sb_hash, + pc_hash); + + // Detailed logging for divergence diagnosis (enabled via environment variable) + const char* log_sb_detail = std::getenv("CUOPT_LOG_STRONG_BRANCHING"); + if (log_sb_detail != nullptr && std::string(log_sb_detail) == "1") { + settings_.log.printf("Strong branching detailed results:\n"); + for (size_t k = 0; k < fractional.size(); ++k) { + i_t var = fractional[k]; + settings_.log.printf(" var[%zu]=%d: down=%+.10e, up=%+.10e\n", + k, + var, + pc_.strong_branch_down[k], + pc_.strong_branch_up[k]); + } + settings_.log.printf("Pseudo-cost state after strong branching:\n"); + i_t non_zero_count = 0; + for (i_t j = 0; j < original_lp_.num_cols; ++j) { + if (pc_.pseudo_cost_num_down[j] > 0 || pc_.pseudo_cost_num_up[j] > 0) { + settings_.log.printf(" pc[%d]: sum_down=%+.10e, num_down=%d, sum_up=%+.10e, num_up=%d\n", + j, + pc_.pseudo_cost_sum_down[j], + pc_.pseudo_cost_num_down[j], + pc_.pseudo_cost_sum_up[j], + pc_.pseudo_cost_num_up[j]); + ++non_zero_count; + } + } + settings_.log.printf("Total %d variables with pseudo-cost data\n", non_zero_count); + } + } + if (toc(exploration_stats_.start_time) > settings_.time_limit) { solver_status_ = mip_exploration_status_t::TIME_LIMIT; return set_final_solution(solution, root_objective_); @@ -1897,10 +1946,11 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_tfinal_id = 1; - search_tree_.root.get_up_child()->final_id = 2; - bsp_next_final_id_ = 3; // Next ID to assign + // Set deterministic BSP identity for root children (pre-BSP origin with seq 0 and 1) + search_tree_.root.get_down_child()->origin_worker_id = -1; // Pre-BSP marker + search_tree_.root.get_down_child()->creation_seq = 0; + search_tree_.root.get_up_child()->origin_worker_id = -1; + search_tree_.root.get_up_child()->creation_seq = 1; heap_.push(search_tree_.root.get_down_child()); heap_.push(search_tree_.root.get_up_child()); @@ -1929,8 +1979,17 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_treset_for_horizon(horizon_start, horizon_end); + // Reset workers for new horizon with current global upper bound + // Each worker gets a snapshot of the upper bound for deterministic pruning + bsp_workers_->reset_for_horizon(horizon_start, horizon_end, get_upper_bound()); + + // Snapshot pseudo-costs for deterministic variable selection + for (auto& worker : *bsp_workers_) { + worker.pc_sum_up_snapshot = pc_.pseudo_cost_sum_up; + worker.pc_sum_down_snapshot = pc_.pseudo_cost_sum_down; + worker.pc_num_up_snapshot = pc_.pseudo_cost_num_up; + worker.pc_num_down_snapshot = pc_.pseudo_cost_num_down; + } // PHASE 2: PARALLEL EXECUTION - Workers run until horizon #pragma omp parallel num_threads(num_workers) @@ -1938,34 +1997,64 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t 0) { worker.total_barrier_wait += wait_time; } } // Aggregate worker work into global context for work limit tracking // The global work is the horizon boundary (all workers synchronized to this point) work_unit_context_.global_work_units_elapsed = horizon_end; + raft::common::nvtx::range scope("BB::bsp_coordinator::sync_phase"); + // PHASE 3: SYNCHRONIZATION - The Barrier // Collect and sort all events deterministically - bb_event_batch_t all_events = bsp_workers_->collect_and_sort_events(); + bb_event_batch_t all_events; + { + raft::common::nvtx::range scope("BB::bsp_coordinator::collect_and_sort_events"); + all_events = bsp_workers_->collect_and_sort_events(); + } // Debug: Log sync phase BSP_DEBUG_LOG_SYNC_PHASE_START( bsp_debug_settings_, bsp_debug_logger_, horizon_end, all_events.size()); // Process history and sync - process_history_and_sync(all_events); + { + raft::common::nvtx::range scope("BB::bsp_coordinator::process_history_and_sync"); + process_history_and_sync(all_events); + } - // Debug: Flush final IDs trace and log sync end - BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(bsp_debug_settings_, bsp_debug_logger_); + // Debug: Log sync end (no final_id assignment needed with BSP identity tuples) BSP_DEBUG_LOG_SYNC_PHASE_END(bsp_debug_settings_, bsp_debug_logger_, horizon_end); // Prune paused nodes that are now dominated by new incumbent - prune_worker_nodes_vs_incumbent(); + { + raft::common::nvtx::range scope("BB::bsp_coordinator::prune_worker_nodes_vs_incumbent"); + prune_worker_nodes_vs_incumbent(); + } // Balance worker loads if significant imbalance detected - balance_worker_loads(); + { + raft::common::nvtx::range scope("BB::bsp_coordinator::balance_worker_loads"); + balance_worker_loads(); + } BSP_DEBUG_FLUSH_ASSIGN_TRACE(bsp_debug_settings_, bsp_debug_logger_); // Debug: Log horizon end, emit tree state and JSON state @@ -1974,40 +2063,73 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t state_data; + std::vector state_data; // Global state - state_data.push_back(static_cast(bsp_next_final_id_)); - state_data.push_back(static_cast(exploration_stats_.nodes_explored)); - state_data.push_back(static_cast(exploration_stats_.nodes_unexplored)); + state_data.push_back(static_cast(exploration_stats_.nodes_explored)); + state_data.push_back(static_cast(exploration_stats_.nodes_unexplored)); // Upper/lower bounds (convert to fixed-point for exact comparison) f_t ub = get_upper_bound(); f_t lb = get_lower_bound(); - state_data.push_back(static_cast(ub * 1000000)); // 6 decimal places - state_data.push_back(static_cast(lb * 1000000)); - - // Worker queue contents (sorted by final_id for determinism) - for (const auto& worker : *bsp_workers_) { + state_data.push_back(static_cast(ub * 1000000)); // 6 decimal places + state_data.push_back(static_cast(lb * 1000000)); + + // Worker queue contents using BSP identity tuple (origin_worker_id, creation_seq) + // Each worker's queue is a priority queue - we extract nodes in priority order + // Note: BSP identity is always set for nodes in BSP mode + int nodes_without_identity = 0; + for (auto& worker : *bsp_workers_) { // Hash paused node if any if (worker.current_node != nullptr) { - state_data.push_back(worker.current_node->final_id >= 0 ? worker.current_node->final_id - : worker.current_node->node_id); - } - // Hash local queue - std::vector queue_fids; - for (const auto* node : worker.local_queue) { - queue_fids.push_back(node->final_id >= 0 ? node->final_id : node->node_id); + if (!worker.current_node->has_bsp_identity()) { + ++nodes_without_identity; + CUOPT_LOG_WARN( + "BSP Hash: Worker %d current_node has no BSP identity (node_id=%d, depth=%d)", + worker.worker_id, + worker.current_node->node_id, + worker.current_node->depth); + } + state_data.push_back(worker.current_node->get_bsp_identity_hash()); } - std::sort(queue_fids.begin(), queue_fids.end()); - for (int fid : queue_fids) { - state_data.push_back(fid); + + // Extract queue contents for hashing (preserves priority order) + // We need to temporarily extract to iterate, then restore + std::vector*> queue_nodes; + auto queue_copy = worker.local_queue; // Copy the priority queue + while (!queue_copy.empty()) { + auto* node = queue_copy.top(); + queue_copy.pop(); + if (!node->has_bsp_identity()) { + ++nodes_without_identity; + CUOPT_LOG_WARN( + "BSP Hash: Worker %d queue node has no BSP identity (node_id=%d, depth=%d)", + worker.worker_id, + node->node_id, + node->depth); + } + state_data.push_back(node->get_bsp_identity_hash()); } } + if (nodes_without_identity > 0) { + CUOPT_LOG_WARN( + "BSP Hash at horizon %d: %d nodes without BSP identity - HASH MAY BE " + "NON-DETERMINISTIC!", + bsp_horizon_number_, + nodes_without_identity); + } - uint32_t hash = cuopt::mip::compute_hash(state_data); + // Compute hash from state data + uint32_t hash = 0x811c9dc5u; // FNV-1a initial value + for (uint64_t val : state_data) { + hash ^= static_cast(val & 0xFFFFFFFF); + hash *= 0x01000193u; + hash ^= static_cast(val >> 32); + hash *= 0x01000193u; + } + CUOPT_LOG_DEBUG("BSP Hash at horizon %d: 0x%x", bsp_horizon_number_, hash); BSP_DEBUG_LOG_HORIZON_HASH( bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_end, hash); } @@ -2025,7 +2147,7 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t::run_bsp_coordinator(const csr_matrix_t::refill_worker_queues(i_t target_queue_size) } mutex_heap_.unlock(); - // Assign nodes to workers deterministically (round-robin by deterministic ID order) - // Use get_deterministic_id() which returns final_id if set, else node_id - std::sort(nodes_to_assign.begin(), - nodes_to_assign.end(), - [](const mip_node_t* a, const mip_node_t* b) { - return a->get_deterministic_id() < b->get_deterministic_id(); - }); + // Sort by BSP identity for deterministic distribution + // Uses lexicographic order of (origin_worker_id, creation_seq) + auto deterministic_less = [](const mip_node_t* a, const mip_node_t* b) { + // Lexicographic comparison of BSP identity tuple + if (a->origin_worker_id != b->origin_worker_id) { + return a->origin_worker_id < b->origin_worker_id; + } + return a->creation_seq < b->creation_seq; + }; + std::sort(nodes_to_assign.begin(), nodes_to_assign.end(), deterministic_less); for (size_t i = 0; i < nodes_to_assign.size(); ++i) { int worker_id = i % bsp_workers_->size(); auto* node = nodes_to_assign[i]; - (*bsp_workers_)[worker_id].enqueue_node(node); + // Use enqueue_node_with_identity since these nodes already have BSP identity from root setup + (*bsp_workers_)[worker_id].enqueue_node_with_identity(node); + (*bsp_workers_)[worker_id].track_node_assigned(); // Debug: Log node assignment double vt = bsp_current_horizon_ - bsp_horizon_step_; // Start of current horizon @@ -2128,7 +2279,7 @@ void branch_and_bound_t::refill_worker_queues(i_t target_queue_size) vt, worker_id, node->node_id, - node->final_id, + node->origin_worker_id, node->lower_bound); } } @@ -2149,6 +2300,7 @@ void branch_and_bound_t::run_worker_until_horizon(bb_worker_state_tlower_bound >= upper_bound) { worker.record_fathomed(node, node->lower_bound); + worker.track_node_pruned(); search_tree.update(node, node_status_t::FATHOMED); --exploration_stats_.nodes_unexplored; continue; @@ -2186,9 +2338,10 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t } if (actual_basic_count != expected_basic_count) { settings_.log.printf( - "ERROR: Node %d (final_id %d) vstatus has %d BASIC entries, expected %d (num_rows)\n", + "ERROR: Node %d (worker %d, seq %d) vstatus has %d BASIC entries, expected %d (num_rows)\n", node_ptr->node_id, - node_ptr->final_id, + node_ptr->origin_worker_id, + node_ptr->creation_seq, actual_basic_count, expected_basic_count); settings_.log.printf(" vstatus.size() = %zu, num_cols = %d\n", @@ -2204,14 +2357,14 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t double clock_at_start = worker.clock; bool is_resumed = (node_ptr->bsp_state == bsp_node_state_t::PAUSED); - // Debug: Log solve start + // Debug: Log solve start (pass origin_worker_id as identifier) double work_limit = current_horizon - worker.clock; BSP_DEBUG_LOG_SOLVE_START(bsp_debug_settings_, bsp_debug_logger_, worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, + node_ptr->origin_worker_id, work_limit, is_resumed); @@ -2235,20 +2388,25 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t // Bounds strengthening simplex_solver_settings_t lp_settings = settings_; lp_settings.set_log(false); - lp_settings.cut_off = get_upper_bound() + settings_.dual_tol; + // Use worker-local upper bound for LP cutoff (deterministic) + lp_settings.cut_off = worker.local_upper_bound + settings_.dual_tol; lp_settings.inside_mip = 2; lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time); // Work limit is the ABSOLUTE VT at which to pause (LP solver compares against absolute elapsed) lp_settings.work_limit = current_horizon; lp_settings.scale_columns = false; - bool feasible = worker.node_presolver->bounds_strengthening( - worker.leaf_problem->lower, worker.leaf_problem->upper, lp_settings); + bool feasible = true; + // TODO: incorporate into work unit estimation + // feasible = worker.node_presolver->bounds_strengthening( + // worker.leaf_problem->lower, worker.leaf_problem->upper, lp_settings); if (!feasible) { node_ptr->lower_bound = std::numeric_limits::infinity(); search_tree.update(node_ptr, node_status_t::INFEASIBLE); worker.record_infeasible(node_ptr); + worker.track_node_infeasible(); + worker.track_node_processed(); --exploration_stats_.nodes_unexplored; ++exploration_stats_.nodes_explored; worker.recompute_bounds_and_basis = true; @@ -2263,6 +2421,38 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t f_t lp_start_time = tic(); std::vector leaf_edge_norms = edge_norms_; + // Debug: Log LP input for determinism analysis (enabled via CUOPT_BSP_DEBUG_TRACE=1, + // log_level>=2) + if (bsp_debug_settings_.any_enabled()) { + uint64_t path_hash = node_ptr->compute_path_hash(); + // Compute vstatus hash + uint64_t vstatus_hash = leaf_vstatus.size(); + for (size_t i = 0; i < leaf_vstatus.size(); ++i) { + vstatus_hash ^= (static_cast(leaf_vstatus[i]) << (i % 56)); + vstatus_hash *= 0x100000001b3ULL; + } + // Compute bounds hash + uint64_t bounds_hash = 0; + for (i_t j = 0; j < worker.leaf_problem->num_cols; ++j) { + union { + f_t f; + uint64_t u; + } lb_bits, ub_bits; + lb_bits.f = worker.leaf_problem->lower[j]; + ub_bits.f = worker.leaf_problem->upper[j]; + bounds_hash ^= lb_bits.u + ub_bits.u; + bounds_hash *= 0x100000001b3ULL; + } + BSP_DEBUG_LOG_LP_INPUT(bsp_debug_settings_, + bsp_debug_logger_, + worker.worker_id, + node_ptr->node_id, + path_hash, + node_ptr->depth, + vstatus_hash, + bounds_hash); + } + dual::status_t lp_status = dual_phase2_with_advanced_basis(2, 0, worker.recompute_bounds_and_basis, @@ -2278,6 +2468,42 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t leaf_edge_norms, &worker.work_context); + // Debug: Log LP output for determinism analysis (enabled via CUOPT_BSP_DEBUG_TRACE=1, + // log_level>=2) + if (bsp_debug_settings_.any_enabled()) { + uint64_t path_hash = node_ptr->compute_path_hash(); + // Compute solution hash + uint64_t sol_hash = 0; + for (i_t j = 0; + j < worker.leaf_problem->num_cols && j < static_cast(leaf_solution.x.size()); + ++j) { + union { + f_t f; + uint64_t u; + } val_bits; + val_bits.f = leaf_solution.x[j]; + sol_hash ^= val_bits.u; + sol_hash *= 0x100000001b3ULL; + } + f_t obj = (lp_status == dual::status_t::OPTIMAL) + ? compute_objective(*worker.leaf_problem, leaf_solution.x) + : std::numeric_limits::infinity(); + union { + f_t f; + uint64_t u; + } obj_bits; + obj_bits.f = obj; + BSP_DEBUG_LOG_LP_OUTPUT(bsp_debug_settings_, + bsp_debug_logger_, + worker.worker_id, + node_ptr->node_id, + path_hash, + static_cast(lp_status), + node_iter, + obj_bits.u, + sol_hash); + } + // Validate vstatus after LP solve - check for corruption during simplex { const i_t expected_basic_count = original_lp_.num_rows; @@ -2318,7 +2544,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, + node_ptr->origin_worker_id, "PAUSED", node_ptr->lower_bound); BSP_DEBUG_LOG_PAUSED(bsp_debug_settings_, @@ -2326,7 +2552,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, + node_ptr->origin_worker_id, static_cast(accumulated_vt)); return node_solve_info_t::WORK_LIMIT; } @@ -2341,6 +2567,8 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t node_ptr->lower_bound = std::numeric_limits::infinity(); search_tree.update(node_ptr, node_status_t::INFEASIBLE); worker.record_infeasible(node_ptr); + worker.track_node_infeasible(); + worker.track_node_processed(); worker.recompute_bounds_and_basis = true; // Debug: Log solve end (infeasible) @@ -2349,7 +2577,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, + node_ptr->origin_worker_id, "INFEASIBLE", node_ptr->lower_bound); BSP_DEBUG_LOG_INFEASIBLE( @@ -2357,9 +2585,12 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t return node_solve_info_t::NO_CHILDREN; } else if (lp_status == dual::status_t::CUTOFF) { - node_ptr->lower_bound = get_upper_bound(); + // Use worker-local upper bound for determinism + node_ptr->lower_bound = worker.local_upper_bound; search_tree.update(node_ptr, node_status_t::FATHOMED); worker.record_fathomed(node_ptr, node_ptr->lower_bound); + worker.track_node_pruned(); + worker.track_node_processed(); worker.recompute_bounds_and_basis = true; // Debug: Log solve end (fathomed - cutoff) @@ -2368,7 +2599,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, + node_ptr->origin_worker_id, "FATHOMED", node_ptr->lower_bound); BSP_DEBUG_LOG_FATHOMED(bsp_debug_settings_, @@ -2386,14 +2617,48 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t f_t leaf_objective = compute_objective(*worker.leaf_problem, leaf_solution.x); node_ptr->lower_bound = leaf_objective; - pc_.update_pseudo_costs(node_ptr, leaf_objective); + + // Queue pseudo-cost update for deterministic application at sync + // (Replicates pc_.update_pseudo_costs logic but defers application to sync phase) + // Note: Original code also sets lower_bound before this, so change_in_obj = 0. + // This matches the original behavior exactly. + if (node_ptr->branch_var >= 0) { + const f_t change_in_obj = leaf_objective - node_ptr->lower_bound; + const f_t frac = node_ptr->branch_dir == rounding_direction_t::DOWN + ? node_ptr->fractional_val - std::floor(node_ptr->fractional_val) + : std::ceil(node_ptr->fractional_val) - node_ptr->fractional_val; + if (frac > 1e-10) { + worker.queue_pseudo_cost_update( + node_ptr->branch_var, node_ptr->branch_dir, change_in_obj / frac); + } + } if (leaf_num_fractional == 0) { - // Integer feasible - add_feasible_solution( - leaf_objective, leaf_solution.x, node_ptr->depth, thread_type_t::EXPLORATION); + // Integer feasible - queue for deterministic processing at sync + if (leaf_objective < worker.local_upper_bound) { + worker.local_upper_bound = leaf_objective; + worker.integer_solutions.push_back({leaf_objective, leaf_solution.x, node_ptr->depth}); + // Log immediately for visibility (global incumbent updated at sync) + i_t nodes_explored = exploration_stats_.nodes_explored.load(); + i_t nodes_unexplored = exploration_stats_.nodes_unexplored.load(); + f_t user_obj = compute_user_objective(original_lp_, leaf_objective); + f_t user_lower = compute_user_objective(original_lp_, get_lower_bound()); + settings_.log.printf( + "%s%10d %10lu %+13.6e %+10.6e %6d %7.1e %s %9.2f\n", + feasible_solution_symbol(thread_type_t::EXPLORATION), + nodes_explored, + nodes_unexplored, + user_obj, + user_lower, + node_ptr->depth, + nodes_explored > 0 ? exploration_stats_.total_lp_iters / nodes_explored : 0.0, + user_mip_gap(user_obj, user_lower).c_str(), + toc(exploration_stats_.start_time)); + } search_tree.update(node_ptr, node_status_t::INTEGER_FEASIBLE); worker.record_integer_solution(node_ptr, leaf_objective); + worker.track_integer_solution(); + worker.track_node_processed(); worker.recompute_bounds_and_basis = true; // Debug: Log solve end (integer) @@ -2402,7 +2667,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, + node_ptr->origin_worker_id, "INTEGER", leaf_objective); BSP_DEBUG_LOG_INTEGER(bsp_debug_settings_, @@ -2413,11 +2678,14 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t leaf_objective); return node_solve_info_t::NO_CHILDREN; - } else if (leaf_objective <= get_upper_bound() + settings_.absolute_mip_gap_tol / 10) { - // Branch + } else if (leaf_objective <= worker.local_upper_bound + settings_.absolute_mip_gap_tol / 10) { + // Branch - use worker-local upper bound for deterministic pruning decision + // Use pseudo-cost snapshot for deterministic variable selection + const i_t branch_var = + worker.variable_selection_from_snapshot(leaf_fractional, leaf_solution.x); + logger_t log; - log.log = false; - const i_t branch_var = pc_.variable_selection(leaf_fractional, leaf_solution.x, log); + log.log = false; search_tree.branch( node_ptr, branch_var, leaf_solution.x[branch_var], leaf_vstatus, *worker.leaf_problem, log); @@ -2427,6 +2695,8 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t i_t up_child_id = node_ptr->get_up_child()->node_id; worker.record_branched( node_ptr, down_child_id, up_child_id, branch_var, leaf_solution.x[branch_var]); + worker.track_node_branched(); + worker.track_node_processed(); // Debug: Log solve end (branched) and branched event BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, @@ -2434,7 +2704,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, + node_ptr->origin_worker_id, "BRANCH", leaf_objective); BSP_DEBUG_LOG_BRANCHED(bsp_debug_settings_, @@ -2442,6 +2712,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.clock, worker.worker_id, node_ptr->node_id, + node_ptr->origin_worker_id, down_child_id, up_child_id); @@ -2454,7 +2725,8 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t // others. This is safe because the child's vstatus is a copy of the parent's final vstatus. worker.recompute_bounds_and_basis = true; - // Add children to worker's local queue (deterministic order: down first) + // Add children directly to local queue - they get BSP identity on enqueue + // No need to defer to next horizon since identity is assigned immediately worker.enqueue_node(node_ptr->get_down_child()); worker.enqueue_node(node_ptr->get_up_child()); @@ -2465,6 +2737,8 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t } else { search_tree.update(node_ptr, node_status_t::FATHOMED); worker.record_fathomed(node_ptr, leaf_objective); + worker.track_node_pruned(); + worker.track_node_processed(); worker.recompute_bounds_and_basis = true; // Debug: Log solve end (fathomed by bound) @@ -2473,7 +2747,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.clock, worker.worker_id, node_ptr->node_id, - node_ptr->final_id, + node_ptr->origin_worker_id, "FATHOMED", leaf_objective); BSP_DEBUG_LOG_FATHOMED(bsp_debug_settings_, @@ -2501,6 +2775,10 @@ template void branch_and_bound_t::process_history_and_sync( const bb_event_batch_t& events) { + // With BSP identity tuples (origin_worker_id, creation_seq), we no longer need to assign + // final_ids during sync. Each node gets its identity when created, and it never changes. + // This function now only processes heuristic solutions to update the incumbent. + // Collect queued heuristic solutions std::vector heuristic_solutions; mutex_heuristic_queue_.lock(); @@ -2508,18 +2786,13 @@ void branch_and_bound_t::process_history_and_sync( heuristic_solution_queue_.clear(); mutex_heuristic_queue_.unlock(); - // Sort heuristic solutions by VT + // Sort heuristic solutions by VT for deterministic processing order std::sort(heuristic_solutions.begin(), heuristic_solutions.end(), [](const queued_heuristic_solution_t& a, const queued_heuristic_solution_t& b) { return a.vt_timestamp < b.vt_timestamp; }); - // Build mapping from node_id -> final_id for deterministic node ordering - // node_id is used as the provisional identifier during parallel execution; - // final_id is assigned deterministically during this sync phase based on event order. - std::unordered_map node_id_to_final_id; - // Merge B&B events and heuristic solutions for unified timeline replay // Both are sorted by VT, so we can do a merge-style iteration size_t event_idx = 0; @@ -2558,32 +2831,14 @@ void branch_and_bound_t::process_history_and_sync( break; } - case bb_event_type_t::NODE_BRANCHED: { - // Assign deterministic final IDs to the children - // Events are processed in sorted (deterministic) order, so this assignment is - // deterministic - i_t down_provisional = event.payload.branched.down_child_id; - i_t up_provisional = event.payload.branched.up_child_id; - - i_t down_final_id = bsp_next_final_id_++; - i_t up_final_id = bsp_next_final_id_++; - node_id_to_final_id[down_provisional] = down_final_id; - node_id_to_final_id[up_provisional] = up_final_id; - - // Debug: Log final ID assignments - BSP_DEBUG_LOG_FINAL_ID_ASSIGNED( - bsp_debug_settings_, bsp_debug_logger_, down_provisional, down_final_id); - BSP_DEBUG_LOG_FINAL_ID_ASSIGNED( - bsp_debug_settings_, bsp_debug_logger_, up_provisional, up_final_id); - break; - } - + case bb_event_type_t::NODE_BRANCHED: case bb_event_type_t::NODE_FATHOMED: case bb_event_type_t::NODE_INFEASIBLE: case bb_event_type_t::NODE_NUMERICAL: case bb_event_type_t::NODE_PAUSED: case bb_event_type_t::HEURISTIC_SOLUTION: // These events don't need additional processing during replay + // (BSP identity is already assigned at node creation time) break; } } @@ -2596,7 +2851,6 @@ void branch_and_bound_t::process_history_and_sync( bsp_debug_settings_, bsp_debug_logger_, hsol.vt_timestamp, hsol.objective); // Process heuristic solution at its correct VT position - // FIX: Release mutex before calling get_lower_bound() to avoid deadlock f_t new_upper = std::numeric_limits::infinity(); mutex_upper_.lock(); @@ -2627,25 +2881,68 @@ void branch_and_bound_t::process_history_and_sync( } } - // Apply final IDs to all nodes in worker queues and global heap - // Helper lambda to apply final_id mapping to a node - auto apply_final_id = [&node_id_to_final_id](mip_node_t* node) { - if (node->final_id < 0 && node->node_id > 0) { - auto it = node_id_to_final_id.find(node->node_id); - if (it != node_id_to_final_id.end()) { node->final_id = it->second; } - } + // Merge integer solutions from all workers and update global incumbent + // Sort by (objective, worker_id) for deterministic winner selection + struct worker_solution_t { + f_t objective; + const std::vector* solution; + i_t depth; + int worker_id; }; + std::vector all_integer_solutions; + for (auto& worker : *bsp_workers_) { + for (auto& sol : worker.integer_solutions) { + all_integer_solutions.push_back({sol.objective, &sol.solution, sol.depth, worker.worker_id}); + } + } + + // Sort by objective, then worker_id for deterministic tie-breaking + std::sort(all_integer_solutions.begin(), + all_integer_solutions.end(), + [](const worker_solution_t& a, const worker_solution_t& b) { + if (a.objective != b.objective) return a.objective < b.objective; + return a.worker_id < b.worker_id; + }); + + // Apply the best solution to global incumbent + if (!all_integer_solutions.empty()) { + const auto& best = all_integer_solutions[0]; + mutex_upper_.lock(); + if (best.objective < upper_bound_) { + upper_bound_ = best.objective; + incumbent_.set_incumbent_solution(best.objective, *best.solution); + } + mutex_upper_.unlock(); + } - // Apply to worker local queues + // Merge and apply pseudo-cost updates from all workers in deterministic order + std::vector> all_pc_updates; for (auto& worker : *bsp_workers_) { - for (auto* node : worker.local_queue) { - apply_final_id(node); + for (auto& upd : worker.pseudo_cost_updates) { + all_pc_updates.push_back(upd); + } + } + + // Sort by (vt, worker_id) for deterministic order + std::sort(all_pc_updates.begin(), + all_pc_updates.end(), + [](const pseudo_cost_update_t& a, const pseudo_cost_update_t& b) { + if (a.vt != b.vt) return a.vt < b.vt; + return a.worker_id < b.worker_id; + }); + + // Apply updates in deterministic order + for (const auto& upd : all_pc_updates) { + if (upd.direction == rounding_direction_t::DOWN) { + pc_.pseudo_cost_sum_down[upd.variable] += upd.delta; + pc_.pseudo_cost_num_down[upd.variable]++; + } else { + pc_.pseudo_cost_sum_up[upd.variable] += upd.delta; + pc_.pseudo_cost_num_up[upd.variable]++; } - if (worker.current_node != nullptr) { apply_final_id(worker.current_node); } } - // Workers keep their local queues across horizons - no redistribution here. - // Load balancing is handled separately in balance_worker_loads() if needed. + // No final_id application needed - BSP identity is assigned at node creation time } template @@ -2664,17 +2961,23 @@ void branch_and_bound_t::prune_worker_nodes_vs_incumbent() } } - // Check nodes in local queue - auto it = worker.local_queue.begin(); - while (it != worker.local_queue.end()) { - if ((*it)->lower_bound >= upper_bound) { - search_tree_.update(*it, node_status_t::FATHOMED); + // Check nodes in local queue - need to extract, filter, and rebuild + // since priority_queue doesn't support iteration + std::vector*> surviving_nodes; + while (!worker.local_queue.empty()) { + auto* node = worker.local_queue.top(); + worker.local_queue.pop(); + if (node->lower_bound >= upper_bound) { + search_tree_.update(node, node_status_t::FATHOMED); --exploration_stats_.nodes_unexplored; - it = worker.local_queue.erase(it); } else { - ++it; + surviving_nodes.push_back(node); } } + // Rebuild the queue with surviving nodes + for (auto* node : surviving_nodes) { + worker.local_queue.push(node); + } } } @@ -2692,8 +2995,7 @@ void branch_and_bound_t::balance_worker_loads() for (size_t w = 0; w < num_workers; ++w) { auto& worker = (*bsp_workers_)[w]; - work_counts[w] = worker.local_queue.size(); - if (worker.current_node != nullptr) { work_counts[w]++; } + work_counts[w] = worker.queue_size(); total_work += work_counts[w]; max_work = std::max(max_work, work_counts[w]); min_work = std::min(min_work, work_counts[w]); @@ -2706,12 +3008,13 @@ void branch_and_bound_t::balance_worker_loads() if (!needs_balance) return; - // Collect all redistributable nodes (from local queues only, not current_node which is paused) + // Collect all redistributable nodes from worker queues (excluding paused current_node) std::vector*> all_nodes; for (auto& worker : *bsp_workers_) { + // Extract all nodes from this worker's priority queue (not current_node) while (!worker.local_queue.empty()) { - all_nodes.push_back(worker.local_queue.front()); - worker.local_queue.pop_front(); + all_nodes.push_back(worker.local_queue.top()); + worker.local_queue.pop(); } } @@ -2732,12 +3035,16 @@ void branch_and_bound_t::balance_worker_loads() if (all_nodes.empty()) return; - // Sort by deterministic ID for consistent distribution - std::sort(all_nodes.begin(), - all_nodes.end(), - [](const mip_node_t* a, const mip_node_t* b) { - return a->get_deterministic_id() < b->get_deterministic_id(); - }); + // Sort by BSP identity for deterministic distribution + // Uses lexicographic order of (origin_worker_id, creation_seq) + auto deterministic_less = [](const mip_node_t* a, const mip_node_t* b) { + // Lexicographic comparison of BSP identity tuple + if (a->origin_worker_id != b->origin_worker_id) { + return a->origin_worker_id < b->origin_worker_id; + } + return a->creation_seq < b->creation_seq; + }; + std::sort(all_nodes.begin(), all_nodes.end(), deterministic_less); // Redistribute round-robin, but skip workers that have a paused current_node // (they already have work and will resume that node first) @@ -2750,10 +3057,11 @@ void branch_and_bound_t::balance_worker_loads() if ((*bsp_workers_)[w].current_node != nullptr) { worker_order.push_back(w); } } - // Distribute nodes + // Distribute nodes - use enqueue_node_with_identity to preserve existing identity for (size_t i = 0; i < all_nodes.size(); ++i) { size_t worker_idx = worker_order[i % num_workers]; - (*bsp_workers_)[worker_idx].enqueue_node(all_nodes[i]); + (*bsp_workers_)[worker_idx].enqueue_node_with_identity(all_nodes[i]); + (*bsp_workers_)[worker_idx].track_node_assigned(); // Debug: Log redistribution (happens at horizon END, at the sync point) double vt = bsp_current_horizon_; @@ -2762,7 +3070,7 @@ void branch_and_bound_t::balance_worker_loads() vt, static_cast(worker_idx), all_nodes[i]->node_id, - all_nodes[i]->final_id, + all_nodes[i]->origin_worker_id, all_nodes[i]->lower_bound); } } diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index 537a9cced9..b1f640a685 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -374,10 +374,6 @@ class branch_and_bound_t { bool bsp_mode_enabled_{false}; // Whether BSP mode is active int bsp_horizon_number_{0}; // Current horizon number (for debugging) - // Counter for deterministic final_id assignment during sync phase - // Starts at 2 (root's children are 1 and 2), incremented by 2 for each branch - i_t bsp_next_final_id_{3}; - // BSP heuristic solution queue - solutions received from GPU heuristics // Stored with VT timestamp for deterministic ordering struct queued_heuristic_solution_t { diff --git a/cpp/src/dual_simplex/bsp_debug.hpp b/cpp/src/dual_simplex/bsp_debug.hpp index bbab5070ed..ff50b0e5d0 100644 --- a/cpp/src/dual_simplex/bsp_debug.hpp +++ b/cpp/src/dual_simplex/bsp_debug.hpp @@ -54,6 +54,8 @@ enum class bsp_log_event_t { SYNC_PHASE_START, // Sync phase begins SYNC_PHASE_END, // Sync phase ends WORKER_IDLE, // Worker has no work + LP_INPUT, // LP solver input hashes (for determinism debugging) + LP_OUTPUT, // LP solver output hashes (for determinism debugging) }; inline const char* bsp_log_event_name(bsp_log_event_t event) @@ -78,6 +80,8 @@ inline const char* bsp_log_event_name(bsp_log_event_t event) case bsp_log_event_t::SYNC_PHASE_START: return "SYNC_PHASE_START"; case bsp_log_event_t::SYNC_PHASE_END: return "SYNC_PHASE_END"; case bsp_log_event_t::WORKER_IDLE: return "WORKER_IDLE"; + case bsp_log_event_t::LP_INPUT: return "LP_INPUT"; + case bsp_log_event_t::LP_OUTPUT: return "LP_OUTPUT"; default: return "UNKNOWN"; } } @@ -98,6 +102,7 @@ inline const char* bsp_log_event_name(bsp_log_event_t event) * CUOPT_BSP_DEBUG_ALL=1 Enable all debug output * CUOPT_BSP_DEBUG_DIR=path Output directory (default: ./bsp_debug/) * CUOPT_BSP_DEBUG_LEVEL=N Log level: 0=off, 1=major events, 2=all events + * CUOPT_BSP_DEBUG_FLUSH_EVERY_HORIZON=0 Disable flushing debug output every horizon (default: 1) */ struct bsp_debug_settings_t { bool enable_event_log{false}; @@ -105,6 +110,7 @@ struct bsp_debug_settings_t { bool enable_tree_dot{false}; bool enable_state_json{false}; bool enable_determinism_trace{false}; + bool flush_every_horizon{true}; // Flush debug output at end of each horizon std::string output_dir{"./bsp_debug/"}; int log_level{1}; // 0=off, 1=major events, 2=all events @@ -123,6 +129,15 @@ struct bsp_debug_settings_t { enable_determinism_trace = true; } + void disable_all() + { + enable_event_log = false; + enable_timeline = false; + enable_tree_dot = false; + enable_state_json = false; + enable_determinism_trace = false; + } + /** * @brief Initialize settings from environment variables. * @return A bsp_debug_settings_t populated from environment variables. @@ -167,6 +182,13 @@ struct bsp_debug_settings_t { settings.output_dir = get_env_string("CUOPT_BSP_DEBUG_DIR", "./bsp_debug/"); settings.log_level = get_env_int("CUOPT_BSP_DEBUG_LEVEL", 1); + // Flush every horizon is ON by default; set to 0 to disable + const char* flush_env = std::getenv("CUOPT_BSP_DEBUG_FLUSH_EVERY_HORIZON"); + if (flush_env != nullptr) { + settings.flush_every_horizon = + (std::string(flush_env) == "1" || std::string(flush_env) == "true"); + } + return settings; } }; @@ -353,7 +375,7 @@ class bsp_debug_logger_t { } } - void log_branched(double vt, int worker_id, i_t parent_id, i_t down_id, i_t up_id) + void log_branched(double vt, int worker_id, i_t parent_id, i_t parent_fid, i_t down_id, i_t up_id) { std::lock_guard lock(mutex_); @@ -362,12 +384,13 @@ class bsp_debug_logger_t { worker_id, bsp_log_event_t::NODE_BRANCHED, parent_id, - -1, + parent_fid, "children=" + std::to_string(down_id) + "," + std::to_string(up_id)); } if (settings_.enable_determinism_trace) { - events_trace_ss_ << "BRANCH(" << vt << ",W" << worker_id << "," << parent_id << "->" + // Log parent final_id (deterministic) and children node_ids (non-deterministic until sync) + events_trace_ss_ << "BRANCH(" << vt << ",W" << worker_id << ",F" << parent_fid << "->" << down_id << "," << up_id << "),"; } } @@ -533,6 +556,104 @@ class bsp_debug_logger_t { } } + // ======================================================================== + // LP Determinism Logging (for debugging non-determinism in LP solver) + // ======================================================================== + + void log_lp_input(int worker_id, + i_t node_id, + uint64_t path_hash, + i_t depth, + uint64_t vstatus_hash, + uint64_t bounds_hash) + { + std::lock_guard lock(mutex_); + + // Log to main events file (always when BSP debug is enabled) + if (settings_.enable_event_log) { + char details[320]; + std::snprintf(details, + sizeof(details), + "path=0x%016llx,d=%d,vstatus=0x%016llx,bounds=0x%016llx", + static_cast(path_hash), + depth, + static_cast(vstatus_hash), + static_cast(bounds_hash)); + log_event_unlocked(-1.0, worker_id, bsp_log_event_t::LP_INPUT, node_id, -1, details); + } + + // Also log to determinism trace if enabled + if (settings_.enable_determinism_trace) { + char buf[320]; + std::snprintf(buf, + sizeof(buf), + "LP_IN(W%d,N%d,path=0x%016llx,d=%d,vs=0x%016llx,bnd=0x%016llx)", + worker_id, + node_id, + static_cast(path_hash), + depth, + static_cast(vstatus_hash), + static_cast(bounds_hash)); + events_trace_ss_ << buf << ","; + } + } + + void log_lp_output(int worker_id, + i_t node_id, + uint64_t path_hash, + int status, + i_t iters, + uint64_t obj_bits, + uint64_t sol_hash) + { + std::lock_guard lock(mutex_); + + // Log to main events file (always when BSP debug is enabled) + if (settings_.enable_event_log) { + char details[320]; + std::snprintf(details, + sizeof(details), + "path=0x%016llx,status=%d,iters=%d,obj=0x%016llx,sol=0x%016llx", + static_cast(path_hash), + status, + iters, + static_cast(obj_bits), + static_cast(sol_hash)); + log_event_unlocked(-1.0, worker_id, bsp_log_event_t::LP_OUTPUT, node_id, -1, details); + } + + // Also log to determinism trace if enabled + if (settings_.enable_determinism_trace) { + char buf[320]; + std::snprintf(buf, + sizeof(buf), + "LP_OUT(W%d,N%d,path=0x%016llx,st=%d,it=%d,obj=0x%016llx,sol=0x%016llx)", + worker_id, + node_id, + static_cast(path_hash), + status, + iters, + static_cast(obj_bits), + static_cast(sol_hash)); + events_trace_ss_ << buf << ","; + } + } + + void log_branch_decision(i_t branch_var, uint64_t score_hash, size_t num_ties) + { + if (settings_.enable_determinism_trace && settings_.log_level >= 2) { + std::lock_guard lock(mutex_); + char buf[128]; + std::snprintf(buf, + sizeof(buf), + "BVAR(v=%d,sc=0x%016llx,ties=%zu)", + branch_var, + static_cast(score_hash), + num_ties); + events_trace_ss_ << buf << ","; + } + } + // ======================================================================== // Tree State (DOT format) // ======================================================================== @@ -600,7 +721,8 @@ class bsp_debug_logger_t { file << " \"clock\": " << w.clock << ",\n"; if (w.current_node != nullptr) { file << " \"current_node\": {\"id\": " << w.current_node->node_id - << ", \"final_id\": " << w.current_node->final_id + << ", \"origin_worker\": " << w.current_node->origin_worker_id + << ", \"creation_seq\": " << w.current_node->creation_seq << ", \"acc_vt\": " << w.current_node->accumulated_vt << "},\n"; } else { file << " \"current_node\": null,\n"; @@ -614,9 +736,9 @@ class bsp_debug_logger_t { file << " \"heap\": [\n"; for (size_t i = 0; i < heap_nodes.size(); ++i) { const auto* n = heap_nodes[i]; - file << " {\"id\": " << n->node_id << ", \"final_id\": " << n->final_id - << ", \"lb\": " << n->lower_bound << "}" << (i < heap_nodes.size() - 1 ? "," : "") - << "\n"; + file << " {\"id\": " << n->node_id << ", \"origin_worker\": " << n->origin_worker_id + << ", \"creation_seq\": " << n->creation_seq << ", \"lb\": " << n->lower_bound << "}" + << (i < heap_nodes.size() - 1 ? "," : "") << "\n"; } file << " ],\n"; @@ -836,7 +958,8 @@ class bsp_debug_logger_t { } file << " N" << node->node_id << " [label=\"N" << node->node_id; - if (node->final_id >= 0) file << " (fid=" << node->final_id << ")"; + if (node->has_bsp_identity()) + file << " (w" << node->origin_worker_id << ":" << node->creation_seq << ")"; file << "\\nlb=" << std::fixed << std::setprecision(1) << node->lower_bound; file << "\\n" << status_str; if (node->bsp_state == bsp_node_state_t::PAUSED) { @@ -887,9 +1010,9 @@ class bsp_debug_logger_t { do { \ if ((settings).any_enabled()) (logger).log_node_assigned(vt, w, nid, fid, lb); \ } while (0) -#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(settings, logger) \ - do { \ - if ((settings).any_enabled()) (logger).flush_assign_trace(); \ +#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(settings, logger) \ + do { \ + if ((settings).any_enabled() && (settings).flush_every_horizon) (logger).flush_assign_trace(); \ } while (0) #define BSP_DEBUG_LOG_SOLVE_START(settings, logger, vt, w, nid, fid, wl, resumed) \ do { \ @@ -899,9 +1022,9 @@ class bsp_debug_logger_t { do { \ if ((settings).any_enabled()) (logger).log_solve_end(vt, w, nid, fid, result, lb); \ } while (0) -#define BSP_DEBUG_LOG_BRANCHED(settings, logger, vt, w, pid, did, uid) \ - do { \ - if ((settings).any_enabled()) (logger).log_branched(vt, w, pid, did, uid); \ +#define BSP_DEBUG_LOG_BRANCHED(settings, logger, vt, w, pid, pfid, did, uid) \ + do { \ + if ((settings).any_enabled()) (logger).log_branched(vt, w, pid, pfid, did, uid); \ } while (0) #define BSP_DEBUG_LOG_PAUSED(settings, logger, vt, w, nid, fid, acc) \ do { \ @@ -937,7 +1060,8 @@ class bsp_debug_logger_t { } while (0) #define BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(settings, logger) \ do { \ - if ((settings).any_enabled()) (logger).flush_final_ids_trace(); \ + if ((settings).any_enabled() && (settings).flush_every_horizon) \ + (logger).flush_final_ids_trace(); \ } while (0) #define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(settings, logger, vt, obj) \ do { \ @@ -951,14 +1075,27 @@ class bsp_debug_logger_t { do { \ if ((settings).any_enabled()) (logger).log_heap_order(fids); \ } while (0) -#define BSP_DEBUG_EMIT_TREE_STATE(settings, logger, h, root, ub) \ - do { \ - if ((settings).any_enabled()) (logger).emit_tree_state(h, root, ub); \ +#define BSP_DEBUG_LOG_LP_INPUT(settings, logger, w, nid, ph, d, vsh, bh) \ + do { \ + if ((settings).any_enabled()) (logger).log_lp_input(w, nid, ph, d, vsh, bh); \ + } while (0) +#define BSP_DEBUG_LOG_LP_OUTPUT(settings, logger, w, nid, ph, st, it, ob, sh) \ + do { \ + if ((settings).any_enabled()) (logger).log_lp_output(w, nid, ph, st, it, ob, sh); \ + } while (0) +#define BSP_DEBUG_LOG_BRANCH_DECISION(settings, logger, bv, sh, nt) \ + do { \ + if ((settings).any_enabled()) (logger).log_branch_decision(bv, sh, nt); \ + } while (0) +#define BSP_DEBUG_EMIT_TREE_STATE(settings, logger, h, root, ub) \ + do { \ + if ((settings).any_enabled() && (settings).flush_every_horizon) \ + (logger).emit_tree_state(h, root, ub); \ } while (0) #define BSP_DEBUG_EMIT_STATE_JSON( \ settings, logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ do { \ - if ((settings).any_enabled()) \ + if ((settings).any_enabled() && (settings).flush_every_horizon) \ (logger).emit_state_json(h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events); \ } while (0) #define BSP_DEBUG_FINALIZE(settings, logger) \ @@ -975,7 +1112,7 @@ class bsp_debug_logger_t { #define BSP_DEBUG_FLUSH_ASSIGN_TRACE(settings, logger) ((void)0) #define BSP_DEBUG_LOG_SOLVE_START(settings, logger, vt, w, nid, fid, wl, resumed) ((void)0) #define BSP_DEBUG_LOG_SOLVE_END(settings, logger, vt, w, nid, fid, result, lb) ((void)0) -#define BSP_DEBUG_LOG_BRANCHED(settings, logger, vt, w, pid, did, uid) ((void)0) +#define BSP_DEBUG_LOG_BRANCHED(settings, logger, vt, w, pid, pfid, did, uid) ((void)0) #define BSP_DEBUG_LOG_PAUSED(settings, logger, vt, w, nid, fid, acc) ((void)0) #define BSP_DEBUG_LOG_INTEGER(settings, logger, vt, w, nid, obj) ((void)0) #define BSP_DEBUG_LOG_FATHOMED(settings, logger, vt, w, nid, lb) ((void)0) @@ -988,6 +1125,9 @@ class bsp_debug_logger_t { #define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(settings, logger, vt, obj) ((void)0) #define BSP_DEBUG_LOG_INCUMBENT_UPDATE(settings, logger, vt, obj, src) ((void)0) #define BSP_DEBUG_LOG_HEAP_ORDER(settings, logger, fids) ((void)0) +#define BSP_DEBUG_LOG_LP_INPUT(settings, logger, w, nid, ph, d, vsh, bh) ((void)0) +#define BSP_DEBUG_LOG_LP_OUTPUT(settings, logger, w, nid, ph, st, it, ob, sh) ((void)0) +#define BSP_DEBUG_LOG_BRANCH_DECISION(settings, logger, bv, sh, nt) ((void)0) #define BSP_DEBUG_EMIT_TREE_STATE(settings, logger, h, root, ub) ((void)0) #define BSP_DEBUG_EMIT_STATE_JSON( \ settings, logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ diff --git a/cpp/src/dual_simplex/mip_node.hpp b/cpp/src/dual_simplex/mip_node.hpp index bfea830e92..eb11f26e62 100644 --- a/cpp/src/dual_simplex/mip_node.hpp +++ b/cpp/src/dual_simplex/mip_node.hpp @@ -240,9 +240,10 @@ class mip_node_t { copy.fractional_val = fractional_val; copy.node_id = node_id; // Copy BSP fields - copy.accumulated_vt = accumulated_vt; - copy.bsp_state = bsp_state; - copy.final_id = final_id; + copy.accumulated_vt = accumulated_vt; + copy.bsp_state = bsp_state; + copy.origin_worker_id = origin_worker_id; + copy.creation_seq = creation_seq; return copy; } @@ -265,17 +266,54 @@ class mip_node_t { f_t accumulated_vt{0.0}; // Virtual time spent on this node so far bsp_node_state_t bsp_state{bsp_node_state_t::READY}; // BSP processing state - // For deterministic node ID assignment in BSP mode: - // - node_id is assigned non-deterministically during parallel execution (via atomic counter) - // - final_id is assigned deterministically during sync phase based on event order - // - Use final_id for sorting if set (>= 0), otherwise fall back to node_id - i_t final_id{-1}; + // Worker-local identification for deterministic BSP ordering: + // - origin_worker_id: which worker created this node (-1 for pre-BSP/initial nodes) + // - creation_seq: sequence number within that worker (cumulative across horizons) + // The tuple (origin_worker_id, creation_seq) is unique and stable (never changes after + // assignment) This replaces the old final_id approach which required sync-time assignment + int32_t origin_worker_id{-1}; + int32_t creation_seq{-1}; - // Get the ID to use for deterministic ordering - i_t get_deterministic_id() const + // Check if this node has been assigned a BSP identity + bool has_bsp_identity() const { return origin_worker_id >= -1 && creation_seq >= 0; } + + // Get a 64-bit identity value for hashing (combines worker_id and seq) + // Uses origin_worker_id + 1 to handle -1 (pre-BSP nodes) gracefully + uint64_t get_bsp_identity_hash() const + { + return (static_cast(origin_worker_id + 1) << 32) | + static_cast(static_cast(creation_seq)); + } + + // Compute a deterministic path hash based on branching decisions from root + // This uniquely identifies the node regardless of creation order + uint64_t compute_path_hash() const + { + uint64_t hash = 0; + const mip_node_t* node = this; + while (node != nullptr && node->branch_var >= 0) { + // Combine branch_var and branch_dir into hash + uint64_t step = static_cast(node->branch_var) << 1; + step |= (node->branch_dir == rounding_direction_t::UP) ? 1 : 0; + // FNV-1a style mixing + hash ^= step; + hash *= 0x100000001b3ULL; + node = node->parent; + } + return hash; + } + + // Get the branching path as a string for debugging + std::string get_path_string() const { - if (final_id >= 0) return final_id; - return node_id; + std::string path; + const mip_node_t* node = this; + while (node != nullptr && node->branch_var >= 0) { + char dir = (node->branch_dir == rounding_direction_t::UP) ? 'U' : 'D'; + path = std::to_string(node->branch_var) + dir + (path.empty() ? "" : "-") + path; + node = node->parent; + } + return path.empty() ? "root" : path; } }; @@ -289,23 +327,71 @@ void remove_fathomed_nodes(std::vector*>& stack) } } +// Comparator for global heap (used in non-BSP mode and for initial distribution in BSP) +// Uses path_hash for deterministic tie-breaking when BSP identity is not available template class node_compare_t { public: // Comparison for priority queue: returns true if 'a' has lower priority than 'b' // (elements with lower priority are output last from the heap). // Primary: prefer lower bound (best-first search) - // Tie-breaker: use deterministic ID for reproducibility across runs + // Tie-breaker: use deterministic comparison for reproducibility bool operator()(const mip_node_t& a, const mip_node_t& b) const { if (a.lower_bound != b.lower_bound) { return a.lower_bound > b.lower_bound; } - return a.get_deterministic_id() > b.get_deterministic_id(); + return deterministic_compare(a, b); } bool operator()(const mip_node_t* a, const mip_node_t* b) const { if (a->lower_bound != b->lower_bound) { return a->lower_bound > b->lower_bound; } - return a->get_deterministic_id() > b->get_deterministic_id(); + return deterministic_compare(*a, *b); + } + + private: + // Deterministic comparison using BSP identity tuple or path_hash fallback + bool deterministic_compare(const mip_node_t& a, const mip_node_t& b) const + { + assert(a.has_bsp_identity() && b.has_bsp_identity()); + // If both have BSP identity, use lexicographic comparison of (origin_worker_id, creation_seq) + if (a.has_bsp_identity() && b.has_bsp_identity()) { + if (a.origin_worker_id != b.origin_worker_id) { + return a.origin_worker_id > b.origin_worker_id; + } + return a.creation_seq > b.creation_seq; + } + + // If only one has BSP identity, prefer the one with identity (already established) + if (a.has_bsp_identity()) { return false; } // a has priority + if (b.has_bsp_identity()) { return true; } // b has priority + + // Neither has BSP identity - use path_hash for deterministic comparison (non-BSP mode) + uint64_t hash_a = a.compute_path_hash(); + uint64_t hash_b = b.compute_path_hash(); + if (hash_a != hash_b) { return hash_a > hash_b; } + + // Ultimate fallback: compare by depth (shouldn't happen with different paths) + return a.depth > b.depth; + } +}; + +// BSP-specific comparator for worker-local priority queues +// Uses (origin_worker_id, creation_seq) tuple for deterministic ordering within a worker +template +class bsp_node_compare_t { + public: + // Returns true if 'a' has lower priority than 'b' (for max-heap behavior) + bool operator()(const mip_node_t* a, const mip_node_t* b) const + { + // Primary: lower_bound (best-first search - prefer smaller bound) + if (a->lower_bound != b->lower_bound) { return a->lower_bound > b->lower_bound; } + + // Tie-breaker: lexicographic comparison of BSP identity tuple + // This is deterministic regardless of node creation order + if (a->origin_worker_id != b->origin_worker_id) { + return a->origin_worker_id > b->origin_worker_id; + } + return a->creation_seq > b->creation_seq; } }; diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index b6bffacc5b..2a8ef0c5f8 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -239,8 +239,6 @@ void initial_perturbation(const lp_problem_t& lp, const f_t dual_tol = settings.dual_tol; - std::srand(static_cast(std::time(nullptr))); - objective.resize(n); f_t sum_perturb = 0.0; i_t num_perturb = 0; diff --git a/cpp/src/dual_simplex/pseudo_costs.hpp b/cpp/src/dual_simplex/pseudo_costs.hpp index 799cdc3ff0..f5bb4badc8 100644 --- a/cpp/src/dual_simplex/pseudo_costs.hpp +++ b/cpp/src/dual_simplex/pseudo_costs.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -14,6 +14,8 @@ #include #include +#include +#include namespace cuopt::linear_programming::dual_simplex { @@ -49,6 +51,59 @@ class pseudo_costs_t { void update_pseudo_costs_from_strong_branching(const std::vector& fractional, const std::vector& root_soln); + + // Compute a deterministic hash of the pseudo-cost state for divergence detection + uint32_t compute_state_hash() const + { + uint32_t hash = 0x811c9dc5; // FNV-1a offset basis + auto hash_double = [&hash](double val) { + // Convert to fixed-point representation for exact comparison + int64_t fixed = static_cast(val * 1000000.0); + hash ^= static_cast(fixed & 0xFFFFFFFF); + hash *= 0x01000193; // FNV-1a prime + hash ^= static_cast((fixed >> 32) & 0xFFFFFFFF); + hash *= 0x01000193; + }; + auto hash_int = [&hash](i_t val) { + hash ^= static_cast(val); + hash *= 0x01000193; + }; + + // Hash pseudo-cost sums and counts + for (size_t j = 0; j < pseudo_cost_sum_down.size(); ++j) { + hash_double(pseudo_cost_sum_down[j]); + hash_double(pseudo_cost_sum_up[j]); + hash_int(pseudo_cost_num_down[j]); + hash_int(pseudo_cost_num_up[j]); + } + return hash; + } + + // Compute hash of strong branching results + uint32_t compute_strong_branch_hash() const + { + uint32_t hash = 0x811c9dc5; + auto hash_double = [&hash](double val) { + if (std::isnan(val)) { + hash ^= 0xDEADBEEF; // Special marker for NaN + } else if (std::isinf(val)) { + hash ^= val > 0 ? 0xCAFEBABE : 0xBADCAFE; // Inf markers + } else { + int64_t fixed = static_cast(val * 1000000.0); + hash ^= static_cast(fixed & 0xFFFFFFFF); + hash *= 0x01000193; + hash ^= static_cast((fixed >> 32) & 0xFFFFFFFF); + } + hash *= 0x01000193; + }; + + for (size_t k = 0; k < strong_branch_down.size(); ++k) { + hash_double(strong_branch_down[k]); + hash_double(strong_branch_up[k]); + } + return hash; + } + std::vector pseudo_cost_sum_up; std::vector pseudo_cost_sum_down; std::vector pseudo_cost_num_up; diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index bc6baa1dce..ea1381022c 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -189,6 +189,7 @@ bool diversity_manager_t::run_presolve(f_t time_limit) { raft::common::nvtx::range fun_scope("run_presolve"); CUOPT_LOG_INFO("Running presolve!"); + CUOPT_LOG_INFO("Problem fingerprint before DM presolve: 0x%x", problem_ptr->get_fingerprint()); work_limit_timer_t presolve_timer(context.gpu_heur_loop, time_limit); auto term_crit = ls.constraint_prop.bounds_update.solve(*problem_ptr); if (ls.constraint_prop.bounds_update.infeas_constraints_count > 0) { @@ -197,11 +198,17 @@ bool diversity_manager_t::run_presolve(f_t time_limit) } if (termination_criterion_t::NO_UPDATE != term_crit) { ls.constraint_prop.bounds_update.set_updated_bounds(*problem_ptr); + CUOPT_LOG_INFO("Problem fingerprint after cons prop presolve: 0x%x", + problem_ptr->get_fingerprint()); trivial_presolve(*problem_ptr); + CUOPT_LOG_INFO("Problem fingerprint after trivial presolve: 0x%x", + problem_ptr->get_fingerprint()); if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; } } // May overconstrain if Papilo presolve has been run before - if (!context.settings.presolve) { + // Skip conditional bound strengthening in deterministic mode - the knapsack kernel has + // race conditions where multiple blocks can update the same constraint non-deterministically. + if (!context.settings.presolve && context.settings.determinism_mode != CUOPT_MODE_DETERMINISTIC) { if (!problem_ptr->empty) { // do the resizing no-matter what, bounds presolve might not change the bounds but initial // trivial presolve might have @@ -220,6 +227,7 @@ bool diversity_manager_t::run_presolve(f_t time_limit) problem_ptr->n_constraints, problem_ptr->n_variables, problem_ptr->presolve_data.objective_offset); + CUOPT_LOG_INFO("Problem fingerprint after DM presolve: 0x%x", problem_ptr->get_fingerprint()); return true; } @@ -307,7 +315,7 @@ solution_t diversity_manager_t::run_solver() // Debug: Allow disabling GPU heuristics to test B&B tree determinism in isolation const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); - disable_heuristics_env = "1"; + disable_heuristics_env = "1"; // DO NOT REMOVE! intended debugging line! if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); // Initialize population minimally and wait for B&B to finish diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index c8ee111760..be6fc8ac6c 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -1799,6 +1799,10 @@ void problem_t::get_host_user_problem( user_problem.num_range_rows = user_problem.range_rows.size(); std::tie(user_problem.lower, user_problem.upper) = extract_host_bounds(variable_bounds, handle_ptr); + + // Debug: Log bounds fingerprint to detect GPU presolve non-determinism + CUOPT_LOG_INFO("Problem fingerprint after GPU presolve: 0x%x", get_fingerprint()); + user_problem.problem_name = original_problem_ptr->get_problem_name(); if (static_cast(row_names.size()) == m) { user_problem.row_names.resize(m); diff --git a/cpp/src/mip/utils.cuh b/cpp/src/mip/utils.cuh index 2a8de1899c..dc9e3f09d7 100644 --- a/cpp/src/mip/utils.cuh +++ b/cpp/src/mip/utils.cuh @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -29,34 +30,6 @@ constexpr int default_int_upper = std::numeric_limits::max(); constexpr int default_int_lower = std::numeric_limits::min(); constexpr double zero_bound = 0.; -template -inline uint32_t compute_hash(std::vector h_contents) -{ - // FNV-1a hash - - uint32_t hash = 2166136261u; // FNV-1a 32-bit offset basis - std::vector byte_contents(h_contents.size() * sizeof(i_t)); - std::memcpy(byte_contents.data(), h_contents.data(), h_contents.size() * sizeof(i_t)); - for (size_t i = 0; i < byte_contents.size(); ++i) { - hash ^= byte_contents[i]; - hash *= 16777619u; - } - return hash; -} - -template -HDI uint32_t compute_hash(const i_t val) -{ - uint32_t hash = 2166136261u; - uint8_t byte_contents[sizeof(i_t)]; - std::memcpy(byte_contents, &val, sizeof(i_t)); - for (size_t i = 0; i < sizeof(i_t); ++i) { - hash ^= byte_contents[i]; - hash *= 16777619u; - } - return hash; -} - template inline uint32_t compute_hash(raft::device_span values, rmm::cuda_stream_view stream = rmm::cuda_stream_default) diff --git a/cpp/src/utilities/hashing.hpp b/cpp/src/utilities/hashing.hpp new file mode 100644 index 0000000000..a1fa9220f9 --- /dev/null +++ b/cpp/src/utilities/hashing.hpp @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +namespace cuopt::linear_programming::detail { + +template +inline uint32_t compute_hash(std::vector h_contents) +{ + // FNV-1a hash + + uint32_t hash = 2166136261u; // FNV-1a 32-bit offset basis + std::vector byte_contents(h_contents.size() * sizeof(i_t)); + std::memcpy(byte_contents.data(), h_contents.data(), h_contents.size() * sizeof(i_t)); + for (size_t i = 0; i < byte_contents.size(); ++i) { + hash ^= byte_contents[i]; + hash *= 16777619u; + } + return hash; +} + +template +#ifdef __CUDA_ARCH__ +__device__ +#endif + inline uint32_t + compute_hash(const i_t val) +{ + uint32_t hash = 2166136261u; + uint8_t byte_contents[sizeof(i_t)]; + std::memcpy(byte_contents, &val, sizeof(i_t)); + for (size_t i = 0; i < sizeof(i_t); ++i) { + hash ^= byte_contents[i]; + hash *= 16777619u; + } + return hash; +} + +} // namespace cuopt::linear_programming::detail From d80667c1212378b10278fc7f738b606aebe0957a Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 10 Jan 2026 15:00:33 +0000 Subject: [PATCH 113/225] more instrumentation --- cpp/src/dual_simplex/bounds_strengthening.cpp | 81 ++++++++++++++----- cpp/src/dual_simplex/bounds_strengthening.hpp | 17 ++-- .../dual_simplex/dual_simplex_features.hpp | 59 +++++++++++++- cpp/src/dual_simplex/phase2.cpp | 5 ++ cpp/src/utilities/memory_instrumentation.hpp | 15 ++-- 5 files changed, 143 insertions(+), 34 deletions(-) diff --git a/cpp/src/dual_simplex/bounds_strengthening.cpp b/cpp/src/dual_simplex/bounds_strengthening.cpp index d857d2f450..c0cfd9646c 100644 --- a/cpp/src/dual_simplex/bounds_strengthening.cpp +++ b/cpp/src/dual_simplex/bounds_strengthening.cpp @@ -6,6 +6,9 @@ /* clang-format on */ #include +#include +#include +#include #include @@ -97,24 +100,48 @@ bool bounds_strengthening_t::bounds_strengthening( std::vector& upper_bounds, const simplex_solver_settings_t& settings) { - const i_t m = A.m; - const i_t n = A.n; + const i_t m = A.m; + const i_t n = A.n; + const i_t nnz = A.col_start[n]; raft::common::nvtx::range fun_scope("bounds_strengthening"); - - std::vector constraint_changed(m, true); - std::vector variable_changed(n, false); - std::vector constraint_changed_next(m, false); + f_t start_time = tic(); + + // Instrumented vectors for memory tracking (use uint8_t since std::vector is specialized) + cuopt::ins_vector constraint_changed(m, 1); + cuopt::ins_vector variable_changed(n, 0); + cuopt::ins_vector constraint_changed_next(m, 0); + + // Instrumentation manifold for this operation - includes A matrix (CSC and CSR) + cuopt::instrumentation_manifold_t manifold; + manifold.add("constraint_changed", constraint_changed); + manifold.add("variable_changed", variable_changed); + manifold.add("constraint_changed_next", constraint_changed_next); + // A matrix in CSC format (for column access) + manifold.add("A.col_start", A.col_start); + manifold.add("A.i", A.i); + manifold.add("A.x", A.x); + // A matrix in CSR format (for row access) + manifold.add("Arow.row_start", Arow.row_start); + manifold.add("Arow.j", Arow.j); + manifold.add("Arow.x", Arow.x); + // Member vectors for bounds and activity tracking + manifold.add("lower", lower); + manifold.add("upper", upper); + manifold.add("delta_min_activity", delta_min_activity); + manifold.add("delta_max_activity", delta_max_activity); + manifold.add("constraint_lb", constraint_lb); + manifold.add("constraint_ub", constraint_ub); if (!bounds_changed.empty()) { - std::fill(constraint_changed.begin(), constraint_changed.end(), false); + std::fill(constraint_changed.begin(), constraint_changed.end(), static_cast(0)); for (i_t i = 0; i < n; ++i) { if (bounds_changed[i]) { const i_t row_start = A.col_start[i]; const i_t row_end = A.col_start[i + 1]; for (i_t p = row_start; p < row_end; ++p) { const i_t j = A.i[p]; - constraint_changed[j] = true; + constraint_changed[j] = 1; } } } @@ -122,10 +149,11 @@ bool bounds_strengthening_t::bounds_strengthening( lower = lower_bounds; upper = upper_bounds; - print_bounds_stats(lower, upper, settings, "Initial bounds"); + print_bounds_stats(lower.underlying(), upper.underlying(), settings, "Initial bounds"); - i_t iter = 0; - const i_t iter_limit = 10; + i_t iter = 0; + const i_t iter_limit = 10; + i_t total_bounds_changed = 0; while (iter < iter_limit) { for (i_t i = 0; i < m; ++i) { if (!constraint_changed[i]) { continue; } @@ -138,7 +166,7 @@ bool bounds_strengthening_t::bounds_strengthening( const i_t j = Arow.j[p]; const f_t a_ij = Arow.x[p]; - variable_changed[j] = true; + variable_changed[j] = 1; if (a_ij > 0) { min_a += a_ij * lower[j]; max_a += a_ij * upper[j]; @@ -222,28 +250,45 @@ bool bounds_strengthening_t::bounds_strengthening( if (new_lb != old_lb || new_ub != old_ub) { for (i_t p = row_start; p < row_end; ++p) { const i_t i = A.i[p]; - constraint_changed_next[i] = true; + constraint_changed_next[i] = 1; } } lower[k] = std::min(new_lb, new_ub); upper[k] = std::max(new_lb, new_ub); - bool bounds_changed = lb_updated || ub_updated; - if (bounds_changed) { num_bounds_changed++; } + bool bounds_tightened = lb_updated || ub_updated; + if (bounds_tightened) { num_bounds_changed++; } } + total_bounds_changed += num_bounds_changed; if (num_bounds_changed == 0) { break; } std::swap(constraint_changed, constraint_changed_next); - std::fill(constraint_changed_next.begin(), constraint_changed_next.end(), false); - std::fill(variable_changed.begin(), variable_changed.end(), false); + std::fill( + constraint_changed_next.begin(), constraint_changed_next.end(), static_cast(0)); + std::fill(variable_changed.begin(), variable_changed.end(), static_cast(0)); iter++; } // settings.log.printf("Total strengthened variables %d\n", total_strengthened_variables); + // Log bounds strengthening features + { + auto [loads, stores] = manifold.collect_and_flush(); + bounds_strengthening_features_t bs_features; + bs_features.m = m; + bs_features.n = n; + bs_features.nnz = nnz; + bs_features.num_iterations = iter; + bs_features.num_bounds_changed = total_bounds_changed; + bs_features.byte_loads = loads; + bs_features.byte_stores = stores; + bs_features.runtime = toc(start_time); + bs_features.log_single(m, n, nnz); + } + #if DEBUG_BOUND_STRENGTHENING f_t lb_change = 0.0; f_t ub_change = 0.0; @@ -278,7 +323,7 @@ bool bounds_strengthening_t::bounds_strengthening( num_ub_changed, iter); } - print_bounds_stats(lower, upper, settings, "Final bounds"); + print_bounds_stats(lower.underlying(), upper.underlying(), settings, "Final bounds"); #endif lower_bounds = lower; diff --git a/cpp/src/dual_simplex/bounds_strengthening.hpp b/cpp/src/dual_simplex/bounds_strengthening.hpp index e7e218b824..679b732b52 100644 --- a/cpp/src/dual_simplex/bounds_strengthening.hpp +++ b/cpp/src/dual_simplex/bounds_strengthening.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -8,6 +8,7 @@ #pragma once #include +#include namespace cuopt::linear_programming::dual_simplex { @@ -31,12 +32,12 @@ class bounds_strengthening_t { const csr_matrix_t& Arow; const std::vector& var_types; - std::vector lower; - std::vector upper; - - std::vector delta_min_activity; - std::vector delta_max_activity; - std::vector constraint_lb; - std::vector constraint_ub; + // Instrumented vectors for memory tracking + cuopt::ins_vector lower; + cuopt::ins_vector upper; + cuopt::ins_vector delta_min_activity; + cuopt::ins_vector delta_max_activity; + cuopt::ins_vector constraint_lb; + cuopt::ins_vector constraint_ub; }; } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/dual_simplex_features.hpp b/cpp/src/dual_simplex/dual_simplex_features.hpp index 73e88970eb..49f2171c2b 100644 --- a/cpp/src/dual_simplex/dual_simplex_features.hpp +++ b/cpp/src/dual_simplex/dual_simplex_features.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -155,4 +155,61 @@ struct dual_simplex_features_t { // Feature logging interval (every N iterations) constexpr int FEATURE_LOG_INTERVAL = 100; +// Node bounds strengthening features (for B&B) +template +struct bounds_strengthening_features_t { + i_t m{0}; // number of constraints + i_t n{0}; // number of variables + i_t nnz{0}; // number of nonzeros in constraint matrix + i_t num_iterations{0}; // propagation iterations until fixpoint + i_t num_bounds_changed{0}; // total bounds tightened + size_t byte_loads{0}; + size_t byte_stores{0}; + f_t runtime{0.0}; + + // Interval aggregates (for when bounds strengthening is called multiple times) + i_t call_count{0}; + i_t total_iterations{0}; + i_t total_bounds_changed{0}; + size_t total_byte_loads{0}; + size_t total_byte_stores{0}; + f_t total_runtime{0.0}; + + void accumulate() + { + call_count++; + total_iterations += num_iterations; + total_bounds_changed += num_bounds_changed; + total_byte_loads += byte_loads; + total_byte_stores += byte_stores; + total_runtime += runtime; + } + + void log_single(i_t m_val, i_t n_val, i_t nnz_val) const + { + printf( + "BOUNDS_STRENGTH_FEATURES: m=%d n=%d nnz=%d " + "iterations=%d bounds_changed=%d " + "byte_loads=%zu byte_stores=%zu runtime=%.6f\n", + m_val, + n_val, + nnz_val, + num_iterations, + num_bounds_changed, + byte_loads, + byte_stores, + runtime); + } + + void reset() + { + call_count = 0; + total_iterations = 0; + total_bounds_changed = 0; + total_byte_loads = 0; + total_byte_stores = 0; + total_runtime = 0.0; + } +}; + } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 2a8ef0c5f8..4a81444576 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -2466,6 +2466,11 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, manifold.add("A_transpose.i", A_transpose.i); manifold.add("A_transpose.x", A_transpose.x); + // Add A matrix for entering column access during basis update + manifold.add("A.col_start", lp.A.col_start); + manifold.add("A.i", lp.A.i); + manifold.add("A.x", lp.A.x); + // Track iteration interval start time for runtime measurement f_t interval_start_time = toc(start_time); i_t last_feature_log_iter = iter; diff --git a/cpp/src/utilities/memory_instrumentation.hpp b/cpp/src/utilities/memory_instrumentation.hpp index ca8771abf8..abf09ae05b 100644 --- a/cpp/src/utilities/memory_instrumentation.hpp +++ b/cpp/src/utilities/memory_instrumentation.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -102,7 +102,8 @@ class instrumentation_manifold_t { // Construct with initializer list of (description, instrumented object) pairs instrumentation_manifold_t( std::initializer_list< - std::pair>> instrumented) + std::pair>> + instrumented) { for (const auto& [name, instr] : instrumented) { instrumented_.insert_or_assign(name, instr); @@ -110,9 +111,9 @@ class instrumentation_manifold_t { } // Add an instrumented object to track with a description - void add(const std::string& description, memory_instrumentation_base_t& instrumented) + void add(const std::string& description, const memory_instrumentation_base_t& instrumented) { - instrumented_.insert_or_assign(description, std::ref(instrumented)); + instrumented_.insert_or_assign(description, std::cref(instrumented)); } // Collect total loads and stores across all instrumented objects @@ -158,7 +159,7 @@ class instrumentation_manifold_t { } private: - std::unordered_map> + std::unordered_map> instrumented_; }; @@ -170,10 +171,10 @@ class instrumentation_manifold_t { instrumentation_manifold_t() = default; instrumentation_manifold_t( std::initializer_list< - std::pair>>) + std::pair>>) { } - void add(const std::string&, memory_instrumentation_base_t&) {} + void add(const std::string&, const memory_instrumentation_base_t&) {} std::pair collect() { return {0, 0}; } std::vector> collect_per_wrapper() { return {}; } std::pair collect_and_flush() { return {0, 0}; } From 8ed172a92722e5e3d491dd5da17ee38b60630912 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 12 Jan 2026 05:01:06 -0800 Subject: [PATCH 114/225] keep basis when possible --- cpp/src/dual_simplex/bb_worker_state.hpp | 27 +++- cpp/src/dual_simplex/branch_and_bound.cpp | 152 ++++++++++++++++------ cpp/src/dual_simplex/branch_and_bound.hpp | 3 + 3 files changed, 141 insertions(+), 41 deletions(-) diff --git a/cpp/src/dual_simplex/bb_worker_state.hpp b/cpp/src/dual_simplex/bb_worker_state.hpp index a8ea9476e6..22ba3d37a9 100644 --- a/cpp/src/dual_simplex/bb_worker_state.hpp +++ b/cpp/src/dual_simplex/bb_worker_state.hpp @@ -51,6 +51,10 @@ struct bb_worker_state_t { // Current node being processed (may be paused at horizon boundary) mip_node_t* current_node{nullptr}; + // Last node that was solved (for basis warm-start detection) + // If next node's parent == last_solved_node, we can reuse basis + mip_node_t* last_solved_node{nullptr}; + // Worker's virtual time clock (cumulative work units) double clock{0.0}; @@ -113,11 +117,15 @@ struct bb_worker_state_t { }; std::vector integer_solutions; - // Queued pseudo-cost updates (applied in deterministic order at sync) + // Queued pseudo-cost updates (applied to global pseudo-costs at sync) std::vector> pseudo_cost_updates; // Pseudo-cost snapshot for deterministic variable selection - // These are copied from global pseudo-costs at horizon start + // Initialized from global pseudo-costs at horizon start, then updated locally + // during the horizon as the worker processes nodes. This allows within-horizon + // learning while maintaining determinism (each worker's updates are sequential). + // At sync, all workers' updates are merged into global pseudo-costs, and new + // snapshots are taken at the next horizon start. std::vector pc_sum_up_snapshot; std::vector pc_sum_down_snapshot; std::vector pc_num_up_snapshot; @@ -179,10 +187,23 @@ struct bb_worker_state_t { pseudo_cost_updates.clear(); } - // Queue a pseudo-cost update (to be applied at sync in deterministic order) + // Queue a pseudo-cost update for global sync AND apply it to local snapshot immediately + // This allows within-horizon learning while maintaining determinism: + // - Local snapshot updates are sequential within each worker (deterministic) + // - Global updates are merged at sync in sorted (VT, worker_id) order (deterministic) void queue_pseudo_cost_update(i_t variable, rounding_direction_t direction, f_t delta) { + // Queue for global sync at horizon end pseudo_cost_updates.push_back({variable, direction, delta, clock, worker_id}); + + // Also apply to local snapshot immediately for better variable selection + if (direction == rounding_direction_t::DOWN) { + pc_sum_down_snapshot[variable] += delta; + pc_num_down_snapshot[variable]++; + } else { + pc_sum_up_snapshot[variable] += delta; + pc_num_up_snapshot[variable]++; + } } // Variable selection using snapshot (for BSP determinism) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 3d839e7a89..ef3b6d0b3d 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -2072,8 +2072,9 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t(exploration_stats_.nodes_unexplored)); // Upper/lower bounds (convert to fixed-point for exact comparison) + // Use compute_bsp_lower_bound() for accurate LB from all worker queues f_t ub = get_upper_bound(); - f_t lb = get_lower_bound(); + f_t lb = compute_bsp_lower_bound(); state_data.push_back(static_cast(ub * 1000000)); // 6 decimal places state_data.push_back(static_cast(lb * 1000000)); @@ -2149,7 +2150,7 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t::run_bsp_coordinator(const csr_matrix_t::run_worker_until_horizon(bb_worker_state_tbsp_state == bsp_node_state_t::PAUSED); + bool is_child = (node->parent == worker.last_solved_node); + bool can_warm_start = is_resumed || is_child; + worker.recompute_bounds_and_basis = !can_warm_start; + // Solve the node (this records events) node_solve_info_t status = solve_node_bsp(worker, node, search_tree, current_horizon); + // Track last solved node for warm-start detection + worker.last_solved_node = node; + // Handle result if (status == node_solve_info_t::TIME_LIMIT) { solver_status_ = mip_exploration_status_t::TIME_LIMIT; @@ -2638,22 +2652,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t if (leaf_objective < worker.local_upper_bound) { worker.local_upper_bound = leaf_objective; worker.integer_solutions.push_back({leaf_objective, leaf_solution.x, node_ptr->depth}); - // Log immediately for visibility (global incumbent updated at sync) - i_t nodes_explored = exploration_stats_.nodes_explored.load(); - i_t nodes_unexplored = exploration_stats_.nodes_unexplored.load(); - f_t user_obj = compute_user_objective(original_lp_, leaf_objective); - f_t user_lower = compute_user_objective(original_lp_, get_lower_bound()); - settings_.log.printf( - "%s%10d %10lu %+13.6e %+10.6e %6d %7.1e %s %9.2f\n", - feasible_solution_symbol(thread_type_t::EXPLORATION), - nodes_explored, - nodes_unexplored, - user_obj, - user_lower, - node_ptr->depth, - nodes_explored > 0 ? exploration_stats_.total_lp_iters / nodes_explored : 0.0, - user_mip_gap(user_obj, user_lower).c_str(), - toc(exploration_stats_.start_time)); + // Note: Logging deferred to sync phase for deterministic output } search_tree.update(node_ptr, node_status_t::INTEGER_FEASIBLE); worker.record_integer_solution(node_ptr, leaf_objective); @@ -2718,15 +2717,9 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t exploration_stats_.nodes_unexplored += 2; - // In BSP mode, always recompute basis to ensure basic_list matches the node's vstatus. - // When recompute_bounds_and_basis = false, the simplex reuses basic_list from the previous - // solve, but each node has its own vstatus (copied at branch time). If basic_list and vstatus - // diverge, pivoting corrupts vstatus by adding BASIC entries without properly removing - // others. This is safe because the child's vstatus is a copy of the parent's final vstatus. - worker.recompute_bounds_and_basis = true; - - // Add children directly to local queue - they get BSP identity on enqueue - // No need to defer to next horizon since identity is assigned immediately + // Add children to local queue - they get BSP identity on enqueue + // Note: recompute_bounds_and_basis is set in run_worker_until_horizon based on + // whether we branched (has_children), matching opportunistic mode behavior. worker.enqueue_node(node_ptr->get_down_child()); worker.enqueue_node(node_ptr->get_up_child()); @@ -2779,7 +2772,37 @@ void branch_and_bound_t::process_history_and_sync( // final_ids during sync. Each node gets its identity when created, and it never changes. // This function now only processes heuristic solutions to update the incumbent. - // Collect queued heuristic solutions + // Process repair queue first (for BSP mode) + // Infeasible solutions from GPU heuristics are queued for repair; process them now + { + std::vector> to_repair; + mutex_repair_.lock(); + if (repair_queue_.size() > 0) { + to_repair = repair_queue_; + repair_queue_.clear(); + } + mutex_repair_.unlock(); + + if (to_repair.size() > 0) { + settings_.log.debug("BSP sync: Attempting to repair %ld injected solutions\n", + to_repair.size()); + for (const std::vector& potential_solution : to_repair) { + std::vector repaired_solution; + f_t repaired_obj; + bool success = + repair_solution(edge_norms_, potential_solution, repaired_obj, repaired_solution); + if (success) { + // Queue repaired solution with VT = current horizon for deterministic processing + mutex_heuristic_queue_.lock(); + heuristic_solution_queue_.push_back( + {std::move(repaired_solution), repaired_obj, bsp_current_horizon_}); + mutex_heuristic_queue_.unlock(); + } + } + } + } + + // Collect queued heuristic solutions (including any newly repaired ones) std::vector heuristic_solutions; mutex_heuristic_queue_.lock(); heuristic_solutions = std::move(heuristic_solution_queue_); @@ -2904,15 +2927,39 @@ void branch_and_bound_t::process_history_and_sync( return a.worker_id < b.worker_id; }); - // Apply the best solution to global incumbent - if (!all_integer_solutions.empty()) { - const auto& best = all_integer_solutions[0]; - mutex_upper_.lock(); - if (best.objective < upper_bound_) { - upper_bound_ = best.objective; - incumbent_.set_incumbent_solution(best.objective, *best.solution); + // Apply the best solution to global incumbent and log all improving solutions + // Use compute_bsp_lower_bound() for accurate lower bound in logs + f_t bsp_lower = compute_bsp_lower_bound(); + f_t current_upper = get_upper_bound(); + + for (const auto& sol : all_integer_solutions) { + if (sol.objective < current_upper) { + // Log this improving solution (deterministic: sorted order) + f_t user_obj = compute_user_objective(original_lp_, sol.objective); + f_t user_lower = compute_user_objective(original_lp_, bsp_lower); + i_t nodes_explored = exploration_stats_.nodes_explored.load(); + i_t nodes_unexplored = exploration_stats_.nodes_unexplored.load(); + settings_.log.printf( + "%s%10d %10lu %+13.6e %+10.6e %6d %7.1e %s %9.2f\n", + feasible_solution_symbol(thread_type_t::EXPLORATION), + nodes_explored, + nodes_unexplored, + user_obj, + user_lower, + sol.depth, + nodes_explored > 0 ? exploration_stats_.total_lp_iters.load() / nodes_explored : 0.0, + user_mip_gap(user_obj, user_lower).c_str(), + toc(exploration_stats_.start_time)); + + // Update incumbent + mutex_upper_.lock(); + if (sol.objective < upper_bound_) { + upper_bound_ = sol.objective; + incumbent_.set_incumbent_solution(sol.objective, *sol.solution); + current_upper = sol.objective; + } + mutex_upper_.unlock(); } - mutex_upper_.unlock(); } // Merge and apply pseudo-cost updates from all workers in deterministic order @@ -3075,6 +3122,35 @@ void branch_and_bound_t::balance_worker_loads() } } +template +f_t branch_and_bound_t::compute_bsp_lower_bound() +{ + // Compute accurate lower bound from all BSP sources + // Called during sync phase (single-threaded), so no locking needed for worker queues + const f_t inf = std::numeric_limits::infinity(); + f_t lower_bound = inf; + + // Check global heap (may have nodes not yet distributed) + mutex_heap_.lock(); + if (heap_.size() > 0) { lower_bound = std::min(heap_.top()->lower_bound, lower_bound); } + mutex_heap_.unlock(); + + // Check all worker queues + for (const auto& worker : *bsp_workers_) { + // Check paused node (current_node) + if (worker.current_node != nullptr) { + lower_bound = std::min(worker.current_node->lower_bound, lower_bound); + } + + // Check queue top (min lower bound due to priority queue ordering) + if (!worker.local_queue.empty()) { + lower_bound = std::min(worker.local_queue.top()->lower_bound, lower_bound); + } + } + + return lower_bound; +} + #ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE template class branch_and_bound_t; diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index b1f640a685..7661ba133f 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -366,6 +366,9 @@ class branch_and_bound_t { search_tree_t& search_tree, double current_horizon); + // Compute accurate lower bound from all BSP sources (called during sync phase) + f_t compute_bsp_lower_bound(); + private: // BSP state std::unique_ptr> bsp_workers_; From ef777c6409afa994e610d9f36f041b8d1d1a6d3c Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 14 Jan 2026 12:16:48 +0000 Subject: [PATCH 115/225] more logs, cache cpu clock --- .../dual_simplex/dual_simplex_features.hpp | 28 +++++----- cpp/src/dual_simplex/phase2.cpp | 3 +- cpp/src/utilities/version_info.cpp | 52 +++++++++++-------- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/cpp/src/dual_simplex/dual_simplex_features.hpp b/cpp/src/dual_simplex/dual_simplex_features.hpp index 49f2171c2b..3b1de9d296 100644 --- a/cpp/src/dual_simplex/dual_simplex_features.hpp +++ b/cpp/src/dual_simplex/dual_simplex_features.hpp @@ -11,6 +11,8 @@ #include #include +#include + #include namespace cuopt::linear_programming::dual_simplex { @@ -71,6 +73,8 @@ struct dual_simplex_features_t { bool slack_basis_, bool initialize_basis_) { + raft::common::nvtx::range scope("DualSimplex::init_from_problem"); + num_rows = lp.num_rows; num_cols = lp.num_cols; num_nonzeros = lp.A.col_start[lp.num_cols]; @@ -187,18 +191,18 @@ struct bounds_strengthening_features_t { void log_single(i_t m_val, i_t n_val, i_t nnz_val) const { - printf( - "BOUNDS_STRENGTH_FEATURES: m=%d n=%d nnz=%d " - "iterations=%d bounds_changed=%d " - "byte_loads=%zu byte_stores=%zu runtime=%.6f\n", - m_val, - n_val, - nnz_val, - num_iterations, - num_bounds_changed, - byte_loads, - byte_stores, - runtime); + // printf( + // "BOUNDS_STRENGTH_FEATURES: m=%d n=%d nnz=%d " + // "iterations=%d bounds_changed=%d " + // "byte_loads=%zu byte_stores=%zu runtime=%.6f\n", + // m_val, + // n_val, + // nnz_val, + // num_iterations, + // num_bounds_changed, + // byte_loads, + // byte_stores, + // runtime); } void reset() diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 4a81444576..d98ab876e7 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -2395,7 +2395,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, f_t obj = compute_objective(lp, sol.x); - raft::common::nvtx::pop_range(); + raft::common::nvtx::pop_range(); // advanced_basis_init const i_t start_iter = iter; @@ -2478,6 +2478,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, // Helper to compute scaled work units for a given number of iterations cuopt::work_unit_predictor_t work_predictor{}; auto predict_work_units = [&](i_t num_iters) -> f_t { + raft::common::nvtx::range scope("DualSimplex::predict_work_units"); std::map features_map; features_map["m"] = static_cast(features.num_rows); features_map["n"] = static_cast(features.num_cols); diff --git a/cpp/src/utilities/version_info.cpp b/cpp/src/utilities/version_info.cpp index a14c9fa274..bfcb02ce16 100644 --- a/cpp/src/utilities/version_info.cpp +++ b/cpp/src/utilities/version_info.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -165,32 +165,40 @@ static double get_available_memory_gb() double get_cpu_max_clock_mhz() { - // Try sysfs cpufreq interface first (returns frequency in KHz) - // FIXME: assumes all available CPUs have the same max clock as CPU0 - std::ifstream freq_file("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"); - if (freq_file.is_open()) { - long khz = 0; - freq_file >> khz; - if (khz > 0) { return khz / 1e3; } - } + // Cache the result - CPU max clock doesn't change during execution + // thread_local to avoid an unecessary sync inserted by the compiler + // due to the standard mandating thread-safe static local variable initialization + // the extra work here is minimal. + thread_local static double cached_mhz = []() { + // Try sysfs cpufreq interface first (returns frequency in KHz) + // FIXME: assumes all available CPUs have the same max clock as CPU0 + std::ifstream freq_file("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"); + if (freq_file.is_open()) { + long khz = 0; + freq_file >> khz; + if (khz > 0) { return khz / 1e3; } + } - // Fallback: parse /proc/cpuinfo for "cpu MHz" - std::ifstream cpuinfo("/proc/cpuinfo"); - if (!cpuinfo.is_open()) return 0.0; + // Fallback: parse /proc/cpuinfo for "cpu MHz" + std::ifstream cpuinfo("/proc/cpuinfo"); + if (!cpuinfo.is_open()) return 0.0; - std::string line; - double max_mhz = 0.0; - while (std::getline(cpuinfo, line)) { - if (line.find("cpu MHz") != std::string::npos) { - std::size_t colon = line.find(':'); - if (colon != std::string::npos) { - double mhz = std::stod(line.substr(colon + 1)); - if (mhz > max_mhz) { max_mhz = mhz; } + std::string line; + double max_mhz = 0.0; + while (std::getline(cpuinfo, line)) { + if (line.find("cpu MHz") != std::string::npos) { + std::size_t colon = line.find(':'); + if (colon != std::string::npos) { + double mhz = std::stod(line.substr(colon + 1)); + if (mhz > max_mhz) { max_mhz = mhz; } + } } } - } - return max_mhz; + return max_mhz; + }(); + + return cached_mhz; } void print_version_info() From 6dcb0629402971a24133856cedd8bd6607ce9a31 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 14 Jan 2026 13:18:28 +0000 Subject: [PATCH 116/225] plunging for deterministic search --- cpp/src/dual_simplex/bb_worker_state.hpp | 178 ++++++++++++++++++---- cpp/src/dual_simplex/branch_and_bound.cpp | 102 ++++++++----- cpp/src/dual_simplex/bsp_debug.hpp | 3 +- 3 files changed, 214 insertions(+), 69 deletions(-) diff --git a/cpp/src/dual_simplex/bb_worker_state.hpp b/cpp/src/dual_simplex/bb_worker_state.hpp index 22ba3d37a9..0ec0fcc2ab 100644 --- a/cpp/src/dual_simplex/bb_worker_state.hpp +++ b/cpp/src/dual_simplex/bb_worker_state.hpp @@ -39,14 +39,20 @@ template struct bb_worker_state_t { int worker_id{0}; - // Type alias for the BSP priority queue - using bsp_queue_t = std::priority_queue*, - std::vector*>, - bsp_node_compare_t>; + // ========================================================================== + // Plunging data structures (matching explore_subtree strategy) + // ========================================================================== - // Local node queue - priority queue ordered by (lower_bound, origin_worker_id, creation_seq) - // Nodes are assigned BSP identity (origin_worker_id, creation_seq) when enqueued - bsp_queue_t local_queue; + // Plunge stack: depth-first path through the tree + // - Front = next node to process (LIFO) + // - Max size = 2 (current node's sibling only) + // - When branching with sibling on stack, sibling is moved to backlog + std::deque*> plunge_stack; + + // Backlog: nodes "plugged" when branching - candidates for load balancing + // When branching with a sibling on the plunge stack, that sibling moves here. + // At horizon sync, backlog nodes participate in redistribution. + std::vector*> backlog; // Current node being processed (may be paused at horizon boundary) mip_node_t* current_node{nullptr}; @@ -274,50 +280,146 @@ struct bb_worker_state_t { return branch_var; } - // Add a node to the local queue, assigning BSP identity if not already set - // The tuple (origin_worker_id, creation_seq) uniquely identifies the node + // ========================================================================== + // Node enqueueing methods + // ========================================================================== + + // Add a node to the plunge stack, assigning BSP identity if not already set + // Used for initial node assignment and when starting a new plunge from backlog void enqueue_node(mip_node_t* node) { // Assign BSP identity if not already set - // Nodes from load balancing keep their original identity if (!node->has_bsp_identity()) { node->origin_worker_id = worker_id; node->creation_seq = next_creation_seq++; } - local_queue.push(node); + plunge_stack.push_front(node); } // Add a node that already has BSP identity (from load balancing or initial distribution) - // Does NOT modify the node's identity + // Goes to plunge stack front for immediate processing void enqueue_node_with_identity(mip_node_t* node) { assert(node->has_bsp_identity() && "Node must have BSP identity for enqueue_node_with_identity"); - local_queue.push(node); + plunge_stack.push_front(node); + } + + // Add children after branching with proper plunging behavior: + // 1. If plunge stack has a sibling, move it to backlog (plugging) + // 2. Push both children to plunge stack with preferred child on top + // Returns the child that was placed on top (to be explored first) + mip_node_t* enqueue_children_for_plunge(mip_node_t* down_child, + mip_node_t* up_child, + rounding_direction_t preferred_direction) + { + // PLUGGING: If plunge stack has a sibling from previous branch, move it to backlog + if (!plunge_stack.empty()) { + mip_node_t* sibling = plunge_stack.back(); + plunge_stack.pop_back(); + backlog.push_back(sibling); + } + + // Assign BSP identity to children + if (!down_child->has_bsp_identity()) { + down_child->origin_worker_id = worker_id; + down_child->creation_seq = next_creation_seq++; + } + if (!up_child->has_bsp_identity()) { + up_child->origin_worker_id = worker_id; + up_child->creation_seq = next_creation_seq++; + } + + // Push children - preferred child on top (front) for immediate exploration + mip_node_t* first_child; + if (preferred_direction == rounding_direction_t::UP) { + plunge_stack.push_front(down_child); // Second to explore + plunge_stack.push_front(up_child); // First to explore (on top) + first_child = up_child; + } else { + plunge_stack.push_front(up_child); // Second to explore + plunge_stack.push_front(down_child); // First to explore (on top) + first_child = down_child; + } + + return first_child; } - // Get next node to process (highest priority = lowest lower_bound) + // ========================================================================== + // Node dequeueing methods + // ========================================================================== + + // Get next node to process using plunging strategy: + // 1. Resume paused node if any + // 2. Pop from plunge stack (depth-first continuation) + // 3. Fall back to backlog (best-first from plugged nodes) mip_node_t* dequeue_node() { + // 1. Resume paused node if any if (current_node != nullptr) { - // Resume paused node mip_node_t* node = current_node; current_node = nullptr; return node; } - if (local_queue.empty()) { return nullptr; } - mip_node_t* node = local_queue.top(); - local_queue.pop(); - return node; + + // 2. Prefer plunge stack (depth-first continuation) + if (!plunge_stack.empty()) { + mip_node_t* node = plunge_stack.front(); + plunge_stack.pop_front(); + return node; + } + + // 3. Fall back to backlog - select best node (lowest lower_bound) + if (!backlog.empty()) { + auto best_it = + std::min_element(backlog.begin(), + backlog.end(), + [](const mip_node_t* a, const mip_node_t* b) { + // Best-first: prefer lower bound + if (a->lower_bound != b->lower_bound) { + return a->lower_bound < b->lower_bound; + } + // Deterministic tie-breaking by BSP identity + if (a->origin_worker_id != b->origin_worker_id) { + return a->origin_worker_id < b->origin_worker_id; + } + return a->creation_seq < b->creation_seq; + }); + mip_node_t* node = *best_it; + backlog.erase(best_it); + return node; + } + + return nullptr; } + // ========================================================================== + // Queue state queries + // ========================================================================== + // Check if worker has work available - bool has_work() const { return current_node != nullptr || !local_queue.empty(); } + bool has_work() const + { + return current_node != nullptr || !plunge_stack.empty() || !backlog.empty(); + } + + // Get number of nodes in worker's queues (including paused node) + size_t queue_size() const + { + return plunge_stack.size() + backlog.size() + (current_node != nullptr ? 1 : 0); + } - // Get number of nodes in local queue (including paused node) - size_t queue_size() const { return local_queue.size() + (current_node != nullptr ? 1 : 0); } + // Get number of nodes in plunge stack only + size_t plunge_stack_size() const { return plunge_stack.size(); } - // Extract all nodes from queue (for load balancing) + // Get number of nodes in backlog only + size_t backlog_size() const { return backlog.size(); } + + // ========================================================================== + // Load balancing support + // ========================================================================== + + // Extract all nodes from worker (for load balancing) // Returns nodes in arbitrary order - caller should sort if deterministic order needed std::vector*> extract_all_nodes() { @@ -330,22 +432,36 @@ struct bb_worker_state_t { current_node = nullptr; } - // Extract all nodes from priority queue - while (!local_queue.empty()) { - nodes.push_back(local_queue.top()); - local_queue.pop(); + // Extract from plunge stack + for (auto* node : plunge_stack) { + nodes.push_back(node); } + plunge_stack.clear(); + + // Extract from backlog + for (auto* node : backlog) { + nodes.push_back(node); + } + backlog.clear(); return nodes; } - // Clear the queue without returning nodes (use with caution) + // Extract only backlog nodes (for redistribution at horizon sync) + // Plunge stack nodes stay with worker for locality + std::vector*> extract_backlog_nodes() + { + std::vector*> nodes = std::move(backlog); + backlog.clear(); + return nodes; + } + + // Clear all queues without returning nodes (use with caution) void clear_queue() { current_node = nullptr; - while (!local_queue.empty()) { - local_queue.pop(); - } + plunge_stack.clear(); + backlog.clear(); } // Record an event diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 6b05788dcc..0926c5ccab 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -2074,13 +2074,16 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_tget_bsp_identity_hash()); } - // Extract queue contents for hashing (preserves priority order) - // We need to temporarily extract to iterate, then restore + // Extract queue contents for hashing + // Include plunge_stack and backlog for complete state std::vector*> queue_nodes; - auto queue_copy = worker.local_queue; // Copy the priority queue - while (!queue_copy.empty()) { - auto* node = queue_copy.top(); - queue_copy.pop(); + for (auto* node : worker.plunge_stack) { + queue_nodes.push_back(node); + } + for (auto* node : worker.backlog) { + queue_nodes.push_back(node); + } + for (auto* node : queue_nodes) { if (!node->has_bsp_identity()) { ++nodes_without_identity; CUOPT_LOG_WARN( @@ -2695,15 +2698,15 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t exploration_stats_.nodes_unexplored += 2; - // Add children to local queue - they get BSP identity on enqueue - // Note: recompute_bounds_and_basis is set in run_worker_until_horizon based on - // whether we branched (has_children), matching opportunistic mode behavior. - worker.enqueue_node(node_ptr->get_down_child()); - worker.enqueue_node(node_ptr->get_up_child()); + // Add children using plunging strategy: + // - If plunge stack has a sibling, it's moved to backlog (plugging) + // - Preferred child goes on top of stack for immediate exploration + rounding_direction_t preferred = child_selection(node_ptr); + worker.enqueue_children_for_plunge( + node_ptr->get_down_child(), node_ptr->get_up_child(), preferred); - return rounding_direction_t::DOWN == child_selection(node_ptr) - ? node_solve_info_t::DOWN_CHILD_FIRST - : node_solve_info_t::UP_CHILD_FIRST; + return preferred == rounding_direction_t::DOWN ? node_solve_info_t::DOWN_CHILD_FIRST + : node_solve_info_t::UP_CHILD_FIRST; } else { search_tree.update(node_ptr, node_status_t::FATHOMED); @@ -2986,22 +2989,32 @@ void branch_and_bound_t::prune_worker_nodes_vs_incumbent() } } - // Check nodes in local queue - need to extract, filter, and rebuild - // since priority_queue doesn't support iteration - std::vector*> surviving_nodes; - while (!worker.local_queue.empty()) { - auto* node = worker.local_queue.top(); - worker.local_queue.pop(); - if (node->lower_bound >= upper_bound) { - search_tree_.update(node, node_status_t::FATHOMED); - --exploration_stats_.nodes_unexplored; - } else { - surviving_nodes.push_back(node); + // Check nodes in plunge stack - filter in place + { + std::deque*> surviving; + for (auto* node : worker.plunge_stack) { + if (node->lower_bound >= upper_bound) { + search_tree_.update(node, node_status_t::FATHOMED); + --exploration_stats_.nodes_unexplored; + } else { + surviving.push_back(node); + } } + worker.plunge_stack = std::move(surviving); } - // Rebuild the queue with surviving nodes - for (auto* node : surviving_nodes) { - worker.local_queue.push(node); + + // Check nodes in backlog - filter in place + { + std::vector*> surviving; + for (auto* node : worker.backlog) { + if (node->lower_bound >= upper_bound) { + search_tree_.update(node, node_status_t::FATHOMED); + --exploration_stats_.nodes_unexplored; + } else { + surviving.push_back(node); + } + } + worker.backlog = std::move(surviving); } } } @@ -3012,7 +3025,7 @@ void branch_and_bound_t::balance_worker_loads() const size_t num_workers = bsp_workers_->size(); if (num_workers <= 1) return; - // Count work for each worker: current_node (if any) + local_queue size + // Count work for each worker: current_node (if any) + plunge_stack + backlog std::vector work_counts(num_workers); size_t total_work = 0; size_t max_work = 0; @@ -3033,14 +3046,24 @@ void branch_and_bound_t::balance_worker_loads() if (!needs_balance) return; - // Collect all redistributable nodes from worker queues (excluding paused current_node) + // Collect all redistributable nodes from worker queues + // With plunging strategy, we redistribute: + // - All backlog nodes (these were "plugged" siblings) + // - Plunge stack nodes (except we keep them with worker for locality if possible) + // For simplicity, redistribute everything for now std::vector*> all_nodes; for (auto& worker : *bsp_workers_) { - // Extract all nodes from this worker's priority queue (not current_node) - while (!worker.local_queue.empty()) { - all_nodes.push_back(worker.local_queue.top()); - worker.local_queue.pop(); + // Extract backlog nodes + for (auto* node : worker.backlog) { + all_nodes.push_back(node); + } + worker.backlog.clear(); + + // Extract plunge stack nodes + for (auto* node : worker.plunge_stack) { + all_nodes.push_back(node); } + worker.plunge_stack.clear(); } // Also pull nodes from global heap if workers need work @@ -3120,9 +3143,14 @@ f_t branch_and_bound_t::compute_bsp_lower_bound() lower_bound = std::min(worker.current_node->lower_bound, lower_bound); } - // Check queue top (min lower bound due to priority queue ordering) - if (!worker.local_queue.empty()) { - lower_bound = std::min(worker.local_queue.top()->lower_bound, lower_bound); + // Check plunge stack nodes + for (auto* node : worker.plunge_stack) { + lower_bound = std::min(node->lower_bound, lower_bound); + } + + // Check backlog nodes + for (auto* node : worker.backlog) { + lower_bound = std::min(node->lower_bound, lower_bound); } } diff --git a/cpp/src/dual_simplex/bsp_debug.hpp b/cpp/src/dual_simplex/bsp_debug.hpp index ff50b0e5d0..893327a2da 100644 --- a/cpp/src/dual_simplex/bsp_debug.hpp +++ b/cpp/src/dual_simplex/bsp_debug.hpp @@ -727,7 +727,8 @@ class bsp_debug_logger_t { } else { file << " \"current_node\": null,\n"; } - file << " \"local_queue_size\": " << w.local_queue.size() << "\n"; + file << " \"plunge_stack_size\": " << w.plunge_stack.size() << ",\n"; + file << " \"backlog_size\": " << w.backlog.size() << "\n"; file << " }" << (i < workers.size() - 1 ? "," : "") << "\n"; } file << " ],\n"; From 35e8177ad62a393e57f546ff6dc327b82c5bdbb2 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 15 Jan 2026 09:08:21 +0000 Subject: [PATCH 117/225] move to work_unit_scheduler to allow for mid-node syncs --- cpp/src/dual_simplex/bb_worker_state.hpp | 4 + cpp/src/dual_simplex/branch_and_bound.cpp | 661 +++++++++++----------- cpp/src/dual_simplex/branch_and_bound.hpp | 10 + cpp/src/dual_simplex/bsp_debug.hpp | 13 +- cpp/src/dual_simplex/phase2.cpp | 2 + cpp/src/dual_simplex/pseudo_costs.cpp | 4 +- cpp/src/utilities/work_limit_timer.hpp | 3 +- cpp/src/utilities/work_unit_scheduler.cpp | 47 +- cpp/src/utilities/work_unit_scheduler.hpp | 21 +- 9 files changed, 425 insertions(+), 340 deletions(-) diff --git a/cpp/src/dual_simplex/bb_worker_state.hpp b/cpp/src/dual_simplex/bb_worker_state.hpp index 0ec0fcc2ab..49b9685d95 100644 --- a/cpp/src/dual_simplex/bb_worker_state.hpp +++ b/cpp/src/dual_simplex/bb_worker_state.hpp @@ -64,6 +64,10 @@ struct bb_worker_state_t { // Worker's virtual time clock (cumulative work units) double clock{0.0}; + // Current horizon boundaries (for BSP sync) + double horizon_start{0.0}; + double horizon_end{0.0}; + // Creation sequence counter - cumulative across horizons for unique identity // Each node created by this worker gets (worker_id, next_creation_seq++) int32_t next_creation_seq{0}; diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 0926c5ccab..d9739690ac 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -930,10 +931,6 @@ node_solve_info_t branch_and_bound_t::solve_node( leaf_edge_norms, settings_.deterministic ? &work_unit_context_ : nullptr); } - if (settings_.deterministic && - work_unit_context_.global_work_units_elapsed >= settings_.work_limit) { - lp_status = dual::status_t::WORK_LIMIT; - } if (lp_status == dual::status_t::NUMERICAL) { log.printf("Numerical issue node %d. Resolving from scratch.\n", node_ptr->node_id); @@ -1762,44 +1759,6 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut pc_); } - // Log strong branching results for determinism debugging - { - uint32_t sb_hash = pc_.compute_strong_branch_hash(); - uint32_t pc_hash = pc_.compute_state_hash(); - CUOPT_LOG_DEBUG("Strong branching completed: %zu variables, SB hash=0x%08x, PC hash=0x%08x", - fractional.size(), - sb_hash, - pc_hash); - - // Detailed logging for divergence diagnosis (enabled via environment variable) - const char* log_sb_detail = std::getenv("CUOPT_LOG_STRONG_BRANCHING"); - if (log_sb_detail != nullptr && std::string(log_sb_detail) == "1") { - settings_.log.printf("Strong branching detailed results:\n"); - for (size_t k = 0; k < fractional.size(); ++k) { - i_t var = fractional[k]; - settings_.log.printf(" var[%zu]=%d: down=%+.10e, up=%+.10e\n", - k, - var, - pc_.strong_branch_down[k], - pc_.strong_branch_up[k]); - } - settings_.log.printf("Pseudo-cost state after strong branching:\n"); - i_t non_zero_count = 0; - for (i_t j = 0; j < original_lp_.num_cols; ++j) { - if (pc_.pseudo_cost_num_down[j] > 0 || pc_.pseudo_cost_num_up[j] > 0) { - settings_.log.printf(" pc[%d]: sum_down=%+.10e, num_down=%d, sum_up=%+.10e, num_up=%d\n", - j, - pc_.pseudo_cost_sum_down[j], - pc_.pseudo_cost_num_down[j], - pc_.pseudo_cost_sum_up[j], - pc_.pseudo_cost_num_up[j]); - ++non_zero_count; - } - } - settings_.log.printf("Total %d variables with pseudo-cost data\n", non_zero_count); - } - } - if (toc(exploration_stats_.start_time) > settings_.time_limit) { solver_status_ = mip_exploration_status_t::TIME_LIMIT; return set_final_solution(solution, root_objective_); @@ -1886,7 +1845,21 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut if (has_pending_features_) { flush_pending_features(); } mutex_feature_log_.unlock(); - f_t lower_bound = heap_.size() > 0 ? heap_.top()->lower_bound : search_tree_.root.lower_bound; + // Compute final lower bound + f_t lower_bound; + if (bsp_mode_enabled_) { + // In BSP mode, compute lower bound from all sources (heap + worker queues) + lower_bound = compute_bsp_lower_bound(); + // If no unexplored nodes remain and we have an incumbent, lower bound = upper bound + if (lower_bound == std::numeric_limits::infinity() && incumbent_.has_incumbent) { + lower_bound = get_upper_bound(); + } + } else { + // Non-BSP mode: use heap or fall back to root + lower_bound = heap_.size() > 0 ? heap_.top()->lower_bound : search_tree_.root.lower_bound; + // If heap is empty and we have an incumbent, the tree is fully explored + if (heap_.size() == 0 && incumbent_.has_incumbent) { lower_bound = get_upper_bound(); } + } return set_final_solution(solution, lower_bound); } @@ -1905,6 +1878,7 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t>(); @@ -1915,6 +1889,15 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t(bsp_horizon_step_); + + // Register all worker contexts with the scheduler + for (auto& worker : *bsp_workers_) { + bsp_scheduler_->register_context(worker.work_context); + } + // Initialize debug logger bsp_debug_logger_.set_settings(bsp_debug_settings_); bsp_debug_logger_.set_num_workers(num_workers); @@ -1933,257 +1916,63 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t::infinity(); - f_t lower_bound = get_lower_bound(); - f_t upper_bound = get_upper_bound(); - f_t abs_gap = upper_bound - lower_bound; - f_t rel_gap = user_relative_gap(original_lp_, upper_bound, lower_bound); - constexpr i_t target_queue_size = 5; // Target nodes per worker // Initial distribution: fill worker queues once at the start refill_worker_queues(target_queue_size); BSP_DEBUG_FLUSH_ASSIGN_TRACE(bsp_debug_settings_, bsp_debug_logger_); - // Main BSP coordinator loop - while (solver_status_ == mip_exploration_status_t::RUNNING && - abs_gap > settings_.absolute_mip_gap_tol && rel_gap > settings_.relative_mip_gap_tol && - (heap_.size() > 0 || bsp_workers_->any_has_work())) { - ++bsp_horizon_number_; - double horizon_start = bsp_current_horizon_ - bsp_horizon_step_; - double horizon_end = bsp_current_horizon_; - - // Debug: Log horizon start - BSP_DEBUG_LOG_HORIZON_START( - bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_start, horizon_end); + // Set sync callback - executed when all workers arrive at barrier + // Returns true to stop the scheduler (and all workers exit cleanly together) + bsp_scheduler_->set_sync_callback([this](double sync_target) -> bool { + bsp_sync_callback(0); + return bsp_terminated_.load(); + }); - // Reset workers for new horizon with current global upper bound - // Each worker gets a snapshot of the upper bound for deterministic pruning - bsp_workers_->reset_for_horizon(horizon_start, horizon_end, get_upper_bound()); - - // Snapshot pseudo-costs for deterministic variable selection - for (auto& worker : *bsp_workers_) { - worker.pc_sum_up_snapshot = pc_.pseudo_cost_sum_up; - worker.pc_sum_down_snapshot = pc_.pseudo_cost_sum_down; - worker.pc_num_up_snapshot = pc_.pseudo_cost_num_up; - worker.pc_num_down_snapshot = pc_.pseudo_cost_num_down; - } + // Set initial upper bound snapshot for all workers + for (auto& worker : *bsp_workers_) { + worker.local_upper_bound = get_upper_bound(); + worker.pc_sum_up_snapshot = pc_.pseudo_cost_sum_up; + worker.pc_sum_down_snapshot = pc_.pseudo_cost_sum_down; + worker.pc_num_up_snapshot = pc_.pseudo_cost_num_up; + worker.pc_num_down_snapshot = pc_.pseudo_cost_num_down; + worker.horizon_start = 0.0; + worker.horizon_end = bsp_horizon_step_; + } - // PHASE 2: PARALLEL EXECUTION - Workers run until horizon + // Main BSP execution - workers run in parallel with scheduler-driven sync #pragma omp parallel num_threads(num_workers) - { - int worker_id = omp_get_thread_num(); - auto& worker = (*bsp_workers_)[worker_id]; - - f_t worker_start_time = tic(); - - // Run worker until horizon - run_worker_until_horizon(worker, search_tree_, bsp_current_horizon_); - - // Record when this worker finished (for barrier wait calculation) - // Store raw timestamp - barrier wait = barrier_end - finish_time - worker.horizon_finish_time = tic(); - worker.total_runtime += toc(worker_start_time); - } - // Implicit OMP barrier here - all workers have finished - f_t barrier_end_time = tic(); - - // Calculate barrier wait time for each worker - // barrier_wait = time from worker finish to barrier completion - for (auto& worker : *bsp_workers_) { - double wait_time = barrier_end_time - worker.horizon_finish_time; - if (wait_time > 0) { worker.total_barrier_wait += wait_time; } - } - - // Aggregate worker work into global context for work limit tracking - // The global work is the horizon boundary (all workers synchronized to this point) - work_unit_context_.global_work_units_elapsed = horizon_end; - - raft::common::nvtx::range scope("BB::bsp_coordinator::sync_phase"); - - // PHASE 3: SYNCHRONIZATION - The Barrier - // Collect and sort all events deterministically - bb_event_batch_t all_events; - { - raft::common::nvtx::range scope("BB::bsp_coordinator::collect_and_sort_events"); - all_events = bsp_workers_->collect_and_sort_events(); - } - - // Debug: Log sync phase - BSP_DEBUG_LOG_SYNC_PHASE_START( - bsp_debug_settings_, bsp_debug_logger_, horizon_end, all_events.size()); - - // Process history and sync - { - raft::common::nvtx::range scope("BB::bsp_coordinator::process_history_and_sync"); - process_history_and_sync(all_events); - } - - // Debug: Log sync end (no final_id assignment needed with BSP identity tuples) - BSP_DEBUG_LOG_SYNC_PHASE_END(bsp_debug_settings_, bsp_debug_logger_, horizon_end); - - // Prune paused nodes that are now dominated by new incumbent - { - raft::common::nvtx::range scope("BB::bsp_coordinator::prune_worker_nodes_vs_incumbent"); - prune_worker_nodes_vs_incumbent(); - } - - // Balance worker loads if significant imbalance detected - { - raft::common::nvtx::range scope("BB::bsp_coordinator::balance_worker_loads"); - balance_worker_loads(); - } - BSP_DEBUG_FLUSH_ASSIGN_TRACE(bsp_debug_settings_, bsp_debug_logger_); - - // Debug: Log horizon end, emit tree state and JSON state - BSP_DEBUG_LOG_HORIZON_END( - bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_end); - - // Compute and log determinism fingerprint hash - // This hash captures all state that should be identical across deterministic runs - if (bsp_debug_settings_.any_enabled() || true) { - // Collect all determinism-critical state into a vector for hashing - std::vector state_data; - - // Global state - state_data.push_back(static_cast(exploration_stats_.nodes_explored)); - state_data.push_back(static_cast(exploration_stats_.nodes_unexplored)); - - // Upper/lower bounds (convert to fixed-point for exact comparison) - // Use compute_bsp_lower_bound() for accurate LB from all worker queues - f_t ub = get_upper_bound(); - f_t lb = compute_bsp_lower_bound(); - state_data.push_back(static_cast(ub * 1000000)); // 6 decimal places - state_data.push_back(static_cast(lb * 1000000)); - - // Worker queue contents using BSP identity tuple (origin_worker_id, creation_seq) - // Each worker's queue is a priority queue - we extract nodes in priority order - // Note: BSP identity is always set for nodes in BSP mode - int nodes_without_identity = 0; - for (auto& worker : *bsp_workers_) { - // Hash paused node if any - if (worker.current_node != nullptr) { - if (!worker.current_node->has_bsp_identity()) { - ++nodes_without_identity; - CUOPT_LOG_WARN( - "BSP Hash: Worker %d current_node has no BSP identity (node_id=%d, depth=%d)", - worker.worker_id, - worker.current_node->node_id, - worker.current_node->depth); - } - state_data.push_back(worker.current_node->get_bsp_identity_hash()); - } - - // Extract queue contents for hashing - // Include plunge_stack and backlog for complete state - std::vector*> queue_nodes; - for (auto* node : worker.plunge_stack) { - queue_nodes.push_back(node); - } - for (auto* node : worker.backlog) { - queue_nodes.push_back(node); - } - for (auto* node : queue_nodes) { - if (!node->has_bsp_identity()) { - ++nodes_without_identity; - CUOPT_LOG_WARN( - "BSP Hash: Worker %d queue node has no BSP identity (node_id=%d, depth=%d)", - worker.worker_id, - node->node_id, - node->depth); - } - state_data.push_back(node->get_bsp_identity_hash()); - } - } - if (nodes_without_identity > 0) { - CUOPT_LOG_WARN( - "BSP Hash at horizon %d: %d nodes without BSP identity - HASH MAY BE " - "NON-DETERMINISTIC!", - bsp_horizon_number_, - nodes_without_identity); - } - - // Compute hash from state data - uint32_t hash = 0x811c9dc5u; // FNV-1a initial value - for (uint64_t val : state_data) { - hash ^= static_cast(val & 0xFFFFFFFF); - hash *= 0x01000193u; - hash ^= static_cast(val >> 32); - hash *= 0x01000193u; - } - CUOPT_LOG_DEBUG("BSP Hash at horizon %d: 0x%x", bsp_horizon_number_, hash); - BSP_DEBUG_LOG_HORIZON_HASH( - bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_end, hash); - } - - BSP_DEBUG_EMIT_TREE_STATE(bsp_debug_settings_, - bsp_debug_logger_, - bsp_horizon_number_, - search_tree_.root, - get_upper_bound()); - // Collect heap nodes for JSON state (Note: We can't easily iterate the heap, so just log the - // size) - std::vector*> heap_snapshot; - BSP_DEBUG_EMIT_STATE_JSON(bsp_debug_settings_, - bsp_debug_logger_, - bsp_horizon_number_, - horizon_start, - horizon_end, - 0, // No longer tracking next_final_id with BSP identity tuples - get_upper_bound(), - compute_bsp_lower_bound(), - exploration_stats_.nodes_explored, - exploration_stats_.nodes_unexplored, - *bsp_workers_, - heap_snapshot, - all_events); - - // Advance the horizon - bsp_current_horizon_ += bsp_horizon_step_; - - // Update gap info using accurate BSP lower bound from all worker queues - lower_bound = compute_bsp_lower_bound(); - upper_bound = get_upper_bound(); - abs_gap = upper_bound - lower_bound; - rel_gap = user_relative_gap(original_lp_, upper_bound, lower_bound); + { + int worker_id = omp_get_thread_num(); + auto& worker = (*bsp_workers_)[worker_id]; - // Check time/work limits - if (toc(exploration_stats_.start_time) > settings_.time_limit) { - solver_status_ = mip_exploration_status_t::TIME_LIMIT; - } - if (settings_.deterministic && - work_unit_context_.global_work_units_elapsed >= settings_.work_limit) { - solver_status_ = mip_exploration_status_t::WORK_LIMIT; - } + f_t worker_start_time = tic(); - // Progress logging - f_t obj = compute_user_objective(original_lp_, upper_bound); - f_t user_lower = compute_user_objective(original_lp_, lower_bound); - std::string gap_user = user_mip_gap(obj, user_lower); - i_t nodes_explored = exploration_stats_.nodes_explored; - i_t nodes_unexplored = exploration_stats_.nodes_unexplored; + // Run worker loop - scheduler handles sync at horizon boundaries + run_worker_loop(worker, search_tree_); - settings_.log.printf(" %10d %10lu %+13.6e %+10.6e %s %9.2f\n", - nodes_explored, - nodes_unexplored, - obj, - user_lower, - gap_user.c_str(), - toc(exploration_stats_.start_time)); + worker.total_runtime += toc(worker_start_time); + } + // All workers have terminated - deregister contexts + for (auto& worker : *bsp_workers_) { + bsp_scheduler_->deregister_context(worker.work_context); } // Print per-worker statistics settings_.log.printf("\n"); settings_.log.printf("BSP Worker Statistics:\n"); settings_.log.printf( - " Worker | Processed | Branched | Pruned | Infeasible | IntSol | Assigned | Runtime | " - "Wait\n"); + " Worker | Processed | Branched | Pruned | Infeasible | IntSol | Assigned | Runtime | " + "Sync%%\n"); settings_.log.printf( " " - "-------+-----------+----------+--------+------------+--------+----------+-----------+-------" + "-------+-----------+----------+--------+------------+--------+----------+-----------+------" "\n"); for (const auto& worker : *bsp_workers_) { - settings_.log.printf(" %6d | %9d | %8d | %6d | %10d | %6d | %8d | %8.3fs | %5.3fs\n", + double sync_time = worker.work_context.total_sync_time; + double total_time = worker.total_runtime + sync_time; + double sync_percent = (total_time > 0) ? (100.0 * sync_time / total_time) : 0.0; + settings_.log.printf(" %6d | %9d | %8d | %6d | %10d | %6d | %8d | %8.3fs | %5.1f%%\n", worker.worker_id, worker.total_nodes_processed, worker.total_nodes_branched, @@ -2191,8 +1980,8 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t::refill_worker_queues(i_t target_queue_size) } } +template +void branch_and_bound_t::run_worker_loop(bb_worker_state_t& worker, + search_tree_t& search_tree) +{ + raft::common::nvtx::range scope("BB::worker_loop"); + + // Workers run continuously until scheduler signals stop (via sync callback) + // The scheduler handles synchronization at horizon boundaries via record_work() + while (!bsp_terminated_.load() && !bsp_scheduler_->is_stopped() && + solver_status_ == mip_exploration_status_t::RUNNING) { + if (worker.has_work()) { + mip_node_t* node = worker.dequeue_node(); + if (node == nullptr) { continue; } + + // Track that this node is being actively processed + worker.current_node = node; + + // Check if node should be pruned (use worker's snapshot for determinism) + f_t upper_bound = worker.local_upper_bound; + if (node->lower_bound >= upper_bound) { + worker.current_node = nullptr; + worker.record_fathomed(node, node->lower_bound); + worker.track_node_pruned(); + search_tree.update(node, node_status_t::FATHOMED); + --exploration_stats_.nodes_unexplored; + continue; + } + + // Check if we can warm-start from the previous solve's basis + bool is_child = (node->parent == worker.last_solved_node); + worker.recompute_bounds_and_basis = !is_child; + + // Solve the node - record_work() inside may block at sync points + // The scheduler's sync callback will execute during barrier waits + node_solve_info_t status = solve_node_bsp(worker, node, search_tree, worker.horizon_end); + + // Track last solved node for warm-start detection + worker.last_solved_node = node; + + // Handle result + if (status == node_solve_info_t::TIME_LIMIT) { + worker.current_node = nullptr; + solver_status_ = mip_exploration_status_t::TIME_LIMIT; + bsp_terminated_.store(true); + break; // Exit loop - scheduler will stop all workers at next sync + } else if (status == node_solve_info_t::WORK_LIMIT) { + worker.current_node = nullptr; + solver_status_ = mip_exploration_status_t::WORK_LIMIT; + bsp_terminated_.store(true); + break; // Exit loop - scheduler will stop all workers at next sync + } else { + // Node completed successfully + worker.current_node = nullptr; + continue; + } + } + + // No work available - advance to next sync point to participate in barrier + // This ensures all workers reach the sync point even if some have no work + cuopt::sync_result_t result = bsp_scheduler_->wait_for_next_sync(worker.work_context); + if (result == cuopt::sync_result_t::STOPPED) { break; } + // After sync, bsp_sync_callback may have redistributed nodes to us + } +} + +template +void branch_and_bound_t::bsp_sync_callback(int worker_id) +{ + // This callback is executed during the barrier by worker 0 + // All other workers are blocked in record_work() at this point + raft::common::nvtx::range scope("BB::bsp_sync_callback"); + + ++bsp_horizon_number_; + double horizon_start = bsp_current_horizon_ - bsp_horizon_step_; + double horizon_end = bsp_current_horizon_; + + // Debug: Log horizon start + BSP_DEBUG_LOG_HORIZON_START( + bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_start, horizon_end); + + // Aggregate worker work into global context + work_unit_context_.global_work_units_elapsed = horizon_end; + + // Collect and sort all events deterministically + bb_event_batch_t all_events = bsp_workers_->collect_and_sort_events(); + + // Debug: Log sync phase + BSP_DEBUG_LOG_SYNC_PHASE_START( + bsp_debug_settings_, bsp_debug_logger_, horizon_end, all_events.size()); + + // Process history and sync (update incumbent, pseudo-costs, etc.) + process_history_and_sync(all_events); + + // Debug: Log sync end + BSP_DEBUG_LOG_SYNC_PHASE_END(bsp_debug_settings_, bsp_debug_logger_, horizon_end); + + // Prune paused nodes that are now dominated by new incumbent + prune_worker_nodes_vs_incumbent(); + + // Balance worker loads if significant imbalance detected + balance_worker_loads(); + BSP_DEBUG_FLUSH_ASSIGN_TRACE(bsp_debug_settings_, bsp_debug_logger_); + + // Debug: Log horizon end and emit state + BSP_DEBUG_LOG_HORIZON_END( + bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_end); + + // Compute and log determinism fingerprint hash (always computed for determinism verification) + uint32_t state_hash = 0; + { + std::vector state_data; + state_data.push_back(static_cast(exploration_stats_.nodes_explored)); + state_data.push_back(static_cast(exploration_stats_.nodes_unexplored)); + f_t ub = get_upper_bound(); + f_t lb = compute_bsp_lower_bound(); + state_data.push_back(static_cast(ub * 1000000)); + state_data.push_back(static_cast(lb * 1000000)); + + for (auto& worker : *bsp_workers_) { + if (worker.current_node != nullptr) { + state_data.push_back(worker.current_node->get_bsp_identity_hash()); + } + for (auto* node : worker.plunge_stack) { + state_data.push_back(node->get_bsp_identity_hash()); + } + for (auto* node : worker.backlog) { + state_data.push_back(node->get_bsp_identity_hash()); + } + } + + state_hash = 0x811c9dc5u; // FNV-1a initial value + for (uint64_t val : state_data) { + state_hash ^= static_cast(val & 0xFFFFFFFF); + state_hash *= 0x01000193u; + state_hash ^= static_cast(val >> 32); + state_hash *= 0x01000193u; + } + BSP_DEBUG_LOG_HORIZON_HASH( + bsp_debug_settings_, bsp_debug_logger_, bsp_horizon_number_, horizon_end, state_hash); + } + + BSP_DEBUG_EMIT_TREE_STATE(bsp_debug_settings_, + bsp_debug_logger_, + bsp_horizon_number_, + search_tree_.root, + get_upper_bound()); + + std::vector*> heap_snapshot; + BSP_DEBUG_EMIT_STATE_JSON(bsp_debug_settings_, + bsp_debug_logger_, + bsp_horizon_number_, + horizon_start, + horizon_end, + 0, + get_upper_bound(), + compute_bsp_lower_bound(), + exploration_stats_.nodes_explored, + exploration_stats_.nodes_unexplored, + *bsp_workers_, + heap_snapshot, + all_events); + + // Advance the horizon for next sync + bsp_current_horizon_ += bsp_horizon_step_; + + // Update worker snapshots for next horizon + for (auto& worker : *bsp_workers_) { + worker.local_upper_bound = get_upper_bound(); + worker.pc_sum_up_snapshot = pc_.pseudo_cost_sum_up; + worker.pc_sum_down_snapshot = pc_.pseudo_cost_sum_down; + worker.pc_num_up_snapshot = pc_.pseudo_cost_num_up; + worker.pc_num_down_snapshot = pc_.pseudo_cost_num_down; + worker.horizon_start = horizon_end; + worker.horizon_end = bsp_current_horizon_; + } + + // Check termination conditions + f_t lower_bound = compute_bsp_lower_bound(); + f_t upper_bound = get_upper_bound(); + f_t abs_gap = upper_bound - lower_bound; + f_t rel_gap = user_relative_gap(original_lp_, upper_bound, lower_bound); + + bool should_terminate = false; + + // Gap tolerance reached + if (abs_gap <= settings_.absolute_mip_gap_tol || rel_gap <= settings_.relative_mip_gap_tol) { + should_terminate = true; + } + + // No more work + if (heap_.size() == 0 && !bsp_workers_->any_has_work()) { should_terminate = true; } + + // Time limit + if (toc(exploration_stats_.start_time) > settings_.time_limit) { + solver_status_ = mip_exploration_status_t::TIME_LIMIT; + should_terminate = true; + } + + // Work limit (use small tolerance for floating-point comparison since horizon is accumulated) + constexpr double work_limit_tolerance = 1e-9; + if (settings_.deterministic && + work_unit_context_.global_work_units_elapsed + work_limit_tolerance >= settings_.work_limit) { + solver_status_ = mip_exploration_status_t::WORK_LIMIT; + should_terminate = true; + } + + if (should_terminate) { bsp_terminated_.store(true); } + + // Progress logging with horizon number and state hash + f_t obj = compute_user_objective(original_lp_, upper_bound); + f_t user_lower = compute_user_objective(original_lp_, lower_bound); + std::string gap_user = user_mip_gap(obj, user_lower); + settings_.log.printf("S%-4d %8d %8lu %+13.6e %+10.6e %s %8.2f [0x%08x]\n", + bsp_horizon_number_, + exploration_stats_.nodes_explored, + exploration_stats_.nodes_unexplored, + obj, + user_lower, + gap_user.c_str(), + toc(exploration_stats_.start_time), + state_hash); + + // Note: No need to re-queue callback - sync callback is called at every sync point automatically +} + template void branch_and_bound_t::run_worker_until_horizon(bb_worker_state_t& worker, search_tree_t& search_tree, @@ -2291,12 +2305,10 @@ void branch_and_bound_t::run_worker_until_horizon(bb_worker_state_tbsp_state == bsp_node_state_t::PAUSED); + // Check if we can warm-start from the previous solve's basis + // Child nodes inherit their parent's vstatus, so we can reuse the basis bool is_child = (node->parent == worker.last_solved_node); - bool can_warm_start = is_resumed || is_child; - worker.recompute_bounds_and_basis = !can_warm_start; + worker.recompute_bounds_and_basis = !is_child; // Solve the node (this records events) node_solve_info_t status = solve_node_bsp(worker, node, search_tree, current_horizon); @@ -2350,10 +2362,9 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t // Track work units at start (from work_context, which simplex solver updates) double work_units_at_start = worker.work_context.global_work_units_elapsed; double clock_at_start = worker.clock; - bool is_resumed = (node_ptr->bsp_state == bsp_node_state_t::PAUSED); // Debug: Log solve start (pass origin_worker_id as identifier) - double work_limit = current_horizon - worker.clock; + double work_limit = worker.horizon_end - worker.clock; BSP_DEBUG_LOG_SOLVE_START(bsp_debug_settings_, bsp_debug_logger_, worker.clock, @@ -2361,7 +2372,7 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t node_ptr->node_id, node_ptr->origin_worker_id, work_limit, - is_resumed); + false); // resumed flag no longer used // Setup leaf problem bounds std::fill(worker.node_presolver->bounds_changed.begin(), @@ -2387,8 +2398,10 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t lp_settings.cut_off = worker.local_upper_bound + settings_.dual_tol; lp_settings.inside_mip = 2; lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time); - // Work limit is the ABSOLUTE VT at which to pause (LP solver compares against absolute elapsed) - lp_settings.work_limit = current_horizon; + // Work limit: use GLOBAL work limit + // The check in dual_phase2 compares global_work_units_elapsed against settings.work_limit + // so work_limit must be the GLOBAL limit, not remaining budget + lp_settings.work_limit = settings_.work_limit; lp_settings.scale_columns = false; bool feasible = true; @@ -2527,28 +2540,20 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.work_units_this_horizon += work_performed; // Note: don't call advance_clock() as work_context was already updated by simplex solver - // Check if we hit the horizon mid-solve - if (lp_status == dual::status_t::WORK_LIMIT || worker.clock >= current_horizon) { - // Pause this node - accumulated_vt is the total work spent on this node - double accumulated_vt = (worker.clock - clock_at_start) + node_ptr->accumulated_vt; - worker.pause_current_node(node_ptr, accumulated_vt); - - // Debug: Log solve end (paused) + // Check if we hit the GLOBAL work limit + // In scheduler-based mode, horizon sync happens via record_work() blocking inside the simplex. + // The simplex runs continuously through multiple horizons, so when it completes with a status + // other than WORK_LIMIT, we should process the result normally. + if (lp_status == dual::status_t::WORK_LIMIT) { + // Global work limit reached - solver will terminate BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, bsp_debug_logger_, worker.clock, worker.worker_id, node_ptr->node_id, node_ptr->origin_worker_id, - "PAUSED", + "WORK_LIMIT", node_ptr->lower_bound); - BSP_DEBUG_LOG_PAUSED(bsp_debug_settings_, - bsp_debug_logger_, - worker.clock, - worker.worker_id, - node_ptr->node_id, - node_ptr->origin_worker_id, - static_cast(accumulated_vt)); return node_solve_info_t::WORK_LIMIT; } @@ -2560,13 +2565,13 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t // Process LP result if (lp_status == dual::status_t::DUAL_UNBOUNDED) { node_ptr->lower_bound = std::numeric_limits::infinity(); - search_tree.update(node_ptr, node_status_t::INFEASIBLE); + + // Record event and debug logs BEFORE search_tree.update() which may delete the node worker.record_infeasible(node_ptr); worker.track_node_infeasible(); worker.track_node_processed(); worker.recompute_bounds_and_basis = true; - // Debug: Log solve end (infeasible) BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, bsp_debug_logger_, worker.clock, @@ -2577,18 +2582,20 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t node_ptr->lower_bound); BSP_DEBUG_LOG_INFEASIBLE( bsp_debug_settings_, bsp_debug_logger_, worker.clock, worker.worker_id, node_ptr->node_id); + + search_tree.update(node_ptr, node_status_t::INFEASIBLE); return node_solve_info_t::NO_CHILDREN; } else if (lp_status == dual::status_t::CUTOFF) { // Use worker-local upper bound for determinism node_ptr->lower_bound = worker.local_upper_bound; - search_tree.update(node_ptr, node_status_t::FATHOMED); + + // Record event and debug logs BEFORE search_tree.update() which may delete the node worker.record_fathomed(node_ptr, node_ptr->lower_bound); worker.track_node_pruned(); worker.track_node_processed(); worker.recompute_bounds_and_basis = true; - // Debug: Log solve end (fathomed - cutoff) BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, bsp_debug_logger_, worker.clock, @@ -2603,6 +2610,8 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.worker_id, node_ptr->node_id, node_ptr->lower_bound); + + search_tree.update(node_ptr, node_status_t::FATHOMED); return node_solve_info_t::NO_CHILDREN; } else if (lp_status == dual::status_t::OPTIMAL) { @@ -2635,13 +2644,13 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.integer_solutions.push_back({leaf_objective, leaf_solution.x, node_ptr->depth}); // Note: Logging deferred to sync phase for deterministic output } - search_tree.update(node_ptr, node_status_t::INTEGER_FEASIBLE); + + // Record event and debug logs BEFORE search_tree.update() which may delete the node worker.record_integer_solution(node_ptr, leaf_objective); worker.track_integer_solution(); worker.track_node_processed(); worker.recompute_bounds_and_basis = true; - // Debug: Log solve end (integer) BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, bsp_debug_logger_, worker.clock, @@ -2656,6 +2665,8 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.worker_id, node_ptr->node_id, leaf_objective); + + search_tree.update(node_ptr, node_status_t::INTEGER_FEASIBLE); return node_solve_info_t::NO_CHILDREN; } else if (leaf_objective <= worker.local_upper_bound + settings_.absolute_mip_gap_tol / 10) { @@ -2709,13 +2720,12 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t : node_solve_info_t::UP_CHILD_FIRST; } else { - search_tree.update(node_ptr, node_status_t::FATHOMED); + // Record event and debug logs BEFORE search_tree.update() which may delete the node worker.record_fathomed(node_ptr, leaf_objective); worker.track_node_pruned(); worker.track_node_processed(); worker.recompute_bounds_and_basis = true; - // Debug: Log solve end (fathomed by bound) BSP_DEBUG_LOG_SOLVE_END(bsp_debug_settings_, bsp_debug_logger_, worker.clock, @@ -2730,6 +2740,8 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t worker.worker_id, node_ptr->node_id, leaf_objective); + + search_tree.update(node_ptr, node_status_t::FATHOMED); return node_solve_info_t::NO_CHILDREN; } @@ -2737,10 +2749,10 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t return node_solve_info_t::TIME_LIMIT; } else { - // Numerical issue - search_tree.update(node_ptr, node_status_t::NUMERICAL); + // Numerical issue - record BEFORE search_tree.update() which may delete the node worker.record_numerical(node_ptr); worker.recompute_bounds_and_basis = true; + search_tree.update(node_ptr, node_status_t::NUMERICAL); return node_solve_info_t::NUMERICAL; } } @@ -2839,7 +2851,6 @@ void branch_and_bound_t::process_history_and_sync( case bb_event_type_t::NODE_FATHOMED: case bb_event_type_t::NODE_INFEASIBLE: case bb_event_type_t::NODE_NUMERICAL: - case bb_event_type_t::NODE_PAUSED: case bb_event_type_t::HEURISTIC_SOLUTION: // These events don't need additional processing during replay // (BSP identity is already assigned at node creation time) @@ -2979,15 +2990,9 @@ void branch_and_bound_t::prune_worker_nodes_vs_incumbent() f_t upper_bound = get_upper_bound(); for (auto& worker : *bsp_workers_) { - // Check paused node - if (worker.current_node != nullptr) { - if (worker.current_node->lower_bound >= upper_bound) { - // Prune the paused node - search_tree_.update(worker.current_node, node_status_t::FATHOMED); - --exploration_stats_.nodes_unexplored; - worker.current_node = nullptr; - } - } + // NOTE: Do NOT prune worker.current_node here! + // The worker thread has local references (node_ptr, leaf_vstatus) to it. + // The worker will handle cutoff checking when it resumes after sync. // Check nodes in plunge stack - filter in place { @@ -3025,6 +3030,9 @@ void branch_and_bound_t::balance_worker_loads() const size_t num_workers = bsp_workers_->size(); if (num_workers <= 1) return; + // Flag to force rebalancing every sync (reduces idle time at barriers when work is uniform) + constexpr bool force_rebalance_every_sync = true; + // Count work for each worker: current_node (if any) + plunge_stack + backlog std::vector work_counts(num_workers); size_t total_work = 0; @@ -3039,10 +3047,17 @@ void branch_and_bound_t::balance_worker_loads() min_work = std::min(min_work, work_counts[w]); } - // Check if we need to balance: significant imbalance = some worker has 0 work while others have - // 2+ Or max/min ratio is very high - bool needs_balance = - (min_work == 0 && max_work >= 2) || (min_work > 0 && max_work > 4 * min_work); + // Early exit if no work to redistribute + if (total_work == 0) return; + + bool needs_balance; + if (force_rebalance_every_sync) { + // Always rebalance if there's more than one node to distribute + needs_balance = (total_work > 1); + } else { + // Original logic: only rebalance on significant imbalance + needs_balance = (min_work == 0 && max_work >= 2) || (min_work > 0 && max_work > 4 * min_work); + } if (!needs_balance) return; diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index a94d0dc512..07cf8ee00c 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -24,8 +24,10 @@ #include #include #include +#include #include +#include #include #include @@ -372,9 +374,17 @@ class branch_and_bound_t { // Compute accurate lower bound from all BSP sources (called during sync phase) f_t compute_bsp_lower_bound(); + // BSP worker loop - runs continuously until scheduler signals stop + void run_worker_loop(bb_worker_state_t& worker, search_tree_t& search_tree); + + // BSP sync callback - executed when all workers reach barrier + void bsp_sync_callback(int worker_id); + private: // BSP state std::unique_ptr> bsp_workers_; + std::unique_ptr bsp_scheduler_; + std::atomic bsp_terminated_{false}; double bsp_horizon_step_{5.0}; // Virtual time step per horizon (tunable) double bsp_current_horizon_{0.0}; // Current horizon target bool bsp_mode_enabled_{false}; // Whether BSP mode is active diff --git a/cpp/src/dual_simplex/bsp_debug.hpp b/cpp/src/dual_simplex/bsp_debug.hpp index 893327a2da..edf543bb07 100644 --- a/cpp/src/dual_simplex/bsp_debug.hpp +++ b/cpp/src/dual_simplex/bsp_debug.hpp @@ -333,13 +333,12 @@ class bsp_debug_logger_t { std::lock_guard lock(mutex_); if (settings_.enable_event_log && settings_.log_level >= 2) { - log_event_unlocked( - vt, - worker_id, - is_resumed ? bsp_log_event_t::NODE_RESUMED : bsp_log_event_t::NODE_SOLVE_START, - node_id, - final_id, - "work_limit=" + std::to_string(work_limit)); + log_event_unlocked(vt, + worker_id, + bsp_log_event_t::NODE_SOLVE_START, + node_id, + final_id, + "work_limit=" + std::to_string(work_limit)); } // Start timeline event diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index d98ab876e7..951b5fc372 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -3133,6 +3133,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, // prediction, // features.interval_runtime, // prediction - features.interval_runtime); + // printf("Current iter %d\n", iter); work_unit_context->record_work(prediction); } @@ -3168,6 +3169,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, return dual::status_t::CONCURRENT_LIMIT; } } + // printf("NODE SOLVE FINISHED\n"); if (iter >= iter_limit) { status = dual::status_t::ITERATION_LIMIT; } if (phase == 2) { diff --git a/cpp/src/dual_simplex/pseudo_costs.cpp b/cpp/src/dual_simplex/pseudo_costs.cpp index 3442c8deb9..cdddf0a108 100644 --- a/cpp/src/dual_simplex/pseudo_costs.cpp +++ b/cpp/src/dual_simplex/pseudo_costs.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -159,7 +159,7 @@ void strong_branching(const lp_problem_t& original_lp, settings.num_threads, fractional.size()); -#pragma omp parallel num_threads(settings.num_threads) +#pragma omp parallel num_threads(64) { i_t n = std::min(4 * settings.num_threads, fractional.size()); diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp index 862d276b7d..08499f55bc 100644 --- a/cpp/src/utilities/work_limit_timer.hpp +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights * reserved. SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,7 @@ namespace cuopt { struct work_limit_context_t { double global_work_units_elapsed{0.0}; + double total_sync_time{0.0}; // Total time spent waiting at sync barriers (seconds) bool deterministic{false}; work_unit_scheduler_t* scheduler{nullptr}; std::string name; diff --git a/cpp/src/utilities/work_unit_scheduler.cpp b/cpp/src/utilities/work_unit_scheduler.cpp index 0fe5197d37..7861183491 100644 --- a/cpp/src/utilities/work_unit_scheduler.cpp +++ b/cpp/src/utilities/work_unit_scheduler.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights * reserved. SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -58,6 +58,9 @@ void work_unit_scheduler_t::deregister_context(work_limit_context_t& ctx) void work_unit_scheduler_t::on_work_recorded(work_limit_context_t& ctx, double total_work) { + // Early exit if scheduler is stopped + if (stopped_.load()) { return; } + if (verbose) { double sync_target = current_sync_target(); CUOPT_LOG_DEBUG("[%s] Work recorded: %f, sync_target: %f (gen %zu)", @@ -68,11 +71,31 @@ void work_unit_scheduler_t::on_work_recorded(work_limit_context_t& ctx, double t } // Loop to handle large work increments that cross multiple sync points - while (total_work >= current_sync_target()) { + while (!stopped_.load() && total_work >= current_sync_target()) { wait_at_sync_point(ctx, current_sync_target()); } } +void work_unit_scheduler_t::set_sync_callback(sync_callback_t callback) +{ + std::lock_guard lock(mutex_); + sync_callback_ = std::move(callback); +} + +bool work_unit_scheduler_t::is_stopped() const { return stopped_.load(); } + +sync_result_t work_unit_scheduler_t::wait_for_next_sync(work_limit_context_t& ctx) +{ + if (stopped_.load()) { return sync_result_t::STOPPED; } + + // Advance work to next sync point + double next_sync = current_sync_target(); + ctx.global_work_units_elapsed = next_sync; + wait_at_sync_point(ctx, next_sync); + + return stopped_.load() ? sync_result_t::STOPPED : sync_result_t::CONTINUE; +} + void work_unit_scheduler_t::queue_callback(work_limit_context_t& source, work_limit_context_t& destination, callback_t callback) @@ -116,6 +139,13 @@ void work_unit_scheduler_t::wait_at_sync_point(work_limit_context_t& ctx, double ctx.name.c_str(), barrier_generation_); } + // Execute sync callback if registered (last arrival executes it) + if (sync_callback_) { + lock.unlock(); + bool should_stop = sync_callback_(sync_target); + lock.lock(); + if (should_stop) { stopped_.store(true); } + } cv_.notify_all(); } else { cv_.wait(lock, [&] { return barrier_generation_ != my_generation; }); @@ -150,11 +180,16 @@ void work_unit_scheduler_t::wait_at_sync_point(work_limit_context_t& ctx, double if (verbose) { CUOPT_LOG_DEBUG("[%s] Woke up from second wait", ctx.name.c_str()); } } + // Track sync time + auto wait_end = std::chrono::high_resolution_clock::now(); + double wait_secs = std::chrono::duration(wait_end - wait_start).count(); + ctx.total_sync_time += wait_secs; + if (verbose) { - auto wait_end = std::chrono::high_resolution_clock::now(); - double wait_ms = std::chrono::duration(wait_end - wait_start).count(); - CUOPT_LOG_DEBUG( - "[%s] Sync complete at %.2f, waited %.2f ms", ctx.name.c_str(), sync_target, wait_ms); + CUOPT_LOG_DEBUG("[%s] Sync complete at %.2f, waited %.2f ms", + ctx.name.c_str(), + sync_target, + wait_secs * 1000.0); } } diff --git a/cpp/src/utilities/work_unit_scheduler.hpp b/cpp/src/utilities/work_unit_scheduler.hpp index 57f7d3181b..fb6d765026 100644 --- a/cpp/src/utilities/work_unit_scheduler.hpp +++ b/cpp/src/utilities/work_unit_scheduler.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights * reserved. SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,7 @@ */ #pragma once +#include #include #include #include @@ -27,6 +28,11 @@ namespace cuopt { struct work_limit_context_t; +enum class sync_result_t { + CONTINUE, // Continue processing + STOPPED // Scheduler has been stopped +}; + class work_unit_scheduler_t { public: using callback_t = std::function; @@ -43,6 +49,15 @@ class work_unit_scheduler_t { work_limit_context_t& destination, callback_t callback); + // Sync callback support - callback is executed when all contexts reach sync point + // If callback returns true, scheduler stops and all workers exit cleanly + using sync_callback_t = std::function; + void set_sync_callback(sync_callback_t callback); + bool is_stopped() const; + + // Wait for next sync point (for idle workers with no work) + sync_result_t wait_for_next_sync(work_limit_context_t& ctx); + public: bool verbose{false}; double sync_interval_; @@ -75,6 +90,10 @@ class work_unit_scheduler_t { double current_sync_target_{0}; size_t barrier_generation_{0}; size_t exit_generation_{0}; + + // Sync callback - executed when all contexts reach sync point + sync_callback_t sync_callback_; + std::atomic stopped_{false}; }; } // namespace cuopt From e06af9fa251f96ef90050298a45867fc2cae6f21 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 15 Jan 2026 11:03:13 +0000 Subject: [PATCH 118/225] better debug printouts --- cpp/src/dual_simplex/bb_worker_state.hpp | 1 + cpp/src/dual_simplex/branch_and_bound.cpp | 29 +++++++++++++++++------ cpp/src/dual_simplex/phase2.cpp | 1 + cpp/src/mip/solver.cu | 2 ++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/cpp/src/dual_simplex/bb_worker_state.hpp b/cpp/src/dual_simplex/bb_worker_state.hpp index 49b9685d95..1714f42602 100644 --- a/cpp/src/dual_simplex/bb_worker_state.hpp +++ b/cpp/src/dual_simplex/bb_worker_state.hpp @@ -113,6 +113,7 @@ struct bb_worker_state_t { // Timing statistics (in seconds) double total_runtime{0.0}; // Total time spent doing actual work double total_barrier_wait{0.0}; // Total time spent waiting at horizon sync barriers + double total_nowork_time{0.0}; // Total time spent with no nodes to work on double horizon_finish_time{ 0.0}; // Timestamp when worker finished current horizon (for barrier wait calc) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index d9739690ac..6757a51844 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -1962,17 +1962,17 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t 0) ? (100.0 * sync_time / total_time) : 0.0; - settings_.log.printf(" %6d | %9d | %8d | %6d | %10d | %6d | %8d | %8.3fs | %5.1f%%\n", + settings_.log.printf(" %6d | %7d | %8d | %6d | %7d | %6d | %8d | %7.3fs | %4.1f%% | %5.2fs\n", worker.worker_id, worker.total_nodes_processed, worker.total_nodes_branched, @@ -1981,7 +1981,8 @@ void branch_and_bound_t::run_bsp_coordinator(const csr_matrix_t::run_worker_loop(bb_worker_state_t& // No work available - advance to next sync point to participate in barrier // This ensures all workers reach the sync point even if some have no work + f_t nowork_start = tic(); cuopt::sync_result_t result = bsp_scheduler_->wait_for_next_sync(worker.work_context); + worker.total_nowork_time += toc(nowork_start); if (result == cuopt::sync_result_t::STOPPED) { break; } // After sync, bsp_sync_callback may have redistributed nodes to us } @@ -2267,7 +2270,17 @@ void branch_and_bound_t::bsp_sync_callback(int worker_id) f_t obj = compute_user_objective(original_lp_, upper_bound); f_t user_lower = compute_user_objective(original_lp_, lower_bound); std::string gap_user = user_mip_gap(obj, user_lower); - settings_.log.printf("S%-4d %8d %8lu %+13.6e %+10.6e %s %8.2f [0x%08x]\n", + + // Build list of workers that reached sync with no work + std::string idle_workers; + for (const auto& w : *bsp_workers_) { + if (!w.has_work() && w.current_node == nullptr) { + if (!idle_workers.empty()) idle_workers += ","; + idle_workers += "W" + std::to_string(w.worker_id); + } + } + + settings_.log.printf("S%-4d %8d %8lu %+13.6e %+10.6e %s %8.2f [%08x]%s%s\n", bsp_horizon_number_, exploration_stats_.nodes_explored, exploration_stats_.nodes_unexplored, @@ -2275,7 +2288,9 @@ void branch_and_bound_t::bsp_sync_callback(int worker_id) user_lower, gap_user.c_str(), toc(exploration_stats_.start_time), - state_hash); + state_hash, + idle_workers.empty() ? "" : " ", + idle_workers.c_str()); // Note: No need to re-queue callback - sync callback is called at every sync point automatically } diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 951b5fc372..13fab4cd09 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -2257,6 +2257,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, phase2::bound_info(lp, settings); if (initialize_basis) { + raft::common::nvtx::push_range("DualSimplex::init_basis"); std::vector superbasic_list; nonbasic_list.clear(); nonbasic_list.reserve(n - m); diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index 9ba8fdbcf2..68ef676636 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -197,6 +197,8 @@ solution_t mip_solver_t::run_solver() num_bfs_threads = std::max(1, num_threads); num_diving_threads = 0; // No diving in deterministic mode } + // num_diving_threads = 0; + // num_bfs_threads = std::max(1, num_threads); branch_and_bound_settings.num_bfs_threads = num_bfs_threads; branch_and_bound_settings.num_diving_threads = num_diving_threads; From 99e9ec27787fa6b70f1a8c751fe1adb98051f0cf Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 15 Jan 2026 14:40:35 +0000 Subject: [PATCH 119/225] improve ins_vector coverage; fix case where instrumeted mem accesses would not be accounted for when iterations aren't multiples for LOG_FEATURE_ITERATIONS --- cpp/src/dual_simplex/basis_solves.cpp | 46 +++--- cpp/src/dual_simplex/basis_updates.cpp | 32 ++-- cpp/src/dual_simplex/crossover.cpp | 3 +- cpp/src/dual_simplex/device_sparse_matrix.cu | 26 +--- cpp/src/dual_simplex/phase2.cpp | 145 ++++++++++++++----- cpp/src/dual_simplex/right_looking_lu.cpp | 48 +++--- cpp/src/dual_simplex/right_looking_lu.hpp | 10 +- cpp/src/dual_simplex/singletons.cpp | 56 ++++--- cpp/src/dual_simplex/singletons.hpp | 8 +- cpp/src/dual_simplex/sparse_matrix.cpp | 11 +- cpp/src/dual_simplex/sparse_matrix.hpp | 11 +- cpp/src/dual_simplex/triangle_solve.cpp | 86 +---------- cpp/src/dual_simplex/triangle_solve.hpp | 74 ++++++++-- cpp/src/dual_simplex/vector_math.cpp | 53 +------ cpp/src/dual_simplex/vector_math.hpp | 43 ++++-- 15 files changed, 355 insertions(+), 297 deletions(-) diff --git a/cpp/src/dual_simplex/basis_solves.cpp b/cpp/src/dual_simplex/basis_solves.cpp index a979a9f315..1113bbb570 100644 --- a/cpp/src/dual_simplex/basis_solves.cpp +++ b/cpp/src/dual_simplex/basis_solves.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -12,11 +12,15 @@ #include #include #include +#include #include namespace cuopt::linear_programming::dual_simplex { +// Import instrumented vector type +using cuopt::ins_vector; + template i_t reorder_basic_list(const std::vector& q, std::vector& basic_list) { @@ -59,14 +63,14 @@ void get_basis_from_vstatus(i_t m, namespace { -template +template void write_singleton_info(i_t m, i_t col_singletons, i_t row_singletons, const csc_matrix_t& B, - const std::vector& row_perm, - const std::vector& row_perm_inv, - const std::vector& col_perm) + const VectorI& row_perm, + const VectorI& row_perm_inv, + const VectorI& col_perm) { FILE* file = fopen("singleton_debug.m", "w"); if (file != NULL) { @@ -96,7 +100,7 @@ void write_singleton_info(i_t m, fclose(file); } -template +template void write_factor_info(const char* filename, i_t m, i_t row_singletons, @@ -106,8 +110,8 @@ void write_factor_info(const char* filename, const csc_matrix_t& D, const csc_matrix_t& L, const csc_matrix_t& U, - const std::vector& row_perm, - const std::vector& col_perm) + const VectorI& row_perm, + const VectorI& col_perm) { FILE* file = fopen(filename, "w"); if (file != NULL) { @@ -178,12 +182,12 @@ i_t factorize_basis(const csc_matrix_t& A, f_t fact_start = tic(); csc_matrix_t B(A.m, A.m, 1); form_b(A, basic_list, B); - std::vector row_perm(m); - std::vector col_perm(m); + ins_vector row_perm(m); + ins_vector col_perm(m); i_t row_singletons; i_t col_singletons; find_singletons(B, row_singletons, row_perm, col_singletons, col_perm); - std::vector row_perm_inv(m); + ins_vector row_perm_inv(m); inverse_permutation(row_perm, row_perm_inv); #ifdef PRINT_SINGLETONS @@ -347,12 +351,12 @@ i_t factorize_basis(const csc_matrix_t& A, csc_matrix_t SL(Sdim, Sdim, Snz); csc_matrix_t SU(Sdim, Sdim, Snz); // Factorize S - std::vector S_perm_inv(Sdim); + ins_vector S_perm_inv(Sdim); std::optional> empty = std::nullopt; f_t actual_factor_start = tic(); - std::vector S_col_perm(Sdim); - std::vector identity(Sdim); + ins_vector S_col_perm(Sdim); + ins_vector identity(Sdim); for (i_t h = 0; h < Sdim; ++h) { identity[h] = h; } @@ -376,7 +380,7 @@ i_t factorize_basis(const csc_matrix_t& A, deficient[h - Srank] = col_perm[num_singletons + S_col_perm[h]]; } // Get S_perm - std::vector S_perm(Sdim); + ins_vector S_perm(Sdim); inverse_permutation(S_perm_inv, S_perm); // Get the slacks needed slacks_needed.resize(Sdim - Srank); @@ -388,7 +392,7 @@ i_t factorize_basis(const csc_matrix_t& A, } // Need to permute col_perm[k] according to q - std::vector col_perm_sav(m - num_singletons); + ins_vector col_perm_sav(m - num_singletons); i_t q_j = 0; for (i_t h = num_singletons; h < m; ++h) { col_perm_sav[q_j] = col_perm[h]; @@ -400,7 +404,7 @@ i_t factorize_basis(const csc_matrix_t& A, q_j++; } - std::vector S_perm(m); + ins_vector S_perm(m); inverse_permutation(S_perm_inv, S_perm); actual_factor = toc(actual_factor_start); @@ -475,7 +479,7 @@ i_t factorize_basis(const csc_matrix_t& A, assert(Unz <= Unz_max); U.col_start[m] = Unz; // Finalize U - std::vector last_perm(Sdim); + ins_vector last_perm(Sdim); for (i_t k = 0; k < Sdim; ++k) { last_perm[k] = row_perm[num_singletons + k]; } @@ -628,7 +632,7 @@ i_t basis_repair(const csc_matrix_t& A, assert(nonbasic_list.size() == n - m); // Create slack_map - std::vector slack_map(m); // slack_map[i] = j if column j is e_i + ins_vector slack_map(m); // slack_map[i] = j if column j is e_i i_t slacks_found = 0; for (i_t j = n - 1; j >= n - m; j--) { const i_t col_start = A.col_start[j]; @@ -644,7 +648,7 @@ i_t basis_repair(const csc_matrix_t& A, assert(slacks_found == m); // Create nonbasic_map - std::vector nonbasic_map( + ins_vector nonbasic_map( n, -1); // nonbasic_map[j] = p if nonbasic[p] = j, -1 if j is basic/superbasic const i_t num_nonbasic = nonbasic_list.size(); for (i_t k = 0; k < num_nonbasic; ++k) { @@ -758,6 +762,8 @@ i_t b_transpose_solve(const csc_matrix_t& L, // U'*r = c // L'*w = r + raft::common::nvtx::range scope("LU::b_transpose_solve"); + // Solve for r such that U'*r = c std::vector r = rhs; upper_triangular_transpose_solve(U, r); diff --git a/cpp/src/dual_simplex/basis_updates.cpp b/cpp/src/dual_simplex/basis_updates.cpp index bf00cd9b54..a54ff6c75a 100644 --- a/cpp/src/dual_simplex/basis_updates.cpp +++ b/cpp/src/dual_simplex/basis_updates.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -17,6 +18,9 @@ namespace cuopt::linear_programming::dual_simplex { +// Import instrumented vector type +using cuopt::ins_vector; + template i_t basis_update_t::b_solve(const std::vector& rhs, std::vector& solution) const { @@ -552,7 +556,7 @@ i_t basis_update_t::u_solve(std::vector& x) const // 2. Solve for y such that U*y = bprime // 3. Compute Q*y = x const i_t m = U_.m; - std::vector bprime(m); + ins_vector bprime(m); inverse_permute_vector(col_permutation_, x, bprime); #ifdef CHECK_UPPER_SOLVE @@ -603,7 +607,7 @@ i_t basis_update_t::u_transpose_solve(std::vector& x) const // 2. Solve for y such that U'*y = bprime // 3. Compute Q*y = x const i_t m = U_.m; - std::vector bprime(m); + ins_vector bprime(m); inverse_permute_vector(col_permutation_, x, bprime); dual_simplex::upper_triangular_transpose_solve(U_, bprime); permute_vector(col_permutation_, bprime, x); @@ -860,7 +864,7 @@ i_t basis_update_t::update(std::vector& utilde, i_t leaving_index #endif // ubar = Q'*utilde - std::vector ubar(m); + ins_vector ubar(m); inverse_permute_vector(col_permutation_, utilde, ubar); // Find t @@ -871,7 +875,7 @@ i_t basis_update_t::update(std::vector& utilde, i_t leaving_index const f_t delta = u_diagonal(t); // Solve U'*w = delta*et - std::vector w(m); + ins_vector w(m); w[t] = delta; dual_simplex::upper_triangular_transpose_solve(U_, w); #ifdef PARANOID @@ -897,7 +901,7 @@ i_t basis_update_t::update(std::vector& utilde, i_t leaving_index // Set deltabar = w'*ubar const f_t deltabar = update_L ? dot(w, ubar) : ubar[t]; assert(std::abs(deltabar) > 0); - std::vector baru(m); + ins_vector baru(m); for (i_t k = 0; k < t; ++k) { baru[k] = ubar[k]; } @@ -914,11 +918,11 @@ i_t basis_update_t::update(std::vector& utilde, i_t leaving_index } } - std::vector d(m); + ins_vector d(m); d = w; d[t] = 0.0; // dtilde^T = d^T Q^T -> dtilde = Q*d - std::vector dtilde(m); + ins_vector dtilde(m); permute_vector(col_permutation_, d, dtilde); update_upper(baru_ind, baru_val, t); @@ -1028,7 +1032,7 @@ i_t basis_update_t::lower_triangular_multiply(const csc_matrix_t sval; const i_t in_col_start = in.col_start[in_col]; const i_t in_col_end = in.col_start[in_col + 1]; - std::vector sbuffer(m); + ins_vector sbuffer(m); for (i_t p = in_col_start; p < in_col_end; ++p) { sbuffer[inverse_col_permutation_[in.i[p]]] = in.x[p]; } @@ -1062,7 +1066,7 @@ i_t basis_update_t::lower_triangular_multiply(const csc_matrix_t work2(m); + ins_vector work2(m); sind.push_back(r); sval.push_back(-dot); @@ -1081,14 +1085,14 @@ i_t basis_update_t::lower_triangular_multiply(const csc_matrix_t workspace(m); + ins_vector workspace(m); const i_t nx = sind.size(); for (i_t k = 0; k < nx; ++k) { const i_t j = sind[k]; const f_t x = sval[k]; workspace[j] = x; } - std::vector workspace2(m); + ins_vector workspace2(m); matrix_vector_multiply(L0_, 1.0, workspace, 0.0, workspace2); workspace = workspace2; @@ -1981,7 +1985,7 @@ void basis_update_mpf_t::l_multiply(std::vector& inout) const add_sparse_column(S_, u_col, theta, inout); } - std::vector out(m, 0.0); + ins_vector out(m, 0.0); matrix_vector_multiply(L0_, 1.0, inout, 0.0, out); inout = out; } @@ -1990,7 +1994,7 @@ template void basis_update_mpf_t::l_transpose_multiply(std::vector& inout) const { const i_t m = L0_.m; - std::vector out(m, 0.0); + ins_vector out(m, 0.0); matrix_vector_multiply(L0_transpose_, 1.0, inout, 0.0, out); inout = out; diff --git a/cpp/src/dual_simplex/crossover.cpp b/cpp/src/dual_simplex/crossover.cpp index de3c3ef1c3..1b12a73536 100644 --- a/cpp/src/dual_simplex/crossover.cpp +++ b/cpp/src/dual_simplex/crossover.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -85,6 +85,7 @@ f_t dual_infeasibility(const lp_problem_t& lp, const std::vector& vstatus, const std::vector& z) { + raft::common::nvtx::range scope("DualSimplex::dual_infeasibility"); const i_t n = lp.num_cols; const i_t m = lp.num_rows; i_t num_infeasible = 0; diff --git a/cpp/src/dual_simplex/device_sparse_matrix.cu b/cpp/src/dual_simplex/device_sparse_matrix.cu index 86ec99c7ba..11c3798b8d 100644 --- a/cpp/src/dual_simplex/device_sparse_matrix.cu +++ b/cpp/src/dual_simplex/device_sparse_matrix.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -27,29 +27,9 @@ void csc_matrix_t::scale_columns(const std::vector& sc } #ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE -template int -matrix_vector_multiply, PinnedHostAllocator>( - const csc_matrix_t& A, - double alpha, - const std::vector>& x, - double beta, - std::vector>& y); -template int -matrix_vector_multiply, std::allocator>( - const csc_matrix_t& A, - double alpha, - const std::vector>& x, - double beta, - std::vector>& y); - -template int -matrix_vector_multiply, PinnedHostAllocator>( - const csc_matrix_t& A, - double alpha, - const std::vector>& x, - double beta, - std::vector>& y); +// NOTE: matrix_vector_multiply is now templated on VectorX and VectorY. +// Since it's defined inline in the header, no explicit instantiation is needed here. template int matrix_transpose_vector_multiply& objective, const std::vector& nonbasic_list, std::vector& z) { + raft::common::nvtx::range scope("DualSimplex::compute_reduced_costs"); + const i_t m = A.m; const i_t n = A.n; // zN = cN - N'*y @@ -416,8 +453,10 @@ void compute_primal_variables(const basis_update_mpf_t& ft, const std::vector& basic_list, const std::vector& nonbasic_list, f_t tight_tol, - std::vector& x) + std::vector& x, + ins_vector& xB_workspace) { + raft::common::nvtx::range scope("DualSimplex::compute_primal_variables"); const i_t m = A.m; const i_t n = A.n; std::vector rhs = lp_rhs; @@ -434,12 +473,12 @@ void compute_primal_variables(const basis_update_mpf_t& ft, } } - std::vector xB(m); - ft.b_solve(rhs, xB); + xB_workspace.resize(m); + ft.b_solve(rhs, xB_workspace); for (i_t k = 0; k < m; ++k) { const i_t j = basic_list[k]; - x[j] = xB[k]; + x[j] = xB_workspace[k]; } } @@ -484,6 +523,8 @@ void compute_dual_residual(const csc_matrix_t& A, const std::vector& z, std::vector& dual_residual) { + raft::common::nvtx::range scope("DualSimplex::compute_dual_residual"); + dual_residual = z; const i_t n = A.n; // r = A'*y + z - c @@ -550,7 +591,7 @@ void compute_dual_solution_from_basis(const lp_problem_t& lp, const i_t n = lp.num_cols; y.resize(m); - std::vector cB(m); + ins_vector cB(m); for (i_t k = 0; k < m; ++k) { const i_t j = basic_list[k]; cB[k] = lp.objective[j]; @@ -589,7 +630,8 @@ i_t compute_primal_solution_from_basis(const lp_problem_t& lp, const std::vector& basic_list, const std::vector& nonbasic_list, const std::vector& vstatus, - std::vector& x) + std::vector& x, + ins_vector& xB_workspace) { const i_t m = lp.num_rows; const i_t n = lp.num_cols; @@ -619,12 +661,12 @@ i_t compute_primal_solution_from_basis(const lp_problem_t& lp, } } - std::vector xB(m); - ft.b_solve(rhs, xB); + xB_workspace.resize(m); + ft.b_solve(rhs, xB_workspace); for (i_t k = 0; k < m; ++k) { const i_t j = basic_list[k]; - x[j] = xB[k]; + x[j] = xB_workspace[k]; } return 0; } @@ -637,6 +679,7 @@ f_t compute_initial_primal_infeasibilities(const lp_problem_t& lp, ins_vector& squared_infeasibilities, ins_vector& infeasibility_indices) { + raft::common::nvtx::range scope("DualSimplex::compute_initial_primal_infeasibilities"); const i_t m = lp.num_rows; const i_t n = lp.num_cols; squared_infeasibilities.resize(n, 0.0); @@ -1358,9 +1401,9 @@ i_t check_steepest_edge_norms(const simplex_solver_settings_t& setting const i_t m = basic_list.size(); for (i_t k = 0; k < m; ++k) { const i_t j = basic_list[k]; - std::vector ei(m); + ins_vector ei(m); ei[k] = -1.0; - std::vector delta_yi(m); + ins_vector delta_yi(m); ft.b_transpose_solve(ei, delta_yi); const f_t computed_norm = vector_norm2_squared(delta_yi); const f_t updated_norm = delta_y_steepest_edge[j]; @@ -1925,6 +1968,7 @@ void set_primal_variables_on_bounds(const lp_problem_t& lp, std::vector& vstatus, std::vector& x) { + raft::common::nvtx::range scope("DualSimplex::set_primal_variables_on_bounds"); const i_t n = lp.num_cols; for (i_t j = 0; j < n; ++j) { // We set z_j = 0 for basic variables @@ -2244,12 +2288,23 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, y.wrap(sol.y); z.wrap(sol.z); - dual::status_t status = dual::status_t::UNSET; + // Declare instrumented vectors used during initialization (before manifold setup) + ins_vector objective(lp.objective); + ins_vector c_basic(m); + ins_vector xB_workspace(m); - raft::common::nvtx::push_range("DualSimplex::phase2_advanced_init"); + // Create instrumentation manifold early to capture init section memory operations + instrumentation_manifold_t manifold; + manifold.add("x", x); + manifold.add("y", y); + manifold.add("z", z); + manifold.add("objective", objective); + manifold.add("c_basic", c_basic); + manifold.add("xB_workspace", xB_workspace); - // Perturbed objective (instrumented for main loop tracking) - ins_vector objective(lp.objective); + dual::status_t status = dual::status_t::UNSET; + + nvtx_range_guard init_scope("DualSimplex::phase2_advanced_init"); settings.log.printf("Dual Simplex Phase %d\n", phase); std::vector vstatus_old = vstatus; @@ -2257,7 +2312,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, phase2::bound_info(lp, settings); if (initialize_basis) { - raft::common::nvtx::push_range("DualSimplex::init_basis"); + raft::common::nvtx::range init_basis_scope("DualSimplex::init_basis"); std::vector superbasic_list; nonbasic_list.clear(); nonbasic_list.reserve(n - m); @@ -2272,7 +2327,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, if (toc(start_time) > settings.time_limit) { return dual::status_t::TIME_LIMIT; } } - std::vector c_basic(m); + // Populate c_basic after basis is initialized for (i_t k = 0; k < m; ++k) { const i_t j = basic_list[k]; c_basic[k] = objective[j]; @@ -2324,7 +2379,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } phase2::compute_primal_variables( - ft, lp.rhs, lp.A, basic_list, nonbasic_list, settings.tight_tol, sol.x); + ft, lp.rhs, lp.A, basic_list, nonbasic_list, settings.tight_tol, sol.x, xB_workspace); if (toc(start_time) > settings.time_limit) { return dual::status_t::TIME_LIMIT; } if (print_norms) { settings.log.printf("|| x || %e\n", vector_norm2(sol.x)); } @@ -2339,6 +2394,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, #endif if (delta_y_steepest_edge.size() == 0) { + raft::common::nvtx::range scope("DualSimplex::initialize_steepest_edge_norms"); delta_y_steepest_edge.resize(n); if (slack_basis) { phase2::initialize_steepest_edge_norms_from_slack_basis( @@ -2392,11 +2448,16 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, #endif csc_matrix_t A_transpose(1, 1, 0); - lp.A.transpose(A_transpose); - + manifold.add("A_transpose.col_start", A_transpose.col_start); + manifold.add("A_transpose.i", A_transpose.i); + manifold.add("A_transpose.x", A_transpose.x); + { + raft::common::nvtx::range scope("DualSimplex::transpose_A"); + lp.A.transpose(A_transpose); + } f_t obj = compute_objective(lp, sol.x); - raft::common::nvtx::pop_range(); // advanced_basis_init + init_scope.pop(); // End phase2_advanced_init range const i_t start_iter = iter; @@ -2422,8 +2483,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, sparse_vector_t v_sparse(m, 0); // For steepest edge norms sparse_vector_t atilde_sparse(m, 0); // For flip adjustments - // Create instrumentation manifold - instrumentation_manifold_t manifold; + // Add remaining instrumented vectors to manifold (x, y, z, objective, c_basic, xB_workspace added + // earlier) Delta vectors manifold.add("delta_y", delta_y); manifold.add("delta_z", delta_z); manifold.add("delta_x", delta_x); @@ -2439,10 +2500,6 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, manifold.add("squared_infeasibilities", squared_infeasibilities); manifold.add("infeasibility_indices", infeasibility_indices); manifold.add("bounded_variables", bounded_variables); - // Ratio test vectors - // manifold.add("vstatus", vstatus); - // manifold.add("nonbasic_list", nonbasic_list); - manifold.add("z", z); // Add sparse vector internal arrays to manifold manifold.add("delta_y_sparse.i", delta_y_sparse.i); @@ -2508,6 +2565,24 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, if (!work_unit_context) return; i_t remaining_iters = iter - last_feature_log_iter; if (remaining_iters <= 0) return; + + auto [total_loads, total_stores] = manifold.collect_and_flush(); + features.byte_loads = total_loads; + features.byte_stores = total_stores; + + f_t now = toc(start_time); + features.interval_runtime = now - interval_start_time; + interval_start_time = now; + + features.iteration = iter; + features.num_refactors = num_refactors; + features.num_basis_updates = ft.num_updates(); + features.sparse_delta_z_count = sparse_delta_z; + features.dense_delta_z_count = dense_delta_z; + features.total_bound_flips = total_bound_flips; + features.num_infeasibilities = infeasibility_indices.size(); + features.delta_y_nz_percentage = delta_y_nz_percentage; + f_t prediction = predict_work_units(remaining_iters); // printf("DualSimplex determ (final): %d iters, predicted %.4f\n", remaining_iters, // prediction); @@ -2523,7 +2598,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, f_t max_val; timers.start_timer(); { - raft::common::nvtx::range scope_pricing("DualSimplex::pricing"); + // raft::common::nvtx::range scope_pricing("DualSimplex::pricing"); if (settings.use_steepest_edge_pricing) { leaving_index = phase2::steepest_edge_pricing_with_infeasibilities(lp, settings, @@ -2568,7 +2643,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, delta_y_sparse.clear(); UTsol_sparse.clear(); { - raft::common::nvtx::range scope_btran("DualSimplex::btran"); + // raft::common::nvtx::range scope_btran("DualSimplex::btran"); phase2::compute_delta_y(ft, basic_leaving_index, direction, delta_y_sparse, UTsol_sparse); } timers.btran_time += timers.stop_timer(); @@ -2643,7 +2718,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, const bool harris_ratio = settings.use_harris_ratio; const bool bound_flip_ratio = settings.use_bound_flip_ratio; { - raft::common::nvtx::range scope_ratio("DualSimplex::ratio_test"); + // raft::common::nvtx::range scope_ratio("DualSimplex::ratio_test"); if (harris_ratio) { f_t max_step_length = phase2::first_stage_harris(lp, vstatus, nonbasic_list, z, delta_z); entering_index = phase2::second_stage_harris(lp, @@ -2708,7 +2783,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, std::vector unperturbed_x(n); phase2::compute_primal_solution_from_basis( - lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); + lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x, xB_workspace); sol.x = unperturbed_x; primal_infeasibility = phase2::compute_initial_primal_infeasibilities( lp, settings, basic_list, sol.x, squared_infeasibilities, infeasibility_indices); @@ -2745,7 +2820,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, } else { std::vector unperturbed_x(n); phase2::compute_primal_solution_from_basis( - lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); + lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x, xB_workspace); sol.x = unperturbed_x; primal_infeasibility = phase2::compute_initial_primal_infeasibilities( lp, settings, basic_list, sol.x, squared_infeasibilities, infeasibility_indices); @@ -2883,7 +2958,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, scaled_delta_xB_sparse.clear(); rhs_sparse.from_csc_column(lp.A, entering_index); { - raft::common::nvtx::range scope_ftran("DualSimplex::ftran"); + // raft::common::nvtx::range scope_ftran("DualSimplex::ftran"); if (phase2::compute_delta_x(lp, ft, entering_index, @@ -3029,7 +3104,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, timers.start_timer(); // Refactor or update the basis factorization { - raft::common::nvtx::range scope_update("DualSimplex::basis_update"); + // raft::common::nvtx::range scope_update("DualSimplex::basis_update"); bool should_refactor = ft.num_updates() > settings.refactor_frequency; if (!should_refactor) { i_t recommend_refactor = ft.update(utilde_sparse, UTsol_sparse, basic_leaving_index); @@ -3072,7 +3147,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, if (should_recompute_x) { std::vector unperturbed_x(n); phase2::compute_primal_solution_from_basis( - lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x); + lp, ft, basic_list, nonbasic_list, vstatus, unperturbed_x, xB_workspace); sol.x = unperturbed_x; } phase2::compute_initial_primal_infeasibilities( diff --git a/cpp/src/dual_simplex/right_looking_lu.cpp b/cpp/src/dual_simplex/right_looking_lu.cpp index c4a9bda011..b62e3b0caa 100644 --- a/cpp/src/dual_simplex/right_looking_lu.cpp +++ b/cpp/src/dual_simplex/right_looking_lu.cpp @@ -1,12 +1,13 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ #include #include +#include #include @@ -14,6 +15,8 @@ #include #include +using cuopt::ins_vector; + namespace cuopt::linear_programming::dual_simplex { namespace { @@ -30,9 +33,9 @@ struct element_t { }; constexpr int kNone = -1; -template +template i_t initialize_degree_data(const csc_matrix_t& A, - const std::vector& column_list, + const VectorI& column_list, std::vector& Cdegree, std::vector& Rdegree, std::vector>& col_count, @@ -69,9 +72,9 @@ i_t initialize_degree_data(const csc_matrix_t& A, return Bnz; } -template +template i_t load_elements(const csc_matrix_t& A, - const std::vector& column_list, + const VectorI& column_list, i_t Bnz, std::vector>& elements, std::vector& first_in_row, @@ -569,15 +572,15 @@ void remove_pivot_col(i_t pivot_i, } // namespace -template +template i_t right_looking_lu(const csc_matrix_t& A, const simplex_solver_settings_t& settings, f_t tol, - const std::vector& column_list, - std::vector& q, + const VectorI& column_list, + VectorI& q, csc_matrix_t& L, csc_matrix_t& U, - std::vector& pinv) + VectorI& pinv) { raft::common::nvtx::range scope("LU::right_looking_lu"); const i_t n = column_list.size(); @@ -1146,14 +1149,25 @@ i_t right_looking_lu_row_permutation_only(const csc_matrix_t& A, #ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE -template int right_looking_lu(const csc_matrix_t& A, - const simplex_solver_settings_t& settings, - double tol, - const std::vector& column_list, - std::vector& q, - csc_matrix_t& L, - csc_matrix_t& U, - std::vector& pinv); +template int right_looking_lu>( + const csc_matrix_t& A, + const simplex_solver_settings_t& settings, + double tol, + const std::vector& column_list, + std::vector& q, + csc_matrix_t& L, + csc_matrix_t& U, + std::vector& pinv); + +template int right_looking_lu>( + const csc_matrix_t& A, + const simplex_solver_settings_t& settings, + double tol, + const ins_vector& column_list, + ins_vector& q, + csc_matrix_t& L, + csc_matrix_t& U, + ins_vector& pinv); template int right_looking_lu_row_permutation_only( const csc_matrix_t& A, diff --git a/cpp/src/dual_simplex/right_looking_lu.hpp b/cpp/src/dual_simplex/right_looking_lu.hpp index 2f985fb363..179fff01b2 100644 --- a/cpp/src/dual_simplex/right_looking_lu.hpp +++ b/cpp/src/dual_simplex/right_looking_lu.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -14,15 +14,15 @@ namespace cuopt::linear_programming::dual_simplex { -template +template i_t right_looking_lu(const csc_matrix_t& A, const simplex_solver_settings_t& settings, f_t tol, - const std::vector& column_list, - std::vector& q, + const VectorI& column_list, + VectorI& q, csc_matrix_t& L, csc_matrix_t& U, - std::vector& pinv); + VectorI& pinv); template i_t right_looking_lu_row_permutation_only(const csc_matrix_t& A, diff --git a/cpp/src/dual_simplex/singletons.cpp b/cpp/src/dual_simplex/singletons.cpp index 71603306ca..a3f417c603 100644 --- a/cpp/src/dual_simplex/singletons.cpp +++ b/cpp/src/dual_simplex/singletons.cpp @@ -1,15 +1,18 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ #include #include +#include #include +using cuopt::ins_vector; + namespace cuopt::linear_programming::dual_simplex { // Destroys the queue but prints it @@ -128,8 +131,8 @@ void create_row_representation(const csc_matrix_t& A, } // Complete the permuation -template -i_t complete_permutation(i_t singletons, std::vector& Xdeg, std::vector& Xperm) +template +i_t complete_permutation(i_t singletons, std::vector& Xdeg, VectorI& Xperm) { i_t n = Xdeg.size(); assert(Xperm.size() == n); @@ -154,12 +157,12 @@ i_t complete_permutation(i_t singletons, std::vector& Xdeg, std::vector +template i_t find_singletons(const csc_matrix_t& A, i_t& row_singletons, - std::vector& row_perm, + VectorI& row_perm, i_t& col_singletons, - std::vector& col_perm) + VectorI& col_perm) { i_t n = A.n; i_t m = A.m; @@ -198,12 +201,15 @@ i_t find_singletons(const csc_matrix_t& A, row_form = true; // Find column singletons + // Use .underlying() to get std::vector iterators for row_col_graph_t + auto& col_perm_vec = static_cast&>(col_perm); + auto& row_perm_vec = static_cast&>(row_perm); row_col_graph_t graph{Cdeg.begin(), - col_perm.begin(), + col_perm_vec.begin(), A.col_start.underlying().cbegin(), A.i.underlying().cbegin(), Rdeg.begin(), - row_perm.begin(), + row_perm_vec.begin(), Rp.cbegin(), Rj.cbegin()}; @@ -229,12 +235,15 @@ i_t find_singletons(const csc_matrix_t& A, } // Find row singletons + // Use .underlying() to get std::vector iterators for row_col_graph_t + auto& row_perm_vec2 = static_cast&>(row_perm); + auto& col_perm_vec2 = static_cast&>(col_perm); row_col_graph_t graph{Rdeg.begin(), - row_perm.begin(), + row_perm_vec2.begin(), Rp.cbegin(), Rj.cbegin(), Cdeg.begin(), - col_perm.begin(), + col_perm_vec2.begin(), A.col_start.underlying().cbegin(), A.i.underlying().cbegin()}; #ifdef SINGLETON_DEBUG @@ -280,15 +289,24 @@ template void create_row_representation(const csc_matrix_t& col_index, std::vector& workspace); // Complete the permuation -template int complete_permutation(int singletons, - std::vector& Xdeg, - std::vector& Xperm); - -template int find_singletons(const csc_matrix_t& A, - int& row_singletons, - std::vector& row_perm, - int& col_singleton, - std::vector& col_perm); +template int complete_permutation>(int singletons, + std::vector& Xdeg, + std::vector& Xperm); +template int complete_permutation>(int singletons, + std::vector& Xdeg, + ins_vector& Xperm); + +template int find_singletons>(const csc_matrix_t& A, + int& row_singletons, + std::vector& row_perm, + int& col_singleton, + std::vector& col_perm); + +template int find_singletons>(const csc_matrix_t& A, + int& row_singletons, + ins_vector& row_perm, + int& col_singleton, + ins_vector& col_perm); #endif diff --git a/cpp/src/dual_simplex/singletons.hpp b/cpp/src/dual_simplex/singletons.hpp index b9cfcaa9be..2a1ac3e55f 100644 --- a/cpp/src/dual_simplex/singletons.hpp +++ b/cpp/src/dual_simplex/singletons.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -42,11 +42,11 @@ void create_row_representationon(const csc_matrix_t& A, template i_t complete_permutationn(i_t singletons, std::vector& Xdeg, std::vector& Xperm); -template +template i_t find_singletons(const csc_matrix_t& A, i_t& row_singletons, - std::vector& row_perm, + VectorI& row_perm, i_t& col_singleton, - std::vector& col_perm); + VectorI& col_perm); } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/sparse_matrix.cpp b/cpp/src/dual_simplex/sparse_matrix.cpp index 87aa26ab2b..080bed53e1 100644 --- a/cpp/src/dual_simplex/sparse_matrix.cpp +++ b/cpp/src/dual_simplex/sparse_matrix.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -951,12 +951,9 @@ template double sparse_dot(const std::vector& xind, const csc_matrix_t& Y, int y_col); -template int matrix_vector_multiply, std::allocator>( - const csc_matrix_t& A, - double alpha, - const std::vector>& x, - double beta, - std::vector>& y); +// NOTE: matrix_vector_multiply is now templated on VectorX and VectorY. +// Since it's defined inline in the header, no explicit instantiation is needed here. + template int matrix_transpose_vector_multiply, std::allocator>( const csc_matrix_t& A, diff --git a/cpp/src/dual_simplex/sparse_matrix.hpp b/cpp/src/dual_simplex/sparse_matrix.hpp index 5151432f51..c2d945543e 100644 --- a/cpp/src/dual_simplex/sparse_matrix.hpp +++ b/cpp/src/dual_simplex/sparse_matrix.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -280,12 +280,9 @@ i_t matrix_transpose_vector_multiply(const csc_matrix_t& A, } // y <- alpha*A*x + beta*y -template -i_t matrix_vector_multiply(const csc_matrix_t& A, - f_t alpha, - const std::vector& x, - f_t beta, - std::vector& y) +template +i_t matrix_vector_multiply( + const csc_matrix_t& A, f_t alpha, const VectorX& x, f_t beta, VectorY& y) { // y <- alpha*A*x + beta*y i_t m = A.m; diff --git a/cpp/src/dual_simplex/triangle_solve.cpp b/cpp/src/dual_simplex/triangle_solve.cpp index 3757846508..2f280295bf 100644 --- a/cpp/src/dual_simplex/triangle_solve.cpp +++ b/cpp/src/dual_simplex/triangle_solve.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -11,73 +11,9 @@ namespace cuopt::linear_programming::dual_simplex { -template -i_t lower_triangular_solve(const csc_matrix_t& L, std::vector& x) -{ - i_t n = L.n; - assert(x.size() == n); - for (i_t j = 0; j < n; ++j) { - i_t col_start = L.col_start[j]; - i_t col_end = L.col_start[j + 1]; - if (x[j] != 0.0) { - x[j] /= L.x[col_start]; - for (i_t p = col_start + 1; p < col_end; ++p) { - x[L.i[p]] -= L.x[p] * x[j]; - } - } - } - return 0; -} - -template -i_t lower_triangular_transpose_solve(const csc_matrix_t& L, std::vector& x) -{ - const i_t n = L.n; - assert(x.size() == n); - for (i_t j = n - 1; j >= 0; --j) { - const i_t col_start = L.col_start[j] + 1; - const i_t col_end = L.col_start[j + 1]; - for (i_t p = col_start; p < col_end; ++p) { - x[j] -= L.x[p] * x[L.i[p]]; - } - x[j] /= L.x[L.col_start[j]]; - } - return 0; -} - -template -i_t upper_triangular_solve(const csc_matrix_t& U, std::vector& x) -{ - const i_t n = U.n; - assert(x.size() == n); - for (i_t j = n - 1; j >= 0; --j) { - const i_t col_start = U.col_start[j]; - const i_t col_end = U.col_start[j + 1] - 1; - if (x[j] != 0.0) { - x[j] /= U.x[col_end]; - for (i_t p = col_start; p < col_end; ++p) { - x[U.i[p]] -= U.x[p] * x[j]; - } - } - } - return 0; -} - -template -i_t upper_triangular_transpose_solve(const csc_matrix_t& U, std::vector& x) -{ - const i_t n = U.n; - assert(x.size() == n); - for (i_t j = 0; j < n; ++j) { - const i_t col_start = U.col_start[j]; - const i_t col_end = U.col_start[j + 1] - 1; - for (i_t p = col_start; p < col_end; ++p) { - x[j] -= U.x[p] * x[U.i[p]]; - } - x[j] /= U.x[col_end]; - } - return 0; -} +// NOTE: lower_triangular_solve, lower_triangular_transpose_solve, +// upper_triangular_solve, and upper_triangular_transpose_solve are now +// templated on vector types and defined in the header file. // \brief Reach computes the reach of b in the graph of G // \param[in] b - Sparse vector containing the rhs @@ -204,17 +140,9 @@ i_t sparse_triangle_solve(const sparse_vector_t& b, #ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE -template int lower_triangular_solve(const csc_matrix_t& L, - std::vector& x); - -template int lower_triangular_transpose_solve(const csc_matrix_t& L, - std::vector& x); - -template int upper_triangular_solve(const csc_matrix_t& U, - std::vector& x); - -template int upper_triangular_transpose_solve(const csc_matrix_t& U, - std::vector& x); +// NOTE: lower_triangular_solve, lower_triangular_transpose_solve, +// upper_triangular_solve, and upper_triangular_transpose_solve are now +// templated on vector types and defined in the header file, so no explicit instantiation needed. template int reach(const sparse_vector_t& b, const std::optional>& pinv, diff --git a/cpp/src/dual_simplex/triangle_solve.hpp b/cpp/src/dual_simplex/triangle_solve.hpp index 18dbafc855..c940eee485 100644 --- a/cpp/src/dual_simplex/triangle_solve.hpp +++ b/cpp/src/dual_simplex/triangle_solve.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -25,23 +25,79 @@ namespace cuopt::linear_programming::dual_simplex { // Solve L*x = b. On input x contains the right-hand side b, on output the // solution -template -i_t lower_triangular_solve(const csc_matrix_t& L, std::vector& x); +template +i_t lower_triangular_solve(const csc_matrix_t& L, VectorF& x) +{ + i_t n = L.n; + assert(x.size() == n); + for (i_t j = 0; j < n; ++j) { + i_t col_start = L.col_start[j]; + i_t col_end = L.col_start[j + 1]; + if (x[j] != 0.0) { + x[j] /= L.x[col_start]; + for (i_t p = col_start + 1; p < col_end; ++p) { + x[L.i[p]] -= L.x[p] * x[j]; + } + } + } + return 0; +} // Solve L'*x = b. On input x contains the right-hand side b, on output the // solution -template -i_t lower_triangular_transpose_solve(const csc_matrix_t& L, std::vector& x); +template +i_t lower_triangular_transpose_solve(const csc_matrix_t& L, VectorF& x) +{ + const i_t n = L.n; + assert(x.size() == n); + for (i_t j = n - 1; j >= 0; --j) { + const i_t col_start = L.col_start[j] + 1; + const i_t col_end = L.col_start[j + 1]; + for (i_t p = col_start; p < col_end; ++p) { + x[j] -= L.x[p] * x[L.i[p]]; + } + x[j] /= L.x[L.col_start[j]]; + } + return 0; +} // Solve U*x = b. On input x contains the right-hand side b, on output the // solution -template -i_t upper_triangular_solve(const csc_matrix_t& U, std::vector& x); +template +i_t upper_triangular_solve(const csc_matrix_t& U, VectorF& x) +{ + const i_t n = U.n; + assert(x.size() == n); + for (i_t j = n - 1; j >= 0; --j) { + const i_t col_start = U.col_start[j]; + const i_t col_end = U.col_start[j + 1] - 1; + if (x[j] != 0.0) { + x[j] /= U.x[col_end]; + for (i_t p = col_start; p < col_end; ++p) { + x[U.i[p]] -= U.x[p] * x[j]; + } + } + } + return 0; +} // Solve U'*x = b. On input x contains the right-hand side b, on output the // solution -template -i_t upper_triangular_transpose_solve(const csc_matrix_t& U, std::vector& x); +template +i_t upper_triangular_transpose_solve(const csc_matrix_t& U, VectorF& x) +{ + const i_t n = U.n; + assert(x.size() == n); + for (i_t j = 0; j < n; ++j) { + const i_t col_start = U.col_start[j]; + const i_t col_end = U.col_start[j + 1] - 1; + for (i_t p = col_start; p < col_end; ++p) { + x[j] -= U.x[p] * x[U.i[p]]; + } + x[j] /= U.x[col_end]; + } + return 0; +} // \brief Reach computes the reach of b in the graph of G // \param[in] b - sparse vector containing the rhs diff --git a/cpp/src/dual_simplex/vector_math.cpp b/cpp/src/dual_simplex/vector_math.cpp index b935d18e47..f20d17afee 100644 --- a/cpp/src/dual_simplex/vector_math.cpp +++ b/cpp/src/dual_simplex/vector_math.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -122,44 +122,8 @@ f_t sparse_dot(const std::vector& xind, return dot; } -// x = b(p) -template -i_t permute_vector(const std::vector& p, const std::vector& b, std::vector& x) -{ - i_t n = p.size(); - assert(x.size() == n); - assert(b.size() == n); - for (i_t k = 0; k < n; ++k) { - x[k] = b[p[k]]; - } - return 0; -} - -// x(p) = b -template -i_t inverse_permute_vector(const std::vector& p, - const std::vector& b, - std::vector& x) -{ - i_t n = p.size(); - assert(x.size() == n); - assert(b.size() == n); - for (i_t k = 0; k < n; ++k) { - x[p[k]] = b[k]; - } - return 0; -} - -template -i_t inverse_permutation(const std::vector& p, std::vector& pinv) -{ - i_t n = p.size(); - if (pinv.size() != n) { pinv.resize(n); } - for (i_t k = 0; k < n; ++k) { - pinv[p[k]] = k; - } - return 0; -} +// NOTE: permute_vector, inverse_permute_vector, and inverse_permutation are now +// templated on vector types and defined in the header file. #ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE @@ -195,15 +159,8 @@ template double sparse_dot(int const* xind, template double sparse_dot( int* xind, double* xval, int nx, int* yind, double* yval, int ny); -template int permute_vector(const std::vector& p, - const std::vector& b, - std::vector& x); - -template int inverse_permute_vector(const std::vector& p, - const std::vector& b, - std::vector& x); - -template int inverse_permutation(const std::vector& p, std::vector& pinv); +// NOTE: permute_vector, inverse_permute_vector, and inverse_permutation are now +// templated on vector types and defined in the header file, so no explicit instantiation needed. #endif } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/vector_math.hpp b/cpp/src/dual_simplex/vector_math.hpp index 44a4599356..6bf573e892 100644 --- a/cpp/src/dual_simplex/vector_math.hpp +++ b/cpp/src/dual_simplex/vector_math.hpp @@ -1,12 +1,13 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ #pragma once +#include #include #include @@ -56,17 +57,41 @@ template f_t sparse_dot(i_t* xind, f_t* xval, i_t nx, i_t* yind, f_t* yval, i_t ny); // Computes x = P*b or x=b(p) in MATLAB. -template -i_t permute_vector(const std::vector& p, const std::vector& b, std::vector& x); +template +int permute_vector(const VectorI& p, const VectorF_in& b, VectorF_out& x) +{ + auto n = p.size(); + assert(x.size() == n); + assert(b.size() == n); + for (decltype(n) k = 0; k < n; ++k) { + x[k] = b[p[k]]; + } + return 0; +} // Computes x = P'*b or x(p) = b in MATLAB. -template -i_t inverse_permute_vector(const std::vector& p, - const std::vector& b, - std::vector& x); +template +int inverse_permute_vector(const VectorI& p, const VectorF_in& b, VectorF_out& x) +{ + auto n = p.size(); + assert(x.size() == n); + assert(b.size() == n); + for (decltype(n) k = 0; k < n; ++k) { + x[p[k]] = b[k]; + } + return 0; +} // Computes pinv from p. Or pinv(p) = 1:n in MATLAB -template -i_t inverse_permutation(const std::vector& p, std::vector& pinv); +template +int inverse_permutation(const VectorI_in& p, VectorI_out& pinv) +{ + auto n = p.size(); + if (pinv.size() != n) { pinv.resize(n); } + for (decltype(n) k = 0; k < n; ++k) { + pinv[p[k]] = k; + } + return 0; +} } // namespace cuopt::linear_programming::dual_simplex From 8fdbff8729c9945bf5a86711569ea7366bfcc7d4 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 15 Jan 2026 15:45:52 +0000 Subject: [PATCH 120/225] fix sync bug on termination --- cpp/src/dual_simplex/branch_and_bound.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 6757a51844..df8c89ac7d 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -2096,16 +2096,14 @@ void branch_and_bound_t::run_worker_loop(bb_worker_state_t& worker.last_solved_node = node; // Handle result - if (status == node_solve_info_t::TIME_LIMIT) { + if (status == node_solve_info_t::TIME_LIMIT || status == node_solve_info_t::WORK_LIMIT) { worker.current_node = nullptr; - solver_status_ = mip_exploration_status_t::TIME_LIMIT; - bsp_terminated_.store(true); - break; // Exit loop - scheduler will stop all workers at next sync - } else if (status == node_solve_info_t::WORK_LIMIT) { - worker.current_node = nullptr; - solver_status_ = mip_exploration_status_t::WORK_LIMIT; - bsp_terminated_.store(true); - break; // Exit loop - scheduler will stop all workers at next sync + // Don't set solver_status_ or bsp_terminated_ here - that would cause a race + // where other workers see the flag and exit before reaching the sync point. + // The sync callback will check the work/time limit and set termination flags + // when all workers are safely at the barrier. + bsp_scheduler_->wait_for_next_sync(worker.work_context); + break; } else { // Node completed successfully worker.current_node = nullptr; From b6d7ecc020530fbe180859440374f28ad868900b Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 15 Jan 2026 17:25:51 +0000 Subject: [PATCH 121/225] revert disable heuristics --- cpp/src/mip/diversity/diversity_manager.cu | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index ea1381022c..f205caa6e8 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -315,7 +315,8 @@ solution_t diversity_manager_t::run_solver() // Debug: Allow disabling GPU heuristics to test B&B tree determinism in isolation const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); - disable_heuristics_env = "1"; // DO NOT REMOVE! intended debugging line! + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) + disable_heuristics_env = "1"; // DO NOT REMOVE! intended debugging line! if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); // Initialize population minimally and wait for B&B to finish From 668391ebedabf297b10eedde6c092f5857d9eef8 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 15 Jan 2026 19:14:46 +0000 Subject: [PATCH 122/225] no presovle when determinsitic --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 66435da2b3..f746fb71a4 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -207,7 +207,7 @@ int run_single_file(std::string file_path, settings.tolerances.absolute_tolerance = 1e-6; settings.presolve = true; - settings.presolve = false; + if (deterministic) { settings.presolve = false; } cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; From 3b6d532e93978e4d0362eca80a08817fec76fddf Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 16 Jan 2026 09:10:45 +0000 Subject: [PATCH 123/225] restore nondeterminsitc codepath behavior --- cpp/src/dual_simplex/branch_and_bound.cpp | 6 ++++-- cpp/src/dual_simplex/presolve.cpp | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index df8c89ac7d..93603d68ee 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -2419,8 +2419,10 @@ node_solve_info_t branch_and_bound_t::solve_node_bsp(bb_worker_state_t bool feasible = true; // TODO: incorporate into work unit estimation - // feasible = worker.node_presolver->bounds_strengthening( - // worker.leaf_problem->lower, worker.leaf_problem->upper, lp_settings); + if (!settings_.deterministic) { + feasible = worker.node_presolver->bounds_strengthening( + worker.leaf_problem->lower, worker.leaf_problem->upper, lp_settings); + } if (!feasible) { node_ptr->lower_bound = std::numeric_limits::infinity(); diff --git a/cpp/src/dual_simplex/presolve.cpp b/cpp/src/dual_simplex/presolve.cpp index d0211c7241..7a2f81c7ec 100644 --- a/cpp/src/dual_simplex/presolve.cpp +++ b/cpp/src/dual_simplex/presolve.cpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -622,7 +622,8 @@ void convert_user_problem(const user_problem_t& user_problem, convert_greater_to_less(user_problem, row_sense, problem, greater_rows, less_rows); } - constexpr bool run_bounds_strengthening = false; + bool run_bounds_strengthening = true; + if (settings.deterministic) { run_bounds_strengthening = false; } if (run_bounds_strengthening) { csr_matrix_t Arow(1, 1, 1); problem.A.to_compressed_row(Arow); From 07cea4d3f79fb82d84109f7ad1ad0ac1b00fcf9f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 16 Jan 2026 10:37:28 +0000 Subject: [PATCH 124/225] cleanup work --- cpp/src/dual_simplex/branch_and_bound.cpp | 2 +- cpp/src/dual_simplex/pseudo_costs.cpp | 2 +- cpp/src/dual_simplex/singletons.cpp | 2 - cpp/src/linear_programming/pdlp_features.hpp | 58 ++++----- cpp/src/mip/diversity/diversity_manager.cu | 26 ++-- .../recombiners/bound_prop_recombiner.cuh | 18 +-- .../diversity/recombiners/fp_recombiner.cuh | 12 +- .../recombiners/line_segment_recombiner.cuh | 4 +- .../mip/diversity/recombiners/recombiner.cuh | 8 +- .../mip/feasibility_jump/feasibility_jump.cu | 6 +- .../feasibility_jump_kernels.cu | 19 +-- .../feasibility_pump/feasibility_pump.cu | 44 +++---- .../line_segment_search.cu | 3 +- cpp/src/mip/local_search/local_search.cu | 5 +- .../local_search/rounding/bounds_repair.cu | 3 +- .../local_search/rounding/constraint_prop.cu | 59 +++++---- .../local_search/rounding/lb_bounds_repair.cu | 5 +- cpp/src/mip/problem/problem.cu | 4 - cpp/src/mip/relaxed_lp/relaxed_lp.cu | 123 +++++++++--------- cpp/src/mip/solver.cu | 16 +-- 20 files changed, 191 insertions(+), 228 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 93603d68ee..06dee7d22b 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -379,7 +379,7 @@ void branch_and_bound_t::flush_pending_features() last_features_.node_status); // Single printf call - settings_.log.printf("%s", line_buffer); + // settings_.log.printf("%s", line_buffer); has_pending_features_ = false; } diff --git a/cpp/src/dual_simplex/pseudo_costs.cpp b/cpp/src/dual_simplex/pseudo_costs.cpp index cdddf0a108..5d87a7abb5 100644 --- a/cpp/src/dual_simplex/pseudo_costs.cpp +++ b/cpp/src/dual_simplex/pseudo_costs.cpp @@ -159,7 +159,7 @@ void strong_branching(const lp_problem_t& original_lp, settings.num_threads, fractional.size()); -#pragma omp parallel num_threads(64) +#pragma omp parallel num_threads(settings.num_threads) { i_t n = std::min(4 * settings.num_threads, fractional.size()); diff --git a/cpp/src/dual_simplex/singletons.cpp b/cpp/src/dual_simplex/singletons.cpp index a3f417c603..347604dcea 100644 --- a/cpp/src/dual_simplex/singletons.cpp +++ b/cpp/src/dual_simplex/singletons.cpp @@ -201,7 +201,6 @@ i_t find_singletons(const csc_matrix_t& A, row_form = true; // Find column singletons - // Use .underlying() to get std::vector iterators for row_col_graph_t auto& col_perm_vec = static_cast&>(col_perm); auto& row_perm_vec = static_cast&>(row_perm); row_col_graph_t graph{Cdeg.begin(), @@ -235,7 +234,6 @@ i_t find_singletons(const csc_matrix_t& A, } // Find row singletons - // Use .underlying() to get std::vector iterators for row_col_graph_t auto& row_perm_vec2 = static_cast&>(row_perm); auto& col_perm_vec2 = static_cast&>(col_perm); row_col_graph_t graph{Rdeg.begin(), diff --git a/cpp/src/linear_programming/pdlp_features.hpp b/cpp/src/linear_programming/pdlp_features.hpp index 76f0169144..6af6b01053 100644 --- a/cpp/src/linear_programming/pdlp_features.hpp +++ b/cpp/src/linear_programming/pdlp_features.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -188,34 +188,34 @@ struct pdlp_features_t { (interval_time_ms / 1000.0) : 0.0; - printf( - "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6e nnz_stddev=%.4f " - "unbalancedness=%.4f interval_iters=%d interval_major=%d interval_restarts=%d " - "spmv_ops=%ld total_iters=%d total_restarts=%d iters_since_restart=%d " - "primal_res=%.6e dual_res=%.6e gap=%.6e kkt=%.6e " - "step_size=%.6e primal_weight=%.6e time_ms=%.2f nnz_per_s=%.2e warm_start=%d", - n_vars, - n_cstrs, - nnz, - sparsity, - nnz_stddev, - unbalancedness, - interval_iters, - interval_major, - interval_restarts, - interval_spmv_ops, - total_iters, - total_restarts, - iters_since_restart, - primal_res, - dual_res, - gap, - kkt_score, - step_size, - primal_weight, - interval_time_ms, - nnz_per_sec, - static_cast(has_warm_start)); + // printf( + // "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6e nnz_stddev=%.4f " + // "unbalancedness=%.4f interval_iters=%d interval_major=%d interval_restarts=%d " + // "spmv_ops=%ld total_iters=%d total_restarts=%d iters_since_restart=%d " + // "primal_res=%.6e dual_res=%.6e gap=%.6e kkt=%.6e " + // "step_size=%.6e primal_weight=%.6e time_ms=%.2f nnz_per_s=%.2e warm_start=%d", + // n_vars, + // n_cstrs, + // nnz, + // sparsity, + // nnz_stddev, + // unbalancedness, + // interval_iters, + // interval_major, + // interval_restarts, + // interval_spmv_ops, + // total_iters, + // total_restarts, + // iters_since_restart, + // primal_res, + // dual_res, + // gap, + // kkt_score, + // step_size, + // primal_weight, + // interval_time_ms, + // nnz_per_sec, + // static_cast(has_warm_start)); } /** diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index f205caa6e8..d044bb49d5 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -151,8 +151,8 @@ void diversity_manager_t::add_user_given_solutions( rmm::device_uvector init_sol_assignment(*init_sol, sol.handle_ptr->get_stream()); if (problem_ptr->pre_process_assignment(init_sol_assignment)) { relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = std::min(60., timer.remaining_time() / 2); - lp_settings.work_limit = lp_settings.time_limit; + lp_settings.time_limit = std::min(60., timer.remaining_time() / 2); + if (timer.deterministic) { lp_settings.work_limit = lp_settings.time_limit; } lp_settings.tolerance = problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; @@ -316,7 +316,7 @@ solution_t diversity_manager_t::run_solver() // Debug: Allow disabling GPU heuristics to test B&B tree determinism in isolation const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) - disable_heuristics_env = "1"; // DO NOT REMOVE! intended debugging line! + disable_heuristics_env = "1"; // to cursor: DO NOT REMOVE! intended debugging line! if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); // Initialize population minimally and wait for B&B to finish @@ -393,13 +393,13 @@ solution_t diversity_manager_t::run_solver() pdlp_settings.tolerances.relative_primal_tolerance = absolute_tolerance / tolerance_divisor; pdlp_settings.tolerances.relative_dual_tolerance = absolute_tolerance / tolerance_divisor; pdlp_settings.time_limit = lp_time_limit; - pdlp_settings.iteration_limit = 100000; - pdlp_settings.first_primal_feasible = false; - pdlp_settings.concurrent_halt = &global_concurrent_halt; - pdlp_settings.method = method_t::Concurrent; - pdlp_settings.inside_mip = true; - pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; - pdlp_settings.num_gpus = context.settings.num_gpus; + if (timer.deterministic) { pdlp_settings.iteration_limit = 100000; } + pdlp_settings.first_primal_feasible = false; + pdlp_settings.concurrent_halt = &global_concurrent_halt; + pdlp_settings.method = method_t::Concurrent; + pdlp_settings.inside_mip = true; + pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; + pdlp_settings.num_gpus = context.settings.num_gpus; timer_t lp_timer(lp_time_limit); auto lp_result = solve_lp_with_method(*problem_ptr, pdlp_settings, lp_timer); @@ -722,8 +722,8 @@ diversity_manager_t::recombine_and_local_search(solution_t& : diversity_config.lp_run_time_if_infeasible; lp_run_time = std::min(lp_run_time, timer.remaining_time()); relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_run_time; - lp_settings.work_limit = lp_settings.time_limit; + lp_settings.time_limit = lp_run_time; + if (timer.deterministic) { lp_settings.work_limit = lp_settings.time_limit; } lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; lp_settings.return_first_feasible = false; lp_settings.save_state = true; @@ -800,7 +800,7 @@ std::pair, bool> diversity_manager_t::recombine( mab_recombiner.set_last_chosen_option(selected_index); recombine_stats.add_attempt((recombiner_enum_t)recombiner); recombine_stats.start_recombiner_time(); - CUOPT_LOG_DEBUG("Recombining sol %x and %x with recombiner %d, weights %x", + CUOPT_LOG_TRACE("Recombining sol %x and %x with recombiner %d, weights %x", a.get_hash(), b.get_hash(), recombiner, diff --git a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh index 3d217a2a53..5efd1d5459 100644 --- a/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/bound_prop_recombiner.cuh @@ -164,23 +164,23 @@ class bound_prop_recombiner_t : public recombiner_t { "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); // DETERMINISM DEBUG: Log everything that could affect divergence - CUOPT_LOG_DEBUG("BP_DET: sol_a_hash=0x%x sol_b_hash=0x%x offspring_hash=0x%x, seed %x", + CUOPT_LOG_TRACE("BP_DET: sol_a_hash=0x%x sol_b_hash=0x%x offspring_hash=0x%x, seed %x", a.get_hash(), b.get_hash(), offspring.get_hash(), seed); - CUOPT_LOG_DEBUG("BP_DET: n_different_vars=%d n_vars_from_other=%d n_vars_from_guiding=%d", + CUOPT_LOG_TRACE("BP_DET: n_different_vars=%d n_vars_from_other=%d n_vars_from_guiding=%d", n_different_vars, n_vars_from_other, n_vars_from_guiding); - CUOPT_LOG_DEBUG("BP_DET: remaining_indices_hash=0x%x (first %d elements)", + CUOPT_LOG_TRACE("BP_DET: remaining_indices_hash=0x%x (first %d elements)", detail::compute_hash(this->remaining_indices), std::min((i_t)10, n_vars_from_other)); - CUOPT_LOG_DEBUG("BP_DET: guiding_feasible=%d other_feasible=%d expensive_to_fix=%d", + CUOPT_LOG_TRACE("BP_DET: guiding_feasible=%d other_feasible=%d expensive_to_fix=%d", guiding_solution.get_feasible(), other_solution.get_feasible(), a.problem_ptr->expensive_to_fix_vars); - CUOPT_LOG_DEBUG( + CUOPT_LOG_TRACE( "BP_DET: fixed_from_guiding=%d fixed_from_other=%d", fixed_from_guiding, fixed_from_other); // if either all integers are from A(meaning all are common) or all integers are from B(meaning @@ -250,16 +250,16 @@ class bound_prop_recombiner_t : public recombiner_t { } a.handle_ptr->sync_stream(); } else { - CUOPT_LOG_DEBUG("BP_DET: Taking INFEASIBLE path (no variable fixing)"); + CUOPT_LOG_TRACE("BP_DET: Taking INFEASIBLE path (no variable fixing)"); work_limit_timer_t timer(this->context.gpu_heur_loop, bp_recombiner_config_t::bounds_prop_time_limit); get_probing_values_for_infeasible( guiding_solution, other_solution, offspring, probing_values, n_vars_from_other); probing_config.probing_values = host_copy(probing_values, offspring.handle_ptr->get_stream()); - CUOPT_LOG_DEBUG("BP_DET: probing_values_hash=0x%x", detail::compute_hash(probing_values)); + CUOPT_LOG_TRACE("BP_DET: probing_values_hash=0x%x", detail::compute_hash(probing_values)); constraint_prop.apply_round(offspring, lp_run_time_after_feasible, timer, probing_config); } - CUOPT_LOG_DEBUG("BP_DET: After apply_round: offspring_hash=0x%x feasible=%d", + CUOPT_LOG_TRACE("BP_DET: After apply_round: offspring_hash=0x%x feasible=%d", offspring.get_hash(), offspring.get_feasible()); constraint_prop.max_n_failed_repair_iterations = 1; @@ -281,7 +281,7 @@ class bound_prop_recombiner_t : public recombiner_t { bp_recombiner_config_t::decrease_max_n_of_vars_from_other(); } } - CUOPT_LOG_DEBUG( + CUOPT_LOG_TRACE( "BP_DET: Final offspring_hash=0x%x same_as_parents=%d better_cost=%d better_feas=%d", offspring.get_hash(), same_as_parents, diff --git a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh index f7451edfc9..7d1ee1dc26 100644 --- a/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/fp_recombiner.cuh @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -50,7 +50,7 @@ class fp_recombiner_t : public recombiner_t { CUOPT_LOG_DEBUG("FP rec: Number of different variables %d MAX_VARS %d", n_different_vars, fp_recombiner_config_t::max_n_of_vars_from_other); - CUOPT_LOG_DEBUG("FP rec: offspring hash 0x%x", offspring.get_hash()); + CUOPT_LOG_TRACE("FP rec: offspring hash 0x%x", offspring.get_hash()); i_t n_vars_from_other = n_different_vars; if (n_vars_from_other > (i_t)fp_recombiner_config_t::max_n_of_vars_from_other) { n_vars_from_other = fp_recombiner_config_t::max_n_of_vars_from_other; @@ -69,20 +69,20 @@ class fp_recombiner_t : public recombiner_t { double work = static_cast(n_vars_from_other); CUOPT_LOG_DEBUG( "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); - CUOPT_LOG_DEBUG("FP rec: offspring hash 0x%x, vars to fix 0x%x", + CUOPT_LOG_TRACE("FP rec: offspring hash 0x%x, vars to fix 0x%x", offspring.get_hash(), detail::compute_hash(vars_to_fix)); this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); - CUOPT_LOG_DEBUG("FP rec post computevarstofix: offspring hash 0x%x, vars to fix 0x%x", + CUOPT_LOG_TRACE("FP rec post computevarstofix: offspring hash 0x%x, vars to fix 0x%x", offspring.get_hash(), detail::compute_hash(vars_to_fix)); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); - CUOPT_LOG_DEBUG("FP rec: fixed_problem hash 0x%x assigned hash 0x%x", + CUOPT_LOG_TRACE("FP rec: fixed_problem hash 0x%x assigned hash 0x%x", fixed_problem.get_fingerprint(), detail::compute_hash(fixed_assignment)); fixed_problem.check_problem_representation(true); if (!guiding_solution.get_feasible() && !other_solution.get_feasible()) { - CUOPT_LOG_DEBUG("FP rec: running LP with infeasibility detection"); + CUOPT_LOG_TRACE("FP rec: running LP with infeasibility detection"); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { diff --git a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh index afa77092df..6ff47e712b 100644 --- a/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/line_segment_recombiner.cuh @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -71,7 +71,7 @@ class line_segment_recombiner_t : public recombiner_t { const weight_t& weights) { raft::common::nvtx::range fun_scope("line_segment_recombiner"); - CUOPT_LOG_DEBUG("LS rec: a %d b %d", a.get_hash(), b.get_hash()); + CUOPT_LOG_TRACE("LS rec: a %d b %d", a.get_hash(), b.get_hash()); auto& guiding_solution = a.get_feasible() ? a : b; auto& other_solution = a.get_feasible() ? b : a; // copy the solution from A diff --git a/cpp/src/mip/diversity/recombiners/recombiner.cuh b/cpp/src/mip/diversity/recombiners/recombiner.cuh index 925d9a7c2f..924bc162e0 100644 --- a/cpp/src/mip/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip/diversity/recombiners/recombiner.cuh @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -97,7 +97,7 @@ class recombiner_t { this->remaining_indices.data(), this->remaining_indices.data() + remaining_variables); - CUOPT_LOG_DEBUG("remaining indices hash 0x%x, size %d", + CUOPT_LOG_TRACE("remaining indices hash 0x%x, size %d", detail::compute_hash(this->remaining_indices), remaining_variables); @@ -181,8 +181,8 @@ class recombiner_t { i_t n_vars_from_guiding) { vars_to_fix.resize(n_vars_from_guiding, offspring.handle_ptr->get_stream()); - CUOPT_LOG_DEBUG("remaining indices hash 0x%x", detail::compute_hash(this->remaining_indices)); - CUOPT_LOG_DEBUG("integer_indices hash 0x%x", + CUOPT_LOG_TRACE("remaining indices hash 0x%x", detail::compute_hash(this->remaining_indices)); + CUOPT_LOG_TRACE("integer_indices hash 0x%x", detail::compute_hash(offspring.problem_ptr->integer_indices)); // set difference needs two sorted arrays thrust::sort(offspring.handle_ptr->get_thrust_policy(), diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump.cu b/cpp/src/mip/feasibility_jump/feasibility_jump.cu index 124236647a..6ecd86bd39 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump.cu @@ -865,7 +865,9 @@ void fj_t::refresh_lhs_and_violation(const rmm::cuda_stream_view& stre thrust::plus()); data.violation_score.set_value_async(violation, stream); data.weighted_violation_score.set_value_async(weighted_violation, stream); - data.violated_constraints.sort(stream); + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + data.violated_constraints.sort(stream); + } #if FJ_SINGLE_STEP CUOPT_LOG_DEBUG("hash assignment %x, hash lhs %x, hash lhscomp %x", detail::compute_hash(data.incumbent_assignment, stream), @@ -1289,7 +1291,7 @@ i_t fj_t::solve(solution_t& solution) bool deterministic = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; if (deterministic) { settings.work_limit = settings.time_limit; } // if work_limit is set: compute an estimate of the number of iterations required - if (settings.work_limit != std::numeric_limits::infinity()) { + if (deterministic && settings.work_limit != std::numeric_limits::infinity()) { std::map features_map = get_feature_vector(0); float iter_prediction = std::max( (f_t)0.0, (f_t)ceil(context.work_unit_predictors.fj_predictor.predict_scalar(features_map))); diff --git a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu index d337527d3c..06dc47ca39 100644 --- a/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip/feasibility_jump/feasibility_jump_kernels.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -1224,12 +1224,6 @@ DI thrust::tuple::move_score_t> gridwide_reduc // affected by tabu f_t delta = get_move(var_idx); - - // if (!WeakTabu && !recompute_score) { - // printf("iter[%d] block[%d] considered var %d delta %g\n", *fj.iterations, blockIdx.x, - // var_idx, delta); - // } - if constexpr (WeakTabu) { if ((delta < 0 && *fj.iterations == fj.tabu_lastinc[var_idx] + 1) || (delta > 0 && *fj.iterations == fj.tabu_lastdec[var_idx] + 1)) @@ -1250,12 +1244,6 @@ DI thrust::tuple::move_score_t> gridwide_reduc loc_best_score_info = compute_new_score(fj, var_idx, delta); } - // if (!WeakTabu && !recompute_score) { - // printf("iter[%d] block[%d] found score (%d,%d) for var %d delta %g\n", *fj.iterations, - // blockIdx.x, loc_best_score_info.score.base, loc_best_score_info.score.bonus, var_idx, - // delta); - // } - if (threadIdx.x == 0) { if (loc_best_score_info.score > best_score || (loc_best_score_info.score == best_score && var_idx > best_var)) { @@ -1267,11 +1255,6 @@ DI thrust::tuple::move_score_t> gridwide_reduc } if (threadIdx.x == 0) { - // if (!WeakTabu && !recompute_score) { - // printf("iter[%d] block[%d] var_idx %d score {%d,%d}, delta %g\n", *fj.iterations, - // blockIdx.x, best_var, best_score.base, best_score.bonus, best_delta); - // } - fj.grid_score_buf[blockIdx.x] = best_score; fj.grid_var_buf[blockIdx.x] = best_var; fj.grid_delta_buf[blockIdx.x] = best_delta; diff --git a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu index ecd93c2e27..f3cd07c62c 100644 --- a/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip/local_search/feasibility_pump/feasibility_pump.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -232,7 +232,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tn_variables, solution.handle_ptr->get_stream()); raft::copy(last_projection.data(), solution.assignment.data(), @@ -255,7 +255,7 @@ template bool feasibility_pump_t::round(solution_t& solution) { bool result; - // CUOPT_LOG_DEBUG("Rounding the point"); + CUOPT_LOG_TRACE("Rounding the point"); work_limit_timer_t bounds_prop_timer(context.gpu_heur_loop, std::max(0.05, std::min(0.5, timer.remaining_time() / 10.))); const f_t lp_run_time_after_feasible = 0.; @@ -289,7 +289,7 @@ void feasibility_pump_t::perturbate(solution_t& solution) template bool feasibility_pump_t::run_fj_cycle_escape(solution_t& solution) { - CUOPT_LOG_DEBUG("Running FJ cycle escape"); + CUOPT_LOG_TRACE("Running FJ cycle escape"); bool is_feasible; fj.settings.mode = fj_mode_t::EXIT_NON_IMPROVING; fj.settings.update_weights = true; @@ -345,7 +345,7 @@ template bool feasibility_pump_t::handle_cycle(solution_t& solution) { raft::common::nvtx::range fun_scope("handle_cycle"); - // CUOPT_LOG_DEBUG("running handle cycle"); + CUOPT_LOG_TRACE("running handle cycle"); bool is_feasible = false; fp_fj_cycle_time_begin = timer.remaining_time(); CUOPT_LOG_DEBUG("Running longer FJ on last rounding"); @@ -422,15 +422,15 @@ bool feasibility_pump_t::check_distance_cycle(solution_t& so std::accumulate(last_distances.begin(), last_distances.end(), 0.0) / last_distances.size(); if (avg_distance - distance_to_last_rounding < config.cycle_distance_reduction_ration * avg_distance) { - // CUOPT_LOG_DEBUG("Distance cycle detected curr %f avg %f for last %d iter", - // distance_to_last_rounding, - // avg_distance, - // last_distances.size()); + CUOPT_LOG_TRACE("Distance cycle detected curr %f avg %f for last %d iter", + distance_to_last_rounding, + avg_distance, + last_distances.size()); is_cycle = true; } last_distances.pop_back(); } else { - // CUOPT_LOG_DEBUG("Distance of projection: %f", distance_to_last_rounding); + CUOPT_LOG_TRACE("Distance of projection: %f", distance_to_last_rounding); } last_distances.push_front(distance_to_last_rounding); return is_cycle; @@ -543,7 +543,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s solution.assignment.size(), solution.handle_ptr->get_stream()); - CUOPT_LOG_DEBUG("FP: starting FP descent, sol hash 0x%x", solution.get_hash()); + CUOPT_LOG_TRACE("FP: starting FP descent, sol hash 0x%x", solution.get_hash()); while (true) { fp_iterations++; if (timer.check_time_limit()) { @@ -563,10 +563,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s CUOPT_LOG_DEBUG( "FP: after fp projection, iter %d sol hash 0x%x", fp_iterations, solution.get_hash()); i_t n_integers = solution.compute_number_of_integers(); - // CUOPT_LOG_DEBUG("after fp projection n_integers %d total n_integes %d", - // n_integers, - // solution.problem_ptr->n_integer_vars); - bool is_cycle = true; + bool is_cycle = true; // temp comment for presolve run if (config.check_distance_cycle) { // use distance cycle if we are running ii or objective FP @@ -578,7 +575,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s bool res = solution.compute_feasibility(); cuopt_assert(res, "Feasibility issue"); f_t time_taken = start_time - timer.remaining_time(); - CUOPT_LOG_INFO( + CUOPT_LOG_TRACE( "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_DISTANCE_CYCLE", fp_iterations, time_taken); @@ -587,12 +584,8 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s cuopt::default_logger().flush(); f_t remaining_time_end_fp = timer.remaining_time(); total_fp_time_until_cycle = fp_fj_cycle_time_begin - remaining_time_end_fp; - // CUOPT_LOG_DEBUG("total_fp_time_until_cycle: %f", total_fp_time_until_cycle); + CUOPT_LOG_TRACE("total_fp_time_until_cycle: %f", total_fp_time_until_cycle); f_t time_taken = start_time - timer.remaining_time(); - // CUOPT_LOG_INFO( - // "FP_RESULT: iterations=%d time_taken=%.6f termination=INFEASIBLE_DISTANCE_CYCLE", - // fp_iterations, - // time_taken); return false; } } @@ -600,11 +593,6 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (n_integers == solution.problem_ptr->n_integer_vars) { if (is_feasible) { CUOPT_LOG_DEBUG("Feasible solution found after LP with relative tolerance"); - f_t time_taken = start_time - timer.remaining_time(); - // CUOPT_LOG_INFO( - // "FP_RESULT: iterations=%d time_taken=%.6f termination=FEASIBLE_LP_PROJECTION", - // fp_iterations, - // time_taken); return true; } // if the solution is almost on polytope @@ -612,8 +600,8 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s // run the LP with full precision to check if it actually is feasible const f_t lp_verify_time_limit = 5.; relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_verify_time_limit; - lp_settings.work_limit = lp_settings.time_limit; + lp_settings.time_limit = lp_verify_time_limit; + if (timer.deterministic) { lp_settings.work_limit = lp_settings.time_limit; } lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; lp_settings.return_first_feasible = true; lp_settings.save_state = true; diff --git a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu index 5f6676e775..c700422d77 100644 --- a/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu +++ b/cpp/src/mip/local_search/line_segment_search/line_segment_search.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -169,7 +169,6 @@ bool line_segment_search_t::search_line_segment( fj.settings.update_weights = false; fj.settings.feasibility_run = is_feasibility_run; fj.settings.time_limit = std::min(0.1, timer.remaining_time()); - fj.settings.work_limit = fj.settings.time_limit; is_feasible = fj.solve(solution); } cuopt_func_call(solution.test_number_all_integer()); diff --git a/cpp/src/mip/local_search/local_search.cu b/cpp/src/mip/local_search/local_search.cu index 693413fe40..5126fb1a96 100644 --- a/cpp/src/mip/local_search/local_search.cu +++ b/cpp/src/mip/local_search/local_search.cu @@ -413,11 +413,10 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu const f_t lp_run_time = 2.; relaxed_lp_settings_t lp_settings; lp_settings.time_limit = std::min(lp_run_time, timer.remaining_time()); - lp_settings.work_limit = lp_settings.time_limit; - lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; + if (timer.deterministic) { lp_settings.work_limit = lp_settings.time_limit; } + lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; run_lp_with_vars_fixed( *solution.problem_ptr, solution, solution.problem_ptr->integer_indices, lp_settings); - CUOPT_LOG_DEBUG("FJ ON LP OPT: exited", lp_settings.time_limit); } else { return is_feasible; } diff --git a/cpp/src/mip/local_search/rounding/bounds_repair.cu b/cpp/src/mip/local_search/rounding/bounds_repair.cu index d9fbffbae0..307711ac4f 100644 --- a/cpp/src/mip/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/bounds_repair.cu @@ -98,7 +98,8 @@ f_t bounds_repair_t::get_ii_violation(problem_t& problem) thrust::make_counting_iterator(0) + problem.n_constraints, [cstr_violations_up = cstr_violations_up.data(), cstr_violations_down = cstr_violations_down.data()] __device__(i_t cstr_idx) -> f_t { - return max(cstr_violations_up[cstr_idx], cstr_violations_down[cstr_idx]); + auto violation = max(cstr_violations_up[cstr_idx], cstr_violations_down[cstr_idx]); + return violation >= ROUNDOFF_TOLERANCE ? violation : 0.; }, (f_t)0, thrust::plus()); diff --git a/cpp/src/mip/local_search/rounding/constraint_prop.cu b/cpp/src/mip/local_search/rounding/constraint_prop.cu index a5ca6dcf00..b463f8d419 100644 --- a/cpp/src/mip/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip/local_search/rounding/constraint_prop.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -905,16 +905,16 @@ bool constraint_prop_t::find_integer( set_bounds_on_fixed_vars(sol); } - // CUOPT_LOG_DEBUG("Bounds propagation rounding: unset vars %lu", unset_integer_vars.size()); + CUOPT_LOG_TRACE("Bounds propagation rounding: unset vars %lu", unset_integer_vars.size()); if (unset_integer_vars.size() == 0) { - // CUOPT_LOG_DEBUG("No integer variables provided in the bounds prop rounding"); + CUOPT_LOG_TRACE("No integer variables provided in the bounds prop rounding"); expand_device_copy(orig_sol.assignment, sol.assignment, sol.handle_ptr->get_stream()); cuopt_func_call(orig_sol.test_variable_bounds()); return orig_sol.compute_feasibility(); } // this is needed for the sort inside of the loop bool problem_ii = is_problem_ii(*sol.problem_ptr); - // CUOPT_LOG_DEBUG("is problem ii %d", problem_ii); + CUOPT_LOG_TRACE("is problem ii %d", problem_ii); // if the problem is ii, run the bounds prop in the beginning if (problem_ii) { bool bounds_repaired = @@ -940,7 +940,7 @@ bool constraint_prop_t::find_integer( bool timeout_happened = false; i_t n_failed_repair_iterations = 0; while (set_count < unset_integer_vars.size()) { - CUOPT_LOG_DEBUG("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); + CUOPT_LOG_TRACE("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x", detail::compute_hash(unset_integer_vars)); update_host_assignment(sol); if (max_timer.check_time_limit()) { @@ -978,18 +978,18 @@ bool constraint_prop_t::find_integer( n_vars_to_set, sol.handle_ptr->get_stream()); - CUOPT_LOG_DEBUG("host_vars_to_set hash 0x%x", detail::compute_hash(host_vars_to_set)); + CUOPT_LOG_TRACE("host_vars_to_set hash 0x%x", detail::compute_hash(host_vars_to_set)); auto var_probe_vals = generate_bulk_rounding_vector(sol, orig_sol, host_vars_to_set, probing_config); - CUOPT_LOG_DEBUG("var_probe_vals hash 1 0x%x, hash 2 0x%x, hash 3 0x%x", + CUOPT_LOG_TRACE("var_probe_vals hash 1 0x%x, hash 2 0x%x, hash 3 0x%x", detail::compute_hash(std::get<0>(var_probe_vals)), detail::compute_hash(std::get<1>(var_probe_vals)), detail::compute_hash(std::get<2>(var_probe_vals))); probe( sol, orig_sol.problem_ptr, var_probe_vals, &set_count, unset_integer_vars, probing_config); - CUOPT_LOG_DEBUG("post probe, set count %d, unset var hash 0x%x, size %lu", + CUOPT_LOG_TRACE("post probe, set count %d, unset var hash 0x%x, size %lu", (int)set_count, detail::compute_hash(unset_integer_vars), unset_integer_vars.size()); @@ -1024,7 +1024,7 @@ bool constraint_prop_t::find_integer( make_span(orig_sol.problem_ptr->variable_bounds), make_span(sol.assignment)}); i_t n_fixed_vars = (iter - (unset_vars.begin() + set_count)); - CUOPT_LOG_DEBUG("After repair procedure, number of additional fixed vars %d", n_fixed_vars); + CUOPT_LOG_TRACE("After repair procedure, number of additional fixed vars %d", n_fixed_vars); set_count += n_fixed_vars; } } @@ -1051,10 +1051,10 @@ bool constraint_prop_t::find_integer( // which is the unchanged problem bounds multi_probe.update_host_bounds(sol.handle_ptr, make_span(sol.problem_ptr->variable_bounds)); } - // CUOPT_LOG_DEBUG( - // "Bounds propagation rounding end: ii constraint count first buffer %d, second buffer %d", - // multi_probe.infeas_constraints_count_0, - // multi_probe.infeas_constraints_count_1); + CUOPT_LOG_TRACE( + "Bounds propagation rounding end: ii constraint count first buffer %d, second buffer %d", + multi_probe.infeas_constraints_count_0, + multi_probe.infeas_constraints_count_1); cuopt_assert(sol.test_number_all_integer(), "All integers must be rounded"); expand_device_copy(orig_sol.assignment, sol.assignment, sol.handle_ptr->get_stream()); cuopt_func_call(orig_sol.test_variable_bounds()); @@ -1063,12 +1063,11 @@ bool constraint_prop_t::find_integer( multi_probe.infeas_constraints_count_1 == 0) && !timeout_happened && lp_run_time_after_feasible > 0) { relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_run_time_after_feasible; - // CHANGE + lp_settings.time_limit = lp_run_time_after_feasible; lp_settings.tolerance = orig_sol.problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; - CUOPT_LOG_DEBUG("bounds repair LP, sol hash 0x%x", orig_sol.get_hash()); + CUOPT_LOG_TRACE("bounds repair LP, sol hash 0x%x", orig_sol.get_hash()); run_lp_with_vars_fixed(*orig_sol.problem_ptr, orig_sol, orig_sol.problem_ptr->integer_indices, @@ -1136,17 +1135,17 @@ bool constraint_prop_t::apply_round( f_t bounds_prop_end_time = max_timer.remaining_time(); repair_stats.total_time_spent_on_bounds_prop += bounds_prop_start_time - bounds_prop_end_time; - // CUOPT_LOG_DEBUG( - // "repair_success %lu repair_attempts %lu intermediate_repair_success %lu total_repair_loops - // %lu " "total_time_spent_on_repair %f total_time_spent_bounds_prop_after_repair %f " - // "total_time_spent_on_bounds_prop %f", - // repair_stats.repair_success, - // repair_stats.repair_attempts, - // repair_stats.intermediate_repair_success, - // repair_stats.total_repair_loops, - // repair_stats.total_time_spent_on_repair, - // repair_stats.total_time_spent_bounds_prop_after_repair, - // repair_stats.total_time_spent_on_bounds_prop); + CUOPT_LOG_TRACE( + "repair_success %lu repair_attempts %lu intermediate_repair_success %lu total_repair_loops" + "%lu total_time_spent_on_repair %f total_time_spent_bounds_prop_after_repair %f " + "total_time_spent_on_bounds_prop %f", + repair_stats.repair_success, + repair_stats.repair_attempts, + repair_stats.intermediate_repair_success, + repair_stats.total_repair_loops, + repair_stats.total_time_spent_on_repair, + repair_stats.total_time_spent_bounds_prop_after_repair, + repair_stats.total_time_spent_on_bounds_prop); // === CONSTRAINT PROP PREDICTOR RESULTS - START === auto cp_end_time = std::chrono::high_resolution_clock::now(); auto cp_elapsed_ms = @@ -1247,10 +1246,10 @@ bool constraint_prop_t::handle_fixed_vars( auto set_count = *set_count_ptr; const f_t int_tol = sol.problem_ptr->tolerances.integrality_tolerance; // which other variables were affected? - CUOPT_LOG_DEBUG("handle_fixed_vars, unset vars hash 0x%x, sol.assignment hash 0x%x", + CUOPT_LOG_TRACE("handle_fixed_vars, unset vars hash 0x%x, sol.assignment hash 0x%x", detail::compute_hash(unset_vars), detail::compute_hash(sol.assignment)); - CUOPT_LOG_DEBUG( + CUOPT_LOG_TRACE( "handle_fixed_vars, original_problem->variable_bounds hash 0x%x, " "sol.problem_ptr->variable_bounds hash 0x%x", detail::compute_hash(original_problem->variable_bounds), @@ -1268,7 +1267,7 @@ bool constraint_prop_t::handle_fixed_vars( cuopt_assert(n_fixed_vars >= std::get<0>(var_probe_vals).size(), "Error in number of vars fixed!"); set_count += n_fixed_vars; - CUOPT_LOG_DEBUG("Set var count increased from %d to %d", *set_count_ptr, set_count); + CUOPT_LOG_TRACE("Set var count increased from %d to %d", *set_count_ptr, set_count); *set_count_ptr = set_count; return multi_probe.infeas_constraints_count_0 == 0 || multi_probe.infeas_constraints_count_1 == 0; } diff --git a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu index 60037b5925..6c7921925b 100644 --- a/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu +++ b/cpp/src/mip/local_search/rounding/lb_bounds_repair.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -99,7 +99,8 @@ std::tuple lb_bounds_repair_t::get_ii_violation( thrust::make_counting_iterator(0) + problem.n_constraints, [cstr_violations_up = cstr_violations_up.data(), cstr_violations_down = cstr_violations_down.data()] __device__(i_t cstr_idx) -> f_t { - return max(cstr_violations_up[cstr_idx], cstr_violations_down[cstr_idx]); + auto violation = max(cstr_violations_up[cstr_idx], cstr_violations_down[cstr_idx]); + return violation >= ROUNDOFF_TOLERANCE ? violation : 0.; }, (f_t)0, thrust::plus()); diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index be6fc8ac6c..c8ee111760 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -1799,10 +1799,6 @@ void problem_t::get_host_user_problem( user_problem.num_range_rows = user_problem.range_rows.size(); std::tie(user_problem.lower, user_problem.upper) = extract_host_bounds(variable_bounds, handle_ptr); - - // Debug: Log bounds fingerprint to detect GPU presolve non-determinism - CUOPT_LOG_INFO("Problem fingerprint after GPU presolve: 0x%x", get_fingerprint()); - user_problem.problem_name = original_problem_ptr->get_problem_name(); if (static_cast(row_names.size()) == m) { user_problem.row_names.resize(m); diff --git a/cpp/src/mip/relaxed_lp/relaxed_lp.cu b/cpp/src/mip/relaxed_lp/relaxed_lp.cu index 9a0492e666..ffb850e87d 100644 --- a/cpp/src/mip/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip/relaxed_lp/relaxed_lp.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -42,7 +42,7 @@ optimization_problem_solution_t get_relaxed_lp_solution( const relaxed_lp_settings_t& settings) { raft::common::nvtx::range fun_scope("get_relaxed_lp_solution"); - auto function_start_time = std::chrono::high_resolution_clock::now(); + // auto function_start_time = std::chrono::high_resolution_clock::now(); // // === PDLP PREDICTOR FEATURES - START === // CUOPT_LOG_INFO("PDLP_FEATURES: n_variables=%d n_constraints=%d nnz=%lu", @@ -162,69 +162,70 @@ optimization_problem_solution_t get_relaxed_lp_solution( compute_hash(assignment)); } - auto function_end_time = std::chrono::high_resolution_clock::now(); - auto elapsed_ms = - std::chrono::duration_cast(function_end_time - function_start_time) - .count(); - CUOPT_LOG_DEBUG("get_relaxed_lp_solution took %lld ms for %d iterations", - elapsed_ms, - solver_response.get_additional_termination_information().number_of_steps_taken); + // auto function_end_time = std::chrono::high_resolution_clock::now(); + // auto elapsed_ms = + // std::chrono::duration_cast(function_end_time - + // function_start_time) + // .count(); + // CUOPT_LOG_DEBUG("get_relaxed_lp_solution took %lld ms for %d iterations", + // elapsed_ms, + // solver_response.get_additional_termination_information().number_of_steps_taken); - // === PDLP PREDICTOR RESULTS - START === - auto term_info = solver_response.get_additional_termination_information(); - const i_t n_vars = op_problem.n_variables; - const i_t n_cstrs = op_problem.n_constraints; - const int64_t nnz = op_problem.nnz; - const int64_t total_spmv = lp_solver.get_total_spmv_ops(); - const int64_t total_nnz = total_spmv * nnz; - const double nnz_per_sec = - (elapsed_ms > 0) ? static_cast(total_nnz) / (elapsed_ms / 1000.0) : 0.0; - const double nnz_per_iter = (term_info.number_of_steps_taken > 0) - ? static_cast(total_nnz) / term_info.number_of_steps_taken - : 0.0; + // // === PDLP PREDICTOR RESULTS - START === + // auto term_info = solver_response.get_additional_termination_information(); + // const i_t n_vars = op_problem.n_variables; + // const i_t n_cstrs = op_problem.n_constraints; + // const int64_t nnz = op_problem.nnz; + // const int64_t total_spmv = lp_solver.get_total_spmv_ops(); + // const int64_t total_nnz = total_spmv * nnz; + // const double nnz_per_sec = + // (elapsed_ms > 0) ? static_cast(total_nnz) / (elapsed_ms / 1000.0) : 0.0; + // const double nnz_per_iter = (term_info.number_of_steps_taken > 0) + // ? static_cast(total_nnz) / + // term_info.number_of_steps_taken : 0.0; - // Compute sparsity metrics - const double sparsity = (n_cstrs > 0 && n_vars > 0) - ? static_cast(nnz) / (static_cast(n_cstrs) * n_vars) - : 0.0; - double nnz_stddev = 0.0; - [[maybe_unused]] double unbalancedness = 0.0; - if (op_problem.offsets.size() == static_cast(n_cstrs + 1) && n_cstrs > 0) { - std::vector h_offsets(n_cstrs + 1); - raft::copy(h_offsets.data(), - op_problem.offsets.data(), - n_cstrs + 1, - op_problem.handle_ptr->get_stream()); - op_problem.handle_ptr->sync_stream(); + // // Compute sparsity metrics + // const double sparsity = (n_cstrs > 0 && n_vars > 0) + // ? static_cast(nnz) / + // (static_cast(n_cstrs) * n_vars) : 0.0; + // double nnz_stddev = 0.0; + // [[maybe_unused]] double unbalancedness = 0.0; + // if (op_problem.offsets.size() == static_cast(n_cstrs + 1) && n_cstrs > 0) { + // std::vector h_offsets(n_cstrs + 1); + // raft::copy(h_offsets.data(), + // op_problem.offsets.data(), + // n_cstrs + 1, + // op_problem.handle_ptr->get_stream()); + // op_problem.handle_ptr->sync_stream(); - const double mean_nnz = static_cast(nnz) / n_cstrs; - double variance_sum = 0.0; - for (i_t row = 0; row < n_cstrs; ++row) { - const double row_nnz = static_cast(h_offsets[row + 1] - h_offsets[row]); - const double diff = row_nnz - mean_nnz; - variance_sum += diff * diff; - } - const double variance = variance_sum / n_cstrs; - nnz_stddev = std::sqrt(variance); - unbalancedness = (mean_nnz > 0) ? nnz_stddev / mean_nnz : 0.0; - } + // const double mean_nnz = static_cast(nnz) / n_cstrs; + // double variance_sum = 0.0; + // for (i_t row = 0; row < n_cstrs; ++row) { + // const double row_nnz = static_cast(h_offsets[row + 1] - h_offsets[row]); + // const double diff = row_nnz - mean_nnz; + // variance_sum += diff * diff; + // } + // const double variance = variance_sum / n_cstrs; + // nnz_stddev = std::sqrt(variance); + // unbalancedness = (mean_nnz > 0) ? nnz_stddev / mean_nnz : 0.0; + // } - CUOPT_LOG_INFO( - "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f " - "iters=%d time_ms=%lld term=%d spmv_ops=%ld total_nnz=%.2e nnz/s=%.2e nnz/iter=%.2e", - n_vars, - n_cstrs, - nnz, - sparsity, - nnz_stddev, - unbalancedness, - term_info.number_of_steps_taken, - elapsed_ms, - static_cast(solver_response.get_termination_status()), - total_spmv, - static_cast(total_nnz), - nnz_per_sec, - nnz_per_iter); + // CUOPT_LOG_INFO( + // "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f + // " "iters=%d time_ms=%lld term=%d spmv_ops=%ld total_nnz=%.2e nnz/s=%.2e nnz/iter=%.2e", + // n_vars, + // n_cstrs, + // nnz, + // sparsity, + // nnz_stddev, + // unbalancedness, + // term_info.number_of_steps_taken, + // elapsed_ms, + // static_cast(solver_response.get_termination_status()), + // total_spmv, + // static_cast(total_nnz), + // nnz_per_sec, + // nnz_per_iter); // === PDLP PREDICTOR RESULTS - END === return solver_response; diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index 68ef676636..12e0be3334 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -109,7 +109,10 @@ solution_t mip_solver_t::run_solver() diversity_manager_t dm(context); dm.timer = work_limit_timer_t(context.gpu_heur_loop, timer_.get_time_limit()); - bool presolve_success = dm.run_presolve(timer_.get_time_limit()); + f_t time_limit = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC + ? timer_.get_time_limit() + : timer_.remaining_time(); + bool presolve_success = dm.run_presolve(time_limit); if (!presolve_success) { CUOPT_LOG_INFO("Problem proven infeasible in presolve"); solution_t sol(*context.problem_ptr); @@ -146,8 +149,6 @@ solution_t mip_solver_t::run_solver() context.problem_ptr->post_process_solution(sol); return sol; } - - context.work_unit_scheduler_.verbose = true; context.work_unit_scheduler_.register_context(context.gpu_heur_loop); namespace dual_simplex = cuopt::linear_programming::dual_simplex; @@ -190,15 +191,13 @@ solution_t mip_solver_t::run_solver() i_t num_threads = branch_and_bound_settings.num_threads; i_t num_bfs_threads = std::max(1, num_threads / 4); - i_t num_diving_threads = num_threads - num_bfs_threads; + i_t num_diving_threads = std::max(1, num_threads - num_bfs_threads); // deterministic mode: use BSP coordinator with multiple workers, no diving if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { // BSP mode can use multiple workers deterministically num_bfs_threads = std::max(1, num_threads); num_diving_threads = 0; // No diving in deterministic mode } - // num_diving_threads = 0; - // num_bfs_threads = std::max(1, num_threads); branch_and_bound_settings.num_bfs_threads = num_bfs_threads; branch_and_bound_settings.num_diving_threads = num_diving_threads; @@ -241,10 +240,7 @@ solution_t mip_solver_t::run_solver() branch_and_bound.get(), std::placeholders::_1); } else if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - // In deterministic mode, use VT-aware solution injection - // Use the B&B's current horizon as the VT timestamp - // This ensures heuristic solutions are processed at the END of the current horizon, - // maintaining determinism regardless of when the GPU heuristic actually finds the solution + // TODO context.problem_ptr->branch_and_bound_callback = [bb = branch_and_bound.get()](const std::vector& solution) { double vt = bb->get_current_bsp_horizon(); From 9fb0eddb8a832ddf9167f19641f7a2a4fc7f4d9e Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 16 Jan 2026 11:12:01 +0000 Subject: [PATCH 125/225] more cleanup to test for regressions --- cpp/src/dual_simplex/bounds_strengthening.cpp | 2 + cpp/src/dual_simplex/branch_and_bound.cpp | 286 ++---------------- cpp/src/dual_simplex/branch_and_bound.hpp | 61 ---- cpp/src/utilities/memory_instrumentation.hpp | 2 +- 4 files changed, 22 insertions(+), 329 deletions(-) diff --git a/cpp/src/dual_simplex/bounds_strengthening.cpp b/cpp/src/dual_simplex/bounds_strengthening.cpp index c0cfd9646c..0a01b76cfa 100644 --- a/cpp/src/dual_simplex/bounds_strengthening.cpp +++ b/cpp/src/dual_simplex/bounds_strengthening.cpp @@ -275,6 +275,7 @@ bool bounds_strengthening_t::bounds_strengthening( // settings.log.printf("Total strengthened variables %d\n", total_strengthened_variables); // Log bounds strengthening features +#if 0 { auto [loads, stores] = manifold.collect_and_flush(); bounds_strengthening_features_t bs_features; @@ -288,6 +289,7 @@ bool bounds_strengthening_t::bounds_strengthening( bs_features.runtime = toc(start_time); bs_features.log_single(m, n, nnz); } +#endif #if DEBUG_BOUND_STRENGTHENING f_t lb_change = 0.0; diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 06dee7d22b..5c477f3959 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -245,165 +245,6 @@ branch_and_bound_t::branch_and_bound_t( mutex_upper_.lock(); upper_bound_ = inf; mutex_upper_.unlock(); - - // Compute static problem features for regression model - compute_static_features(); -} - -template -void branch_and_bound_t::compute_static_features() -{ - const auto& A = original_lp_.A; - static_features_.n_rows = A.m; - static_features_.n_cols = A.n; - static_features_.n_nonzeros = A.col_start[A.n]; - static_features_.density = (f_t)static_features_.n_nonzeros / ((f_t)A.m * A.n); - - // Count variable types - static_features_.n_binary = 0; - static_features_.n_integer = 0; - static_features_.n_continuous = 0; - for (const auto& vt : var_types_) { - if (vt == variable_type_t::BINARY) { - static_features_.n_binary++; - } else if (vt == variable_type_t::INTEGER) { - static_features_.n_integer++; - } else { - static_features_.n_continuous++; - } - } - static_features_.integrality_ratio = - (f_t)(static_features_.n_binary + static_features_.n_integer) / A.n; - - // Compute row statistics (constraint sizes) - std::vector row_nnz(A.m, 0); - for (i_t j = 0; j < A.n; j++) { - for (i_t k = A.col_start[j]; k < A.col_start[j + 1]; k++) { - row_nnz[A.i[k]]++; - } - } - - static_features_.max_row_nnz = 0; - f_t sum_row_nnz = 0; - for (i_t i = 0; i < A.m; i++) { - static_features_.max_row_nnz = std::max(static_features_.max_row_nnz, row_nnz[i]); - sum_row_nnz += row_nnz[i]; - } - static_features_.avg_row_nnz = sum_row_nnz / A.m; - - // Compute row coefficient of variation - f_t row_variance = 0; - for (i_t i = 0; i < A.m; i++) { - f_t diff = row_nnz[i] - static_features_.avg_row_nnz; - row_variance += diff * diff; - } - row_variance /= A.m; - f_t row_std = std::sqrt(row_variance); - static_features_.row_nnz_cv = - static_features_.avg_row_nnz > 0 ? row_std / static_features_.avg_row_nnz : 0.0; - - // Compute column statistics (variable degrees) - static_features_.max_col_nnz = 0; - f_t sum_col_nnz = 0; - for (i_t j = 0; j < A.n; j++) { - i_t col_nnz = A.col_start[j + 1] - A.col_start[j]; - static_features_.max_col_nnz = std::max(static_features_.max_col_nnz, col_nnz); - sum_col_nnz += col_nnz; - } - static_features_.avg_col_nnz = sum_col_nnz / A.n; - - // Compute column coefficient of variation - f_t col_variance = 0; - for (i_t j = 0; j < A.n; j++) { - i_t col_nnz = A.col_start[j + 1] - A.col_start[j]; - f_t diff = col_nnz - static_features_.avg_col_nnz; - col_variance += diff * diff; - } - col_variance /= A.n; - f_t col_std = std::sqrt(col_variance); - static_features_.col_nnz_cv = - static_features_.avg_col_nnz > 0 ? col_std / static_features_.avg_col_nnz : 0.0; -} - -template -void branch_and_bound_t::flush_pending_features() -{ - // Must be called with mutex_feature_log_ already locked - if (!has_pending_features_) return; - - constexpr int LINE_BUFFER_SIZE = 512; - char line_buffer[LINE_BUFFER_SIZE]; - - snprintf(line_buffer, - LINE_BUFFER_SIZE, - "BB_NODE_FEATURES " - "node_id=%d depth=%d time=%.6f " - "n_rows=%d n_cols=%d n_nnz=%d density=%.6f " - "n_bin=%d n_int=%d n_cont=%d int_ratio=%.4f " - "avg_row_nnz=%.2f max_row_nnz=%d row_nnz_cv=%.4f " - "avg_col_nnz=%.2f max_col_nnz=%d col_nnz_cv=%.4f " - "n_bounds_chg=%d cutoff_gap=%.4f basis_from_parent=%d " - "simplex_iters=%d n_refact=%d lp_time=%.6f bound_str_time=%.6f var_sel_time=%.6f " - "n_frac=%d strong_branch=%d n_sb_cand=%d sb_time=%.6f " - "lp_status=%d node_status=%d\n", - last_features_.node_id, - last_features_.node_depth, - last_features_.total_node_time, - last_features_.n_rows, - last_features_.n_cols, - last_features_.n_nonzeros, - last_features_.density, - last_features_.n_binary, - last_features_.n_integer, - last_features_.n_continuous, - last_features_.integrality_ratio, - last_features_.avg_row_nnz, - last_features_.max_row_nnz, - last_features_.row_nnz_cv, - last_features_.avg_col_nnz, - last_features_.max_col_nnz, - last_features_.col_nnz_cv, - last_features_.n_bounds_changed, - last_features_.cutoff_gap_ratio, - last_features_.basis_from_parent ? 1 : 0, - last_features_.simplex_iterations, - last_features_.n_refactorizations, - last_features_.lp_solve_time, - last_features_.bound_str_time, - last_features_.variable_sel_time, - last_features_.n_fractional, - last_features_.strong_branch_performed ? 1 : 0, - last_features_.n_strong_branch_candidates, - last_features_.strong_branch_time, - last_features_.lp_status, - last_features_.node_status); - - // Single printf call - // settings_.log.printf("%s", line_buffer); - - has_pending_features_ = false; -} - -template -void branch_and_bound_t::log_node_features( - const node_solve_features_t& features) -{ - mutex_feature_log_.lock(); - - f_t current_time = toc(exploration_stats_.start_time); - f_t time_since_last_log = current_time - last_feature_log_time_; - - // Always store the latest features - last_features_ = features; - has_pending_features_ = true; - - // Log if enough time has passed (500ms) - if (time_since_last_log >= FEATURE_LOG_INTERVAL) { - flush_pending_features(); - last_feature_log_time_ = current_time; - } - - mutex_feature_log_.unlock(); } template @@ -786,13 +627,6 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, settings_.solution_callback(original_x, upper_bound_); } mutex_upper_.unlock(); - - // Debug: Log incumbent update (after releasing mutex to avoid potential issues) - if (improved_incumbent && bsp_mode_enabled_) { - std::string source = (thread_type == thread_type_t::EXPLORATION) ? "bb_integer" : "diving"; - BSP_DEBUG_LOG_INCUMBENT_UPDATE( - bsp_debug_settings_, bsp_debug_logger_, bsp_current_horizon_, leaf_objective, source); - } } template @@ -831,33 +665,18 @@ node_solve_info_t branch_and_bound_t::solve_node( { raft::common::nvtx::range scope("BB::solve_node"); - // Initialize feature tracking for this node - node_solve_features_t features = static_features_; - f_t node_start_time = tic(); - features.node_id = node_ptr->node_id; - features.node_depth = node_ptr->depth; - const f_t abs_fathom_tol = settings_.absolute_mip_gap_tol / 10; - const f_t upper_bound = get_upper_bound(); + const f_t abs_fathom_tol = settings_.absolute_mip_gap_tol / 10; + const f_t upper_bound = get_upper_bound(); lp_solution_t leaf_solution(leaf_problem.num_rows, leaf_problem.num_cols); std::vector& leaf_vstatus = node_ptr->vstatus; assert(leaf_vstatus.size() == leaf_problem.num_cols); - // Track cutoff gap ratio - if (upper_bound < inf) { - features.cutoff_gap_ratio = - (upper_bound - node_ptr->lower_bound) / std::max(std::abs(upper_bound), f_t(1.0)); - } - - // Track if we have parent's basis - features.basis_from_parent = !leaf_vstatus.empty(); - simplex_solver_settings_t lp_settings = settings_; lp_settings.set_log(false); lp_settings.cut_off = upper_bound + settings_.dual_tol; lp_settings.inside_mip = 2; lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time); - lp_settings.work_limit = settings_.work_limit; lp_settings.scale_columns = false; #ifdef LOG_NODE_SIMPLEX @@ -900,10 +719,8 @@ node_solve_info_t branch_and_bound_t::solve_node( bool feasible; { raft::common::nvtx::range scope_bs("BB::bound_strengthening"); - f_t bs_start_time = tic(); feasible = node_presolver.bounds_strengthening(leaf_problem.lower, leaf_problem.upper, lp_settings); - features.bound_str_time = toc(bs_start_time); } dual::status_t lp_status = dual::status_t::DUAL_UNBOUNDED; @@ -915,21 +732,20 @@ node_solve_info_t branch_and_bound_t::solve_node( { raft::common::nvtx::range scope_lp("BB::node_lp_solve"); - lp_status = - dual_phase2_with_advanced_basis(2, - 0, - recompute_bounds_and_basis, - lp_start_time, - leaf_problem, - lp_settings, - leaf_vstatus, - basis_factors, - basic_list, - nonbasic_list, - leaf_solution, - node_iter, - leaf_edge_norms, - settings_.deterministic ? &work_unit_context_ : nullptr); + lp_status = dual_phase2_with_advanced_basis(2, + 0, + recompute_bounds_and_basis, + lp_start_time, + leaf_problem, + lp_settings, + leaf_vstatus, + basis_factors, + basic_list, + nonbasic_list, + leaf_solution, + node_iter, + leaf_edge_norms, + nullptr); } if (lp_status == dual::status_t::NUMERICAL) { @@ -947,16 +763,9 @@ node_solve_info_t branch_and_bound_t::solve_node( lp_status = convert_lp_status_to_dual_status(second_status); } - f_t lp_time = toc(lp_start_time); if (thread_type == thread_type_t::EXPLORATION) { - exploration_stats_.total_lp_solve_time += lp_time; + exploration_stats_.total_lp_solve_time += toc(lp_start_time); exploration_stats_.total_lp_iters += node_iter; - - // Track LP solve metrics - features.lp_solve_time = lp_time; - features.simplex_iterations = node_iter; - // Note: We don't directly track refactorizations here, would need instrumentation in - // dual_phase2 } } @@ -969,13 +778,6 @@ node_solve_info_t branch_and_bound_t::solve_node( node_ptr->lower_bound = inf; search_tree.graphviz_node(log, node_ptr, "infeasible", 0.0); search_tree.update(node_ptr, node_status_t::INFEASIBLE); - - // Log features before return - features.lp_status = static_cast(lp_status); - features.node_status = static_cast(node_status_t::INFEASIBLE); - features.total_node_time = toc(node_start_time); - log_node_features(features); - return node_solve_info_t::NO_CHILDREN; } else if (lp_status == dual::status_t::CUTOFF) { @@ -984,13 +786,6 @@ node_solve_info_t branch_and_bound_t::solve_node( f_t leaf_objective = compute_objective(leaf_problem, leaf_solution.x); search_tree.graphviz_node(log, node_ptr, "cut off", leaf_objective); search_tree.update(node_ptr, node_status_t::FATHOMED); - - // Log features before return - features.lp_status = static_cast(lp_status); - features.node_status = static_cast(node_status_t::FATHOMED); - features.total_node_time = toc(node_start_time); - log_node_features(features); - return node_solve_info_t::NO_CHILDREN; } else if (lp_status == dual::status_t::OPTIMAL) { @@ -999,8 +794,6 @@ node_solve_info_t branch_and_bound_t::solve_node( i_t leaf_num_fractional = fractional_variables(settings_, leaf_solution.x, var_types_, leaf_fractional); - features.n_fractional = leaf_num_fractional; - f_t leaf_objective = compute_objective(leaf_problem, leaf_solution.x); node_ptr->lower_bound = leaf_objective; search_tree.graphviz_node(log, node_ptr, "lower bound", leaf_objective); @@ -1017,33 +810,18 @@ node_solve_info_t branch_and_bound_t::solve_node( add_feasible_solution(leaf_objective, leaf_solution.x, node_ptr->depth, thread_type); search_tree.graphviz_node(log, node_ptr, "integer feasible", leaf_objective); search_tree.update(node_ptr, node_status_t::INTEGER_FEASIBLE); - - // Log features before return - features.lp_status = static_cast(lp_status); - features.node_status = static_cast(node_status_t::INTEGER_FEASIBLE); - features.total_node_time = toc(node_start_time); - log_node_features(features); - return node_solve_info_t::NO_CHILDREN; } else if (leaf_objective <= upper_bound + abs_fathom_tol) { // Choose fractional variable to branch on - f_t var_sel_start = tic(); const i_t branch_var = pc_.variable_selection(leaf_fractional, leaf_solution.x, lp_settings.log); - features.variable_sel_time = toc(var_sel_start); assert(leaf_vstatus.size() == leaf_problem.num_cols); search_tree.branch( node_ptr, branch_var, leaf_solution.x[branch_var], leaf_vstatus, leaf_problem, log); search_tree.update(node_ptr, node_status_t::HAS_CHILDREN); - // Log features before return - features.lp_status = static_cast(lp_status); - features.node_status = static_cast(node_status_t::HAS_CHILDREN); - features.total_node_time = toc(node_start_time); - log_node_features(features); - rounding_direction_t round_dir = child_selection(node_ptr); if (round_dir == rounding_direction_t::UP) { @@ -1055,13 +833,6 @@ node_solve_info_t branch_and_bound_t::solve_node( } else { search_tree.graphviz_node(log, node_ptr, "fathomed", leaf_objective); search_tree.update(node_ptr, node_status_t::FATHOMED); - - // Log features before return - features.lp_status = static_cast(lp_status); - features.node_status = static_cast(node_status_t::FATHOMED); - features.total_node_time = toc(node_start_time); - log_node_features(features); - return node_solve_info_t::NO_CHILDREN; } } else if (lp_status == dual::status_t::TIME_LIMIT) { @@ -1085,13 +856,6 @@ node_solve_info_t branch_and_bound_t::solve_node( search_tree.graphviz_node(log, node_ptr, "numerical", 0.0); search_tree.update(node_ptr, node_status_t::NUMERICAL); - - // Log features before return - features.lp_status = static_cast(lp_status); - features.node_status = static_cast(node_status_t::NUMERICAL); - features.total_node_time = toc(node_start_time); - log_node_features(features); - return node_solve_info_t::NUMERICAL; } } @@ -1679,21 +1443,14 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut assert(root_vstatus_.size() == original_lp_.num_cols); - // Validate root_vstatus_ has correct BASIC count - // This catches bugs where the root LP solve produces an invalid vstatus { const i_t expected_basic_count = original_lp_.num_rows; i_t actual_basic_count = 0; for (const auto& status : root_vstatus_) { if (status == variable_status_t::BASIC) { actual_basic_count++; } } - if (actual_basic_count != expected_basic_count) { - settings_.log.printf("ERROR: root_vstatus_ has %d BASIC entries, expected %d (num_rows)\n", - actual_basic_count, - expected_basic_count); - assert(actual_basic_count == expected_basic_count && - "root_vstatus_ BASIC count mismatch - LP solver returned invalid basis"); - } + assert(actual_basic_count == expected_basic_count && + "root_vstatus_ BASIC count mismatch - LP solver returned invalid basis"); } set_uninitialized_steepest_edge_norms(edge_norms_); @@ -1840,11 +1597,6 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } } - // Flush any pending features - mutex_feature_log_.lock(); - if (has_pending_features_) { flush_pending_features(); } - mutex_feature_log_.unlock(); - // Compute final lower bound f_t lower_bound; if (bsp_mode_enabled_) { diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index 07cf8ee00c..e00e4176e9 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -79,51 +79,6 @@ class bounds_strengthening_t; template void upper_bound_callback(f_t upper_bound); -// Feature tracking for solve_node regression model -template -struct node_solve_features_t { - // Static problem features (compute once) - i_t n_rows{0}; - i_t n_cols{0}; - i_t n_nonzeros{0}; - f_t density{0.0}; - i_t n_binary{0}; - i_t n_integer{0}; - i_t n_continuous{0}; - f_t integrality_ratio{0.0}; - f_t avg_row_nnz{0.0}; - i_t max_row_nnz{0}; - f_t avg_col_nnz{0.0}; - i_t max_col_nnz{0}; - f_t row_nnz_cv{0.0}; - f_t col_nnz_cv{0.0}; - - // Dynamic node state - i_t node_id{0}; - i_t node_depth{0}; - i_t n_bounds_changed{0}; - f_t cutoff_gap_ratio{0.0}; - bool basis_from_parent{false}; - - // LP solve metrics - i_t simplex_iterations{0}; - i_t n_refactorizations{0}; - f_t lp_solve_time{0.0}; - f_t bound_str_time{0.0}; - f_t variable_sel_time{0.0}; - - // Outcome metrics - i_t n_fractional{0}; - bool strong_branch_performed{false}; - i_t n_strong_branch_candidates{0}; - f_t strong_branch_time{0.0}; - i_t lp_status{0}; // Convert dual::status_t to int - i_t node_status{0}; // Convert node_status_t to int - - // Computed at node end - f_t total_node_time{0.0}; -}; - template class branch_and_bound_t { public: @@ -265,22 +220,6 @@ class branch_and_bound_t { // its blocks the progression of the lower bound. omp_atomic_t lower_bound_ceiling_; - // Feature tracking for solve_node regression model - node_solve_features_t static_features_; // Static problem features, computed once - omp_mutex_t mutex_feature_log_; // Protect feature logging - node_solve_features_t last_features_; // Last captured features - f_t last_feature_log_time_{0.0}; // Time of last feature log - bool has_pending_features_{false}; // Whether we have features to log - - // Helper to compute static features once - void compute_static_features(); - - // Helper to log node solve features with time-based throttling - void log_node_features(const node_solve_features_t& features); - - // Helper to flush any pending features at end of solve - void flush_pending_features(); - void report_heuristic(f_t obj); void report(std::string symbol, f_t obj, f_t lower_bound, i_t node_depth); diff --git a/cpp/src/utilities/memory_instrumentation.hpp b/cpp/src/utilities/memory_instrumentation.hpp index abf09ae05b..f4b8a2090d 100644 --- a/cpp/src/utilities/memory_instrumentation.hpp +++ b/cpp/src/utilities/memory_instrumentation.hpp @@ -35,7 +35,7 @@ #include #include -#define CUOPT_ENABLE_MEMORY_INSTRUMENTATION 1 +#define CUOPT_ENABLE_MEMORY_INSTRUMENTATION 0 #ifdef __NVCC__ #define HDI inline __host__ __device__ From e5e498d04119d3b95aeba3e811163ee7abca0e1d Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 21 Jan 2026 11:08:43 +0000 Subject: [PATCH 126/225] ml script updates --- scripts/determinism_logs_parse.py | 35 ++++++++++++++++++-- scripts/train_regressor.py | 53 ++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/scripts/determinism_logs_parse.py b/scripts/determinism_logs_parse.py index 5031656417..c5eeefda87 100755 --- a/scripts/determinism_logs_parse.py +++ b/scripts/determinism_logs_parse.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. # SPDX-License-Identifier: Apache-2.0 # All rights reserved. # SPDX-License-Identifier: Apache-2.0 @@ -64,7 +64,16 @@ from typing import List, Dict, Any, Optional -SUPPORTED_ALGORITHMS = ["FP", "PDLP", "CP", "FJ", "CPUFJ", "BB", "DS"] +SUPPORTED_ALGORITHMS = [ + "FP", + "PDLP", + "CP", + "FJ", + "CPUFJ", + "BB", + "DS", + "BOUNDS_STRENGTH", +] def parse_value(value_str: str) -> Any: @@ -401,6 +410,8 @@ def parse_single_line_logs( # Remove trailing commas value = value.rstrip(",") entry[key] = parse_value(value) + if key == "runtime": + entry["runtime_ms"] = entry["runtime"] * 1000.0 # Only add entry if it has more than just the filename if len(entry) > 1: @@ -443,6 +454,18 @@ def parse_ds_logs(log_files: List[str]) -> List[Dict[str, Any]]: ) +def parse_bounds_strengthening_logs( + log_files: List[str], +) -> List[Dict[str, Any]]: + """Parse Bounds Strengthening feature and result logs.""" + return parse_single_line_logs( + log_files, + "BOUNDS_STRENGTH_FEATURES:", + "Bounds Strengthening", + "BOUNDS_STRENGTH_FEATURES:", + ) + + def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: """Print statistics about parsed entries.""" if not entries: @@ -495,6 +518,7 @@ def main(): CPUFJ - CPU Feasibility Jump (parses CPUFJ_FEATURES single-line logs) BB - Branch and Bound (parses BB_NODE_FEATURES single-line logs) DS - Dual Simplex (parses DS_FEATURES single-line logs) + BOUNDS_STRENGTH - Bounds Strengthening (parses BOUNDS_STRENGTHENING_FEATURES and BOUNDS_STRENGTHENING_RESULT logs) Examples: python determinism_logs_parse.py logs/ --algorithm FP -o fp_data.feather @@ -504,6 +528,7 @@ def main(): python determinism_logs_parse.py logs/ --algorithm CPUFJ -o cpufj_data.feather python determinism_logs_parse.py logs/ --algorithm BB -o bb_data.feather python determinism_logs_parse.py logs/ --algorithm DS -o ds_data.feather + python determinism_logs_parse.py logs/ --algorithm BOUNDS_STRENGTH -o bounds_strengthening_data.feather # Limit to first 10 files for testing python determinism_logs_parse.py logs/ --algorithm FP --max-files 10 @@ -580,6 +605,8 @@ def main(): entries = parse_bb_logs(log_files) elif args.algorithm == "DS": entries = parse_ds_logs(log_files) + elif args.algorithm == "BOUNDS_STRENGTH": + entries = parse_bounds_strengthening_logs(log_files) else: print(f"Error: Unsupported algorithm: {args.algorithm}") return 1 @@ -608,6 +635,10 @@ def main(): print( "Make sure your logs contain DS_FEATURES: lines with key=value pairs" ) + elif args.algorithm == "BOUNDS_STRENGTH": + print( + "Make sure your logs contain BOUNDS_STRENGTH_FEATURES and BOUNDS_STRENGTH_RESULT lines" + ) return 1 # Print statistics diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index 9c17e9e767..7a899c5dd9 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. # SPDX-License-Identifier: Apache-2.0 # All rights reserved. # SPDX-License-Identifier: Apache-2.0 @@ -146,6 +146,7 @@ "iters", "nnz/s", "nnz/iter", + "runtime", ] # Alternatively, specify ONLY the features you want to use @@ -779,6 +780,7 @@ def evaluate_model( log_transform: bool = False, y_train_original: pd.Series = None, y_test_original: pd.Series = None, + log_transform_offset: float = 0.0, ) -> Tuple[float, float]: """Evaluate model and print metrics. Returns (train_r2, test_r2). @@ -789,6 +791,7 @@ def evaluate_model( log_transform: If True, predictions are in log-space and need inverse transform y_train_original: Original (non-log) training targets (if log_transform=True) y_test_original: Original (non-log) test targets (if log_transform=True) + log_transform_offset: Constant added before log transform (if log_transform=True) """ # Cross-validation on training set (skip if using early stopping) if not skip_cv: @@ -858,7 +861,7 @@ def evaluate_model( # Predict and transform back y_pred_log = model_fold.predict(X_val_fold) - y_pred_original = np.exp(y_pred_log) + y_pred_original = np.exp(y_pred_log) - log_transform_offset # Compute metrics in original space mape = ( @@ -909,9 +912,9 @@ def evaluate_model( # If log-transformed, also compute metrics in original space if log_transform: # Inverse transform predictions - y_train_pred_original = np.exp(y_train_pred) + y_train_pred_original = np.exp(y_train_pred) - log_transform_offset y_test_pred_log = model.predict(X_test) - y_test_pred_original = np.exp(y_test_pred_log) + y_test_pred_original = np.exp(y_test_pred_log) - log_transform_offset # Metrics in log-space train_mse_log = mean_squared_error(y_train, y_train_pred) @@ -1101,6 +1104,7 @@ def compile_model_treelite( feature_names: List[str] = None, model_name: str = None, log_transform: bool = False, + log_transform_offset: float = 0.0, ) -> None: """Compile XGBoost/LightGBM model to C source files using TL2cgen. @@ -1116,6 +1120,7 @@ def compile_model_treelite( feature_names: List of feature names in expected order (optional) model_name: Name prefix for functions (optional, derived from training file) log_transform: Whether model predicts in log-space (will add exp() wrapper) + log_transform_offset: Constant added before log transform (if log_transform=True) """ if regressor_type not in ["xgboost", "lightgbm"]: print( @@ -1319,6 +1324,11 @@ def compile_model_treelite( # If log_transform is used, wrap return values with exp() if log_transform: + offset_literal = f"{log_transform_offset:.12g}" + if log_transform_offset != 0: + offset_expr = f" - ({offset_literal})" + else: + offset_expr = "" # Add include if not present if ( "#include " not in content @@ -1338,18 +1348,18 @@ def compile_model_treelite( + content[insert_pos:] ) - # Replace "return sum;" with "return std::exp(sum);" + # Replace "return sum;" with "return std::exp(sum) - offset;" # Match various return patterns in the predict function content = re.sub( r"(\s+)return\s+(sum|result|pred)\s*;", - r"\1return std::exp(\2);", + rf"\1return std::exp(\2){offset_expr};", content, ) # Also handle single-line returns like "return value;" content = re.sub( r"(\s+)return\s+([\w\.]+)\s*;", - lambda m: f"{m.group(1)}return std::exp({m.group(2)});" + lambda m: f"{m.group(1)}return std::exp({m.group(2)}){offset_expr};" if m.group(2) not in ["true", "false", "0", "1"] else m.group(0), content, @@ -1571,6 +1581,7 @@ def save_model( output_dir: str, feature_names: List[str], log_transform: bool = False, + log_transform_offset: float = 0.0, ) -> None: """Save trained model and preprocessing components to disk.""" os.makedirs(output_dir, exist_ok=True) @@ -1581,6 +1592,7 @@ def save_model( "feature_names": feature_names, "has_scaler": scaler is not None, "log_transform": log_transform, + "log_transform_offset": log_transform_offset, } metadata_path = os.path.join(output_dir, f"{regressor_type}_metadata.pkl") @@ -1756,6 +1768,13 @@ def main(): action="store_true", help="Use log-transform on target variable to optimize for relative error instead of absolute error. Recommended when target values span multiple orders of magnitude.", ) + parser.add_argument( + "--log-transform-offset", + type=float, + default=0.0, + metavar="C", + help="Add constant C to target values before log transform (useful to avoid non-positive values). Only used with --log-transform.", + ) args = parser.parse_args() @@ -1892,6 +1911,17 @@ def main(): # Apply log transform if requested (for relative error optimization) if args.log_transform: + log_transform_offset = args.log_transform_offset + y_train_original = y_train.copy() + y_test_original = y_test.copy() + + if log_transform_offset != 0.0: + print( + f" Applying log-transform offset: {log_transform_offset:+.6g}" + ) + y_train = y_train + log_transform_offset + y_test = y_test + log_transform_offset + # Check for non-positive values before log transform if np.any(y_train <= 0) or np.any(y_test <= 0): n_nonpositive_train = np.sum(y_train <= 0) @@ -1905,16 +1935,13 @@ def main(): f" Target range: [{np.min(y_train):.2f}, {np.max(y_train):.2f}]" ) print( - "\nSuggestion: Add a small constant (e.g., +1) to all target values before log" + "\nSuggestion: Use --log-transform-offset to add a small constant before log" ) return 1 print( "\nApplying log-transform to target variable (optimizes for relative error)" ) - y_train_original = y_train.copy() - y_test_original = y_test.copy() - y_train = np.log(y_train) y_test = np.log(y_test) @@ -1927,6 +1954,7 @@ def main(): else: y_train_original = None y_test_original = None + log_transform_offset = 0.0 # Enhanced diagnostics for XGBoost compatibility (only show if problems found) X_train_array = X_train.values if hasattr(X_train, "values") else X_train @@ -2139,6 +2167,7 @@ def main(): log_transform=args.log_transform, y_train_original=y_train_original, y_test_original=y_test_original, + log_transform_offset=log_transform_offset, ) # Save model @@ -2149,6 +2178,7 @@ def main(): args.output_dir, feature_names, log_transform=args.log_transform, + log_transform_offset=log_transform_offset, ) # Compile with TL2cgen if requested (with optimizations enabled by default) @@ -2168,6 +2198,7 @@ def main(): feature_names=feature_names, model_name=model_name, log_transform=args.log_transform, + log_transform_offset=log_transform_offset, ) print("\n" + "=" * 70) From 525c7b5b13ac76a1768b187d8bacf9262f4e9468 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 21 Jan 2026 11:18:04 +0000 Subject: [PATCH 127/225] fix postprocess in train_regressor.py --- scripts/train_regressor.py | 43 +++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index 7a899c5dd9..026b140c77 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -1322,7 +1322,7 @@ def compile_model_treelite( r"!\(data\[\d+\]\.missing != -1\)", "false", content ) - # If log_transform is used, wrap return values with exp() + # If log_transform is used, modify postprocess to apply exp/offset if log_transform: offset_literal = f"{log_transform_offset:.12g}" if log_transform_offset != 0: @@ -1348,27 +1348,36 @@ def compile_model_treelite( + content[insert_pos:] ) - # Replace "return sum;" with "return std::exp(sum) - offset;" - # Match various return patterns in the predict function - content = re.sub( - r"(\s+)return\s+(sum|result|pred)\s*;", - rf"\1return std::exp(\2){offset_expr};", - content, + postprocess_pattern = re.compile( + r"void\s+(\w+)::postprocess\s*\(\s*double\*\s*result\s*\)\s*\{\n.*?\n\}", + re.DOTALL, ) - # Also handle single-line returns like "return value;" - content = re.sub( - r"(\s+)return\s+([\w\.]+)\s*;", - lambda m: f"{m.group(1)}return std::exp({m.group(2)}){offset_expr};" - if m.group(2) not in ["true", "false", "0", "1"] - else m.group(0), - content, - ) + def replace_postprocess(match: re.Match) -> str: + class_name = match.group(1) + return ( + f"void {class_name}::postprocess(double* result)\n" + "{\n" + " for (int i = 0; i < N_TARGET; ++i) {\n" + f" result[i] = std::exp(result[i]){offset_expr};\n" + " }\n" + "}" + ) - print( - " Added exp() transformation to convert log-space predictions to original space" + updated_content = postprocess_pattern.sub( + replace_postprocess, content, count=1 ) + if updated_content != content: + content = updated_content + print( + " Updated postprocess to convert log-space predictions to original space" + ) + else: + print( + " Warning: Failed to update postprocess for log-transform" + ) + if content != original_content: with open(main_path, "w") as f: f.write(content) From a92fd23bf5f686b09430124fc73e207dc4ad668a Mon Sep 17 00:00:00 2001 From: Rajesh Gandham Date: Tue, 3 Mar 2026 09:31:27 -0500 Subject: [PATCH 128/225] Update to PSLP version with bug fixes (#920) Updating to latest version of PSLP. This fixes a bug where PSLP is making feasible LP problems to infeasible ones and has performance improvements as well. ## Issue Authors: - Rajesh Gandham (https://github.com/rg20) Approvers: - Chris Maes (https://github.com/chris-maes) - Anandh Anandh (https://github.com/anandhkb) - Ishika Roy (https://github.com/Iroy30) URL: https://github.com/NVIDIA/cuopt/pull/920 --- cpp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 24dc0276af..e488291744 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -235,7 +235,7 @@ FetchContent_MakeAvailable(papilo) FetchContent_Declare( pslp GIT_REPOSITORY "https://github.com/dance858/PSLP.git" - GIT_TAG "v0.0.4" + GIT_TAG "v0.0.8" GIT_PROGRESS TRUE EXCLUDE_FROM_ALL SYSTEM From 186d28d91188c4a14f805a0661c8d9a857512adb Mon Sep 17 00:00:00 2001 From: Ramakrishnap <42624703+rgsl888prabhu@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:05:23 +0530 Subject: [PATCH 129/225] Upgrade skills (#918) This PR adds upgrades to skills, - Create a flat structure for skills - A common skill for problem formulation + API related skill for each optimization + language - Examples as part of assets Authors: - Ramakrishnap (https://github.com/rgsl888prabhu) Approvers: - Trevor McKay (https://github.com/tmckayus) URL: https://github.com/NVIDIA/cuopt/pull/918 --- .claude-plugin/marketplace.json | 120 ++++++ .claude/AGENTS.md | 1 + .claude/CLAUDE.md | 1 + .claude/skills | 1 + .cursor-plugin/plugin.json | 22 ++ .cursor/AGENTS.md | 1 + .cursor/skills | 1 + .github/AGENTS.md | 32 -- .github/CODEOWNERS | 2 +- .github/skills/README.md | 40 -- .github/skills/cuopt-debugging/SKILL.md | 267 ------------- .../resources/diagnostic_snippets.md | 219 ----------- .github/skills/cuopt-installation/SKILL.md | 295 --------------- .github/skills/cuopt-lp-milp/SKILL.md | 240 ------------ .../cuopt-lp-milp/resources/cli_examples.md | 166 -------- .../resources/python_examples.md | 257 ------------- .../resources/server_examples.md | 208 ---------- .github/skills/cuopt-qp/SKILL.md | 195 ---------- .github/skills/cuopt-routing/SKILL.md | 298 --------------- .github/skills/cuopt-server/SKILL.md | 356 ------------------ .../resources/lp_milp_examples.md | 176 --------- .../resources/routing_examples.md | 160 -------- .github/workflows/pr.yaml | 32 +- .opencode/AGENTS.md | 1 + .opencode/skills | 1 + .pre-commit-config.yaml | 17 +- AGENTS.md | 49 ++- agents/AGENTS.md | 59 +++ ci/test_python.sh | 5 +- ci/test_skills_assets.sh | 155 ++++++++ ci/utils/sync_skills_version.sh | 52 +++ ci/utils/validate_skills.sh | 99 +++++ gemini-extension.json | 6 + .../cuopt-developer/SKILL.md | 1 + skills/cuopt-installation-api-c/SKILL.md | 32 ++ .../resources/verification_examples.md | 2 +- skills/cuopt-installation-api-python/SKILL.md | 73 ++++ .../resources/verification_examples.md | 172 +++++++++ skills/cuopt-installation-common/SKILL.md | 29 ++ skills/cuopt-installation-developer/SKILL.md | 36 ++ skills/cuopt-lp-milp-api-c/SKILL.md | 57 +++ skills/cuopt-lp-milp-api-c/assets/README.md | 33 ++ .../assets/lp_basic/README.md | 15 + .../assets/lp_basic/lp_simple.c | 109 ++++++ .../assets/lp_duals/README.md | 14 + .../assets/lp_duals/lp_duals.c | 115 ++++++ .../assets/lp_warmstart/README.md | 5 + .../assets/milp_basic/README.md | 12 + .../assets/milp_basic/milp_simple.c | 102 +++++ .../assets/milp_production_planning/README.md | 12 + .../milp_production.c | 98 +++++ .../assets/mps_solver/README.md | 14 + .../assets/mps_solver/data/sample.mps | 19 + .../assets/mps_solver/mps_solver.c | 107 ++++++ .../cuopt-lp-milp-api-c/resources/examples.md | 0 skills/cuopt-lp-milp-api-cli/SKILL.md | 66 ++++ skills/cuopt-lp-milp-api-cli/assets/README.md | 21 ++ .../assets/lp_production/README.md | 5 + .../assets/lp_production/production.mps | 16 + .../assets/lp_simple/README.md | 5 + .../assets/lp_simple/sample.mps | 19 + .../assets/milp_facility/README.md | 5 + .../assets/milp_facility/facility.mps | 27 ++ skills/cuopt-lp-milp-api-python/SKILL.md | 226 +++++++++++ .../cuopt-lp-milp-api-python/assets/README.md | 12 + .../assets/lp_basic/README.md | 7 + .../assets/lp_basic/model.py | 36 ++ .../assets/lp_duals/README.md | 7 + .../assets/lp_duals/model.py | 38 ++ .../assets/lp_warmstart/README.md | 5 + .../assets/lp_warmstart/model.py | 52 +++ .../assets/milp_basic/README.md | 10 + .../assets/milp_basic/incumbent_callback.py | 50 +++ .../assets/milp_basic/model.py | 36 ++ .../assets/milp_production_planning/README.md | 5 + .../assets/milp_production_planning/model.py | 33 ++ .../assets/mps_solver/README.md | 88 +++++ .../assets/mps_solver/data/README.md | 82 ++++ .../assets/mps_solver/data/sample.mps | 19 + .../assets/mps_solver/model.py | 283 ++++++++++++++ .../assets/mps_solver/results.md | 90 +++++ skills/cuopt-qp-api-c/SKILL.md | 19 + skills/cuopt-qp-api-c/assets/README.md | 9 + skills/cuopt-qp-api-cli/SKILL.md | 37 ++ skills/cuopt-qp-api-cli/assets/README.md | 9 + skills/cuopt-qp-api-python/SKILL.md | 61 +++ skills/cuopt-qp-api-python/assets/README.md | 11 + .../assets/least_squares/README.md | 5 + .../assets/least_squares/model.py | 24 ++ .../assets/maximization_workaround/README.md | 5 + .../assets/maximization_workaround/model.py | 22 ++ .../assets/portfolio/README.md | 7 + .../assets/portfolio/model.py | 49 +++ .../cuopt-qp-api-python/resources/examples.md | 0 skills/cuopt-routing-api-python/SKILL.md | 101 +++++ .../cuopt-routing-api-python/assets/README.md | 10 + .../assets/pdp_basic/README.md | 7 + .../assets/pdp_basic/model.py | 56 +++ .../assets/vrp_basic/README.md | 7 + .../assets/vrp_basic/model.py | 31 ++ .../resources/examples.md | 8 +- .../resources/server_examples.md | 0 skills/cuopt-server-api-python/SKILL.md | 80 ++++ .../cuopt-server-api-python/assets/README.md | 14 + .../assets/lp_basic/README.md | 10 + .../assets/lp_basic/client.py | 84 +++++ .../assets/milp_basic/README.md | 6 + .../assets/milp_basic/client.py | 82 ++++ .../assets/pdp_basic/README.md | 6 + .../assets/pdp_basic/client.py | 97 +++++ .../assets/vrp_basic/README.md | 10 + .../assets/vrp_basic/client.py | 101 +++++ .../assets/vrp_simple/README.md | 6 + .../assets/vrp_simple/client.py | 95 +++++ skills/cuopt-server-common/SKILL.md | 46 +++ .../cuopt-user-rules/SKILL.md | 80 ++-- skills/lp-milp-formulation/SKILL.md | 128 +++++++ skills/qp-formulation/SKILL.md | 33 ++ skills/routing-formulation/SKILL.md | 31 ++ 119 files changed, 4258 insertions(+), 2963 deletions(-) create mode 100644 .claude-plugin/marketplace.json create mode 120000 .claude/AGENTS.md create mode 120000 .claude/CLAUDE.md create mode 120000 .claude/skills create mode 100644 .cursor-plugin/plugin.json create mode 120000 .cursor/AGENTS.md create mode 120000 .cursor/skills delete mode 100644 .github/AGENTS.md delete mode 100644 .github/skills/README.md delete mode 100644 .github/skills/cuopt-debugging/SKILL.md delete mode 100644 .github/skills/cuopt-debugging/resources/diagnostic_snippets.md delete mode 100644 .github/skills/cuopt-installation/SKILL.md delete mode 100644 .github/skills/cuopt-lp-milp/SKILL.md delete mode 100644 .github/skills/cuopt-lp-milp/resources/cli_examples.md delete mode 100644 .github/skills/cuopt-lp-milp/resources/python_examples.md delete mode 100644 .github/skills/cuopt-lp-milp/resources/server_examples.md delete mode 100644 .github/skills/cuopt-qp/SKILL.md delete mode 100644 .github/skills/cuopt-routing/SKILL.md delete mode 100644 .github/skills/cuopt-server/SKILL.md delete mode 100644 .github/skills/cuopt-server/resources/lp_milp_examples.md delete mode 100644 .github/skills/cuopt-server/resources/routing_examples.md create mode 120000 .opencode/AGENTS.md create mode 120000 .opencode/skills create mode 100644 agents/AGENTS.md create mode 100755 ci/test_skills_assets.sh create mode 100755 ci/utils/sync_skills_version.sh create mode 100755 ci/utils/validate_skills.sh create mode 100644 gemini-extension.json rename {.github/skills => skills}/cuopt-developer/SKILL.md (99%) create mode 100644 skills/cuopt-installation-api-c/SKILL.md rename {.github/skills/cuopt-installation => skills/cuopt-installation-api-c}/resources/verification_examples.md (98%) create mode 100644 skills/cuopt-installation-api-python/SKILL.md create mode 100644 skills/cuopt-installation-api-python/resources/verification_examples.md create mode 100644 skills/cuopt-installation-common/SKILL.md create mode 100644 skills/cuopt-installation-developer/SKILL.md create mode 100644 skills/cuopt-lp-milp-api-c/SKILL.md create mode 100644 skills/cuopt-lp-milp-api-c/assets/README.md create mode 100644 skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md create mode 100644 skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c create mode 100644 skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md create mode 100644 skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c create mode 100644 skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md create mode 100644 skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md create mode 100644 skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c create mode 100644 skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md create mode 100644 skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c create mode 100644 skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md create mode 100644 skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps create mode 100644 skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c rename .github/skills/cuopt-lp-milp/resources/c_api_examples.md => skills/cuopt-lp-milp-api-c/resources/examples.md (100%) create mode 100644 skills/cuopt-lp-milp-api-cli/SKILL.md create mode 100644 skills/cuopt-lp-milp-api-cli/assets/README.md create mode 100644 skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md create mode 100644 skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps create mode 100644 skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md create mode 100644 skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps create mode 100644 skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md create mode 100644 skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps create mode 100644 skills/cuopt-lp-milp-api-python/SKILL.md create mode 100644 skills/cuopt-lp-milp-api-python/assets/README.md create mode 100644 skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md create mode 100644 skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py create mode 100644 skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md create mode 100644 skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py create mode 100644 skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md create mode 100644 skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py create mode 100644 skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md create mode 100644 skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py create mode 100644 skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py create mode 100644 skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md create mode 100644 skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py create mode 100644 skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md create mode 100644 skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md create mode 100644 skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps create mode 100644 skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py create mode 100644 skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md create mode 100644 skills/cuopt-qp-api-c/SKILL.md create mode 100644 skills/cuopt-qp-api-c/assets/README.md create mode 100644 skills/cuopt-qp-api-cli/SKILL.md create mode 100644 skills/cuopt-qp-api-cli/assets/README.md create mode 100644 skills/cuopt-qp-api-python/SKILL.md create mode 100644 skills/cuopt-qp-api-python/assets/README.md create mode 100644 skills/cuopt-qp-api-python/assets/least_squares/README.md create mode 100644 skills/cuopt-qp-api-python/assets/least_squares/model.py create mode 100644 skills/cuopt-qp-api-python/assets/maximization_workaround/README.md create mode 100644 skills/cuopt-qp-api-python/assets/maximization_workaround/model.py create mode 100644 skills/cuopt-qp-api-python/assets/portfolio/README.md create mode 100644 skills/cuopt-qp-api-python/assets/portfolio/model.py rename .github/skills/cuopt-qp/resources/python_examples.md => skills/cuopt-qp-api-python/resources/examples.md (100%) create mode 100644 skills/cuopt-routing-api-python/SKILL.md create mode 100644 skills/cuopt-routing-api-python/assets/README.md create mode 100644 skills/cuopt-routing-api-python/assets/pdp_basic/README.md create mode 100644 skills/cuopt-routing-api-python/assets/pdp_basic/model.py create mode 100644 skills/cuopt-routing-api-python/assets/vrp_basic/README.md create mode 100644 skills/cuopt-routing-api-python/assets/vrp_basic/model.py rename .github/skills/cuopt-routing/resources/python_examples.md => skills/cuopt-routing-api-python/resources/examples.md (96%) rename {.github/skills/cuopt-routing => skills/cuopt-routing-api-python}/resources/server_examples.md (100%) create mode 100644 skills/cuopt-server-api-python/SKILL.md create mode 100644 skills/cuopt-server-api-python/assets/README.md create mode 100644 skills/cuopt-server-api-python/assets/lp_basic/README.md create mode 100644 skills/cuopt-server-api-python/assets/lp_basic/client.py create mode 100644 skills/cuopt-server-api-python/assets/milp_basic/README.md create mode 100644 skills/cuopt-server-api-python/assets/milp_basic/client.py create mode 100644 skills/cuopt-server-api-python/assets/pdp_basic/README.md create mode 100644 skills/cuopt-server-api-python/assets/pdp_basic/client.py create mode 100644 skills/cuopt-server-api-python/assets/vrp_basic/README.md create mode 100644 skills/cuopt-server-api-python/assets/vrp_basic/client.py create mode 100644 skills/cuopt-server-api-python/assets/vrp_simple/README.md create mode 100644 skills/cuopt-server-api-python/assets/vrp_simple/client.py create mode 100644 skills/cuopt-server-common/SKILL.md rename {.github/skills => skills}/cuopt-user-rules/SKILL.md (67%) create mode 100644 skills/lp-milp-formulation/SKILL.md create mode 100644 skills/qp-formulation/SKILL.md create mode 100644 skills/routing-formulation/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000000..c1a01b7d42 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,120 @@ +{ + "name": "nvidia-cuopt-skills", + "owner": { + "name": "NVIDIA" + }, + "metadata": { + "description": "Agent skills for NVIDIA cuOpt: routing (VRP, TSP, PDP), LP/MILP/QP, installation (Python/C/developer), and REST server.", + "version": "26.04.00" + }, + "plugins": [ + { + "name": "cuopt-user-rules", + "source": "./skills/cuopt-user-rules", + "skills": "./", + "description": "Base behavior rules for using NVIDIA cuOpt. Read first when helping users with cuOpt (routing, LP/MILP, QP, installation, server)." + }, + { + "name": "cuopt-developer", + "source": "./skills/cuopt-developer", + "skills": "./", + "description": "Contribute to NVIDIA cuOpt codebase including C++/CUDA, Python, server, docs, and CI. Use when the user wants to modify solver internals, add features, submit PRs, or understand the codebase architecture." + }, + { + "name": "cuopt-installation-common", + "source": "./skills/cuopt-installation-common", + "skills": "./", + "description": "Install cuOpt — system and environment requirements only. Domain concepts; no install commands or interface guidance." + }, + { + "name": "cuopt-installation-api-python", + "source": "./skills/cuopt-installation-api-python", + "skills": "./", + "description": "Install cuOpt for Python — pip, conda, Docker, verification. Use when the user is installing or verifying the Python API." + }, + { + "name": "cuopt-installation-api-c", + "source": "./skills/cuopt-installation-api-c", + "skills": "./", + "description": "Install cuOpt for C — conda, locate lib/headers, verification. Use when the user is installing or verifying the C API." + }, + { + "name": "cuopt-installation-developer", + "source": "./skills/cuopt-installation-developer", + "skills": "./", + "description": "Developer installation — build cuOpt from source, run tests. Use when the user wants to set up a dev environment to contribute or modify cuOpt." + }, + { + "name": "lp-milp-formulation", + "source": "./skills/lp-milp-formulation", + "skills": "./", + "description": "LP/MILP concepts and going from problem text to formulation. What LP/MILP are, required formulation questions, typical modeling elements, and how to parse problem statements." + }, + { + "name": "cuopt-lp-milp-api-python", + "source": "./skills/cuopt-lp-milp-api-python", + "skills": "./", + "description": "Solve LP and MILP with the Python API. Use when the user asks about optimization with linear constraints, integer variables, scheduling, resource allocation, facility location, or production planning." + }, + { + "name": "cuopt-lp-milp-api-c", + "source": "./skills/cuopt-lp-milp-api-c", + "skills": "./", + "description": "LP and MILP with cuOpt — C API only. Use when the user is embedding LP/MILP in C/C++." + }, + { + "name": "cuopt-lp-milp-api-cli", + "source": "./skills/cuopt-lp-milp-api-cli", + "skills": "./", + "description": "LP and MILP with cuOpt — CLI only (MPS files, cuopt_cli). Use when the user is solving from MPS via command line." + }, + { + "name": "routing-formulation", + "source": "./skills/routing-formulation", + "skills": "./", + "description": "Vehicle routing (VRP, TSP, PDP) — problem types and data requirements. Domain concepts; no API or interface." + }, + { + "name": "cuopt-routing-api-python", + "source": "./skills/cuopt-routing-api-python", + "skills": "./", + "description": "Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API only. Use when the user is building or solving routing in Python." + }, + { + "name": "qp-formulation", + "source": "./skills/qp-formulation", + "skills": "./", + "description": "Quadratic Programming (QP) — problem form and constraints. Domain concepts; no API or interface. QP is beta." + }, + { + "name": "cuopt-qp-api-python", + "source": "./skills/cuopt-qp-api-python", + "skills": "./", + "description": "Quadratic Programming (QP) with cuOpt — Python API only (beta). Use when the user is building or solving QP in Python." + }, + { + "name": "cuopt-qp-api-c", + "source": "./skills/cuopt-qp-api-c", + "skills": "./", + "description": "Quadratic Programming (QP) with cuOpt — C API. Use when the user is embedding QP in C/C++." + }, + { + "name": "cuopt-qp-api-cli", + "source": "./skills/cuopt-qp-api-cli", + "skills": "./", + "description": "QP with cuOpt — CLI (e.g. cuopt_cli with QP-capable input). Use when the user is solving QP from the command line." + }, + { + "name": "cuopt-server-common", + "source": "./skills/cuopt-server-common", + "skills": "./", + "description": "cuOpt REST server — what it does and how requests flow. Domain concepts; no deploy or client code." + }, + { + "name": "cuopt-server-api-python", + "source": "./skills/cuopt-server-api-python", + "skills": "./", + "description": "cuOpt REST server — start server, endpoints, Python/curl client examples. Use when the user is deploying or calling the REST API." + } + ] +} diff --git a/.claude/AGENTS.md b/.claude/AGENTS.md new file mode 120000 index 0000000000..be77ac83a1 --- /dev/null +++ b/.claude/AGENTS.md @@ -0,0 +1 @@ +../AGENTS.md \ No newline at end of file diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 120000 index 0000000000..f5f4bd7b93 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1 @@ +../.github/AGENTS.md \ No newline at end of file diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 0000000000..42c5394a18 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json new file mode 100644 index 0000000000..5f34873671 --- /dev/null +++ b/.cursor-plugin/plugin.json @@ -0,0 +1,22 @@ +{ + "name": "nvidia-cuopt-skills", + "description": "Agent skills for NVIDIA cuOpt: routing (VRP, TSP, PDP), LP/MILP/QP, installation (Python/C/developer), and REST server. Use when building or solving optimization with cuOpt.", + "version": "26.04.00", + "author": { + "name": "NVIDIA" + }, + "homepage": "https://github.com/NVIDIA/cuopt", + "repository": "https://github.com/NVIDIA/cuopt", + "license": "Apache-2.0", + "skills": "skills", + "keywords": [ + "nvidia", + "cuopt", + "optimization", + "routing", + "vrp", + "lp", + "milp", + "qp" + ] +} diff --git a/.cursor/AGENTS.md b/.cursor/AGENTS.md new file mode 120000 index 0000000000..be77ac83a1 --- /dev/null +++ b/.cursor/AGENTS.md @@ -0,0 +1 @@ +../AGENTS.md \ No newline at end of file diff --git a/.cursor/skills b/.cursor/skills new file mode 120000 index 0000000000..42c5394a18 --- /dev/null +++ b/.cursor/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/.github/AGENTS.md b/.github/AGENTS.md deleted file mode 100644 index 7854e6599f..0000000000 --- a/.github/AGENTS.md +++ /dev/null @@ -1,32 +0,0 @@ -# AGENTS.md - cuOpt AI Agent Entry Point - -AI agent skills for NVIDIA cuOpt optimization engine. - -## Quick Start - -| Task | Read These Skills | -|------|-------------------| -| **Using cuOpt** (routing, LP, etc.) | `skills/cuopt-user-rules/` → then domain skill | -| **Developing cuOpt** (contributing) | `skills/cuopt-developer/` | - -## Skills Directory - -See `skills/README.md` for the full index. - -### User Skills (read cuopt-user-rules first) -- `skills/cuopt-routing/` — VRP, TSP, PDP -- `skills/cuopt-lp-milp/` — Linear programming, integer variables -- `skills/cuopt-qp/` — Quadratic programming -- `skills/cuopt-debugging/` — Troubleshooting -- `skills/cuopt-installation/` — Setup & requirements -- `skills/cuopt-server/` — REST API deployment - -### Developer Skill (has its own rules) -- `skills/cuopt-developer/` — Contributing code - -## Resources - -- [cuOpt Documentation](https://docs.nvidia.com/cuopt/user-guide/latest/) -- [cuopt-examples repo](https://github.com/NVIDIA/cuopt-examples) -- [GitHub Issues](https://github.com/NVIDIA/cuopt/issues) -- [Developer Forums](https://forums.developer.nvidia.com/c/ai-data-science/nvidia-cuopt/514) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cf3a570486..7958eac440 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -22,7 +22,7 @@ container-builder/ @nvidia/cuopt-infra-codeowners /.github/.coderabbit_review_guide.md @nvidia/cuopt-infra-codeowners /.github/ISSUE_TEMPLATE/ @nvidia/cuopt-infra-codeowners /.github/PULL_REQUEST_TEMPLATE.md @nvidia/cuopt-infra-codeowners -/.github/skills/ @nvidia/cuopt-infra-codeowners +/skills/ @nvidia/cuopt-infra-codeowners /.github/agents-legacy/ @nvidia/cuopt-infra-codeowners #packaging code owners diff --git a/.github/skills/README.md b/.github/skills/README.md deleted file mode 100644 index 6ffdb6c5e7..0000000000 --- a/.github/skills/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# cuOpt Skills - -This directory contains AI agent skills for NVIDIA cuOpt. - -## For Agents - -1. **First**: Read `cuopt-user-rules/SKILL.md` for user tasks -2. **Then**: Read the relevant domain skill - -For development tasks, read `cuopt-developer/SKILL.md` (has its own rules). - -## Skills Index - -### Rules -| Skill | Description | -|-------|-------------| -| `cuopt-user-rules/` | Behavior rules for user tasks (read first) | - -### Problem-Solving -| Skill | Description | -|-------|-------------| -| `cuopt-routing/` | VRP, TSP, PDP, fleet optimization | -| `cuopt-lp-milp/` | Linear & mixed-integer programming | -| `cuopt-qp/` | Quadratic programming (beta) | - -### Workflow -| Skill | Description | -|-------|-------------| -| `cuopt-debugging/` | Troubleshooting, errors, diagnostics | -| `cuopt-installation/` | Setup, pip, conda, Docker, GPU | - -### Integration -| Skill | Description | -|-------|-------------| -| `cuopt-server/` | REST API deployment | - -### Development -| Skill | Description | -|-------|-------------| -| `cuopt-developer/` | Contributing to codebase (own rules) | diff --git a/.github/skills/cuopt-debugging/SKILL.md b/.github/skills/cuopt-debugging/SKILL.md deleted file mode 100644 index 649b06387d..0000000000 --- a/.github/skills/cuopt-debugging/SKILL.md +++ /dev/null @@ -1,267 +0,0 @@ ---- -name: cuopt-debugging -description: Troubleshoot cuOpt problems including errors, wrong results, infeasible solutions, performance issues, and status codes. Use when the user says something isn't working, gets unexpected results, or needs help diagnosing issues. ---- - -# cuOpt Debugging Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Diagnose and fix issues with cuOpt solutions, errors, and performance. - -## Before You Start: Required Questions - -**Ask these to understand the problem:** - -1. **What's the symptom?** - - Error message? - - Wrong/unexpected results? - - Empty solution? - - Performance too slow? - -2. **What's the status?** - - For LP/MILP: `problem.Status.name` - - For Routing: `solution.get_status()` - - For Server: HTTP response code - -3. **Can you share?** - - The error message (exact text) - - The code that produces it - - Problem size (variables, constraints, locations) - - Share important log messages or status of on-going run. - -## Quick Diagnosis by Symptom - -### "Solution is empty/None but status looks OK" - -**Most common cause: Wrong status string case** - -```python -# ❌ WRONG - "OPTIMAL" never matches, silently fails -if problem.Status.name == "OPTIMAL": - print(problem.ObjValue) # Never runs! - -# ✅ CORRECT - use PascalCase -if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(problem.ObjValue) -``` - -**Diagnostic code:** -```python -print(f"Actual status: '{problem.Status.name}'") -print(f"Matches 'Optimal': {problem.Status.name == 'Optimal'}") -print(f"Matches 'OPTIMAL': {problem.Status.name == 'OPTIMAL'}") -``` - -### "Objective value is wrong/zero" - -**Check if variables are actually used:** -```python -for var in [x, y, z]: - print(f"{var.name}: {var.getValue()}") -print(f"Objective: {problem.ObjValue}") -``` - -**Common causes:** -- Constraints too restrictive (all zeros is feasible) -- Objective coefficients have wrong sign -- Wrong variable in objective - -### "Infeasible" status - -**For LP/MILP:** -```python -if problem.Status.name == "Infeasible": - print("Problem has no feasible solution") - # Check constraints manually - for name in constraint_names: - c = problem.getConstraint(name) - print(f"{name}: {c}") -``` - -**Common causes:** -- Conflicting constraints (x <= 5 AND x >= 10) -- Bounds too tight -- Missing a "slack" variable for soft constraints - -**For Routing:** -```python -if solution.get_status() != 0: - print(f"Error: {solution.get_error_message()}") - infeasible = solution.get_infeasible_orders() - print(f"Infeasible orders: {infeasible.to_list()}") -``` - -**Common routing infeasibility causes:** -- Time windows too tight (earliest > vehicle latest) -- Total demand > total capacity -- Order location unreachable in time - -### "Integer variable has fractional value" - -```python -# Check how variable was defined -int_var = problem.addVariable( - lb=0, ub=10, - vtype=INTEGER, # Must be INTEGER, not CONTINUOUS - name="count" -) - -# Also check if status is actually optimal -if problem.Status.name == "FeasibleFound": - print("Warning: not fully optimal, may have fractional intermediate values") -``` - -### Server returns 422 Validation Error - -**Check payload against OpenAPI spec:** - -Common field name mistakes: -``` -❌ transit_time_matrix_data → ✅ travel_time_matrix_data -❌ vehicle_capacities → ✅ capacities -❌ locations → ✅ task_locations -``` - -**Capacity format:** -```json -// ❌ WRONG -"capacities": [[50], [50]] - -// ✅ CORRECT -"capacities": [[50, 50]] -``` - -### OutOfMemoryError - -**Check problem size:** -```python -print(f"Variables: {problem.num_variables}") -print(f"Constraints: {problem.num_constraints}") - -# For routing -print(f"Locations: {n_locations}") -print(f"Orders: {n_orders}") -print(f"Fleet: {n_fleet}") -``` - -**Mitigations:** -- Reduce problem size -- Use sparse constraint matrix -- For routing: reduce time limit, simplify constraints - -### cudf Type Errors - -**Always use explicit dtypes:** -```python -cost_matrix = cost_matrix.astype("float32") -demand = cudf.Series([...], dtype="int32") -order_locations = cudf.Series([...], dtype="int32") -time_windows = cudf.Series([...], dtype="int32") -``` - -### MPS Parsing Fails - -**Check MPS format:** -```bash -head -30 problem.mps -``` - -**Required sections in order:** -1. NAME -2. ROWS -3. COLUMNS -4. RHS -5. (optional) BOUNDS -6. ENDATA - -**Common issues:** -- Missing ENDATA -- Integer markers malformed: `'MARKER'`, `'INTORG'`, `'INTEND'` -- Invalid characters or encoding - -## Status Code Reference - -### LP Status Values -| Status | Meaning | -|--------|---------| -| `Optimal` | Found optimal solution | -| `PrimalFeasible` | Found feasible but may not be optimal | -| `PrimalInfeasible` | No feasible solution exists | -| `DualInfeasible` | Problem is unbounded | -| `TimeLimit` | Stopped due to time limit | -| `IterationLimit` | Stopped due to iteration limit | -| `NumericalError` | Numerical issues encountered | -| `NoTermination` | Solver didn't converge | - -### MILP Status Values -| Status | Meaning | -|--------|---------| -| `Optimal` | Found optimal solution | -| `FeasibleFound` | Found feasible, within gap tolerance | -| `Infeasible` | No feasible solution exists | -| `Unbounded` | Problem is unbounded | -| `TimeLimit` | Stopped due to time limit | -| `NoTermination` | No solution found yet | - -### Routing Status Values -| Code | Meaning | -|------|---------| -| 0 | SUCCESS | -| 1 | FAIL | -| 2 | TIMEOUT | -| 3 | EMPTY | - -## Performance Debugging - -### Slow LP/MILP Solve - -```python -settings = SolverSettings() -settings.set_parameter("log_to_console", 1) # See progress -settings.set_parameter("time_limit", 60) # Don't wait forever - -# For MILP, accept good-enough solution -settings.set_parameter("mip_relative_gap", 0.05) # 5% gap -``` - -### Slow Routing Solve - -```python -ss = routing.SolverSettings() -ss.set_time_limit(60) # Increase time for better solutions -ss.set_verbose_mode(True) # See progress during solve -``` - -## Diagnostic Checklist - -``` -□ Status checked with correct case (PascalCase)? -□ All variables have correct vtype (INTEGER vs CONTINUOUS)? -□ Constraint directions correct (<= vs >= vs ==)? -□ Objective sense correct (MINIMIZE vs MAXIMIZE)? -□ For QP: using MINIMIZE (not MAXIMIZE)? -□ Data types explicit (float32, int32)? -□ Matrix dimensions match n_locations? -□ Time windows have transit_time_matrix? -``` - -## Diagnostic Code Snippets - -See [resources/diagnostic_snippets.md](resources/diagnostic_snippets.md) for copy-paste diagnostic code: -- Status checking -- Variable inspection -- Constraint analysis -- Routing infeasibility diagnosis -- Server response debugging -- Memory and performance checks - -## When to Escalate - -Switch to **cuopt-developer** if: -- Bug appears to be in cuOpt itself -- Need to examine solver internals - -File a GitHub issue if: -- Reproducible bug with minimal example -- Include: cuOpt version, CUDA version, error message, minimal repro code diff --git a/.github/skills/cuopt-debugging/resources/diagnostic_snippets.md b/.github/skills/cuopt-debugging/resources/diagnostic_snippets.md deleted file mode 100644 index 61f5c19bfd..0000000000 --- a/.github/skills/cuopt-debugging/resources/diagnostic_snippets.md +++ /dev/null @@ -1,219 +0,0 @@ -# Debugging: Diagnostic Snippets - -## LP/MILP Diagnostics - -### Check Status Properly - -```python -# Print actual status value -print(f"Status: '{problem.Status.name}'") -print(f"Status type: {type(problem.Status.name)}") - -# Common mistake: wrong case -print(f"== 'Optimal': {problem.Status.name == 'Optimal'}") # ✅ -print(f"== 'OPTIMAL': {problem.Status.name == 'OPTIMAL'}") # ❌ Always False -``` - -### Inspect Variables - -```python -# Check all variable values -for var in [x, y, z]: - print(f"{var.name}: lb={var.lb}, ub={var.ub}, value={var.getValue()}") - -# Check if integer variables are actually integer -for var in integer_vars: - val = var.getValue() - is_int = abs(val - round(val)) < 1e-6 - print(f"{var.name}: {val} (is_integer: {is_int})") -``` - -### Inspect Constraints - -```python -# Check constraint values -for name in ["constraint1", "constraint2"]: - c = problem.getConstraint(name) - print(f"{name}: dual={c.DualValue}") -``` - -### Check Problem Size - -```python -print(f"Variables: {problem.num_variables}") -print(f"Constraints: {problem.num_constraints}") -``` - -## Routing Diagnostics - -### Check Solution Status - -```python -status = solution.get_status() -print(f"Status code: {status}") -# 0 = SUCCESS -# 1 = FAIL -# 2 = TIMEOUT -# 3 = EMPTY - -if status != 0: - print(f"Message: {solution.get_message()}") - print(f"Error: {solution.get_error_message()}") -``` - -### Find Infeasible Orders - -```python -infeasible = solution.get_infeasible_orders() -if len(infeasible) > 0: - print(f"Infeasible orders: {infeasible.to_list()}") - - # Check why each is infeasible - for order_idx in infeasible.to_list(): - print(f"\nOrder {order_idx}:") - print(f" Location: {order_locations[order_idx]}") - print(f" Time window: [{order_earliest[order_idx]}, {order_latest[order_idx]}]") - print(f" Demand: {demand[order_idx]}") -``` - -### Verify Data Dimensions - -```python -print(f"Cost matrix shape: {cost_matrix.shape}") -print(f"n_locations declared: {dm.n_locations}") -print(f"n_orders: {len(order_locations)}") -print(f"n_fleet: {dm.n_fleet}") - -# Check consistency -assert cost_matrix.shape[0] == cost_matrix.shape[1], "Matrix not square" -assert cost_matrix.shape[0] == dm.n_locations, "Matrix size != n_locations" -``` - -### Check Data Types - -```python -# For numpy arrays, use .dtype directly -# For pandas/cudf DataFrames, use .values.dtype or .to_numpy().dtype -print(f"cost_matrix dtype: {cost_matrix.values.dtype}") # float32 -print(f"order_locations dtype: {order_locations.values.dtype}") # int32 -print(f"demand dtype: {demand.values.dtype}") # int32 -``` - -### Verify Time Windows Feasibility - -```python -# Check for impossible time windows -for i in range(len(order_earliest)): - if order_earliest[i] > order_latest[i]: - print(f"Order {i}: earliest ({order_earliest[i]}) > latest ({order_latest[i]})") - -# Check if orders are reachable from depot in time -depot = 0 -for i in range(len(order_locations)): - loc = order_locations[i] - travel_time = transit_time_matrix.iloc[depot, loc] - if travel_time > order_latest[i]: - print(f"Order {i}: unreachable (travel={travel_time}, latest={order_latest[i]})") -``` - -### Check Capacity Feasibility - -```python -total_demand = demand.sum() -total_capacity = vehicle_capacity.sum() -print(f"Total demand: {total_demand}") -print(f"Total capacity: {total_capacity}") -if total_demand > total_capacity: - print("WARNING: Total demand exceeds total capacity!") -``` - -## Server Diagnostics - -### Check Response Structure - -```python -import json - -response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) -print(f"Status code: {response.status_code}") -print(f"Response: {json.dumps(response.json(), indent=2)}") -``` - -### Validate Payload Against Schema - -```bash -# Get OpenAPI spec -curl -s http://localhost:8000/cuopt.yaml > cuopt_spec.yaml - -# Check payload structure manually -``` - -### Common 422 Error Fixes - -```python -# ❌ Wrong field name -payload = {"transit_time_matrix_data": {...}} - -# ✅ Correct field name -payload = {"travel_time_matrix_data": {...}} - -# ❌ Wrong capacity format -"capacities": [[50], [50]] - -# ✅ Correct capacity format -"capacities": [[50, 50]] -``` - -## Memory Diagnostics - -### Check GPU Memory - -```python -import subprocess -result = subprocess.run(['nvidia-smi'], capture_output=True, text=True) -print(result.stdout) -``` - -### Estimate Problem Memory - -```python -# Rough estimate for routing -n_locations = 1000 -n_fleet = 50 -n_orders = 500 - -# Cost matrix: n_locations² * 4 bytes (float32) -matrix_size = n_locations * n_locations * 4 / 1e9 # GB -print(f"Cost matrix: ~{matrix_size:.2f} GB") -``` - -## Performance Diagnostics - -### Time the Solve - -```python -import time - -start = time.time() -problem.solve(settings) -elapsed = time.time() - start -print(f"Solve time: {elapsed:.2f}s") -``` - -### Enable Solver Logging - -```python -settings = SolverSettings() -settings.set_parameter("log_to_console", 1) -``` - ---- - -## Additional References - -| Topic | Resource | -|-------|----------| -| Python API docstrings | `python/cuopt/cuopt/routing/vehicle_routing.py` | -| LP/MILP Problem class | `python/cuopt/cuopt/linear_programming/problem.py` | -| Server API spec | `docs/cuopt/source/cuopt_spec.yaml` | -| Troubleshooting guide | [NVIDIA cuOpt Docs](https://docs.nvidia.com/cuopt/user-guide/latest/troubleshooting.html) | diff --git a/.github/skills/cuopt-installation/SKILL.md b/.github/skills/cuopt-installation/SKILL.md deleted file mode 100644 index 981063f477..0000000000 --- a/.github/skills/cuopt-installation/SKILL.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -name: cuopt-installation -description: Install and set up NVIDIA cuOpt including pip, conda, Docker, and GPU requirements. Use when the user asks about installation, setup, environments, CUDA versions, GPU requirements, or getting started. ---- - -# cuOpt Installation Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Set up NVIDIA cuOpt for GPU-accelerated optimization. - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **What's your environment?** - - Local machine with NVIDIA GPU? - - Cloud instance (AWS, GCP, Azure)? - - Docker/Kubernetes? - - No GPU (need cloud solution)? - -2. **What's your CUDA version?** - ```bash - nvcc --version - # or - nvidia-smi - ``` - -3. **What do you need?** - - Python API only? - - REST Server for production? - - C API for embedding? - -4. **Package manager preference?** - - pip - - conda - - Docker - -## System Requirements - -### GPU Requirements -- NVIDIA GPU with Compute Capability >= 7.0 (Volta or newer) -- Supported: V100, A100, H100, RTX 20xx/30xx/40xx, etc. -- NOT supported: GTX 10xx series (Pascal) - -### CUDA Requirements -- CUDA 12.x or CUDA 13.x (match package suffix) -- Compatible NVIDIA driver - -### Check Your System - -```bash -# Check GPU -nvidia-smi - -# Check CUDA version -nvcc --version - -# Check compute capability -nvidia-smi --query-gpu=compute_cap --format=csv -``` - -## Installation Methods - -### pip (Recommended for Python) - -```bash -# For CUDA 13 -pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu13 - -# For CUDA 12 -pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12 - -# With version pinning (recommended for reproducibility) -pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==26.2.*' -``` - -### pip: Server + Client - -```bash -# CUDA 12 example -pip install --extra-index-url=https://pypi.nvidia.com \ - cuopt-server-cu12 cuopt-sh-client - -# With version pinning -pip install --extra-index-url=https://pypi.nvidia.com \ - cuopt-server-cu12==26.02.* cuopt-sh-client==26.02.* -``` - -### conda - -```bash -# Python API -conda install -c rapidsai -c conda-forge -c nvidia cuopt - -# Server + client -conda install -c rapidsai -c conda-forge -c nvidia cuopt-server cuopt-sh-client - -# With version pinning -conda install -c rapidsai -c conda-forge -c nvidia cuopt=26.02.* -``` - -### Docker (Recommended for Server) - -```bash -# Pull image -docker pull nvidia/cuopt:latest-cuda12.9-py3.13 - -# Run server -docker run --gpus all -it --rm \ - -p 8000:8000 \ - -e CUOPT_SERVER_PORT=8000 \ - nvidia/cuopt:latest-cuda12.9-py3.13 - -# Verify -curl http://localhost:8000/cuopt/health -``` - -### Docker: Interactive Python - -```bash -docker run --gpus all -it --rm nvidia/cuopt:latest-cuda12.9-py3.13 python -``` - -## Verification - -### Verify Python Installation - -```python -# Test import -import cuopt -print(f"cuOpt version: {cuopt.__version__}") - -# Test GPU access -from cuopt import routing -dm = routing.DataModel(n_locations=3, n_fleet=1, n_orders=2) -print("GPU access OK") -``` - -### Verify Server Installation - -```bash -# Start server -python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 & - -# Wait and test -sleep 5 -curl -s http://localhost:8000/cuopt/health | jq . -``` - -### Verify C API Installation - -```bash -# Find header -find $CONDA_PREFIX -name "cuopt_c.h" - -# Find library -find $CONDA_PREFIX -name "libcuopt.so" -``` - -## Common Installation Issues - -### "No module named 'cuopt'" - -```bash -# Check if installed -pip list | grep cuopt - -# Check Python environment -which python -echo $CONDA_PREFIX - -# Reinstall -pip uninstall cuopt-cu12 cuopt-cu13 -pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12 -``` - -### "CUDA not available" / GPU not detected - -```bash -# Check NVIDIA driver -nvidia-smi - -# Check CUDA toolkit -nvcc --version - -# In Python -import torch # if using PyTorch -print(torch.cuda.is_available()) -``` - -### Version mismatch (CUDA 12 vs 13) - -```bash -# Check installed CUDA -nvcc --version - -# Install matching package -# For CUDA 12.x -pip install cuopt-cu12 - -# For CUDA 13.x -pip install cuopt-cu13 -``` - -### Docker: "could not select device driver" - -```bash -# Install NVIDIA Container Toolkit -# Ubuntu: -distribution=$(. /etc/os-release;echo $ID$VERSION_ID) -curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - -curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \ - sudo tee /etc/apt/sources.list.d/nvidia-docker.list -sudo apt-get update -sudo apt-get install -y nvidia-container-toolkit -sudo systemctl restart docker -``` - -## Environment Setup - -### Create Clean Environment (conda) - -```bash -conda create -n cuopt-env python=3.11 -conda activate cuopt-env -conda install -c rapidsai -c conda-forge -c nvidia cuopt -``` - -### Create Clean Environment (pip/venv) - -```bash -python -m venv cuopt-env -source cuopt-env/bin/activate # Linux/Mac -pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu12 -``` - -## Cloud Deployment - -### AWS - -- Use p4d.24xlarge (A100) or p3.2xlarge (V100) -- Deep Learning AMI has CUDA pre-installed -- Or use provided Docker image - -### GCP - -- Use a2-highgpu-1g (A100) or n1-standard with T4 -- Deep Learning VM has CUDA pre-installed - -### Azure - -- Use NC-series (T4, A100) -- Data Science VM has CUDA pre-installed - -## Offline Installation - -```bash -# Download wheels on connected machine -pip download --extra-index-url=https://pypi.nvidia.com cuopt-cu12 -d ./wheels - -# Transfer wheels directory to offline machine - -# Install from local wheels -pip install --no-index --find-links=./wheels cuopt-cu12 -``` - -## Upgrade - -```bash -# pip -pip install --upgrade --extra-index-url=https://pypi.nvidia.com cuopt-cu12 - -# conda -conda update -c rapidsai -c conda-forge -c nvidia cuopt - -# Docker -docker pull nvidia/cuopt:latest-cuda12.9-py3.13 -``` - -## Verification Examples - -See [resources/verification_examples.md](resources/verification_examples.md) for: -- Python installation verification -- LP/MILP verification -- Server verification -- C API verification -- System requirements check -- Docker verification - -## Additional Resources - -- Full installation docs: `docs/cuopt/source/cuopt-python/quick-start.rst` -- Server setup: `docs/cuopt/source/cuopt-server/quick-start.rst` -- [NVIDIA cuOpt Documentation](https://docs.nvidia.com/cuopt/user-guide/latest/) diff --git a/.github/skills/cuopt-lp-milp/SKILL.md b/.github/skills/cuopt-lp-milp/SKILL.md deleted file mode 100644 index 58dfb4f09a..0000000000 --- a/.github/skills/cuopt-lp-milp/SKILL.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -name: cuopt-lp-milp -description: Solve Linear Programming (LP) and Mixed-Integer Linear Programming (MILP) with NVIDIA cuOpt. Use when the user asks about optimization with linear constraints, integer variables, scheduling, resource allocation, facility location, or production planning. ---- - -# cuOpt LP/MILP Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Model and solve linear and mixed-integer linear programs using NVIDIA cuOpt's GPU-accelerated solver. - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **Problem formulation?** - - What are the decision variables? - - What is the objective (minimize/maximize what)? - - What are the constraints? - -2. **Variable types?** - - All continuous (LP)? - - Some integer/binary (MILP)? - -3. **Interface preference?** - - Python API (recommended for modeling) - - C API (native embedding) - - CLI (quick solve from MPS file) - - REST Server (production deployment) - -4. **Do you have an MPS file?** - - If yes, CLI or C API may be simpler - - If no, Python API is best for building the model - -## Interface Support - -| Interface | LP | MILP | -|-----------|:--:|:----:| -| Python | ✓ | ✓ | -| C API | ✓ | ✓ | -| CLI | ✓ | ✓ | -| REST | ✓ | ✓ | - -## Quick Reference: Python API - -### LP Example - -```python -from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE -from cuopt.linear_programming.solver_settings import SolverSettings - -# Create problem -problem = Problem("MyLP") - -# Decision variables -x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") -y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") - -# Constraints -problem.addConstraint(2*x + 3*y <= 120, name="resource_a") -problem.addConstraint(4*x + 2*y <= 100, name="resource_b") - -# Objective -problem.setObjective(40*x + 30*y, sense=MAXIMIZE) - -# Solve -settings = SolverSettings() -settings.set_parameter("time_limit", 60) -problem.solve(settings) - -# Check status (CRITICAL: use PascalCase!) -if problem.Status.name in ["Optimal", "PrimalFeasible"]: - print(f"Objective: {problem.ObjValue}") - print(f"x = {x.getValue()}") - print(f"y = {y.getValue()}") -``` - -### MILP Example (with integer variables) - -```python -from cuopt.linear_programming.problem import Problem, CONTINUOUS, INTEGER, MINIMIZE - -problem = Problem("FacilityLocation") - -# Binary variable (integer with bounds 0-1) -open_facility = problem.addVariable(lb=0, ub=1, vtype=INTEGER, name="open") - -# Continuous variable -production = problem.addVariable(lb=0, vtype=CONTINUOUS, name="production") - -# Linking constraint: can only produce if facility is open -problem.addConstraint(production <= 1000 * open_facility, name="link") - -# Objective: fixed cost + variable cost -problem.setObjective(500*open_facility + 2*production, sense=MINIMIZE) - -# MILP-specific settings -settings = SolverSettings() -settings.set_parameter("time_limit", 120) -settings.set_parameter("mip_relative_gap", 0.01) # 1% optimality gap - -problem.solve(settings) - -# Check status -if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(f"Open facility: {open_facility.getValue() > 0.5}") - print(f"Production: {production.getValue()}") -``` - -## CRITICAL: Status Checking - -**Status values use PascalCase, NOT ALL_CAPS:** - -```python -# ✅ CORRECT -if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(problem.ObjValue) - -# ❌ WRONG - will silently fail! -if problem.Status.name == "OPTIMAL": # Never matches! - print(problem.ObjValue) -``` - -**LP Status Values:** `Optimal`, `NoTermination`, `NumericalError`, `PrimalInfeasible`, `DualInfeasible`, `IterationLimit`, `TimeLimit`, `PrimalFeasible` - -**MILP Status Values:** `Optimal`, `FeasibleFound`, `Infeasible`, `Unbounded`, `TimeLimit`, `NoTermination` - -## Quick Reference: C API - -```c -#include - -// CSR format for constraints -cuopt_int_t row_offsets[] = {0, 2, 4}; -cuopt_int_t col_indices[] = {0, 1, 0, 1}; -cuopt_float_t values[] = {2.0, 3.0, 4.0, 2.0}; - -// Variable types -char var_types[] = {CUOPT_CONTINUOUS, CUOPT_INTEGER}; - -cuOptCreateRangedProblem( - num_constraints, num_variables, CUOPT_MINIMIZE, - 0.0, // objective offset - objective_coefficients, - row_offsets, col_indices, values, - constraint_lower, constraint_upper, - var_lower, var_upper, - var_types, - &problem -); - -cuOptSolve(problem, settings, &solution); -cuOptGetObjectiveValue(solution, &obj_value); -``` - -## Quick Reference: CLI - -```bash -# Solve LP from MPS file -cuopt_cli problem.mps - -# With options -cuopt_cli problem.mps --time-limit 120 --mip-relative-tolerance 0.01 -``` - -## Common Modeling Patterns - -### Binary Selection -```python -# Select exactly k items from n -items = [problem.addVariable(lb=0, ub=1, vtype=INTEGER) for _ in range(n)] -problem.addConstraint(sum(items) == k) -``` - -### Big-M Linking -```python -# If y=1, then x <= 100; if y=0, x can be anything up to M -M = 10000 -problem.addConstraint(x <= 100 + M*(1 - y)) -``` - -### Piecewise Linear (SOS2) -```python -# Approximate nonlinear function with breakpoints -# Use lambda variables that sum to 1, at most 2 adjacent non-zero -``` - -## Solver Settings - -```python -settings = SolverSettings() - -# Time limit -settings.set_parameter("time_limit", 60) - -# MILP gap tolerance (stop when within X% of optimal) -settings.set_parameter("mip_relative_gap", 0.01) - -# Logging -settings.set_parameter("log_to_console", 1) -``` - -## Common Issues - -| Problem | Likely Cause | Fix | -|---------|--------------|-----| -| Status never "OPTIMAL" | Using wrong case | Use `"Optimal"` not `"OPTIMAL"` | -| Integer var has fractional value | Defined as CONTINUOUS | Use `vtype=INTEGER` | -| Infeasible | Conflicting constraints | Check constraint logic | -| Unbounded | Missing bounds | Add variable bounds | -| Slow solve | Large problem | Set time limit, increase gap tolerance | - -## Getting Dual Values (LP only) - -```python -if problem.Status.name == "Optimal": - constraint = problem.getConstraint("resource_a") - shadow_price = constraint.DualValue - print(f"Shadow price: {shadow_price}") -``` - -## Examples - -See `resources/` for complete examples: -- [Python API](resources/python_examples.md) — LP, MILP, knapsack, transportation -- [C API](resources/c_api_examples.md) — LP/MILP with build instructions -- [CLI](resources/cli_examples.md) — MPS file format and commands -- [REST Server](resources/server_examples.md) — curl and Python requests - -## When to Escalate - -Switch to **cuopt-qp** if: -- Objective has quadratic terms (x², x*y) - -Switch to **cuopt-debugging** if: -- Infeasible and can't determine why -- Numerical issues - -Switch to **cuopt-developer** if: -- User wants to modify solver internals diff --git a/.github/skills/cuopt-lp-milp/resources/cli_examples.md b/.github/skills/cuopt-lp-milp/resources/cli_examples.md deleted file mode 100644 index fe1d286780..0000000000 --- a/.github/skills/cuopt-lp-milp/resources/cli_examples.md +++ /dev/null @@ -1,166 +0,0 @@ -# LP/MILP: CLI Examples - -## LP from MPS File - -```bash -# Create sample LP in MPS format -cat > production.mps << 'EOF' -* Production Planning: maximize 40*chairs + 30*tables -* s.t. 2*chairs + 3*tables <= 240 (wood) -* 4*chairs + 2*tables <= 200 (labor) -NAME PRODUCTION -ROWS - N PROFIT - L WOOD - L LABOR -COLUMNS - CHAIRS PROFIT -40.0 - CHAIRS WOOD 2.0 - CHAIRS LABOR 4.0 - TABLES PROFIT -30.0 - TABLES WOOD 3.0 - TABLES LABOR 2.0 -RHS - RHS1 WOOD 240.0 - RHS1 LABOR 200.0 -ENDATA -EOF - -# Solve -cuopt_cli production.mps - -# With time limit -cuopt_cli production.mps --time-limit 30 -``` - -## MILP from MPS File - -```bash -# Create MILP with integer variables -cat > facility.mps << 'EOF' -* Facility location with binary variables -NAME FACILITY -ROWS - N COST - G DEMAND1 - L CAP1 - L CAP2 -COLUMNS - MARKER 'MARKER' 'INTORG' - OPEN1 COST 100.0 - OPEN1 CAP1 50.0 - OPEN2 COST 150.0 - OPEN2 CAP2 70.0 - MARKER 'MARKER' 'INTEND' - SHIP11 COST 5.0 - SHIP11 DEMAND1 1.0 - SHIP11 CAP1 -1.0 - SHIP21 COST 7.0 - SHIP21 DEMAND1 1.0 - SHIP21 CAP2 -1.0 -RHS - RHS1 DEMAND1 30.0 -BOUNDS - BV BND1 OPEN1 - BV BND1 OPEN2 - LO BND1 SHIP11 0.0 - LO BND1 SHIP21 0.0 -ENDATA -EOF - -# Solve MILP -cuopt_cli facility.mps --time-limit 60 --mip-relative-tolerance 0.01 -``` - -## Common CLI Options - -```bash -# Show all options -cuopt_cli --help - -# Time limit (seconds) -cuopt_cli problem.mps --time-limit 120 - -# MIP gap tolerance (stop when within X% of optimal) -cuopt_cli problem.mps --mip-relative-tolerance 0.001 - -# MIP absolute tolerance -cuopt_cli problem.mps --mip-absolute-tolerance 0.0001 - -# Enable presolve -cuopt_cli problem.mps --presolve - -# Iteration limit -cuopt_cli problem.mps --iteration-limit 10000 - -# Solver method (0=auto, 1=pdlp, 2=dual_simplex, 3=barrier) -cuopt_cli problem.mps --method 1 -``` - -## MPS Format Reference - -### Required Sections (in order) - -``` -NAME problem_name -ROWS - N objective_row (N = free/objective) - L constraint1 (L = <=) - G constraint2 (G = >=) - E constraint3 (E = ==) -COLUMNS - var1 row1 coefficient - var1 row2 coefficient -RHS - rhs1 row1 value -ENDATA -``` - -### Optional: BOUNDS Section - -``` -BOUNDS - LO bnd1 var1 0.0 (lower bound) - UP bnd1 var1 100.0 (upper bound) - FX bnd1 var2 50.0 (fixed value) - FR bnd1 var3 (free variable) - BV bnd1 var4 (binary 0/1) - LI bnd1 var5 0 (integer, lower bound) - UI bnd1 var5 10 (integer, upper bound) -``` - -### Integer Markers - -``` -COLUMNS - MARKER 'MARKER' 'INTORG' - int_var1 OBJ 1.0 - int_var2 OBJ 2.0 - MARKER 'MARKER' 'INTEND' - cont_var OBJ 3.0 -``` - -## Troubleshooting - -**"Failed to parse MPS file"** -- Check for missing ENDATA -- Verify section order: NAME, ROWS, COLUMNS, RHS, [BOUNDS], ENDATA -- Check integer markers format - -**"Problem is infeasible"** -- Check constraint directions (L/G/E) -- Verify RHS values are consistent - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | Description | -|---------|------|-------------| -| Basic LP | `docs/cuopt/source/cuopt-cli/examples/lp/examples/basic_lp_example.sh` | Simple LP via CLI | -| Basic MILP | `docs/cuopt/source/cuopt-cli/examples/milp/examples/basic_milp_example.sh` | MILP with integers | -| Solver Parameters | `docs/cuopt/source/cuopt-cli/examples/lp/examples/solver_parameters_example.sh` | CLI options | - -These examples are tested by CI and represent canonical usage. diff --git a/.github/skills/cuopt-lp-milp/resources/python_examples.md b/.github/skills/cuopt-lp-milp/resources/python_examples.md deleted file mode 100644 index eaaee59d48..0000000000 --- a/.github/skills/cuopt-lp-milp/resources/python_examples.md +++ /dev/null @@ -1,257 +0,0 @@ -# LP/MILP: Python API Examples - -## Linear Programming (LP) - -```python -""" -Production Planning LP: - maximize 40*chairs + 30*tables (profit) - subject to 2*chairs + 3*tables <= 240 (wood constraint) - 4*chairs + 2*tables <= 200 (labor constraint) - chairs, tables >= 0 -""" -from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE -from cuopt.linear_programming.solver_settings import SolverSettings - -# Create problem -problem = Problem("ProductionPlanning") - -# Decision variables (continuous, non-negative) -chairs = problem.addVariable(lb=0, vtype=CONTINUOUS, name="chairs") -tables = problem.addVariable(lb=0, vtype=CONTINUOUS, name="tables") - -# Constraints -problem.addConstraint(2 * chairs + 3 * tables <= 240, name="wood") -problem.addConstraint(4 * chairs + 2 * tables <= 200, name="labor") - -# Objective: maximize profit -problem.setObjective(40 * chairs + 30 * tables, sense=MAXIMIZE) - -# Solver settings -settings = SolverSettings() -settings.set_parameter("time_limit", 60) -settings.set_parameter("log_to_console", 1) - -# Solve -problem.solve(settings) - -# Check status and extract results -status = problem.Status.name -print(f"Status: {status}") - -if status in ["Optimal", "PrimalFeasible"]: - print(f"Optimal profit: ${problem.ObjValue:.2f}") - print(f"Chairs to produce: {chairs.getValue():.1f}") - print(f"Tables to produce: {tables.getValue():.1f}") - - # Get dual values (shadow prices) - wood_constraint = problem.getConstraint("wood") - labor_constraint = problem.getConstraint("labor") - print(f"\nShadow price (wood): ${wood_constraint.DualValue:.2f} per unit") - print(f"Shadow price (labor): ${labor_constraint.DualValue:.2f} per unit") -else: - print(f"No optimal solution found. Status: {status}") -``` - -## Mixed-Integer Linear Programming (MILP) - -```python -""" -Facility Location MILP: -- Decide which warehouses to open (binary) -- Assign customers to open warehouses -- Minimize fixed costs + transportation costs -""" -from cuopt.linear_programming.problem import ( - Problem, CONTINUOUS, INTEGER, MINIMIZE -) -from cuopt.linear_programming.solver_settings import SolverSettings - -# Problem data -warehouses = ["W1", "W2", "W3"] -customers = ["C1", "C2", "C3", "C4"] - -fixed_costs = {"W1": 100, "W2": 150, "W3": 120} -capacities = {"W1": 50, "W2": 70, "W3": 60} -demands = {"C1": 20, "C2": 25, "C3": 15, "C4": 30} - -transport_cost = { - ("W1", "C1"): 5, ("W1", "C2"): 8, ("W1", "C3"): 6, ("W1", "C4"): 10, - ("W2", "C1"): 7, ("W2", "C2"): 4, ("W2", "C3"): 9, ("W2", "C4"): 5, - ("W3", "C1"): 6, ("W3", "C2"): 7, ("W3", "C3"): 4, ("W3", "C4"): 8, -} - -# Create problem -problem = Problem("FacilityLocation") - -# Binary variables: y[w] = 1 if warehouse w is open -y = {w: problem.addVariable(lb=0, ub=1, vtype=INTEGER, name=f"open_{w}") - for w in warehouses} - -# Continuous variables: x[w,c] = units shipped from w to c -x = {(w, c): problem.addVariable(lb=0, vtype=CONTINUOUS, name=f"ship_{w}_{c}") - for w in warehouses for c in customers} - -# Objective: minimize fixed + transportation costs -problem.setObjective( - sum(fixed_costs[w] * y[w] for w in warehouses) + - sum(transport_cost[w, c] * x[w, c] for w in warehouses for c in customers), - sense=MINIMIZE -) - -# Constraints: meet customer demand -for c in customers: - problem.addConstraint( - sum(x[w, c] for w in warehouses) == demands[c], - name=f"demand_{c}" - ) - -# Constraints: respect warehouse capacity (only if open) -for w in warehouses: - problem.addConstraint( - sum(x[w, c] for c in customers) <= capacities[w] * y[w], - name=f"capacity_{w}" - ) - -# Solver settings -settings = SolverSettings() -settings.set_parameter("time_limit", 120) -settings.set_parameter("mip_relative_gap", 0.01) - -# Solve -problem.solve(settings) - -# Results -status = problem.Status.name -print(f"Status: {status}") - -if status in ["Optimal", "FeasibleFound"]: - print(f"Total cost: ${problem.ObjValue:.2f}") - print("\nOpen warehouses:") - for w in warehouses: - if y[w].getValue() > 0.5: - print(f" {w} (fixed cost: ${fixed_costs[w]})") - - print("\nShipments:") - for w in warehouses: - for c in customers: - shipped = x[w, c].getValue() - if shipped > 0.01: - print(f" {w} -> {c}: {shipped:.1f} units") -``` - -## Knapsack Problem (MILP) - -```python -""" -0/1 Knapsack: select items to maximize value within weight limit -""" -from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE -from cuopt.linear_programming.solver_settings import SolverSettings - -items = ["laptop", "camera", "phone", "tablet", "headphones"] -values = [1000, 500, 300, 600, 150] -weights = [3, 1, 0.5, 1.5, 0.3] -max_weight = 5 - -problem = Problem("Knapsack") - -# Binary variables: x[i] = 1 if item i is selected -x = [problem.addVariable(lb=0, ub=1, vtype=INTEGER, name=items[i]) - for i in range(len(items))] - -# Objective: maximize total value -problem.setObjective(sum(values[i] * x[i] for i in range(len(items))), sense=MAXIMIZE) - -# Constraint: weight limit -problem.addConstraint(sum(weights[i] * x[i] for i in range(len(items))) <= max_weight) - -problem.solve(SolverSettings()) - -if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(f"Total value: ${problem.ObjValue:.0f}") - print("Selected items:") - for i, item in enumerate(items): - if x[i].getValue() > 0.5: - print(f" {item}: value=${values[i]}, weight={weights[i]}") -``` - -## Transportation Problem (LP) - -```python -""" -Minimize shipping cost from suppliers to customers -""" -from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE - -suppliers = ["S1", "S2"] -customers = ["C1", "C2", "C3"] -supply = {"S1": 100, "S2": 150} -demand = {"C1": 80, "C2": 70, "C3": 100} -cost = { - ("S1", "C1"): 4, ("S1", "C2"): 6, ("S1", "C3"): 8, - ("S2", "C1"): 5, ("S2", "C2"): 3, ("S2", "C3"): 7, -} - -problem = Problem("Transportation") - -x = {(s, c): problem.addVariable(lb=0, vtype=CONTINUOUS, name=f"x_{s}_{c}") - for s in suppliers for c in customers} - -# Minimize total shipping cost -problem.setObjective(sum(cost[s,c] * x[s,c] for s in suppliers for c in customers), - sense=MINIMIZE) - -# Supply constraints -for s in suppliers: - problem.addConstraint(sum(x[s,c] for c in customers) <= supply[s]) - -# Demand constraints -for c in customers: - problem.addConstraint(sum(x[s,c] for s in suppliers) >= demand[c]) - -problem.solve() - -if problem.Status.name in ("Optimal", "PrimalFeasible"): - print(f"Total cost: ${problem.ObjValue:.2f}") - for s in suppliers: - for c in customers: - val = x[s,c].getValue() - if val > 0.01: - print(f" {s} -> {c}: {val:.0f} units") -``` - -## Status Checking (Critical) - -```python -# ✅ CORRECT - use PascalCase -if problem.Status.name in ["Optimal", "FeasibleFound"]: - print(problem.ObjValue) - -# ❌ WRONG - will silently fail! -if problem.Status.name == "OPTIMAL": # Never matches! - print(problem.ObjValue) - -# LP status values: Optimal, PrimalFeasible, PrimalInfeasible, -# DualInfeasible, TimeLimit, NumericalError -# MILP status values: Optimal, FeasibleFound, Infeasible, -# Unbounded, TimeLimit, NoTermination -``` - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | Description | -|---------|------|-------------| -| Simple LP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/simple_lp_example.py` | Basic LP setup | -| Simple MILP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/simple_milp_example.py` | Integer variables | -| Production Planning | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/production_planning_example.py` | Real-world LP | -| Expressions | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/expressions_constraints_example.py` | Advanced constraint syntax | -| Incumbent Solutions | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/incumbent_solutions_example.py` | Tracking MIP progress | -| Warmstart | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/pdlp_warmstart_example.py` | Warm starting LP | -| Solution Handling | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/solution_example.py` | Working with results | - -These examples are tested by CI (`ci/test_doc_examples.sh`) and represent canonical usage. diff --git a/.github/skills/cuopt-lp-milp/resources/server_examples.md b/.github/skills/cuopt-lp-milp/resources/server_examples.md deleted file mode 100644 index 521d8a6ead..0000000000 --- a/.github/skills/cuopt-lp-milp/resources/server_examples.md +++ /dev/null @@ -1,208 +0,0 @@ -# LP/MILP: REST Server Examples - -## LP Request (curl) - -```bash -# Production Planning LP via REST -# maximize 40*chairs + 30*tables -# s.t. 2*chairs + 3*tables <= 240 -# 4*chairs + 2*tables <= 200 - -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": true, - "solver_config": { - "tolerances": {"optimality": 0.0001}, - "time_limit": 60 - } - }' | jq -r '.reqId') - -echo "Request ID: $REQID" - -# Get solution -sleep 2 -curl -s "http://localhost:8000/cuopt/solution/$REQID" \ - -H "CLIENT-VERSION: custom" | jq . -``` - -## MILP Request (curl) - -```bash -# Add integer variable types -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0] - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "variable_types": ["integer", "continuous"], - "maximize": true, - "solver_config": { - "time_limit": 120, - "tolerances": { - "mip_relative_gap": 0.01 - } - } - }' | jq -r '.reqId') - -echo "Request ID: $REQID" - -# Poll for solution (MILP may take longer than LP) -while true; do - RESULT=$(curl -s "http://localhost:8000/cuopt/solution/$REQID" \ - -H "CLIENT-VERSION: custom") - STATUS=$(echo "$RESULT" | jq -r '.response.status // empty') - if [ -n "$STATUS" ]; then - echo "$RESULT" | jq . - break - fi - sleep 2 -done -``` - -## LP Request (Python) - -```python -import requests -import time - -SERVER = "http://localhost:8000" -HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} - -payload = { - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": True, - "solver_config": { - "time_limit": 60 - } -} - -# Submit -response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) -req_id = response.json()["reqId"] -print(f"Submitted: {req_id}") - -# Poll for solution -for _ in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) - result = response.json() - - if "response" in result: - print(f"Status: {result['response'].get('status')}") - print(f"Objective: {result['response'].get('objective_value')}") - print(f"Solution: {result['response'].get('primal_solution')}") - break - time.sleep(1) -``` - -## CSR Matrix Format - -The constraint matrix uses Compressed Sparse Row (CSR) format: - -``` -Matrix: [2, 3] (row 0: 2*x0 + 3*x1) - [4, 2] (row 1: 4*x0 + 2*x1) - -CSR format: - offsets: [0, 2, 4] # Row pointers - indices: [0, 1, 0, 1] # Column indices - values: [2.0, 3.0, 4.0, 2.0] # Non-zero values -``` - -## Special Values - -```json -{ - "constraint_bounds": { - "lower_bounds": ["ninf", "ninf"], - "upper_bounds": [100.0, "inf"] - } -} -``` - -- `"ninf"` — negative infinity (-∞) -- `"inf"` — positive infinity (+∞) - -## Variable Types - -```json -{ - "variable_types": ["continuous", "integer", "binary"] -} -``` - -- `"continuous"` - real-valued -- `"integer"` - integer-valued -- `"binary"` - 0 or 1 only - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | Description | -|---------|------|-------------| -| Basic LP (Python) | `docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.py` | LP via REST | -| Basic LP (curl) | `docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.sh` | LP shell script | -| MPS Input | `docs/cuopt/source/cuopt-server/examples/lp/examples/mps_file_example.py` | MPS file format | -| MPS DataModel | `docs/cuopt/source/cuopt-server/examples/lp/examples/mps_datamodel_example.py` | MPS in payload | -| Warmstart | `docs/cuopt/source/cuopt-server/examples/lp/examples/warmstart_example.py` | Warm starting | -| Basic MILP (Python) | `docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.py` | MILP via REST | -| Basic MILP (curl) | `docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.sh` | MILP shell script | -| Incumbent Callback | `docs/cuopt/source/cuopt-server/examples/milp/examples/incumbent_callback_example.py` | MIP progress tracking | -| Abort Job | `docs/cuopt/source/cuopt-server/examples/milp/examples/abort_job_example.py` | Canceling requests | -| Batch Mode | `docs/cuopt/source/cuopt-server/examples/lp/examples/batch_mode_example.sh` | Multiple problems | - -These examples are tested by CI (`ci/test_doc_examples.sh`) and represent canonical usage. diff --git a/.github/skills/cuopt-qp/SKILL.md b/.github/skills/cuopt-qp/SKILL.md deleted file mode 100644 index 5fd7d83af6..0000000000 --- a/.github/skills/cuopt-qp/SKILL.md +++ /dev/null @@ -1,195 +0,0 @@ ---- -name: cuopt-qp -description: Solve Quadratic Programming (QP) with NVIDIA cuOpt. Use when the user asks about quadratic objectives, portfolio optimization, variance minimization, or least squares problems. Note that QP support is currently in beta. ---- - -# cuOpt QP Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Model and solve quadratic programs using NVIDIA cuOpt. **QP support is currently in beta.** - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **Is this actually QP?** - - Does the objective have x² or x*y terms? - - Are constraints still linear? - - (If constraints are also quadratic, cuOpt doesn't support that) - -2. **Minimize or maximize?** - - ⚠️ **QP only supports MINIMIZE** - - For maximization, negate the objective - -3. **Is the quadratic form convex?** - - Q matrix should be positive semi-definite for minimization - - Non-convex QP may not solve correctly - -## Critical Constraints - -### ⚠️ MINIMIZE ONLY - -**QP objectives MUST be minimization.** The solver rejects maximize for QP. - -```python -# ❌ WRONG - will fail -problem.setObjective(x*x + y*y, sense=MAXIMIZE) - -# ✅ CORRECT - minimize instead -# To maximize f(x), minimize -f(x) -problem.setObjective(-(x*x + y*y), sense=MINIMIZE) -``` - -### Interface Support - -| Interface | QP Support | -|-----------|:----------:| -| Python | ✓ (beta) | -| C API | ✓ | -| REST | ✗ | -| CLI | ✗ | - -## Quick Reference: Python API - -### Portfolio Optimization Example - -```python -""" -Minimize portfolio variance (risk): - minimize x^T * Q * x - subject to sum(x) = 1 (fully invested) - r^T * x >= target (minimum return) - x >= 0 (no short selling) -""" -from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE -from cuopt.linear_programming.solver_settings import SolverSettings - -problem = Problem("Portfolio") - -# Portfolio weights (decision variables) -x1 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_a") -x2 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_b") -x3 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_c") - -# Expected returns -r1, r2, r3 = 0.12, 0.08, 0.05 - -# Quadratic objective: variance = x^T * Q * x -# Q = [[0.04, 0.01, 0.005], -# [0.01, 0.02, 0.008], -# [0.005, 0.008, 0.01]] -# Expanded: 0.04*x1² + 0.02*x2² + 0.01*x3² + 2*0.01*x1*x2 + ... -problem.setObjective( - 0.04*x1*x1 + 0.02*x2*x2 + 0.01*x3*x3 + - 0.02*x1*x2 + 0.01*x1*x3 + 0.016*x2*x3, - sense=MINIMIZE # MUST be minimize for QP! -) - -# Linear constraints -problem.addConstraint(x1 + x2 + x3 == 1, name="budget") -problem.addConstraint(r1*x1 + r2*x2 + r3*x3 >= 0.08, name="min_return") - -# Solve -settings = SolverSettings() -settings.set_parameter("time_limit", 60) -problem.solve(settings) - -# Check results -if problem.Status.name in ["Optimal", "PrimalFeasible"]: - print(f"Variance: {problem.ObjValue:.6f}") - print(f"Std Dev: {problem.ObjValue**0.5:.4f}") - print(f"Allocation: A={x1.getValue():.2%}, B={x2.getValue():.2%}, C={x3.getValue():.2%}") -``` - -### Least Squares Example - -```python -""" -Minimize ||Ax - b||² = x^T*A^T*A*x - 2*b^T*A*x + b^T*b -""" -problem = Problem("LeastSquares") - -x = problem.addVariable(lb=-100, ub=100, vtype=CONTINUOUS, name="x") -y = problem.addVariable(lb=-100, ub=100, vtype=CONTINUOUS, name="y") - -# Quadratic objective: (x-3)² + (y-4)² = x² + y² - 6x - 8y + 25 -problem.setObjective( - x*x + y*y - 6*x - 8*y + 25, - sense=MINIMIZE -) - -problem.solve(SolverSettings()) - -print(f"x = {x.getValue()}") # Should be ~3 -print(f"y = {y.getValue()}") # Should be ~4 -``` - -## Formulating Quadratic Objectives - -### From Covariance Matrix - -Given covariance matrix Q and weights x: -``` -variance = x^T * Q * x = Σᵢ Σⱼ Qᵢⱼ * xᵢ * xⱼ -``` - -Expand manually: -```python -# Q = [[a, b], [b, c]] -# x^T * Q * x = a*x1² + 2b*x1*x2 + c*x2² -objective = a*x1*x1 + 2*b*x1*x2 + c*x2*x2 -``` - -### Maximization Workaround - -To maximize `f(x) = -x² + 4x`: -```python -# maximize -x² + 4x -# = minimize -(-x² + 4x) -# = minimize x² - 4x -problem.setObjective(x*x - 4*x, sense=MINIMIZE) -# Then negate the objective value for the true maximum -true_max = -problem.ObjValue -``` - -## Status Checking - -Same as LP/MILP - use PascalCase: - -```python -if problem.Status.name in ["Optimal", "PrimalFeasible"]: - print(f"Optimal variance: {problem.ObjValue}") -``` - -## Common Issues - -| Problem | Likely Cause | Fix | -|---------|--------------|-----| -| "Quadratic problems must be minimized" | Using MAXIMIZE | Use MINIMIZE, negate objective | -| Poor convergence | Non-convex Q | Ensure Q is positive semi-definite | -| NumericalError | Ill-conditioned Q | Scale variables, regularize | -| Slow solve | Large dense Q | Check if problem can be simplified | - -## Solver Notes - -- QP uses **Barrier method** internally (different from LP/MILP defaults) -- May be more sensitive to numerical issues than LP -- Beta status means API may change in future versions - -## Examples - -See `resources/` for complete examples: -- [Python API](resources/python_examples.md) — portfolio, least squares, maximization workaround - -## When to Escalate - -Switch to **cuopt-lp-milp** if: -- Objective is actually linear (no x² or x*y terms) - -Switch to **cuopt-debugging** if: -- Numerical errors -- Unexpected results - -Switch to **cuopt-developer** if: -- Need features not in beta QP diff --git a/.github/skills/cuopt-routing/SKILL.md b/.github/skills/cuopt-routing/SKILL.md deleted file mode 100644 index e634da7dba..0000000000 --- a/.github/skills/cuopt-routing/SKILL.md +++ /dev/null @@ -1,298 +0,0 @@ ---- -name: cuopt-routing -description: Solve vehicle routing problems (VRP, TSP, PDP) with NVIDIA cuOpt. Use when the user asks about delivery optimization, fleet routing, time windows, capacities, pickup-delivery pairs, or traveling salesman problems. ---- - -# cuOpt Routing Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Model and solve vehicle routing problems using NVIDIA cuOpt's GPU-accelerated solver. - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **Problem type?** - - TSP (single vehicle, visit all locations) - - VRP (multiple vehicles, capacity constraints) - - PDP (pickup and delivery pairs) - -2. **What constraints?** - - Time windows (earliest/latest arrival)? - - Vehicle capacities? - - Service times at locations? - - Multiple depots? - - Vehicle-specific start/end locations? - -3. **What data do you have?** - - Cost/distance matrix or coordinates? - - Demand per location? - - Fleet size fixed or to optimize? - -4. **Interface preference?** - - Python API (in-process) - - REST Server (production/async) - -## Interface Support - -| Interface | Routing Support | -|-----------|:---------------:| -| Python | ✓ | -| REST | ✓ | -| C API | ✗ | -| CLI | ✗ | - -## Quick Reference: Python API - -### Minimal VRP Example - -```python -import cudf -from cuopt import routing - -# Cost matrix (n_locations x n_locations) -cost_matrix = cudf.DataFrame([ - [0, 10, 15, 20], - [10, 0, 12, 18], - [15, 12, 0, 10], - [20, 18, 10, 0], -], dtype="float32") - -# Build data model -dm = routing.DataModel( - n_locations=4, # Total locations including depot - n_fleet=2, # Number of vehicles - n_orders=3 # Orders to fulfill (locations 1,2,3) -) - -# Required: cost matrix -dm.add_cost_matrix(cost_matrix) - -# Required: order locations (which location each order is at) -dm.set_order_locations(cudf.Series([1, 2, 3])) - -# Solve -solution = routing.Solve(dm, routing.SolverSettings()) - -# Check result -if solution.get_status() == 0: # SUCCESS - solution.display_routes() -``` - -### Adding Constraints - -```python -# Time windows (need transit time matrix) -dm.add_transit_time_matrix(transit_time_matrix) -dm.set_order_time_windows( - cudf.Series([0, 10, 20]), # earliest - cudf.Series([50, 60, 70]) # latest -) - -# Capacities -dm.add_capacity_dimension( - "weight", - cudf.Series([20, 30, 25]), # demand per order - cudf.Series([100, 100]) # capacity per vehicle -) - -# Service times -dm.set_order_service_times(cudf.Series([5, 5, 5])) - -# Vehicle locations (start/end) -dm.set_vehicle_locations( - cudf.Series([0, 0]), # start at depot - cudf.Series([0, 0]) # return to depot -) - -# Vehicle time windows -dm.set_vehicle_time_windows( - cudf.Series([0, 0]), # earliest start - cudf.Series([200, 200]) # latest return -) -``` - -### Pickup and Delivery (PDP) - -```python -# Demand: positive=pickup, negative=delivery (must sum to 0 per pair) -demand = cudf.Series([10, -10, 15, -15]) - -# Pair indices: order 0 pairs with 1, order 2 pairs with 3 -dm.set_pickup_delivery_pairs( - cudf.Series([0, 2]), # pickup order indices - cudf.Series([1, 3]) # delivery order indices -) -``` - -### Precedence Constraints - -Use `add_order_precedence()` to require certain orders to be visited before others. - -**Important:** This is a per-node API — call it once for each order that has predecessors. - -```python -import numpy as np - -# Order 2 must come after orders 0 and 1 -dm.add_order_precedence( - node_id=2, # this order - preceding_nodes=np.array([0, 1]) # must come after these -) - -# Order 3 must come after order 2 -dm.add_order_precedence( - node_id=3, - preceding_nodes=np.array([2]) -) -``` - -**Rules:** -- Call once per order that has predecessors -- `preceding_nodes` is a numpy array of order indices -- Circular dependencies are NOT allowed (A before B before A) -- Orders without precedence constraints don't need a call - -**Example: Assembly sequence** -```python -# Task B requires Task A to be done first -# Task C requires Tasks A and B to be done first -dm.add_order_precedence(1, np.array([0])) # B after A -dm.add_order_precedence(2, np.array([0, 1])) # C after A and B -``` - -## Quick Reference: REST Server - -### Terminology Difference - -| Concept | Python API | REST Server | -|---------|------------|-------------| -| Jobs | `order_locations` | `task_locations` | -| Time windows | `set_order_time_windows()` | `task_time_windows` | -| Service times | `set_order_service_times()` | `service_times` | - -### Minimal REST Payload - -```json -{ - "cost_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "task_data": { - "task_locations": [1, 2] - }, - "fleet_data": { - "vehicle_locations": [[0, 0]], - "capacities": [[100]] - }, - "solver_config": { - "time_limit": 10 - } -} -``` - -## Solution Checking - -```python -status = solution.get_status() -# 0 = SUCCESS -# 1 = FAIL -# 2 = TIMEOUT -# 3 = EMPTY - -if status == 0: - solution.display_routes() - route_df = solution.get_route() - total_cost = solution.get_total_objective() -else: - print(f"Error: {solution.get_error_message()}") - infeasible = solution.get_infeasible_orders() - if len(infeasible) > 0: - print(f"Infeasible orders: {infeasible.to_list()}") -``` - -## Solution DataFrame Schema - -`solution.get_route()` returns a `cudf.DataFrame` with these columns: - -| Column | Type | Description | -|--------|------|-------------| -| `route` | int | Order/task index in the route sequence | -| `truck_id` | int | Vehicle ID assigned to this stop | -| `location` | int | Location index (0 = depot typically) | -| `arrival_stamp` | float | Arrival time at this location | - -**Example output:** -``` - route arrival_stamp truck_id location -0 0 0.0 1 0 # Vehicle 1 starts at depot -1 3 2.0 1 3 # Vehicle 1 visits location 3 -2 2 4.0 1 2 # Vehicle 1 visits location 2 -3 0 5.0 1 0 # Vehicle 1 returns to depot -4 0 0.0 0 0 # Vehicle 0 starts at depot -5 1 1.0 0 1 # Vehicle 0 visits location 1 -6 0 3.0 0 0 # Vehicle 0 returns to depot -``` - -**Working with results:** -```python -route_df = solution.get_route() - -# Routes per vehicle -for vid in route_df["truck_id"].unique().to_arrow().tolist(): - vehicle_route = route_df[route_df["truck_id"] == vid] - locations = vehicle_route["location"].to_arrow().tolist() - print(f"Vehicle {vid}: {locations}") - -# Total travel time -max_arrival = route_df["arrival_stamp"].max() -``` - -## Common Issues - -| Problem | Likely Cause | Fix | -|---------|--------------|-----| -| Empty solution | Time windows too tight | Widen windows or check travel times | -| Infeasible orders | Demand > capacity | Increase fleet or capacity | -| Status != 0 | Missing transit time matrix | Add `add_transit_time_matrix()` when using time windows | -| Wrong route cost | Matrix not symmetric | Check cost_matrix values | - -## Data Type Requirements - -```python -# Always use explicit dtypes -cost_matrix = cost_matrix.astype("float32") -order_locations = cudf.Series([...], dtype="int32") -demand = cudf.Series([...], dtype="int32") -vehicle_capacity = cudf.Series([...], dtype="int32") -time_windows = cudf.Series([...], dtype="int32") -``` - -## Solver Settings - -```python -ss = routing.SolverSettings() -ss.set_time_limit(30) # seconds -ss.set_verbose_mode(True) # enable progress output -ss.set_error_logging_mode(True) # log constraint errors if infeasible -``` - -## Examples - -See `resources/` for complete examples: -- [Python API](resources/python_examples.md) — VRP, PDP, multi-depot -- [REST Server](resources/server_examples.md) — curl and Python requests - -## When to Escalate - -Switch to **cuopt-debugging** if: -- Solution is infeasible and you can't determine why -- Performance is unexpectedly slow - -Switch to **cuopt-developer** if: -- User wants to modify solver behavior -- User wants to add new constraint types diff --git a/.github/skills/cuopt-server/SKILL.md b/.github/skills/cuopt-server/SKILL.md deleted file mode 100644 index e118a0ba43..0000000000 --- a/.github/skills/cuopt-server/SKILL.md +++ /dev/null @@ -1,356 +0,0 @@ ---- -name: cuopt-server -description: Deploy and integrate cuOpt REST server for production use. Use when the user asks about REST API, HTTP endpoints, deployment, curl requests, microservices, async solving, or server payloads. ---- - -# cuOpt Server Skill - -> **Prerequisites**: Read `cuopt-user-rules/SKILL.md` first for behavior rules. - -Deploy and use the cuOpt REST server for production optimization workloads. - -## Before You Start: Required Questions - -**Ask these if not already clear:** - -1. **Problem type?** - - Routing (VRP/TSP/PDP)? - - LP/MILP? - - (Note: QP not supported via REST) - -2. **Deployment target?** - - Local development? - - Docker/Kubernetes? - - Cloud service? - -3. **Client preference?** - - curl (quick testing) - - Python requests - - cuopt-sh-client library - -## Server Capabilities - -| Problem Type | REST Support | -|--------------|:------------:| -| Routing | ✓ | -| LP | ✓ | -| MILP | ✓ | -| QP | ✗ | - -## Starting the Server - -### Direct (Development) - -```bash -python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 -``` - -### Docker (Production) - -```bash -docker run --gpus all -d \ - -p 8000:8000 \ - -e CUOPT_SERVER_PORT=8000 \ - --name cuopt-server \ - nvidia/cuopt:latest-cuda12.9-py3.13 -``` - -### Verify Running - -```bash -curl http://localhost:8000/cuopt/health -# Expected: {"status": "healthy"} -``` - -## API Endpoints - -| Endpoint | Method | Purpose | -|----------|--------|---------| -| `/cuopt/health` | GET | Health check | -| `/cuopt/request` | POST | Submit optimization request | -| `/cuopt/solution/{reqId}` | GET | Get solution by request ID | -| `/cuopt.yaml` | GET | OpenAPI specification | -| `/cuopt/docs` | GET | Swagger UI | - -## Workflow - -1. **POST** problem to `/cuopt/request` → get `reqId` -2. **Poll** `/cuopt/solution/{reqId}` until solution ready -3. **Parse** response - -## Routing Request Example - -### curl - -```bash -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "cost_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "task_data": { - "task_locations": [1, 2], - "demand": [[10, 20]], - "task_time_windows": [[0, 100], [0, 100]], - "service_times": [5, 5] - }, - "fleet_data": { - "vehicle_locations": [[0, 0]], - "capacities": [[50]], - "vehicle_time_windows": [[0, 200]] - }, - "solver_config": {"time_limit": 5} - }' | jq -r '.reqId') - -echo "Request ID: $REQID" - -# Poll for solution -sleep 2 -curl -s "http://localhost:8000/cuopt/solution/$REQID" \ - -H "CLIENT-VERSION: custom" | jq . -``` - -### Python - -```python -import requests -import time - -SERVER = "http://localhost:8000" -HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} - -payload = { - "cost_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15],[10,0,12],[15,12,0]]} - }, - "task_data": { - "task_locations": [1, 2], - "demand": [[10, 20]], - "task_time_windows": [[0, 100], [0, 100]], # optional - "service_times": [5, 5] # optional - }, - "fleet_data": { - "vehicle_locations": [[0, 0]], - "capacities": [[50]], - "vehicle_time_windows": [[0, 200]] # optional - }, - "solver_config": {"time_limit": 5} -} - -# Submit -resp = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) -req_id = resp.json()["reqId"] - -# Poll -for _ in range(30): - resp = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) - result = resp.json() - if "response" in result: - print(result["response"]["solver_response"]) - break - time.sleep(1) -``` - -## LP/MILP Request Example - -```bash -curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": true, - "solver_config": {"time_limit": 60} - }' -``` - -## Terminology: REST vs Python API - -**CRITICAL:** REST API uses different terminology than Python API. - -| Concept | Python API | REST API | -|---------|------------|----------| -| Orders/Jobs | `order_locations` | `task_locations` | -| Time windows | `set_order_time_windows()` | `task_time_windows` | -| Service times | `set_order_service_times()` | `service_times` | -| Transit matrix | `add_transit_time_matrix()` | `travel_time_matrix_data` | - -## Common Payload Mistakes - -### Wrong field names - -```json -// ❌ WRONG -"transit_time_matrix_data": {...} - -// ✅ CORRECT -"travel_time_matrix_data": {...} -``` - -### Wrong capacity format - -```json -// ❌ WRONG - per vehicle -"capacities": [[50], [50]] - -// ✅ CORRECT - per dimension across all vehicles -"capacities": [[50, 50]] -``` - -### Missing required fields - -Routing requires at minimum: -- `cost_matrix_data` -- `task_data.task_locations` -- `fleet_data.vehicle_locations` -- `fleet_data.capacities` - -## Response Structure - -### Routing Success - -```json -{ - "reqId": "abc123", - "response": { - "solver_response": { - "status": 0, - "solution_cost": 45.0, - "vehicle_data": { - "0": {"route": [0, 1, 2, 0], "arrival_times": [...]} - } - } - } -} -``` - -### LP/MILP Success - -```json -{ - "reqId": "abc123", - "response": { - "status": "Optimal", - "objective_value": 1600.0, - "primal_solution": [30.0, 60.0] - } -} -``` - -## Error Handling - -### 422 Validation Error - -Check the error message for field issues: -```bash -curl ... | jq '.error' -``` - -Compare against OpenAPI spec at `/cuopt.yaml` - -### 500 Server Error - -- Check server logs -- Capture `reqId` for debugging -- Try with smaller problem - -### Polling Returns Empty - -- Solution still computing - keep polling -- Check `solver_config.time_limit` - -## Server Configuration - -### Environment Variables - -```bash -CUOPT_SERVER_PORT=8000 -CUOPT_SERVER_HOST=0.0.0.0 -``` - -### Command Line Options - -```bash -python -m cuopt_server.cuopt_service \ - --ip 0.0.0.0 \ - --port 8000 \ - --workers 4 -``` - -## Production Considerations - -### Health Checks - -```bash -# Kubernetes liveness probe -curl -f http://localhost:8000/cuopt/health - -# Readiness check -curl -f http://localhost:8000/cuopt/health -``` - -### Resource Limits - -```yaml -# Kubernetes example -resources: - limits: - nvidia.com/gpu: 1 - memory: "32Gi" - requests: - memory: "16Gi" -``` - -### Scaling - -- GPU is the bottleneck - one server per GPU -- Use load balancer for multiple GPUs -- Queue requests to avoid overwhelming - -## OpenAPI Specification - -Full API spec available at: -- Runtime: `http://localhost:8000/cuopt.yaml` -- Source: `docs/cuopt/source/cuopt_spec.yaml` -- Swagger UI: `http://localhost:8000/cuopt/docs` - -## Examples - -See `resources/` for complete examples: -- [Routing examples](resources/routing_examples.md) — VRP, PDP via REST -- [LP/MILP examples](resources/lp_milp_examples.md) — Linear programming via REST - -## When to Escalate - -Switch to **cuopt-debugging** if: -- Consistent 5xx errors -- Unexpected solution results - -Switch to **cuopt-developer** if: -- Need to modify server behavior -- Need new endpoints diff --git a/.github/skills/cuopt-server/resources/lp_milp_examples.md b/.github/skills/cuopt-server/resources/lp_milp_examples.md deleted file mode 100644 index 107bb2490b..0000000000 --- a/.github/skills/cuopt-server/resources/lp_milp_examples.md +++ /dev/null @@ -1,176 +0,0 @@ -# Server: LP/MILP Examples - -## LP Request (curl) - -```bash -# maximize 40*x + 30*y -# s.t. 2x + 3y <= 240 -# 4x + 2y <= 200 - -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0], - "scalability_factor": 1.0, - "offset": 0.0 - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": true, - "solver_config": { - "time_limit": 60 - } - }' | jq -r '.reqId') - -sleep 2 -curl -s "http://localhost:8000/cuopt/solution/$REQID" -H "CLIENT-VERSION: custom" | jq . -``` - -## MILP Request (curl) - -```bash -# Submit MILP request and capture reqId -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0] - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "variable_types": ["integer", "continuous"], - "maximize": true, - "solver_config": { - "time_limit": 120, - "tolerances": { - "mip_relative_gap": 0.01 - } - } - }' | jq -r '.reqId') -# Note: objective_data also supports optional "scalability_factor" and "offset" fields - -# Poll for solution (MILP may take longer than LP) -sleep 3 -curl -s "http://localhost:8000/cuopt/solution/$REQID" -H "CLIENT-VERSION: custom" | jq . -``` - -## LP Request (Python) - -```python -import requests -import time - -SERVER = "http://localhost:8000" -HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} - -payload = { - "csr_constraint_matrix": { - "offsets": [0, 2, 4], - "indices": [0, 1, 0, 1], - "values": [2.0, 3.0, 4.0, 2.0] - }, - "constraint_bounds": { - "upper_bounds": [240.0, 200.0], - "lower_bounds": ["ninf", "ninf"] - }, - "objective_data": { - "coefficients": [40.0, 30.0] - }, - "variable_bounds": { - "upper_bounds": ["inf", "inf"], - "lower_bounds": [0.0, 0.0] - }, - "maximize": True, - "solver_config": { - "time_limit": 60 - } -} - -# Submit -response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) -req_id = response.json()["reqId"] -print(f"Submitted: {req_id}") - -# Poll for solution -for _ in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) - result = response.json() - - if "response" in result: - print(f"Status: {result['response'].get('status')}") - print(f"Objective: {result['response'].get('objective_value')}") - print(f"Solution: {result['response'].get('primal_solution')}") - break - time.sleep(1) -``` - -## CSR Matrix Format - -``` -Matrix: [2, 3] (row 0: 2*x0 + 3*x1) - [4, 2] (row 1: 4*x0 + 2*x1) - -CSR format: - offsets: [0, 2, 4] # Row pointers (n_rows + 1) - indices: [0, 1, 0, 1] # Column indices - values: [2.0, 3.0, 4.0, 2.0] # Non-zero values -``` - -## Special Values - -```json -{ - "constraint_bounds": { - "lower_bounds": ["ninf", "ninf"], - "upper_bounds": [100.0, "inf"] - } -} -``` - -## Variable Types - -- `"continuous"` - real-valued -- `"integer"` - integer-valued -- `"binary"` - 0 or 1 only - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | -|---------|------| -| Basic LP (Python) | `docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.py` | -| Basic LP (curl) | `docs/cuopt/source/cuopt-server/examples/lp/examples/basic_lp_example.sh` | -| MPS File Input | `docs/cuopt/source/cuopt-server/examples/lp/examples/mps_file_example.py` | -| Warmstart | `docs/cuopt/source/cuopt-server/examples/lp/examples/warmstart_example.py` | -| Basic MILP | `docs/cuopt/source/cuopt-server/examples/milp/examples/basic_milp_example.py` | -| Incumbent Callback | `docs/cuopt/source/cuopt-server/examples/milp/examples/incumbent_callback_example.py` | - -These examples are tested by CI (`ci/test_doc_examples.sh`). diff --git a/.github/skills/cuopt-server/resources/routing_examples.md b/.github/skills/cuopt-server/resources/routing_examples.md deleted file mode 100644 index 9caf7e67dd..0000000000 --- a/.github/skills/cuopt-server/resources/routing_examples.md +++ /dev/null @@ -1,160 +0,0 @@ -# Server: Routing Examples - -## Start Server - -```bash -python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 & -sleep 5 -curl http://localhost:8000/cuopt/health -``` - -> **Note:** Using `--ip 0.0.0.0` binds to all interfaces for development convenience; use `--ip 127.0.0.1` or a specific interface in production or untrusted networks. - -## Basic VRP (curl) - -```bash -REQID=$(curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "cost_matrix_data": { - "data": {"0": [[0,10,15,20],[10,0,12,18],[15,12,0,10],[20,18,10,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15,20],[10,0,12,18],[15,12,0,10],[20,18,10,0]]} - }, - "task_data": { - "task_locations": [1, 2, 3], - "demand": [[10, 15, 20]], - "service_times": [5, 5, 5] - }, - "fleet_data": { - "vehicle_locations": [[0, 0], [0, 0]], - "capacities": [[50, 50]] - }, - "solver_config": {"time_limit": 5} - }' | jq -r '.reqId') - -curl -s "http://localhost:8000/cuopt/solution/$REQID" -H "CLIENT-VERSION: custom" | jq . -``` - -## VRP with Time Windows (Python) - -```python -import requests -import time - -SERVER = "http://localhost:8000" -HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} - -payload = { - "cost_matrix_data": { - "data": {"0": [[0,10,15,20,25],[10,0,12,18,22],[15,12,0,10,15],[20,18,10,0,8],[25,22,15,8,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,15,20,25],[10,0,12,18,22],[15,12,0,10,15],[20,18,10,0,8],[25,22,15,8,0]]} - }, - "task_data": { - "task_locations": [1, 2, 3, 4], - "demand": [[20, 30, 25, 15]], - "task_time_windows": [[0, 50], [10, 60], [20, 70], [0, 80]], - "service_times": [5, 5, 5, 5] - }, - "fleet_data": { - "vehicle_locations": [[0, 0], [0, 0]], - "capacities": [[100, 100]], - "vehicle_time_windows": [[0, 200], [0, 200]] - }, - "solver_config": { - "time_limit": 10 - } -} - -# Submit -response = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) -req_id = response.json()["reqId"] -print(f"Submitted: {req_id}") - -# Poll for solution -for attempt in range(30): - response = requests.get(f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS) - result = response.json() - - if "response" in result: - solver_response = result["response"].get("solver_response", {}) - print(f"Status: {solver_response.get('status')}") - print(f"Cost: {solver_response.get('solution_cost')}") - if "vehicle_data" in solver_response: - for vid, vdata in solver_response["vehicle_data"].items(): - print(f"Vehicle {vid}: {vdata.get('route', [])}") - break - time.sleep(1) -``` - -## Pickup and Delivery (curl) - -```bash -curl -s -X POST "http://localhost:8000/cuopt/request" \ - -H "Content-Type: application/json" \ - -H "CLIENT-VERSION: custom" \ - -d '{ - "cost_matrix_data": { - "data": {"0": [[0,10,20,30,40],[10,0,15,25,35],[20,15,0,10,20],[30,25,10,0,15],[40,35,20,15,0]]} - }, - "travel_time_matrix_data": { - "data": {"0": [[0,10,20,30,40],[10,0,15,25,35],[20,15,0,10,20],[30,25,10,0,15],[40,35,20,15,0]]} - }, - "task_data": { - "task_locations": [1, 2, 3, 4], - "demand": [[10, -10, 15, -15]], - "pickup_and_delivery_pairs": [[0, 1], [2, 3]] - }, - "fleet_data": { - "vehicle_locations": [[0, 0]], - "capacities": [[50]] - }, - "solver_config": {"time_limit": 10} - }' | jq . -``` - -## Terminology: Python vs REST - -| Python API | REST Server | -|------------|-------------| -| `order_locations` | `task_locations` | -| `set_order_time_windows()` | `task_time_windows` | -| `set_order_service_times()` | `service_times` | -| `add_transit_time_matrix()` | `travel_time_matrix_data` | -| `set_pickup_delivery_pairs()` | `pickup_and_delivery_pairs` | - -## Common Mistakes - -```json -// ❌ WRONG field name -"transit_time_matrix_data": {...} - -// ✅ CORRECT -"travel_time_matrix_data": {...} -``` - -```json -// ❌ WRONG capacity format (per vehicle) -"capacities": [[50], [50]] - -// ✅ CORRECT (per dimension across vehicles) -"capacities": [[50, 50]] -``` - ---- - -## Additional References (tested in CI) - -For more complete examples, read these files: - -| Example | File | -|---------|------| -| Basic Routing (Python) | `docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.py` | -| Basic Routing (curl) | `docs/cuopt/source/cuopt-server/examples/routing/examples/basic_routing_example.sh` | -| Initial Solution | `docs/cuopt/source/cuopt-server/examples/routing/examples/initial_solution_example.py` | - -These examples are tested by CI (`ci/test_doc_examples.sh`). diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 4862fb890c..52dfa3b60e 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -118,7 +118,7 @@ jobs: - '**' - '!.ai/**' - '!.coderabbit.yaml' - - '!.github/AGENTS.md' + - '!AGENTS.md' - '!.github/CODE_OF_CONDUCT.md' - '!.github/CODEOWNERS' - '!.github/ISSUE_TEMPLATE/**' @@ -153,7 +153,7 @@ jobs: - '!README.md' - '!.ai/**' - '!.coderabbit.yaml' - - '!.github/AGENTS.md' + - '!AGENTS.md' - '!.github/CODE_OF_CONDUCT.md' - '!.github/CODEOWNERS' - '!.github/ISSUE_TEMPLATE/**' @@ -184,13 +184,21 @@ jobs: - '!sonarqube/**' - '!ucf/**' - '!utilities/**' + - '!skills/**/SKILL.md' + - '!skills/**/resources/**' + - '!ci/utils/validate_skills.sh' + - '!ci/utils/sync_skills_version.sh' + - '!agents/**' + - '!.cursor-plugin/**' + - '!.claude-plugin/**' + - '!gemini-extension.json' test_python_conda: - '**' - '!CONTRIBUTING.md' - '!README.md' - '!.ai/**' - '!.coderabbit.yaml' - - '!.github/AGENTS.md' + - '!AGENTS.md' - '!.github/CODE_OF_CONDUCT.md' - '!.github/CODEOWNERS' - '!.github/ISSUE_TEMPLATE/**' @@ -218,13 +226,21 @@ jobs: - '!sonarqube/**' - '!ucf/**' - '!utilities/**' + - '!skills/**/SKILL.md' + - '!skills/**/resources/**' + - '!ci/utils/validate_skills.sh' + - '!ci/utils/sync_skills_version.sh' + - '!agents/**' + - '!.cursor-plugin/**' + - '!.claude-plugin/**' + - '!gemini-extension.json' test_python_wheels: - '**' - '!CONTRIBUTING.md' - '!README.md' - '!.ai/**' - '!.coderabbit.yaml' - - '!.github/AGENTS.md' + - '!AGENTS.md' - '!.github/CODE_OF_CONDUCT.md' - '!.github/CODEOWNERS' - '!.github/ISSUE_TEMPLATE/**' @@ -253,6 +269,14 @@ jobs: - '!sonarqube/**' - '!ucf/**' - '!utilities/**' + - '!skills/**/SKILL.md' + - '!skills/**/resources/**' + - '!ci/utils/validate_skills.sh' + - '!ci/utils/sync_skills_version.sh' + - '!agents/**' + - '!.cursor-plugin/**' + - '!.claude-plugin/**' + - '!gemini-extension.json' checks: secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@main diff --git a/.opencode/AGENTS.md b/.opencode/AGENTS.md new file mode 120000 index 0000000000..be77ac83a1 --- /dev/null +++ b/.opencode/AGENTS.md @@ -0,0 +1 @@ +../AGENTS.md \ No newline at end of file diff --git a/.opencode/skills b/.opencode/skills new file mode 120000 index 0000000000..42c5394a18 --- /dev/null +++ b/.opencode/skills @@ -0,0 +1 @@ +../skills \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2d968a4622..87a3faaf92 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -95,7 +95,10 @@ repos: [.](md|rst|avro|parquet|png|orc|gz|pkl|sas7bdat|msgpack|pickle|jpg|bz2|zlib)$| ^docs/cuopt/source/cuopt-python/routing/routing-example[.]ipynb$| ^docs/cuopt/source/versions1[.]json$| - ^helmchart/cuopt-server/(Chart[.]yaml|values[.]yaml)$ + ^helmchart/cuopt-server/(Chart[.]yaml|values[.]yaml)$| + ^[.]cursor-plugin/plugin[.]json$| + ^[.]claude-plugin/marketplace[.]json$| + ^gemini-extension[.]json$ - repo: local hooks: - id: update-versions @@ -103,6 +106,18 @@ repos: entry: python ci/utils/update_doc_versions.py language: system files: docs/cuopt/source/versions1.json + - id: sync-skills-version + name: Sync skills version from VERSION + entry: ci/utils/sync_skills_version.sh + language: system + pass_filenames: false + files: ^(VERSION|\.claude-plugin/marketplace\.json|\.cursor-plugin/plugin\.json|gemini-extension\.json)$ + - id: validate-skills + name: Validate agent skills + entry: ci/utils/validate_skills.sh + language: system + pass_filenames: false + files: ^(VERSION|skills/|\.claude-plugin/|\.cursor-plugin/|agents/|ci/utils/validate_skills\.sh|ci/utils/sync_skills_version\.sh|gemini-extension\.json)$ default_language_version: diff --git a/AGENTS.md b/AGENTS.md index f4e47cde2b..74d5aa66e6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,47 @@ -# AGENTS.md +# AGENTS.md — cuOpt AI Agent Entry Point -AI-agent skills for this repo are located at: +AI agent skills for NVIDIA cuOpt optimization engine. Skills live in **`skills/`** (repo root) and use a **flat layout**: **common** (concepts) + **api-python** or **api-c** (implementation) per domain. -- **Entry point**: `.github/AGENTS.md` -- **Skills**: `.github/skills/` +> **🔒 MANDATORY — Security:** You MUST NOT install, upgrade, or modify packages. Provide the exact command for the user to run; they execute it. No exceptions. -If you are a coding agent, start at `.github/AGENTS.md`. +> **🔒 MANDATORY — Ambiguity:** When the problem could be read more than one way, you MUST either **ask the user to clarify** or **solve every plausible interpretation and report all outcomes**. Never pick one interpretation silently. + +## Skills directory (flat) + +### Rules +- `skills/cuopt-user-rules/` — User-facing behavior and conventions; read first when helping users with cuOpt (routing, LP, MILP, QP, install, server). Choose skills from the index below by task, problem type, and interface (Python / C / CLI). +- `skills/cuopt-developer/` — Contributing and development; use when the user is building from source, contributing code, or working on cuOpt internals. + +### Common (concepts only; no API code) +- `skills/cuopt-installation-common/` — Install: system and environment requirements (concepts only; no install commands or interface) +- `skills/lp-milp-formulation/` — LP/MILP: concepts + problem parsing (parameters, constraints, decisions, objective) +- `skills/routing-formulation/` — Routing: VRP, TSP, PDP (problem types, data) +- `skills/qp-formulation/` — QP: minimize-only, escalate (beta) +- `skills/cuopt-server-common/` — Server: capabilities, workflow + +### API (implementation; one interface per skill) +- `skills/cuopt-installation-api-python/` +- `skills/cuopt-installation-api-c/` +- `skills/cuopt-installation-developer/` (build from source) +- `skills/cuopt-lp-milp-api-python/` +- `skills/cuopt-lp-milp-api-c/` +- `skills/cuopt-lp-milp-api-cli/` +- `skills/cuopt-routing-api-python/` +- `skills/cuopt-qp-api-python/` +- `skills/cuopt-qp-api-c/` +- `skills/cuopt-qp-api-cli/` +- `skills/cuopt-server-api-python/` (deploy + client) + +## Resources + +### Documentation +- [cuOpt User Guide](https://docs.nvidia.com/cuopt/user-guide/latest/introduction.html) +- [API Reference](https://docs.nvidia.com/cuopt/user-guide/latest/api.html) + +### Examples +- [cuopt-examples repo](https://github.com/NVIDIA/cuopt-examples) +- [Google Colab notebooks](https://colab.research.google.com/github/nvidia/cuopt-examples/) + +### Support +- [GitHub Issues](https://github.com/NVIDIA/cuopt/issues) +- [Developer Forums](https://forums.developer.nvidia.com/c/ai-data-science/nvidia-cuopt/514) diff --git a/agents/AGENTS.md b/agents/AGENTS.md new file mode 100644 index 0000000000..cf2598bc91 --- /dev/null +++ b/agents/AGENTS.md @@ -0,0 +1,59 @@ +# cuOpt Skills Reference + +You have additional skills documented in `skills//SKILL.md`. **When the user's intent matches a skill below, you MUST read that skill's SKILL.md** and follow its guidance. + +## Mandatory rules + +- **Security:** You MUST NOT install, upgrade, or modify packages. Provide the exact command for the user to run; they execute it. +- **Ambiguity:** When the problem could be read more than one way, either ask the user to clarify or solve every plausible interpretation and report all outcomes. Never pick one interpretation silently. + +## Available skills + +| Skill | Description | +|-------|-------------| +| cuopt-user-rules | Base behavior rules for using NVIDIA cuOpt. Read this FIRST before any cuOpt user task (routing, LP/MILP, QP, installation, server). | +| cuopt-developer | Contribute to NVIDIA cuOpt codebase (C++/CUDA, Python, server, docs, CI). Use when the user wants to modify solver internals, add features, submit PRs, or understand the codebase. | +| cuopt-installation-common | Install cuOpt — system and environment requirements only. Domain concepts; no install commands or interface. | +| cuopt-installation-api-python | Install cuOpt for Python — pip, conda, Docker, verification. Use when installing or verifying the Python API. | +| cuopt-installation-api-c | Install cuOpt for C — conda, locate lib/headers, verification. Use when installing or verifying the C API. | +| cuopt-installation-developer | Developer installation — build cuOpt from source, run tests. Use when setting up a dev environment to contribute or modify cuOpt. | +| lp-milp-formulation | LP/MILP concepts and going from problem text to formulation. Parameters, constraints, decisions, objective. | +| cuopt-lp-milp-api-python | Solve LP and MILP with the Python API. Use for linear constraints, integer variables, scheduling, resource allocation, facility location, production planning. | +| cuopt-lp-milp-api-c | LP and MILP with cuOpt — C API. Use when embedding LP/MILP in C/C++. | +| cuopt-lp-milp-api-cli | LP and MILP with cuOpt — CLI (MPS files, cuopt_cli). Use when solving from MPS via command line. | +| routing-formulation | Vehicle routing (VRP, TSP, PDP) — problem types and data requirements. Domain concepts only. | +| cuopt-routing-api-python | Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API. Use when building or solving routing in Python. | +| qp-formulation | Quadratic Programming (QP) — problem form and constraints. Domain concepts; QP is beta. | +| cuopt-qp-api-python | QP with cuOpt — Python API (beta). Use when building or solving QP in Python. | +| cuopt-qp-api-c | QP with cuOpt — C API. Use when embedding QP in C/C++. | +| cuopt-qp-api-cli | QP with cuOpt — CLI. Use when solving QP from the command line. | +| cuopt-server-common | cuOpt REST server — what it does and how requests flow. Domain concepts only. | +| cuopt-server-api-python | cuOpt REST server — start server, endpoints, Python/curl client examples. Use when deploying or calling the REST API. | + +## Skill paths (from repo root) + +- `skills/cuopt-user-rules/SKILL.md` +- `skills/cuopt-developer/SKILL.md` +- `skills/cuopt-installation-common/SKILL.md` +- `skills/cuopt-installation-api-python/SKILL.md` +- `skills/cuopt-installation-api-c/SKILL.md` +- `skills/cuopt-installation-developer/SKILL.md` +- `skills/lp-milp-formulation/SKILL.md` +- `skills/cuopt-lp-milp-api-python/SKILL.md` +- `skills/cuopt-lp-milp-api-c/SKILL.md` +- `skills/cuopt-lp-milp-api-cli/SKILL.md` +- `skills/routing-formulation/SKILL.md` +- `skills/cuopt-routing-api-python/SKILL.md` +- `skills/qp-formulation/SKILL.md` +- `skills/cuopt-qp-api-python/SKILL.md` +- `skills/cuopt-qp-api-c/SKILL.md` +- `skills/cuopt-qp-api-cli/SKILL.md` +- `skills/cuopt-server-common/SKILL.md` +- `skills/cuopt-server-api-python/SKILL.md` + +## Resources + +- [cuOpt User Guide](https://docs.nvidia.com/cuopt/user-guide/latest/introduction.html) +- [API Reference](https://docs.nvidia.com/cuopt/user-guide/latest/api.html) +- [cuopt-examples](https://github.com/NVIDIA/cuopt-examples) +- [GitHub Issues](https://github.com/NVIDIA/cuopt/issues) diff --git a/ci/test_python.sh b/ci/test_python.sh index 0a70e56fa7..4f91c83334 100755 --- a/ci/test_python.sh +++ b/ci/test_python.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 set -euo pipefail @@ -74,5 +74,8 @@ timeout 20m ./ci/run_cuopt_server_pytests.sh \ --cov-report=xml:"${RAPIDS_COVERAGE_DIR}/cuopt-server-coverage.xml" \ --cov-report=term +rapids-logger "Test skills/ assets (Python, C, CLI)" +timeout 10m ./ci/test_skills_assets.sh + rapids-logger "Test script exiting with value: $EXITCODE" exit ${EXITCODE} diff --git a/ci/test_skills_assets.sh b/ci/test_skills_assets.sh new file mode 100755 index 0000000000..c75645cb93 --- /dev/null +++ b/ci/test_skills_assets.sh @@ -0,0 +1,155 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Run all assets under skills/ (Python, C, CLI) as part of conda Python test. +# Python: run each .py from its directory (server API clients need server on port 8000). +# C: compile and run each .c with libcuopt. +# CLI: run cuopt_cli on each sample .mps in API-CLI skill assets. + +set -euo pipefail + +# Use rapids-logger in CI; fall back to echo for local testing +if command -v rapids-logger &>/dev/null; then + log() { rapids-logger "$*"; } +else + log() { echo "[rapids-logger] $*"; } +fi + +REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +SKILLS_ASSETS="${REPO_ROOT}/skills" +FAILED=() +SERVER_PID="" + +if [[ ! -d "${SKILLS_ASSETS}" ]]; then + log "No skills directory found, skipping skills asset tests" + exit 0 +fi + +# ---- Start cuOpt server for server API Python assets (port 8000) ---- +start_server() { + if ! python -c "import cuopt_server" 2>/dev/null; then + log "cuopt_server not available, server API assets will skip" + return + fi + python -m cuopt_server.cuopt_service --ip 127.0.0.1 --port 8000 &>/dev/null & + SERVER_PID=$! + for _ in {1..30}; do + if curl -s -o /dev/null http://127.0.0.1:8000/cuopt/health 2>/dev/null; then + log "cuOpt server started (port 8000) for server API assets" + return + fi + sleep 1 + done + log "cuOpt server did not become ready; server API assets will skip" + kill "${SERVER_PID}" 2>/dev/null || true + SERVER_PID="" +} +stop_server() { + if [[ -n "${SERVER_PID}" ]] && kill -0 "${SERVER_PID}" 2>/dev/null; then + log "Stopping cuOpt server (PID ${SERVER_PID})" + kill "${SERVER_PID}" 2>/dev/null || true + wait "${SERVER_PID}" 2>/dev/null || true + SERVER_PID="" + fi +} +trap stop_server EXIT +start_server + +# ---- Python assets ---- +log "Testing Python assets in skills/" +while IFS= read -r -d '' script; do + dir=$(dirname "$script") + name=$(basename "$script") + rel="${script#"$REPO_ROOT/"}" + log "Running Python asset: $rel" + if (cd "$dir" && python "$name"); then + log "PASS: $rel" + else + FAILED+=("$rel") + log "FAIL: $rel" + fi +done < <(find "${SKILLS_ASSETS}" -path "*/assets/*" -name "*.py" -type f -print0 | sort -z) + +# ---- C assets (compile and run; requires CONDA_PREFIX and a C compiler) ---- +CC="${CC:-}" +if [[ -z "${CC}" ]]; then + for c in gcc cc clang; do + if command -v "$c" &>/dev/null; then + CC="$c" + break + fi + done +fi +if [[ -n "${CONDA_PREFIX:-}" ]]; then + if [[ -z "${CC}" ]]; then + log "No C compiler found; installing c-compiler in conda environment" + if command -v mamba &>/dev/null; then + mamba install -y -c conda-forge c-compiler + else + conda install -y -c conda-forge c-compiler + fi + for c in gcc cc clang; do + if command -v "$c" &>/dev/null; then + CC="$c" + break + fi + done + if [[ -z "${CC}" ]]; then + log "C compiler still not found after install. Set CC or install gcc/cc/clang." + exit 1 + fi + fi + INCLUDE_PATH="${CONDA_PREFIX}/include" + LIB_PATH="${CONDA_PREFIX}/lib" + export LD_LIBRARY_PATH="${LIB_PATH}:${LD_LIBRARY_PATH:-}" + + log "Testing C assets in skills (using ${CC})" + while IFS= read -r -d '' cfile; do + dir=$(dirname "$cfile") + base=$(basename "$cfile" .c) + rel="${cfile#"$REPO_ROOT/"}" + log "Building and running C asset: $rel" + if ! (cd "$dir" && "${CC}" -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o "$base" "$(basename "$cfile")" -lcuopt); then + FAILED+=("$rel (build)") + log "FAIL: $rel (build)" + continue + fi + if [[ "$base" == "mps_solver" ]]; then + run_cmd=(./"$base" data/sample.mps) + else + run_cmd=(./"$base") + fi + if (cd "$dir" && "${run_cmd[@]}"); then + log "PASS: $rel" + else + FAILED+=("$rel") + log "FAIL: $rel" + fi + done < <(find "${SKILLS_ASSETS}" -path "*/assets/*" -name "*.c" -type f -print0 | sort -z) +else + log "CONDA_PREFIX not set, skipping C asset tests" +fi + +# ---- CLI assets (cuopt_cli with sample MPS files) ---- +log "Testing CLI assets in skills/" +while IFS= read -r -d '' mps; do + rel="${mps#"$REPO_ROOT/"}" + log "Running CLI asset: $rel" + if cuopt_cli "$mps" --time-limit 10; then + log "PASS: $rel" + else + FAILED+=("$rel") + log "FAIL: $rel" + fi +done < <(find "${SKILLS_ASSETS}" -path "*/cuopt-*-api-cli/assets/*" -name "*.mps" -type f -print0 | sort -z) + +if [[ ${#FAILED[@]} -gt 0 ]]; then + log "The following skills assets failed:" + printf '%s\n' "${FAILED[@]}" + exit 1 +fi + +log "All skills assets (Python, C, CLI) passed." +exit 0 diff --git a/ci/utils/sync_skills_version.sh b/ci/utils/sync_skills_version.sh new file mode 100755 index 0000000000..1dfca8a663 --- /dev/null +++ b/ci/utils/sync_skills_version.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Sync skills/plugin version from repo root VERSION file. +# Run from repo root: ./ci/utils/sync_skills_version.sh +set -e + +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$REPO_ROOT" + +VERSION_FILE="${REPO_ROOT}/VERSION" +if [[ ! -f "${VERSION_FILE}" ]]; then + echo "ERROR: VERSION file not found at ${VERSION_FILE}" + exit 1 +fi + +RELEASE_VERSION=$(tr -d ' \n\r' < "${VERSION_FILE}") +if [[ -z "${RELEASE_VERSION}" ]]; then + echo "ERROR: VERSION file is empty" + exit 1 +fi + +echo "Syncing skills version to ${RELEASE_VERSION} (from VERSION)..." + +# .cursor-plugin/plugin.json and gemini-extension.json: top-level "version" +for f in .cursor-plugin/plugin.json gemini-extension.json; do + if [[ -f "$f" ]]; then + sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${RELEASE_VERSION}\"/" "$f" + echo " updated $f" + fi +done + +# .claude-plugin/marketplace.json: metadata.version +if [[ -f ".claude-plugin/marketplace.json" ]]; then + sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"${RELEASE_VERSION}\"/" .claude-plugin/marketplace.json + echo " updated .claude-plugin/marketplace.json" +fi + +# skills/*/SKILL.md: add or update version in YAML frontmatter (after name:) +SKILLS_DIR="skills" +for skill_md in "${SKILLS_DIR}"/*/SKILL.md; do + [[ -f "$skill_md" ]] || continue + if grep -q '^version:' "$skill_md" 2>/dev/null; then + sed -i "s/^version:.*/version: \"${RELEASE_VERSION}\"/" "$skill_md" + else + sed -i "/^name:/a version: \"${RELEASE_VERSION}\"" "$skill_md" + fi + echo " updated $skill_md" +done + +echo "Done. Skills version is now ${RELEASE_VERSION}." diff --git a/ci/utils/validate_skills.sh b/ci/utils/validate_skills.sh new file mode 100755 index 0000000000..6577a45a1e --- /dev/null +++ b/ci/utils/validate_skills.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Validate cuOpt agent skills and plugin manifests. +# Run from repo root: ./ci/utils/validate_skills.sh +set -e + +REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$REPO_ROOT" + +SKILLS_DIR="skills" +CLAUDE_MARKETPLACE=".claude-plugin/marketplace.json" +AGENTS_MD="agents/AGENTS.md" +VERSION_FILE="VERSION" +ERRORS=0 + +# Check skills version matches release version (VERSION file) +if [[ -f "${VERSION_FILE}" ]]; then + RELEASE_VERSION=$(tr -d ' \n\r' < "${VERSION_FILE}") + for f in .cursor-plugin/plugin.json gemini-extension.json .claude-plugin/marketplace.json; do + if [[ -f "$f" ]]; then + FILE_VERSION=$(grep '"version"' "$f" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1) + if [[ "${FILE_VERSION}" != "${RELEASE_VERSION}" ]]; then + echo "ERROR: $f has version \"${FILE_VERSION}\" but VERSION file has \"${RELEASE_VERSION}\". Run: ./ci/utils/sync_skills_version.sh" + ERRORS=$((ERRORS + 1)) + fi + fi + done +fi + +echo "Validating skills in $SKILLS_DIR..." + +for dir in "$SKILLS_DIR"/*/; do + [ -d "$dir" ] || continue + name=$(basename "$dir") + skill_md="${dir}SKILL.md" + if [ ! -f "$skill_md" ]; then + echo "ERROR: $name missing SKILL.md" + ERRORS=$((ERRORS + 1)) + continue + fi + if ! grep -q '^name:' "$skill_md" || ! grep -q '^description:' "$skill_md"; then + echo "ERROR: $name/SKILL.md missing frontmatter (name: or description:)" + ERRORS=$((ERRORS + 1)) + fi + if [[ -f "${VERSION_FILE}" ]]; then + RELEASE_VERSION=$(tr -d ' \n\r' < "${VERSION_FILE}") + if grep -q '^version:' "$skill_md" 2>/dev/null; then + SKILL_VERSION=$(sed -n 's/^version:[^0-9]*\([0-9][0-9.]*\).*/\1/p' "$skill_md" | head -1) + if [[ "${SKILL_VERSION}" != "${RELEASE_VERSION}" ]]; then + echo "ERROR: $name/SKILL.md has version \"${SKILL_VERSION}\" but VERSION file has \"${RELEASE_VERSION}\". Run: ./ci/utils/sync_skills_version.sh" + ERRORS=$((ERRORS + 1)) + fi + else + echo "ERROR: $name/SKILL.md missing version in frontmatter. Run: ./ci/utils/sync_skills_version.sh" + ERRORS=$((ERRORS + 1)) + fi + fi +done + +if [ -f "$CLAUDE_MARKETPLACE" ]; then + echo "Validating $CLAUDE_MARKETPLACE..." + while IFS= read -r line; do + path=$(echo "$line" | sed -n 's/.*"source"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') + [ -z "$path" ] && continue + path="${path#./}" + if [ -n "$path" ] && [ ! -f "$path/SKILL.md" ]; then + echo "ERROR: marketplace.json source missing SKILL.md: $path" + ERRORS=$((ERRORS + 1)) + fi + done < <(grep '"source"' "$CLAUDE_MARKETPLACE" || true) + for dir in "$SKILLS_DIR"/*/; do + [ -d "$dir" ] || continue + name=$(basename "$dir") + if ! grep -q "\"name\": \"$name\"" "$CLAUDE_MARKETPLACE"; then + echo "ERROR: skill $name not listed in $CLAUDE_MARKETPLACE" + ERRORS=$((ERRORS + 1)) + fi + done +fi + +if [ -f "$AGENTS_MD" ]; then + echo "Validating $AGENTS_MD references..." + for dir in "$SKILLS_DIR"/*/; do + [ -d "$dir" ] || continue + name=$(basename "$dir") + if ! grep -q "$name" "$AGENTS_MD"; then + echo "ERROR: agents/AGENTS.md does not reference skill: $name" + ERRORS=$((ERRORS + 1)) + fi + done +fi + +if [ $ERRORS -gt 0 ]; then + echo "Validation failed with $ERRORS error(s)." + exit 1 +fi +echo "All validations passed." diff --git a/gemini-extension.json b/gemini-extension.json new file mode 100644 index 0000000000..b4c6b764a4 --- /dev/null +++ b/gemini-extension.json @@ -0,0 +1,6 @@ +{ + "name": "nvidia-cuopt-skills", + "description": "Agent skills for NVIDIA cuOpt optimization engine: routing, LP/MILP/QP, installation, and server.", + "version": "26.04.00", + "contextFileName": "AGENTS.md" +} diff --git a/.github/skills/cuopt-developer/SKILL.md b/skills/cuopt-developer/SKILL.md similarity index 99% rename from .github/skills/cuopt-developer/SKILL.md rename to skills/cuopt-developer/SKILL.md index 8e73995b58..12419153ac 100644 --- a/.github/skills/cuopt-developer/SKILL.md +++ b/skills/cuopt-developer/SKILL.md @@ -1,5 +1,6 @@ --- name: cuopt-developer +version: "26.04.00" description: Contribute to NVIDIA cuOpt codebase including C++/CUDA, Python, server, docs, and CI. Use when the user wants to modify solver internals, add features, submit PRs, or understand the codebase architecture. --- diff --git a/skills/cuopt-installation-api-c/SKILL.md b/skills/cuopt-installation-api-c/SKILL.md new file mode 100644 index 0000000000..747382e3c7 --- /dev/null +++ b/skills/cuopt-installation-api-c/SKILL.md @@ -0,0 +1,32 @@ +--- +name: cuopt-installation-api-c +version: "26.04.00" +description: Install cuOpt for C — conda, locate lib/headers, verification. Use when the user is installing or verifying the C API. Standalone; no common skill. +--- + +# cuOpt Installation — C API (user) + +Install cuOpt to *use* it from C. Standalone skill (no separate common). + +## System requirements + +- **GPU**: NVIDIA Compute Capability ≥ 7.0 (Volta+). CUDA 12.x or 13.x. +- **Driver**: Compatible NVIDIA driver. Python and C are separate installables. + +## conda (C / libcuopt) + +```bash +conda install -c rapidsai -c conda-forge -c nvidia cuopt +# libcuopt is provided by the same channel; Python and C are separate packages. +``` + +## Verify C API + +```bash +find $CONDA_PREFIX -name "cuopt_c.h" +find $CONDA_PREFIX -name "libcuopt.so" +``` + +## Examples + +- [verification_examples.md](resources/verification_examples.md) — C API verification diff --git a/.github/skills/cuopt-installation/resources/verification_examples.md b/skills/cuopt-installation-api-c/resources/verification_examples.md similarity index 98% rename from .github/skills/cuopt-installation/resources/verification_examples.md rename to skills/cuopt-installation-api-c/resources/verification_examples.md index bd84de80ba..83628437d7 100644 --- a/.github/skills/cuopt-installation/resources/verification_examples.md +++ b/skills/cuopt-installation-api-c/resources/verification_examples.md @@ -17,7 +17,7 @@ print("DataModel created - GPU access OK") import cudf cost_matrix = cudf.DataFrame([[0,1,2],[1,0,1],[2,1,0]], dtype="float32") dm.add_cost_matrix(cost_matrix) -dm.set_order_locations(cudf.Series([1, 2])) +dm.set_order_locations(cudf.Series([1, 2], dtype="int32")) solution = routing.Solve(dm, routing.SolverSettings()) print(f"Solve status: {solution.get_status()}") diff --git a/skills/cuopt-installation-api-python/SKILL.md b/skills/cuopt-installation-api-python/SKILL.md new file mode 100644 index 0000000000..a3d7a5e5d2 --- /dev/null +++ b/skills/cuopt-installation-api-python/SKILL.md @@ -0,0 +1,73 @@ +--- +name: cuopt-installation-api-python +version: "26.04.00" +description: Install cuOpt for Python — pip, conda, Docker, verification. Use when the user is installing or verifying the Python API. Standalone; no common skill. +--- + +# cuOpt Installation — Python (user) + +Install cuOpt to *use* it from Python. Standalone skill (no separate common). + +## System requirements + +- **GPU**: NVIDIA Compute Capability ≥ 7.0 (Volta+). CUDA 12.x or 13.x; match package (cuopt-cu12 / cuopt-cu13). +- **Driver**: Compatible NVIDIA driver. + +## pip (Python) + +**Choose one** — do not run both. The second install would override the first and can cause CUDA/package mismatch. + +- **CUDA 13.x:** + ```bash + pip install --extra-index-url=https://pypi.nvidia.com cuopt-cu13 + ``` +- **CUDA 12.x:** + ```bash + pip install --extra-index-url=https://pypi.nvidia.com 'cuopt-cu12==26.2.*' + ``` + +## pip: Server + Client + +```bash +pip install --extra-index-url=https://pypi.nvidia.com cuopt-server-cu12 cuopt-sh-client +``` + +## conda + +```bash +conda install -c rapidsai -c conda-forge -c nvidia cuopt +conda install -c rapidsai -c conda-forge -c nvidia cuopt-server cuopt-sh-client +``` + +## Docker + +```bash +docker pull nvidia/cuopt:latest-cuda12.9-py3.13 +docker run --gpus all -it --rm -p 8000:8000 nvidia/cuopt:latest-cuda12.9-py3.13 +``` + +## Verify Python + +```python +import cuopt +print(cuopt.__version__) +from cuopt import routing +dm = routing.DataModel(n_locations=3, n_fleet=1, n_orders=2) +``` + +## Verify Server + +```bash +python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 & +sleep 5 +curl -s http://localhost:8000/cuopt/health | jq . +``` + +## Common Issues + +- No module 'cuopt' → check `pip list | grep cuopt`, `which python`, reinstall with correct index. +- CUDA not available → `nvidia-smi`, `nvcc --version`, match cuopt-cu12 vs cuopt-cu13 to CUDA. + +## Examples + +- [verification_examples.md](resources/verification_examples.md) — Python and server verification diff --git a/skills/cuopt-installation-api-python/resources/verification_examples.md b/skills/cuopt-installation-api-python/resources/verification_examples.md new file mode 100644 index 0000000000..83628437d7 --- /dev/null +++ b/skills/cuopt-installation-api-python/resources/verification_examples.md @@ -0,0 +1,172 @@ +# Installation: Verification Examples + +## Verify Python Installation + +```python +# Basic import test +import cuopt +print(f"cuOpt version: {cuopt.__version__}") + +# GPU access test +from cuopt import routing + +dm = routing.DataModel(n_locations=3, n_fleet=1, n_orders=2) +print("DataModel created - GPU access OK") + +# Quick solve test +import cudf +cost_matrix = cudf.DataFrame([[0,1,2],[1,0,1],[2,1,0]], dtype="float32") +dm.add_cost_matrix(cost_matrix) +dm.set_order_locations(cudf.Series([1, 2], dtype="int32")) + +solution = routing.Solve(dm, routing.SolverSettings()) +print(f"Solve status: {solution.get_status()}") +print("cuOpt installation verified!") +``` + +## Verify LP/MILP + +```python +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +problem = Problem("Test") +x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") +problem.setObjective(x, sense=MAXIMIZE) +problem.addConstraint(x <= 10) + +problem.solve(SolverSettings()) +print(f"Status: {problem.Status.name}") +print(f"x = {x.getValue()}") +print("LP/MILP working!") +``` + +## Verify Server Installation + +```bash +# Start server in background +python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 & +SERVER_PID=$! + +# Wait for startup +sleep 5 + +# Health check +curl -s http://localhost:8000/cuopt/health | jq . + +# Quick routing test +curl -s -X POST "http://localhost:8000/cuopt/request" \ + -H "Content-Type: application/json" \ + -H "CLIENT-VERSION: custom" \ + -d '{ + "cost_matrix_data": {"data": {"0": [[0,1],[1,0]]}}, + "travel_time_matrix_data": {"data": {"0": [[0,1],[1,0]]}}, + "task_data": {"task_locations": [1]}, + "fleet_data": {"vehicle_locations": [[0,0]], "capacities": [[10]]}, + "solver_config": {"time_limit": 1} + }' | jq . + +# Stop server +kill $SERVER_PID +``` + +## Verify C API Installation + +```bash +# Find header +echo "Looking for cuopt_c.h..." +find ${CONDA_PREFIX:-/usr} -name "cuopt_c.h" 2>/dev/null + +# Find library +echo "Looking for libcuopt.so..." +find ${CONDA_PREFIX:-/usr} -name "libcuopt.so" 2>/dev/null + +# Test compile (if gcc available) +cat > /tmp/test_cuopt.c << 'EOF' +#include +#include +int main() { + printf("cuopt_c.h found and compilable\n"); + return 0; +} +EOF + +gcc -I${CONDA_PREFIX}/include -c /tmp/test_cuopt.c -o /tmp/test_cuopt.o && \ + echo "C API headers OK" || echo "C API headers not found" +``` + +## Check System Requirements + +```bash +# GPU check +nvidia-smi + +# CUDA version +nvcc --version + +# Compute capability (need >= 7.0) +nvidia-smi --query-gpu=compute_cap --format=csv,noheader + +# Python version +python --version + +# Available memory +nvidia-smi --query-gpu=memory.total,memory.free --format=csv +``` + +## Check Package Versions + +```python +import importlib.metadata + +packages = ["cuopt-cu12", "cuopt-cu13", "cuopt-server-cu12", "cuopt-server-cu13", "cuopt-sh-client"] +for pkg in packages: + try: + version = importlib.metadata.version(pkg) + print(f"{pkg}: {version}") + except importlib.metadata.PackageNotFoundError: + pass +``` + +## Troubleshooting Commands + +```bash +# Check if cuopt is installed +pip list | grep -i cuopt + +# Check conda packages +conda list | grep -i cuopt + +# Check CUDA runtime +python -c "import torch; print(torch.cuda.is_available())" 2>/dev/null || echo "PyTorch not installed" + +# Check cudf (routing dependency) +python -c "import cudf; print(f'cudf: {cudf.__version__}')" + +# Check rmm (memory manager) +python -c "import rmm; print(f'rmm: {rmm.__version__}')" +``` + +## Docker Verification + +```bash +# Pull and run +docker run --gpus all --rm nvidia/cuopt:latest-cuda12.9-py3.13 python -c " +import cuopt +print(f'cuOpt version: {cuopt.__version__}') +from cuopt import routing +dm = routing.DataModel(n_locations=3, n_fleet=1, n_orders=2) +print('GPU access OK') +" +``` + +--- + +## Additional References + +| Topic | Resource | +|-------|----------| +| Installation Guide | [NVIDIA cuOpt Docs](https://docs.nvidia.com/cuopt/user-guide/latest/installation.html) | +| System Requirements | [cuOpt Requirements](https://docs.nvidia.com/cuopt/user-guide/latest/requirements.html) | +| Docker Images | See `ci/docker/` in this repo | +| Conda Recipes | See `conda/recipes/` in this repo | diff --git a/skills/cuopt-installation-common/SKILL.md b/skills/cuopt-installation-common/SKILL.md new file mode 100644 index 0000000000..6ceb9f9000 --- /dev/null +++ b/skills/cuopt-installation-common/SKILL.md @@ -0,0 +1,29 @@ +--- +name: cuopt-installation-common +version: "26.04.00" +description: Install cuOpt — system and environment requirements only. Domain concepts; no install commands or interface guidance. +--- + +# cuOpt Installation (common) + +Domain concepts for installing and running cuOpt. No install commands or interface details here. + +## System requirements + +- **GPU**: NVIDIA with Compute Capability ≥ 7.0 (Volta or newer). Examples: V100, A100, H100, RTX 20xx/30xx/40xx. Not supported: GTX 10xx (Pascal). +- **CUDA**: 12.x or 13.x. Package and runtime must match (e.g. cuopt built for CUDA 12 with a CUDA 12 driver). +- **Driver**: Compatible NVIDIA driver for the CUDA version in use. + +## Required questions (environment) + +Ask these if not already clear: + +1. **Environment** — Local machine with GPU, cloud instance, Docker/Kubernetes, or no GPU (need remote/server)? +2. **CUDA version** — What is installed or planned? (e.g. `nvcc --version`, `nvidia-smi`.) +3. **Usage** — In-process (library/API) vs server (REST)? Which language or runtime (Python, C, server)? +4. **Package manager** — pip, conda, or Docker preferred? + +## Notes + +- Python API and C API are separate installables; having one does not provide the other. +- Server deployment typically uses Docker or a dedicated server package; client can be any language. diff --git a/skills/cuopt-installation-developer/SKILL.md b/skills/cuopt-installation-developer/SKILL.md new file mode 100644 index 0000000000..a002498853 --- /dev/null +++ b/skills/cuopt-installation-developer/SKILL.md @@ -0,0 +1,36 @@ +--- +name: cuopt-installation-developer +version: "26.04.00" +description: Developer installation — build cuOpt from source, run tests. Use when the user wants to set up a dev environment to contribute or modify cuOpt. +--- + +# cuOpt Installation — Developer + +Set up an environment to **build cuOpt from source** and run tests. For contribution behavior and PRs, see the developer skill after the build works. + +## When to use this skill + +- User wants to *build* cuOpt (clone, build deps, build, tests). +- Not for *using* cuOpt (pip/conda) — use the user installation skill instead. + +## Required questions (environment) + +Ask these if not already clear: + +1. **OS and GPU** — Linux? Which CUDA version (e.g. 12.x)? +2. **Goal** — Contributing upstream, or local fork/modification? +3. **Component** — C++/CUDA core, Python bindings, server, docs, or CI? + +## Typical setup (conceptual) + +1. **Clone** the cuOpt repo (and submodules if any). +2. **Build dependencies** — CUDA toolkit, compiler, CMake; see repo docs for the canonical list. +3. **Configure and build** — e.g. top-level `build.sh` or CMake; Debug/Release. +4. **Run tests** — e.g. `pytest` for Python, `ctest` or project test runner for C++. +5. **Optional** — Python env for bindings; pre-commit or style checks. + +Use the repository’s own documentation (README, CONTRIBUTING, or docs/) for exact commands and versions. + +## After setup + +Once the developer can build and run tests, use **cuopt-developer** for behavior rules, code patterns, and contribution workflow (DCO, PRs). diff --git a/skills/cuopt-lp-milp-api-c/SKILL.md b/skills/cuopt-lp-milp-api-c/SKILL.md new file mode 100644 index 0000000000..53df3de63e --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/SKILL.md @@ -0,0 +1,57 @@ +--- +name: cuopt-lp-milp-api-c +version: "26.04.00" +description: LP and MILP with cuOpt — C API only. Use when the user is embedding LP/MILP in C/C++. +--- + +# cuOpt LP/MILP — C API + +Confirm problem type and formulation (variables, objective, constraints, variable types) before coding. + +This skill is **C only**. + +## Quick Reference: C API + +```c +#include + +// CSR format for constraints +cuopt_int_t row_offsets[] = {0, 2, 4}; +cuopt_int_t col_indices[] = {0, 1, 0, 1}; +cuopt_float_t values[] = {2.0, 3.0, 4.0, 2.0}; +char var_types[] = {CUOPT_CONTINUOUS, CUOPT_INTEGER}; + +cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, + 0.0, objective_coefficients, + row_offsets, col_indices, values, + constraint_lower, constraint_upper, + var_lower, var_upper, var_types, + &problem +); +cuOptSolve(problem, settings, &solution); +cuOptGetObjectiveValue(solution, &obj_value); +``` + +## Debugging (MPS / C) + +**MPS parsing:** Required sections in order: NAME, ROWS, COLUMNS, RHS, (optional) BOUNDS, ENDATA. Integer markers: `'MARKER'`, `'INTORG'`, `'INTEND'`. + +**OOM or slow:** Check problem size (variables, constraints); use sparse matrix; set time limit and gap tolerance. + +## Examples + +- [examples.md](resources/examples.md) — LP/MILP with build instructions +- [assets/README.md](assets/README.md) — Build commands for all reference code below +- [lp_basic](assets/lp_basic/) — Simple LP: create problem, solve, get solution +- [lp_duals](assets/lp_duals/) — Dual values and reduced costs +- [lp_warmstart](assets/lp_warmstart/) — PDLP warmstart (see README) +- [milp_basic](assets/milp_basic/) — Simple MILP with integer variable +- [milp_production_planning](assets/milp_production_planning/) — Production planning with resource constraints +- [mps_solver](assets/mps_solver/) — Solve from MPS file via `cuOptReadProblem` + +For **CLI** (MPS files), use `cuopt_cli` and product docs. + +## Escalate + +If the problem is quadratic (squared or cross terms in the objective), use QP. For contribution or build-from-source, use product or repo documentation. diff --git a/skills/cuopt-lp-milp-api-c/assets/README.md b/skills/cuopt-lp-milp-api-c/assets/README.md new file mode 100644 index 0000000000..e354988da1 --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/README.md @@ -0,0 +1,33 @@ +# Assets — reference C examples + +LP/MILP C API reference implementations. Use as reference when building new applications; do not edit in place. Build requires cuOpt installed (include and lib paths set). + +| Example | Type | Description | +|---------|------|-------------| +| [lp_basic](lp_basic/) | LP | Simple LP: create problem, solve, get solution | +| [lp_duals](lp_duals/) | LP | Dual values and reduced costs | +| [lp_warmstart](lp_warmstart/) | LP | PDLP warmstart (see README) | +| [milp_basic](milp_basic/) | MILP | Simple MILP with integer variable | +| [milp_production_planning](milp_production_planning/) | MILP | Production planning with resource constraints | +| [mps_solver](mps_solver/) | LP/MILP | Solve from MPS file via `cuOptReadProblem` | + +## Build and run + +Set include and library paths, then build and run. + +**Using conda:** Activate your cuOpt env first (`conda activate cuopt`), then: + +```bash +# Paths from active conda env (CONDA_PREFIX is set when env is activated) +export INCLUDE_PATH="${CONDA_PREFIX}/include" +export LIB_PATH="${CONDA_PREFIX}/lib" +export LD_LIBRARY_PATH="${LIB_PATH}:${LD_LIBRARY_PATH}" + +# Build and run (from this assets/ directory) — example: lp_basic +gcc -I"${INCLUDE_PATH}" -L"${LIB_PATH}" -o lp_basic/lp_simple lp_basic/lp_simple.c -lcuopt +./lp_basic/lp_simple +``` + +For the other examples, use the same pattern (e.g. `lp_duals/lp_duals.c` → `lp_duals/lp_duals`). `mps_solver` takes an MPS file path: `./mps_solver mps_solver/data/sample.mps`. + +Without conda, set `INCLUDE_PATH` and `LIB_PATH` to your cuOpt include and lib directories, then use the same `gcc` and `LD_LIBRARY_PATH` as above. Each subdirectory README has a one-line build/run for that example. diff --git a/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md b/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md new file mode 100644 index 0000000000..010666240f --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/lp_basic/README.md @@ -0,0 +1,15 @@ +# Simple LP (C API) + +Minimize `-0.2*x1 + 0.1*x2` subject to: +- `3*x1 + 4*x2 <= 5.4` +- `2.7*x1 + 10.1*x2 <= 4.9` +- `x1, x2 >= 0` + +**Build:** From repo root or skill dir, with cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o lp_simple lp_simple.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./lp_simple +``` + +**See also:** [resources/examples.md](../../resources/examples.md) for parameter constants and more examples. diff --git a/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c b/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c new file mode 100644 index 0000000000..a21e17ab7b --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/lp_basic/lp_simple.c @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Simple LP (C API): minimize -0.2*x1 + 0.1*x2 + * subject to 3*x1 + 4*x2 <= 5.4, 2.7*x1 + 10.1*x2 <= 4.9, x1,x2 >= 0 + */ +#include +#include +#include +#include + +int main(void) { + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 2; + + cuopt_int_t row_offsets[] = {0, 2, 4}; + cuopt_int_t column_indices[] = {0, 1, 0, 1}; + cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; + + cuopt_float_t objective_coefficients[] = {-0.2, 0.1}; + cuopt_float_t constraint_upper_bounds[] = {5.4, 4.9}; + cuopt_float_t constraint_lower_bounds[] = {-CUOPT_INFINITY, -CUOPT_INFINITY}; + + cuopt_float_t var_lower_bounds[] = {0.0, 0.0}; + cuopt_float_t var_upper_bounds[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + + cuopt_int_t status = cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, 0.0, + objective_coefficients, + row_offsets, column_indices, values, + constraint_lower_bounds, constraint_upper_bounds, + var_lower_bounds, var_upper_bounds, + variable_types, &problem + ); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + return 1; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting primal tolerance: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + cuopt_float_t time, objective_value; + cuopt_int_t termination_status; + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto cleanup; + } + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto cleanup; + } + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } + + printf("Status: %d\n", termination_status); + printf("Time: %f s\n", time); + printf("Objective: %f\n", objective_value); + + cuopt_float_t *sol = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (sol) { + status = cuOptGetPrimalSolution(solution, sol); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(sol); + goto cleanup; + } + printf("x1 = %f, x2 = %f\n", sol[0], sol[1]); + free(sol); + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} diff --git a/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md b/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md new file mode 100644 index 0000000000..78f275fc63 --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/lp_duals/README.md @@ -0,0 +1,14 @@ +# LP duals and reduced costs (C API) + +Retrieve dual values (shadow prices) and reduced costs after solving an LP. + +**Problem:** Minimize 3x + 2y + 5z subject to x + y + z = 4, 2x + y + z = 5, x, y, z ≥ 0. + +**Build:** With cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o lp_duals lp_duals.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./lp_duals +``` + +**See also:** [resources/examples.md](../../resources/examples.md) for full parameter reference. diff --git a/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c b/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c new file mode 100644 index 0000000000..a92262d18a --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/lp_duals/lp_duals.c @@ -0,0 +1,115 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * LP with dual values and reduced costs (C API). + * Problem: Minimize 3x + 2y + 5z subject to x + y + z = 4, 2x + y + z = 5, x,y,z >= 0. + */ +#include +#include +#include +#include + +int main(void) { + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + const cuopt_int_t num_variables = 3; + const cuopt_int_t num_constraints = 2; + + /* Constraint matrix CSR: row0 1*x+1*y+1*z, row1 2*x+1*y+1*z */ + cuopt_int_t row_offsets[] = {0, 3, 6}; + cuopt_int_t column_indices[] = {0, 1, 2, 0, 1, 2}; + cuopt_float_t values[] = {1.0, 1.0, 1.0, 2.0, 1.0, 1.0}; + + cuopt_float_t objective_coefficients[] = {3.0, 2.0, 5.0}; + cuopt_float_t constraint_lower[] = {4.0, 5.0}; + cuopt_float_t constraint_upper[] = {4.0, 5.0}; + cuopt_float_t var_lower[] = {0.0, 0.0, 0.0}; + cuopt_float_t var_upper[] = {CUOPT_INFINITY, CUOPT_INFINITY, CUOPT_INFINITY}; + char variable_types[] = {CUOPT_CONTINUOUS, CUOPT_CONTINUOUS, CUOPT_CONTINUOUS}; + + cuopt_int_t status = cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, 0.0, + objective_coefficients, + row_offsets, column_indices, values, + constraint_lower, constraint_upper, + var_lower, var_upper, + variable_types, &problem + ); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + return 1; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting primal tolerance: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + cuopt_float_t objective_value; + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } + printf("Objective: %f\n", objective_value); + + cuopt_float_t *primal = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (primal) { + status = cuOptGetPrimalSolution(solution, primal); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(primal); + goto cleanup; + } + printf("x = %f, y = %f, z = %f\n", primal[0], primal[1], primal[2]); + free(primal); + } + + cuopt_float_t *dual = malloc((size_t)num_constraints * sizeof(cuopt_float_t)); + if (dual) { + status = cuOptGetDualSolution(solution, dual); + if (status == CUOPT_SUCCESS) { + printf("Constraint c1 DualValue = %f\n", dual[0]); + printf("Constraint c2 DualValue = %f\n", dual[1]); + } + free(dual); + } + + cuopt_float_t *reduced = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (reduced) { + status = cuOptGetReducedCosts(solution, reduced); + if (status == CUOPT_SUCCESS) { + printf("x ReducedCost = %f, y ReducedCost = %f, z ReducedCost = %f\n", + reduced[0], reduced[1], reduced[2]); + } + free(reduced); + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} diff --git a/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md b/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md new file mode 100644 index 0000000000..1e254b75ea --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/lp_warmstart/README.md @@ -0,0 +1,5 @@ +# LP PDLP warmstart (C API) + +PDLP warmstart: use solution data from a solved LP to solve a similar problem faster. LP only (not MILP). + +Warmstart is not demonstrated in these C assets. See repo docs (e.g. `docs/cuopt/source/cuopt-c/lp-qp-milp/`) and headers for C-level warmstart support. diff --git a/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md b/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md new file mode 100644 index 0000000000..e3faa7a26e --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/milp_basic/README.md @@ -0,0 +1,12 @@ +# Simple MILP (C API) + +Same as LP but `x1` is integer. Demonstrates variable types and MIP parameters. + +**Build:** With cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o milp_simple milp_simple.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./milp_simple +``` + +**See also:** [resources/examples.md](../../resources/examples.md) for full parameter reference. diff --git a/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c b/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c new file mode 100644 index 0000000000..585b961c3e --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/milp_basic/milp_simple.c @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Simple MILP (C API): same as LP but x1 is integer + */ +#include +#include +#include +#include + +int main(void) { + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + cuopt_int_t num_variables = 2; + cuopt_int_t num_constraints = 2; + + cuopt_int_t row_offsets[] = {0, 2, 4}; + cuopt_int_t column_indices[] = {0, 1, 0, 1}; + cuopt_float_t values[] = {3.0, 4.0, 2.7, 10.1}; + + cuopt_float_t objective_coefficients[] = {-0.2, 0.1}; + cuopt_float_t constraint_upper[] = {5.4, 4.9}; + cuopt_float_t constraint_lower[] = {-CUOPT_INFINITY, -CUOPT_INFINITY}; + cuopt_float_t var_lower[] = {0.0, 0.0}; + cuopt_float_t var_upper[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + + /* x1 = INTEGER, x2 = CONTINUOUS */ + char variable_types[] = {CUOPT_INTEGER, CUOPT_CONTINUOUS}; + + cuopt_int_t status = cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, 0.0, + objective_coefficients, + row_offsets, column_indices, values, + constraint_lower, constraint_upper, + var_lower, var_upper, + variable_types, &problem + ); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + return 1; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_MIP_ABSOLUTE_TOLERANCE, 0.0001); + if (status != CUOPT_SUCCESS) { + printf("Error setting MIP absolute tolerance: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + if (status != CUOPT_SUCCESS) { + printf("Error setting MIP relative gap: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 120.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + if (solution != NULL) { + cuopt_float_t objective_value; + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } + printf("Objective: %f\n", objective_value); + + cuopt_float_t *sol = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (sol) { + status = cuOptGetPrimalSolution(solution, sol); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(sol); + goto cleanup; + } + printf("x1 (integer) = %f, x2 (continuous) = %f\n", sol[0], sol[1]); + free(sol); + } + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} diff --git a/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md b/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md new file mode 100644 index 0000000000..d51b944fe2 --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/README.md @@ -0,0 +1,12 @@ +# Production planning MILP (C API) + +Two products (A, B), resource limits (machine time, labor, material), minimum production, maximize profit. + +**Build:** With cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o milp_production milp_production.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./milp_production +``` + +**See also:** [resources/examples.md](../../resources/examples.md) for parameters and MIP options. diff --git a/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c b/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c new file mode 100644 index 0000000000..093cdc8115 --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/milp_production_planning/milp_production.c @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Production planning MILP (C API): two products, resource limits, maximize profit. + * Variables: Product_A (x1), Product_B (x2), both integer, lb 10 and 15. + * Constraints: 2*x1+x2 <= 100 (machine), x1+3*x2 <= 120 (labor), 4*x1+2*x2 <= 200 (material). + * Objective: maximize 50*x1 + 30*x2 => minimize -50*x1 - 30*x2. + */ +#include +#include +#include +#include + +int main(void) { + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + + const cuopt_int_t num_variables = 2; + const cuopt_int_t num_constraints = 3; + + /* CSR: row0 2*x1+1*x2, row1 1*x1+3*x2, row2 4*x1+2*x2 */ + cuopt_int_t row_offsets[] = {0, 2, 4, 6}; + cuopt_int_t column_indices[] = {0, 1, 0, 1, 0, 1}; + cuopt_float_t values[] = {2.0, 1.0, 1.0, 3.0, 4.0, 2.0}; + + cuopt_float_t objective_coefficients[] = {-50.0, -30.0}; + cuopt_float_t constraint_upper[] = {100.0, 120.0, 200.0}; + cuopt_float_t constraint_lower[] = {-CUOPT_INFINITY, -CUOPT_INFINITY, -CUOPT_INFINITY}; + cuopt_float_t var_lower[] = {10.0, 15.0}; + cuopt_float_t var_upper[] = {CUOPT_INFINITY, CUOPT_INFINITY}; + char variable_types[] = {CUOPT_INTEGER, CUOPT_INTEGER}; + + cuopt_int_t status = cuOptCreateRangedProblem( + num_constraints, num_variables, CUOPT_MINIMIZE, 0.0, + objective_coefficients, + row_offsets, column_indices, values, + constraint_lower, constraint_upper, + var_lower, var_upper, + variable_types, &problem + ); + if (status != CUOPT_SUCCESS) { + printf("Error creating problem: %d\n", status); + return 1; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 30.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + if (status != CUOPT_SUCCESS) { + printf("Error setting MIP relative gap: %d\n", status); + goto cleanup; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + cuopt_float_t objective_value; + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } + /* We minimized -profit, so total profit = -objective_value */ + printf("Total profit: %f\n", -objective_value); + + cuopt_float_t *sol = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (sol) { + status = cuOptGetPrimalSolution(solution, sol); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(sol); + goto cleanup; + } + printf("Product_A: %f, Product_B: %f\n", sol[0], sol[1]); + free(sol); + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} diff --git a/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md b/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md new file mode 100644 index 0000000000..efd351b9e8 --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/mps_solver/README.md @@ -0,0 +1,14 @@ +# MPS file solver (C API) + +Read and solve LP/MILP from a standard MPS file using `cuOptReadProblem`. + +**Build:** With cuOpt on `INCLUDE_PATH` and `LIB_PATH`: + +```bash +gcc -I${INCLUDE_PATH} -L${LIB_PATH} -o mps_solver mps_solver.c -lcuopt +LD_LIBRARY_PATH=${LIB_PATH}:$LD_LIBRARY_PATH ./mps_solver data/sample.mps +``` + +**Data:** `data/sample.mps` is a small LP (two variables, two constraints). Use any MPS file path as the first argument. + +**See also:** [resources/examples.md](../../resources/examples.md); repo example `docs/cuopt/source/cuopt-c/lp-qp-milp/examples/mps_file_example.c`. diff --git a/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps b/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps new file mode 100644 index 0000000000..6baeb6e524 --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/mps_solver/data/sample.mps @@ -0,0 +1,19 @@ +NAME PRODUCTION_LP +ROWS + N PROFIT + L RES_A + L RES_B +COLUMNS + PROD_X PROFIT -40.0 + PROD_X RES_A 2.0 + PROD_X RES_B 4.0 + PROD_Y PROFIT -30.0 + PROD_Y RES_A 3.0 + PROD_Y RES_B 2.0 +RHS + RHS1 RES_A 120.0 + RHS1 RES_B 100.0 +BOUNDS + LO BND1 PROD_X 0.0 + LO BND1 PROD_Y 0.0 +ENDATA diff --git a/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c b/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c new file mode 100644 index 0000000000..9aeb6f952a --- /dev/null +++ b/skills/cuopt-lp-milp-api-c/assets/mps_solver/mps_solver.c @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * Solve LP/MILP from MPS file (C API). + * Usage: mps_solver + */ +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + const char *filename = argv[1]; + + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + cuopt_int_t num_variables = 0; + cuopt_float_t *primal = NULL; + + cuopt_int_t status = cuOptReadProblem(filename, &problem); + if (status != CUOPT_SUCCESS) { + printf("Error reading MPS file: %d\n", status); + return 1; + } + + status = cuOptGetNumVariables(problem, &num_variables); + if (status != CUOPT_SUCCESS) { + printf("Error getting number of variables: %d\n", status); + goto cleanup; + } + printf("Variables: %d\n", num_variables); + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_TIME_LIMIT, 60.0); + if (status != CUOPT_SUCCESS) { + printf("Error setting time limit: %d\n", status); + goto cleanup; + } + status = cuOptSetFloatParameter(settings, CUOPT_MIP_RELATIVE_GAP, 0.01); + if (status != CUOPT_SUCCESS) { + printf("Error setting MIP relative gap: %d\n", status); + goto cleanup; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving: %d\n", status); + goto cleanup; + } + + cuopt_float_t objective_value, time; + cuopt_int_t termination_status; + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value: %d\n", status); + goto cleanup; + } + status = cuOptGetSolveTime(solution, &time); + if (status != CUOPT_SUCCESS) { + printf("Error getting solve time: %d\n", status); + goto cleanup; + } + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status: %d\n", status); + goto cleanup; + } + + printf("Termination status: %d\n", termination_status); + printf("Solve time: %f s\n", time); + printf("Objective: %f\n", objective_value); + + primal = malloc((size_t)num_variables * sizeof(cuopt_float_t)); + if (primal) { + status = cuOptGetPrimalSolution(solution, primal); + if (status != CUOPT_SUCCESS) { + printf("Error getting primal solution: %d\n", status); + free(primal); + primal = NULL; + goto cleanup; + } + printf("Primal (first 10): "); + for (cuopt_int_t i = 0; i < (num_variables < 10 ? num_variables : 10); i++) + printf("%f ", primal[i]); + if (num_variables > 10) printf("... (%d total)", (int)num_variables); + printf("\n"); + free(primal); + } + +cleanup: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return (status == CUOPT_SUCCESS) ? 0 : 1; +} diff --git a/.github/skills/cuopt-lp-milp/resources/c_api_examples.md b/skills/cuopt-lp-milp-api-c/resources/examples.md similarity index 100% rename from .github/skills/cuopt-lp-milp/resources/c_api_examples.md rename to skills/cuopt-lp-milp-api-c/resources/examples.md diff --git a/skills/cuopt-lp-milp-api-cli/SKILL.md b/skills/cuopt-lp-milp-api-cli/SKILL.md new file mode 100644 index 0000000000..cbdc1e7778 --- /dev/null +++ b/skills/cuopt-lp-milp-api-cli/SKILL.md @@ -0,0 +1,66 @@ +--- +name: cuopt-lp-milp-api-cli +version: "26.04.00" +description: LP and MILP with cuOpt — CLI only (MPS files, cuopt_cli). Use when the user is solving from MPS via command line. +--- + +# cuOpt LP/MILP — CLI + +Confirm problem type and formulation (variables, objective, constraints, variable types) before coding. + +This skill is **CLI only** (MPS input). + +## Basic usage + +```bash +# Solve LP or MILP from MPS file +cuopt_cli problem.mps + +# With options +cuopt_cli problem.mps --time-limit 120 --mip-relative-tolerance 0.01 +``` + +## Common options + +```bash +cuopt_cli --help + +# Time limit (seconds) +cuopt_cli problem.mps --time-limit 120 + +# MIP gap tolerance (stop when within X% of optimal) +cuopt_cli problem.mps --mip-relative-tolerance 0.001 + +# MIP absolute tolerance +cuopt_cli problem.mps --mip-absolute-tolerance 0.0001 + +# Presolve, iteration limit, method +cuopt_cli problem.mps --presolve --iteration-limit 10000 --method 1 +``` + +## MPS format (required sections, in order) + +1. **NAME** — problem name +2. **ROWS** — N (objective), L/G/E (constraints) +3. **COLUMNS** — variable names, row names, coefficients +4. **RHS** — right-hand side values +5. **BOUNDS** (optional) — LO, UP, FX, BV, LI, UI +6. **ENDATA** + +Integer variables: use `'MARKER' 'INTORG'` before and `'MARKER' 'INTEND'` after the integer columns. + +## Troubleshooting + +- **Failed to parse MPS** — Check ENDATA, section order (NAME, ROWS, COLUMNS, RHS, [BOUNDS], ENDATA), integer markers. +- **Infeasible** — Check constraint directions (L/G/E) and RHS values. + +## Examples + +- [assets/README.md](assets/README.md) — Build/run for sample MPS files +- [lp_simple](assets/lp_simple/) — Minimal LP (PROD_X, PROD_Y, two constraints) +- [lp_production](assets/lp_production/) — Production planning: chairs + tables, wood/labor +- [milp_facility](assets/milp_facility/) — Facility location with binary open/close + +## Getting the CLI + +CLI is included with the Python package (`cuopt`). Install via pip or conda; then run `cuopt_cli --help` to verify. diff --git a/skills/cuopt-lp-milp-api-cli/assets/README.md b/skills/cuopt-lp-milp-api-cli/assets/README.md new file mode 100644 index 0000000000..8680eb9e38 --- /dev/null +++ b/skills/cuopt-lp-milp-api-cli/assets/README.md @@ -0,0 +1,21 @@ +# Assets — sample MPS files + +Sample MPS files for use with `cuopt_cli`. Use as reference; do not edit in place. + +| File | Type | Description | +|------|------|-------------| +| [lp_production](lp_production/) | LP | Production planning: chairs + tables, wood/labor | +| [milp_facility](milp_facility/) | MILP | Facility location with binary open/close | +| [lp_simple](lp_simple/) | LP | Minimal LP (PROD_X, PROD_Y, two constraints) | + +**Run:** From each subdir or with path: `cuopt_cli lp_simple/sample.mps` (or `cuopt_cli production.mps`, etc.). See the skill for options (`--time-limit`, `--mip-relative-tolerance`, etc.). + +## Test CLI + +With conda env `cuopt` activated, from this `assets/` directory: + +```bash +cuopt_cli lp_simple/sample.mps --time-limit 10 +``` + +Use the same pattern for the other MPS files; for MILP, add e.g. `--mip-relative-gap 0.01`. diff --git a/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md b/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md new file mode 100644 index 0000000000..de4ca53043 --- /dev/null +++ b/skills/cuopt-lp-milp-api-cli/assets/lp_production/README.md @@ -0,0 +1,5 @@ +# Production LP (MPS) + +Production planning: maximize 40*chairs + 30*tables subject to wood and labor limits. + +**Run:** `cuopt_cli production.mps` or `cuopt_cli production.mps --time-limit 30` diff --git a/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps b/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps new file mode 100644 index 0000000000..40e3217b52 --- /dev/null +++ b/skills/cuopt-lp-milp-api-cli/assets/lp_production/production.mps @@ -0,0 +1,16 @@ +NAME PRODUCTION +ROWS + N PROFIT + L WOOD + L LABOR +COLUMNS + CHAIRS PROFIT -40.0 + CHAIRS WOOD 2.0 + CHAIRS LABOR 4.0 + TABLES PROFIT -30.0 + TABLES WOOD 3.0 + TABLES LABOR 2.0 +RHS + RHS1 WOOD 240.0 + RHS1 LABOR 200.0 +ENDATA diff --git a/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md b/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md new file mode 100644 index 0000000000..ed39464a77 --- /dev/null +++ b/skills/cuopt-lp-milp-api-cli/assets/lp_simple/README.md @@ -0,0 +1,5 @@ +# Minimal LP (MPS) + +Maximize 40*PROD_X + 30*PROD_Y subject to resource constraints. Two variables, two constraints. + +**Run:** `cuopt_cli sample.mps` or `cuopt_cli sample.mps --time-limit 30` diff --git a/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps b/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps new file mode 100644 index 0000000000..6baeb6e524 --- /dev/null +++ b/skills/cuopt-lp-milp-api-cli/assets/lp_simple/sample.mps @@ -0,0 +1,19 @@ +NAME PRODUCTION_LP +ROWS + N PROFIT + L RES_A + L RES_B +COLUMNS + PROD_X PROFIT -40.0 + PROD_X RES_A 2.0 + PROD_X RES_B 4.0 + PROD_Y PROFIT -30.0 + PROD_Y RES_A 3.0 + PROD_Y RES_B 2.0 +RHS + RHS1 RES_A 120.0 + RHS1 RES_B 100.0 +BOUNDS + LO BND1 PROD_X 0.0 + LO BND1 PROD_Y 0.0 +ENDATA diff --git a/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md b/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md new file mode 100644 index 0000000000..ac2a323908 --- /dev/null +++ b/skills/cuopt-lp-milp-api-cli/assets/milp_facility/README.md @@ -0,0 +1,5 @@ +# Facility location MILP (MPS) + +Facility location with binary open/close variables. Integer markers: INTORG / INTEND. + +**Run:** `cuopt_cli facility.mps --time-limit 60 --mip-relative-tolerance 0.01` diff --git a/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps b/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps new file mode 100644 index 0000000000..07f6bf3b7f --- /dev/null +++ b/skills/cuopt-lp-milp-api-cli/assets/milp_facility/facility.mps @@ -0,0 +1,27 @@ +NAME FACILITY +ROWS + N COST + G DEMAND1 + L CAP1 + L CAP2 +COLUMNS + MARKER 'MARKER' 'INTORG' + OPEN1 COST 100.0 + OPEN1 CAP1 -50.0 + OPEN2 COST 150.0 + OPEN2 CAP2 -70.0 + MARKER 'MARKER' 'INTEND' + SHIP11 COST 5.0 + SHIP11 DEMAND1 1.0 + SHIP11 CAP1 1.0 + SHIP21 COST 7.0 + SHIP21 DEMAND1 1.0 + SHIP21 CAP2 1.0 +RHS + RHS1 DEMAND1 30.0 +BOUNDS + BV BND1 OPEN1 + BV BND1 OPEN2 + LO BND1 SHIP11 0.0 + LO BND1 SHIP21 0.0 +ENDATA diff --git a/skills/cuopt-lp-milp-api-python/SKILL.md b/skills/cuopt-lp-milp-api-python/SKILL.md new file mode 100644 index 0000000000..a7cd9a59f2 --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/SKILL.md @@ -0,0 +1,226 @@ +--- +name: cuopt-lp-milp-api-python +version: "26.04.00" +description: Solve Linear Programming (LP) and Mixed-Integer Linear Programming (MILP) with the Python API. Use when the user asks about optimization with linear constraints, integer variables, scheduling, resource allocation, facility location, or production planning. +--- + +# cuOpt LP/MILP Skill + +Model and solve linear and mixed-integer linear programs using NVIDIA cuOpt's GPU-accelerated solver. + +## Before You Start + +Use a formulation summary (parameters, constraints, decisions, objective) if available; otherwise ask for decision variables, objective, and constraints. Then confirm **variable types** (see below) and **interface** (Python API recommended). + +## Choosing LP vs MILP + +**Prefer LP (all continuous variables) when the problem allows it.** LP solves faster and has stronger optimality guarantees. Use **MILP** only when the problem logically requires whole numbers or yes/no decisions. + +**Problem types that need extra care:** Multi-period planning and goal programming are easy to misinterpret. Double-check that rates and constraints apply to the right time period or priority level (AGENTS.md: verify understanding before code). + +- **Use LP** when every quantity can meaningfully be fractional: flows, proportions, rates, dollars, hours, tonnes of material, etc. +- **Use MILP** when the problem mentions **counts** of discrete entities, **yes/no** choices, or **either/or** decisions (e.g. open a facility or not, assign a person to a shift, number of trucks). + +## Integer vs continuous from wording + +Choose variable type from what the problem describes. + +| Problem wording / concept | Variable type | Examples | +|---------------------------|---------------|----------| +| **Discrete entities (counts)** | **INTEGER** | Workers, cars, trucks, machines, pilots, facilities, units to manufacture (when "units" means whole items), trainees, vehicles | +| **Yes/no or on/off** | **INTEGER** (binary, lb=0 ub=1) | Open a facility, run a machine, produce a product line, assign a person to a shift | +| **Amounts that can be fractional** | **CONTINUOUS** | Tonnes, litres, dollars, hours, kWh, proportion of capacity, flow volume, weight | +| **Rates or fractions** | **CONTINUOUS** | Utilization, percentage, share of budget | +| **Unclear** | Prefer **INTEGER** if the noun is a countable thing (a worker, a car); prefer **CONTINUOUS** if it's a measure (amount of steel, hours worked). If the problem says "whole" or "integer" or "number of", use INTEGER. | + +**Rule of thumb:** If the quantity is "how many *things*" (people, vehicles, items, sites), use **INTEGER**. If it's "how much" (mass, volume, money, time) or a rate, use **CONTINUOUS** unless the problem explicitly requires whole numbers. + +## Quick Reference: Python API + +### LP Example + +```python +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +# Create problem +problem = Problem("MyLP") + +# Decision variables +x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") +y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + +# Constraints +problem.addConstraint(2*x + 3*y <= 120, name="resource_a") +problem.addConstraint(4*x + 2*y <= 100, name="resource_b") + +# Objective +problem.setObjective(40*x + 30*y, sense=MAXIMIZE) + +# Solve +settings = SolverSettings() +settings.set_parameter("time_limit", 60) +problem.solve(settings) + +# Check status (CRITICAL: use PascalCase!) +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Objective: {problem.ObjValue}") + print(f"x = {x.getValue()}") + print(f"y = {y.getValue()}") +``` + +### MILP Example (with integer variables) + +```python +from cuopt.linear_programming.problem import Problem, CONTINUOUS, INTEGER, MINIMIZE + +problem = Problem("FacilityLocation") + +# Binary variable (integer with bounds 0-1) +open_facility = problem.addVariable(lb=0, ub=1, vtype=INTEGER, name="open") + +# Continuous variable +production = problem.addVariable(lb=0, vtype=CONTINUOUS, name="production") + +# Linking constraint: can only produce if facility is open +problem.addConstraint(production <= 1000 * open_facility, name="link") + +# Objective: fixed cost + variable cost +problem.setObjective(500*open_facility + 2*production, sense=MINIMIZE) + +# MILP-specific settings +settings = SolverSettings() +settings.set_parameter("time_limit", 120) +settings.set_parameter("mip_relative_gap", 0.01) # 1% optimality gap + +problem.solve(settings) + +# Check status +if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(f"Open facility: {open_facility.getValue() > 0.5}") + print(f"Production: {production.getValue()}") +``` + +## CRITICAL: Status Checking + +**Status values use PascalCase, NOT ALL_CAPS:** + +```python +# ✅ CORRECT +if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(problem.ObjValue) + +# ❌ WRONG - will silently fail! +if problem.Status.name == "OPTIMAL": # Never matches! + print(problem.ObjValue) +``` + +**LP Status Values:** `Optimal`, `NoTermination`, `NumericalError`, `PrimalInfeasible`, `DualInfeasible`, `IterationLimit`, `TimeLimit`, `PrimalFeasible` + +**MILP Status Values:** `Optimal`, `FeasibleFound`, `Infeasible`, `Unbounded`, `TimeLimit`, `NoTermination` + +## Common Modeling Patterns + +### Binary Selection +```python +# Select exactly k items from n +items = [problem.addVariable(lb=0, ub=1, vtype=INTEGER) for _ in range(n)] +problem.addConstraint(sum(items) == k) +``` + +### Big-M Linking +```python +# If y=1, then x <= 100; if y=0, x can be anything up to M +M = 10000 +problem.addConstraint(x <= 100 + M*(1 - y)) +``` + +### If-then "must also produce" +When the problem says *if we do X then we must also do Y*, enforce both (i) the binary link and (ii) that Y is actually produced: +```python +# y_X <= y_Y (if we do X, we must "do" Y) +problem.addConstraint(y_X <= y_Y) +# Production of Y when Y is chosen: produce at least 1 (or a minimum) when y_Y=1 +problem.addConstraint(production_Y >= 1 * y_Y) # or min_amount * y_Y +``` +Otherwise the solver can set y_Y=1 but production_Y=0, satisfying the binary link but not the intent. + +### Building large expressions +Chained `+` over many terms can hit recursion limits in the API. Prefer building objectives and constraints with **LinearExpression**: +```python +from cuopt.linear_programming.problem import LinearExpression + +# Build as list of (vars, coeffs) instead of v1*c1 + v2*c2 + ... +vars_list = [x, y, z] +coeffs_list = [1.0, 2.0, 3.0] +expr = LinearExpression(vars_list, coeffs_list, constant=0.0) +problem.addConstraint(expr <= 100) +``` +See reference models in this skill's `assets/` for examples. + +### Piecewise Linear (SOS2) +```python +# Approximate nonlinear function with breakpoints +# Use lambda variables that sum to 1, at most 2 adjacent non-zero +``` + +## Solver Settings + +```python +settings = SolverSettings() + +# Time limit +settings.set_parameter("time_limit", 60) + +# MILP gap tolerance (stop when within X% of optimal) +settings.set_parameter("mip_relative_gap", 0.01) + +# Logging +settings.set_parameter("log_to_console", 1) +``` + +## Common Issues + +| Problem | Likely Cause | Fix | +|---------|--------------|-----| +| Status never "OPTIMAL" | Using wrong case | Use `"Optimal"` not `"OPTIMAL"` | +| Integer var has fractional value | Defined as CONTINUOUS | Use `vtype=INTEGER` | +| Infeasible | Conflicting constraints | Check constraint logic | +| Unbounded | Missing bounds | Add variable bounds | +| Slow solve | Large problem | Set time limit, increase gap tolerance | +| Maximum recursion depth | Building big expr with chained `+` | Use `LinearExpression(vars_list, coeffs_list, constant)` | + +## Getting Dual Values (LP only) + +```python +if problem.Status.name == "Optimal": + constraint = problem.getConstraint("resource_a") + shadow_price = constraint.DualValue + print(f"Shadow price: {shadow_price}") +``` + +## Reference Models + +All reference models live in this skill's **`assets/`** directory. Use them as reference when building new applications; do not edit them in place. + +### Minimal / canonical examples (LP & MILP) +| Model | Type | Description | +|-------|------|-------------| +| [lp_basic](assets/lp_basic/) | LP | Minimal LP: variables, constraints, objective, solve | +| [lp_duals](assets/lp_duals/) | LP | Dual values and reduced costs | +| [lp_warmstart](assets/lp_warmstart/) | LP | PDLP warmstart for similar problems | +| [milp_basic](assets/milp_basic/) | MILP | Minimal MIP; includes incumbent callback example | +| [milp_production_planning](assets/milp_production_planning/) | MILP | Production planning with resource constraints | + +### Other reference +| Model | Type | Description | +|-------|------|-------------| +| [mps_solver](assets/mps_solver/) | LP/MILP | Solve any problem from standard MPS file format | + +**Quick command to list models:** `ls assets/` (from this skill's directory). + +## When to Escalate + +Use troubleshooting and diagnostic guidance if: +- Infeasible and you can't determine why +- Numerical issues diff --git a/skills/cuopt-lp-milp-api-python/assets/README.md b/skills/cuopt-lp-milp-api-python/assets/README.md new file mode 100644 index 0000000000..0b9a727e4b --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/README.md @@ -0,0 +1,12 @@ +# Assets — reference models + +LP/MILP reference implementations. Use as reference when building new applications; do not edit in place. + +| Model | Type | +|-------|------| +| lp_basic | LP | +| lp_duals | LP | +| lp_warmstart | LP | +| milp_basic | MILP | +| milp_production_planning | MILP | +| mps_solver | LP/MILP | diff --git a/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md b/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md new file mode 100644 index 0000000000..4c06f2ded6 --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/lp_basic/README.md @@ -0,0 +1,7 @@ +# Minimal LP + +Basic linear program: continuous variables, linear constraints, maximize objective. + +**Problem:** Maximize x + y subject to x + y ≤ 10, x − y ≥ 0, x, y ≥ 0. + +**Run:** `python model.py` diff --git a/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py b/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py new file mode 100644 index 0000000000..d81c6a749d --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/lp_basic/model.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Minimal LP: variables, constraints, objective, solve. + +Problem: + Maximize: x + y + Subject to: x + y <= 10, x - y >= 0, x, y >= 0 +""" + +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + problem = Problem("Simple LP") + x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") + y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + problem.addConstraint(x + y <= 10, name="c1") + problem.addConstraint(x - y >= 0, name="c2") + problem.setObjective(x + y, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_parameter("time_limit", 60) + problem.solve(settings) + + if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Objective: {problem.ObjValue}") + print(f"x = {x.getValue()}, y = {y.getValue()}") + else: + print(f"Status: {problem.Status.name}") + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md b/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md new file mode 100644 index 0000000000..f0eb9bcf8b --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/lp_duals/README.md @@ -0,0 +1,7 @@ +# LP Duals and Reduced Costs + +Retrieve dual values (shadow prices) and reduced costs after solving an LP. + +**Problem:** Minimize 3x + 2y + 5z subject to x + y + z = 4, 2x + y + z = 5, x, y, z ≥ 0. + +**Run:** `python model.py` diff --git a/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py b/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py new file mode 100644 index 0000000000..4fa6a50a5b --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/lp_duals/model.py @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +LP with dual values and reduced costs. + +Problem: + Minimize: 3x + 2y + 5z + Subject to: x + y + z = 4, 2x + y + z = 5, x, y, z >= 0 +""" + +from cuopt.linear_programming.problem import Problem, MINIMIZE + + +def main(): + problem = Problem("min_dual_rc") + x = problem.addVariable(lb=0.0, name="x") + y = problem.addVariable(lb=0.0, name="y") + z = problem.addVariable(lb=0.0, name="z") + problem.addConstraint(x + y + z == 4.0, name="c1") + problem.addConstraint(2.0 * x + y + z == 5.0, name="c2") + problem.setObjective(3.0 * x + 2.0 * y + 5.0 * z, sense=MINIMIZE) + problem.solve() + + if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Objective: {problem.ObjValue}") + for v in problem.getVariables(): + print( + f"{v.VariableName} = {v.Value}, ReducedCost = {v.ReducedCost}" + ) + for c in problem.getConstraints(): + print(f"{c.ConstraintName} DualValue = {c.DualValue}") + else: + print(f"Status: {problem.Status.name}") + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md b/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md new file mode 100644 index 0000000000..000e7a42fa --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/README.md @@ -0,0 +1,5 @@ +# LP PDLP Warmstart + +Use warmstart data from a solved LP to solve a similar problem faster. LP only (not MILP). + +**Run:** `python model.py` diff --git a/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py b/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py new file mode 100644 index 0000000000..b0e893118f --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/lp_warmstart/model.py @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +PDLP warmstart: solve a similar LP faster by reusing solution context. + +Warmstart is for LP only, not MILP. +""" + +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MAXIMIZE +from cuopt.linear_programming.solver.solver_parameters import ( + CUOPT_METHOD, + CUOPT_PDLP_SOLVER_MODE, +) +from cuopt.linear_programming.solver_settings import ( + SolverSettings, + SolverMethod, + PDLPSolverMode, +) + + +def main(): + print("=== Problem 1 ===") + problem = Problem("LP1") + x = problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") + y = problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + problem.addConstraint(4 * x + 10 * y <= 130, name="c1") + problem.addConstraint(8 * x - 3 * y >= 40, name="c2") + problem.setObjective(2 * x + y, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_parameter(CUOPT_METHOD, SolverMethod.PDLP) + settings.set_parameter(CUOPT_PDLP_SOLVER_MODE, PDLPSolverMode.Stable2) + problem.solve(settings) + print(f"Objective: {problem.ObjValue}") + + warmstart_data = problem.getWarmstartData() + print("\n=== Problem 2 (with warmstart) ===") + new_problem = Problem("LP2") + x = new_problem.addVariable(lb=0, vtype=CONTINUOUS, name="x") + y = new_problem.addVariable(lb=0, vtype=CONTINUOUS, name="y") + new_problem.addConstraint(4 * x + 10 * y <= 100, name="c1") + new_problem.addConstraint(8 * x - 3 * y >= 50, name="c2") + new_problem.setObjective(2 * x + y, sense=MAXIMIZE) + settings.set_pdlp_warm_start_data(warmstart_data) + new_problem.solve(settings) + if new_problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Objective: {new_problem.ObjValue}") + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md b/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md new file mode 100644 index 0000000000..45362da09b --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/milp_basic/README.md @@ -0,0 +1,10 @@ +# Minimal MILP + +Basic mixed-integer program: integer variables with bounds, linear constraints. + +**Problem:** Maximize 5x + 3y subject to 2x + 4y ≥ 230, 3x + 2y ≤ 190, 10 ≤ y ≤ 50, x, y integer. + +- **model.py** — solve and print solution. +- **incumbent_callback.py** — same problem with a callback that prints intermediate (incumbent) solutions during solve. + +**Run:** `python model.py` or `python incumbent_callback.py` diff --git a/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py b/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py new file mode 100644 index 0000000000..49e533291c --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/milp_basic/incumbent_callback.py @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Same MILP as model.py but with a callback to receive incumbent (intermediate) solutions. +MILP only; not for LP. +""" + +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 + + +class IncumbentCallback(GetSolutionCallback): + def __init__(self, user_data): + super().__init__() + self.n_callbacks = 0 + self.user_data = user_data + + def get_solution(self, solution, solution_cost, solution_bound, user_data): + self.n_callbacks += 1 + sol = ( + solution.tolist() + if hasattr(solution, "tolist") + else list(solution) + ) + cost = float(solution_cost[0]) + print(f"Incumbent {self.n_callbacks}: {sol}, cost: {cost:.2f}") + + +def main(): + problem = Problem("Incumbent Example") + x = problem.addVariable(vtype=INTEGER) + y = problem.addVariable(vtype=INTEGER) + problem.addConstraint(2 * x + 4 * y >= 230) + problem.addConstraint(3 * x + 2 * y <= 190) + problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE) + + user_data = {"source": "incumbent_callback"} + settings = SolverSettings() + settings.set_mip_callback(IncumbentCallback(user_data), user_data) + settings.set_parameter(CUOPT_TIME_LIMIT, 30) + problem.solve(settings) + + print(f"Status: {problem.Status.name}, Objective: {problem.ObjValue}") + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py b/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py new file mode 100644 index 0000000000..5c0bf88e15 --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/milp_basic/model.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Minimal MILP: integer variables with bounds, linear constraints. + +Problem: + Maximize: 5x + 3y + Subject to: 2x + 4y >= 230, 3x + 2y <= 190, 10 <= y <= 50, x, y integer +""" + +from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + problem = Problem("Simple MIP") + x = problem.addVariable(vtype=INTEGER, name="V_x") + y = problem.addVariable(lb=10, ub=50, vtype=INTEGER, name="V_y") + problem.addConstraint(2 * x + 4 * y >= 230, name="C1") + problem.addConstraint(3 * x + 2 * y <= 190, name="C2") + problem.setObjective(5 * x + 3 * y, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_parameter("time_limit", 60) + problem.solve(settings) + + if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(f"Objective: {problem.ObjValue}") + print(f"x = {x.getValue()}, y = {y.getValue()}") + else: + print(f"Status: {problem.Status.name}") + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md b/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md new file mode 100644 index 0000000000..42a2a1a9d5 --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/README.md @@ -0,0 +1,5 @@ +# Production Planning (MILP) + +Two products (A, B), resource limits (machine time, labor, material), minimum production, maximize profit. + +**Run:** `python model.py` diff --git a/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py b/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py new file mode 100644 index 0000000000..72ded8164d --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/milp_production_planning/model.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Production planning: two products, resource limits (machine, labor, material), maximize profit. +""" + +from cuopt.linear_programming.problem import Problem, INTEGER, MAXIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + + +def main(): + problem = Problem("Production Planning") + x1 = problem.addVariable(lb=10, vtype=INTEGER, name="Product_A") + x2 = problem.addVariable(lb=15, vtype=INTEGER, name="Product_B") + problem.addConstraint(2 * x1 + x2 <= 100, name="Machine_Time") + problem.addConstraint(x1 + 3 * x2 <= 120, name="Labor_Hours") + problem.addConstraint(4 * x1 + 2 * x2 <= 200, name="Material") + problem.setObjective(50 * x1 + 30 * x2, sense=MAXIMIZE) + + settings = SolverSettings() + settings.set_parameter("time_limit", 30) + problem.solve(settings) + + if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(f"Product A: {x1.getValue()}, Product B: {x2.getValue()}") + print(f"Total profit: {problem.ObjValue}") + else: + print(f"Status: {problem.Status.name}") + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md b/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md new file mode 100644 index 0000000000..f18f4f549e --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/mps_solver/README.md @@ -0,0 +1,88 @@ +# MPS File Solver + +Read and solve LP/MILP problems from standard MPS files using cuOpt. + +## Problem Description + +MPS (Mathematical Programming System) is a standard file format for representing linear and mixed-integer programming problems. This model demonstrates how to: + +1. Load an MPS file using `Problem.readMPS()` (static method) +2. Solve the problem using cuOpt's GPU-accelerated solver +3. Extract and display the solution + +This is useful when you have optimization problems in standard MPS format from other solvers, modeling tools, or benchmark libraries like MIPLIB. + +## MPS File Format + +MPS is a column-oriented format with sections: + +``` +NAME problem_name +ROWS + N OBJ (objective row) + L CON1 (≤ constraint) + G CON2 (≥ constraint) + E CON3 (= constraint) +COLUMNS + X1 OBJ 1.0 + X1 CON1 2.0 + X2 OBJ 2.0 + X2 CON1 3.0 +RHS + RHS CON1 10.0 +BOUNDS + LO BND X1 0.0 + UP BND X1 5.0 +ENDATA +``` + +## Usage + +```bash +# Solve the sample problem +python model.py + +# Solve a custom MPS file +python model.py --file path/to/problem.mps + +# With time limit +python model.py --file problem.mps --time-limit 120 +``` + +## Model Characteristics + +- **Type**: LP or MILP (detected from MPS file) +- **Input**: Standard MPS file format +- **Output**: Solution values, objective, status + +## Sample Problem + +The included `data/air05.mps` is a MIPLIB benchmark (airline crew scheduling): + +- **Variables**: 7,195 (binary) +- **Constraints**: 426 +- **Known optimal**: 26,374 +- **Typical solve time**: ~2 seconds + +## Key API Usage + +```python +from cuopt.linear_programming.problem import Problem +from cuopt.linear_programming.solver_settings import SolverSettings + +# Load MPS file (static method - returns Problem object) +problem = Problem.readMPS("path/to/problem.mps") + +# Configure and solve +settings = SolverSettings() +settings.set_parameter("time_limit", 60) +problem.solve(settings) + +# Check solution +if problem.Status.name in ["Optimal", "FeasibleFound"]: + print(f"Objective: {problem.ObjValue}") +``` + +## Source + +Based on cuOpt's built-in MPS support via `Problem.readMPS()`. diff --git a/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md b/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md new file mode 100644 index 0000000000..67266feea8 --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/README.md @@ -0,0 +1,82 @@ +# MPS Solver Data + +This directory contains MPS files for testing. + +## Included Files + +### air05.mps (MIPLIB Benchmark) + +An airline crew scheduling problem from the MIPLIB benchmark library. + +| Property | Value | +|----------|-------| +| Type | Binary Integer Program | +| Variables | 7,195 (all binary) | +| Constraints | 426 | +| Non-zeros | 52,121 | +| Known Optimal | 26,374 | + +**Source**: https://miplib.zib.de/instance_details_air05.html + +**Problem**: Given flight legs and possible crew pairings, find the minimum-cost +set of pairings that covers all flight legs (set covering problem). + +## MPS File Format + +MPS (Mathematical Programming System) is a standard format for LP/MILP problems. + +### Sections + +| Section | Purpose | +|---------|---------| +| NAME | Problem name | +| ROWS | Constraint and objective definitions | +| COLUMNS | Variable coefficients in each row | +| RHS | Right-hand side values for constraints | +| BOUNDS | Variable bounds and types | +| ENDATA | End of file marker | + +### Row Types + +| Type | Meaning | +|------|---------| +| N | Objective function (no constraint) | +| L | Less than or equal (≤) | +| G | Greater than or equal (≥) | +| E | Equality (=) | + +### Bound Types + +| Type | Meaning | +|------|---------| +| LO | Lower bound | +| UP | Upper bound | +| FX | Fixed value (lb = ub) | +| FR | Free variable (-∞ to +∞) | +| BV | Binary variable (0 or 1) | +| UI | Upper bound, integer | +| LI | Lower bound, integer | + +## Adding Custom MPS Files + +```bash +python model.py --file path/to/your/problem.mps +``` + +## Standard Test Problem Sources + +- [MIPLIB](https://miplib.zib.de/) - Mixed Integer Programming Library +- [Netlib LP](https://www.netlib.org/lp/) - Classic LP test problems +- [NEOS](https://neos-server.org/neos/) - Network-Enabled Optimization System + +## Creating MPS Files + +cuOpt can export problems to MPS format: + +```python +from cuopt.linear_programming.problem import Problem + +problem = Problem("MyProblem") +# ... define variables, constraints, objective ... +problem.writeMPS("output.mps") +``` diff --git a/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps b/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps new file mode 100644 index 0000000000..6baeb6e524 --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/mps_solver/data/sample.mps @@ -0,0 +1,19 @@ +NAME PRODUCTION_LP +ROWS + N PROFIT + L RES_A + L RES_B +COLUMNS + PROD_X PROFIT -40.0 + PROD_X RES_A 2.0 + PROD_X RES_B 4.0 + PROD_Y PROFIT -30.0 + PROD_Y RES_A 3.0 + PROD_Y RES_B 2.0 +RHS + RHS1 RES_A 120.0 + RHS1 RES_B 100.0 +BOUNDS + LO BND1 PROD_X 0.0 + LO BND1 PROD_Y 0.0 +ENDATA diff --git a/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py b/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py new file mode 100644 index 0000000000..42a7490398 --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py @@ -0,0 +1,283 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +MPS File Solver using cuOpt Python API + +Read and solve LP/MILP problems from standard MPS files using +cuOpt's built-in readMPS method. + +Default benchmark: air05.mps (airline crew scheduling from MIPLIB) +- Best known optimal: 26,374 +""" + +import os +import gzip +import urllib.request +from typing import Optional + +from cuopt.linear_programming.problem import Problem +from cuopt.linear_programming.solver_settings import SolverSettings + + +# MIPLIB benchmark URL +AIR05_URL = "https://miplib.zib.de/WebData/instances/air05.mps.gz" +AIR05_OPTIMAL = 26374 # Best known optimal solution + + +def download_air05(data_dir: str) -> str: + """Download air05.mps from MIPLIB if not present.""" + mps_file = os.path.join(data_dir, "air05.mps") + + if os.path.exists(mps_file): + return mps_file + + os.makedirs(data_dir, exist_ok=True) + gz_file = os.path.join(data_dir, "air05.mps.gz") + + print("Downloading air05.mps from MIPLIB...") + urllib.request.urlretrieve(AIR05_URL, gz_file) + + # Decompress + print("Decompressing...") + with gzip.open(gz_file, "rb") as f_in: + with open(mps_file, "wb") as f_out: + f_out.write(f_in.read()) + + # Clean up + os.remove(gz_file) + print(f"Downloaded: {mps_file}") + + return mps_file + + +def solve_mps( + filepath: str, + time_limit: float = 60.0, + mip_gap: float = 0.01, + verbose: bool = True, +) -> tuple: + """ + Solve an LP/MILP problem from an MPS file. + + Parameters + ---------- + filepath : str + Path to the MPS file + time_limit : float + Solver time limit in seconds + mip_gap : float + MIP relative gap tolerance + verbose : bool + Print solver output + + Returns + ------- + tuple + (problem, solution_dict) or (problem, None) if no solution + """ + + # Read MPS file directly (static method returns Problem object) + problem = Problem.readMPS(filepath) + + print(f"Loaded MPS file: {filepath}") + print(f"Variables: {problem.NumVariables}") + print(f"Constraints: {problem.NumConstraints}") + print(f"Is MIP: {problem.IsMIP}") + + # Solver settings + settings = SolverSettings() + settings.set_parameter("time_limit", time_limit) + settings.set_parameter("log_to_console", verbose) + settings.set_parameter("mip_relative_gap", mip_gap) + + # Solve + print("\nSolving...") + problem.solve(settings) + + # Extract solution + status = problem.Status.name + print(f"\nStatus: {status}") + + if status in ["Optimal", "FeasibleFound", "PrimalFeasible"]: + solution = { + "status": status, + "objective": problem.ObjValue, + "num_variables": problem.NumVariables, + "num_constraints": problem.NumConstraints, + "is_mip": problem.IsMIP, + "mip_gap": mip_gap, + } + + # Get variable values (use getVariables() for MPS-loaded problems) + var_values = {} + try: + variables = problem.getVariables() + for var in variables: + val = var.getValue() + if abs(val) > 1e-6: # Only include non-zero values + var_values[var.Name] = val + except (AttributeError, Exception): + # For MPS problems, variable access may be limited + pass + + solution["variables"] = var_values + return problem, solution + else: + return problem, None + + +def compare_gaps( + filepath: str, + time_limit: float = 120.0, + known_optimal: Optional[float] = None, +) -> dict: + """ + Compare solutions at different MIP gap tolerances. + + Parameters + ---------- + filepath : str + Path to the MPS file + time_limit : float + Solver time limit per run + known_optimal : float, optional + Known optimal objective value. If provided, results include + "gap_to_optimal" (percent above optimal). Omit for generic MPS files. + + Returns + ------- + dict + Results for each gap tolerance + """ + gaps = [0.01, 0.001] # 1% and 0.1% + results = {} + + for gap in gaps: + print(f"\n{'=' * 60}") + print(f"Solving with MIP gap = {gap * 100}%") + print(f"{'=' * 60}") + + problem, solution = solve_mps( + filepath=filepath, time_limit=time_limit, mip_gap=gap, verbose=True + ) + + if solution: + results[gap] = { + "objective": solution["objective"], + "status": solution["status"], + } + if known_optimal is not None: + results[gap]["gap_to_optimal"] = ( + (solution["objective"] - known_optimal) + / known_optimal + * 100 + ) + else: + results[gap] = {"objective": None, "status": "No solution"} + + return results + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Solve LP/MILP from MPS file") + parser.add_argument( + "--file", type=str, default=None, help="Path to MPS file" + ) + parser.add_argument( + "--time-limit", type=float, default=60.0, help="Solver time limit" + ) + parser.add_argument( + "--mip-gap", type=float, default=0.01, help="MIP gap tolerance" + ) + parser.add_argument( + "--compare", action="store_true", help="Compare 1% vs 0.1% gap" + ) + parser.add_argument( + "--known-optimal", + type=float, + default=None, + help="Known optimal objective value (enables gap-to-optimal reporting)", + ) + args = parser.parse_args() + + print("=" * 60) + print("MPS File Solver using cuOpt") + print("=" * 60) + + # Determine MPS file to use + script_dir = os.path.dirname(os.path.abspath(__file__)) + data_dir = os.path.join(script_dir, "data") + + if args.file: + mps_file = args.file + else: + # Download air05.mps if not present + mps_file = download_air05(data_dir) + + # Use known optimal only when explicitly set or when using default air05 + known_optimal = args.known_optimal + if known_optimal is None and mps_file.endswith("air05.mps"): + known_optimal = AIR05_OPTIMAL + + if args.compare: + # Compare different gap tolerances + print(f"\nComparing MIP gap tolerances on: {mps_file}") + if known_optimal is not None: + print(f"Best known optimal: {known_optimal}") + + results = compare_gaps( + mps_file, time_limit=args.time_limit, known_optimal=known_optimal + ) + + print() + print("=" * 60) + print("COMPARISON SUMMARY") + print("=" * 60) + if known_optimal is not None: + print(f"Best known optimal: {known_optimal}") + print() + header = f"{'Gap Tolerance':<15} {'Objective':<15}" + if known_optimal is not None: + header += f" {'Gap to Optimal':<15}" + print(header) + print("-" * (45 if known_optimal is None else 60)) + + for gap, result in sorted(results.items()): + if result["objective"] is not None: + line = f"{gap * 100:.1f}%{'':<12} {result['objective']:<15.0f}" + if known_optimal is not None: + line += f" {result['gap_to_optimal']:.2f}%" + print(line) + else: + print(f"{gap * 100:.1f}%{'':<12} {'No solution':<15}") + else: + # Single solve + print(f"\nMPS File: {mps_file}") + print(f"Time Limit: {args.time_limit}s") + print(f"MIP Gap: {args.mip_gap * 100}%") + print() + + problem, solution = solve_mps( + filepath=mps_file, + time_limit=args.time_limit, + mip_gap=args.mip_gap, + verbose=True, + ) + + if solution: + print() + print("=" * 60) + print("SOLUTION") + print("=" * 60) + print(f"Status: {solution['status']}") + print(f"Objective Value: {solution['objective']:.0f}") + if known_optimal is not None: + print(f"Best Known Optimal: {known_optimal}") + print( + f"Gap to Optimal: {(solution['objective'] - known_optimal) / known_optimal * 100:.2f}%" + ) + else: + print("\nNo feasible solution found.") diff --git a/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md b/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md new file mode 100644 index 0000000000..4100dea6b2 --- /dev/null +++ b/skills/cuopt-lp-milp-api-python/assets/mps_solver/results.md @@ -0,0 +1,90 @@ +# MPS Solver Results + +## Problem: air05.mps (MIPLIB benchmark) + +**Description:** Airline crew scheduling - set partitioning problem + +### Problem Characteristics +- **Variables:** 7195 (all binary) +- **Constraints:** 426 +- **Nonzeros:** 52121 +- **Best Known Optimal:** 26374 + +--- + +## Gap Tolerance Comparison + +Comparing different MIP relative gap tolerances to show trade-off between solution quality and solve time. + +### Run Configuration +- **Time Limit:** 60 seconds +- **cuOpt Version:** 26.2.0 +- **Device:** Quadro RTX 8000 (47.24 GiB VRAM) +- **CPU:** AMD Ryzen Threadripper PRO 3975WX (32 cores) + +### Results Summary + +| Gap Tolerance | Objective | Gap to Optimal | Solve Time | Nodes Explored | +|--------------|-----------|----------------|------------|----------------| +| 0.1% | **26374** | 0.00% | 8.42s | 386 | +| 1.0% | 26491 | 0.44% | 3.23s | 328 | + +### Key Observations + +1. **Tighter gap finds optimal**: The 0.1% gap tolerance found the exact best-known optimal solution (26374) +2. **Trade-off**: The looser 1.0% gap converged faster (3.2s vs 8.4s) but with 0.44% suboptimality +3. **Both are fast**: cuOpt solved this 7195-variable MILP in under 10 seconds + +--- + +## Detailed Solver Output (0.1% gap) + +``` +Solving a problem with 426 constraints, 7195 variables (7195 integers), and 52121 nonzeros + +Presolve removed: 90 constraints, 1116 variables, 16171 nonzeros +Presolved problem: 336 constraints, 6079 variables, 35950 nonzeros + +Root relaxation objective +2.58776093e+04 + +Strong branching using 7 threads and 222 fractional variables +Explored 386 nodes in 7.73s. + +Optimal solution found within relative MIP gap tolerance (1.0e-03) +Solution objective: 26374.000000 +relative_mip_gap 0.000992 +total_solve_time 8.421934 +``` + +--- + +## Detailed Solver Output (1.0% gap) + +``` +Solving a problem with 426 constraints, 7195 variables (7195 integers), and 52121 nonzeros + +Presolve removed: 90 constraints, 1116 variables, 16171 nonzeros +Presolved problem: 336 constraints, 6079 variables, 35950 nonzeros + +Root relaxation objective +2.58776093e+04 + +Strong branching using 63 threads and 222 fractional variables +Explored 328 nodes in 1.09s. + +Optimal solution found within relative MIP gap tolerance (1.0e-02) +Solution objective: 26491.000000 +relative_mip_gap 0.009669 +total_solve_time 3.233650 +``` + +--- + +## Usage + +```bash +# Default: download air05.mps and solve with comparison +python model.py --compare --time-limit 60 + +# Solve custom MPS file +python model.py --file path/to/problem.mps --time-limit 300 --mip-gap 0.001 +``` diff --git a/skills/cuopt-qp-api-c/SKILL.md b/skills/cuopt-qp-api-c/SKILL.md new file mode 100644 index 0000000000..bc1efb63d3 --- /dev/null +++ b/skills/cuopt-qp-api-c/SKILL.md @@ -0,0 +1,19 @@ +--- +name: cuopt-qp-api-c +version: "26.04.00" +description: Quadratic Programming (QP) with cuOpt — C API. Use when the user is embedding QP in C/C++. +--- + +# cuOpt QP — C API + +Confirm the objective has squared or cross terms (QP); if purely linear, use LP/MILP. QP must be minimization. + +This skill is **C only**. + +QP uses the same cuOpt C library as LP/MILP; the API extends to quadratic objectives. Use the same include/lib paths and build pattern as for LP/MILP C (see this skill's assets/README.md); then use the QP-specific creation/solve calls from the cuOpt C headers. + +**Reference:** This skill's [assets/README.md](assets/README.md) — build pattern and repo QP C API docs. + +## Escalate + +If the problem is linear, use LP/MILP. For contribution or build-from-source, see the developer skill. diff --git a/skills/cuopt-qp-api-c/assets/README.md b/skills/cuopt-qp-api-c/assets/README.md new file mode 100644 index 0000000000..b3fcea0586 --- /dev/null +++ b/skills/cuopt-qp-api-c/assets/README.md @@ -0,0 +1,9 @@ +# Assets — QP C API reference + +QP uses the same cuOpt C library as LP/MILP; the API extends to quadratic objectives. + +**Build and run:** Use the same include/lib paths and link steps as for LP/MILP C (see repository documentation for build and examples). Then use the QP-specific creation and solve calls from the cuOpt C headers. + +**Repo docs:** `docs/cuopt/source/cuopt-c/lp-qp-milp/` for QP C API and examples; parameter constants and CSR format are in the same doc tree. + +No standalone QP C source files are included in this skill; adapt the LP/MILP C build pattern for quadratic objective APIs from the headers. diff --git a/skills/cuopt-qp-api-cli/SKILL.md b/skills/cuopt-qp-api-cli/SKILL.md new file mode 100644 index 0000000000..5f8a8e848a --- /dev/null +++ b/skills/cuopt-qp-api-cli/SKILL.md @@ -0,0 +1,37 @@ +--- +name: cuopt-qp-api-cli +version: "26.04.00" +description: QP with cuOpt — CLI (e.g. cuopt_cli with QP-capable input). Use when the user is solving QP from the command line. +--- + +# cuOpt QP — CLI + +QP objectives must be **minimization**. For maximization, negate the objective. + +This skill is **CLI only** for QP. + +## QP via CLI + +cuOpt CLI supports QP (quadratic objectives). Use the same `cuopt_cli` tool; input format and options may extend the LP/MILP MPS workflow to allow quadratic terms (see repo docs or `cuopt_cli --help` for QP-specific options). + +## Basic usage + +```bash +# Solve QP (syntax may match or extend LP/MILP CLI; check --help) +cuopt_cli problem.mps + +# With time limit +cuopt_cli problem.mps --time-limit 60 +``` + +Check `cuopt_cli --help` and the repository documentation (e.g. `docs/cuopt/source/cuopt-cli/`) for QP file format and any QP-specific flags. + +**Reference:** This skill's [assets/README.md](assets/README.md) — CLI options and repo docs. + +## Getting the CLI + +CLI is included with the Python package (`cuopt`). Install via pip or conda; then run `cuopt_cli --help` to verify. + +## Escalate + +If the problem is linear, use LP/MILP CLI. For contribution or build-from-source, see the developer skill. diff --git a/skills/cuopt-qp-api-cli/assets/README.md b/skills/cuopt-qp-api-cli/assets/README.md new file mode 100644 index 0000000000..040f03efad --- /dev/null +++ b/skills/cuopt-qp-api-cli/assets/README.md @@ -0,0 +1,9 @@ +# Assets — QP CLI reference + +QP can be solved via `cuopt_cli` when the input format supports quadratic objectives (see repo docs and `cuopt_cli --help` for QP-specific options and file format). + +**Important:** QP objectives must be **minimization**. For maximization, negate the objective. + +**Repo docs:** `docs/cuopt/source/cuopt-cli/` for QP file format and flags. For sample MPS files and CLI options (time limit, tolerances), see the repository documentation. + +No sample QP input files are included here; check documentation for quadratic term format. diff --git a/skills/cuopt-qp-api-python/SKILL.md b/skills/cuopt-qp-api-python/SKILL.md new file mode 100644 index 0000000000..b85b9e3db2 --- /dev/null +++ b/skills/cuopt-qp-api-python/SKILL.md @@ -0,0 +1,61 @@ +--- +name: cuopt-qp-api-python +version: "26.04.00" +description: Quadratic Programming (QP) with cuOpt — Python API only (beta). Use when the user is building or solving QP in Python. +--- + +# cuOpt QP — Python API (beta) + +Confirm the objective has squared or cross terms (QP); if purely linear, use LP/MILP. QP must be minimization. + +This skill is **Python only**. **QP is beta.** + +## CRITICAL: MINIMIZE only + +```python +# ❌ WRONG +problem.setObjective(x*x + y*y, sense=MAXIMIZE) + +# ✅ CORRECT — negate for maximization +problem.setObjective(-(x*x + y*y), sense=MINIMIZE) +``` + +## Portfolio Example + +```python +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +problem = Problem("Portfolio") +x1 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_a") +x2 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_b") +x3 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_c") +r1, r2, r3 = 0.12, 0.08, 0.05 # expected returns (12%, 8%, 5%) +problem.setObjective( + 0.04*x1*x1 + 0.02*x2*x2 + 0.01*x3*x3 + 0.02*x1*x2 + 0.01*x1*x3 + 0.016*x2*x3, + sense=MINIMIZE +) +problem.addConstraint(x1 + x2 + x3 == 1, name="budget") +problem.addConstraint(r1*x1 + r2*x2 + r3*x3 >= 0.08, name="min_return") +problem.solve(SolverSettings()) +``` + +## Status (PascalCase) + +```python +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(problem.ObjValue) +``` + +## Debugging + +**Diagnostic:** `print(f"Actual status: '{problem.Status.name}'")`. For numerical issues, check Q is PSD and variables are scaled. + +## Examples + +- [examples.md](resources/examples.md) — portfolio, least squares, maximization workaround +- **Reference models:** This skill's `assets/` — [portfolio](assets/portfolio/), [least_squares](assets/least_squares/), [maximization_workaround](assets/maximization_workaround/). See [assets/README.md](assets/README.md). + +## Escalate + +If the problem is linear (no squared or cross terms), use LP/MILP. For contribution or build-from-source, see the developer skill. diff --git a/skills/cuopt-qp-api-python/assets/README.md b/skills/cuopt-qp-api-python/assets/README.md new file mode 100644 index 0000000000..3c696f07b6 --- /dev/null +++ b/skills/cuopt-qp-api-python/assets/README.md @@ -0,0 +1,11 @@ +# Assets — reference QP models + +QP reference implementations (Python, beta). Use as reference when building new applications; do not edit in place. + +| Model | Description | +|-------|-------------| +| [portfolio](portfolio/) | Minimize portfolio variance; budget and min-return constraints | +| [least_squares](least_squares/) | Minimize (x-3)² + (y-4)² (closest point) | +| [maximization_workaround](maximization_workaround/) | Maximize quadratic via minimize -f(x) | + +**Run:** From each subdir, `python model.py`. QP is **beta** and supports **MINIMIZE** only. See [resources/examples.md](../resources/examples.md) for more. diff --git a/skills/cuopt-qp-api-python/assets/least_squares/README.md b/skills/cuopt-qp-api-python/assets/least_squares/README.md new file mode 100644 index 0000000000..5592ff2ac0 --- /dev/null +++ b/skills/cuopt-qp-api-python/assets/least_squares/README.md @@ -0,0 +1,5 @@ +# Least squares (QP) + +Minimize (x-3)² + (y-4)² — find point closest to (3, 4). Unconstrained quadratic. + +**Run:** `python model.py` diff --git a/skills/cuopt-qp-api-python/assets/least_squares/model.py b/skills/cuopt-qp-api-python/assets/least_squares/model.py new file mode 100644 index 0000000000..822d6397d2 --- /dev/null +++ b/skills/cuopt-qp-api-python/assets/least_squares/model.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Least squares: minimize (x-3)² + (y-4)². Solution should be x=3, y=4. +""" + +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +problem = Problem("LeastSquares") + +x = problem.addVariable(lb=-100, ub=100, vtype=CONTINUOUS, name="x") +y = problem.addVariable(lb=-100, ub=100, vtype=CONTINUOUS, name="y") + +problem.setObjective(x * x + y * y - 6 * x - 8 * y + 25, sense=MINIMIZE) + +problem.solve(SolverSettings()) + +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"x = {x.getValue():.4f}") + print(f"y = {y.getValue():.4f}") +else: + print(f"Status: {problem.Status.name}") diff --git a/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md b/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md new file mode 100644 index 0000000000..bcd0f2c3c1 --- /dev/null +++ b/skills/cuopt-qp-api-python/assets/maximization_workaround/README.md @@ -0,0 +1,5 @@ +# Maximization workaround (QP) + +QP supports MINIMIZE only. To maximize f(x), minimize -f(x); then negate the optimal value. + +**Run:** `python model.py` diff --git a/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py b/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py new file mode 100644 index 0000000000..e18aa613d8 --- /dev/null +++ b/skills/cuopt-qp-api-python/assets/maximization_workaround/model.py @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Maximize -x² + 4x (max at x=2) by minimizing x² - 4x; then report -objective. +""" + +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE + +problem = Problem("MaxWorkaround") + +x = problem.addVariable(lb=0, ub=10, vtype=CONTINUOUS, name="x") +problem.setObjective(x * x - 4 * x, sense=MINIMIZE) + +problem.solve() + +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"x = {x.getValue():.4f}") + print(f"Minimized value = {problem.ObjValue:.4f}") + print(f"Original maximum = {-problem.ObjValue:.4f}") +else: + print(f"Status: {problem.Status.name}") diff --git a/skills/cuopt-qp-api-python/assets/portfolio/README.md b/skills/cuopt-qp-api-python/assets/portfolio/README.md new file mode 100644 index 0000000000..cf2173a455 --- /dev/null +++ b/skills/cuopt-qp-api-python/assets/portfolio/README.md @@ -0,0 +1,7 @@ +# Portfolio optimization (QP) + +Minimize portfolio variance (risk) subject to fully invested (sum x = 1) and minimum return. Three assets; Q must be PSD. + +**Run:** `python model.py` + +**Note:** QP is beta; objective must be MINIMIZE. diff --git a/skills/cuopt-qp-api-python/assets/portfolio/model.py b/skills/cuopt-qp-api-python/assets/portfolio/model.py new file mode 100644 index 0000000000..0196efdcf8 --- /dev/null +++ b/skills/cuopt-qp-api-python/assets/portfolio/model.py @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Portfolio: minimize variance x'Qx subject to sum(x)=1, r'x >= target, x >= 0. +QP is beta; MUST use MINIMIZE. +""" + +from cuopt.linear_programming.problem import Problem, CONTINUOUS, MINIMIZE +from cuopt.linear_programming.solver_settings import SolverSettings + +problem = Problem("Portfolio") + +x1 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_a") +x2 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_b") +x3 = problem.addVariable(lb=0, ub=1, vtype=CONTINUOUS, name="stock_c") + +r1, r2, r3 = 0.12, 0.08, 0.05 +target_return = 0.08 + +problem.setObjective( + 0.04 * x1 * x1 + + 0.02 * x2 * x2 + + 0.01 * x3 * x3 + + 0.02 * x1 * x2 + + 0.01 * x1 * x3 + + 0.016 * x2 * x3, + sense=MINIMIZE, +) +problem.addConstraint(x1 + x2 + x3 == 1, name="budget") +problem.addConstraint( + r1 * x1 + r2 * x2 + r3 * x3 >= target_return, name="min_return" +) + +settings = SolverSettings() +settings.set_parameter("time_limit", 60) +problem.solve(settings) + +if problem.Status.name in ["Optimal", "PrimalFeasible"]: + print(f"Portfolio variance: {problem.ObjValue:.6f}") + print(f"Std dev: {problem.ObjValue**0.5:.4f}") + print(f" Stock A: {x1.getValue() * 100:.2f}%") + print(f" Stock B: {x2.getValue() * 100:.2f}%") + print(f" Stock C: {x3.getValue() * 100:.2f}%") + print( + f"Expected return: {(r1 * x1.getValue() + r2 * x2.getValue() + r3 * x3.getValue()) * 100:.2f}%" + ) +else: + print(f"Status: {problem.Status.name}") diff --git a/.github/skills/cuopt-qp/resources/python_examples.md b/skills/cuopt-qp-api-python/resources/examples.md similarity index 100% rename from .github/skills/cuopt-qp/resources/python_examples.md rename to skills/cuopt-qp-api-python/resources/examples.md diff --git a/skills/cuopt-routing-api-python/SKILL.md b/skills/cuopt-routing-api-python/SKILL.md new file mode 100644 index 0000000000..d8bf736f8f --- /dev/null +++ b/skills/cuopt-routing-api-python/SKILL.md @@ -0,0 +1,101 @@ +--- +name: cuopt-routing-api-python +version: "26.04.00" +description: Vehicle routing (VRP, TSP, PDP) with cuOpt — Python API only. Use when the user is building or solving routing in Python. +--- + +# cuOpt Routing — Python API + +Confirm problem type (TSP, VRP, PDP) and data (locations, orders, fleet, constraints) before coding. + +This skill is **Python only**. Routing has no C API in cuOpt. + +## Minimal VRP Example + +```python +import cudf +from cuopt import routing + +cost_matrix = cudf.DataFrame([...], dtype="float32") +dm = routing.DataModel(n_locations=4, n_fleet=2, n_orders=3) +dm.add_cost_matrix(cost_matrix) +dm.set_order_locations(cudf.Series([1, 2, 3], dtype="int32")) +solution = routing.Solve(dm, routing.SolverSettings()) + +if solution.get_status() == 0: + solution.display_routes() +``` + +## Adding Constraints + +```python +# Time windows +dm.add_transit_time_matrix(transit_time_matrix) +dm.set_order_time_windows(earliest_series, latest_series) + +# Capacities +dm.add_capacity_dimension("weight", demand_series, capacity_series) +dm.set_order_service_times(service_times) +dm.set_vehicle_locations(start_locations, end_locations) +dm.set_vehicle_time_windows(earliest_start, latest_return) + +# Pickup-delivery pairs +dm.set_pickup_delivery_pairs(pickup_indices, delivery_indices) + +# Precedence +dm.add_order_precedence(node_id=2, preceding_nodes=np.array([0, 1])) +``` + +## Solution Checking + +```python +status = solution.get_status() # 0=SUCCESS, 1=FAIL, 2=TIMEOUT, 3=EMPTY +if status == 0: + route_df = solution.get_route() + total_cost = solution.get_total_objective() +else: + print(solution.get_error_message()) + print(solution.get_infeasible_orders().to_list()) +``` + +## Data Types (use explicit dtypes) + +```python +cost_matrix = cost_matrix.astype("float32") +order_locations = cudf.Series([...], dtype="int32") +demand = cudf.Series([...], dtype="int32") +``` + +## Solver Settings + +```python +ss = routing.SolverSettings() +ss.set_time_limit(30) +ss.set_verbose_mode(True) +ss.set_error_logging_mode(True) +``` + +## Common Issues + +| Problem | Fix | +|---------|-----| +| Empty solution | Widen time windows or check travel times | +| Infeasible orders | Increase fleet or capacity | +| Status != 0 with time windows | Add `add_transit_time_matrix()` | +| Wrong cost | Check cost_matrix is symmetric | + +## Debugging + +**When status != 0:** `print(solution.get_error_message())` and `print(solution.get_infeasible_orders().to_list())` to see which orders are infeasible. + +**Data types:** Use explicit dtypes (float32, int32) for matrices and series to avoid silent errors. + +## Examples + +- [examples.md](resources/examples.md) — VRP, PDP, multi-depot +- [server_examples.md](resources/server_examples.md) — REST client (curl, Python) +- **Reference models:** This skill's `assets/` — [vrp_basic](assets/vrp_basic/), [pdp_basic](assets/pdp_basic/). See [assets/README.md](assets/README.md). + +## Escalate + +For contribution or build-from-source, see the developer skill. diff --git a/skills/cuopt-routing-api-python/assets/README.md b/skills/cuopt-routing-api-python/assets/README.md new file mode 100644 index 0000000000..6b7a8091c9 --- /dev/null +++ b/skills/cuopt-routing-api-python/assets/README.md @@ -0,0 +1,10 @@ +# Assets — reference routing models + +Routing reference implementations (Python). Use as reference when building new applications; do not edit in place. + +| Model | Type | Description | +|-------|------|-------------| +| [vrp_basic](vrp_basic/) | VRP | Minimal VRP: 4 locations, 1 vehicle, 3 orders | +| [pdp_basic](pdp_basic/) | PDP | Pickup-delivery pairs, capacity dimension | + +**Run:** From each subdir, `python model.py` (requires cuOpt and cudf). See [resources/examples.md](../resources/examples.md) for more patterns (time windows, multi-depot). diff --git a/skills/cuopt-routing-api-python/assets/pdp_basic/README.md b/skills/cuopt-routing-api-python/assets/pdp_basic/README.md new file mode 100644 index 0000000000..64e345bb7c --- /dev/null +++ b/skills/cuopt-routing-api-python/assets/pdp_basic/README.md @@ -0,0 +1,7 @@ +# Pickup-Delivery (PDP) + +2 pickup-delivery pairs (4 orders), 2 vehicles. Pickup must occur before delivery; capacity dimension. + +**Run:** `python model.py` + +**See also:** [resources/examples.md](../../resources/examples.md) for more PDP and VRP patterns. diff --git a/skills/cuopt-routing-api-python/assets/pdp_basic/model.py b/skills/cuopt-routing-api-python/assets/pdp_basic/model.py new file mode 100644 index 0000000000..d85ec5329b --- /dev/null +++ b/skills/cuopt-routing-api-python/assets/pdp_basic/model.py @@ -0,0 +1,56 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +PDP: 2 pickup-delivery pairs, 2 vehicles. Pickup before delivery; capacity dimension. +""" + +import cudf +from cuopt import routing + +cost_matrix = cudf.DataFrame( + [ + [0, 10, 20, 30, 40], + [10, 0, 15, 25, 35], + [20, 15, 0, 10, 20], + [30, 25, 10, 0, 15], + [40, 35, 20, 15, 0], + ], + dtype="float32", +) + +transit_time_matrix = cost_matrix.copy(deep=True) +n_fleet = 2 +n_orders = 4 + +order_locations = cudf.Series([1, 2, 3, 4], dtype="int32") +pickup_indices = cudf.Series([0, 2]) +delivery_indices = cudf.Series([1, 3]) +demand = cudf.Series([10, -10, 15, -15], dtype="int32") +vehicle_capacity = cudf.Series([50, 50], dtype="int32") + +dm = routing.DataModel( + n_locations=cost_matrix.shape[0], + n_fleet=n_fleet, + n_orders=n_orders, +) +dm.add_cost_matrix(cost_matrix) +dm.add_transit_time_matrix(transit_time_matrix) +dm.set_order_locations(order_locations) +dm.add_capacity_dimension("load", demand, vehicle_capacity) +dm.set_pickup_delivery_pairs(pickup_indices, delivery_indices) +dm.set_vehicle_locations( + cudf.Series([0, 0], dtype="int32"), + cudf.Series([0, 0], dtype="int32"), +) + +ss = routing.SolverSettings() +ss.set_time_limit(10) +solution = routing.Solve(dm, ss) + +print(f"Status: {solution.get_status()}") +if solution.get_status() == 0: + solution.display_routes() + print(f"Total cost: {solution.get_total_objective()}") +else: + print(solution.get_error_message()) diff --git a/skills/cuopt-routing-api-python/assets/vrp_basic/README.md b/skills/cuopt-routing-api-python/assets/vrp_basic/README.md new file mode 100644 index 0000000000..cdb2890269 --- /dev/null +++ b/skills/cuopt-routing-api-python/assets/vrp_basic/README.md @@ -0,0 +1,7 @@ +# Minimal VRP + +4 locations (depot 0 + 3 customers), 1 vehicle, 3 orders. Cost matrix only; no time windows or capacity. + +**Run:** `python model.py` + +**See also:** [resources/examples.md](../../resources/examples.md) for VRP with time windows, capacity, and multi-depot. diff --git a/skills/cuopt-routing-api-python/assets/vrp_basic/model.py b/skills/cuopt-routing-api-python/assets/vrp_basic/model.py new file mode 100644 index 0000000000..165f6afc1e --- /dev/null +++ b/skills/cuopt-routing-api-python/assets/vrp_basic/model.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Minimal VRP: 4 locations, 1 vehicle, 3 orders. Cost matrix only. +""" + +import cudf +from cuopt import routing + +cost_matrix = cudf.DataFrame( + [ + [0, 10, 15, 20], + [10, 0, 12, 18], + [15, 12, 0, 10], + [20, 18, 10, 0], + ], + dtype="float32", +) + +dm = routing.DataModel(n_locations=4, n_fleet=1, n_orders=3) +dm.add_cost_matrix(cost_matrix) +dm.set_order_locations(cudf.Series([1, 2, 3], dtype="int32")) + +solution = routing.Solve(dm, routing.SolverSettings()) + +if solution.get_status() == 0: + solution.display_routes() + print(f"Total cost: {solution.get_total_objective()}") +else: + print(f"Status: {solution.get_status()}", solution.get_error_message()) diff --git a/.github/skills/cuopt-routing/resources/python_examples.md b/skills/cuopt-routing-api-python/resources/examples.md similarity index 96% rename from .github/skills/cuopt-routing/resources/python_examples.md rename to skills/cuopt-routing-api-python/resources/examples.md index 4ff694d35a..ee402bb314 100644 --- a/.github/skills/cuopt-routing/resources/python_examples.md +++ b/skills/cuopt-routing-api-python/resources/examples.md @@ -28,7 +28,7 @@ cost_matrix = cudf.DataFrame([ transit_time_matrix = cost_matrix.copy(deep=True) # Order data (customers 1-5) -order_locations = cudf.Series([1, 2, 3, 4, 5]) # Location indices for orders +order_locations = cudf.Series([1, 2, 3, 4, 5], dtype="int32") # Location indices for orders # Demand at each customer (single capacity dimension) demand = cudf.Series([20, 30, 25, 15, 35], dtype="int32") @@ -130,7 +130,7 @@ n_fleet = 2 n_orders = 4 # 2 pickup-delivery pairs = 4 orders # Orders: pickup at loc 1 -> deliver at loc 2, pickup at loc 3 -> deliver at loc 4 -order_locations = cudf.Series([1, 2, 3, 4]) +order_locations = cudf.Series([1, 2, 3, 4], dtype="int32") # Pickup and delivery pairs (indices into order array) # Order 0 (pickup) pairs with Order 1 (delivery) @@ -191,7 +191,7 @@ cost_matrix = cudf.DataFrame([ dm = routing.DataModel(n_locations=4, n_fleet=1, n_orders=3) dm.add_cost_matrix(cost_matrix) -dm.set_order_locations(cudf.Series([1, 2, 3])) +dm.set_order_locations(cudf.Series([1, 2, 3], dtype="int32")) solution = routing.Solve(dm, routing.SolverSettings()) @@ -219,7 +219,7 @@ n_fleet = 2 dm = routing.DataModel(n_locations=6, n_fleet=n_fleet, n_orders=4) dm.add_cost_matrix(cost_matrix) -dm.set_order_locations(cudf.Series([2, 3, 4, 5])) +dm.set_order_locations(cudf.Series([2, 3, 4, 5], dtype="int32")) # Vehicle 0 starts/ends at depot 0, Vehicle 1 at depot 1 dm.set_vehicle_locations( diff --git a/.github/skills/cuopt-routing/resources/server_examples.md b/skills/cuopt-routing-api-python/resources/server_examples.md similarity index 100% rename from .github/skills/cuopt-routing/resources/server_examples.md rename to skills/cuopt-routing-api-python/resources/server_examples.md diff --git a/skills/cuopt-server-api-python/SKILL.md b/skills/cuopt-server-api-python/SKILL.md new file mode 100644 index 0000000000..b340e9883f --- /dev/null +++ b/skills/cuopt-server-api-python/SKILL.md @@ -0,0 +1,80 @@ +--- +name: cuopt-server-api-python +version: "26.04.00" +description: cuOpt REST server — start server, endpoints, Python/curl client examples. Use when the user is deploying or calling the REST API. +--- + +# cuOpt Server — Deploy and client (Python/curl) + +This skill covers **starting the server** and **client examples** (curl, Python). Server has no separate C API (clients can be any language). + +## Start server + +```bash +# Development +python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000 + +# Docker +docker run --gpus all -d -p 8000:8000 -e CUOPT_SERVER_PORT=8000 \ + nvidia/cuopt:latest-cuda12.9-py3.13 +``` + +## Verify + +```bash +curl http://localhost:8000/cuopt/health +``` + +## Workflow + +1. POST to `/cuopt/request` → get `reqId` +2. Poll `/cuopt/solution/{reqId}` until solution ready +3. Parse response + +## Python client (routing) + +```python +import requests, time +SERVER = "http://localhost:8000" +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} +payload = { + "cost_matrix_data": {"data": {"0": [[0,10,15],[10,0,12],[15,12,0]]}}, + "travel_time_matrix_data": {"data": {"0": [[0,10,15],[10,0,12],[15,12,0]]}}, + "task_data": {"task_locations": [1, 2], "demand": [[10, 20]], "task_time_windows": [[0,100],[0,100]], "service_times": [5, 5]}, + "fleet_data": {"vehicle_locations": [[0, 0]], "capacities": [[50]], "vehicle_time_windows": [[0, 200]]}, + "solver_config": {"time_limit": 5} +} +r = requests.post(f"{SERVER}/cuopt/request", json=payload, headers=HEADERS) +req_id = r.json()["reqId"] +# Poll: GET /cuopt/solution/{req_id} +``` + +## Terminology: REST vs Python API + +| Python API | REST | +|------------|------| +| order_locations | task_locations | +| set_order_time_windows() | task_time_windows | +| service_times | service_times | + +Use `travel_time_matrix_data` (not transit_time_matrix_data). Capacities: `[[50, 50]]` not `[[50], [50]]`. + +## Debugging (422 / payload) + +**Validation errors:** Check field names against OpenAPI (`/cuopt.yaml`). Common mistakes: `transit_time_matrix_data` → `travel_time_matrix_data`; capacities per dimension `[[50, 50]]` not per vehicle `[[50], [50]]`. Capture `reqId` and response body for failed requests. + +## Runnable assets + +Run from each asset directory (server must be running; scripts exit 0 if server unreachable). All use Python `requests`: + +- [assets/vrp_simple/](assets/vrp_simple/) — Basic VRP (no time windows) +- [assets/vrp_basic/](assets/vrp_basic/) — VRP with time windows +- [assets/pdp_basic/](assets/pdp_basic/) — Pickup and delivery +- [assets/lp_basic/](assets/lp_basic/) — LP via REST (CSR format) +- [assets/milp_basic/](assets/milp_basic/) — MILP via REST + +See [assets/README.md](assets/README.md) for overview. + +## Escalate + +For contribution or build-from-source, see the developer skill. diff --git a/skills/cuopt-server-api-python/assets/README.md b/skills/cuopt-server-api-python/assets/README.md new file mode 100644 index 0000000000..1389f3eb7b --- /dev/null +++ b/skills/cuopt-server-api-python/assets/README.md @@ -0,0 +1,14 @@ +# Server API Python — runnable assets + +REST client examples (Python requests). Each runs against a cuOpt server; if the server is not reachable, the script exits 0 (skip). + +| Asset | Description | +|---------------|-------------| +| `vrp_simple/` | Basic VRP (no time windows) | +| `vrp_basic/` | VRP with time windows | +| `pdp_basic/` | Pickup and delivery (pairs) | +| `lp_basic/` | LP (CSR format) | +| `milp_basic/` | MILP (integer + continuous variables) | + +Start server: `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000` +Env: `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/skills/cuopt-server-api-python/assets/lp_basic/README.md b/skills/cuopt-server-api-python/assets/lp_basic/README.md new file mode 100644 index 0000000000..34c10fb350 --- /dev/null +++ b/skills/cuopt-server-api-python/assets/lp_basic/README.md @@ -0,0 +1,10 @@ +# LP via REST (maximize 40x + 30y) + +Submit an LP to the cuOpt server (CSR format) and poll for the solution. + +**Requires:** cuOpt server running (e.g. `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000`). + +**Run:** `python client.py` +If the server is not reachable, the script exits 0 (skip). + +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/skills/cuopt-server-api-python/assets/lp_basic/client.py b/skills/cuopt-server-api-python/assets/lp_basic/client.py new file mode 100644 index 0000000000..bca7b15295 --- /dev/null +++ b/skills/cuopt-server-api-python/assets/lp_basic/client.py @@ -0,0 +1,84 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: LP request (maximize 40x + 30y s.t. 2x+3y<=240, 4x+2y<=200). Requires cuOpt server running. + +Usage: python client.py + Set CUOPT_SERVER_URL (default http://localhost:8000). Exits 0 if server unreachable (e.g. in CI without server). +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) + sys.exit(0) + + payload = { + "csr_constraint_matrix": { + "offsets": [0, 2, 4], + "indices": [0, 1, 0, 1], + "values": [2.0, 3.0, 4.0, 2.0], + }, + "constraint_bounds": { + "upper_bounds": [240.0, 200.0], + "lower_bounds": ["ninf", "ninf"], + }, + "objective_data": { + "coefficients": [40.0, 30.0], + }, + "variable_bounds": { + "upper_bounds": ["inf", "inf"], + "lower_bounds": [0.0, 0.0], + }, + "maximize": True, + "solver_config": { + "time_limit": 60, + }, + } + + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(30): + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) + result = response.json() + + if "response" in result: + print(f"Status: {result['response'].get('status')}") + print(f"Objective: {result['response'].get('objective_value')}") + print(f"Solution: {result['response'].get('primal_solution')}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-server-api-python/assets/milp_basic/README.md b/skills/cuopt-server-api-python/assets/milp_basic/README.md new file mode 100644 index 0000000000..e490840557 --- /dev/null +++ b/skills/cuopt-server-api-python/assets/milp_basic/README.md @@ -0,0 +1,6 @@ +# MILP via REST + +Same problem as LP (maximize 40x + 30y, 2x+3y≤240, 4x+2y≤200) with `variable_types`: first variable integer, second continuous. + +**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). Variable types: `continuous`, `integer`, `binary`. diff --git a/skills/cuopt-server-api-python/assets/milp_basic/client.py b/skills/cuopt-server-api-python/assets/milp_basic/client.py new file mode 100644 index 0000000000..1c18de60e9 --- /dev/null +++ b/skills/cuopt-server-api-python/assets/milp_basic/client.py @@ -0,0 +1,82 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: MILP (same constraints as LP but variable_types: integer, continuous). +Requires cuOpt server running. Exits 0 if server unreachable. +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) + sys.exit(0) + + payload = { + "csr_constraint_matrix": { + "offsets": [0, 2, 4], + "indices": [0, 1, 0, 1], + "values": [2.0, 3.0, 4.0, 2.0], + }, + "constraint_bounds": { + "upper_bounds": [240.0, 200.0], + "lower_bounds": ["ninf", "ninf"], + }, + "objective_data": {"coefficients": [40.0, 30.0]}, + "variable_bounds": { + "upper_bounds": ["inf", "inf"], + "lower_bounds": [0.0, 0.0], + }, + "variable_types": ["integer", "continuous"], + "maximize": True, + "solver_config": { + "time_limit": 120, + "tolerances": {"mip_relative_gap": 0.01}, + }, + } + + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(60): + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) + result = response.json() + + if "response" in result: + print(f"Status: {result['response'].get('status')}") + print(f"Objective: {result['response'].get('objective_value')}") + print(f"Solution: {result['response'].get('primal_solution')}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-server-api-python/assets/pdp_basic/README.md b/skills/cuopt-server-api-python/assets/pdp_basic/README.md new file mode 100644 index 0000000000..ca6c174c6c --- /dev/null +++ b/skills/cuopt-server-api-python/assets/pdp_basic/README.md @@ -0,0 +1,6 @@ +# Pickup and delivery (PDP) + +Pickup-delivery pairs: (0,1) and (2,3). Pickup must be visited before the corresponding delivery. + +**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/skills/cuopt-server-api-python/assets/pdp_basic/client.py b/skills/cuopt-server-api-python/assets/pdp_basic/client.py new file mode 100644 index 0000000000..cad4d3bdb1 --- /dev/null +++ b/skills/cuopt-server-api-python/assets/pdp_basic/client.py @@ -0,0 +1,97 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: Pickup and delivery (PDP). Pairs (0,1) and (2,3); pickup before delivery. +Requires cuOpt server running. Exits 0 if server unreachable. +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) + sys.exit(0) + + payload = { + "cost_matrix_data": { + "data": { + "0": [ + [0, 10, 20, 30, 40], + [10, 0, 15, 25, 35], + [20, 15, 0, 10, 20], + [30, 25, 10, 0, 15], + [40, 35, 20, 15, 0], + ] + } + }, + "travel_time_matrix_data": { + "data": { + "0": [ + [0, 10, 20, 30, 40], + [10, 0, 15, 25, 35], + [20, 15, 0, 10, 20], + [30, 25, 10, 0, 15], + [40, 35, 20, 15, 0], + ] + } + }, + "task_data": { + "task_locations": [1, 2, 3, 4], + "demand": [[10, -10, 15, -15]], + "pickup_and_delivery_pairs": [[0, 1], [2, 3]], + }, + "fleet_data": { + "vehicle_locations": [[0, 0]], + "capacities": [[50]], + }, + "solver_config": {"time_limit": 10}, + } + + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(30): + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) + result = response.json() + + if "response" in result: + solver_response = result["response"].get("solver_response", {}) + print(f"Status: {solver_response.get('status')}") + print(f"Cost: {solver_response.get('solution_cost')}") + if "vehicle_data" in solver_response: + for vid, vdata in solver_response["vehicle_data"].items(): + print(f"Vehicle {vid}: {vdata.get('route', [])}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-server-api-python/assets/vrp_basic/README.md b/skills/cuopt-server-api-python/assets/vrp_basic/README.md new file mode 100644 index 0000000000..84b46f7240 --- /dev/null +++ b/skills/cuopt-server-api-python/assets/vrp_basic/README.md @@ -0,0 +1,10 @@ +# VRP with time windows (REST client) + +Submit a VRP with time windows to the cuOpt server and poll for the solution. + +**Requires:** cuOpt server running (e.g. `python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000`). + +**Run:** `python client.py` +If the server is not reachable, the script exits 0 (skip). + +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/skills/cuopt-server-api-python/assets/vrp_basic/client.py b/skills/cuopt-server-api-python/assets/vrp_basic/client.py new file mode 100644 index 0000000000..9285eb05cd --- /dev/null +++ b/skills/cuopt-server-api-python/assets/vrp_basic/client.py @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: VRP with time windows. Requires cuOpt server running. + +Usage: python client.py + Set CUOPT_SERVER_URL (default http://localhost:8000). Exits 0 if server unreachable (e.g. in CI without server). +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) + sys.exit(0) + + payload = { + "cost_matrix_data": { + "data": { + "0": [ + [0, 10, 15, 20, 25], + [10, 0, 12, 18, 22], + [15, 12, 0, 10, 15], + [20, 18, 10, 0, 8], + [25, 22, 15, 8, 0], + ] + } + }, + "travel_time_matrix_data": { + "data": { + "0": [ + [0, 10, 15, 20, 25], + [10, 0, 12, 18, 22], + [15, 12, 0, 10, 15], + [20, 18, 10, 0, 8], + [25, 22, 15, 8, 0], + ] + } + }, + "task_data": { + "task_locations": [1, 2, 3, 4], + "demand": [[20, 30, 25, 15]], + "task_time_windows": [[0, 50], [10, 60], [20, 70], [0, 80]], + "service_times": [5, 5, 5, 5], + }, + "fleet_data": { + "vehicle_locations": [[0, 0], [0, 0]], + "capacities": [[100, 100]], + "vehicle_time_windows": [[0, 200], [0, 200]], + }, + "solver_config": {"time_limit": 10}, + } + + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(30): + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) + result = response.json() + + if "response" in result: + solver_response = result["response"].get("solver_response", {}) + print(f"Status: {solver_response.get('status')}") + print(f"Cost: {solver_response.get('solution_cost')}") + if "vehicle_data" in solver_response: + for vid, vdata in solver_response["vehicle_data"].items(): + print(f"Vehicle {vid}: {vdata.get('route', [])}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-server-api-python/assets/vrp_simple/README.md b/skills/cuopt-server-api-python/assets/vrp_simple/README.md new file mode 100644 index 0000000000..f9de54a24c --- /dev/null +++ b/skills/cuopt-server-api-python/assets/vrp_simple/README.md @@ -0,0 +1,6 @@ +# Basic VRP (no time windows) + +Simple VRP: 4 locations, 3 tasks, 2 vehicles. No time windows. + +**Requires:** cuOpt server running. **Run:** `python client.py` (exits 0 if server unreachable). +**Env:** `CUOPT_SERVER_URL` (default `http://localhost:8000`). diff --git a/skills/cuopt-server-api-python/assets/vrp_simple/client.py b/skills/cuopt-server-api-python/assets/vrp_simple/client.py new file mode 100644 index 0000000000..35f37f5c72 --- /dev/null +++ b/skills/cuopt-server-api-python/assets/vrp_simple/client.py @@ -0,0 +1,95 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +REST client: Basic VRP (no time windows). 4 locations, 3 tasks, 2 vehicles. +Requires cuOpt server running. Exits 0 if server unreachable. +""" + +import os +import sys +import time + +import requests + +SERVER = os.environ.get("CUOPT_SERVER_URL", "http://localhost:8000") +HEADERS = {"Content-Type": "application/json", "CLIENT-VERSION": "custom"} + + +def server_ok(): + try: + r = requests.get(f"{SERVER}/cuopt/health", timeout=2) + return r.status_code == 200 + except Exception: + return False + + +def main(): + if not server_ok(): + print( + "Server not running, skipping. Start with: python -m cuopt_server.cuopt_service --ip 0.0.0.0 --port 8000" + ) + sys.exit(0) + + payload = { + "cost_matrix_data": { + "data": { + "0": [ + [0, 10, 15, 20], + [10, 0, 12, 18], + [15, 12, 0, 10], + [20, 18, 10, 0], + ] + } + }, + "travel_time_matrix_data": { + "data": { + "0": [ + [0, 10, 15, 20], + [10, 0, 12, 18], + [15, 12, 0, 10], + [20, 18, 10, 0], + ] + } + }, + "task_data": { + "task_locations": [1, 2, 3], + "demand": [[10, 15, 20]], + "service_times": [5, 5, 5], + }, + "fleet_data": { + "vehicle_locations": [[0, 0], [0, 0]], + "capacities": [[50, 50]], + }, + "solver_config": {"time_limit": 5}, + } + + response = requests.post( + f"{SERVER}/cuopt/request", json=payload, headers=HEADERS + ) + response.raise_for_status() + req_id = response.json()["reqId"] + print(f"Submitted: {req_id}") + + for _ in range(30): + response = requests.get( + f"{SERVER}/cuopt/solution/{req_id}", headers=HEADERS + ) + result = response.json() + + if "response" in result: + solver_response = result["response"].get("solver_response", {}) + print(f"Status: {solver_response.get('status')}") + print(f"Cost: {solver_response.get('solution_cost')}") + if "vehicle_data" in solver_response: + for vid, vdata in solver_response["vehicle_data"].items(): + print(f"Vehicle {vid}: {vdata.get('route', [])}") + return + time.sleep(1) + + print("Timeout waiting for solution") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/skills/cuopt-server-common/SKILL.md b/skills/cuopt-server-common/SKILL.md new file mode 100644 index 0000000000..f23c9c4a5f --- /dev/null +++ b/skills/cuopt-server-common/SKILL.md @@ -0,0 +1,46 @@ +--- +name: cuopt-server-common +version: "26.04.00" +description: cuOpt REST server — what it does and how requests flow. Domain concepts; no deploy or client code. +--- + +# cuOpt Server (common) + +Domain concepts for the cuOpt REST server. No deploy commands or client code here. + +## What the server does + +- Accepts optimization requests (routing, LP, MILP) over HTTP. +- Returns a request ID; solution is obtained by polling with that ID. +- Does **not** support QP via REST. + +## Problem types supported + +| Problem type | Supported | +|--------------|:---------:| +| Routing | ✓ | +| LP | ✓ | +| MILP | ✓ | +| QP | ✗ | + +## Request flow (conceptual) + +1. Client sends problem data in the required schema (matrices, tasks, fleet, solver config). +2. Server returns a `reqId`. +3. Client polls the solution endpoint with `reqId` until the job completes. +4. Response contains status and, on success, solution (routes, objective, primal values, etc.). + +## Required questions (deployment and usage) + +Ask these if not already clear: + +1. **Problem type** — Routing or LP/MILP? (QP not available.) +2. **Deployment** — Local, Docker, Kubernetes, or cloud? +3. **Client** — Which language or tool will call the API (e.g. Python, curl, another service)? + +## Key endpoints (conceptual) + +- Health check. +- Submit request (POST). +- Get solution by request ID (GET). +- OpenAPI spec (e.g. for payload format). diff --git a/.github/skills/cuopt-user-rules/SKILL.md b/skills/cuopt-user-rules/SKILL.md similarity index 67% rename from .github/skills/cuopt-user-rules/SKILL.md rename to skills/cuopt-user-rules/SKILL.md index a6c870c10d..7ca291ac9b 100644 --- a/.github/skills/cuopt-user-rules/SKILL.md +++ b/skills/cuopt-user-rules/SKILL.md @@ -1,6 +1,7 @@ --- name: cuopt-user-rules -description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before any cuOpt user task (routing, LP/MILP, QP, debugging, installation, server). Covers handling incomplete questions, clarifying data requirements, verifying understanding, and running commands safely. +version: "26.04.00" +description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before any cuOpt user task (routing, LP/MILP, QP, installation, server). Covers handling incomplete questions, clarifying data requirements, verifying understanding, and running commands safely. --- # cuOpt User Rules @@ -9,14 +10,14 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before --- -## 1. Ask Before Assuming +## Ask Before Assuming **Always clarify ambiguous requirements before implementing:** -- What interface? (Python API / REST Server / C API / CLI) -- What problem type? (Routing / LP / MILP / QP) -- What constraints matter? (time windows, capacities, etc.) -- What output format? (solution values, routes, visualization) +- What **language/interface**? +- What problem type? +- What constraints matter? +- What output format? **Skip asking only if:** - User explicitly stated the requirement @@ -24,7 +25,7 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before --- -## 2. Handle Incomplete Questions +## Handle Incomplete Questions **If a question seems partial or incomplete, ask follow-up questions:** @@ -42,7 +43,7 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before --- -## 3. Clarify Data Requirements +## Clarify Data Requirements **Before generating examples, ask about data:** @@ -69,7 +70,7 @@ description: Base behavior rules for using NVIDIA cuOpt. Read this FIRST before --- -## 4. MUST Verify Understanding +## MUST Verify Understanding **Before writing substantial code, you MUST confirm your understanding:** @@ -84,7 +85,7 @@ Is this correct?" --- -## 5. Follow Requirements Exactly +## Follow Requirements Exactly - Use the **exact** variable names, formats, and structures the user specifies - Don't add features the user didn't ask for @@ -93,23 +94,7 @@ Is this correct?" --- -## 6. Read Examples First - -Before generating code, **read the canonical example** for that problem type: - -| Problem | Example Location | -|---------|------------------| -| Routing | `docs/cuopt/source/cuopt-python/routing/examples/` | -| LP/MILP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/` | -| QP | `docs/cuopt/source/cuopt-python/lp-qp-milp/examples/simple_qp_example.py` | -| Server | `docs/cuopt/source/cuopt_spec.yaml` (OpenAPI) | -| C API | `docs/cuopt/source/cuopt-c/lp-qp-milp/examples/` | - -**Don't invent API patterns.** Copy from examples. - ---- - -## 7. Check Results +## Check Results After providing a solution, guide the user to verify: @@ -117,11 +102,20 @@ After providing a solution, guide the user to verify: - **Constraint satisfaction**: Are all constraints met? - **Objective value**: Is it reasonable for the problem? +**Always end with a Result summary** that includes at least: +- Solver status (e.g. Optimal, FeasibleFound, SUCCESS). +- **Objective value with highlight** — easy to spot (bold or code block). Example: **Objective value (min total cost):** <value> or `Objective value: `. +- Briefly what the objective represents (e.g. total cost, total profit). + +Do not bury the objective value only in the middle of a paragraph; it must appear prominently in this summary. Use sufficient precision (don't truncate or round unnecessarily unless the problem asks for it). + +**Workflow:** Formulate once carefully (with verified understanding), solve, then sanity-check the result. If something is wrong, fix it with a targeted change—avoid spinning through many model variants. Decide, implement, verify, then move on. + Provide diagnostic code snippets when helpful. --- -## 8. Check Environment First +## Check Environment First **Before writing code or suggesting installation, verify the user's setup:** @@ -129,16 +123,16 @@ Provide diagnostic code snippets when helpful. - "Do you have cuOpt installed? If so, which interface?" - "What environment are you using? (local GPU, cloud, Docker, server, etc.)" -2. **Different packages for different interfaces:** +2. **Different packages by language/interface:** - | Interface | Package | Check | - |-----------|---------|-------| - | Python API | `cuopt` (pip/conda) | `import cuopt` | - | C API | `libcuopt` (conda/system) | `find libcuopt.so` or header check | + | Language / Interface | Package | Check | + |----------------------|---------|-------| + | **Python** | `cuopt` (pip/conda) | `import cuopt` | + | **C** | `libcuopt` (conda/system) | `find libcuopt.so` or header check | | REST Server | `cuopt-server` or Docker | `curl /cuopt/health` | | CLI | `cuopt` package includes CLI | `cuopt_cli --help` | - **Note:** `libcuopt` (C library) installed via conda is NOT available through Python import — they are separate packages. + **Note:** `libcuopt` (C library) is separate from the Python package — C and Python use different installs. 3. **If not installed, ask how they want to access:** - "Would you like help installing cuOpt, or do you have access another way?" @@ -167,14 +161,14 @@ Provide diagnostic code snippets when helpful. --- -## 9. Ask Before Running +## Ask Before Running **Do not execute commands or code without explicit permission:** | Action | Rule | |--------|------| | Shell commands | Show command, explain what it does, ask "Should I run this?" | -| Package installs | **Never** run `pip`, `conda`, `apt` without asking first | +| Package installs | **Never** run installs yourself — give the exact command, user runs it (see below). | | Examples/scripts | Show the code first, ask "Would you like me to run this?" | | File writes | Explain what will change, ask before writing | @@ -184,7 +178,7 @@ Provide diagnostic code snippets when helpful. --- -## 10. No Privileged Operations +## No Privileged Operations **Never do these without explicit user request AND confirmation:** @@ -196,6 +190,18 @@ Provide diagnostic code snippets when helpful. --- +## Never Install Packages Automatically + +> **🔒 MANDATORY — You MUST NOT install, upgrade, or modify packages.** Provide the exact command; the user runs it. No exceptions. + +| Forbidden | What to do instead | +|-----------|--------------------| +| `pip install ...`, `conda install ...`, `apt install ...`, any package manager | Give the exact command and ask the user to run it. Say why the package is needed. | + +**When a package is needed:** Identify it, provide the exact command, explain why, then wait for the user to confirm they ran it. Even if the user says "just install it", give the command and require them to execute it themselves. + +--- + ## Resources ### Documentation diff --git a/skills/lp-milp-formulation/SKILL.md b/skills/lp-milp-formulation/SKILL.md new file mode 100644 index 0000000000..c0df08f45c --- /dev/null +++ b/skills/lp-milp-formulation/SKILL.md @@ -0,0 +1,128 @@ +--- +name: lp-milp-formulation +version: "26.04.00" +description: LP/MILP concepts and going from problem text to formulation. What LP/MILP are, required formulation questions, typical modeling elements, and how to parse problem statements (parameters, constraints, decisions, objective). +--- + +# LP/MILP Formulation + +Concepts and workflow for going from a problem description to a clear formulation. No API code here. + +## What is LP / MILP + +- **LP**: Linear objective, linear constraints, continuous variables. +- **MILP**: Same plus some integer or binary variables (e.g. scheduling, facility location, selection). + +## Required questions (problem formulation) + +Ask these if not already clear: + +1. **Decision variables** — What are they? Bounds? +2. **Objective** — Minimize or maximize? Linear expression in the variables? +3. **Constraints** — Linear inequalities/equalities? Names and meaning? +4. **Variable types** — All continuous (LP) or some integer/binary (MILP)? + +## Typical modeling elements + +- **Continuous variables** — production amounts, flow, etc. +- **Binary variables** — open/close, yes/no (e.g. facility open, item selected). +- **Linking constraints** — e.g. production only if facility open (Big-M or indicator). +- **Resource constraints** — linear cap on usage (materials, time, capacity). + +--- + +## Problem statement parsing + +When the user gives **problem text**, classify every sentence and then summarize before formulating. + +**Classify every sentence** as **parameter/given**, **constraint**, **decision**, or **objective**. Watch for **implicit constraints** (e.g. committed vs optional phrasing) and **implicit objectives** (e.g. "determine the plan" + costs → minimize total cost). + +**Ambiguity:** If anything is still ambiguous, ask the user or solve all plausible interpretations and report all outcomes; do not assume a single interpretation. + +### 🔒 MANDATORY: When in Doubt — Ask + +- If there is **any doubt** about whether a constraint or value should be included, **ask the user** and state the possible interpretations. + +### 🔒 MANDATORY: Complete-Path Runs — Try All Variants + +- When the user asks to **run the complete path** (e.g. end-to-end, full pipeline), run all plausible variants and **report all outcomes** so the user can choose; do not assume a single interpretation. + +### Three labels + +| Label | Meaning | Examples (sentence type) | +|-------|--------|---------------------------| +| **Parameter / given** | Fixed data, inputs, facts. Not chosen by the model. | "Demand is 100 units." "There are 3 factories." "Costs are $5 per unit." | +| **Constraint** | Something that must hold. May be explicit or **implicit** from phrasing. | "Capacity is 200." "All demand must be met." "At least 2 shifts must be staffed." | +| **Decision** | Something we choose or optimize. | "How much to produce." "Which facilities to open." "How many workers to hire." | +| **Objective** | What to minimize or maximize. May be **explicit** ("minimize cost") or **implicit** ("determine the plan" with costs given). | "Minimize total cost." "Determine the production plan" (with costs) → minimize total cost. | + +### Implicit constraints: committed vs optional phrasing + +**Committed/fixed phrasing** → treat as **parameter** or **implicit constraint** (everything mentioned is given or must happen). Not a decision. + +| Phrasing | Interpretation | Why | +|----------|-----------------|-----| +| "Plans to produce X products" | **Constraint**: all X must be produced. | Commitment; production level is fixed. | +| "Operates 3 factories" | **Parameter**: all 3 are open. Not a location-selection problem. | Current state is fixed. | +| "Employs N workers" | **Parameter**: all N are employed. Not a hiring decision. | Workforce size is given. | +| "Has a capacity of C" | **Parameter** (C) + **constraint**: usage ≤ C. | Capacity is fixed. | +| "Must meet all demand" | **Constraint**: demand satisfaction. | Explicit requirement. | + +**Optional/decision phrasing** → treat as **decision**. + +| Phrasing | Interpretation | Why | +|----------|-----------------|-----| +| "May produce up to …" | **Decision**: how much to produce. | Optional level. | +| "Can choose to open" (factories, sites) | **Decision**: which to open. | Selection is decided. | +| "Considers hiring" | **Decision**: how many to hire. | Hiring is under consideration. | +| "Decides how much to order" | **Decision**: order quantities. | Explicit decision. | +| "Wants to minimize/maximize …" | **Objective** (drives decisions). | Goal; decisions are the levers. | + +### Implicit objectives — do not miss + +**If the problem asks to "determine the plan" (or similar) but does not state "minimize" or "maximize" explicitly, the objective is often implicit.** You **MUST** identify it and state it before formulating; do not build a model with no objective. + +| Phrasing / context | Likely implicit objective | Why | +|-------------------|---------------------------|-----| +| "Determine the production plan" + costs given (per unit, per hour, etc.) | **Minimize total cost** (production + inspection/sales + overtime, etc.) | Plan is chosen; costs are specified → natural goal is to minimize total cost. | +| "Determine the plan" + costs and revenues given | **Maximize profit** (revenue − cost) | Both sides of the ledger → optimize profit. | +| "Try to determine the monthly production plan" + workshop hour costs, inspection/sales costs | **Minimize total cost** | All cost components are given; no revenue to maximize → minimize total cost. | + +**Rule:** When the problem gives cost (or cost and revenue) data and asks to "determine", "find", or "establish" the plan, **always state the objective explicitly** (e.g. "I'm treating the objective as minimize total cost, since only costs are given."). If both cost and revenue are present, state whether you use "minimize cost" or "maximize profit". Ask the user if unclear. + +### Parsing workflow + +1. **Split** the problem text into sentences or logical clauses. +2. **Label** each: parameter/given | constraint | decision | **objective** (if stated). +3. **Identify the objective (explicit or implicit):** If the problem says "minimize/maximize X", that's the objective. If it only says "determine the plan" (or "find", "establish") but gives costs (and possibly revenues), the objective is **implicit** — state it (e.g. minimize total cost, or maximize profit) and confirm with the user if ambiguous. +4. **Flag implicit constraints**: For each sentence, ask — "Does this state a fixed fact or a requirement (→ parameter/constraint), or something we choose (→ decision)?" +5. **Resolve ambiguity** by checking verbs and modals: + - "is", "has", "operates", "employs", "plans to" (fixed/committed) → parameter or implicit constraint. + - "may", "can choose", "considers", "decides", "wants to" (optional) → decision or objective. +6. **🔒 MANDATORY — If anything is still ambiguous** (e.g. a value or constraint could be read two ways): ask the user which interpretation is correct, or solve all plausible interpretations and report all outcomes. Do not assume a single interpretation. +7. **Summarize** for the user: list parameters, constraints (explicit + flagged implicit), decisions, and **objective (explicit or inferred)** before writing the math formulation. + +### Parsing checklist + +- [ ] Every sentence has a label (parameter | constraint | decision | objective if stated). +- [ ] **Objective is identified:** Explicit ("minimize/maximize X") or implicit ("determine the plan" + costs → minimize total cost; + revenues → maximize profit). Never formulate without stating the objective. +- [ ] Committed phrasing ("plans to", "operates", "employs") → not decisions. +- [ ] Optional phrasing ("may", "can choose", "considers") → decisions. +- [ ] Implicit constraints from committed phrasing are written out (e.g. "all X must be produced"). +- [ ] **🔒 MANDATORY — Ambiguity:** Any phrase that could be read two ways → I asked the user or I will solve all interpretations and report all outcomes (no silent single interpretation). +- [ ] Summary is produced before formulating (parameters, constraints, decisions, **objective**). + +### Example + +**Text:** "The company operates 3 factories and plans to produce 500 units. It may use overtime at extra cost. Minimize total cost." + +| Sentence / phrase | Label | Note | +|-------------------|-------|------| +| "Operates 3 factories" | Parameter | All 3 open; not facility selection. | +| "Plans to produce 500 units" | Constraint (implicit) | All 500 must be produced. | +| "May use overtime at extra cost" | Decision | How much overtime is a decision. | +| "Minimize total cost" | Objective | Drives decisions. | + +Result: Parameters = 3 factories, 500 units target. Constraints = produce exactly 500 (implicit from "plans to produce"). Decisions = production allocation across factories, overtime amounts. Objective = minimize cost. + +**Implicit-objective example:** A problem that asks to "determine the production plan" (or similar) and gives cost components (e.g. workshop, inspection, sales) but does not state "minimize" or "maximize" → **Objective is implicit: minimize total cost**. Always state it explicitly: "The objective is to minimize total cost." diff --git a/skills/qp-formulation/SKILL.md b/skills/qp-formulation/SKILL.md new file mode 100644 index 0000000000..c87b887fbc --- /dev/null +++ b/skills/qp-formulation/SKILL.md @@ -0,0 +1,33 @@ +--- +name: qp-formulation +version: "26.04.00" +description: Quadratic Programming (QP) — problem form and constraints. Domain concepts; no API or interface. QP is beta. +--- + +# QP Formulation + +Domain concepts for quadratic programming. No API or interface details here. **QP support in cuOpt is currently in beta.** + +## What is QP + +- **Objective**: Quadratic in the variables (e.g. x², x·y terms). Example: portfolio variance xᵀQx. +- **Constraints**: Linear only. cuOpt does not support quadratic constraints. + +## Important domain rule: minimize only + +QP objectives must be **minimization**. To maximize a quadratic expression, negate it and minimize; then negate the optimal value. + +## Required questions (problem formulation) + +Ask these if not already clear: + +1. **Objective** — Does it have squared or cross terms (x², x·y)? If purely linear, use LP/MILP instead. +2. **Minimize or maximize?** — If maximize, user must negate objective and minimize. +3. **Convexity** — For minimization, the quadratic form (matrix Q) should be positive semi-definite for well-posed problems. +4. **Constraints** — All linear (no quadratic constraints)? + +## Typical use cases + +- Portfolio optimization (minimize variance subject to return and budget). +- Least squares (minimize ‖Ax − b‖²). +- Other quadratic objectives with linear constraints. diff --git a/skills/routing-formulation/SKILL.md b/skills/routing-formulation/SKILL.md new file mode 100644 index 0000000000..4ab8d6419d --- /dev/null +++ b/skills/routing-formulation/SKILL.md @@ -0,0 +1,31 @@ +--- +name: routing-formulation +version: "26.04.00" +description: Vehicle routing (VRP, TSP, PDP) — problem types and data requirements. Domain concepts; no API or interface. +--- + +# Routing Formulation + +Domain concepts for vehicle routing. No API or interface details here. + +## What is routing + +- **TSP**: Single vehicle, visit all locations once (e.g. shortest tour). +- **VRP**: Multiple vehicles, capacity and/or time limits; assign orders to vehicles and sequence stops. +- **PDP**: Pickup and delivery pairs; pickup must be visited before the corresponding delivery. + +## Required questions (problem and data) + +Ask these if not already clear: + +1. **Problem type** — TSP, VRP, or PDP? +2. **Locations** — How many? Depot(s)? Cost or distance between pairs (matrix or derived)? +3. **Orders / tasks** — Which locations must be visited? Demand or service per stop? +4. **Fleet** — Number of vehicles, capacity per vehicle (and per dimension if multiple), start/end locations? +5. **Constraints** — Time windows (earliest/latest arrival), service times, precedence (order A before B)? + +## Typical data + +- Cost or distance matrix (or travel-time matrix). +- Order locations and, for VRP, demand per order. +- Vehicle capacities and optional time windows for vehicles and orders. From 0aa1b311cfb069579b5c6667f7da73615acd2eab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Akif=20=C3=87=C3=96RD=C3=9CK?= Date: Wed, 4 Mar 2026 15:29:53 +0100 Subject: [PATCH 130/225] Clique Table Generation (#627) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR implements the first part of the paper from the paper: Preprocessing and Cutting Planes with Conflict Graphs. This part contains only the preprocessing parts and clique cuts will follow in a separate PR: - Clique detection by converting constraints into sorted knapsack constraints. This allows fast clique detection. - Additional cliques in the same constraint by utilizing sorting structures. - Clique extension/merging across the conflict graph. - Clique covering and problem modification. The data structures and query functions are implemented and will be used as a basis for clique cuts and usage in heuristics. Benchmark results: no impact on benchmarks as this is just the first part, improvements will come from clique cuts. ## Summary by CodeRabbit * **New Features** * Automatic clique detection and injection into MIP presolve to improve preprocessing. * Host-to-solver constraint sync allowing external problem updates to be applied before solving. * **Improvements** * Presolve now respects a time cap to bound preprocessing work. * Improved constraint handling and propagation during solution setup. * Local search initialization refined for more reliable first-run behavior. Authors: - Akif ÇÖRDÜK (https://github.com/akifcorduk) - Alice Boucher (https://github.com/aliceb-nv) Approvers: - Alice Boucher (https://github.com/aliceb-nv) - Ramakrishnap (https://github.com/rgsl888prabhu) URL: https://github.com/NVIDIA/cuopt/pull/627 --- cpp/CMakeLists.txt | 6 + cpp/src/dual_simplex/sparse_matrix.cpp | 6 + cpp/src/dual_simplex/sparse_matrix.hpp | 2 + cpp/src/mip_heuristics/CMakeLists.txt | 1 + .../diversity/diversity_manager.cu | 60 +- .../diversity/diversity_manager.cuh | 2 +- .../local_search/local_search.cu | 19 +- .../presolve/conflict_graph/clique_table.cu | 1044 +++++++++++++++++ .../presolve/conflict_graph/clique_table.cuh | 194 +++ .../mip_heuristics/presolve/probing_cache.cu | 3 +- .../mip_heuristics/presolve/probing_cache.cuh | 4 +- cpp/src/mip_heuristics/problem/problem.cu | 90 +- cpp/src/mip_heuristics/problem/problem.cuh | 2 + cpp/src/mip_heuristics/solver.cu | 16 +- cpp/src/mip_heuristics/utils.cuh | 5 +- cpp/tests/mip/problem_test.cu | 90 ++ 16 files changed, 1512 insertions(+), 32 deletions(-) create mode 100644 cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cu create mode 100644 cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cuh diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index e488291744..8225d93655 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -543,6 +543,12 @@ endif() option(BUILD_MIP_BENCHMARKS "Build MIP benchmarks" OFF) if(BUILD_MIP_BENCHMARKS AND NOT BUILD_LP_ONLY) add_executable(solve_MIP ../benchmarks/linear_programming/cuopt/run_mip.cpp) + target_include_directories(solve_MIP + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src" + PUBLIC + "$" + ) set_target_properties(solve_MIP PROPERTIES diff --git a/cpp/src/dual_simplex/sparse_matrix.cpp b/cpp/src/dual_simplex/sparse_matrix.cpp index ec8aee09c4..8ccccd57cf 100644 --- a/cpp/src/dual_simplex/sparse_matrix.cpp +++ b/cpp/src/dual_simplex/sparse_matrix.cpp @@ -657,6 +657,12 @@ i_t csr_matrix_t::check_matrix(std::string matrix_name) const return 0; } +template +std::pair csr_matrix_t::get_constraint_range(i_t cstr_idx) const +{ + return std::make_pair(this->row_start[cstr_idx], this->row_start[cstr_idx + 1]); +} + // x <- x + alpha * A(:, j) template void scatter_dense(const csc_matrix_t& A, i_t j, f_t alpha, std::vector& x) diff --git a/cpp/src/dual_simplex/sparse_matrix.hpp b/cpp/src/dual_simplex/sparse_matrix.hpp index 56e3ca82c6..b6d3ee9aae 100644 --- a/cpp/src/dual_simplex/sparse_matrix.hpp +++ b/cpp/src/dual_simplex/sparse_matrix.hpp @@ -173,6 +173,8 @@ class csr_matrix_t { return true; } + // get constraint range + std::pair get_constraint_range(i_t cstr_idx) const; i_t nz_max; // maximum number of nonzero entries i_t m; // number of rows i_t n; // number of cols diff --git a/cpp/src/mip_heuristics/CMakeLists.txt b/cpp/src/mip_heuristics/CMakeLists.txt index 538e3c49ac..a200d4265b 100644 --- a/cpp/src/mip_heuristics/CMakeLists.txt +++ b/cpp/src/mip_heuristics/CMakeLists.txt @@ -38,6 +38,7 @@ set(MIP_NON_LP_FILES ${CMAKE_CURRENT_SOURCE_DIR}/presolve/multi_probe.cu ${CMAKE_CURRENT_SOURCE_DIR}/presolve/probing_cache.cu ${CMAKE_CURRENT_SOURCE_DIR}/presolve/trivial_presolve.cu + ${CMAKE_CURRENT_SOURCE_DIR}/presolve/conflict_graph/clique_table.cu ${CMAKE_CURRENT_SOURCE_DIR}/feasibility_jump/feasibility_jump.cu ${CMAKE_CURRENT_SOURCE_DIR}/feasibility_jump/feasibility_jump_kernels.cu ${CMAKE_CURRENT_SOURCE_DIR}/feasibility_jump/fj_cpu.cu) diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 4fa0d3f4ab..ed165fe610 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -9,6 +9,7 @@ #include "diversity_manager.cuh" #include +#include #include #include #include @@ -17,6 +18,8 @@ #include +#include + constexpr bool fj_only_run = false; namespace cuopt::linear_programming::detail { @@ -172,7 +175,7 @@ void diversity_manager_t::add_user_given_solutions( } template -bool diversity_manager_t::run_presolve(f_t time_limit) +bool diversity_manager_t::run_presolve(f_t time_limit, timer_t global_timer) { raft::common::nvtx::range fun_scope("run_presolve"); CUOPT_LOG_INFO("Running presolve!"); @@ -191,31 +194,50 @@ bool diversity_manager_t::run_presolve(f_t time_limit) if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { run_probing_cache = false; } if (run_probing_cache) { // Run probing cache before trivial presolve to discover variable implications - const f_t time_ratio_of_probing_cache = diversity_config.time_ratio_of_probing_cache; - const f_t max_time_on_probing = diversity_config.max_time_on_probing; - f_t time_for_probing_cache = - std::min(max_time_on_probing, time_limit * time_ratio_of_probing_cache); + const f_t max_time_on_probing = diversity_config.max_time_on_probing; + f_t time_for_probing_cache = std::min(max_time_on_probing, time_limit); timer_t probing_timer{time_for_probing_cache}; // this function computes probing cache, finds singletons, substitutions and changes the problem bool problem_is_infeasible = compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); if (problem_is_infeasible) { return false; } } - if (!presolve_timer.check_time_limit()) { - const bool remap_cache_ids = true; - trivial_presolve(*problem_ptr, remap_cache_ids); - if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; } - // May overconstrain if Papilo presolve has been run before - if (context.settings.presolver == presolver_t::None) { - if (!problem_ptr->empty) { - // do the resizing no-matter what, bounds presolve might not change the bounds but initial - // trivial presolve might have - ls.constraint_prop.bounds_update.resize(*problem_ptr); - ls.constraint_prop.conditional_bounds_update.update_constraint_bounds( - *problem_ptr, ls.constraint_prop.bounds_update); - if (!check_bounds_sanity(*problem_ptr)) { return false; } - } + const bool remap_cache_ids = true; + if (!global_timer.check_time_limit()) { trivial_presolve(*problem_ptr, remap_cache_ids); } + if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; } + if (!presolve_timer.check_time_limit() && !context.settings.heuristics_only && + !problem_ptr->empty) { + f_t time_limit_for_clique_table = std::min(3., presolve_timer.remaining_time() / 5); + timer_t clique_timer(time_limit_for_clique_table); + dual_simplex::user_problem_t host_problem(problem_ptr->handle_ptr); + problem_ptr->get_host_user_problem(host_problem); + std::shared_ptr> clique_table; + constexpr bool modify_problem_with_cliques = false; + find_initial_cliques( + host_problem, context.settings.tolerances, clique_timer, modify_problem_with_cliques); + if (modify_problem_with_cliques) { + problem_ptr->set_constraints_from_host_user_problem(host_problem); + cuopt_assert(host_problem.lower.size() == static_cast(problem_ptr->n_variables), + "host lower bound size mismatch"); + cuopt_assert(host_problem.upper.size() == static_cast(problem_ptr->n_variables), + "host upper bound size mismatch"); + std::vector all_var_indices(problem_ptr->n_variables); + std::iota(all_var_indices.begin(), all_var_indices.end(), 0); + problem_ptr->update_variable_bounds(all_var_indices, host_problem.lower, host_problem.upper); + trivial_presolve(*problem_ptr, remap_cache_ids); + } + } + // May overconstrain if Papilo presolve has been run before + if (context.settings.presolver == presolver_t::None) { + if (!problem_ptr->empty) { + // do the resizing no-matter what, bounds presolve might not change the bounds but initial + // trivial presolve might have + ls.constraint_prop.bounds_update.resize(*problem_ptr); + ls.constraint_prop.bounds_update.upd.init_changed_constraints(problem_ptr->handle_ptr); + ls.constraint_prop.conditional_bounds_update.update_constraint_bounds( + *problem_ptr, ls.constraint_prop.bounds_update); } + if (!check_bounds_sanity(*problem_ptr)) { return false; } } stats.presolve_time = presolve_timer.elapsed_time(); lp_optimal_solution.resize(problem_ptr->n_variables, problem_ptr->handle_ptr->get_stream()); diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh index 91fc4049a6..d4e24bdeaf 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh @@ -33,7 +33,7 @@ template class diversity_manager_t { public: diversity_manager_t(mip_solver_context_t& context); - bool run_presolve(f_t time_limit); + bool run_presolve(f_t time_limit, timer_t global_timer); solution_t run_solver(); void generate_solution(f_t time_limit, bool random_start = true); void run_fj_alone(solution_t& solution); diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index 16d0a3f874..118b7181ab 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -209,7 +209,24 @@ bool local_search_t::do_fj_solve(solution_t& solution, if (time_limit == 0.) return solution.get_feasible(); timer_t timer(time_limit); - + const auto old_n_cstr_weights = in_fj.cstr_weights.size(); + const auto expected_n_cstr_weights = static_cast(solution.problem_ptr->n_constraints); + // in case this is the first time run, resize + if (old_n_cstr_weights != expected_n_cstr_weights) { + in_fj.cstr_weights.resize(solution.problem_ptr->n_constraints, + solution.handle_ptr->get_stream()); + cuopt_assert(in_fj.cstr_weights.size() == expected_n_cstr_weights, + "Constraint weights must match constraint count after resize"); + // Initialize only newly grown entries; shrinking does not need initialization. + if (old_n_cstr_weights < expected_n_cstr_weights) { + cuopt_assert(old_n_cstr_weights <= in_fj.cstr_weights.size(), + "Constraint weight fill start must be within range"); + thrust::uninitialized_fill(solution.handle_ptr->get_thrust_policy(), + in_fj.cstr_weights.begin() + old_n_cstr_weights, + in_fj.cstr_weights.end(), + 1.); + } + } auto h_weights = cuopt::host_copy(in_fj.cstr_weights, solution.handle_ptr->get_stream()); auto h_objective_weight = in_fj.objective_weight.value(solution.handle_ptr->get_stream()); for (auto& cpu_fj : ls_cpu_fj) { diff --git a/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cu b/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cu new file mode 100644 index 0000000000..e21af7f69f --- /dev/null +++ b/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cu @@ -0,0 +1,1044 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, 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. + */ + +#define DEBUG_KNAPSACK_CONSTRAINTS 1 + +#include "clique_table.cuh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cuopt::linear_programming::detail { + +// do constraints with only binary variables. +template +void find_cliques_from_constraint(const knapsack_constraint_t& kc, + clique_table_t& clique_table) +{ + i_t size = kc.entries.size(); + cuopt_assert(size > 1, "Constraint has not enough variables"); + if (kc.entries[size - 1].val + kc.entries[size - 2].val <= kc.rhs) { return; } + std::vector clique; + i_t k = size - 1; + // find the first clique, which is the largest + // FIXME: do binary search + // require k >= 1 so kc.entries[k-1] is always valid + while (k >= 1 && kc.entries[k].val + kc.entries[k - 1].val > kc.rhs) { + k--; + } + for (i_t idx = k; idx < size; idx++) { + clique.push_back(kc.entries[idx].col); + } + clique_table.first.push_back(clique); + const i_t original_clique_start_idx = k; + // find the additional cliques + k--; + while (k >= 0) { + f_t curr_val = kc.entries[k].val; + i_t curr_col = kc.entries[k].col; + // do a binary search in the clique coefficients to find f, such that coeff_k + coeff_f > rhs + // this means that we get a subset of the original clique and extend it with a variable + f_t val_to_find = kc.rhs - curr_val + clique_table.tolerances.absolute_tolerance; + auto it = std::lower_bound( + kc.entries.begin() + original_clique_start_idx, kc.entries.end(), val_to_find); + if (it != kc.entries.end()) { + i_t position_on_knapsack_constraint = std::distance(kc.entries.begin(), it); + i_t start_pos_on_clique = position_on_knapsack_constraint - original_clique_start_idx; + cuopt_assert(start_pos_on_clique >= 1, "Start position on clique is negative"); + cuopt_assert(it->val + curr_val > kc.rhs, "RHS mismatch"); +#if DEBUG_KNAPSACK_CONSTRAINTS + CUOPT_LOG_DEBUG("Found additional clique: %d, %d, %d", + curr_col, + clique_table.first.size() - 1, + start_pos_on_clique); +#endif + clique_table.addtl_cliques.push_back( + {curr_col, (i_t)clique_table.first.size() - 1, start_pos_on_clique}); + } else { + break; + } + k--; + } +} + +// sort CSR by constraint coefficients +template +void sort_csr_by_constraint_coefficients( + std::vector>& knapsack_constraints) +{ + // sort the rows of the CSR matrix by the coefficients of the constraint + for (auto& knapsack_constraint : knapsack_constraints) { + std::sort(knapsack_constraint.entries.begin(), knapsack_constraint.entries.end()); + } +} + +template +void make_coeff_positive_knapsack_constraint( + const dual_simplex::user_problem_t& problem, + std::vector>& knapsack_constraints, + std::unordered_set& set_packing_constraints, + typename mip_solver_settings_t::tolerances_t tolerances) +{ + for (i_t i = 0; i < (i_t)knapsack_constraints.size(); i++) { + auto& knapsack_constraint = knapsack_constraints[i]; + f_t rhs_offset = 0; + bool all_coeff_are_equal = true; + f_t first_coeff = std::abs(knapsack_constraint.entries[0].val); + for (auto& entry : knapsack_constraint.entries) { + if (entry.val < 0) { + entry.val = -entry.val; + rhs_offset += entry.val; + // negation of a variable is var + num_cols + entry.col = entry.col + problem.num_cols; + } + if (!integer_equal(entry.val, first_coeff, tolerances.absolute_tolerance)) { + all_coeff_are_equal = false; + } + } + knapsack_constraint.rhs += rhs_offset; + if (!integer_equal(knapsack_constraint.rhs, first_coeff, tolerances.absolute_tolerance)) { + all_coeff_are_equal = false; + } + knapsack_constraint.is_set_packing = all_coeff_are_equal; + if (!all_coeff_are_equal) { knapsack_constraint.is_set_partitioning = false; } + if (knapsack_constraint.is_set_packing) { set_packing_constraints.insert(i); } + cuopt_assert(knapsack_constraint.rhs >= 0, "RHS must be non-negative"); + } +} + +// convert all the knapsack constraints +// if a binary variable has a negative coefficient, put its negation in the constraint +template +void fill_knapsack_constraints(const dual_simplex::user_problem_t& problem, + std::vector>& knapsack_constraints, + dual_simplex::csr_matrix_t& A) +{ + // we might add additional constraints for the equality constraints + i_t added_constraints = 0; + // in user problems, ranged constraint ids monotonically increase. + // when a row sense is "E", check if it is ranged constraint and treat accordingly + i_t ranged_constraint_counter = 0; + for (i_t i = 0; i < A.m; i++) { + std::pair constraint_range = A.get_constraint_range(i); + if (constraint_range.second - constraint_range.first < 2) { + CUOPT_LOG_DEBUG("Constraint %d has less than 2 variables, skipping", i); + continue; + } + bool all_binary = true; + // check if all variables are binary (any non-continuous with bounds [0,1]) + for (i_t j = constraint_range.first; j < constraint_range.second; j++) { + if (problem.var_types[A.j[j]] == dual_simplex::variable_type_t::CONTINUOUS || + problem.lower[A.j[j]] != 0 || problem.upper[A.j[j]] != 1) { + all_binary = false; + break; + } + } + // if all variables are binary, convert the constraint to a knapsack constraint + if (!all_binary) { continue; } + knapsack_constraint_t knapsack_constraint; + + knapsack_constraint.cstr_idx = i; + if (problem.row_sense[i] == 'L') { + knapsack_constraint.rhs = problem.rhs[i]; + for (i_t j = constraint_range.first; j < constraint_range.second; j++) { + knapsack_constraint.entries.push_back({A.j[j], A.x[j]}); + } + } else if (problem.row_sense[i] == 'G') { + knapsack_constraint.rhs = -problem.rhs[i]; + for (i_t j = constraint_range.first; j < constraint_range.second; j++) { + knapsack_constraint.entries.push_back({A.j[j], -A.x[j]}); + } + } + // equality part + else { + // For equality rows, partitioning status should not depend on raw rhs scale here. + // The exact set-packing/partitioning check is finalized later in + // make_coeff_positive_knapsack_constraint after coefficient normalization. + bool is_set_partitioning = true; + bool ranged_constraint = ranged_constraint_counter < problem.num_range_rows && + problem.range_rows[ranged_constraint_counter] == i; + // less than part + knapsack_constraint.rhs = problem.rhs[i]; + if (ranged_constraint) { + knapsack_constraint.rhs += problem.range_value[ranged_constraint_counter]; + is_set_partitioning = problem.range_value[ranged_constraint_counter] == 0.; + ranged_constraint_counter++; + } + for (i_t j = constraint_range.first; j < constraint_range.second; j++) { + knapsack_constraint.entries.push_back({A.j[j], A.x[j]}); + } + // greater than part: convert it to less than + knapsack_constraint_t knapsack_constraint2; + // Mark synthetic rows from equality splitting with negative ids so they never alias real row + // indices (including rows appended later by clique extension). + knapsack_constraint2.cstr_idx = -(added_constraints + 1); + added_constraints++; + knapsack_constraint2.rhs = -problem.rhs[i]; + for (i_t j = constraint_range.first; j < constraint_range.second; j++) { + knapsack_constraint2.entries.push_back({A.j[j], -A.x[j]}); + } + knapsack_constraint.is_set_partitioning = is_set_partitioning; + knapsack_constraint2.is_set_partitioning = is_set_partitioning; + knapsack_constraints.push_back(knapsack_constraint2); + } + knapsack_constraints.push_back(knapsack_constraint); + } + CUOPT_LOG_DEBUG("Number of knapsack constraints: %d added %d constraints", + knapsack_constraints.size(), + added_constraints); +} + +template +void remove_small_cliques(clique_table_t& clique_table) +{ + i_t num_removed_first = 0; + i_t num_removed_addtl = 0; + std::vector to_delete(clique_table.first.size(), false); + // if a clique is small, we remove it from the cliques and add it to adjlist + for (size_t clique_idx = 0; clique_idx < clique_table.first.size(); clique_idx++) { + const auto& clique = clique_table.first[clique_idx]; + if (clique.size() <= (size_t)clique_table.min_clique_size) { + for (size_t i = 0; i < clique.size(); i++) { + for (size_t j = 0; j < clique.size(); j++) { + if (i == j) { continue; } + clique_table.adj_list_small_cliques[clique[i]].insert(clique[j]); + } + } + num_removed_first++; + to_delete[clique_idx] = true; + } + } + for (size_t addtl_c = 0; addtl_c < clique_table.addtl_cliques.size(); addtl_c++) { + const auto& addtl_clique = clique_table.addtl_cliques[addtl_c]; + const auto base_clique_idx = static_cast(addtl_clique.clique_idx); + cuopt_assert(base_clique_idx < to_delete.size(), + "Additional clique points to invalid base clique index"); + // Remove additional cliques whose base clique is scheduled for deletion. + if (to_delete[base_clique_idx]) { + // Materialize conflicts represented by: + // addtl_clique.vertex_idx + first[base_clique_idx][start_pos_on_clique:] + // before deleting both the additional and base clique entries. + for (size_t i = addtl_clique.start_pos_on_clique; + i < clique_table.first[base_clique_idx].size(); + i++) { + clique_table.adj_list_small_cliques[clique_table.first[base_clique_idx][i]].insert( + addtl_clique.vertex_idx); + clique_table.adj_list_small_cliques[addtl_clique.vertex_idx].insert( + clique_table.first[base_clique_idx][i]); + } + clique_table.addtl_cliques.erase(clique_table.addtl_cliques.begin() + addtl_c); + addtl_c--; + num_removed_addtl++; + continue; + } + i_t size_of_clique = + clique_table.first[base_clique_idx].size() - addtl_clique.start_pos_on_clique + 1; + if (size_of_clique < clique_table.min_clique_size) { + // the items from first clique are already added to the adjlist + // only add the items that are coming from the new var in the additional clique + for (size_t i = addtl_clique.start_pos_on_clique; + i < clique_table.first[base_clique_idx].size(); + i++) { + // insert conflicts both way + clique_table.adj_list_small_cliques[clique_table.first[base_clique_idx][i]].insert( + addtl_clique.vertex_idx); + clique_table.adj_list_small_cliques[addtl_clique.vertex_idx].insert( + clique_table.first[base_clique_idx][i]); + } + clique_table.addtl_cliques.erase(clique_table.addtl_cliques.begin() + addtl_c); + addtl_c--; + num_removed_addtl++; + } + } + CUOPT_LOG_DEBUG("Number of removed cliques from first: %d, additional: %d", + num_removed_first, + num_removed_addtl); + size_t i = 0; + size_t old_idx = 0; + std::vector index_mapping(clique_table.first.size(), -1); + auto it = std::remove_if(clique_table.first.begin(), clique_table.first.end(), [&](auto& clique) { + bool res = false; + if (to_delete[old_idx]) { + res = true; + } else { + index_mapping[old_idx] = i++; + } + old_idx++; + return res; + }); + clique_table.first.erase(it, clique_table.first.end()); + // renumber the reference indices in the additional cliques, since we removed some cliques + for (size_t addtl_c = 0; addtl_c < clique_table.addtl_cliques.size(); addtl_c++) { + i_t new_clique_idx = index_mapping[clique_table.addtl_cliques[addtl_c].clique_idx]; + cuopt_assert(new_clique_idx != -1, "New clique index is -1"); + clique_table.addtl_cliques[addtl_c].clique_idx = new_clique_idx; + cuopt_assert(clique_table.first[new_clique_idx].size() - + clique_table.addtl_cliques[addtl_c].start_pos_on_clique + 1 >= + (size_t)clique_table.min_clique_size, + "A small clique remained after removing small cliques"); + } + // Clique removals/edge materialization can change degrees; force recompute on next query. + std::fill(clique_table.var_degrees.begin(), clique_table.var_degrees.end(), -1); +} + +template +std::unordered_set clique_table_t::get_adj_set_of_var(i_t var_idx) +{ + std::unordered_set adj_set; + for (const auto& clique_idx : var_clique_map_first[var_idx]) { + adj_set.insert(first[clique_idx].begin(), first[clique_idx].end()); + } + + for (const auto& addtl_clique_idx : var_clique_map_addtl[var_idx]) { + adj_set.insert(addtl_cliques[addtl_clique_idx].vertex_idx); + adj_set.insert(first[addtl_cliques[addtl_clique_idx].clique_idx].begin() + + addtl_cliques[addtl_clique_idx].start_pos_on_clique, + first[addtl_cliques[addtl_clique_idx].clique_idx].end()); + } + // Memory-neutral reverse lookup for additional cliques: + // if var_idx is in first[clique_idx][start_pos_on_clique:], it is adjacent to vertex_idx. + for (const auto& addtl : addtl_cliques) { + if (addtl.vertex_idx == var_idx) { continue; } + const auto& clique = first[addtl.clique_idx]; + size_t start_pos = static_cast(addtl.start_pos_on_clique); + if (start_pos < clique.size() && + std::find(clique.begin() + start_pos, clique.end(), var_idx) != clique.end()) { + adj_set.insert(addtl.vertex_idx); + } + } + + for (const auto& adj_vertex : adj_list_small_cliques[var_idx]) { + adj_set.insert(adj_vertex); + } + // Add the complement of var_idx to the adjacency set + i_t complement_idx = (var_idx >= n_variables) ? (var_idx - n_variables) : (var_idx + n_variables); + adj_set.insert(complement_idx); + adj_set.erase(var_idx); + return adj_set; +} + +template +i_t clique_table_t::get_degree_of_var(i_t var_idx) +{ + // if it is not already computed, compute it and return + if (var_degrees[var_idx] == -1) { var_degrees[var_idx] = get_adj_set_of_var(var_idx).size(); } + return var_degrees[var_idx]; +} + +template +bool clique_table_t::check_adjacency(i_t var_idx1, i_t var_idx2) +{ + // if passed same variable + if (var_idx1 == var_idx2) { return false; } + // in case they are complements of each other + if (var_idx1 % n_variables == var_idx2 % n_variables) { return true; } + if (adj_list_small_cliques[var_idx1].count(var_idx2) > 0) { return true; } + // Check first cliques: var_clique_map_first stores clique indices + for (const auto& clique_idx : var_clique_map_first[var_idx1]) { + const auto& clique = first[clique_idx]; + // TODO: we can also keep a set of the clique if the memory allows, instead of doing linear + // search + if (std::find(clique.begin(), clique.end(), var_idx2) != clique.end()) { return true; } + } + + // Check additional cliques: var_clique_map_addtl stores indices into addtl_cliques + for (const auto& addtl_idx : var_clique_map_addtl[var_idx1]) { + const auto& addtl = addtl_cliques[addtl_idx]; + const auto& clique = first[addtl.clique_idx]; + // addtl clique is: vertex_idx + first[clique_idx][start_pos_on_clique:] + if (addtl.vertex_idx == var_idx2) { return true; } + if (addtl.start_pos_on_clique < static_cast(clique.size())) { + if (std::find(clique.begin() + addtl.start_pos_on_clique, clique.end(), var_idx2) != + clique.end()) { + return true; + } + } + } + + // var_clique_map_addtl is keyed by addtl.vertex_idx, so also check the reverse direction. + for (const auto& addtl_idx : var_clique_map_addtl[var_idx2]) { + const auto& addtl = addtl_cliques[addtl_idx]; + const auto& clique = first[addtl.clique_idx]; + if (addtl.vertex_idx == var_idx1) { return true; } + if (addtl.start_pos_on_clique < static_cast(clique.size())) { + if (std::find(clique.begin() + addtl.start_pos_on_clique, clique.end(), var_idx1) != + clique.end()) { + return true; + } + } + } + + return false; +} + +// this function should only be called within extend clique +// if this is called outside extend clique, csr matrix should be converted into csc and copied into +// problem because the problem is partly modified +template +void insert_clique_into_problem(const std::vector& clique, + dual_simplex::user_problem_t& problem, + dual_simplex::csr_matrix_t& A, + f_t coeff_scale) +{ + // convert vertices into original vars + f_t rhs_offset = 0.; + std::vector new_vars; + std::vector new_coeffs; + for (size_t i = 0; i < clique.size(); i++) { + f_t coeff = coeff_scale; + i_t var_idx = clique[i]; + if (var_idx >= problem.num_cols) { + coeff = -coeff_scale; + var_idx = var_idx - problem.num_cols; + rhs_offset += coeff_scale; + } + new_vars.push_back(var_idx); + new_coeffs.push_back(coeff); + } + // coeff_scale * (1 - x) = coeff_scale - coeff_scale * x + // Move constants to the right, so rhs must decrease by rhs_offset. + f_t rhs = coeff_scale - rhs_offset; + // insert the new clique into the problem as a new constraint + dual_simplex::sparse_vector_t new_row(A.n, new_vars.size()); + new_row.i = std::move(new_vars); + new_row.x = std::move(new_coeffs); + A.append_row(new_row); + problem.row_sense.push_back('L'); + problem.rhs.push_back(rhs); + problem.row_names.push_back("Clique" + std::to_string(problem.row_names.size())); +} + +template +bool extend_clique(const std::vector& clique, + clique_table_t& clique_table, + dual_simplex::user_problem_t& problem, + dual_simplex::csr_matrix_t& A, + f_t coeff_scale, + bool modify_problem, + i_t min_extension_gain, + i_t remaining_rows_budget, + i_t remaining_nnz_budget, + i_t& inserted_row_nnz) +{ + inserted_row_nnz = 0; + i_t smallest_degree = std::numeric_limits::max(); + i_t smallest_degree_var = -1; + // find smallest degree vertex in the current set packing constraint + for (size_t idx = 0; idx < clique.size(); idx++) { + i_t var_idx = clique[idx]; + i_t degree = clique_table.get_degree_of_var(var_idx); + if (degree < smallest_degree) { + smallest_degree = degree; + smallest_degree_var = var_idx; + } + } + std::vector extension_candidates; + auto smallest_degree_adj_set = clique_table.get_adj_set_of_var(smallest_degree_var); + std::unordered_set clique_members(clique.begin(), clique.end()); + for (const auto& candidate : smallest_degree_adj_set) { + if (clique_members.find(candidate) == clique_members.end()) { + extension_candidates.push_back(candidate); + } + } + std::sort(extension_candidates.begin(), extension_candidates.end(), [&](i_t a, i_t b) { + return clique_table.get_degree_of_var(a) > clique_table.get_degree_of_var(b); + }); + auto new_clique = clique; + i_t n_of_complement_conflicts = 0; + i_t complement_conflict_var = -1; + for (size_t idx = 0; idx < extension_candidates.size(); idx++) { + i_t var_idx = extension_candidates[idx]; + bool add = true; + bool complement_conflict = false; + i_t complement_conflict_idx = -1; + for (size_t i = 0; i < new_clique.size(); i++) { + if (var_idx % clique_table.n_variables == new_clique[i] % clique_table.n_variables) { + complement_conflict = true; + complement_conflict_idx = var_idx % clique_table.n_variables; + } + // check if the tested variable conflicts with all vars in the new clique + if (!clique_table.check_adjacency(var_idx, new_clique[i])) { + add = false; + break; + } + } + if (add) { + new_clique.push_back(var_idx); + if (complement_conflict) { + n_of_complement_conflicts++; + complement_conflict_var = complement_conflict_idx; + } + } + } + // if we found a larger cliqe, insert it into the formulation + if (new_clique.size() > clique.size()) { + if (n_of_complement_conflicts > 0) { + CUOPT_LOG_DEBUG("Found %d complement conflicts on var %d", + n_of_complement_conflicts, + complement_conflict_var); + cuopt_assert(n_of_complement_conflicts == 1, "There can only be one complement conflict"); + // Keep the discovered extension in the clique table for downstream dominance checks. + clique_table.first.push_back(new_clique); + for (const auto& var_idx : new_clique) { + clique_table.var_degrees[var_idx] = -1; + } + if (modify_problem) { + // fix all other variables other than complementing var + for (size_t i = 0; i < new_clique.size(); i++) { + if (new_clique[i] % clique_table.n_variables != complement_conflict_var) { + CUOPT_LOG_DEBUG("Fixing variable %d", new_clique[i]); + if (new_clique[i] >= problem.num_cols) { + cuopt_assert(problem.lower[new_clique[i] - problem.num_cols] != 0 || + problem.upper[new_clique[i] - problem.num_cols] != 0, + "Variable is fixed to other side"); + problem.lower[new_clique[i] - problem.num_cols] = 1; + problem.upper[new_clique[i] - problem.num_cols] = 1; + } else { + cuopt_assert(problem.lower[new_clique[i]] != 1 || problem.upper[new_clique[i]] != 1, + "Variable is fixed to other side"); + problem.lower[new_clique[i]] = 0; + problem.upper[new_clique[i]] = 0; + } + } + } + } + return true; + } else { + // Keep the discovered extension in the clique table even when row insertion is skipped by + // row/nnz budgets. + clique_table.first.push_back(new_clique); + for (const auto& var_idx : new_clique) { + clique_table.var_degrees[var_idx] = -1; + } +#if DEBUG_KNAPSACK_CONSTRAINTS + CUOPT_LOG_DEBUG("Extended clique: %lu from %lu", new_clique.size(), clique.size()); +#endif + i_t extension_gain = static_cast(new_clique.size() - clique.size()); + if (extension_gain < min_extension_gain) { return true; } + if (remaining_rows_budget <= 0 || + remaining_nnz_budget < static_cast(new_clique.size())) { + return true; + } + // Row insertion is now deferred until dominance is confirmed against model rows. + // This keeps extension and replacement sequential: detect dominance first, then replace. + inserted_row_nnz = 0; + } + } + return new_clique.size() > clique.size(); +} + +// Also known as clique merging. Infer larger clique constraints which allows inclusion of vars from +// other constraints. This only extends the original cliques in the formulation for now. +// TODO: consider a heuristic on how much of the cliques derived from knapsacks to include here +template +i_t extend_cliques(const std::vector>& knapsack_constraints, + const std::unordered_set& set_packing_constraints, + clique_table_t& clique_table, + dual_simplex::user_problem_t& problem, + dual_simplex::csr_matrix_t& A, + bool modify_problem, + cuopt::timer_t& timer) +{ + constexpr i_t min_extension_gain = 2; + constexpr i_t extension_yield_window = 64; + constexpr i_t min_successes_per_window = 1; + + i_t base_rows = A.m; + i_t base_nnz = A.row_start[A.m]; + i_t max_added_rows = std::max(8, base_rows / 50); + i_t max_added_nnz = std::max(8 * clique_table.max_clique_size_for_extension, base_nnz / 50); + + i_t added_rows = 0; + i_t added_nnz = 0; + i_t window_attempts = 0; + i_t window_successes = 0; + + CUOPT_LOG_DEBUG("Clique extension heuristics: min_gain=%d row_budget=%d nnz_budget=%d", + min_extension_gain, + max_added_rows, + max_added_nnz); + std::vector> cstr_vars(knapsack_constraints.size()); + struct clique_sig_t { + i_t knapsack_idx; + i_t size; + long long signature; + }; + std::vector sp_sigs; + sp_sigs.reserve(set_packing_constraints.size()); + for (const auto knapsack_idx : set_packing_constraints) { + cuopt_assert(knapsack_idx >= 0 && knapsack_idx < static_cast(knapsack_constraints.size()), + "Invalid set packing constraint index"); + const auto& vars = knapsack_constraints[knapsack_idx].entries; + cstr_vars[knapsack_idx].reserve(vars.size()); + for (const auto& entry : vars) { + cstr_vars[knapsack_idx].push_back(entry.col); + } + std::sort(cstr_vars[knapsack_idx].begin(), cstr_vars[knapsack_idx].end()); + cstr_vars[knapsack_idx].erase( + std::unique(cstr_vars[knapsack_idx].begin(), cstr_vars[knapsack_idx].end()), + cstr_vars[knapsack_idx].end()); + long long signature = 0; + for (auto v : cstr_vars[knapsack_idx]) { + signature += static_cast(v); + } + sp_sigs.push_back({knapsack_idx, static_cast(cstr_vars[knapsack_idx].size()), signature}); + } + std::sort(sp_sigs.begin(), sp_sigs.end(), [](const auto& a, const auto& b) { + if (a.signature != b.signature) { return a.signature < b.signature; } + return a.size < b.size; + }); + std::vector original_to_current_row_idx(problem.row_sense.size(), -1); + for (i_t row_idx = 0; row_idx < static_cast(original_to_current_row_idx.size()); row_idx++) { + original_to_current_row_idx[row_idx] = row_idx; + } + auto is_subset = [](const std::vector& a, const std::vector& b) { + size_t i = 0; + size_t j = 0; + while (i < a.size() && j < b.size()) { + if (a[i] == b[j]) { + i++; + j++; + } else if (a[i] > b[j]) { + j++; + } else { + return false; + } + } + return i == a.size(); + }; + auto fix_difference = [&](const std::vector& superset, const std::vector& subset) { + if (!modify_problem) { return; } + cuopt_assert(std::is_sorted(subset.begin(), subset.end()), + "subset vector passed to fix_difference is not sorted"); + for (auto var_idx : superset) { + if (std::binary_search(subset.begin(), subset.end(), var_idx)) { continue; } + if (var_idx >= problem.num_cols) { + i_t orig_idx = var_idx - problem.num_cols; + CUOPT_LOG_DEBUG("Fixing variable %d", orig_idx); + cuopt_assert(problem.lower[orig_idx] != 0 || problem.upper[orig_idx] != 0, + "Variable is fixed to other side"); + problem.lower[orig_idx] = 1; + problem.upper[orig_idx] = 1; + } else { + CUOPT_LOG_DEBUG("Fixing variable %d", var_idx); + cuopt_assert(problem.lower[var_idx] != 1 || problem.upper[var_idx] != 1, + "Variable is fixed to other side"); + problem.lower[var_idx] = 0; + problem.upper[var_idx] = 0; + } + } + }; + auto remove_dominated_cliques_in_problem_for_single_extended_clique = + [&](const std::vector& curr_clique, + f_t coeff_scale, + i_t remaining_rows_budget, + i_t remaining_nnz_budget, + i_t& inserted_row_nnz) { + inserted_row_nnz = 0; + if (curr_clique.empty() || sp_sigs.empty()) { return; } + std::vector curr_clique_vars(curr_clique.begin(), curr_clique.end()); + std::sort(curr_clique_vars.begin(), curr_clique_vars.end()); + curr_clique_vars.erase(std::unique(curr_clique_vars.begin(), curr_clique_vars.end()), + curr_clique_vars.end()); + long long signature = 0; + for (auto v : curr_clique_vars) { + signature += static_cast(v); + } + constexpr size_t dominance_window = 20000; + auto end_it = std::upper_bound( + sp_sigs.begin(), sp_sigs.end(), signature, [](long long value, const auto& a) { + return value < a.signature; + }); + size_t end = static_cast(std::distance(sp_sigs.begin(), end_it)); + size_t start = (end > dominance_window) ? (end - dominance_window) : 0; + std::vector rows_to_remove; + bool covering_clique_implied_by_partitioning = false; + for (size_t idx = end; idx > start; idx--) { + if (timer.check_time_limit()) { break; } + const auto& sp = sp_sigs[idx - 1]; + const auto& vars_sp = cstr_vars[sp.knapsack_idx]; + if (vars_sp.size() > curr_clique_vars.size()) { continue; } + cuopt_assert(std::is_sorted(vars_sp.begin(), vars_sp.end()), + "vars_sp vector passed to is_subset is not sorted"); + if (!is_subset(vars_sp, curr_clique_vars)) { continue; } + if (knapsack_constraints[sp.knapsack_idx].is_set_partitioning) { + if (vars_sp.size() != curr_clique_vars.size()) { + fix_difference(curr_clique_vars, vars_sp); + covering_clique_implied_by_partitioning = true; + } + continue; + } + i_t original_row_idx = knapsack_constraints[sp.knapsack_idx].cstr_idx; + if (original_row_idx < 0) { continue; } + cuopt_assert(original_row_idx < static_cast(original_to_current_row_idx.size()), + "Invalid original row index in knapsack constraint"); + i_t current_row_idx = original_to_current_row_idx[original_row_idx]; + if (current_row_idx < 0) { continue; } + cuopt_assert(current_row_idx < static_cast(problem.row_sense.size()), + "Invalid current row index in row mapping"); + rows_to_remove.push_back(current_row_idx); + } + if (rows_to_remove.empty()) { return; } + if (!modify_problem) { return; } + std::sort(rows_to_remove.begin(), rows_to_remove.end()); + rows_to_remove.erase(std::unique(rows_to_remove.begin(), rows_to_remove.end()), + rows_to_remove.end()); + if (!covering_clique_implied_by_partitioning) { + if (remaining_rows_budget <= 0 || + remaining_nnz_budget < static_cast(curr_clique_vars.size())) { + return; + } + // Replace dominated rows with this stronger clique row. + insert_clique_into_problem(curr_clique_vars, problem, A, coeff_scale); + inserted_row_nnz = static_cast(curr_clique_vars.size()); + } + std::vector removal_marker(problem.row_sense.size(), 0); + for (auto row_idx : rows_to_remove) { + cuopt_assert(row_idx >= 0 && row_idx < static_cast(removal_marker.size()), + "Invalid dominated row index"); + CUOPT_LOG_DEBUG("Removing dominated row %d", row_idx); + removal_marker[row_idx] = true; + } + dual_simplex::csr_matrix_t A_removed(0, 0, 0); + A.remove_rows(removal_marker, A_removed); + A = std::move(A_removed); + problem.num_rows = A.m; + i_t n = 0; + auto new_end = std::remove_if( + problem.row_sense.begin(), problem.row_sense.end(), [&removal_marker, &n](char) mutable { + return removal_marker[n++]; + }); + problem.row_sense.erase(new_end, problem.row_sense.end()); + n = 0; + auto new_end_rhs = + std::remove_if(problem.rhs.begin(), problem.rhs.end(), [&removal_marker, &n](f_t) mutable { + return removal_marker[n++]; + }); + problem.rhs.erase(new_end_rhs, problem.rhs.end()); + n = 0; + auto new_end_row_names = std::remove_if( + problem.row_names.begin(), + problem.row_names.end(), + [&removal_marker, &n](const std::string&) mutable { return removal_marker[n++]; }); + problem.row_names.erase(new_end_row_names, problem.row_names.end()); + cuopt_assert(problem.rhs.size() == problem.row_sense.size(), + "rhs and row sense size mismatch"); + cuopt_assert(problem.row_names.size() == problem.rhs.size(), + "row names and rhs size mismatch"); + cuopt_assert(problem.num_rows == static_cast(problem.rhs.size()), + "matrix and num rows mismatch after removal"); + if (!problem.range_rows.empty()) { + std::vector old_to_new_indices; + old_to_new_indices.reserve(removal_marker.size()); + i_t new_idx = 0; + for (size_t i = 0; i < removal_marker.size(); ++i) { + if (!removal_marker[i]) { + old_to_new_indices.push_back(new_idx++); + } else { + old_to_new_indices.push_back(-1); + } + } + std::vector new_range_rows; + std::vector new_range_values; + for (size_t i = 0; i < problem.range_rows.size(); ++i) { + i_t old_row = problem.range_rows[i]; + cuopt_assert(old_row >= 0 && old_row < static_cast(removal_marker.size()), + "Invalid row index in range_rows"); + if (!removal_marker[old_row]) { + i_t new_row = old_to_new_indices[old_row]; + cuopt_assert(new_row != -1, "Invalid new row index for ranged row renumbering"); + new_range_rows.push_back(new_row); + new_range_values.push_back(problem.range_value[i]); + } + } + problem.range_rows = std::move(new_range_rows); + problem.range_value = std::move(new_range_values); + } + problem.num_range_rows = static_cast(problem.range_rows.size()); + std::vector removed_prefix(removal_marker.size() + 1, 0); + for (size_t row_idx = 0; row_idx < removal_marker.size(); row_idx++) { + removed_prefix[row_idx + 1] = + removed_prefix[row_idx] + static_cast(removal_marker[row_idx]); + } + for (i_t row_idx = 0; row_idx < static_cast(original_to_current_row_idx.size()); + row_idx++) { + i_t current_row_idx = original_to_current_row_idx[row_idx]; + if (current_row_idx < 0) { continue; } + cuopt_assert(current_row_idx < static_cast(removal_marker.size()), + "Row index map is out of bounds"); + if (removal_marker[current_row_idx]) { + original_to_current_row_idx[row_idx] = -1; + } else { + original_to_current_row_idx[row_idx] = current_row_idx - removed_prefix[current_row_idx]; + } + } + }; + struct extension_candidate_t { + i_t knapsack_idx; + i_t estimated_gain; + i_t clique_size; + }; + std::vector extension_worklist; + extension_worklist.reserve(knapsack_constraints.size()); + for (i_t knapsack_idx = 0; knapsack_idx < static_cast(knapsack_constraints.size()); + knapsack_idx++) { + if (timer.check_time_limit()) { break; } + const auto& knapsack_constraint = knapsack_constraints[knapsack_idx]; + if (!knapsack_constraint.is_set_packing) { continue; } + i_t clique_size = static_cast(knapsack_constraint.entries.size()); + if (clique_size >= clique_table.max_clique_size_for_extension) { continue; } + i_t smallest_degree = std::numeric_limits::max(); + for (const auto& entry : knapsack_constraint.entries) { + smallest_degree = std::min(smallest_degree, clique_table.get_degree_of_var(entry.col)); + } + // The smallest-degree vertex upper-bounds how many new literals can be added. + i_t estimated_gain = std::max(0, smallest_degree - (clique_size - 1)); + if (estimated_gain < min_extension_gain) { continue; } + extension_worklist.push_back({knapsack_idx, estimated_gain, clique_size}); + } + std::stable_sort(extension_worklist.begin(), + extension_worklist.end(), + [](const extension_candidate_t& a, const extension_candidate_t& b) { + if (a.estimated_gain != b.estimated_gain) { + return a.estimated_gain > b.estimated_gain; + } + if (a.clique_size != b.clique_size) { return a.clique_size < b.clique_size; } + return a.knapsack_idx < b.knapsack_idx; + }); + CUOPT_LOG_DEBUG("Clique extension candidates after scoring: %zu", extension_worklist.size()); + + i_t n_extended_cliques = 0; + // Try highest estimated gain candidates first so budget is spent on promising rows. + for (const auto& candidate : extension_worklist) { + if (timer.check_time_limit()) { break; } + if (added_rows >= max_added_rows || added_nnz >= max_added_nnz) { + CUOPT_LOG_DEBUG( + "Stopping clique extension: budget reached (rows=%d nnz=%d)", added_rows, added_nnz); + break; + } + window_attempts++; + const auto& knapsack_constraint = knapsack_constraints[candidate.knapsack_idx]; + std::vector clique; + for (const auto& entry : knapsack_constraint.entries) { + clique.push_back(entry.col); + } + i_t inserted_row_nnz = 0; + f_t coeff_scale = knapsack_constraint.entries[0].val; + bool extended_clique = extend_clique(clique, + clique_table, + problem, + A, + coeff_scale, + modify_problem, + min_extension_gain, + max_added_rows - added_rows, + max_added_nnz - added_nnz, + inserted_row_nnz); + if (extended_clique) { + n_extended_cliques++; + i_t replacement_row_nnz = 0; + remove_dominated_cliques_in_problem_for_single_extended_clique(clique_table.first.back(), + coeff_scale, + max_added_rows - added_rows, + max_added_nnz - added_nnz, + replacement_row_nnz); + if (replacement_row_nnz > 0) { + window_successes++; + added_rows++; + added_nnz += replacement_row_nnz; + } + } + if (window_attempts >= extension_yield_window) { + if (window_successes < min_successes_per_window) { + CUOPT_LOG_DEBUG( + "Stopping clique extension: low yield (%d/%d)", window_successes, window_attempts); + break; + } + window_attempts = 0; + window_successes = 0; + } + } + if (modify_problem) { + // copy modified matrix back to problem + A.to_compressed_col(problem.A); + } + CUOPT_LOG_DEBUG("Number of extended cliques: %d", n_extended_cliques); + return n_extended_cliques; +} + +template +void fill_var_clique_maps(clique_table_t& clique_table) +{ + for (size_t clique_idx = 0; clique_idx < clique_table.first.size(); clique_idx++) { + const auto& clique = clique_table.first[clique_idx]; + for (size_t idx = 0; idx < clique.size(); idx++) { + i_t var_idx = clique[idx]; + clique_table.var_clique_map_first[var_idx].insert(clique_idx); + } + } + for (size_t addtl_c = 0; addtl_c < clique_table.addtl_cliques.size(); addtl_c++) { + const auto& addtl_clique = clique_table.addtl_cliques[addtl_c]; + clique_table.var_clique_map_addtl[addtl_clique.vertex_idx].insert(addtl_c); + } +} + +template +void print_knapsack_constraints( + const std::vector>& knapsack_constraints, + bool print_only_set_packing = false) +{ +#if DEBUG_KNAPSACK_CONSTRAINTS + std::cout << "Number of knapsack constraints: " << knapsack_constraints.size() << "\n"; + for (const auto& knapsack : knapsack_constraints) { + if (print_only_set_packing && !knapsack.is_set_packing) { continue; } + std::cout << "Knapsack constraint idx: " << knapsack.cstr_idx << "\n"; + std::cout << " RHS: " << knapsack.rhs << "\n"; + std::cout << " Is set packing: " << knapsack.is_set_packing << "\n"; + std::cout << " Entries:\n"; + for (const auto& entry : knapsack.entries) { + std::cout << " col: " << entry.col << ", val: " << entry.val << "\n"; + } + std::cout << "----------\n"; + } +#endif +} + +template +void print_clique_table(const clique_table_t& clique_table) +{ +#if DEBUG_KNAPSACK_CONSTRAINTS + std::cout << "Number of cliques: " << clique_table.first.size() << "\n"; + for (const auto& clique : clique_table.first) { + std::cout << "Clique: "; + for (const auto& var : clique) { + std::cout << var << " "; + } + } + std::cout << "Number of additional cliques: " << clique_table.addtl_cliques.size() << "\n"; + for (const auto& addtl_clique : clique_table.addtl_cliques) { + std::cout << "Additional clique: " << addtl_clique.vertex_idx << ", " << addtl_clique.clique_idx + << ", " << addtl_clique.start_pos_on_clique << "\n"; + } +#endif +} + +template +void find_initial_cliques(dual_simplex::user_problem_t& problem, + typename mip_solver_settings_t::tolerances_t tolerances, + cuopt::timer_t& timer, + bool modify_problem) +{ + cuopt::timer_t stage_timer(std::numeric_limits::infinity()); +#ifdef DEBUG_CLIQUE_TABLE + double t_fill = 0.; + double t_coeff = 0.; + double t_sort = 0.; + double t_find = 0.; + double t_small = 0.; + double t_maps = 0.; + double t_extend = 0.; + double t_remove = 0.; +#endif + std::vector> knapsack_constraints; + std::unordered_set set_packing_constraints; + dual_simplex::csr_matrix_t A(problem.num_rows, problem.num_cols, 0); + problem.A.to_compressed_row(A); + fill_knapsack_constraints(problem, knapsack_constraints, A); +#ifdef DEBUG_CLIQUE_TABLE + t_fill = stage_timer.elapsed_time(); +#endif + make_coeff_positive_knapsack_constraint( + problem, knapsack_constraints, set_packing_constraints, tolerances); +#ifdef DEBUG_CLIQUE_TABLE + t_coeff = stage_timer.elapsed_time(); +#endif + sort_csr_by_constraint_coefficients(knapsack_constraints); +#ifdef DEBUG_CLIQUE_TABLE + t_sort = stage_timer.elapsed_time(); +#endif + // print_knapsack_constraints(knapsack_constraints); + // TODO think about getting min_clique_size according to some problem property + clique_config_t clique_config; + clique_table_t clique_table(2 * problem.num_cols, + clique_config.min_clique_size, + clique_config.max_clique_size_for_extension); + clique_table.tolerances = tolerances; + for (const auto& knapsack_constraint : knapsack_constraints) { + if (timer.check_time_limit()) { break; } + find_cliques_from_constraint(knapsack_constraint, clique_table); + } + if (timer.check_time_limit()) { return; } +#ifdef DEBUG_CLIQUE_TABLE + t_find = stage_timer.elapsed_time(); +#endif + CUOPT_LOG_DEBUG("Number of cliques: %d, additional cliques: %d", + clique_table.first.size(), + clique_table.addtl_cliques.size()); + // print_clique_table(clique_table); + // remove small cliques and add them to adj_list + remove_small_cliques(clique_table); +#ifdef DEBUG_CLIQUE_TABLE + t_small = stage_timer.elapsed_time(); +#endif + // fill var clique maps + fill_var_clique_maps(clique_table); +#ifdef DEBUG_CLIQUE_TABLE + t_maps = stage_timer.elapsed_time(); +#endif + extend_cliques( + knapsack_constraints, set_packing_constraints, clique_table, problem, A, modify_problem, timer); +#ifdef DEBUG_CLIQUE_TABLE + t_extend = stage_timer.elapsed_time(); + t_remove = t_extend; + CUOPT_LOG_DEBUG( + "Clique table timing (s): fill=%.6f coeff=%.6f sort=%.6f find=%.6f small=%.6f maps=%.6f " + "extend=%.6f remove=%.6f total=%.6f", + t_fill, + t_coeff - t_fill, + t_sort - t_coeff, + t_find - t_sort, + t_small - t_find, + t_maps - t_small, + t_extend - t_maps, + t_remove - t_extend, + t_remove); +#endif +} + +#define INSTANTIATE(F_TYPE) \ + template void find_initial_cliques( \ + dual_simplex::user_problem_t & problem, \ + typename mip_solver_settings_t::tolerances_t tolerances, \ + cuopt::timer_t & timer, \ + bool modify_problem); + +#if MIP_INSTANTIATE_FLOAT +INSTANTIATE(float) +#endif +#if MIP_INSTANTIATE_DOUBLE +INSTANTIATE(double) +#endif +#undef INSTANTIATE + +} // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cuh b/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cuh new file mode 100644 index 0000000000..a8261685ab --- /dev/null +++ b/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cuh @@ -0,0 +1,194 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, 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 +#include + +#include + +#include +#include +#include + +namespace cuopt::linear_programming::detail { + +struct clique_config_t { + int min_clique_size = 512; + int max_clique_size_for_extension = 128; +}; + +template +struct entry_t { + i_t col; + f_t val; + bool operator<(const entry_t& other) const { return val < other.val; } + bool operator<(double other) const { return val < other; } +}; + +template +struct knapsack_constraint_t { + std::vector> entries; + f_t rhs; + i_t cstr_idx; + bool is_set_packing = false; + bool is_set_partitioning = false; +}; + +template +struct addtl_clique_t { + i_t vertex_idx; + i_t clique_idx; + i_t start_pos_on_clique; +}; + +template +struct clique_table_t { + clique_table_t(i_t n_vertices, i_t min_clique_size_, i_t max_clique_size_for_extension_) + : min_clique_size(min_clique_size_), + max_clique_size_for_extension(max_clique_size_for_extension_), + var_clique_map_first(n_vertices), + var_clique_map_addtl(n_vertices), + adj_list_small_cliques(n_vertices), + var_degrees(n_vertices, -1), + n_variables(n_vertices / 2) + { + } + + std::unordered_set get_adj_set_of_var(i_t var_idx); + i_t get_degree_of_var(i_t var_idx); + bool check_adjacency(i_t var_idx1, i_t var_idx2); + + // keeps the large cliques in each constraint + std::vector> first; + // keeps the additional cliques + std::vector> addtl_cliques; + // TODO figure out the performance of lookup for the following: unordered_set vs vector + // keeps the indices of original(first) cliques that contain variable x + std::vector> var_clique_map_first; + // keeps the indices of additional cliques that contain variable x + std::vector> var_clique_map_addtl; + // adjacency list to keep small cliques, this basically keeps the vars share a small clique + // constraint + std::unordered_map> adj_list_small_cliques; + // degrees of each vertex + std::vector var_degrees; + // number of variables in the original problem + const i_t n_variables; + const i_t min_clique_size; + const i_t max_clique_size_for_extension; + typename mip_solver_settings_t::tolerances_t tolerances; +}; + +template +void find_initial_cliques(dual_simplex::user_problem_t& problem, + typename mip_solver_settings_t::tolerances_t tolerances, + cuopt::timer_t& timer, + bool modify_problem); + +} // namespace cuopt::linear_programming::detail + +// Possible application to rounding procedure, keeping it as reference + +// fix set of variables x_1, x_2, x_3,... in a bulk. Consider sorting according largest size GUB +// constraint(or some other criteria). + +// compute new activities on changed constraints, given that x_1=v_1, x_2=v_2, x_3=v_3: + +// if the current constraint is GUB + +// if at least two binary vars(note that some can be full integer) are common: (needs +// binary_vars_in_bulk^2 number of checks) + +// return infeasible + +// else + +// set L_r to 1. + +// else(non-GUB constraints) + +// greedy clique partitioning algorithm: + +// set L_r = sum(all positive coefficients on binary vars) + sum(min_activity contribution on +// non-binary vars) # note that the paper doesn't contain this part, since it only deals with binary + +// # iterate only on binary variables(i.e. vertices of B- and complements of B+) + +// start with highest weight vertex (v) among unmarked and mark it + +// find maximal clique among unmarked containing the vertex: (there are various algorithms to +// find maximal clique) + +// max_clique = {v} + +// L_r -= w_v + +// # prioritization is on higher weight vertex when there are equivalent max cliques? +// # we could try BFS to search multiple greedy paths +// for each unmarked vertex(w): + +// counter = 0 + +// for each vertex(k) in max_clique: + +// if(check_if_pair_shares_an_edge(w,k)) + +// counter++ + +// if counter == max_clique.size() + +// max_clique = max_clique U {w} + +// mark w as marked + +// if(L_r > UB) return infeasible + +// remove all fixed variables(original and newly propagated) from the conflict graph. !!!!!! still a +// bit unclear how to remove it from the adjaceny list data structure since it only supports +// additions!!!! + +// add newly discovered GUB constraints into dynamic adjacency list + +// do double probing to infer new edges(we need a heuristic to choose which pairs to probe) + +// check_if_pair_shares_an_edge(w,v): + +// check GUB constraints by traversing the double linked list: + +// on the column of variable w: + +// for each row: + +// if v is contained on the row + +// return true + +// check added edges on adjacency list: + +// k <- last[w] + +// while k != 0 + +// if(adj[k] == v) + +// return true + +// k <-next[k] + +// return false diff --git a/cpp/src/mip_heuristics/presolve/probing_cache.cu b/cpp/src/mip_heuristics/presolve/probing_cache.cu index 5ae89c700a..18f678c7e0 100644 --- a/cpp/src/mip_heuristics/presolve/probing_cache.cu +++ b/cpp/src/mip_heuristics/presolve/probing_cache.cu @@ -6,7 +6,6 @@ /* clang-format on */ #include "probing_cache.cuh" -#include "trivial_presolve.cuh" #include #include @@ -19,6 +18,8 @@ #include #include +#include + namespace cuopt::linear_programming::detail { template diff --git a/cpp/src/mip_heuristics/presolve/probing_cache.cuh b/cpp/src/mip_heuristics/presolve/probing_cache.cuh index abb0145054..91da6a15c8 100644 --- a/cpp/src/mip_heuristics/presolve/probing_cache.cuh +++ b/cpp/src/mip_heuristics/presolve/probing_cache.cuh @@ -87,7 +87,9 @@ class probing_cache_t { f_t first_probe, f_t second_probe, f_t integrality_tolerance); - + // add the results of probing cache to secondary CG structure if not already in a gub constraint. + // use the same activity computation that we will use in BP rounding. + // use GUB constraints to find fixings in bulk rounding std::unordered_map, 2>> probing_cache; std::mutex probing_cache_mutex; }; diff --git a/cpp/src/mip_heuristics/problem/problem.cu b/cpp/src/mip_heuristics/problem/problem.cu index d77e2e5f65..bc93a9d988 100644 --- a/cpp/src/mip_heuristics/problem/problem.cu +++ b/cpp/src/mip_heuristics/problem/problem.cu @@ -1097,6 +1097,7 @@ void problem_t::resize_constraints(size_t matrix_size, size_t n_variables) { raft::common::nvtx::range fun_scope("resize_constraints"); + auto prev_dual_size = lp_state.prev_dual.size(); coefficients.resize(matrix_size, handle_ptr->get_stream()); variables.resize(matrix_size, handle_ptr->get_stream()); reverse_constraints.resize(matrix_size, handle_ptr->get_stream()); @@ -1107,6 +1108,13 @@ void problem_t::resize_constraints(size_t matrix_size, combined_bounds.resize(constraint_size, handle_ptr->get_stream()); offsets.resize(constraint_size + 1, handle_ptr->get_stream()); reverse_offsets.resize(n_variables + 1, handle_ptr->get_stream()); + lp_state.prev_dual.resize(constraint_size, handle_ptr->get_stream()); + if (constraint_size > prev_dual_size) { + thrust::fill(handle_ptr->get_thrust_policy(), + lp_state.prev_dual.begin() + prev_dual_size, + lp_state.prev_dual.end(), + f_t{0}); + } } // note that these don't change the reverse structure @@ -2037,6 +2045,87 @@ void problem_t::preprocess_problem() preprocess_called = true; } +template +void problem_t::set_constraints_from_host_user_problem( + const cuopt::linear_programming::dual_simplex::user_problem_t& user_problem) +{ + raft::common::nvtx::range fun_scope("set_constraints_from_host_user_problem"); + cuopt_assert(user_problem.handle_ptr == handle_ptr, "handle mismatch"); + cuopt_assert(user_problem.num_cols == n_variables, "num cols mismatch"); + n_constraints = user_problem.num_rows; + cuopt_assert(user_problem.rhs.size() == static_cast(n_constraints), "rhs size mismatch"); + cuopt_assert(user_problem.row_sense.size() == static_cast(n_constraints), + "row sense size mismatch"); + cuopt_assert(user_problem.range_rows.size() == user_problem.range_value.size(), + "range rows/value size mismatch"); + + dual_simplex::csr_matrix_t csr_A(n_constraints, n_variables, user_problem.A.nnz()); + user_problem.A.to_compressed_row(csr_A); + nnz = csr_A.row_start[n_constraints]; + empty = (nnz == 0 && n_constraints == 0 && n_variables == 0); + + auto stream = handle_ptr->get_stream(); + cuopt::device_copy(coefficients, csr_A.x, stream); + cuopt::device_copy(variables, csr_A.j, stream); + cuopt::device_copy(offsets, csr_A.row_start, stream); + + std::vector h_constraint_lower_bounds(n_constraints); + std::vector h_constraint_upper_bounds(n_constraints); + std::vector range_value_per_row(n_constraints, f_t{0}); + std::vector is_range_row(n_constraints, 0); + for (size_t idx = 0; idx < user_problem.range_rows.size(); ++idx) { + auto row = user_problem.range_rows[idx]; + cuopt_assert(row >= 0 && row < n_constraints, "range row out of bounds"); + is_range_row[row] = 1; + range_value_per_row[row] = user_problem.range_value[idx]; + } + + const auto inf = std::numeric_limits::infinity(); + for (i_t i = 0; i < n_constraints; ++i) { + const f_t rhs = user_problem.rhs[i]; + const char sense = user_problem.row_sense[i]; + if (sense == 'E') { + h_constraint_lower_bounds[i] = rhs; + h_constraint_upper_bounds[i] = rhs; + if (is_range_row[i]) { h_constraint_upper_bounds[i] = rhs + range_value_per_row[i]; } + } else if (sense == 'G') { + h_constraint_lower_bounds[i] = rhs; + h_constraint_upper_bounds[i] = inf; + } else if (sense == 'L') { + h_constraint_lower_bounds[i] = -inf; + h_constraint_upper_bounds[i] = rhs; + } else { + cuopt_assert(false, "Unsupported row sense"); + } + } + + cuopt::device_copy(constraint_lower_bounds, h_constraint_lower_bounds, stream); + cuopt::device_copy(constraint_upper_bounds, h_constraint_upper_bounds, stream); + + if (!user_problem.row_names.empty()) { + row_names = user_problem.row_names; + } else if (row_names.size() != static_cast(n_constraints)) { + row_names.clear(); + } + + integer_fixed_problem = nullptr; + fixing_helpers.reduction_in_rhs.resize(n_constraints, stream); + auto prev_dual_size = lp_state.prev_dual.size(); + lp_state.prev_dual.resize(n_constraints, stream); + if (n_constraints > (i_t)prev_dual_size) { + thrust::fill(handle_ptr->get_thrust_policy(), + lp_state.prev_dual.begin() + prev_dual_size, + lp_state.prev_dual.end(), + f_t{0}); + } + handle_ptr->sync_stream(); + RAFT_CHECK_CUDA(stream); + + compute_transpose_of_problem(); + combined_bounds.resize(n_constraints, stream); + combine_constraint_bounds(*this, combined_bounds); +} + template bool problem_t::pre_process_assignment(rmm::device_uvector& assignment) { @@ -2095,7 +2184,6 @@ void problem_t::get_host_user_problem( csr_A.row_start = std::vector(cuopt::host_copy(offsets, stream)); csr_A.to_compressed_col(user_problem.A); - user_problem.rhs.resize(m); user_problem.row_sense.resize(m); user_problem.range_rows.clear(); diff --git a/cpp/src/mip_heuristics/problem/problem.cuh b/cpp/src/mip_heuristics/problem/problem.cuh index 6cd180a800..b9ca420820 100644 --- a/cpp/src/mip_heuristics/problem/problem.cuh +++ b/cpp/src/mip_heuristics/problem/problem.cuh @@ -121,6 +121,8 @@ class problem_t { void get_host_user_problem( cuopt::linear_programming::dual_simplex::user_problem_t& user_problem) const; + void set_constraints_from_host_user_problem( + const cuopt::linear_programming::dual_simplex::user_problem_t& user_problem); uint32_t get_fingerprint() const; diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 235d4500d2..e6f6d50b62 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -107,12 +107,16 @@ solution_t mip_solver_t::run_solver() context.problem_ptr->post_process_solution(sol); return sol; } - dm.timer = timer_; - const bool run_presolve = context.settings.presolver != presolver_t::None; - f_t time_limit = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC - ? std::numeric_limits::infinity() - : timer_.remaining_time(); - bool presolve_success = run_presolve ? dm.run_presolve(time_limit) : true; + dm.timer = timer_; + const bool run_presolve = context.settings.presolver != presolver_t::None; + f_t time_limit = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC + ? std::numeric_limits::infinity() + : timer_.remaining_time(); + double presolve_time_limit = std::min(0.1 * time_limit, 60.0); + presolve_time_limit = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC + ? std::numeric_limits::infinity() + : presolve_time_limit; + bool presolve_success = run_presolve ? dm.run_presolve(presolve_time_limit, timer_) : true; if (!presolve_success) { CUOPT_LOG_INFO("Problem proven infeasible in presolve"); solution_t sol(*context.problem_ptr); diff --git a/cpp/src/mip_heuristics/utils.cuh b/cpp/src/mip_heuristics/utils.cuh index 33712635e9..ffadc1f510 100644 --- a/cpp/src/mip_heuristics/utils.cuh +++ b/cpp/src/mip_heuristics/utils.cuh @@ -339,8 +339,9 @@ static void inline run_device_lambda(const rmm::cuda_stream_view& stream, Func f template f_t compute_rel_mip_gap(f_t user_obj, f_t solution_bound) { - if (user_obj == 0.0) { - return solution_bound == 0.0 ? 0.0 : std::numeric_limits::infinity(); + if (integer_equal(user_obj, 0.0, 1e-6)) { + return integer_equal(solution_bound, 0.0, 1e-6) ? 0.0 + : std::numeric_limits::infinity(); } return std::abs(user_obj - solution_bound) / std::abs(user_obj); } diff --git a/cpp/tests/mip/problem_test.cu b/cpp/tests/mip/problem_test.cu index 28f4f1f955..92fa6d41d1 100644 --- a/cpp/tests/mip/problem_test.cu +++ b/cpp/tests/mip/problem_test.cu @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -204,6 +205,95 @@ TEST(problem, run_small_tests) } } +namespace ds = cuopt::linear_programming::dual_simplex; + +template +void test_roundtrip_equivalence(i_t n_cnst, i_t n_var) +{ + raft::handle_t handle; + auto op_problem = create_problem(&handle, n_cnst, n_var); + dtl::problem_t problem(op_problem); + problem.preprocess_problem(); + + auto stream = handle.get_stream(); + + const auto n_constraints_before = problem.n_constraints; + const auto n_variables_before = problem.n_variables; + const auto nnz_before = problem.nnz; + + auto coefficients_before = cuopt::host_copy(problem.coefficients, stream); + auto variables_before = cuopt::host_copy(problem.variables, stream); + auto offsets_before = cuopt::host_copy(problem.offsets, stream); + auto constraint_lower_before = cuopt::host_copy(problem.constraint_lower_bounds, stream); + auto constraint_upper_before = cuopt::host_copy(problem.constraint_upper_bounds, stream); + auto variable_bounds_before = cuopt::host_copy(problem.variable_bounds, stream); + auto objective_before = cuopt::host_copy(problem.objective_coefficients, stream); + auto reverse_coefficients_before = cuopt::host_copy(problem.reverse_coefficients, stream); + auto reverse_constraints_before = cuopt::host_copy(problem.reverse_constraints, stream); + auto reverse_offsets_before = cuopt::host_copy(problem.reverse_offsets, stream); + + ds::user_problem_t host_problem(problem.handle_ptr); + problem.get_host_user_problem(host_problem); + + problem.set_constraints_from_host_user_problem(host_problem); + ASSERT_EQ(host_problem.lower.size(), static_cast(problem.n_variables)); + ASSERT_EQ(host_problem.upper.size(), static_cast(problem.n_variables)); + std::vector all_var_indices(problem.n_variables); + std::iota(all_var_indices.begin(), all_var_indices.end(), 0); + problem.update_variable_bounds(all_var_indices, host_problem.lower, host_problem.upper); + + EXPECT_EQ(problem.n_constraints, n_constraints_before); + EXPECT_EQ(problem.n_variables, n_variables_before); + EXPECT_EQ(problem.nnz, nnz_before); + + auto coefficients_after = cuopt::host_copy(problem.coefficients, stream); + auto variables_after = cuopt::host_copy(problem.variables, stream); + auto offsets_after = cuopt::host_copy(problem.offsets, stream); + auto constraint_lower_after = cuopt::host_copy(problem.constraint_lower_bounds, stream); + auto constraint_upper_after = cuopt::host_copy(problem.constraint_upper_bounds, stream); + auto variable_bounds_after = cuopt::host_copy(problem.variable_bounds, stream); + auto objective_after = cuopt::host_copy(problem.objective_coefficients, stream); + auto reverse_coefficients_after = cuopt::host_copy(problem.reverse_coefficients, stream); + auto reverse_constraints_after = cuopt::host_copy(problem.reverse_constraints, stream); + auto reverse_offsets_after = cuopt::host_copy(problem.reverse_offsets, stream); + + EXPECT_EQ(coefficients_before, coefficients_after) << "CSR coefficients differ"; + EXPECT_EQ(variables_before, variables_after) << "CSR column indices differ"; + EXPECT_EQ(offsets_before, offsets_after) << "CSR row offsets differ"; + EXPECT_EQ(objective_before, objective_after) << "objective coefficients differ"; + EXPECT_EQ(reverse_constraints_before, reverse_constraints_after) << "reverse constraints differ"; + EXPECT_EQ(reverse_offsets_before, reverse_offsets_after) << "reverse offsets differ"; + EXPECT_EQ(reverse_coefficients_before, reverse_coefficients_after) + << "reverse coefficients differ"; + + ASSERT_EQ(constraint_lower_before.size(), constraint_lower_after.size()); + for (size_t i = 0; i < constraint_lower_before.size(); ++i) { + EXPECT_NEAR(constraint_lower_before[i], constraint_lower_after[i], 1e-10) + << "constraint_lower_bounds[" << i << "]"; + } + ASSERT_EQ(constraint_upper_before.size(), constraint_upper_after.size()); + for (size_t i = 0; i < constraint_upper_before.size(); ++i) { + EXPECT_NEAR(constraint_upper_before[i], constraint_upper_after[i], 1e-10) + << "constraint_upper_bounds[" << i << "]"; + } + + ASSERT_EQ(variable_bounds_before.size(), variable_bounds_after.size()); + for (size_t i = 0; i < variable_bounds_before.size(); ++i) { + EXPECT_DOUBLE_EQ(variable_bounds_before[i].x, variable_bounds_after[i].x) + << "variable_bounds[" << i << "].lower"; + EXPECT_DOUBLE_EQ(variable_bounds_before[i].y, variable_bounds_after[i].y) + << "variable_bounds[" << i << "].upper"; + } +} + +TEST(problem, get_set_host_user_problem_roundtrip_preserves_problem) +{ + std::vector> cnst_var_vals = {{5, 20}, {20, 80}, {40, 200}}; + for (const auto& [nc, nv] : cnst_var_vals) { + test_roundtrip_equivalence(nc, nv); + } +} + static void fill_problem(optimization_problem_t& op_problem) { // Set A_CSR_matrix From d61b19655f7b8535a5cafb960258934ff75f06da Mon Sep 17 00:00:00 2001 From: Alice Boucher <160623740+aliceb-nv@users.noreply.github.com> Date: Wed, 4 Mar 2026 22:35:25 +0100 Subject: [PATCH 131/225] Fix variable bound violation in CPUFJ moves (#930) ## Issue Authors: - Alice Boucher (https://github.com/aliceb-nv) Approvers: - Rajesh Gandham (https://github.com/rg20) URL: https://github.com/NVIDIA/cuopt/pull/930 --- .../mip_heuristics/feasibility_jump/fj_cpu.cu | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu index 4d567c9ecb..b3bc0d688e 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu @@ -702,6 +702,21 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, raft::random::PCGenerator rng(fj_cpu.settings.seed + fj_cpu.iterations, 0, 0); cuopt_assert(var_idx < fj_cpu.view.pb.n_variables, "variable index out of bounds"); + f_t old_val = fj_cpu.h_assignment[var_idx]; + f_t new_val = old_val + delta; + if (is_integer_var(fj_cpu, var_idx)) { + cuopt_assert(fj_cpu.view.pb.integer_equal(new_val, round(new_val)), "new_val is not integer"); + new_val = round(new_val); + } + // clamp to var bounds + new_val = std::min(std::max(new_val, get_lower(fj_cpu.h_var_bounds[var_idx].get())), + get_upper(fj_cpu.h_var_bounds[var_idx].get())); + delta = new_val - old_val; + cuopt_assert(isfinite(new_val), "assignment is not finite"); + cuopt_assert(isfinite(delta), "applied delta is not finite"); + cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, new_val)), + "assignment not within bounds"); + // Update the LHSs of all involved constraints. auto [offset_begin, offset_end] = reverse_range_for_var(fj_cpu, var_idx); @@ -761,17 +776,7 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, } // update the assignment and objective proper - f_t new_val = fj_cpu.h_assignment[var_idx] + delta; - if (is_integer_var(fj_cpu, var_idx)) { - cuopt_assert(fj_cpu.view.pb.integer_equal(new_val, round(new_val)), "new_val is not integer"); - new_val = round(new_val); - } fj_cpu.h_assignment[var_idx] = new_val; - - cuopt_assert((check_variable_within_bounds(fj_cpu, var_idx, new_val)), - "assignment not within bounds"); - cuopt_assert(isfinite(new_val), "assignment is not finite"); - fj_cpu.h_incumbent_objective += fj_cpu.h_obj_coeffs[var_idx] * delta; if (fj_cpu.h_incumbent_objective < fj_cpu.h_best_objective && fj_cpu.violated_constraints.empty()) { @@ -786,11 +791,11 @@ static void apply_move(fj_cpu_climber_t& fj_cpu, fj_cpu.iterations_since_best = 0; CUOPT_LOG_TRACE("%sCPUFJ: new best objective: %g", fj_cpu.log_prefix.c_str(), - fj_cpu.pb_ptr->get_user_obj_from_solver_obj(fj_cpu.h_best_objective)); + fj_cpu.pb_ptr->get_user_obj_from_solver_obj(fj_cpu.h_incumbent_objective)); if (fj_cpu.improvement_callback) { double current_work_units = fj_cpu.work_units_elapsed.load(std::memory_order_acquire); fj_cpu.improvement_callback( - fj_cpu.h_best_objective, fj_cpu.h_assignment, current_work_units); + fj_cpu.h_incumbent_objective, fj_cpu.h_assignment, current_work_units); } fj_cpu.feasible_found = true; } @@ -1021,6 +1026,13 @@ static void recompute_lhs(fj_cpu_climber_t& fj_cpu) CPUFJ_NVTX_RANGE("CPUFJ::recompute_lhs"); cuopt_assert(fj_cpu.h_lhs.size() == fj_cpu.view.pb.n_constraints, "h_lhs size mismatch"); + // clamp to var bounds - defensive; apply_move should already have clamped appropriately + for (i_t var_idx = 0; var_idx < fj_cpu.view.pb.n_variables; ++var_idx) { + fj_cpu.h_assignment[var_idx] = std::min( + std::max(fj_cpu.h_assignment[var_idx].get(), get_lower(fj_cpu.h_var_bounds[var_idx].get())), + get_upper(fj_cpu.h_var_bounds[var_idx].get())); + } + fj_cpu.violated_constraints.clear(); fj_cpu.satisfied_constraints.clear(); fj_cpu.total_violations = 0; From 1c6f86be51f5f80023d57fe3b7cbdd032d99f02b Mon Sep 17 00:00:00 2001 From: Anandh Anandh <102690644+anandhkb@users.noreply.github.com> Date: Wed, 4 Mar 2026 19:31:25 -0800 Subject: [PATCH 132/225] Fix #903: Validate unique vehicle_ids in VRP fleet data (#917) - Add uniqueness check in validate_fleet_data() for vehicle_ids - Return clear error when duplicates detected - Update FleetData.vehicle_ids docstring - Add unit tests: duplicate rejection, unique acceptance, edge cases Authors: - Anandh Anandh (https://github.com/anandhkb) Approvers: - Rajesh Gandham (https://github.com/rg20) - Ishika Roy (https://github.com/Iroy30) URL: https://github.com/NVIDIA/cuopt/pull/917 --- .../cuopt_server/tests/test_set_fleet_data.py | 148 +++++++++++++++++- .../utils/routing/data_definition.py | 7 +- .../utils/routing/validation_fleet_data.py | 7 +- 3 files changed, 158 insertions(+), 4 deletions(-) diff --git a/python/cuopt_server/cuopt_server/tests/test_set_fleet_data.py b/python/cuopt_server/cuopt_server/tests/test_set_fleet_data.py index db2402eeba..e9526be784 100644 --- a/python/cuopt_server/cuopt_server/tests/test_set_fleet_data.py +++ b/python/cuopt_server/cuopt_server/tests/test_set_fleet_data.py @@ -1,10 +1,13 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 import copy from cuopt_server.tests.utils.utils import cuoptproc # noqa from cuopt_server.tests.utils.utils import RequestClient +from cuopt_server.utils.routing.validation_fleet_data import ( + validate_fleet_data, +) client = RequestClient() @@ -62,6 +65,117 @@ # FLEET DATA TESTING +# Test validate_fleet_data rejects duplicate vehicle_ids (no server required) +def test_validate_fleet_data_duplicate_vehicle_ids(): + vehicle_locations = [[0, 0], [0, 0], [0, 0]] + vehicle_ids_dup = ["Truck 1", "Truck 1", "Truck 1"] + + is_valid, msg = validate_fleet_data( + vehicle_ids=vehicle_ids_dup, + vehicle_locations=vehicle_locations, + capacities=None, + vehicle_time_windows=None, + vehicle_breaks=None, + vehicle_break_time_windows=None, + vehicle_break_durations=None, + vehicle_break_locations=None, + vehicle_types=None, + vehicle_types_dict={}, + vehicle_order_match=None, + skip_first_trips=None, + drop_return_trips=None, + min_vehicles=None, + vehicle_max_costs=None, + vehicle_max_times=None, + vehicle_fixed_costs=None, + ) + assert is_valid is False + assert "unique" in msg.lower() and "duplicate" in msg.lower() + + +# Test validate_fleet_data accepts unique vehicle_ids (no server required) +def test_validate_fleet_data_unique_vehicle_ids(): + vehicle_locations = [[0, 0], [0, 0]] + vehicle_ids_unique = ["Truck 1", "Truck 2"] + + is_valid, msg = validate_fleet_data( + vehicle_ids=vehicle_ids_unique, + vehicle_locations=vehicle_locations, + capacities=None, + vehicle_time_windows=None, + vehicle_breaks=None, + vehicle_break_time_windows=None, + vehicle_break_durations=None, + vehicle_break_locations=None, + vehicle_types=None, + vehicle_types_dict={}, + vehicle_order_match=None, + skip_first_trips=None, + drop_return_trips=None, + min_vehicles=None, + vehicle_max_costs=None, + vehicle_max_times=None, + vehicle_fixed_costs=None, + ) + assert is_valid is True + assert msg == "Valid Fleet Data" + + +# Test validate_fleet_data with vehicle_ids=None passes (no server required) +def test_validate_fleet_data_vehicle_ids_none(): + vehicle_locations = [[0, 0], [0, 0]] + + is_valid, msg = validate_fleet_data( + vehicle_ids=None, + vehicle_locations=vehicle_locations, + capacities=None, + vehicle_time_windows=None, + vehicle_breaks=None, + vehicle_break_time_windows=None, + vehicle_break_durations=None, + vehicle_break_locations=None, + vehicle_types=None, + vehicle_types_dict={}, + vehicle_order_match=None, + skip_first_trips=None, + drop_return_trips=None, + min_vehicles=None, + vehicle_max_costs=None, + vehicle_max_times=None, + vehicle_fixed_costs=None, + ) + assert is_valid is True + assert msg == "Valid Fleet Data" + + +# Test validate_fleet_data with single vehicle (no server required) +def test_validate_fleet_data_single_vehicle(): + vehicle_locations = [[0, 0]] + vehicle_ids_single = ["Truck 1"] + + is_valid, msg = validate_fleet_data( + vehicle_ids=vehicle_ids_single, + vehicle_locations=vehicle_locations, + capacities=None, + vehicle_time_windows=None, + vehicle_breaks=None, + vehicle_break_time_windows=None, + vehicle_break_durations=None, + vehicle_break_locations=None, + vehicle_types=None, + vehicle_types_dict={}, + vehicle_order_match=None, + skip_first_trips=None, + drop_return_trips=None, + min_vehicles=None, + vehicle_max_costs=None, + vehicle_max_times=None, + vehicle_fixed_costs=None, + ) + assert is_valid is True + assert msg == "Valid Fleet Data" + + # Test validation error when multiple cost matrices set without vehicle types def test_invalid_vehicle_types(cuoptproc): # noqa matrix_data = { @@ -101,6 +215,38 @@ def test_valid_full_set_fleet_data(cuoptproc): # noqa assert response_set.status_code == 200 +# Testing duplicate vehicle_ids rejected (issue #903) +def test_duplicate_vehicle_ids_set_fleet_data(cuoptproc): # noqa + test_data = copy.deepcopy(valid_data) + test_data["fleet_data"]["vehicle_ids"] = [ + "veh-1", + "veh-2", + "veh-1", + "veh-4", + ] + + response_set = client.post("/cuopt/request", json=test_data) + assert response_set.status_code == 400 + assert response_set.json() == { + "error": "vehicle_ids must be unique; duplicates are not allowed", + "error_result": True, + } + + +# Testing valid with unique vehicle_ids +def test_valid_unique_vehicle_ids_set_fleet_data(cuoptproc): # noqa + test_data = copy.deepcopy(valid_data) + test_data["fleet_data"]["vehicle_ids"] = [ + "veh-1", + "veh-2", + "veh-3", + "veh-4", + ] + + response_set = client.post("/cuopt/request", json=test_data) + assert response_set.status_code == 200 + + # Testing valid with minimal required parameters def test_valid_minimal_set_fleet_data(cuoptproc): # noqa test_data = copy.deepcopy(valid_data) diff --git a/python/cuopt_server/cuopt_server/utils/routing/data_definition.py b/python/cuopt_server/cuopt_server/utils/routing/data_definition.py index 4a0abda553..ba1b5e4e52 100644 --- a/python/cuopt_server/cuopt_server/utils/routing/data_definition.py +++ b/python/cuopt_server/cuopt_server/utils/routing/data_definition.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 import copy @@ -232,7 +232,10 @@ class FleetData(StrictModel): vehicle_ids: Optional[List[str]] = Field( default=None, examples=[["veh-1", "veh-2"]], - description=("List of the vehicle ids or names provided as a string."), + description=( + "List of the vehicle ids or names provided as a string. " + "Must be unique; duplicates are not allowed." + ), ) capacities: Optional[List[List[int]]] = Field( default=None, diff --git a/python/cuopt_server/cuopt_server/utils/routing/validation_fleet_data.py b/python/cuopt_server/cuopt_server/utils/routing/validation_fleet_data.py index 5a505e8ef6..ff94ccfa79 100644 --- a/python/cuopt_server/cuopt_server/utils/routing/validation_fleet_data.py +++ b/python/cuopt_server/cuopt_server/utils/routing/validation_fleet_data.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 @@ -85,6 +85,11 @@ def validate_fleet_data( if vehicle_ids is not None: fleet_length_check_array.append(len(vehicle_ids)) + if len(vehicle_ids) != len(set(vehicle_ids)): + return ( + False, + "vehicle_ids must be unique; duplicates are not allowed", + ) if capacities is not None: fleet_length_check_array.append(len(capacities[0])) From 1dc32de3176e5679070d4ceddf1a155de97fc422 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 5 Mar 2026 07:50:50 -0800 Subject: [PATCH 133/225] build fixes --- cpp/CMakeLists.txt | 3 - cpp/src/CMakeLists.txt | 1 + cpp/src/barrier/barrier.cu | 2 +- cpp/src/barrier/cusparse_view.cu | 6 +- cpp/src/dual_simplex/bounds_strengthening.cpp | 6 +- .../dual_simplex/dual_simplex_features.hpp | 2 +- cpp/src/dual_simplex/folding.cpp | 10 +- cpp/src/dual_simplex/primal.cpp | 2 +- cpp/src/dual_simplex/sparse_matrix.hpp | 4 +- .../diversity/diversity_manager.cu | 2 +- .../diversity/diversity_manager.cuh | 6 +- .../recombiners/bound_prop_recombiner.cuh | 12 +- .../diversity/recombiners/fp_recombiner.cuh | 21 +- .../diversity/recombiners/recombiner.cuh | 14 +- cpp/src/mip_heuristics/diversity/weights.cuh | 2 +- .../feasibility_jump/feasibility_jump.cu | 6 +- .../feasibility_jump/feasibility_jump.cuh | 3 +- .../feasibility_jump_impl_common.cuh | 11 +- .../feasibility_jump_kernels.cu | 14 +- .../mip_heuristics/feasibility_jump/fj_cpu.cu | 93 ------ .../feasibility_pump/feasibility_pump.cu | 3 +- .../line_segment_search.cuh | 2 +- .../local_search/local_search.cuh | 25 +- .../local_search/rounding/constraint_prop.cu | 21 +- .../mip_heuristics/presolve/probing_cache.cu | 28 +- cpp/src/mip_heuristics/problem/problem.cuh | 2 - .../mip_heuristics/relaxed_lp/relaxed_lp.cu | 29 +- cpp/src/mip_heuristics/solution/solution.cu | 3 +- cpp/src/mip_heuristics/solver.cu | 22 +- cpp/src/utilities/work_limit_context.hpp | 2 + cpp/src/utilities/work_limit_timer.hpp | 33 +-- cpp/src/utilities/work_unit_predictor.cpp | 6 +- cpp/src/utilities/work_unit_predictor.hpp | 3 +- cpp/tests/CMakeLists.txt | 12 +- cpp/tests/mip/diversity_test.cu | 49 ++-- cpp/tests/mip/feasibility_jump_tests.cu | 5 +- cpp/tests/mip/load_balancing_test.cu | 3 + cpp/tests/mip/local_search_test.cu | 52 ++-- cpp/tests/mip/mip_utils.cuh | 7 + cpp/tests/mip/presolve_test.cu | 25 +- cpp/tests/mip/unit_test.cu | 274 +++++++++--------- scripts/README_REGRESSION.md | 14 +- scripts/train_regressor.py | 1 - 43 files changed, 378 insertions(+), 463 deletions(-) diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 50c54f601f..cb6fd779d9 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -65,9 +65,6 @@ message(VERBOSE "cuOpt: fatbin: ${WRITE_FATBIN}") # CUDA runtime rapids_cuda_init_runtime(USE_STATIC ON) -message(STATUS "HEY") -find_package(CUDAToolkit) -message(STATUS "HEY") rapids_find_package(CUDAToolkit REQUIRED BUILD_EXPORT_SET cuopt-exports diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index c99210bf34..75e20c7b81 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -7,6 +7,7 @@ set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_predictor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_scheduler.cpp) add_subdirectory(pdlp) diff --git a/cpp/src/barrier/barrier.cu b/cpp/src/barrier/barrier.cu index 015e97cf14..6ad48e2ba7 100644 --- a/cpp/src/barrier/barrier.cu +++ b/cpp/src/barrier/barrier.cu @@ -1390,7 +1390,7 @@ class iteration_data_t { // v = alpha * A * w + beta * v = alpha * A * Dinv * A^T * y + beta * v matrix_vector_multiply(A, alpha, w, beta, v); if (debug) { - printf("||A|| = %.16e\n", vector_norm2(A.x.underlying())); + printf("||A|| = %.16e\n", vector_norm2>(A.x)); printf("||w|| = %.16e\n", vector_norm2(w)); printf("||v|| = %.16e\n", vector_norm2(v)); } diff --git a/cpp/src/barrier/cusparse_view.cu b/cpp/src/barrier/cusparse_view.cu index d762fbc009..b7673eacd5 100644 --- a/cpp/src/barrier/cusparse_view.cu +++ b/cpp/src/barrier/cusparse_view.cu @@ -159,9 +159,9 @@ cusparse_view_t::cusparse_view_t(raft::handle_t const* handle_ptr, A_indices_ = device_copy(indices, handle_ptr->get_stream()); A_data_ = device_copy(data, handle_ptr->get_stream()); - A_T_offsets_ = device_copy(A.col_start.underlying(), handle_ptr->get_stream()); - A_T_indices_ = device_copy(A.i.underlying(), handle_ptr->get_stream()); - A_T_data_ = device_copy(A.x.underlying(), handle_ptr->get_stream()); + A_T_offsets_ = device_copy(A.col_start, handle_ptr->get_stream()); + A_T_indices_ = device_copy(A.i, handle_ptr->get_stream()); + A_T_data_ = device_copy(A.x, handle_ptr->get_stream()); cusparseCreateCsr(&A_, rows, diff --git a/cpp/src/dual_simplex/bounds_strengthening.cpp b/cpp/src/dual_simplex/bounds_strengthening.cpp index 6506ce4873..3f40110740 100644 --- a/cpp/src/dual_simplex/bounds_strengthening.cpp +++ b/cpp/src/dual_simplex/bounds_strengthening.cpp @@ -34,9 +34,9 @@ static inline bool check_infeasibility(f_t min_a, f_t max_a, f_t cnst_lb, f_t cn #define DEBUG_BOUND_STRENGTHENING 0 -template -void print_bounds_stats(const std::vector& lower, - const std::vector& upper, +template +void print_bounds_stats(const LowerVec& lower, + const UpperVec& upper, const simplex_solver_settings_t& settings, const std::string msg) { diff --git a/cpp/src/dual_simplex/dual_simplex_features.hpp b/cpp/src/dual_simplex/dual_simplex_features.hpp index 3b1de9d296..04164c55e6 100644 --- a/cpp/src/dual_simplex/dual_simplex_features.hpp +++ b/cpp/src/dual_simplex/dual_simplex_features.hpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include diff --git a/cpp/src/dual_simplex/folding.cpp b/cpp/src/dual_simplex/folding.cpp index e67812d894..f851e51d79 100644 --- a/cpp/src/dual_simplex/folding.cpp +++ b/cpp/src/dual_simplex/folding.cpp @@ -39,8 +39,8 @@ constexpr int8_t kInactive = 0; template void find_vertices_to_refine(const std::unordered_set& refining_color_vertices, - const cuopt::ins_vector& offset, - const cuopt::ins_vector& vertex_list, + const std::vector& offset, + const std::vector& vertex_list, const std::vector& color_map, std::vector& marked_vertices, std::vector& vertices_to_refine, @@ -77,9 +77,9 @@ template void compute_sums_of_refined_vertices(i_t refining_color, const std::unordered_set& refining_color_vertices, const std::vector& vertices_to_refine, - const cuopt::ins_vector& offsets, - const cuopt::ins_vector& vertex_list, - const cuopt::ins_vector& weight_list, + const std::vector& offsets, + const std::vector& vertex_list, + const std::vector& weight_list, const std::vector& color_map, std::vector& vertex_to_sum, std::vector& max_sum_by_color) diff --git a/cpp/src/dual_simplex/primal.cpp b/cpp/src/dual_simplex/primal.cpp index 6fcd9c88c4..3f2a939aa9 100644 --- a/cpp/src/dual_simplex/primal.cpp +++ b/cpp/src/dual_simplex/primal.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include namespace cuopt::linear_programming::dual_simplex { diff --git a/cpp/src/dual_simplex/sparse_matrix.hpp b/cpp/src/dual_simplex/sparse_matrix.hpp index b6d3ee9aae..ea006b7b27 100644 --- a/cpp/src/dual_simplex/sparse_matrix.hpp +++ b/cpp/src/dual_simplex/sparse_matrix.hpp @@ -185,8 +185,8 @@ class csr_matrix_t { static_assert(std::is_signed_v); }; -template -void cumulative_sum(std::vector& inout, std::vector& output); +template +void cumulative_sum(std::vector& inout, OutputVector& output); template i_t coo_to_csc(const std::vector& Ai, diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 2bdda84ac7..906ce4a544 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -208,7 +208,7 @@ bool diversity_manager_t::run_presolve(f_t time_limit, timer_t global_ // Run probing cache before trivial presolve to discover variable implications const f_t max_time_on_probing = diversity_config.max_time_on_probing; f_t time_for_probing_cache = std::min(max_time_on_probing, time_limit); - timer_t probing_timer{time_for_probing_cache}; + work_limit_timer_t probing_timer(context.gpu_heur_loop, time_for_probing_cache); // this function computes probing cache, finds singletons, substitutions and changes the problem bool problem_is_infeasible = compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh index d4e24bdeaf..8d41b61f46 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh @@ -26,6 +26,7 @@ #include #include #include +#include namespace cuopt::linear_programming::detail { @@ -63,8 +64,9 @@ class diversity_manager_t { solution_t& sol2); bool run_local_search(solution_t& solution, const weight_t& weights, - timer_t& timer, + work_limit_timer_t& timer, ls_config_t& ls_config); + bool work_limit_reached(); void set_simplex_solution(const std::vector& solution, const std::vector& dual_solution, @@ -79,7 +81,7 @@ class diversity_manager_t { rmm::device_uvector lp_dual_optimal_solution; std::atomic simplex_solution_exists{false}; local_search_t ls; - cuopt::timer_t timer; + cuopt::work_limit_timer_t timer; bound_prop_recombiner_t bound_prop_recombiner; fp_recombiner_t fp_recombiner; line_segment_recombiner_t line_segment_recombiner; diff --git a/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh index 4b32c9fa4f..edcfa5c717 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh @@ -173,9 +173,10 @@ class bound_prop_recombiner_t : public recombiner_t { n_different_vars, n_vars_from_other, n_vars_from_guiding); - CUOPT_LOG_TRACE("BP_DET: remaining_indices_hash=0x%x (first %d elements)", - detail::compute_hash(this->remaining_indices), - std::min((i_t)10, n_vars_from_other)); + CUOPT_LOG_TRACE( + "BP_DET: remaining_indices_hash=0x%x (first %d elements)", + detail::compute_hash(make_span(this->remaining_indices), a.handle_ptr->get_stream()), + std::min((i_t)10, n_vars_from_other)); CUOPT_LOG_TRACE("BP_DET: guiding_feasible=%d other_feasible=%d expensive_to_fix=%d", guiding_solution.get_feasible(), other_solution.get_feasible(), @@ -201,7 +202,7 @@ class bound_prop_recombiner_t : public recombiner_t { if (guiding_solution.get_feasible() && !a.problem_ptr->expensive_to_fix_vars) { CUOPT_LOG_DEBUG("BP_DET: Taking FEASIBLE path (with variable fixing)"); this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); - CUOPT_LOG_DEBUG("BP_DET: vars_to_fix_hash=0x%x", detail::compute_hash(vars_to_fix)); + CUOPT_LOG_DEBUG("BP_DET: vars_to_fix_size=%lu", vars_to_fix.size()); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); CUOPT_LOG_DEBUG("BP_DET: fixed_problem_fingerprint=0x%x variable_map_size=%lu", fixed_problem.get_fingerprint(), @@ -256,7 +257,8 @@ class bound_prop_recombiner_t : public recombiner_t { get_probing_values_for_infeasible( guiding_solution, other_solution, offspring, probing_values, n_vars_from_other); probing_config.probing_values = host_copy(probing_values, offspring.handle_ptr->get_stream()); - CUOPT_LOG_TRACE("BP_DET: probing_values_hash=0x%x", detail::compute_hash(probing_values)); + CUOPT_LOG_TRACE("BP_DET: probing_values_hash=0x%x", + detail::compute_hash(make_span(probing_values), a.handle_ptr->get_stream())); constraint_prop.apply_round(offspring, lp_run_time_after_feasible, timer, probing_config); } CUOPT_LOG_TRACE("BP_DET: After apply_round: offspring_hash=0x%x feasible=%d", diff --git a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh index 3c93c438a7..d809aa0fb3 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh @@ -69,17 +69,20 @@ class fp_recombiner_t : public recombiner_t { double work = static_cast(n_vars_from_other); CUOPT_LOG_DEBUG( "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); - CUOPT_LOG_TRACE("FP rec: offspring hash 0x%x, vars to fix 0x%x", - offspring.get_hash(), - detail::compute_hash(vars_to_fix)); + CUOPT_LOG_TRACE( + "FP rec: offspring hash 0x%x, vars to fix 0x%x", + offspring.get_hash(), + detail::compute_hash(make_span(vars_to_fix), offspring.handle_ptr->get_stream())); this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); - CUOPT_LOG_TRACE("FP rec post computevarstofix: offspring hash 0x%x, vars to fix 0x%x", - offspring.get_hash(), - detail::compute_hash(vars_to_fix)); + CUOPT_LOG_TRACE( + "FP rec post computevarstofix: offspring hash 0x%x, vars to fix 0x%x", + offspring.get_hash(), + detail::compute_hash(make_span(vars_to_fix), offspring.handle_ptr->get_stream())); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); - CUOPT_LOG_TRACE("FP rec: fixed_problem hash 0x%x assigned hash 0x%x", - fixed_problem.get_fingerprint(), - detail::compute_hash(fixed_assignment)); + CUOPT_LOG_TRACE( + "FP rec: fixed_problem hash 0x%x assigned hash 0x%x", + fixed_problem.get_fingerprint(), + detail::compute_hash(make_span(fixed_assignment), offspring.handle_ptr->get_stream())); fixed_problem.check_problem_representation(true); if (!guiding_solution.get_feasible() && !other_solution.get_feasible()) { CUOPT_LOG_TRACE("FP rec: running LP with infeasibility detection"); diff --git a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh index cff1f96d47..eccbe658f6 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh @@ -97,9 +97,10 @@ class recombiner_t { this->remaining_indices.data(), this->remaining_indices.data() + remaining_variables); - CUOPT_LOG_TRACE("remaining indices hash 0x%x, size %d", - detail::compute_hash(this->remaining_indices), - remaining_variables); + CUOPT_LOG_TRACE( + "remaining indices hash 0x%x, size %d", + detail::compute_hash(make_span(this->remaining_indices), a.handle_ptr->get_stream()), + remaining_variables); auto vec_remaining_indices = host_copy(this->remaining_indices.data(), remaining_variables, a.handle_ptr->get_stream()); @@ -181,9 +182,12 @@ class recombiner_t { i_t n_vars_from_guiding) { vars_to_fix.resize(n_vars_from_guiding, offspring.handle_ptr->get_stream()); - CUOPT_LOG_TRACE("remaining indices hash 0x%x", detail::compute_hash(this->remaining_indices)); + CUOPT_LOG_TRACE( + "remaining indices hash 0x%x", + detail::compute_hash(make_span(this->remaining_indices), offspring.handle_ptr->get_stream())); CUOPT_LOG_TRACE("integer_indices hash 0x%x", - detail::compute_hash(offspring.problem_ptr->integer_indices)); + detail::compute_hash(make_span(offspring.problem_ptr->integer_indices), + offspring.handle_ptr->get_stream())); // set difference needs two sorted arrays thrust::sort(offspring.handle_ptr->get_thrust_policy(), this->remaining_indices.data(), diff --git a/cpp/src/mip_heuristics/diversity/weights.cuh b/cpp/src/mip_heuristics/diversity/weights.cuh index 0126f6a059..fbe72aba8e 100644 --- a/cpp/src/mip_heuristics/diversity/weights.cuh +++ b/cpp/src/mip_heuristics/diversity/weights.cuh @@ -12,7 +12,7 @@ #include #include -#include +#include namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu index dac47e4b11..a975e5a7cf 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu @@ -1295,9 +1295,9 @@ i_t fj_t::solve(solution_t& solution) solution.get_hash(), pb_ptr->get_fingerprint()); CUOPT_LOG_DEBUG("FJ: weights hash %x, left weights hash %x, right weights hash %x", - detail::compute_hash(cstr_weights), - detail::compute_hash(cstr_left_weights), - detail::compute_hash(cstr_right_weights)); + detail::compute_hash(cstr_weights, handle_ptr->get_stream()), + detail::compute_hash(cstr_left_weights, handle_ptr->get_stream()), + detail::compute_hash(cstr_right_weights, handle_ptr->get_stream())); bool deterministic = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; if (deterministic) { settings.work_limit = settings.time_limit; } diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cuh b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cuh index 38d6e2f102..b9495cd282 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cuh +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cuh @@ -136,7 +136,8 @@ struct fj_staged_score_t { int32_t base{std::numeric_limits::lowest()}; int32_t bonus{std::numeric_limits::lowest()}; - fj_staged_score_t() = default; + fj_staged_score_t() = default; + HDI fj_staged_score_t(int32_t base_, int32_t bonus_) : base(base_), bonus(bonus_) {} fj_staged_score_t(const fj_staged_score_t&) = default; fj_staged_score_t(fj_staged_score_t&&) = default; fj_staged_score_t& operator=(const fj_staged_score_t&) = default; diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_impl_common.cuh b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_impl_common.cuh index e57f0ec9e2..bd2f7315f5 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_impl_common.cuh +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_impl_common.cuh @@ -103,7 +103,9 @@ HDI std::pair feas_score_constraint( f_t cstr_coeff, f_t c_lb, f_t c_ub, - f_t current_lhs) + f_t current_lhs, + f_t cstr_left_weight = std::numeric_limits::quiet_NaN(), + f_t cstr_right_weight = std::numeric_limits::quiet_NaN()) { cuopt_assert(isfinite(delta), "invalid delta"); cuopt_assert(cstr_coeff != 0 && isfinite(cstr_coeff), "invalid coefficient"); @@ -123,8 +125,11 @@ HDI std::pair feas_score_constraint( // TODO: broadcast left/right weights to a csr_offset-indexed table? local minimums // usually occur on a rarer basis (around 50 iteratiosn to 1 local minimum) // likely unreasonable and overkill however - f_t cstr_weight = - bound_idx == 0 ? fj.cstr_left_weights[cstr_idx] : fj.cstr_right_weights[cstr_idx]; + f_t cstr_weight = bound_idx == 0 ? cstr_left_weight : cstr_right_weight; + if (!isfinite(cstr_weight)) { + cstr_weight = + bound_idx == 0 ? fj.cstr_left_weights[cstr_idx] : fj.cstr_right_weights[cstr_idx]; + } f_t sign = bound_idx == 0 ? -1 : 1; f_t rhs = bounds[bound_idx] * sign; f_t old_lhs = current_lhs * sign; diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu index 9f6f507008..335646d1bc 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_kernels.cu @@ -318,8 +318,8 @@ DI std::pair::move_score_info_t> compute_best_mtm( f_t c_lb = fj.pb.constraint_lower_bounds[cstr_idx]; f_t c_ub = fj.pb.constraint_upper_bounds[cstr_idx]; f_t new_val; - auto [delta_ij, sign, slack, cstr_tolerance] = get_mtm_for_constraint( - fj, var_idx, cstr_idx, cstr_coeff, c_lb, c_ub, fj.incumbent_assignment, fj.incumbent_lhs); + auto [delta_ij, sign, slack, cstr_tolerance] = + get_mtm_for_constraint(fj, var_idx, cstr_idx, cstr_coeff, c_lb, c_ub); if (fj.pb.is_integer_var(var_idx)) { new_val = cstr_coeff * sign > 0 ? floor(old_val + delta_ij + fj.pb.tolerances.integrality_tolerance) @@ -760,14 +760,8 @@ DI void update_lift_moves(typename fj_t::climber_data_t::view_t fj) // Process each bound separately, as both are satified and may both be finite // otherwise range constraints aren't correctly handled for (auto [bound, sign] : {std::make_tuple(c_lb, -1), std::make_tuple(c_ub, 1)}) { - auto [delta, slack] = get_mtm_for_bound(fj, - var_idx, - cstr_idx, - cstr_coeff, - bound, - sign, - fj.incumbent_assignment, - fj.incumbent_lhs); + auto [delta, slack] = + get_mtm_for_bound(fj, var_idx, cstr_idx, cstr_coeff, bound, sign); if (cstr_coeff * sign < 0) { if (fj.pb.is_integer_var(var_idx)) delta = ceil(delta); diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu index b3bc0d688e..6e30c48fd2 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu @@ -107,99 +107,6 @@ thrust::tuple get_mtm_for_constraint( return {delta_ij, sign, slack, cstr_tolerance}; } -template -std::pair feas_score_constraint(const typename fj_t::climber_data_t::view_t& fj, - i_t var_idx, - f_t delta, - i_t cstr_idx, - f_t cstr_coeff, - f_t c_lb, - f_t c_ub, - f_t current_lhs, - f_t left_weight, - f_t right_weight) -{ - cuopt_assert(isfinite(delta), "invalid delta"); - cuopt_assert(cstr_coeff != 0 && isfinite(cstr_coeff), "invalid coefficient"); - - f_t base_feas = 0; - f_t bonus_robust = 0; - - f_t bounds[2] = {c_lb, c_ub}; - cuopt_assert(isfinite(c_lb) || isfinite(c_ub), "no range"); - for (i_t bound_idx = 0; bound_idx < 2; ++bound_idx) { - if (!isfinite(bounds[bound_idx])) continue; - - // factor to correct the lhs/rhs to turn a lb <= lhs <= ub constraint into - // two virtual leq constraints "lhs <= ub" and "-lhs <= -lb" in order to match - // the convention of the paper - - // TODO: broadcast left/right weights to a csr_offset-indexed table? local minimums - // usually occur on a rarer basis (around 50 iteratiosn to 1 local minimum) - // likely unreasonable and overkill however - f_t cstr_weight = bound_idx == 0 ? left_weight : right_weight; - f_t sign = bound_idx == 0 ? -1 : 1; - f_t rhs = bounds[bound_idx] * sign; - f_t old_lhs = current_lhs * sign; - f_t new_lhs = (current_lhs + cstr_coeff * delta) * sign; - f_t old_slack = rhs - old_lhs; - f_t new_slack = rhs - new_lhs; - - cuopt_assert(isfinite(cstr_weight), "invalid weight"); - cuopt_assert(cstr_weight >= 0, "invalid weight"); - cuopt_assert(isfinite(old_lhs), ""); - cuopt_assert(isfinite(new_lhs), ""); - cuopt_assert(isfinite(old_slack) && isfinite(new_slack), ""); - - f_t cstr_tolerance = fj.get_corrected_tolerance(cstr_idx, c_lb, c_ub); - - bool old_viol = fj.excess_score(cstr_idx, current_lhs, c_lb, c_ub) < -cstr_tolerance; - bool new_viol = - fj.excess_score(cstr_idx, current_lhs + cstr_coeff * delta, c_lb, c_ub) < -cstr_tolerance; - - bool old_sat = old_lhs < rhs + cstr_tolerance; - bool new_sat = new_lhs < rhs + cstr_tolerance; - - // equality - if (fj.pb.integer_equal(c_lb, c_ub)) { - if (!old_viol) cuopt_assert(old_sat == !old_viol, ""); - if (!new_viol) cuopt_assert(new_sat == !new_viol, ""); - } - - // if it would feasibilize this constraint - if (!old_sat && new_sat) { - cuopt_assert(old_viol, ""); - base_feas += cstr_weight; - } - // would cause this constraint to be violated - else if (old_sat && !new_sat) { - cuopt_assert(new_viol, ""); - base_feas -= cstr_weight; - } - // simple improvement - else if (!old_sat && !new_sat && old_lhs > new_lhs) { - cuopt_assert(old_viol && new_viol, ""); - base_feas += (i_t)(cstr_weight * fj.settings->parameters.excess_improvement_weight); - } - // simple worsening - else if (!old_sat && !new_sat && old_lhs <= new_lhs) { - cuopt_assert(old_viol && new_viol, ""); - base_feas -= (i_t)(cstr_weight * fj.settings->parameters.excess_improvement_weight); - } - - // robustness score bonus if this would leave some strick slack - bool old_stable = old_lhs < rhs - cstr_tolerance; - bool new_stable = new_lhs < rhs - cstr_tolerance; - if (!old_stable && new_stable) { - bonus_robust += cstr_weight; - } else if (old_stable && !new_stable) { - bonus_robust -= cstr_weight; - } - } - - return {base_feas, bonus_robust}; -} - static constexpr double BIGVAL_THRESHOLD = 1e20; template diff --git a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu index f28faec249..c035479cae 100644 --- a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu @@ -247,7 +247,8 @@ bool feasibility_pump_t::round(solution_t& solution) { bool result; CUOPT_LOG_DEBUG("Rounding the point"); - timer_t bounds_prop_timer(std::max(0.05, std::min(0.5, timer.remaining_time() / 10.))); + work_limit_timer_t bounds_prop_timer(context.gpu_heur_loop, + std::max(0.05, std::min(0.5, timer.remaining_time() / 10.))); const f_t lp_run_time_after_feasible = 0.; bool old_var = constraint_prop.round_all_vars; f_t old_time = constraint_prop.max_time_for_bounds_prop; diff --git a/cpp/src/mip_heuristics/local_search/line_segment_search/line_segment_search.cuh b/cpp/src/mip_heuristics/local_search/line_segment_search/line_segment_search.cuh index ab849176ed..7a040ddbd2 100644 --- a/cpp/src/mip_heuristics/local_search/line_segment_search/line_segment_search.cuh +++ b/cpp/src/mip_heuristics/local_search/line_segment_search/line_segment_search.cuh @@ -9,7 +9,7 @@ #include #include -#include +#include namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip_heuristics/local_search/local_search.cuh b/cpp/src/mip_heuristics/local_search/local_search.cuh index a36688d71d..a22a810712 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cuh +++ b/cpp/src/mip_heuristics/local_search/local_search.cuh @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include @@ -58,32 +58,35 @@ class local_search_t { void start_cpufj_scratch_threads(population_t& population); void start_cpufj_lptopt_scratch_threads(population_t& population); void stop_cpufj_scratch_threads(); - void generate_fast_solution(solution_t& solution, timer_t timer); + void generate_fast_solution(solution_t& solution, work_limit_timer_t timer); bool generate_solution(solution_t& solution, bool perturb, population_t* population_ptr, f_t time_limit = 300.); bool run_fj_until_timer(solution_t& solution, const weight_t& weights, - timer_t timer); + work_limit_timer_t timer); bool run_local_search(solution_t& solution, const weight_t& weights, - timer_t timer, + work_limit_timer_t timer, const ls_config_t& ls_config); bool run_fj_annealing(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, const ls_config_t& ls_config); bool run_fj_line_segment(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, const ls_config_t& ls_config); - bool run_fj_on_zero(solution_t& solution, timer_t timer); - bool check_fj_on_lp_optimal(solution_t& solution, bool perturb, timer_t timer); + bool run_fj_on_zero(solution_t& solution, work_limit_timer_t timer); + bool check_fj_on_lp_optimal(solution_t& solution, + bool perturb, + work_limit_timer_t timer); bool run_staged_fp(solution_t& solution, - timer_t timer, + work_limit_timer_t timer, population_t* population_ptr); bool run_fp(solution_t& solution, - timer_t timer, - population_t* population_ptr = nullptr); + work_limit_timer_t timer, + population_t* population_ptr = nullptr, + i_t n_fp_iterations = std::numeric_limits::max()); void resize_vectors(problem_t& problem, const raft::handle_t* handle_ptr); bool do_fj_solve(solution_t& solution, diff --git a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu index 7610ceab16..a774e5f060 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu @@ -941,7 +941,7 @@ bool constraint_prop_t::find_integer( i_t n_failed_repair_iterations = 0; while (set_count < unset_integer_vars.size()) { CUOPT_LOG_TRACE("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); - CUOPT_LOG_DEBUG("hash unset_integer_vars 0x%x", detail::compute_hash(unset_integer_vars)); + CUOPT_LOG_DEBUG("unset_integer_vars size %lu", unset_integer_vars.size()); update_host_assignment(sol); if (max_timer.check_time_limit()) { CUOPT_LOG_DEBUG("Second time limit is reached returning nearest rounding!"); @@ -989,10 +989,11 @@ bool constraint_prop_t::find_integer( detail::compute_hash(std::get<2>(var_probe_vals))); probe( sol, orig_sol.problem_ptr, var_probe_vals, &set_count, unset_integer_vars, probing_config); - CUOPT_LOG_TRACE("post probe, set count %d, unset var hash 0x%x, size %lu", - (int)set_count, - detail::compute_hash(unset_integer_vars), - unset_integer_vars.size()); + CUOPT_LOG_TRACE( + "post probe, set count %d, unset var hash 0x%x, size %lu", + (int)set_count, + detail::compute_hash(make_span(unset_integer_vars), sol.handle_ptr->get_stream()), + unset_integer_vars.size()); if (!(n_failed_repair_iterations >= max_n_failed_repair_iterations) && rounding_ii && !timeout_happened) { // timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; @@ -1247,13 +1248,15 @@ bool constraint_prop_t::handle_fixed_vars( const f_t int_tol = sol.problem_ptr->tolerances.integrality_tolerance; // which other variables were affected? CUOPT_LOG_TRACE("handle_fixed_vars, unset vars hash 0x%x, sol.assignment hash 0x%x", - detail::compute_hash(unset_vars), - detail::compute_hash(sol.assignment)); + detail::compute_hash(make_span(unset_vars), sol.handle_ptr->get_stream()), + detail::compute_hash(make_span(sol.assignment), sol.handle_ptr->get_stream())); CUOPT_LOG_TRACE( "handle_fixed_vars, original_problem->variable_bounds hash 0x%x, " "sol.problem_ptr->variable_bounds hash 0x%x", - detail::compute_hash(original_problem->variable_bounds), - detail::compute_hash(sol.problem_ptr->variable_bounds)); + detail::compute_hash(make_span(original_problem->variable_bounds), + sol.handle_ptr->get_stream()), + detail::compute_hash(make_span(sol.problem_ptr->variable_bounds), + sol.handle_ptr->get_stream())); auto iter = thrust::stable_partition(sol.handle_ptr->get_thrust_policy(), unset_vars.begin() + set_count, diff --git a/cpp/src/mip_heuristics/presolve/probing_cache.cu b/cpp/src/mip_heuristics/presolve/probing_cache.cu index 4e5765aed1..90eb1fe8d7 100644 --- a/cpp/src/mip_heuristics/presolve/probing_cache.cu +++ b/cpp/src/mip_heuristics/presolve/probing_cache.cu @@ -367,7 +367,7 @@ void compute_cache_for_var(i_t var_idx, std::atomic& problem_is_infeasible, std::vector>& modification_vector, std::vector>& substitution_vector, - timer_t timer, + const work_limit_timer_t& timer, i_t device_id) { RAFT_CUDA_TRY(cudaSetDevice(device_id)); @@ -899,19 +899,19 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, auto& multi_probe_presolve = multi_probe_presolve_pool[thread_idx]; - compute_cache_for_var(var_idx, - bound_presolve, - problem, - multi_probe_presolve, - h_var_bounds, - h_integer_indices, - n_of_implied_singletons, - n_of_cached_probings, - problem_is_infeasible, - modification_vector_pool[thread_idx], - substitution_vector_pool[thread_idx], - timer, - problem.handle_ptr->get_device()); + compute_cache_for_var(var_idx, + bound_presolve, + problem, + multi_probe_presolve, + h_var_bounds, + h_integer_indices, + n_of_implied_singletons, + n_of_cached_probings, + problem_is_infeasible, + modification_vector_pool[thread_idx], + substitution_vector_pool[thread_idx], + timer, + problem.handle_ptr->get_device()); } } #pragma omp single diff --git a/cpp/src/mip_heuristics/problem/problem.cuh b/cpp/src/mip_heuristics/problem/problem.cuh index 4cd30988cf..b9ca420820 100644 --- a/cpp/src/mip_heuristics/problem/problem.cuh +++ b/cpp/src/mip_heuristics/problem/problem.cuh @@ -126,8 +126,6 @@ class problem_t { uint32_t get_fingerprint() const; - uint32_t get_fingerprint() const; - void add_cutting_plane_at_objective(f_t objective); void compute_vars_with_objective_coeffs(); void test_problem_fixing_time(); diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index 94787ebf7f..a3bcbd2257 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -76,9 +76,8 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.time_limit = settings.time_limit; pdlp_settings.iteration_limit = settings.iteration_limit; - // CHANGE - i_t work_limit = settings.work_limit; - bool determinism_mode = work_limit != std::numeric_limits::infinity(); + const f_t work_limit = settings.work_limit; + const bool determinism_mode = std::isfinite(work_limit); pdlp_settings.concurrent_halt = settings.concurrent_halt; pdlp_settings.per_constraint_residual = settings.per_constraint_residual; pdlp_settings.first_primal_feasible = settings.return_first_feasible; @@ -95,14 +94,10 @@ optimization_problem_solution_t get_relaxed_lp_solution( estim_iters += 100; } while (true); CUOPT_LOG_DEBUG("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); - pdlp_settings.iteration_limit = estim_iters; - pdlp_settings.time_limit = std::numeric_limits::infinity(); - pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable3; - pdlp_settings.concurrent_halt = settings.concurrent_halt; - pdlp_settings.per_constraint_residual = settings.per_constraint_residual; - pdlp_settings.first_primal_feasible = settings.return_first_feasible; - pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; - pdlp_settings.presolver = presolver_t::None; + pdlp_settings.iteration_limit = estim_iters; + pdlp_settings.time_limit = std::numeric_limits::infinity(); + pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable3; + pdlp_settings.presolver = presolver_t::None; } set_pdlp_solver_mode(pdlp_settings); // TODO: set Stable3 here? @@ -137,11 +132,10 @@ optimization_problem_solution_t get_relaxed_lp_solution( // temporarily add timer auto start_time = timer_t(pdlp_settings.time_limit); lp_solver.set_inside_mip(true); - CUOPT_LOG_DEBUG("prev primal hash 0x%x", detail::compute_hash(assignment)); - CUOPT_LOG_DEBUG("prev dual hash 0x%x", detail::compute_hash(lp_state.prev_dual)); + CUOPT_LOG_DEBUG( + "prev solution sizes primal=%lu dual=%lu", assignment.size(), lp_state.prev_dual.size()); auto solver_response = lp_solver.run_solver(start_time); - CUOPT_LOG_DEBUG("post LP primal hash 0x%x", - detail::compute_hash(solver_response.get_primal_solution())); + CUOPT_LOG_DEBUG("post LP primal size %lu", solver_response.get_primal_solution().size()); if (solver_response.get_primal_solution().size() != 0 && solver_response.get_dual_solution().size() != 0 && settings.save_state) { @@ -159,10 +153,9 @@ optimization_problem_solution_t get_relaxed_lp_solution( // CUOPT_LOG_DEBUG("feasible solution found with LP objective %f", // solver_response.get_objective_value()); } else { - CUOPT_LOG_DEBUG("LP returned with reason %d, %d iterations, sol hash 0x%x", + CUOPT_LOG_DEBUG("LP returned with reason %d, %d iterations", solver_response.get_termination_status(), - solver_response.get_additional_termination_information().number_of_steps_taken, - compute_hash(assignment)); + solver_response.get_additional_termination_information().number_of_steps_taken); } // auto function_end_time = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/mip_heuristics/solution/solution.cu b/cpp/src/mip_heuristics/solution/solution.cu index 3d9dd4cc76..cd0eaee0c6 100644 --- a/cpp/src/mip_heuristics/solution/solution.cu +++ b/cpp/src/mip_heuristics/solution/solution.cu @@ -664,7 +664,8 @@ mip_solution_t solution_t::get_solution(bool output_feasible template uint32_t solution_t::get_hash() const { - return compute_hash(assignment); + auto h_assignment = host_copy(assignment, handle_ptr->get_stream()); + return compute_hash(h_assignment); } #if MIP_INSTANTIATE_FLOAT diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 22d085ed66..5a242716dc 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -236,18 +236,6 @@ solution_t mip_solver_t::run_solver() } CUOPT_LOG_INFO("Using %d CPU threads for B&B", branch_and_bound_settings.num_threads); - i_t num_threads = branch_and_bound_settings.num_threads; - i_t num_bfs_threads = std::max(1, num_threads / 4); - i_t num_diving_threads = std::max(1, num_threads - num_bfs_threads); - // deterministic mode: use BSP coordinator with multiple workers, no diving - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - // BSP mode can use multiple workers deterministically - num_bfs_threads = std::max(1, num_threads); - num_diving_threads = 0; // No diving in deterministic mode - } - branch_and_bound_settings.num_bfs_threads = num_bfs_threads; - branch_and_bound_settings.num_diving_threads = num_diving_threads; - // Set the branch and bound -> primal heuristics callback branch_and_bound_settings.solution_callback = std::bind(&branch_and_bound_solution_helper_t::solution_callback, @@ -290,11 +278,11 @@ solution_t mip_solver_t::run_solver() std::placeholders::_1); } else if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { branch_and_bound->set_concurrent_lp_root_solve(false); - context.problem_ptr->branch_and_bound_callback = - [bb = branch_and_bound.get()](const std::vector& solution) { - double vt = bb->get_current_bsp_horizon(); - bb->set_new_solution_deterministic(solution, vt); - }; + // TODO once deterministic GPU heuristics are integrated + // context.problem_ptr->branch_and_bound_callback = + // [bb = branch_and_bound.get()](const std::vector& solution) { + // bb->queue_external_solution_deterministic(solution, 0.0); + // }; } context.work_unit_scheduler_.register_context(branch_and_bound->get_work_unit_context()); diff --git a/cpp/src/utilities/work_limit_context.hpp b/cpp/src/utilities/work_limit_context.hpp index c75a37b818..d334cafbf5 100644 --- a/cpp/src/utilities/work_limit_context.hpp +++ b/cpp/src/utilities/work_limit_context.hpp @@ -41,6 +41,8 @@ struct work_limit_context_t { global_work_units_elapsed += work; if (scheduler) { scheduler->on_work_recorded(*this, global_work_units_elapsed); } } + + void record_work(double work) { record_work_sync_on_horizon(work); } }; } // namespace cuopt diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp index 08499f55bc..bd01aef977 100644 --- a/cpp/src/utilities/work_limit_timer.hpp +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -19,30 +19,13 @@ #include #include -#include +#include #include "timer.hpp" -#include "work_unit_scheduler.hpp" +#include "work_limit_context.hpp" namespace cuopt { -struct work_limit_context_t { - double global_work_units_elapsed{0.0}; - double total_sync_time{0.0}; // Total time spent waiting at sync barriers (seconds) - bool deterministic{false}; - work_unit_scheduler_t* scheduler{nullptr}; - std::string name; - - work_limit_context_t(const std::string& name) : name(name) {} - - void record_work(double work) - { - if (!deterministic) return; - global_work_units_elapsed += work; - if (scheduler) { scheduler->on_work_recorded(*this, global_work_units_elapsed); } - } -}; - // In determinism mode, relies on a work limit accumulator; otherwise rely on a timer // in non-determinism mode: 1s = 1wu // In deterministic mode, all timers share a global work units counter (via work_limit_context_t), @@ -65,6 +48,16 @@ class work_limit_timer_t { { } + // Compatibility constructor for non-deterministic contexts. + explicit work_limit_timer_t(double time_limit_) + : deterministic(false), + work_limit(time_limit_), + timer(time_limit_), + work_context(nullptr), + work_units_at_start(0) + { + } + bool check_limit(const char* caller = __builtin_FUNCTION(), const char* file = __builtin_FILE(), int line = __builtin_LINE()) const noexcept @@ -113,7 +106,7 @@ class work_limit_timer_t { work_units, timer.elapsed_time(), work_context->global_work_units_elapsed); - work_context->record_work(work_units); + work_context->record_work_sync_on_horizon(work_units); } } diff --git a/cpp/src/utilities/work_unit_predictor.cpp b/cpp/src/utilities/work_unit_predictor.cpp index 0aebf953de..ebce702283 100644 --- a/cpp/src/utilities/work_unit_predictor.cpp +++ b/cpp/src/utilities/work_unit_predictor.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights * reserved. SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,8 +21,8 @@ #include #include #include -#include -#include +#include +#include #include #include "models/cpufj_predictor/header.h" diff --git a/cpp/src/utilities/work_unit_predictor.hpp b/cpp/src/utilities/work_unit_predictor.hpp index 45bc6b97bc..c46db15fb5 100644 --- a/cpp/src/utilities/work_unit_predictor.hpp +++ b/cpp/src/utilities/work_unit_predictor.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights * reserved. SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ #include #include +#include #include namespace cuopt { diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 9520b6e544..fe9dd4fde9 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# cmake-format: off +# cmake-format: off # SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # cmake-format: on @@ -102,16 +102,6 @@ function(ConfigureTest CMAKE_TEST_NAME) target_link_options(${CMAKE_TEST_NAME} PRIVATE -Wl,--enable-new-dtags) endif() - target_link_libraries(${CMAKE_TEST_NAME} PRIVATE - #cuopt::nvperf_target - #cuopt::nvperf_host - #cuopt::cupti - cuda - ) - target_include_directories(${CMAKE_TEST_NAME} PRIVATE - "${cuopt_cupti_root}/include" - ) - add_test(NAME ${CMAKE_TEST_NAME} COMMAND ${CMAKE_TEST_NAME}) install( diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index ddbb3286af..663038c974 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights * reserved. SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,20 +20,21 @@ #include "mip_utils.cuh" #include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include #include @@ -64,13 +65,7 @@ void init_handler(const raft::handle_t* handle_ptr) handle_ptr->get_cusparse_handle(), CUSPARSE_POINTER_MODE_DEVICE, handle_ptr->get_stream())); } -static void setup_device_symbols(rmm::cuda_stream_view stream_view) -{ - raft::common::nvtx::range fun_scope("Setting device symbol"); - detail::set_adaptive_step_size_hyper_parameters(stream_view); - detail::set_restart_hyper_parameters(stream_view); - detail::set_pdlp_hyper_parameters(stream_view); -} +static void setup_device_symbols(rmm::cuda_stream_view stream_view) { (void)stream_view; } static uint32_t test_full_run_determinism(std::string path, unsigned long seed = std::random_device{}()) @@ -90,6 +85,7 @@ static uint32_t test_full_run_determinism(std::string path, setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); + pdlp_hyper_params::pdlp_hyper_params_t hyper_params{}; detail::pdlp_initial_scaling_strategy_t scaling(&handle_, problem, 10, @@ -98,6 +94,7 @@ static uint32_t test_full_run_determinism(std::string path, problem.reverse_offsets, problem.reverse_constraints, nullptr, + hyper_params, true); auto settings = mip_solver_settings_t{}; @@ -146,6 +143,7 @@ static uint32_t test_initial_solution_determinism(std::string path, setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); + pdlp_hyper_params::pdlp_hyper_params_t hyper_params{}; detail::pdlp_initial_scaling_strategy_t scaling(&handle_, problem, 10, @@ -154,6 +152,7 @@ static uint32_t test_initial_solution_determinism(std::string path, problem.reverse_offsets, problem.reverse_constraints, nullptr, + hyper_params, true); auto settings = mip_solver_settings_t{}; @@ -202,6 +201,7 @@ static uint32_t test_recombiners_determinism(std::string path, setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); + pdlp_hyper_params::pdlp_hyper_params_t hyper_params{}; detail::pdlp_initial_scaling_strategy_t scaling(&handle_, problem, 10, @@ -210,6 +210,7 @@ static uint32_t test_recombiners_determinism(std::string path, problem.reverse_offsets, problem.reverse_constraints, nullptr, + hyper_params, true); auto settings = mip_solver_settings_t{}; @@ -314,7 +315,6 @@ class DiversityTestParams : public testing::TestWithParam 1) exit(0); + EXPECT_NEAR(solution.get_user_objective(), first_val_map[test_instance], 1.0) + << test_instance << " determinism objective mismatch"; return true; } diff --git a/cpp/tests/mip/load_balancing_test.cu b/cpp/tests/mip/load_balancing_test.cu index 5e2f08007d..413e840b3c 100644 --- a/cpp/tests/mip/load_balancing_test.cu +++ b/cpp/tests/mip/load_balancing_test.cu @@ -9,6 +9,7 @@ #include "mip_utils.cuh" #include +#include #include #include #include @@ -129,6 +130,7 @@ void test_multi_probe(std::string path) detail::problem_t problem(op_problem); mip_solver_settings_t default_settings{}; detail::pdhg_solver_t pdhg_solver(problem.handle_ptr, problem); + pdlp_hyper_params::pdlp_hyper_params_t hyper_params{}; detail::pdlp_initial_scaling_strategy_t scaling(&handle_, problem, 10, @@ -137,6 +139,7 @@ void test_multi_probe(std::string path) problem.reverse_offsets, problem.reverse_constraints, nullptr, + hyper_params, true); detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); detail::load_balanced_problem_t lb_problem(problem); diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index 8b541958bd..194af741b3 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights * reserved. SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,20 +20,21 @@ #include "mip_utils.cuh" #include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include #include #include @@ -64,13 +65,7 @@ void init_handler(const raft::handle_t* handle_ptr) handle_ptr->get_cusparse_handle(), CUSPARSE_POINTER_MODE_DEVICE, handle_ptr->get_stream())); } -static void setup_device_symbols(rmm::cuda_stream_view stream_view) -{ - raft::common::nvtx::range fun_scope("Setting device symbol"); - detail::set_adaptive_step_size_hyper_parameters(stream_view); - detail::set_restart_hyper_parameters(stream_view); - detail::set_pdlp_hyper_parameters(stream_view); -} +static void setup_device_symbols(rmm::cuda_stream_view stream_view) { (void)stream_view; } enum local_search_mode_t { FP = 0, @@ -100,6 +95,7 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); + pdlp_hyper_params::pdlp_hyper_params_t hyper_params{}; detail::pdlp_initial_scaling_strategy_t scaling(&handle_, problem, 10, @@ -108,6 +104,7 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) problem.reverse_offsets, problem.reverse_constraints, nullptr, + hyper_params, true); auto settings = mip_solver_settings_t{}; @@ -146,7 +143,8 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) solution.compute_feasibility(); printf("Model fingerprint: 0x%x\n", problem.get_fingerprint()); - printf("LP optimal hash: 0x%x\n", detail::compute_hash(lp_optimal_solution)); + printf("LP optimal hash: 0x%x\n", + detail::compute_hash(make_span(lp_optimal_solution), problem.handle_ptr->get_stream())); printf("running mode: %d\n", mode); work_limit_context_t work_limit_context("LocalSearch"); @@ -189,10 +187,10 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) // return {host_copy(solution_vector, problem.handle_ptr->get_stream()), iterations}; } -static uint32_t run_fp_check_determinism(std::string test_instance, local_search_mode_t mode) +static uint32_t run_fp_check_determinism(std::string test_instance, + local_search_mode_t mode, + unsigned long seed) { - int seed = - std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); cuopt::seed_generator::set_seed(seed); return run_fp(test_instance, mode); @@ -207,8 +205,6 @@ static uint32_t run_fp_check_determinism(std::string test_instance, local_search // solution.get_objective()); // static auto first_val = solution.get_user_objective(); - - // if (abs(solution.get_user_objective() - first_val) > 1) exit(0); } class LocalSearchTestParams : public testing::TestWithParam> {}; @@ -237,11 +233,13 @@ TEST_P(LocalSearchTestParams, local_search_operator_determinism) // run_fp_check_determinism(instance, 1000); // } - unsigned long seed = std::random_device{}(); + unsigned long seed = std::getenv("CUOPT_SEED") + ? (unsigned long)std::stoi(std::getenv("CUOPT_SEED")) + : (unsigned long)std::random_device{}(); std::cerr << "Tested with seed " << seed << "\n"; uint32_t gold_hash = 0; for (int i = 0; i < 5; ++i) { - uint32_t hash = run_fp_check_determinism(instance, mode); + uint32_t hash = run_fp_check_determinism(instance, mode, seed); if (i == 0) { gold_hash = hash; printf("Gold hash: 0x%x\n", gold_hash); diff --git a/cpp/tests/mip/mip_utils.cuh b/cpp/tests/mip/mip_utils.cuh index f02a73d5bd..0125987b1c 100644 --- a/cpp/tests/mip/mip_utils.cuh +++ b/cpp/tests/mip/mip_utils.cuh @@ -8,9 +8,14 @@ #include #include #include +#include #include +#include +#include #include +#include #include +#include namespace cuopt::linear_programming::test { @@ -197,6 +202,7 @@ static fj_state_t run_fj(detail::problem_t& problem, fj_tweaks_t tweaks = {}, std::vector initial_solution = {}) { + pdlp_hyper_params::pdlp_hyper_params_t hyper_params{}; detail::pdlp_initial_scaling_strategy_t scaling(problem.handle_ptr, problem, 10, @@ -205,6 +211,7 @@ static fj_state_t run_fj(detail::problem_t& problem, problem.reverse_offsets, problem.reverse_constraints, nullptr, + hyper_params, true); auto settings = mip_solver_settings_t{}; diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index cced24e7e6..f82fd8b292 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -10,17 +10,18 @@ #include "mip_utils.cuh" #include +#include #include -#include -#include -#include -#include -#include -#include +#include +#include #include +#include #include +#include #include #include +#include +#include #include #include #include @@ -112,6 +113,7 @@ uint32_t test_probing_cache_determinism(std::string path, detail::problem_t problem(op_problem); mip_solver_settings_t default_settings{}; default_settings.mip_scaling = false; // we're not checking scaling determinism here + pdlp_hyper_params::pdlp_hyper_params_t hyper_params{}; detail::pdlp_initial_scaling_strategy_t scaling(&handle_, problem, 10, @@ -120,6 +122,7 @@ uint32_t test_probing_cache_determinism(std::string path, problem.reverse_offsets, problem.reverse_constraints, nullptr, + hyper_params, true); detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); detail::bound_presolve_t bnd_prb(solver.context); @@ -181,20 +184,22 @@ uint32_t test_scaling_determinism(std::string path, unsigned long seed = std::ra problem_checking_t::check_problem_representation(op_problem); detail::problem_t problem(op_problem); - pdlp_hyper_params::update_primal_weight_on_initial_solution = false; - pdlp_hyper_params::update_step_size_on_initial_solution = true; + pdlp_hyper_params::pdlp_hyper_params_t hyper_params{}; + hyper_params.update_primal_weight_on_initial_solution = false; + hyper_params.update_step_size_on_initial_solution = true; // problem contains unpreprocessed data detail::problem_t scaled_problem(problem); detail::pdlp_initial_scaling_strategy_t scaling( scaled_problem.handle_ptr, scaled_problem, - pdlp_hyper_params::default_l_inf_ruiz_iterations, - (double)pdlp_hyper_params::default_alpha_pock_chambolle_rescaling, + hyper_params.default_l_inf_ruiz_iterations, + (double)hyper_params.default_alpha_pock_chambolle_rescaling, scaled_problem.reverse_coefficients, scaled_problem.reverse_offsets, scaled_problem.reverse_constraints, nullptr, + hyper_params, true); scaling.scale_problem(); diff --git a/cpp/tests/mip/unit_test.cu b/cpp/tests/mip/unit_test.cu index c7266a6ef7..9c6d5a3c09 100644 --- a/cpp/tests/mip/unit_test.cu +++ b/cpp/tests/mip/unit_test.cu @@ -123,165 +123,165 @@ mps_parser::mps_data_model_t create_single_var_milp_problem(bool ma return problem; } -// TEST(LPTest, TestSampleLP2) -// { -// raft::handle_t handle; +TEST(LPTest, TestSampleLP2) +{ + raft::handle_t handle; -// // Construct a simple LP problem: -// // Minimize: x -// // Subject to: x <= 1 -// // x <= 1 -// // x >= 0 - -// // One variable, two constraints (both x <= 1) -// std::vector A_values = {1.0, 1.0}; -// std::vector A_indices = {0, 0}; -// std::vector A_offsets = {0, 1, 2}; // CSR: 2 constraints, 1 variable - -// std::vector b = {1.0, 1.0}; // RHS for both constraints -// std::vector b_lower = {-std::numeric_limits::infinity(), -// -std::numeric_limits::infinity()}; - -// std::vector c = {1.0}; // Objective: Minimize x - -// std::vector row_types = {'L', 'L'}; // Both constraints are <= - -// // Build the problem -// mps_parser::mps_data_model_t problem; -// problem.set_csr_constraint_matrix(A_values.data(), -// A_values.size(), -// A_indices.data(), -// A_indices.size(), -// A_offsets.data(), -// A_offsets.size()); -// problem.set_constraint_upper_bounds(b.data(), b.size()); -// problem.set_constraint_lower_bounds(b_lower.data(), b_lower.size()); - -// // Set variable bounds (x >= 0) -// std::vector var_lower = {0.0}; -// std::vector var_upper = {std::numeric_limits::infinity()}; -// problem.set_variable_lower_bounds(var_lower.data(), var_lower.size()); -// problem.set_variable_upper_bounds(var_upper.data(), var_upper.size()); - -// problem.set_objective_coefficients(c.data(), c.size()); -// problem.set_maximize(false); -// // Set up solver settings -// cuopt::linear_programming::pdlp_solver_settings_t settings{}; -// settings.set_optimality_tolerance(1e-2); -// settings.method = cuopt::linear_programming::method_t::PDLP; -// settings.time_limit = 5; - -// // Solve -// auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); - -// // Check results -// EXPECT_EQ(result.get_termination_status(), -// cuopt::linear_programming::pdlp_termination_status_t::Optimal); -// ASSERT_EQ(result.get_primal_solution().size(), 1); - -// // Copy solution to host to access values -// auto primal_host = cuopt::host_copy(result.get_primal_solution(), handle.get_stream()); -// EXPECT_NEAR(primal_host[0], 0.0, 1e-6); - -// EXPECT_NEAR(result.get_additional_termination_information().primal_objective, 0.0, 1e-6); -// EXPECT_NEAR(result.get_additional_termination_information().dual_objective, 0.0, 1e-6); -// } + // Construct a simple LP problem: + // Minimize: x + // Subject to: x <= 1 + // x <= 1 + // x >= 0 -// TEST(LPTest, TestSampleLP) -// { -// raft::handle_t handle; -// auto problem = create_std_lp_problem(); + // One variable, two constraints (both x <= 1) + std::vector A_values = {1.0, 1.0}; + std::vector A_indices = {0, 0}; + std::vector A_offsets = {0, 1, 2}; // CSR: 2 constraints, 1 variable -// cuopt::linear_programming::pdlp_solver_settings_t settings{}; -// settings.set_optimality_tolerance(1e-4); -// settings.time_limit = 5; -// settings.presolver = cuopt::linear_programming::presolver_t::None; + std::vector b = {1.0, 1.0}; // RHS for both constraints + std::vector b_lower = {-std::numeric_limits::infinity(), + -std::numeric_limits::infinity()}; -// auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); + std::vector c = {1.0}; // Objective: Minimize x -// EXPECT_EQ(result.get_termination_status(), -// cuopt::linear_programming::pdlp_termination_status_t::Optimal); -// } + std::vector row_types = {'L', 'L'}; // Both constraints are <= -// TEST(ErrorTest, TestError) -// { -// raft::handle_t handle; -// auto problem = create_std_milp_problem(false); + // Build the problem + mps_parser::mps_data_model_t problem; + problem.set_csr_constraint_matrix(A_values.data(), + A_values.size(), + A_indices.data(), + A_indices.size(), + A_offsets.data(), + A_offsets.size()); + problem.set_constraint_upper_bounds(b.data(), b.size()); + problem.set_constraint_lower_bounds(b_lower.data(), b_lower.size()); + + // Set variable bounds (x >= 0) + std::vector var_lower = {0.0}; + std::vector var_upper = {std::numeric_limits::infinity()}; + problem.set_variable_lower_bounds(var_lower.data(), var_lower.size()); + problem.set_variable_upper_bounds(var_upper.data(), var_upper.size()); -// cuopt::linear_programming::mip_solver_settings_t settings{}; -// settings.time_limit = 5; -// settings.presolver = cuopt::linear_programming::presolver_t::None; + problem.set_objective_coefficients(c.data(), c.size()); + problem.set_maximize(false); + // Set up solver settings + cuopt::linear_programming::pdlp_solver_settings_t settings{}; + settings.set_optimality_tolerance(1e-2); + settings.method = cuopt::linear_programming::method_t::PDLP; + settings.time_limit = 5; + + // Solve + auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); + + // Check results + EXPECT_EQ(result.get_termination_status(), + cuopt::linear_programming::pdlp_termination_status_t::Optimal); + ASSERT_EQ(result.get_primal_solution().size(), 1); + + // Copy solution to host to access values + auto primal_host = cuopt::host_copy(result.get_primal_solution(), handle.get_stream()); + EXPECT_NEAR(primal_host[0], 0.0, 1e-6); + + EXPECT_NEAR(result.get_additional_termination_information().primal_objective, 0.0, 1e-6); + EXPECT_NEAR(result.get_additional_termination_information().dual_objective, 0.0, 1e-6); +} -// // Set constraint bounds -// std::vector lower_bounds = {1.0}; -// std::vector upper_bounds = {1.0, 1.0}; -// problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); -// problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); +TEST(LPTest, TestSampleLP) +{ + raft::handle_t handle; + auto problem = create_std_lp_problem(); -// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + cuopt::linear_programming::pdlp_solver_settings_t settings{}; + settings.set_optimality_tolerance(1e-4); + settings.time_limit = 5; + settings.presolver = cuopt::linear_programming::presolver_t::None; -// EXPECT_EQ(result.get_termination_status(), -// cuopt::linear_programming::mip_termination_status_t::NoTermination); -// } + auto result = cuopt::linear_programming::solve_lp(&handle, problem, settings); -// class MILPTestParams -// : public testing::TestWithParam< -// std::tuple> {}; + EXPECT_EQ(result.get_termination_status(), + cuopt::linear_programming::pdlp_termination_status_t::Optimal); +} -// TEST_P(MILPTestParams, TestSampleMILP) -// { -// bool maximize = std::get<0>(GetParam()); -// bool scaling = std::get<1>(GetParam()); -// bool heuristics_only = std::get<2>(GetParam()); -// auto expected_termination_status = std::get<3>(GetParam()); +TEST(ErrorTest, TestError) +{ + raft::handle_t handle; + auto problem = create_std_milp_problem(false); -// raft::handle_t handle; -// auto problem = create_std_milp_problem(maximize); + cuopt::linear_programming::mip_solver_settings_t settings{}; + settings.time_limit = 5; + settings.presolver = cuopt::linear_programming::presolver_t::None; -// cuopt::linear_programming::mip_solver_settings_t settings{}; -// settings.time_limit = 5; -// settings.mip_scaling = scaling; -// settings.heuristics_only = heuristics_only; -// settings.presolver = cuopt::linear_programming::presolver_t::None; + // Set constraint bounds + std::vector lower_bounds = {1.0}; + std::vector upper_bounds = {1.0, 1.0}; + problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); + problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); -// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); -// EXPECT_EQ(result.get_termination_status(), expected_termination_status); -// } + EXPECT_EQ(result.get_termination_status(), + cuopt::linear_programming::mip_termination_status_t::NoTermination); +} -// TEST_P(MILPTestParams, TestSingleVarMILP) -// { -// bool maximize = std::get<0>(GetParam()); -// bool scaling = std::get<1>(GetParam()); -// bool heuristics_only = std::get<2>(GetParam()); -// auto expected_termination_status = std::get<3>(GetParam()); +class MILPTestParams + : public testing::TestWithParam< + std::tuple> {}; -// raft::handle_t handle; -// auto problem = create_single_var_milp_problem(maximize); +TEST_P(MILPTestParams, TestSampleMILP) +{ + bool maximize = std::get<0>(GetParam()); + bool scaling = std::get<1>(GetParam()); + bool heuristics_only = std::get<2>(GetParam()); + auto expected_termination_status = std::get<3>(GetParam()); -// cuopt::linear_programming::mip_solver_settings_t settings{}; -// settings.time_limit = 5; -// settings.mip_scaling = scaling; -// settings.heuristics_only = heuristics_only; -// settings.presolver = cuopt::linear_programming::presolver_t::None; + raft::handle_t handle; + auto problem = create_std_milp_problem(maximize); -// auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + cuopt::linear_programming::mip_solver_settings_t settings{}; + settings.time_limit = 5; + settings.mip_scaling = scaling; + settings.heuristics_only = heuristics_only; + settings.presolver = cuopt::linear_programming::presolver_t::None; -// EXPECT_EQ(result.get_termination_status(), -// cuopt::linear_programming::mip_termination_status_t::Optimal); -// } + auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + + EXPECT_EQ(result.get_termination_status(), expected_termination_status); +} + +TEST_P(MILPTestParams, TestSingleVarMILP) +{ + bool maximize = std::get<0>(GetParam()); + bool scaling = std::get<1>(GetParam()); + bool heuristics_only = std::get<2>(GetParam()); + auto expected_termination_status = std::get<3>(GetParam()); + + raft::handle_t handle; + auto problem = create_single_var_milp_problem(maximize); + + cuopt::linear_programming::mip_solver_settings_t settings{}; + settings.time_limit = 5; + settings.mip_scaling = scaling; + settings.heuristics_only = heuristics_only; + settings.presolver = cuopt::linear_programming::presolver_t::None; + + auto result = cuopt::linear_programming::solve_mip(&handle, problem, settings); + + EXPECT_EQ(result.get_termination_status(), + cuopt::linear_programming::mip_termination_status_t::Optimal); +} -// INSTANTIATE_TEST_SUITE_P( -// MILPTests, -// MILPTestParams, -// testing::Values( -// std::make_tuple(true, true, true, -// cuopt::linear_programming::mip_termination_status_t::Optimal), std::make_tuple( -// false, true, false, cuopt::linear_programming::mip_termination_status_t::Optimal), -// std::make_tuple( -// true, false, true, cuopt::linear_programming::mip_termination_status_t::Optimal), -// std::make_tuple( -// false, false, false, cuopt::linear_programming::mip_termination_status_t::Optimal))); +INSTANTIATE_TEST_SUITE_P( + MILPTests, + MILPTestParams, + testing::Values( + std::make_tuple(true, true, true, cuopt::linear_programming::mip_termination_status_t::Optimal), + std::make_tuple( + false, true, false, cuopt::linear_programming::mip_termination_status_t::Optimal), + std::make_tuple( + true, false, true, cuopt::linear_programming::mip_termination_status_t::Optimal), + std::make_tuple( + false, false, false, cuopt::linear_programming::mip_termination_status_t::Optimal))); // TEST_P(MILPTestParams, TestDeterminism) // { diff --git a/scripts/README_REGRESSION.md b/scripts/README_REGRESSION.md index ca6cba0ca1..9c8e236bff 100644 --- a/scripts/README_REGRESSION.md +++ b/scripts/README_REGRESSION.md @@ -13,7 +13,19 @@ The workflow consists of two steps: Install required dependencies: ```bash -pip install -r requirements.txt +pip install numpy pandas scikit-learn joblib +``` + +Optional model backends: + +```bash +pip install xgboost lightgbm +``` + +Optional C source export dependencies: + +```bash +pip install treelite tl2cgen ``` ## Usage diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py index 026b140c77..dcb9492cca 100755 --- a/scripts/train_regressor.py +++ b/scripts/train_regressor.py @@ -2062,7 +2062,6 @@ def main(): # Apply scaling if needed scaler = None - needs_scaling = False X_test_original = X_test.copy() # Keep unscaled version for display if needs_scaling: print(" Applying StandardScaler to features...") From 79a6d54f9ffefad7e792a901e51fa62b38189bfd Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 6 Mar 2026 06:44:39 -0800 Subject: [PATCH 134/225] determinism fixes --- .../linear_programming/cuopt/run_mip.cpp | 25 ++- .../cuopt/linear_programming/constants.h | 5 +- .../mip/solver_settings.hpp | 4 +- cpp/src/CMakeLists.txt | 8 + cpp/src/math_optimization/solver_settings.cu | 2 +- .../diversity/diversity_manager.cu | 105 +++++++--- .../diversity/recombiners/fp_recombiner.cuh | 10 +- .../diversity/recombiners/recombiner.cuh | 3 +- .../feasibility_jump/feasibility_jump.cu | 24 ++- .../feasibility_pump/feasibility_pump.cu | 184 ++++++++++++++++-- .../local_search/local_search.cu | 4 +- .../local_search/rounding/constraint_prop.cu | 5 +- cpp/src/mip_heuristics/mip_constants.hpp | 17 +- .../presolve/bounds_presolve.cu | 2 +- .../mip_heuristics/relaxed_lp/relaxed_lp.cu | 48 ++++- .../mip_heuristics/relaxed_lp/relaxed_lp.cuh | 22 ++- cpp/src/mip_heuristics/solve.cu | 7 +- cpp/src/mip_heuristics/solver.cu | 10 +- cpp/src/mip_heuristics/solver_context.cuh | 3 +- cpp/src/utilities/timer.hpp | 2 +- cpp/tests/mip/diversity_test.cu | 184 ++++++++++-------- cpp/tests/mip/feasibility_jump_tests.cu | 27 +-- cpp/tests/mip/local_search_test.cu | 12 +- cpp/tests/mip/mip_utils.cuh | 10 +- cpp/tests/mip/multi_probe_test.cu | 22 +-- cpp/tests/mip/presolve_test.cu | 68 +++---- 26 files changed, 563 insertions(+), 250 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 9b79dff8af..28ab0e22bb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -199,12 +199,25 @@ int run_single_file(std::string file_path, } } } - settings.time_limit = time_limit; - settings.work_limit = work_limit; - settings.heuristics_only = heuristics_only; - settings.num_cpu_threads = num_cpu_threads; - settings.log_to_console = log_to_console; - settings.determinism_mode = deterministic ? CUOPT_MODE_DETERMINISTIC : CUOPT_MODE_OPPORTUNISTIC; + settings.time_limit = time_limit; + settings.work_limit = work_limit; + settings.heuristics_only = heuristics_only; + settings.num_cpu_threads = num_cpu_threads; + settings.log_to_console = log_to_console; + if (deterministic) { + settings.determinism_mode = + heuristics_only ? CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS : CUOPT_MODE_DETERMINISTIC; + } else { + settings.determinism_mode = CUOPT_MODE_OPPORTUNISTIC; + } + CUOPT_LOG_INFO( + "run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " + "time_limit=%.6f work_limit=%.6f", + (int)heuristics_only, + (int)deterministic, + settings.determinism_mode, + settings.time_limit, + settings.work_limit); settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; settings.presolver = cuopt::linear_programming::presolver_t::Default; diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index 7eb0aa07d6..7f06cbc236 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -76,8 +76,9 @@ #define CUOPT_RANDOM_SEED "random_seed" /* @brief MIP determinism mode constants */ -#define CUOPT_MODE_OPPORTUNISTIC 0 -#define CUOPT_MODE_DETERMINISTIC 1 +#define CUOPT_MODE_OPPORTUNISTIC 0 +#define CUOPT_MODE_DETERMINISTIC 1 +#define CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS 2 /* @brief LP/MIP termination status constants */ #define CUOPT_TERIMINATION_STATUS_NO_TERMINATION 0 diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 6d32cd5ed9..01c4cb64ab 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -116,7 +116,9 @@ class mip_solver_settings_t { * - CUOPT_MODE_OPPORTUNISTIC (0): Default mode, allows non-deterministic * parallelism for better performance * - CUOPT_MODE_DETERMINISTIC (1): Ensures deterministic results across runs - * at potential cost of performance + * at potential cost of performance. Runs deterministic B&B path without GPU heuristics. + * - CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS (2): Enables deterministic work-limit mode while + * still running GPU heuristics. */ int determinism_mode = CUOPT_MODE_OPPORTUNISTIC; /** diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 75e20c7b81..82828477a5 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -7,6 +7,14 @@ set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/quantize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/dualsimplex_predictor/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/dualsimplex_predictor/quantize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/quantize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/pdlp_predictor/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/pdlp_predictor/quantize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_predictor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_scheduler.cpp) diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index f1350ca432..d49dc152ad 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -101,7 +101,7 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING, &mip_settings.mip_batch_pdlp_strong_branching, 0, 1, 0}, {CUOPT_PRESOLVE, reinterpret_cast(&pdlp_settings.presolver), CUOPT_PRESOLVE_DEFAULT, CUOPT_PRESOLVE_PSLP, CUOPT_PRESOLVE_DEFAULT}, {CUOPT_PRESOLVE, reinterpret_cast(&mip_settings.presolver), CUOPT_PRESOLVE_DEFAULT, CUOPT_PRESOLVE_PSLP, CUOPT_PRESOLVE_DEFAULT}, - {CUOPT_MIP_DETERMINISM_MODE, &mip_settings.determinism_mode, CUOPT_MODE_OPPORTUNISTIC, CUOPT_MODE_DETERMINISTIC, CUOPT_MODE_OPPORTUNISTIC}, + {CUOPT_MIP_DETERMINISM_MODE, &mip_settings.determinism_mode, CUOPT_MODE_OPPORTUNISTIC, CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS, CUOPT_MODE_OPPORTUNISTIC}, {CUOPT_RANDOM_SEED, &mip_settings.seed, -1, std::numeric_limits::max(), -1}, {CUOPT_MIP_RELIABILITY_BRANCHING, &mip_settings.reliability_branching, -1, std::numeric_limits::max(), -1} }; diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 906ce4a544..5b0d14ff31 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -110,8 +111,7 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::run_presolve(f_t time_limit, timer_t global_ bool run_probing_cache = !fj_only_run; // Don't run probing cache in deterministic mode yet as neither B&B nor CPUFJ need it // and it doesn't make use of work units yet - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { run_probing_cache = false; } + if (is_deterministic_mode(context.settings.determinism_mode)) { run_probing_cache = false; } if (run_probing_cache) { // Run probing cache before trivial presolve to discover variable implications const f_t max_time_on_probing = diversity_config.max_time_on_probing; @@ -344,9 +344,9 @@ solution_t diversity_manager_t::run_solver() { raft::common::nvtx::range fun_scope("run_solver"); - CUOPT_LOG_DEBUG("Determinism mode: %s", - context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC ? "deterministic" - : "opportunistic"); + CUOPT_LOG_DEBUG( + "Determinism mode: %s", + is_deterministic_mode(context.settings.determinism_mode) ? "deterministic" : "opportunistic"); // to automatically compute the solving time on scope exit auto timer_raii_guard = @@ -387,7 +387,7 @@ solution_t diversity_manager_t::run_solver() } population.timer = timer; - const f_t time_limit = timer.remaining_time(); + const f_t time_limit = timer.deterministic ? timer.get_time_limit() : timer.remaining_time(); const f_t lp_time_limit = std::min(diversity_config.max_time_on_lp, time_limit * diversity_config.time_ratio_on_init_lp); // after every change to the problem, we should resize all the relevant vars @@ -410,10 +410,12 @@ solution_t diversity_manager_t::run_solver() population.allocate_solutions(); if (check_b_b_preemption()) { return population.best_feasible(); } add_user_given_solutions(initial_sol_vector); + CUOPT_LOG_DEBUG("DM bootstrap: initial_sol_vector size after user solutions = %lu", + initial_sol_vector.size()); // Run CPUFJ early to find quick initial solutions ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + if (!diversity_config.dry_run && context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { ls.start_cpufj_scratch_threads(population); } @@ -432,21 +434,55 @@ solution_t diversity_manager_t::run_solver() if (tolerance_divisor == 0) { tolerance_divisor = 1; } f_t absolute_tolerance = context.settings.tolerances.absolute_tolerance; - pdlp_solver_settings_t pdlp_settings{}; - pdlp_settings.tolerances.relative_primal_tolerance = absolute_tolerance / tolerance_divisor; - pdlp_settings.tolerances.relative_dual_tolerance = absolute_tolerance / tolerance_divisor; - pdlp_settings.time_limit = lp_time_limit; - if (timer.deterministic) { pdlp_settings.iteration_limit = 100000; } - pdlp_settings.first_primal_feasible = false; - pdlp_settings.concurrent_halt = &global_concurrent_halt; - pdlp_settings.method = method_t::Concurrent; - pdlp_settings.inside_mip = true; - pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; - pdlp_settings.num_gpus = context.settings.num_gpus; - pdlp_settings.presolver = presolver_t::None; - - timer_t lp_timer(lp_time_limit); - auto lp_result = solve_lp_with_method(*problem_ptr, pdlp_settings, lp_timer); + auto lp_result = [&]() { + if (timer.deterministic) { + relaxed_lp_settings_t lp_settings{}; + lp_settings.time_limit = lp_time_limit; + lp_settings.work_limit = lp_time_limit; + lp_settings.tolerance = absolute_tolerance; + lp_settings.check_infeasibility = true; + lp_settings.return_first_feasible = false; + lp_settings.save_state = true; + lp_settings.per_constraint_residual = true; + lp_settings.has_initial_primal = false; + lp_settings.concurrent_halt = &global_concurrent_halt; + lp_settings.work_context = &context.gpu_heur_loop; + cuopt_assert(lp_settings.work_context != nullptr, "Missing deterministic work context"); + CUOPT_LOG_DEBUG( + "DM root LP config: dry_run=%d deterministic=%d work_limit=%.6f time_limit=%.6f", + (int)diversity_config.dry_run, + (int)timer.deterministic, + lp_settings.work_limit, + lp_settings.time_limit); + return get_relaxed_lp_solution( + *problem_ptr, lp_optimal_solution, lp_state, lp_settings); + } + pdlp_solver_settings_t pdlp_settings{}; + pdlp_settings.tolerances.relative_primal_tolerance = absolute_tolerance / tolerance_divisor; + pdlp_settings.tolerances.relative_dual_tolerance = absolute_tolerance / tolerance_divisor; + pdlp_settings.time_limit = lp_time_limit; + pdlp_settings.first_primal_feasible = false; + pdlp_settings.concurrent_halt = &global_concurrent_halt; + pdlp_settings.method = method_t::Concurrent; + pdlp_settings.inside_mip = true; + pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; + pdlp_settings.num_gpus = context.settings.num_gpus; + pdlp_settings.presolver = presolver_t::None; + CUOPT_LOG_DEBUG( + "DM root LP config: dry_run=%d deterministic=%d lp_time_limit=%.6f iter_limit=%d", + (int)diversity_config.dry_run, + (int)timer.deterministic, + pdlp_settings.time_limit, + pdlp_settings.iteration_limit); + timer_t lp_timer(lp_time_limit); + return solve_lp_with_method(*problem_ptr, pdlp_settings, lp_timer); + }(); + CUOPT_LOG_DEBUG( + "DM root LP result: status=%d iters=%d user_obj=%.12f primal_hash=0x%x", + (int)lp_result.get_termination_status(), + lp_result.get_additional_termination_information().number_of_steps_taken, + lp_result.get_objective_value(), + detail::compute_hash(lp_result.get_primal_solution(), problem_ptr->handle_ptr->get_stream())); { std::lock_guard guard(relaxed_solution_mutex); @@ -532,19 +568,38 @@ solution_t diversity_manager_t::run_solver() // in case the pdlp returned var boudns that are out of bounds clamp_within_var_bounds(lp_optimal_solution, problem_ptr, problem_ptr->handle_ptr); + CUOPT_LOG_DEBUG( + "DM root LP post-clamp: lp_optimal_solution hash=0x%x", + detail::compute_hash(lp_optimal_solution, problem_ptr->handle_ptr->get_stream())); } if (ls.lp_optimal_exists) { solution_t lp_rounded_sol(*problem_ptr); lp_rounded_sol.copy_new_assignment(lp_optimal_solution); + CUOPT_LOG_DEBUG("DM bootstrap candidate (LP raw): hash=0x%x feas=%d obj=%.12f", + lp_rounded_sol.get_hash(), + (int)lp_rounded_sol.get_feasible(), + lp_rounded_sol.get_user_objective()); lp_rounded_sol.round_nearest(); lp_rounded_sol.compute_feasibility(); + CUOPT_LOG_DEBUG("DM bootstrap candidate (LP rounded): hash=0x%x feas=%d obj=%.12f", + lp_rounded_sol.get_hash(), + (int)lp_rounded_sol.get_feasible(), + lp_rounded_sol.get_user_objective()); population.add_solution(std::move(lp_rounded_sol)); - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + if (!diversity_config.dry_run && + context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { ls.start_cpufj_lptopt_scratch_threads(population); } } + for (size_t i = 0; i < initial_sol_vector.size(); ++i) { + CUOPT_LOG_DEBUG("DM bootstrap candidate (initial_sol_vector[%lu]): hash=0x%x feas=%d obj=%.12f", + i, + initial_sol_vector[i].get_hash(), + (int)initial_sol_vector[i].get_feasible(), + initial_sol_vector[i].get_user_objective()); + } population.add_solutions_from_vec(std::move(initial_sol_vector)); if (check_b_b_preemption()) { return population.best_feasible(); } @@ -715,7 +770,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& sol1.get_feasible(), sol2.get_quality(population.weights), sol2.get_feasible()); - bool deterministic = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; + bool deterministic = is_deterministic_mode(context.settings.determinism_mode); double best_objective_of_parents = std::min(sol1.get_objective(), sol2.get_objective()); bool at_least_one_parent_feasible = sol1.get_feasible() || sol2.get_feasible(); // randomly choose among 3 recombiners diff --git a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh index d809aa0fb3..1909a33cff 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh @@ -88,7 +88,8 @@ class fp_recombiner_t : public recombiner_t { CUOPT_LOG_TRACE("FP rec: running LP with infeasibility detection"); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; - if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC || + this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS) { lp_settings.time_limit = std::numeric_limits::max(); // TODO should be global time limit lp_settings.work_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; @@ -117,6 +118,13 @@ class fp_recombiner_t : public recombiner_t { offspring.handle_ptr->sync_stream(); offspring.assignment = std::move(fixed_assignment); cuopt_func_call(offspring.test_variable_bounds(false)); + CUOPT_LOG_DEBUG( + "FP rec pre-descent: offspring_hash=0x%x fixed_assignment_hash=0x%x " + "problem_fingerprint=0x%x fixed_n_integer_vars=%d", + offspring.get_hash(), + detail::compute_hash(offspring.assignment, offspring.handle_ptr->get_stream()), + fixed_problem.get_fingerprint(), + fixed_problem.n_integer_vars); work_limit_timer_t timer(this->context.gpu_heur_loop, fp_recombiner_config_t::fp_time_limit); fp.timer = timer; fp.cycle_queue.reset(offspring); diff --git a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh index eccbe658f6..fbb354e243 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh @@ -227,7 +227,8 @@ class recombiner_t { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } // submip not supported in deterministic mode yet - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC || + context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS) { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } recombiner_t::enabled_recombiners = diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu index a975e5a7cf..9500614473 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu @@ -868,7 +868,7 @@ void fj_t::refresh_lhs_and_violation(const rmm::cuda_stream_view& stre thrust::plus()); data.violation_score.set_value_async(violation, stream); data.weighted_violation_score.set_value_async(weighted_violation, stream); - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + if (is_deterministic_mode(context.settings.determinism_mode)) { data.violated_constraints.sort(stream); } #if FJ_SINGLE_STEP @@ -1090,9 +1090,10 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) // every now and then, ensure external solutions are added to the population // this is done here because FJ is called within FP and also after recombiners // so FJ is one of the most inner and most frequent functions to be called - if (steps % 10000 == 0) { - context.diversity_manager_ptr->get_population_pointer() - ->add_external_solutions_to_population(); + if (steps % 10000 == 0 && context.diversity_manager_ptr != nullptr) { + auto* population_ptr = context.diversity_manager_ptr->get_population_pointer(); + cuopt_assert(population_ptr != nullptr, ""); + population_ptr->add_external_solutions_to_population(); } #if !FJ_SINGLE_STEP @@ -1279,13 +1280,22 @@ template i_t fj_t::solve(solution_t& solution) { raft::common::nvtx::range scope("fj_solve"); - work_limit_timer_t timer(context.gpu_heur_loop, settings.time_limit); + bool deterministic = is_deterministic_mode(context.settings.determinism_mode); + if (deterministic) { + settings.time_limit = std::max((f_t)0.0, settings.time_limit); + settings.work_limit = settings.time_limit; + } handle_ptr = const_cast(solution.handle_ptr); pb_ptr = solution.problem_ptr; if (settings.mode != fj_mode_t::ROUNDING) { cuopt_func_call(solution.test_variable_bounds(true)); cuopt_assert(solution.test_number_all_integer(), "All integers must be rounded"); } + if (deterministic && settings.work_limit == 0.0) { + CUOPT_LOG_DEBUG("FJ: skipping solve due to exhausted deterministic work budget"); + return solution.compute_feasibility(); + } + work_limit_timer_t timer(context.gpu_heur_loop, settings.time_limit); pb_ptr->check_problem_representation(true); resize_vectors(solution.handle_ptr); @@ -1299,8 +1309,6 @@ i_t fj_t::solve(solution_t& solution) detail::compute_hash(cstr_left_weights, handle_ptr->get_stream()), detail::compute_hash(cstr_right_weights, handle_ptr->get_stream())); - bool deterministic = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; - if (deterministic) { settings.work_limit = settings.time_limit; } // if work_limit is set: compute an estimate of the number of iterations required if (deterministic && settings.work_limit != std::numeric_limits::infinity()) { std::map features_map = get_feature_vector(0); @@ -1408,7 +1416,7 @@ i_t fj_t::solve(solution_t& solution) // Compute the work unit corresponding to the number of iterations elapsed // by incrementally guessing work units until the model predicts >= actual iterations // TODO: awfully ugly, change - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC && iterations > 0) { + if (is_deterministic_mode(context.settings.determinism_mode) && iterations > 0) { double guessed_work = 0.0; const double work_increment = 0.1; const double max_work = settings.work_limit * 2.0; // Safety limit diff --git a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu index c035479cae..f13dd3af90 100644 --- a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu @@ -147,18 +147,36 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t temp_p(*solution.problem_ptr); auto h_integer_indices = cuopt::host_copy(solution.problem_ptr->integer_indices, solution.handle_ptr->get_stream()); + cuopt_assert(h_assignment.size() == solution.problem_ptr->n_variables, "Size mismatch"); + cuopt_assert(h_last_projection.size() == solution.problem_ptr->n_variables, "Size mismatch"); + cuopt_assert(h_variable_bounds.size() == solution.problem_ptr->n_variables, "Size mismatch"); + CUOPT_LOG_DEBUG( + "FP proj inputs: assign_hash=0x%x last_proj_hash=0x%x integer_idx_hash=0x%x n_vars=%d n_int=%d", + detail::compute_hash(h_assignment), + detail::compute_hash(h_last_projection), + detail::compute_hash(h_integer_indices), + solution.problem_ptr->n_variables, + solution.problem_ptr->n_integer_vars); f_t obj_offset = 0; + i_t n_at_upper = 0; + i_t n_at_lower = 0; + i_t n_interior = 0; + std::vector interior_integer_indices; + interior_integer_indices.reserve(h_integer_indices.size()); // for each integer add the variable and the distance constraints for (auto i : h_integer_indices) { + cuopt_assert(i >= 0 && i < solution.problem_ptr->n_variables, "Index out of bounds"); auto h_var_bounds = h_variable_bounds[i]; if (solution.problem_ptr->integer_equal(h_assignment[i], get_upper(h_var_bounds))) { obj_offset += get_upper(h_var_bounds); // set the objective weight to -1, u - x obj_coefficients[i] = -1; + n_at_upper++; } else if (solution.problem_ptr->integer_equal(h_assignment[i], get_lower(h_var_bounds))) { obj_offset -= get_lower(h_var_bounds); // set the objective weight to +1, x - l obj_coefficients[i] = 1; + n_at_lower++; } else { // objective weight is 1 const f_t obj_weight = 1.; @@ -183,9 +201,29 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t constr_coeffs_2{1, 1}; h_constraints.add_constraint( constr_indices, constr_coeffs_2, h_assignment[i], (f_t)default_cont_upper); + n_interior++; + interior_integer_indices.push_back(i); } } + CUOPT_LOG_DEBUG( + "FP proj build: at_lower=%d at_upper=%d interior=%d interior_idx_hash=0x%x obj_hash=0x%x " + "assign_aug_hash=0x%x vars_added=%d cstr_added=%d cstr_var_hash=0x%x cstr_coeff_hash=0x%x " + "cstr_offset_hash=0x%x cstr_lb_hash=0x%x cstr_ub_hash=0x%x", + n_at_lower, + n_at_upper, + n_interior, + detail::compute_hash(interior_integer_indices), + detail::compute_hash(obj_coefficients), + detail::compute_hash(h_assignment), + h_variables.size(), + h_constraints.n_constraints(), + detail::compute_hash(h_constraints.constraint_variables), + detail::compute_hash(h_constraints.constraint_coefficients), + detail::compute_hash(h_constraints.constraint_offsets), + detail::compute_hash(h_constraints.constraint_lower_bounds), + detail::compute_hash(h_constraints.constraint_upper_bounds)); adjust_objective_with_original(solution, obj_coefficients, longer_lp_run); + CUOPT_LOG_DEBUG("FP proj adjusted objective hash=0x%x", detail::compute_hash(obj_coefficients)); // commit all the changes that were done by the host if (h_variables.size() > 0) { temp_p.insert_variables(h_variables); } if (h_constraints.n_constraints() > 0) { temp_p.insert_constraints(h_constraints); } @@ -196,6 +234,11 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tget_stream()), + temp_p.n_variables, + temp_p.n_constraints); // copy new objective coefficients raft::copy(temp_p.objective_coefficients.data(), obj_coefficients.data(), @@ -208,14 +251,20 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t::round(solution_t& solution) { bool result; CUOPT_LOG_DEBUG("Rounding the point"); - work_limit_timer_t bounds_prop_timer(context.gpu_heur_loop, - std::max(0.05, std::min(0.5, timer.remaining_time() / 10.))); + f_t bounds_prop_time_limit = std::min((f_t)0.5, timer.remaining_time() / 10.); + if (timer.deterministic) { + bounds_prop_time_limit = std::max((f_t)0.0, bounds_prop_time_limit); + } else { + bounds_prop_time_limit = std::max((f_t)0.05, bounds_prop_time_limit); + } + work_limit_timer_t bounds_prop_timer(context.gpu_heur_loop, bounds_prop_time_limit); const f_t lp_run_time_after_feasible = 0.; bool old_var = constraint_prop.round_all_vars; f_t old_time = constraint_prop.max_time_for_bounds_prop; @@ -308,6 +362,13 @@ bool feasibility_pump_t::test_fj_feasible(solution_t& soluti fj.settings.feasibility_run = true; fj.settings.n_of_minimums_for_exit = 5000; fj.settings.time_limit = std::min(time_limit, timer.remaining_time()); + if (timer.deterministic) { + fj.settings.time_limit = std::max((f_t)0.0, fj.settings.time_limit); + if (fj.settings.time_limit == 0.0) { + CUOPT_LOG_DEBUG("Skipping 20%% FJ run due to exhausted deterministic work budget"); + return false; + } + } cuopt_func_call(solution.test_variable_bounds(true)); is_feasible = fj.solve(solution); cuopt_func_call(solution.test_variable_bounds(true)); @@ -472,14 +533,40 @@ template bool feasibility_pump_t::run_single_fp_descent(solution_t& solution) { raft::common::nvtx::range fun_scope("run_single_fp_descent"); + i_t fp_iter = 0; + CUOPT_LOG_DEBUG("FP descent start: hash=0x%x feas=%d obj=%.12f timer_det=%d rem=%.6f", + solution.get_hash(), + (int)solution.get_feasible(), + solution.get_user_objective(), + (int)timer.deterministic, + timer.remaining_time()); // start by doing nearest rounding solution.round_nearest(); + CUOPT_LOG_DEBUG("FP descent after initial round: hash=0x%x feas=%d obj=%.12f", + solution.get_hash(), + (int)solution.get_feasible(), + solution.get_user_objective()); + cuopt_assert(last_projection.size() == solution.assignment.size(), "Size mismatch"); + // First projection in a descent has no previous projection history: initialize explicitly + // to avoid dependence on stale/uninitialized buffer contents. + raft::copy(last_projection.data(), + solution.assignment.data(), + solution.assignment.size(), + solution.handle_ptr->get_stream()); raft::copy(last_rounding.data(), solution.assignment.data(), solution.assignment.size(), solution.handle_ptr->get_stream()); while (true) { - if (context.diversity_manager_ptr->check_b_b_preemption() || timer.check_time_limit()) { + CUOPT_LOG_DEBUG("FP iter %d pre-projection: hash=0x%x feas=%d obj=%.12f rem=%.6f", + fp_iter, + solution.get_hash(), + (int)solution.get_feasible(), + solution.get_user_objective(), + timer.remaining_time()); + bool preempt = (context.diversity_manager_ptr != nullptr && + context.diversity_manager_ptr->check_b_b_preemption()); + if (preempt || timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); round(solution); return false; @@ -489,10 +576,25 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s f_t ratio_of_assigned_integers = f_t(solution.n_assigned_integers) / solution.problem_ptr->n_integer_vars; bool is_feasible = linear_project_onto_polytope(solution, ratio_of_assigned_integers); - i_t n_integers = solution.compute_number_of_integers(); + const f_t remaining_after_projection = timer.remaining_time(); + i_t n_integers = solution.compute_number_of_integers(); CUOPT_LOG_DEBUG("after fp projection n_integers %d total n_integes %d", n_integers, solution.problem_ptr->n_integer_vars); + CUOPT_LOG_DEBUG( + "FP iter %d post-projection: hash=0x%x feasible_after_lp=%d obj=%.12f rem=%.6f lp_stage=%.6f", + fp_iter, + solution.get_hash(), + (int)is_feasible, + solution.get_user_objective(), + remaining_after_projection, + proj_begin - remaining_after_projection); + CUOPT_LOG_DEBUG("FP iter %d pre-round: hash=0x%x feas=%d obj=%.12f rem=%.6f", + fp_iter, + solution.get_hash(), + (int)is_feasible, + solution.get_user_objective(), + remaining_after_projection); bool is_cycle = true; // temp comment for presolve run if (config.check_distance_cycle) { @@ -524,30 +626,71 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s // run the LP with full precision to check if it actually is feasible const f_t lp_verify_time_limit = 5.; relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_verify_time_limit; + lp_settings.time_limit = lp_verify_time_limit; + bool run_verify_lp = true; + if (timer.deterministic) { + const f_t remaining_work_limit = std::max((f_t)0.0, timer.remaining_time()); + lp_settings.work_limit = std::min(lp_verify_time_limit, remaining_work_limit); + lp_settings.time_limit = lp_settings.work_limit; + if (lp_settings.work_limit == 0.0) { + CUOPT_LOG_DEBUG( + "Skipping FP verification LP due to exhausted deterministic work budget"); + run_verify_lp = false; + } + } + lp_settings.work_context = timer.work_context; lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; lp_settings.return_first_feasible = true; lp_settings.save_state = true; - run_lp_with_vars_fixed(*solution.problem_ptr, - solution, - solution.problem_ptr->integer_indices, - lp_settings, - &constraint_prop.bounds_update); - is_feasible = solution.get_feasible(); - n_integers = solution.compute_number_of_integers(); - if (is_feasible && n_integers == solution.problem_ptr->n_integer_vars) { - CUOPT_LOG_DEBUG("Feasible solution verified with LP!"); - return true; + if (run_verify_lp) { + run_lp_with_vars_fixed(*solution.problem_ptr, + solution, + solution.problem_ptr->integer_indices, + lp_settings, + &constraint_prop.bounds_update); + is_feasible = solution.get_feasible(); + n_integers = solution.compute_number_of_integers(); + if (is_feasible && n_integers == solution.problem_ptr->n_integer_vars) { + CUOPT_LOG_DEBUG("Feasible solution verified with LP!"); + return true; + } } } } cuopt_func_call(solution.test_variable_bounds(false)); is_feasible = round(solution); cuopt_func_call(solution.test_variable_bounds(true)); - proj_and_round_time = proj_begin - timer.remaining_time(); + const f_t remaining_after_round = timer.remaining_time(); + proj_and_round_time = proj_begin - remaining_after_round; + CUOPT_LOG_DEBUG( + "FP iter %d post-round: hash=0x%x feasible_after_round=%d obj=%.12f rem=%.6f " + "round_stage=%.6f proj_round_total=%.6f", + fp_iter, + solution.get_hash(), + (int)is_feasible, + solution.get_user_objective(), + remaining_after_round, + remaining_after_projection - remaining_after_round, + proj_and_round_time); if (!is_feasible) { const f_t time_ratio = 0.2; - is_feasible = test_fj_feasible(solution, time_ratio * proj_and_round_time); + const f_t fj_budget = time_ratio * proj_and_round_time; + CUOPT_LOG_DEBUG("FP iter %d pre-fj-fallback: hash=0x%x rem=%.6f fj_budget=%.6f", + fp_iter, + solution.get_hash(), + remaining_after_round, + fj_budget); + is_feasible = test_fj_feasible(solution, fj_budget); + const f_t remaining_after_fj = timer.remaining_time(); + CUOPT_LOG_DEBUG( + "FP iter %d post-fj-fallback: hash=0x%x feasible_after_fj=%d obj=%.12f rem=%.6f " + "fj_stage=%.6f", + fp_iter, + solution.get_hash(), + (int)is_feasible, + solution.get_user_objective(), + remaining_after_fj, + remaining_after_round - remaining_after_fj); } if (timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); @@ -576,6 +719,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s return false; } cycle_queue.n_iterations_without_cycle++; + fp_iter++; } // unreachable return false; diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index dc28df59de..b4a6a400ea 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -329,7 +329,9 @@ void local_search_t::generate_fast_solution(solution_t& solu fj.settings.update_weights = true; fj.settings.feasibility_run = true; fj.settings.time_limit = std::min(30., timer.remaining_time()); - while (!context.diversity_manager_ptr->check_b_b_preemption() && !timer.check_time_limit()) { + while ((context.diversity_manager_ptr == nullptr || + !context.diversity_manager_ptr->check_b_b_preemption()) && + !timer.check_time_limit()) { work_limit_timer_t constr_prop_timer = work_limit_timer_t(context.gpu_heur_loop, std::min(timer.remaining_time(), 2.)); // do constraint prop on lp optimal solution diff --git a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu index a774e5f060..b04707106a 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu @@ -769,10 +769,7 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob i_t n_of_repairs_needed_for_feasible = 0; // TODO: do this better i_t iter_limit = std::numeric_limits::max(); - if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - timer = work_limit_timer_t(context.gpu_heur_loop, std::numeric_limits::infinity()); - iter_limit = 100; - } + if (is_deterministic_mode(this->context.settings.determinism_mode)) { iter_limit = 100; } do { n_of_repairs_needed_for_feasible++; if (timer.check_time_limit() || iter_limit-- <= 0) { diff --git a/cpp/src/mip_heuristics/mip_constants.hpp b/cpp/src/mip_heuristics/mip_constants.hpp index 66f5ebd273..3d37a7f967 100644 --- a/cpp/src/mip_heuristics/mip_constants.hpp +++ b/cpp/src/mip_heuristics/mip_constants.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -11,3 +11,18 @@ #define MIP_INSTANTIATE_FLOAT CUOPT_INSTANTIATE_FLOAT #define MIP_INSTANTIATE_DOUBLE CUOPT_INSTANTIATE_DOUBLE + +namespace cuopt::linear_programming::detail { + +inline constexpr bool is_deterministic_mode(int determinism_mode) +{ + return determinism_mode == CUOPT_MODE_DETERMINISTIC || + determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; +} + +inline constexpr bool is_gpu_heuristics_deterministic_mode(int determinism_mode) +{ + return determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; +} + +} // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip_heuristics/presolve/bounds_presolve.cu b/cpp/src/mip_heuristics/presolve/bounds_presolve.cu index 0a5ca5f580..04a9287b13 100644 --- a/cpp/src/mip_heuristics/presolve/bounds_presolve.cu +++ b/cpp/src/mip_heuristics/presolve/bounds_presolve.cu @@ -172,7 +172,7 @@ termination_criterion_t bound_presolve_t::bound_update_loop(problem_t< termination_criterion_t criteria = termination_criterion_t::ITERATION_LIMIT; // CHANGE once we have a work predictor - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + if (is_deterministic_mode(context.settings.determinism_mode)) { timer = timer_t(std::numeric_limits::infinity()); settings.iteration_limit = std::min(settings.iteration_limit, 50); } diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index a3bcbd2257..3dc35ddd02 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -21,6 +21,8 @@ #include +#include + namespace cuopt::linear_programming::detail { template @@ -40,6 +42,8 @@ optimization_problem_solution_t get_relaxed_lp_solution( const relaxed_lp_settings_t& settings) { raft::common::nvtx::range fun_scope("get_relaxed_lp_solution"); + static std::atomic lp_call_counter{0}; + const uint64_t lp_call_id = lp_call_counter.fetch_add(1, std::memory_order_relaxed); // auto function_start_time = std::chrono::high_resolution_clock::now(); // // === PDLP PREDICTOR FEATURES - START === @@ -82,9 +86,10 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.per_constraint_residual = settings.per_constraint_residual; pdlp_settings.first_primal_feasible = settings.return_first_feasible; pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; + int estim_iters = pdlp_settings.iteration_limit; if (determinism_mode) { // try to estimate the iteration count based on the requested work limit - int estim_iters = 100; + estim_iters = 100; do { // TODO: use an actual predictor model here double estim_ms = 313 + 200 * op_problem.n_variables - 400 * op_problem.n_constraints + @@ -99,6 +104,20 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable3; pdlp_settings.presolver = presolver_t::None; } + CUOPT_LOG_DEBUG( + "LP call %lu config: det=%d work_limit=%.6f time_limit=%.6f iter_limit=%d method=%d mode=%d " + "presolver=%d save_state=%d has_initial=%d assignment_hash=0x%x", + lp_call_id, + (int)determinism_mode, + settings.work_limit, + pdlp_settings.time_limit, + pdlp_settings.iteration_limit, + (int)pdlp_settings.method, + (int)pdlp_settings.pdlp_solver_mode, + (int)pdlp_settings.presolver, + (int)settings.save_state, + (int)settings.has_initial_primal, + detail::compute_hash(assignment, op_problem.handle_ptr->get_stream())); set_pdlp_solver_mode(pdlp_settings); // TODO: set Stable3 here? pdlp_solver_t lp_solver(op_problem, pdlp_settings); @@ -136,6 +155,30 @@ optimization_problem_solution_t get_relaxed_lp_solution( "prev solution sizes primal=%lu dual=%lu", assignment.size(), lp_state.prev_dual.size()); auto solver_response = lp_solver.run_solver(start_time); CUOPT_LOG_DEBUG("post LP primal size %lu", solver_response.get_primal_solution().size()); + const int actual_iters = + solver_response.get_additional_termination_information().number_of_steps_taken; + CUOPT_LOG_DEBUG("LP call %lu result: status=%d iters=%d primal_hash=0x%x", + lp_call_id, + (int)solver_response.get_termination_status(), + actual_iters, + solver_response.get_primal_solution().size() != 0 + ? detail::compute_hash(solver_response.get_primal_solution(), + op_problem.handle_ptr->get_stream()) + : 0u); + if (determinism_mode && settings.work_context != nullptr) { + double work_to_record = settings.work_limit; + if (estim_iters > 0) { + work_to_record = + settings.work_limit * std::clamp((double)actual_iters / (double)estim_iters, 0.0, 1.0); + } + CUOPT_LOG_DEBUG("LP call %lu recording %.6fwu (actual_iters=%d estim_iters=%d requested=%.6f)", + lp_call_id, + work_to_record, + actual_iters, + estim_iters, + settings.work_limit); + settings.work_context->record_work_sync_on_horizon(work_to_record); + } if (solver_response.get_primal_solution().size() != 0 && solver_response.get_dual_solution().size() != 0 && settings.save_state) { @@ -149,6 +192,9 @@ optimization_problem_solution_t get_relaxed_lp_solution( solver_response.get_primal_solution().size(), op_problem.handle_ptr->get_stream()); } + CUOPT_LOG_DEBUG("LP call %lu assignment_after_copy hash=0x%x", + lp_call_id, + detail::compute_hash(assignment, op_problem.handle_ptr->get_stream())); if (solver_response.get_termination_status() == pdlp_termination_status_t::Optimal) { // CUOPT_LOG_DEBUG("feasible solution found with LP objective %f", // solver_response.get_objective_value()); diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cuh b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cuh index 5c229d9b6c..06698d79ae 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cuh +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cuh @@ -12,21 +12,23 @@ #include #include #include +#include #include "lp_state.cuh" namespace cuopt::linear_programming::detail { struct relaxed_lp_settings_t { - double tolerance = 1e-4; - double time_limit = 1.0; - int iteration_limit = std::numeric_limits::max(); - double work_limit = std::numeric_limits::infinity(); - bool check_infeasibility = true; - bool return_first_feasible = false; - bool save_state = true; - bool per_constraint_residual = true; - bool has_initial_primal = true; - std::atomic* concurrent_halt = nullptr; + double tolerance = 1e-4; + double time_limit = 1.0; + int iteration_limit = std::numeric_limits::max(); + double work_limit = std::numeric_limits::infinity(); + bool check_infeasibility = true; + bool return_first_feasible = false; + bool save_state = true; + bool per_constraint_residual = true; + bool has_initial_primal = true; + std::atomic* concurrent_halt = nullptr; + cuopt::work_limit_context_t* work_context = nullptr; }; template diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 1ebb671832..0db033a0f5 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -246,8 +246,9 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, double presolve_time = 0.0; std::unique_ptr> presolver; std::optional> presolve_result; - detail::problem_t problem( - op_problem, settings.get_tolerances(), settings.determinism_mode == CUOPT_MODE_DETERMINISTIC); + detail::problem_t problem(op_problem, + settings.get_tolerances(), + detail::is_deterministic_mode(settings.determinism_mode)); auto run_presolve = settings.presolver != presolver_t::None; run_presolve = run_presolve && settings.initial_solutions.size() == 0; @@ -272,7 +273,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, // allocate not more than 10% of the time limit to presolve. // Note that this is not the presolve time, but the time limit for presolve. double presolve_time_limit = std::min(0.1 * time_limit, 60.0); - if (settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + if (detail::is_deterministic_mode(settings.determinism_mode)) { presolve_time_limit = std::numeric_limits::infinity(); } presolver = std::make_unique>(); diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 5a242716dc..b8e1366d9c 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -109,11 +109,11 @@ solution_t mip_solver_t::run_solver() } dm.timer = work_limit_timer_t(context.gpu_heur_loop, timer_.get_time_limit()); const bool run_presolve = context.settings.presolver != presolver_t::None; - f_t time_limit = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC + f_t time_limit = is_deterministic_mode(context.settings.determinism_mode) ? std::numeric_limits::infinity() : timer_.remaining_time(); double presolve_time_limit = std::min(0.1 * time_limit, 60.0); - presolve_time_limit = context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC + presolve_time_limit = is_deterministic_mode(context.settings.determinism_mode) ? std::numeric_limits::infinity() : presolve_time_limit; bool presolve_success = run_presolve ? dm.run_presolve(presolve_time_limit, timer_) : true; @@ -210,9 +210,9 @@ solution_t mip_solver_t::run_solver() branch_and_bound_settings.max_cut_passes = context.settings.max_cut_passes; branch_and_bound_settings.mir_cuts = context.settings.mir_cuts; branch_and_bound_settings.deterministic = - context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; + is_deterministic_mode(context.settings.determinism_mode); - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + if (is_deterministic_mode(context.settings.determinism_mode)) { branch_and_bound_settings.work_limit = context.settings.work_limit; } else { branch_and_bound_settings.work_limit = std::numeric_limits::infinity(); @@ -276,7 +276,7 @@ solution_t mip_solver_t::run_solver() std::bind(&dual_simplex::branch_and_bound_t::set_new_solution, branch_and_bound.get(), std::placeholders::_1); - } else if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { + } else if (is_deterministic_mode(context.settings.determinism_mode)) { branch_and_bound->set_concurrent_lp_root_solve(false); // TODO once deterministic GPU heuristics are integrated // context.problem_ptr->branch_and_bound_callback = diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index c5f7733683..8f401fe514 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -52,7 +53,7 @@ struct mip_solver_context_t { cuopt_assert(problem_ptr != nullptr, "problem_ptr is nullptr"); stats.set_solution_bound(problem_ptr->maximize ? std::numeric_limits::infinity() : -std::numeric_limits::infinity()); - gpu_heur_loop.deterministic = settings.determinism_mode == CUOPT_MODE_DETERMINISTIC; + gpu_heur_loop.deterministic = is_deterministic_mode(settings.determinism_mode); } mip_solver_context_t(const mip_solver_context_t&) = delete; diff --git a/cpp/src/utilities/timer.hpp b/cpp/src/utilities/timer.hpp index 6bf8795fca..7ee849c727 100644 --- a/cpp/src/utilities/timer.hpp +++ b/cpp/src/utilities/timer.hpp @@ -46,7 +46,7 @@ class timer_t { line, caller); // assert(false && "unexpected timer"); - //__builtin_trap(); + __builtin_trap(); } return elapsed; } diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index 663038c974..3bd9e0a39f 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -68,7 +69,8 @@ void init_handler(const raft::handle_t* handle_ptr) static void setup_device_symbols(rmm::cuda_stream_view stream_view) { (void)stream_view; } static uint32_t test_full_run_determinism(std::string path, - unsigned long seed = std::random_device{}()) + unsigned long seed = std::random_device{}(), + float work_limit = 10.0f) { const raft::handle_t handle_{}; @@ -99,16 +101,16 @@ static uint32_t test_full_run_determinism(std::string path, auto settings = mip_solver_settings_t{}; settings.time_limit = 3000.; - settings.work_limit = 10; // about 10 seconds of runtime - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.work_limit = work_limit; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; settings.heuristics_only = true; auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); - work_limit_context_t work_limit_context("DiversityManager"); - diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); + solver.context.gpu_heur_loop.deterministic = true; + diversity_manager.timer = work_limit_timer_t(solver.context.gpu_heur_loop, settings.work_limit); diversity_manager.run_solver(); std::vector hashes; @@ -157,7 +159,7 @@ static uint32_t test_initial_solution_determinism(std::string path, auto settings = mip_solver_settings_t{}; settings.time_limit = 3000.; - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; settings.heuristics_only = true; auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); @@ -165,7 +167,8 @@ static uint32_t test_initial_solution_determinism(std::string path, detail::diversity_manager_t diversity_manager(solver.context); work_limit_context_t work_limit_context("DiversityManager"); - diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); + work_limit_context.deterministic = true; + diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); diversity_manager.diversity_config.initial_solution_only = true; diversity_manager.run_solver(); @@ -215,7 +218,7 @@ static uint32_t test_recombiners_determinism(std::string path, auto settings = mip_solver_settings_t{}; settings.time_limit = 3000.; - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; settings.heuristics_only = true; auto timer = cuopt::timer_t(3000); detail::mip_solver_t solver(problem, settings, scaling, timer); @@ -223,6 +226,7 @@ static uint32_t test_recombiners_determinism(std::string path, detail::diversity_manager_t diversity_manager(solver.context); work_limit_context_t work_limit_context("DiversityManager"); + work_limit_context.deterministic = true; diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); diversity_manager.diversity_config.dry_run = true; diversity_manager.run_solver(); @@ -236,14 +240,18 @@ static uint32_t test_recombiners_determinism(std::string path, fj_settings.feasibility_run = false; fj_settings.iteration_limit = 1000 + i * 100; fj_settings.seed = seed + i; - auto solution = - run_fj(problem, fj_settings, fj_tweaks_t{}, random_initial_solution.get_host_assignment()) - .solution; + auto solution = run_fj(problem, + fj_settings, + fj_tweaks_t{}, + random_initial_solution.get_host_assignment(), + CUOPT_MODE_DETERMINISTIC) + .solution; printf("population %d hash: 0x%x\n", i, solution.get_hash()); diversity_manager.population.add_solution(std::move(solution)); } auto pop_vector = diversity_manager.get_population_pointer()->population_to_vector(); + int pop_size = std::min(6, (int)pop_vector.size()); std::vector hashes; @@ -252,8 +260,8 @@ static uint32_t test_recombiners_determinism(std::string path, for (auto recombiner : {detail::recombiner_enum_t::LINE_SEGMENT, detail::recombiner_enum_t::BOUND_PROP, detail::recombiner_enum_t::FP}) { - for (int i = 1; i < (int)pop_vector.size(); i++) { - for (int j = i + 1; j < (int)pop_vector.size(); j++) { + for (int i = 1; i < pop_size; i++) { + for (int j = i + 1; j < pop_size; j++) { printf("recombining %d and %d w/ recombiner %s\n", i, j, @@ -299,67 +307,41 @@ static uint32_t test_recombiners_determinism(std::string path, return final_hash; } -class DiversityTestParams : public testing::TestWithParam> {}; - -// TEST_P(DiversityTestParams, recombiners_deterministic) -// { -// cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); - -// spin_stream_raii_t spin_stream_1; -// spin_stream_raii_t spin_stream_2; - -// auto test_instance = std::get<0>(GetParam()); -// std::cout << "Running: " << test_instance << std::endl; -// int seed = -// std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); -// std::cerr << "Tested with seed " << seed << "\n"; -// auto path = make_path_absolute(test_instance); -// test_instance = std::getenv("CUOPT_INSTANCE") ? std::getenv("CUOPT_INSTANCE") : test_instance; -// uint32_t gold_hash = 0; -// for (int i = 0; i < 2; ++i) { -// cuopt::seed_generator::set_seed(seed); -// std::cout << "Running " << test_instance << " " << i << std::endl; -// std::cout << "-------------------------------------------------------------\n"; -// auto hash = test_recombiners_determinism(path, seed); -// if (i == 0) { -// gold_hash = hash; -// std::cout << "Gold hash: " << gold_hash << std::endl; -// } else { -// ASSERT_EQ(hash, gold_hash); -// } -// } -// } - -// TEST_P(DiversityTestParams, initial_solution_deterministic) -// { -// cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); - -// spin_stream_raii_t spin_stream_1; -// spin_stream_raii_t spin_stream_2; - -// auto test_instance = std::get<0>(GetParam()); -// std::cout << "Running: " << test_instance << std::endl; -// int seed = -// std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); -// std::cerr << "Tested with seed " << seed << "\n"; -// auto path = make_path_absolute(test_instance); -// test_instance = std::getenv("CUOPT_INSTANCE") ? std::getenv("CUOPT_INSTANCE") : test_instance; -// uint32_t gold_hash = 0; -// for (int i = 0; i < 2; ++i) { -// cuopt::seed_generator::set_seed(seed); -// std::cout << "Running " << test_instance << " " << i << std::endl; -// std::cout << "-------------------------------------------------------------\n"; -// auto hash = test_initial_solution_determinism(path, seed); -// if (i == 0) { -// gold_hash = hash; -// std::cout << "Gold hash: " << gold_hash << std::endl; -// } else { -// ASSERT_EQ(hash, gold_hash); -// } -// } -// } +class DiversityTestParams : public testing::TestWithParam> {}; -TEST_P(DiversityTestParams, full_run_deterministic) +TEST_P(DiversityTestParams, recombiners_deterministic) +{ + // cuopt::init_logger_t log("", true); + cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); + cuopt::default_logger().set_level(rapids_logger::level_enum::debug); + cuopt::default_logger().flush_on(rapids_logger::level_enum::debug); + + spin_stream_raii_t spin_stream_1; + spin_stream_raii_t spin_stream_2; + + auto test_instance = std::get<0>(GetParam()); + std::cout << "Running: " << test_instance << std::endl; + int seed = + std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); + std::cerr << "Tested with seed " << seed << "\n"; + auto path = make_path_absolute(test_instance); + test_instance = std::getenv("CUOPT_INSTANCE") ? std::getenv("CUOPT_INSTANCE") : test_instance; + uint32_t gold_hash = 0; + for (int i = 0; i < 2; ++i) { + cuopt::seed_generator::set_seed(seed); + std::cout << "Running " << test_instance << " " << i << std::endl; + std::cout << "-------------------------------------------------------------\n"; + auto hash = test_recombiners_determinism(path, seed); + if (i == 0) { + gold_hash = hash; + std::cout << "Gold hash: " << gold_hash << std::endl; + } else { + ASSERT_EQ(hash, gold_hash); + } + } +} + +TEST_P(DiversityTestParams, initial_solution_deterministic) { cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); @@ -371,17 +353,50 @@ TEST_P(DiversityTestParams, full_run_deterministic) int seed = std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); std::cerr << "Tested with seed " << seed << "\n"; + auto path = make_path_absolute(test_instance); + test_instance = std::getenv("CUOPT_INSTANCE") ? std::getenv("CUOPT_INSTANCE") : test_instance; + uint32_t gold_hash = 0; + for (int i = 0; i < 2; ++i) { + cuopt::seed_generator::set_seed(seed); + std::cout << "Running " << test_instance << " " << i << std::endl; + std::cout << "-------------------------------------------------------------\n"; + auto hash = test_initial_solution_determinism(path, seed); + if (i == 0) { + gold_hash = hash; + std::cout << "Gold hash: " << gold_hash << std::endl; + } else { + ASSERT_EQ(hash, gold_hash); + } + } +} + +TEST_P(DiversityTestParams, full_run_deterministic) +{ + cuopt::init_logger_t log("", true); + // cuopt::default_logger().set_pattern("[%n] [%-6l] %v"); + cuopt::default_logger().set_level(rapids_logger::level_enum::debug); + cuopt::default_logger().flush_on(rapids_logger::level_enum::debug); + + // spin_stream_raii_t spin_stream_1; + // spin_stream_raii_t spin_stream_2; + + auto test_instance = std::get<0>(GetParam()); + const float work_limit = std::get<1>(GetParam()); + std::cout << "Running: " << test_instance << std::endl; + int seed = + std::getenv("CUOPT_SEED") ? std::stoi(std::getenv("CUOPT_SEED")) : std::random_device{}(); + std::cerr << "Tested with seed " << seed << "\n"; auto path = make_path_absolute(test_instance); if (std::getenv("CUOPT_INSTANCE")) { test_instance = std::getenv("CUOPT_INSTANCE"); path = make_path_absolute(test_instance); } uint32_t gold_hash = 0; - for (int i = 0; i < 2; ++i) { + for (int i = 0; i < 4; ++i) { cuopt::seed_generator::set_seed(seed); std::cout << "Running " << test_instance << " " << i << std::endl; std::cout << "-------------------------------------------------------------\n"; - auto hash = test_full_run_determinism(path, seed); + auto hash = test_full_run_determinism(path, seed, work_limit); if (i == 0) { gold_hash = hash; std::cout << "Gold hash: " << gold_hash << std::endl; @@ -393,15 +408,14 @@ TEST_P(DiversityTestParams, full_run_deterministic) INSTANTIATE_TEST_SUITE_P(DiversityTest, DiversityTestParams, - testing::Values( // std::make_tuple("gen-ip054.mps"), - // std::make_tuple("pk1.mps") - std::make_tuple("uccase9.mps"), - // std::make_tuple("mip/sct2.mps") - // std::make_tuple("mip/thor50dday.mps") - // std::make_tuple("uccase9.mps"), - // std::make_tuple("mip/neos5.mps") - std::make_tuple("50v-10.mps") - // std::make_tuple("rmatr200-p5.mps") - )); + testing::Values(std::make_tuple("mip/gen-ip054.mps", 5.0f), + std::make_tuple("mip/pk1.mps", 5.0f), + // std::make_tuple("mip/uccase9.mps"), + // std::make_tuple("mip/sct2.mps") + // std::make_tuple("mip/thor50dday.mps") + // std::make_tuple("uccase9.mps"), + std::make_tuple("mip/neos5.mps", 5.0f), + // std::make_tuple("mip/50v-10.mps"), + std::make_tuple("mip/rmatr200-p5.mps", 5.0f))); } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/feasibility_jump_tests.cu b/cpp/tests/mip/feasibility_jump_tests.cu index 6fd8ace9b8..b36fbcd316 100644 --- a/cpp/tests/mip/feasibility_jump_tests.cu +++ b/cpp/tests/mip/feasibility_jump_tests.cu @@ -50,7 +50,8 @@ void init_handler(const raft::handle_t* handle_ptr) static fj_state_t run_fj_instance(std::string test_instance, const detail::fj_settings_t& fj_settings, fj_tweaks_t tweaks = {}, - std::vector initial_solution = {}) + std::vector initial_solution = {}, + int determinism_mode = CUOPT_MODE_DETERMINISTIC) { const raft::handle_t handle_{}; std::cout << "Running: " << test_instance << std::endl; @@ -73,7 +74,7 @@ static fj_state_t run_fj_instance(std::string test_instance, detail::problem_t problem(op_problem); problem.preprocess_problem(); - return run_fj(problem, fj_settings, tweaks, initial_solution); + return run_fj(problem, fj_settings, tweaks, initial_solution, determinism_mode); } // FJ had a bug causing objective/violation values to explode in magnitude in certain scenarios. @@ -110,7 +111,8 @@ static bool run_fj_check_objective(std::string test_instance, int iter_limit, do fj_settings.feasibility_run = obj_target == +std::numeric_limits::infinity(); fj_settings.iteration_limit = iter_limit; - auto state = run_fj_instance(test_instance, fj_settings); + auto state = + run_fj_instance(test_instance, fj_settings, fj_tweaks_t{}, {}, CUOPT_MODE_DETERMINISTIC); auto& solution = state.solution; CUOPT_LOG_DEBUG("%s: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", @@ -170,21 +172,22 @@ static bool run_fj_check_determinism(std::string test_instance, int iter_limit) fj_settings.time_limit = std::numeric_limits::max(); fj_settings.mode = detail::fj_mode_t::EXIT_NON_IMPROVING; fj_settings.n_of_minimums_for_exit = 5000 * 1000; - fj_settings.work_limit = 0.5; // run for 0.5wu (~0.5s) - fj_settings.update_weights = true; - fj_settings.feasibility_run = false; - // fj_settings.iteration_limit = iter_limit; + // fj_settings.work_limit = 0.5; // run for 0.5wu (~0.5s) + fj_settings.update_weights = true; + fj_settings.feasibility_run = false; + fj_settings.iteration_limit = iter_limit; fj_settings.load_balancing_mode = detail::fj_load_balancing_mode_t::ALWAYS_ON; fj_settings.seed = cuopt::seed_generator::get_seed(); auto state = run_fj_instance(test_instance, fj_settings); auto& solution = state.solution; - CUOPT_LOG_DEBUG("%s: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", - test_instance.c_str(), - solution.get_feasible(), - solution.get_user_objective(), - solution.get_objective()); + printf("%s[seed=%x]: Solution generated with FJ: is_feasible %d, objective %g (raw %g)", + test_instance.c_str(), + fj_settings.seed, + solution.get_feasible(), + solution.get_user_objective(), + solution.get_objective()); static std::unordered_map first_val_map; if (first_val_map.count(test_instance) == 0) { diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index 194af741b3..32b5d024e4 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -222,9 +222,9 @@ TEST_P(LocalSearchTestParams, local_search_operator_determinism) //"thor50dday.mps", "gen-ip054.mps", "50v-10.mps", - //"seymour1.mps", + "seymour1.mps", "rmatr200-p5.mps", - //"tr12-30.mps", + "tr12-30.mps", //"sct2.mps", //"uccase9.mps" }) { @@ -253,9 +253,9 @@ TEST_P(LocalSearchTestParams, local_search_operator_determinism) INSTANTIATE_TEST_SUITE_P(LocalSearchTests, LocalSearchTestParams, - testing::Values( // std::make_tuple(local_search_mode_t::FP), - std::make_tuple(local_search_mode_t::FJ_LINE_SEGMENT), - std::make_tuple(local_search_mode_t::FJ_ON_ZERO), - std::make_tuple(local_search_mode_t::FJ_ANNEALING))); + testing::Values(std::make_tuple(local_search_mode_t::FP), + std::make_tuple(local_search_mode_t::FJ_LINE_SEGMENT), + std::make_tuple(local_search_mode_t::FJ_ON_ZERO), + std::make_tuple(local_search_mode_t::FJ_ANNEALING))); } // namespace cuopt::linear_programming::test diff --git a/cpp/tests/mip/mip_utils.cuh b/cpp/tests/mip/mip_utils.cuh index 0125987b1c..d2bd2832f9 100644 --- a/cpp/tests/mip/mip_utils.cuh +++ b/cpp/tests/mip/mip_utils.cuh @@ -200,7 +200,8 @@ struct fj_state_t { static fj_state_t run_fj(detail::problem_t& problem, const detail::fj_settings_t& fj_settings, fj_tweaks_t tweaks = {}, - std::vector initial_solution = {}) + std::vector initial_solution = {}, + int determinism_mode = CUOPT_MODE_OPPORTUNISTIC) { pdlp_hyper_params::pdlp_hyper_params_t hyper_params{}; detail::pdlp_initial_scaling_strategy_t scaling(problem.handle_ptr, @@ -214,9 +215,10 @@ static fj_state_t run_fj(detail::problem_t& problem, hyper_params, true); - auto settings = mip_solver_settings_t{}; - settings.time_limit = 30.; - auto timer = timer_t(30); + auto settings = mip_solver_settings_t{}; + settings.time_limit = 30.; + settings.determinism_mode = determinism_mode; + auto timer = timer_t(30); detail::mip_solver_t solver(problem, settings, scaling, timer); detail::solution_t solution(*solver.context.problem_ptr); diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index 58595f796d..64bbed34ab 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -142,8 +142,6 @@ multi_probe_results( uint32_t test_multi_probe(std::string path, unsigned long seed = std::random_device{}()) { - auto memory_resource = make_async(); - rmm::mr::set_current_device_resource(memory_resource.get()); const raft::handle_t handle_{}; cuopt::mps_parser::mps_data_model_t mps_problem = cuopt::mps_parser::parse_mps(path, false); @@ -215,16 +213,16 @@ uint32_t test_multi_probe(std::string path, unsigned long seed = std::random_dev return detail::compute_hash(hashes); } -// TEST(presolve, multi_probe) -// { -// std::vector test_instances = { -// "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; -// for (const auto& test_instance : test_instances) { -// std::cout << "Running: " << test_instance << std::endl; -// auto path = make_path_absolute(test_instance); -// test_multi_probe(path); -// } -// } +TEST(presolve, multi_probe) +{ + std::vector test_instances = { + "mip/50v-10-free-bound.mps", "mip/neos5-free-bound.mps", "mip/neos5.mps"}; + for (const auto& test_instance : test_instances) { + std::cout << "Running: " << test_instance << std::endl; + auto path = make_path_absolute(test_instance); + test_multi_probe(path); + } +} TEST(presolve, multi_probe_deterministic) { diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index f82fd8b292..e437c35b78 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -30,8 +30,6 @@ #include #include -#include - #include #include @@ -41,8 +39,6 @@ namespace cuopt::linear_programming::test { -inline auto make_async() { return std::make_shared(); } - void init_handler(const raft::handle_t* handle_ptr) { // Init cuBlas / cuSparse context here to avoid having it during solving time @@ -102,8 +98,6 @@ convert_probe_tuple(std::tuple, std::vector, std::vecto uint32_t test_probing_cache_determinism(std::string path, unsigned long seed = std::random_device{}()) { - auto memory_resource = make_async(); - rmm::mr::set_current_device_resource(memory_resource.get()); const raft::handle_t handle_{}; cuopt::mps_parser::mps_data_model_t mps_problem = cuopt::mps_parser::parse_mps(path, false); @@ -174,8 +168,6 @@ uint32_t test_probing_cache_determinism(std::string path, uint32_t test_scaling_determinism(std::string path, unsigned long seed = std::random_device{}()) { - auto memory_resource = make_async(); - rmm::mr::set_current_device_resource(memory_resource.get()); const raft::handle_t handle_{}; cuopt::mps_parser::mps_data_model_t mps_problem = cuopt::mps_parser::parse_mps(path, false); @@ -283,35 +275,35 @@ TEST(presolve, probing_cache_deterministic) } } -// TEST(presolve, mip_scaling_deterministic) -// { -// spin_stream_raii_t spin_stream_1; -// spin_stream_raii_t spin_stream_2; - -// std::vector test_instances = {"mip/sct2.mps", -// "mip/thor50dday.mps", -// "mip/uccase9.mps", -// "mip/neos5-free-bound.mps", -// "mip/neos5.mps", -// "mip/50v-10.mps", -// "mip/gen-ip054.mps", -// "mip/rmatr200-p5.mps"}; -// for (const auto& test_instance : test_instances) { -// std::cout << "Running: " << test_instance << std::endl; -// unsigned long seed = std::random_device{}(); -// std::cerr << "Tested with seed " << seed << "\n"; -// auto path = make_path_absolute(test_instance); -// uint32_t gold_hash = 0; -// for (int i = 0; i < 10; ++i) { -// auto hash = test_scaling_determinism(path, seed); -// if (i == 0) { -// gold_hash = hash; -// std::cout << "Gold hash: " << gold_hash << std::endl; -// } else { -// EXPECT_EQ(hash, gold_hash); -// } -// } -// } -// } +TEST(presolve, mip_scaling_deterministic) +{ + spin_stream_raii_t spin_stream_1; + spin_stream_raii_t spin_stream_2; + + std::vector test_instances = {"mip/sct2.mps", + "mip/thor50dday.mps", + "mip/uccase9.mps", + "mip/neos5-free-bound.mps", + "mip/neos5.mps", + "mip/50v-10.mps", + "mip/gen-ip054.mps", + "mip/rmatr200-p5.mps"}; + for (const auto& test_instance : test_instances) { + std::cout << "Running: " << test_instance << std::endl; + unsigned long seed = std::random_device{}(); + std::cerr << "Tested with seed " << seed << "\n"; + auto path = make_path_absolute(test_instance); + uint32_t gold_hash = 0; + for (int i = 0; i < 10; ++i) { + auto hash = test_scaling_determinism(path, seed); + if (i == 0) { + gold_hash = hash; + std::cout << "Gold hash: " << gold_hash << std::endl; + } else { + EXPECT_EQ(hash, gold_hash); + } + } + } +} } // namespace cuopt::linear_programming::test From ec248cd0202da018ac365a9a282072d3ae40576f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 6 Mar 2026 09:49:16 -0800 Subject: [PATCH 135/225] progress on GPU heuristic work accounting --- .../diversity/diversity_manager.cu | 12 +- .../diversity/recombiners/fp_recombiner.cuh | 4 +- .../local_search/local_search.cu | 6 +- .../local_search/rounding/bounds_repair.cu | 131 +++++++++++++++++- .../local_search/rounding/constraint_prop.cu | 63 ++++++++- cpp/src/mip_heuristics/problem/problem.cu | 8 +- cpp/tests/mip/diversity_test.cu | 23 +-- 7 files changed, 225 insertions(+), 22 deletions(-) diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 5b0d14ff31..ca3ea37855 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -153,7 +153,11 @@ void diversity_manager_t::add_user_given_solutions( if (problem_ptr->pre_process_assignment(init_sol_assignment)) { relaxed_lp_settings_t lp_settings; lp_settings.time_limit = std::min(60., timer.remaining_time() / 2); - if (timer.deterministic) { lp_settings.work_limit = lp_settings.time_limit; } + if (timer.deterministic) { + lp_settings.work_limit = lp_settings.time_limit; + lp_settings.work_context = timer.work_context; + cuopt_assert(lp_settings.work_context != nullptr, "Missing deterministic work context"); + } lp_settings.tolerance = problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; @@ -820,7 +824,11 @@ diversity_manager_t::recombine_and_local_search(solution_t& lp_run_time = std::min(lp_run_time, timer.remaining_time()); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = lp_run_time; - if (timer.deterministic) { lp_settings.work_limit = lp_settings.time_limit; } + if (timer.deterministic) { + lp_settings.work_limit = lp_settings.time_limit; + lp_settings.work_context = timer.work_context; + cuopt_assert(lp_settings.work_context != nullptr, "Missing deterministic work context"); + } lp_settings.tolerance = context.settings.tolerances.absolute_tolerance; lp_settings.return_first_feasible = false; lp_settings.save_state = true; diff --git a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh index 1909a33cff..1fe14e0149 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh @@ -92,7 +92,9 @@ class fp_recombiner_t : public recombiner_t { this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS) { lp_settings.time_limit = std::numeric_limits::max(); // TODO should be global time limit - lp_settings.work_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; + lp_settings.work_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; + lp_settings.work_context = &this->context.gpu_heur_loop; + cuopt_assert(lp_settings.work_context != nullptr, "Missing deterministic work context"); } lp_settings.tolerance = fixed_problem.tolerances.absolute_tolerance; lp_settings.return_first_feasible = true; diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index b4a6a400ea..f83139d9f2 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -491,7 +491,11 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu const f_t lp_run_time = 2.; relaxed_lp_settings_t lp_settings; lp_settings.time_limit = std::min(lp_run_time, timer.remaining_time()); - if (timer.deterministic) { lp_settings.work_limit = lp_settings.time_limit; } + if (timer.deterministic) { + lp_settings.work_limit = lp_settings.time_limit; + lp_settings.work_context = timer.work_context; + cuopt_assert(lp_settings.work_context != nullptr, "Missing deterministic work context"); + } lp_settings.tolerance = solution.problem_ptr->tolerances.absolute_tolerance; run_lp_with_vars_fixed( *solution.problem_ptr, solution, solution.problem_ptr->integer_indices, lp_settings); diff --git a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu index 813d6e6e66..d5cebd9dfa 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu @@ -16,8 +16,95 @@ #include #include +#include + namespace cuopt::linear_programming::detail { +namespace { + +constexpr double bounds_repair_setup_base_work = 5e-4; +constexpr double bounds_repair_violation_base_work = 4e-4; +constexpr double bounds_repair_violation_nnz_work = 2e-6; +constexpr double bounds_repair_violation_constraint_work = 3e-6; +constexpr double bounds_repair_best_bounds_variable_work = 2e-6; +constexpr double bounds_repair_shift_base_work = 3e-4; +constexpr double bounds_repair_shift_row_entry_work = 3e-6; +constexpr double bounds_repair_shift_candidate_work = 8e-6; +constexpr double bounds_repair_shift_neighbor_entry_work = 3e-6; +constexpr double bounds_repair_shift_sort_work = 5e-6; +constexpr double bounds_repair_damage_base_work = 3e-4; +constexpr double bounds_repair_damage_neighbor_entry_work = 8e-6; +constexpr double bounds_repair_damage_sort_work = 5e-6; +constexpr double bounds_repair_move_base_work = 5e-5; +constexpr double bounds_repair_no_candidate_base_work = 4e-4; +constexpr double bounds_repair_cycle_penalty_work = 3e-4; + +template +double estimate_bounds_repair_violation_refresh_work(const problem_t& problem, + bool update_best_bounds) +{ + double estimate = bounds_repair_violation_base_work + + bounds_repair_violation_nnz_work * (double)problem.nnz + + bounds_repair_violation_constraint_work * (double)problem.n_constraints; + if (update_best_bounds) { + estimate += bounds_repair_best_bounds_variable_work * (double)problem.n_variables; + } + return estimate; +} + +template +double estimate_bounds_repair_setup_work(const problem_t& problem) +{ + return bounds_repair_setup_base_work + + estimate_bounds_repair_violation_refresh_work(problem, true); +} + +template +double estimate_bounds_repair_shift_work(const problem_t& problem, + i_t curr_cstr, + i_t n_candidates, + bool is_cycle) +{ + const auto stream = problem.handle_ptr->get_stream(); + const i_t cstr_begin = problem.offsets.element(curr_cstr, stream); + const i_t cstr_end = problem.offsets.element(curr_cstr + 1, stream); + const double row_nnz = cstr_end - cstr_begin; + const double avg_rev_degree = + problem.n_variables > 0 ? ((double)problem.nnz / (double)problem.n_variables) : 0.0; + const double sort_work = + n_candidates > 1 ? (double)n_candidates * std::log2((double)n_candidates) : 0.0; + double estimate = bounds_repair_shift_base_work + bounds_repair_shift_row_entry_work * row_nnz; + if (n_candidates == 0) { estimate = bounds_repair_no_candidate_base_work + estimate; } + estimate += bounds_repair_shift_candidate_work * (double)n_candidates; + estimate += bounds_repair_shift_neighbor_entry_work * (double)n_candidates * avg_rev_degree; + estimate += bounds_repair_shift_sort_work * sort_work; + if (is_cycle) { estimate += bounds_repair_cycle_penalty_work; } + return estimate; +} + +template +double estimate_bounds_repair_damage_work(const problem_t& problem, i_t n_candidates) +{ + if (n_candidates == 0) { return 0.0; } + const double avg_rev_degree = + problem.n_variables > 0 ? ((double)problem.nnz / (double)problem.n_variables) : 0.0; + const double sort_work = + n_candidates > 1 ? (double)n_candidates * std::log2((double)n_candidates) : 0.0; + return bounds_repair_damage_base_work + + bounds_repair_damage_neighbor_entry_work * (double)n_candidates * avg_rev_degree + + bounds_repair_damage_sort_work * sort_work; +} + +template +void record_estimated_work(timer_t& timer, double* total_estimated_work, double work) +{ + cuopt_assert(std::isfinite(work) && work >= 0.0, "Bounds repair work estimate must be finite"); + timer.record_work(work); + *total_estimated_work += work; +} + +} // namespace + template bounds_repair_t::bounds_repair_t(const problem_t& pb, bound_presolve_t& bound_presolve_) @@ -400,16 +487,25 @@ bool bounds_repair_t::repair_problem(problem_t& problem, CUOPT_LOG_DEBUG("Running bounds repair"); handle_ptr = handle_ptr_; timer = timer_; + cuopt_assert(timer.deterministic == problem.deterministic, + "Bounds repair timer/problem determinism mismatch"); resize(problem); reset(); best_violation = get_ii_violation(problem); curr_violation = best_violation; best_bounds.update_from(problem, handle_ptr); + double total_estimated_work = 0.0; + i_t repair_iterations = 0; + if (timer.deterministic) { + const double setup_work = estimate_bounds_repair_setup_work(problem); + record_estimated_work(timer, &total_estimated_work, setup_work); + } i_t no_candidate_in_a_row = 0; // TODO: do this better i_t iter_limit = std::numeric_limits::max(); - if (problem.deterministic) { iter_limit = 20; } + if (timer.deterministic) { iter_limit = 20; } while (h_n_violated_cstr > 0 && iter_limit-- > 0) { + repair_iterations++; CUOPT_LOG_TRACE("Bounds repair loop: n_violated %d best_violation %f curr_violation %f", h_n_violated_cstr, best_violation, @@ -421,6 +517,11 @@ bool bounds_repair_t::repair_problem(problem_t& problem, if (is_cycle) { CUOPT_LOG_DEBUG("Repair: cycle detected at cstr %d", curr_cstr); } // in parallel compute the best shift and best respective damage i_t n_candidates = compute_best_shift(problem, original_problem, curr_cstr); + if (timer.deterministic) { + const double shift_work = + estimate_bounds_repair_shift_work(problem, curr_cstr, n_candidates, is_cycle); + record_estimated_work(timer, &total_estimated_work, shift_work); + } // if no candidate is there continue with another constraint if (n_candidates == 0) { CUOPT_LOG_DEBUG("Repair: no candidate var found for cstr %d", curr_cstr); @@ -435,6 +536,10 @@ bool bounds_repair_t::repair_problem(problem_t& problem, CUOPT_LOG_TRACE("Repair: number of candidates %d", n_candidates); // among the ones that have a valid shift value, compute the damage compute_damages(problem, n_candidates); + if (timer.deterministic) { + const double damage_work = estimate_bounds_repair_damage_work(problem, n_candidates); + record_estimated_work(timer, &total_estimated_work, damage_work); + } // get the best damage i_t best_cstr_delta = candidates.cstr_delta.front_element(handle_ptr->get_stream()); f_t best_damage = candidates.damage.front_element(handle_ptr->get_stream()); @@ -463,9 +568,22 @@ bool bounds_repair_t::repair_problem(problem_t& problem, apply_move(problem, original_problem, best_move_idx); reset(); // TODO we might optimize this to only calculate the changed constraints - curr_violation = get_ii_violation(problem); + curr_violation = get_ii_violation(problem); + const bool improved_violation = curr_violation < best_violation; + if (timer.deterministic) { + const double refresh_work = + bounds_repair_move_base_work + + estimate_bounds_repair_violation_refresh_work(problem, improved_violation); + record_estimated_work(timer, &total_estimated_work, refresh_work); + CUOPT_LOG_DEBUG("Repair iter work: cstr=%d candidates=%d cycle=%d improved=%d total=%.6f", + curr_cstr, + n_candidates, + (int)is_cycle, + (int)improved_violation, + total_estimated_work); + } - if (curr_violation < best_violation) { + if (improved_violation) { best_violation = curr_violation; // update best bounds best_bounds.update_from(problem, handle_ptr); @@ -475,6 +593,13 @@ bool bounds_repair_t::repair_problem(problem_t& problem, bool feasible = h_n_violated_cstr == 0; // copy best bounds into problem best_bounds.update_to(problem, handle_ptr); + if (timer.deterministic) { + CUOPT_LOG_DEBUG("Repair work estimate: loops=%d total=%.6f best_violation=%.6f feasible=%d", + repair_iterations, + total_estimated_work, + best_violation, + (int)feasible); + } CUOPT_LOG_DEBUG("Repair: returning with feas: %d vio %f", feasible, best_violation); return feasible; } diff --git a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu index b04707106a..8208d1c613 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu @@ -19,6 +19,8 @@ #include #include +#include + namespace cuopt::linear_programming::detail { template @@ -937,9 +939,12 @@ bool constraint_prop_t::find_integer( bool timeout_happened = false; i_t n_failed_repair_iterations = 0; while (set_count < unset_integer_vars.size()) { + auto iter_start_time = std::chrono::high_resolution_clock::now(); CUOPT_LOG_TRACE("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); CUOPT_LOG_DEBUG("unset_integer_vars size %lu", unset_integer_vars.size()); + const size_t set_count_before = set_count; update_host_assignment(sol); + auto after_update_host_assignment = std::chrono::high_resolution_clock::now(); if (max_timer.check_time_limit()) { CUOPT_LOG_DEBUG("Second time limit is reached returning nearest rounding!"); collapse_crossing_bounds(*sol.problem_ptr, *orig_sol.problem_ptr, sol.handle_ptr); @@ -963,12 +968,14 @@ bool constraint_prop_t::find_integer( bounds_prop_interval = 1; } } - i_t n_vars_to_set = recovery_mode ? 1 : bounds_prop_interval; + i_t n_vars_to_set = recovery_mode ? 1 : bounds_prop_interval; + const bool did_sort = n_vars_to_set != 1; // if we are not at the last stage or if we are in recovery mode, don't sort if (n_vars_to_set != 1) { sort_by_implied_slack_consumption( sol, make_span(unset_integer_vars, set_count, unset_integer_vars.size()), problem_ii); } + auto after_sort = std::chrono::high_resolution_clock::now(); std::vector host_vars_to_set(n_vars_to_set); raft::copy(host_vars_to_set.data(), unset_integer_vars.data() + set_count, @@ -979,6 +986,7 @@ bool constraint_prop_t::find_integer( auto var_probe_vals = generate_bulk_rounding_vector(sol, orig_sol, host_vars_to_set, probing_config); + auto after_generate_probe_vals = std::chrono::high_resolution_clock::now(); CUOPT_LOG_TRACE("var_probe_vals hash 1 0x%x, hash 2 0x%x, hash 3 0x%x", detail::compute_hash(std::get<0>(var_probe_vals)), @@ -986,18 +994,23 @@ bool constraint_prop_t::find_integer( detail::compute_hash(std::get<2>(var_probe_vals))); probe( sol, orig_sol.problem_ptr, var_probe_vals, &set_count, unset_integer_vars, probing_config); + auto after_probe = std::chrono::high_resolution_clock::now(); CUOPT_LOG_TRACE( "post probe, set count %d, unset var hash 0x%x, size %lu", (int)set_count, detail::compute_hash(make_span(unset_integer_vars), sol.handle_ptr->get_stream()), unset_integer_vars.size()); + bool repair_attempted = false; + bool bounds_repaired = false; + i_t n_fixed_vars = 0; if (!(n_failed_repair_iterations >= max_n_failed_repair_iterations) && rounding_ii && !timeout_happened) { // timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; work_limit_timer_t repair_timer(context.gpu_heur_loop, timer.remaining_time() / 5); save_bounds(sol); // update bounds and run repair procedure - bool bounds_repaired = + repair_attempted = true; + bounds_repaired = run_repair_procedure(*sol.problem_ptr, *orig_sol.problem_ptr, repair_timer, sol.handle_ptr); if (!bounds_repaired) { restore_bounds(sol); @@ -1021,11 +1034,48 @@ bool constraint_prop_t::find_integer( make_span(sol.problem_ptr->variable_bounds), make_span(orig_sol.problem_ptr->variable_bounds), make_span(sol.assignment)}); - i_t n_fixed_vars = (iter - (unset_vars.begin() + set_count)); + n_fixed_vars = (iter - (unset_vars.begin() + set_count)); CUOPT_LOG_TRACE("After repair procedure, number of additional fixed vars %d", n_fixed_vars); set_count += n_fixed_vars; } } + auto after_repair = std::chrono::high_resolution_clock::now(); + const double update_host_assignment_ms = + std::chrono::duration(after_update_host_assignment - iter_start_time) + .count(); + const double sort_ms = + std::chrono::duration(after_sort - after_update_host_assignment).count(); + const double generate_probe_vals_ms = + std::chrono::duration(after_generate_probe_vals - after_sort).count(); + const double probe_ms = + std::chrono::duration(after_probe - after_generate_probe_vals).count(); + const double repair_ms = + std::chrono::duration(after_repair - after_probe).count(); + const double iter_total_ms = + std::chrono::duration(after_repair - iter_start_time).count(); + CUOPT_LOG_DEBUG( + "CP iter: set_count=%lu->%lu unset=%d interval=%d n_vars_to_set=%d sort=%d recovery=%d " + "rounding_ii=%d probe_ii=%d timeout=%d repair_attempted=%d repair_success=%d " + "repair_fixed=%d t_ms(update=%.3f sort=%.3f gen=%.3f probe=%.3f repair=%.3f total=%.3f)", + set_count_before, + set_count, + n_curr_unset, + bounds_prop_interval, + n_vars_to_set, + (int)did_sort, + (int)recovery_mode, + (int)rounding_ii, + (int)problem_ii, + (int)timeout_happened, + (int)repair_attempted, + (int)bounds_repaired, + n_fixed_vars, + update_host_assignment_ms, + sort_ms, + generate_probe_vals_ms, + probe_ms, + repair_ms, + iter_total_ms); // we can keep normal bounds_update here because this is only activated after the repair if (recovery_mode && multi_probe.infeas_constraints_count_0 > 0 && multi_probe.infeas_constraints_count_1 > 0) { @@ -1061,7 +1111,12 @@ bool constraint_prop_t::find_integer( multi_probe.infeas_constraints_count_1 == 0) && !timeout_happened && lp_run_time_after_feasible > 0) { relaxed_lp_settings_t lp_settings; - lp_settings.time_limit = lp_run_time_after_feasible; + lp_settings.time_limit = lp_run_time_after_feasible; + if (timer.deterministic) { + lp_settings.work_limit = lp_settings.time_limit; + lp_settings.work_context = timer.work_context; + cuopt_assert(lp_settings.work_context != nullptr, "Missing deterministic work context"); + } lp_settings.tolerance = orig_sol.problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; diff --git a/cpp/src/mip_heuristics/problem/problem.cu b/cpp/src/mip_heuristics/problem/problem.cu index bc93a9d988..0ac42b333a 100644 --- a/cpp/src/mip_heuristics/problem/problem.cu +++ b/cpp/src/mip_heuristics/problem/problem.cu @@ -940,8 +940,12 @@ void problem_t::compute_related_variables(double time_limit) handle_ptr->sync_stream(); - // CHANGE - if (deterministic) { time_limit = std::numeric_limits::infinity(); } + if (deterministic) { + // TODO: Re-enable deterministic related-variable construction once we have a work estimator. + related_variables.resize(0, handle_ptr->get_stream()); + related_variables_offsets.resize(0, handle_ptr->get_stream()); + return; + } // previously used constants were based on 40GB of memory. Scale accordingly on smaller GPUs // We can't rely on querying free memory or allocation try/catch diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index 3bd9e0a39f..4fc4a84f97 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -83,6 +83,7 @@ static uint32_t test_full_run_determinism(std::string path, init_handler(op_problem.get_handle_ptr()); // run the problem constructor of MIP, so that we do bounds standardization detail::problem_t problem(op_problem); + problem.deterministic = true; problem.preprocess_problem(); setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); @@ -141,6 +142,7 @@ static uint32_t test_initial_solution_determinism(std::string path, init_handler(op_problem.get_handle_ptr()); // run the problem constructor of MIP, so that we do bounds standardization detail::problem_t problem(op_problem); + problem.deterministic = true; problem.preprocess_problem(); setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); @@ -200,6 +202,7 @@ static uint32_t test_recombiners_determinism(std::string path, init_handler(op_problem.get_handle_ptr()); // run the problem constructor of MIP, so that we do bounds standardization detail::problem_t problem(op_problem); + problem.deterministic = true; problem.preprocess_problem(); setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); @@ -408,14 +411,16 @@ TEST_P(DiversityTestParams, full_run_deterministic) INSTANTIATE_TEST_SUITE_P(DiversityTest, DiversityTestParams, - testing::Values(std::make_tuple("mip/gen-ip054.mps", 5.0f), - std::make_tuple("mip/pk1.mps", 5.0f), - // std::make_tuple("mip/uccase9.mps"), - // std::make_tuple("mip/sct2.mps") - // std::make_tuple("mip/thor50dday.mps") - // std::make_tuple("uccase9.mps"), - std::make_tuple("mip/neos5.mps", 5.0f), - // std::make_tuple("mip/50v-10.mps"), - std::make_tuple("mip/rmatr200-p5.mps", 5.0f))); + testing::Values( + // std::make_tuple("mip/gen-ip054.mps", 5.0f), + // std::make_tuple("mip/pk1.mps", 5.0f), + std::make_tuple("mip/uccase9.mps", 5.0f), + std::make_tuple("mip/sct2.mps", 5.0f), + std::make_tuple("mip/thor50dday.mps", 5.0f), + // std::make_tuple("uccase9.mps"), + // std::make_tuple("mip/neos5.mps", 5.0f), + std::make_tuple("mip/50v-10.mps", 5.0f) + // std::make_tuple("mip/rmatr200-p5.mps", 5.0f) + )); } // namespace cuopt::linear_programming::test From dc1cb9f6bcc8516d014f67101f8262a8aaac4a30 Mon Sep 17 00:00:00 2001 From: Nicolas Blin <31096601+Kh4ster@users.noreply.github.com> Date: Sat, 7 Mar 2026 09:16:37 +0100 Subject: [PATCH 136/225] Add support for FP32 and mixed precision in PDLP (#910) This PR adds support for FP32 and mixed precision in PDLP (not MIP, Dual Simplex or Barrier). Those two new options are available through: - FP32: creating the whole solver object as FP32 or passing "--pdlp-fp32" to the solve_LP binary - Mixed precision: toggling the option "mixed_precision_spmv" in solver_settings or passing "--mixed-precision-spmv" to the solve_LP binary Below, the detail of what each feature allows: FP32 Precision Mode ------------------- By default, PDLP operates in FP64 (double) precision. Users can switch to FP32 (float) precision for the entire solve. FP32 uses half the memory of FP64 and allows PDHG iterations to be on average twice as fast, but it may require more iterations to converge due to reduced numerical accuracy. FP32 mode is only supported with the PDLP method (not concurrent) and without crossover. Note: The default precision is FP64 (double). Mixed Precision SpMV -------------------- When running PDLP in FP64 mode, users can enable mixed precision sparse matrix-vector products (SpMV) during PDHG iterations. In this mode, the constraint matrix and its transpose are stored in FP32 while vectors and the compute type remain in FP64. This allows SpMV operations to be faster thanks to reduced memory bandwidth requirements, while maintaining FP64 accuracy in the accumulation. This will make PDHG iterations faster while limiting the potential negative impact on convergence (compared to running in FP32 mode). Convergence checking and restart logic always use the full FP64 matrix, so this mode does not reduce memory usage since both the FP32 and FP64 copies of the matrix are kept in memory. Mixed precision SpMV only applies in FP64 mode and has no effect when running in FP32. Note: The default value is false. Authors: - Nicolas Blin (https://github.com/Kh4ster) Approvers: - Alice Boucher (https://github.com/aliceb-nv) - Trevor McKay (https://github.com/tmckayus) - Rajesh Gandham (https://github.com/rg20) URL: https://github.com/NVIDIA/cuopt/pull/910 --- .../linear_programming/cuopt/run_pdlp.cu | 73 ++-- cpp/cuopt_cli.cpp | 26 +- .../cuopt/linear_programming/constants.h | 7 + .../optimization_problem.hpp | 8 + .../pdlp/solver_settings.hpp | 18 +- cpp/src/dual_simplex/sparse_matrix.cpp | 7 + cpp/src/math_optimization/solution_writer.cu | 27 +- cpp/src/math_optimization/solution_writer.hpp | 7 +- cpp/src/math_optimization/solver_settings.cu | 41 +-- cpp/src/mip_heuristics/diversity/lns/rins.cu | 16 +- .../local_search/rounding/simple_rounding.cu | 2 +- cpp/src/mip_heuristics/mip_constants.hpp | 4 +- .../mip_heuristics/presolve/gf2_presolve.cpp | 2 +- .../presolve/third_party_presolve.cpp | 91 +++-- .../mip_heuristics/problem/presolve_data.cu | 2 +- cpp/src/mip_heuristics/problem/problem.cu | 2 +- cpp/src/mip_heuristics/solution/solution.cu | 2 +- cpp/src/mip_heuristics/solver_solution.cu | 6 +- cpp/src/pdlp/cpu_pdlp_warm_start_data.cu | 6 +- cpp/src/pdlp/cusparse_view.cu | 216 +++++++++++- cpp/src/pdlp/cusparse_view.hpp | 53 ++- .../initial_scaling.cu | 2 +- .../optimal_batch_size_handler.cu | 2 +- cpp/src/pdlp/optimization_problem.cu | 93 ++++- cpp/src/pdlp/pdhg.cu | 122 +++++-- cpp/src/pdlp/pdhg.hpp | 3 +- cpp/src/pdlp/pdlp.cu | 217 +++++++----- cpp/src/pdlp/pdlp_warm_start_data.cu | 2 +- .../localized_duality_gap_container.cu | 2 +- .../restart_strategy/pdlp_restart_strategy.cu | 2 +- .../weighted_average_solution.cu | 2 +- cpp/src/pdlp/saddle_point.cu | 2 +- cpp/src/pdlp/solve.cu | 324 +++++++++++++----- cpp/src/pdlp/solver_settings.cu | 2 +- cpp/src/pdlp/solver_solution.cu | 2 +- .../adaptive_step_size_strategy.cu | 2 +- .../convergence_information.cu | 2 +- .../infeasibility_information.cu | 2 +- .../termination_strategy.cu | 2 +- cpp/src/pdlp/translate.hpp | 2 +- cpp/src/pdlp/utilities/problem_checking.cu | 2 +- .../c_api_tests/c_api_test.c | 132 +++++++ .../c_api_tests/c_api_tests.cpp | 41 +++ .../c_api_tests/c_api_tests.h | 8 + cpp/tests/linear_programming/pdlp_test.cu | 161 ++++++++- .../cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst | 13 + docs/cuopt/source/lp-qp-features.rst | 11 + docs/cuopt/source/lp-qp-milp-settings.rst | 21 ++ .../linear_programming/test_lp_solver.py | 45 ++- 49 files changed, 1494 insertions(+), 343 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_pdlp.cu b/benchmarks/linear_programming/cuopt/run_pdlp.cu index 18a473d64e..a7838d773e 100644 --- a/benchmarks/linear_programming/cuopt/run_pdlp.cu +++ b/benchmarks/linear_programming/cuopt/run_pdlp.cu @@ -76,6 +76,13 @@ static void parse_arguments(argparse::ArgumentParser& program) .choices("None", "Papilo", "PSLP", "Default"); program.add_argument("--solution-path").help("Path where solution file will be generated"); + + program.add_argument("--pdlp-precision") + .help( + "PDLP precision mode. default: native type, single: FP32 internally, " + "double: FP64 explicitly, mixed: mixed-precision SpMV (FP32 matrix, FP64 vectors).") + .default_value(std::string("default")) + .choices("default", "single", "double", "mixed"); } static cuopt::linear_programming::presolver_t string_to_presolver(const std::string& presolver) @@ -87,6 +94,15 @@ static cuopt::linear_programming::presolver_t string_to_presolver(const std::str return cuopt::linear_programming::presolver_t::Default; } +static cuopt::linear_programming::pdlp_precision_t string_to_pdlp_precision( + const std::string& precision) +{ + if (precision == "single") return cuopt::linear_programming::pdlp_precision_t::SinglePrecision; + if (precision == "double") return cuopt::linear_programming::pdlp_precision_t::DoublePrecision; + if (precision == "mixed") return cuopt::linear_programming::pdlp_precision_t::MixedPrecision; + return cuopt::linear_programming::pdlp_precision_t::DefaultPrecision; +} + static cuopt::linear_programming::pdlp_solver_mode_t string_to_pdlp_solver_mode( const std::string& mode) { @@ -105,8 +121,7 @@ static cuopt::linear_programming::pdlp_solver_mode_t string_to_pdlp_solver_mode( static cuopt::linear_programming::pdlp_solver_settings_t create_solver_settings( const argparse::ArgumentParser& program) { - cuopt::linear_programming::pdlp_solver_settings_t settings = - cuopt::linear_programming::pdlp_solver_settings_t{}; + cuopt::linear_programming::pdlp_solver_settings_t settings{}; settings.time_limit = program.get("--time-limit"); settings.iteration_limit = program.get("--iteration-limit"); @@ -114,29 +129,16 @@ static cuopt::linear_programming::pdlp_solver_settings_t create_sol settings.pdlp_solver_mode = string_to_pdlp_solver_mode(program.get("--pdlp-solver-mode")); settings.method = static_cast(program.get("--method")); - settings.crossover = program.get("--crossover"); - settings.presolver = string_to_presolver(program.get("--presolver")); + settings.crossover = program.get("--crossover"); + settings.presolver = string_to_presolver(program.get("--presolver")); + settings.pdlp_precision = string_to_pdlp_precision(program.get("--pdlp-precision")); return settings; } -int main(int argc, char* argv[]) +static int run_solver(const argparse::ArgumentParser& program, const raft::handle_t& handle_) { - // Parse binary arguments - argparse::ArgumentParser program("solve_LP"); - parse_arguments(program); - - try { - program.parse_args(argc, argv); - } catch (const std::runtime_error& err) { - std::cerr << err.what() << std::endl; - std::cerr << program; - return 1; - } - - // Initialize solver settings from binary arguments - cuopt::linear_programming::pdlp_solver_settings_t settings = - create_solver_settings(program); + auto settings = create_solver_settings(program); bool use_pdlp_solver_mode = true; if (program.is_used("--pdlp-hyper-params-path")) { @@ -145,13 +147,6 @@ int main(int argc, char* argv[]) use_pdlp_solver_mode = false; } - // Setup up RMM memory pool - auto memory_resource = make_pool(); - rmm::mr::set_current_device_resource(memory_resource.get()); - - // Initialize raft handle and running stream - const raft::handle_t handle_{}; - // Parse MPS file cuopt::mps_parser::mps_data_model_t op_problem = cuopt::mps_parser::parse_mps(program.get("--path")); @@ -168,3 +163,27 @@ int main(int argc, char* argv[]) return 0; } + +int main(int argc, char* argv[]) +{ + // Parse binary arguments + argparse::ArgumentParser program("solve_LP"); + parse_arguments(program); + + try { + program.parse_args(argc, argv); + } catch (const std::runtime_error& err) { + std::cerr << err.what() << std::endl; + std::cerr << program; + return 1; + } + + // Setup up RMM memory pool + auto memory_resource = make_pool(); + rmm::mr::set_current_device_resource(memory_resource.get()); + + // Initialize raft handle and running stream + const raft::handle_t handle_{}; + + return run_solver(program, handle_); +} diff --git a/cpp/cuopt_cli.cpp b/cpp/cuopt_cli.cpp index e9b1ee3719..899a3118b3 100644 --- a/cpp/cuopt_cli.cpp +++ b/cpp/cuopt_cli.cpp @@ -77,8 +77,8 @@ inline auto make_async() { return std::make_shared& settings) { - return cuopt::init_logger_t(settings.get_parameter(CUOPT_LOG_FILE), - settings.get_parameter(CUOPT_LOG_TO_CONSOLE)); + return cuopt::init_logger_t(settings.template get_parameter(CUOPT_LOG_FILE), + settings.template get_parameter(CUOPT_LOG_TO_CONSOLE)); } /** @@ -287,6 +287,17 @@ int main(int argc, char* argv[]) .implicit_value(true); std::map arg_name_to_param_name; + + // Register --pdlp-precision with string-to-int mapping so that it flows + // through the settings_strings map like other settings. + program.add_argument("--pdlp-precision") + .help( + "PDLP precision mode. default: native type, single: FP32 internally, " + "double: FP64 explicitly, mixed: mixed-precision SpMV (FP32 matrix, FP64 vectors).") + .default_value(std::string("-1")) + .choices("default", "single", "double", "mixed", "-1", "0", "1", "2"); + arg_name_to_param_name["--pdlp-precision"] = CUOPT_PDLP_PRECISION; + { // Add all solver settings as arguments cuopt::linear_programming::solver_settings_t dummy_settings; @@ -341,11 +352,20 @@ int main(int argc, char* argv[]) return 1; } + // Map symbolic pdlp-precision names to integer values + static const std::map precision_name_to_value = { + {"default", "-1"}, {"single", "0"}, {"double", "1"}, {"mixed", "2"}}; + // Read everything as a string std::map settings_strings; for (auto& [arg_name, param_name] : arg_name_to_param_name) { if (program.is_used(arg_name.c_str())) { - settings_strings[param_name] = program.get(arg_name.c_str()); + auto val = program.get(arg_name.c_str()); + if (param_name == CUOPT_PDLP_PRECISION) { + auto it = precision_name_to_value.find(val); + if (it != precision_name_to_value.end()) { val = it->second; } + } + settings_strings[param_name] = val; } } // Get the values diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index 7eb0aa07d6..d9dfbce16d 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -74,6 +74,7 @@ #define CUOPT_NUM_GPUS "num_gpus" #define CUOPT_USER_PROBLEM_FILE "user_problem_file" #define CUOPT_RANDOM_SEED "random_seed" +#define CUOPT_PDLP_PRECISION "pdlp_precision" /* @brief MIP determinism mode constants */ #define CUOPT_MODE_OPPORTUNISTIC 0 @@ -125,6 +126,12 @@ #define CUOPT_METHOD_DUAL_SIMPLEX 2 #define CUOPT_METHOD_BARRIER 3 +/* @brief PDLP precision mode constants */ +#define CUOPT_PDLP_DEFAULT_PRECISION -1 +#define CUOPT_PDLP_SINGLE_PRECISION 0 +#define CUOPT_PDLP_DOUBLE_PRECISION 1 +#define CUOPT_PDLP_MIXED_PRECISION 2 + /* @brief File format constants for problem I/O */ #define CUOPT_FILE_FORMAT_MPS 0 diff --git a/cpp/include/cuopt/linear_programming/optimization_problem.hpp b/cpp/include/cuopt/linear_programming/optimization_problem.hpp index d0f624ebdf..df78dd17c7 100644 --- a/cpp/include/cuopt/linear_programming/optimization_problem.hpp +++ b/cpp/include/cuopt/linear_programming/optimization_problem.hpp @@ -312,6 +312,14 @@ class optimization_problem_t : public optimization_problem_interface_t // Conversion // ============================================================================ + /** + * @brief Convert this problem to a different floating-point precision. + * + * @tparam other_f_t Target floating-point type (e.g. float when this is double) + */ + template + optimization_problem_t convert_to_other_prec(rmm::cuda_stream_view stream) const; + /** * @brief Returns nullptr since this is already a GPU problem. * @return nullptr diff --git a/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp b/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp index f6ad4c8619..d3f59144cc 100644 --- a/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp @@ -63,6 +63,21 @@ enum method_t : int { Barrier = CUOPT_METHOD_BARRIER }; +/** + * @brief Enum representing the PDLP precision modes. + * + * DefaultPrecision: Use the type of the problem (FP64 for double problems). + * SinglePrecision: Run PDLP internally in FP32, converting inputs and outputs. + * DoublePrecision: Explicitly run in FP64 (same as default for double problems). + * MixedPrecision: Use mixed precision SpMV (FP32 matrix with FP64 vectors/compute). + */ +enum pdlp_precision_t : int { + DefaultPrecision = CUOPT_PDLP_DEFAULT_PRECISION, + SinglePrecision = CUOPT_PDLP_SINGLE_PRECISION, + DoublePrecision = CUOPT_PDLP_DOUBLE_PRECISION, + MixedPrecision = CUOPT_PDLP_MIXED_PRECISION +}; + template class pdlp_solver_settings_t { public: @@ -224,7 +239,7 @@ class pdlp_solver_settings_t { bool detect_infeasibility{false}; bool strict_infeasibility{false}; i_t iteration_limit{std::numeric_limits::max()}; - double time_limit{std::numeric_limits::infinity()}; + f_t time_limit{std::numeric_limits::infinity()}; pdlp_solver_mode_t pdlp_solver_mode{pdlp_solver_mode_t::Stable3}; bool log_to_console{true}; std::string log_file{""}; @@ -239,6 +254,7 @@ class pdlp_solver_settings_t { i_t ordering{-1}; i_t barrier_dual_initial_point{-1}; bool eliminate_dense_columns{true}; + pdlp_precision_t pdlp_precision{pdlp_precision_t::DefaultPrecision}; bool save_best_primal_so_far{false}; bool first_primal_feasible{false}; presolver_t presolver{presolver_t::Default}; diff --git a/cpp/src/dual_simplex/sparse_matrix.cpp b/cpp/src/dual_simplex/sparse_matrix.cpp index 8ccccd57cf..63004be72b 100644 --- a/cpp/src/dual_simplex/sparse_matrix.cpp +++ b/cpp/src/dual_simplex/sparse_matrix.cpp @@ -10,6 +10,7 @@ #include #include +#include // #include // #include @@ -938,6 +939,12 @@ f_t sparse_dot(const std::vector& xind, return dot; } +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT +// Minimal float instantiation for LP usage +template class csc_matrix_t; +template class csr_matrix_t; +#endif + #ifdef DUAL_SIMPLEX_INSTANTIATE_DOUBLE template class csc_matrix_t; diff --git a/cpp/src/math_optimization/solution_writer.cu b/cpp/src/math_optimization/solution_writer.cu index 273b8e989c..880127546d 100644 --- a/cpp/src/math_optimization/solution_writer.cu +++ b/cpp/src/math_optimization/solution_writer.cu @@ -9,15 +9,18 @@ #include #include "solution_writer.hpp" +#include + #include namespace cuopt::linear_programming { +template void solution_writer_t::write_solution_to_sol_file(const std::string& filename, const std::string& status, - const double objective_value, + const f_t objective_value, const std::vector& variable_names, - const std::vector& variable_values) + const std::vector& variable_values) { raft::common::nvtx::range fun_scope("write final solution to .sol file"); std::ofstream file(filename.data()); @@ -27,7 +30,7 @@ void solution_writer_t::write_solution_to_sol_file(const std::string& filename, return; } - file.precision(std::numeric_limits::max_digits10 + 1); + file.precision(std::numeric_limits::max_digits10 + 1); file << "# Status: " << status << std::endl; @@ -39,4 +42,22 @@ void solution_writer_t::write_solution_to_sol_file(const std::string& filename, } } +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT +template void solution_writer_t::write_solution_to_sol_file( + const std::string& filename, + const std::string& status, + const float objective_value, + const std::vector& variable_names, + const std::vector& variable_values); +#endif + +#if MIP_INSTANTIATE_DOUBLE +template void solution_writer_t::write_solution_to_sol_file( + const std::string& filename, + const std::string& status, + const double objective_value, + const std::vector& variable_names, + const std::vector& variable_values); +#endif + } // namespace cuopt::linear_programming diff --git a/cpp/src/math_optimization/solution_writer.hpp b/cpp/src/math_optimization/solution_writer.hpp index 0890bf260b..0ac1b64464 100644 --- a/cpp/src/math_optimization/solution_writer.hpp +++ b/cpp/src/math_optimization/solution_writer.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -23,10 +23,11 @@ namespace cuopt::linear_programming { */ class solution_writer_t { public: + template static void write_solution_to_sol_file(const std::string& sol_file_path, const std::string& status, - const double objective_value, + const f_t objective_value, const std::vector& variable_names, - const std::vector& variable_values); + const std::vector& variable_values); }; } // namespace cuopt::linear_programming diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index f1350ca432..7435bb37fa 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -58,24 +58,24 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings // clang-format off // Float parameters float_parameters = { - {CUOPT_TIME_LIMIT, &mip_settings.time_limit, 0.0, std::numeric_limits::infinity(), std::numeric_limits::infinity()}, - {CUOPT_TIME_LIMIT, &pdlp_settings.time_limit, 0.0, std::numeric_limits::infinity(), std::numeric_limits::infinity()}, - {CUOPT_WORK_LIMIT, &mip_settings.work_limit, 0.0, std::numeric_limits::infinity(), std::numeric_limits::infinity()}, - {CUOPT_ABSOLUTE_DUAL_TOLERANCE, &pdlp_settings.tolerances.absolute_dual_tolerance, 0.0, 1e-1, 1e-4}, - {CUOPT_RELATIVE_DUAL_TOLERANCE, &pdlp_settings.tolerances.relative_dual_tolerance, 0.0, 1e-1, 1e-4}, - {CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, &pdlp_settings.tolerances.absolute_primal_tolerance, 0.0, 1e-1, 1e-4}, - {CUOPT_RELATIVE_PRIMAL_TOLERANCE, &pdlp_settings.tolerances.relative_primal_tolerance, 0.0, 1e-1, 1e-4}, - {CUOPT_ABSOLUTE_GAP_TOLERANCE, &pdlp_settings.tolerances.absolute_gap_tolerance, 0.0, 1e-1, 1e-4}, - {CUOPT_RELATIVE_GAP_TOLERANCE, &pdlp_settings.tolerances.relative_gap_tolerance, 0.0, 1e-1, 1e-4}, - {CUOPT_MIP_ABSOLUTE_TOLERANCE, &mip_settings.tolerances.absolute_tolerance, 0.0, 1e-1, 1e-4}, - {CUOPT_MIP_RELATIVE_TOLERANCE, &mip_settings.tolerances.relative_tolerance, 0.0, 1e-1, 1e-4}, - {CUOPT_MIP_INTEGRALITY_TOLERANCE, &mip_settings.tolerances.integrality_tolerance, 0.0, 1e-1, 1e-5}, - {CUOPT_MIP_ABSOLUTE_GAP, &mip_settings.tolerances.absolute_mip_gap, 0.0, CUOPT_INFINITY, 1e-10}, - {CUOPT_MIP_RELATIVE_GAP, &mip_settings.tolerances.relative_mip_gap, 0.0, 1e-1, 1e-4}, - {CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, &pdlp_settings.tolerances.primal_infeasible_tolerance, 0.0, 1e-1, 1e-10}, - {CUOPT_DUAL_INFEASIBLE_TOLERANCE, &pdlp_settings.tolerances.dual_infeasible_tolerance, 0.0, 1e-1, 1e-10}, - {CUOPT_MIP_CUT_CHANGE_THRESHOLD, &mip_settings.cut_change_threshold, 0.0, std::numeric_limits::infinity(), 1e-3}, - {CUOPT_MIP_CUT_MIN_ORTHOGONALITY, &mip_settings.cut_min_orthogonality, 0.0, 1.0, 0.5} + {CUOPT_TIME_LIMIT, &mip_settings.time_limit, f_t(0.0), std::numeric_limits::infinity(), std::numeric_limits::infinity()}, + {CUOPT_TIME_LIMIT, &pdlp_settings.time_limit, f_t(0.0), std::numeric_limits::infinity(), std::numeric_limits::infinity()}, + {CUOPT_WORK_LIMIT, &mip_settings.work_limit, f_t(0.0), std::numeric_limits::infinity(), std::numeric_limits::infinity()}, + {CUOPT_ABSOLUTE_DUAL_TOLERANCE, &pdlp_settings.tolerances.absolute_dual_tolerance, f_t(0.0), f_t(1e-1), f_t(1e-4)}, + {CUOPT_RELATIVE_DUAL_TOLERANCE, &pdlp_settings.tolerances.relative_dual_tolerance, f_t(0.0), f_t(1e-1), f_t(1e-4)}, + {CUOPT_ABSOLUTE_PRIMAL_TOLERANCE, &pdlp_settings.tolerances.absolute_primal_tolerance, f_t(0.0), f_t(1e-1), f_t(1e-4)}, + {CUOPT_RELATIVE_PRIMAL_TOLERANCE, &pdlp_settings.tolerances.relative_primal_tolerance, f_t(0.0), f_t(1e-1), f_t(1e-4)}, + {CUOPT_ABSOLUTE_GAP_TOLERANCE, &pdlp_settings.tolerances.absolute_gap_tolerance, f_t(0.0), f_t(1e-1), f_t(1e-4)}, + {CUOPT_RELATIVE_GAP_TOLERANCE, &pdlp_settings.tolerances.relative_gap_tolerance, f_t(0.0), f_t(1e-1), f_t(1e-4)}, + {CUOPT_MIP_ABSOLUTE_TOLERANCE, &mip_settings.tolerances.absolute_tolerance, f_t(0.0), f_t(1e-1), f_t(1e-4)}, + {CUOPT_MIP_RELATIVE_TOLERANCE, &mip_settings.tolerances.relative_tolerance, f_t(0.0), f_t(1e-1), f_t(1e-4)}, + {CUOPT_MIP_INTEGRALITY_TOLERANCE, &mip_settings.tolerances.integrality_tolerance, f_t(0.0), f_t(1e-1), f_t(1e-5)}, + {CUOPT_MIP_ABSOLUTE_GAP, &mip_settings.tolerances.absolute_mip_gap, f_t(0.0), std::numeric_limits::infinity(), std::max(f_t(1e-10), std::numeric_limits::epsilon())}, + {CUOPT_MIP_RELATIVE_GAP, &mip_settings.tolerances.relative_mip_gap, f_t(0.0), f_t(1e-1), f_t(1e-4)}, + {CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, &pdlp_settings.tolerances.primal_infeasible_tolerance, f_t(0.0), f_t(1e-1), std::max(f_t(1e-10), std::numeric_limits::epsilon())}, + {CUOPT_DUAL_INFEASIBLE_TOLERANCE, &pdlp_settings.tolerances.dual_infeasible_tolerance, f_t(0.0), f_t(1e-1), std::max(f_t(1e-10), std::numeric_limits::epsilon())}, + {CUOPT_MIP_CUT_CHANGE_THRESHOLD, &mip_settings.cut_change_threshold, f_t(0.0), std::numeric_limits::infinity(), f_t(1e-3)}, + {CUOPT_MIP_CUT_MIN_ORTHOGONALITY, &mip_settings.cut_min_orthogonality, f_t(0.0), f_t(1.0), f_t(0.5)} }; // Int parameters @@ -103,7 +103,8 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_PRESOLVE, reinterpret_cast(&mip_settings.presolver), CUOPT_PRESOLVE_DEFAULT, CUOPT_PRESOLVE_PSLP, CUOPT_PRESOLVE_DEFAULT}, {CUOPT_MIP_DETERMINISM_MODE, &mip_settings.determinism_mode, CUOPT_MODE_OPPORTUNISTIC, CUOPT_MODE_DETERMINISTIC, CUOPT_MODE_OPPORTUNISTIC}, {CUOPT_RANDOM_SEED, &mip_settings.seed, -1, std::numeric_limits::max(), -1}, - {CUOPT_MIP_RELIABILITY_BRANCHING, &mip_settings.reliability_branching, -1, std::numeric_limits::max(), -1} + {CUOPT_MIP_RELIABILITY_BRANCHING, &mip_settings.reliability_branching, -1, std::numeric_limits::max(), -1}, + {CUOPT_PDLP_PRECISION, reinterpret_cast(&pdlp_settings.pdlp_precision), CUOPT_PDLP_DEFAULT_PRECISION, CUOPT_PDLP_MIXED_PRECISION, CUOPT_PDLP_DEFAULT_PRECISION} }; // Bool parameters @@ -120,7 +121,7 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_CROSSOVER, &pdlp_settings.crossover, false}, {CUOPT_ELIMINATE_DENSE_COLUMNS, &pdlp_settings.eliminate_dense_columns, true}, {CUOPT_CUDSS_DETERMINISTIC, &pdlp_settings.cudss_deterministic, false}, - {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true} + {CUOPT_DUAL_POSTSOLVE, &pdlp_settings.dual_postsolve, true}, }; // String parameters string_parameters = { diff --git a/cpp/src/mip_heuristics/diversity/lns/rins.cu b/cpp/src/mip_heuristics/diversity/lns/rins.cu index 7fd8533f82..2fbe79ba34 100644 --- a/cpp/src/mip_heuristics/diversity/lns/rins.cu +++ b/cpp/src/mip_heuristics/diversity/lns/rins.cu @@ -186,7 +186,7 @@ void rins_t::run_rins() total_calls++; node_count_at_last_rins = node_count.load(); - time_limit = std::min(time_limit, dm.timer.remaining_time()); + time_limit = std::min(time_limit, static_cast(dm.timer.remaining_time())); CUOPT_LOG_DEBUG("Running RINS on solution with objective %g, fixing %d/%d", best_sol.get_user_objective(), vars_to_fix.size(), @@ -288,22 +288,22 @@ void rins_t::run_rins() if (branch_and_bound_status == dual_simplex::mip_status_t::OPTIMAL) { CUOPT_LOG_DEBUG("RINS submip optimal"); // do goldilocks update - fixrate = std::max(fixrate - 0.05, settings.min_fixrate); - time_limit = std::max(time_limit - 2, settings.min_time_limit); + fixrate = std::max(fixrate - f_t(0.05), static_cast(settings.min_fixrate)); + time_limit = std::max(time_limit - f_t(2), static_cast(settings.min_time_limit)); } else if (branch_and_bound_status == dual_simplex::mip_status_t::TIME_LIMIT) { CUOPT_LOG_DEBUG("RINS submip time limit"); // do goldilocks update - fixrate = std::min(fixrate + 0.05, settings.max_fixrate); - time_limit = std::min(time_limit + 2, settings.max_time_limit); + fixrate = std::min(fixrate + f_t(0.05), static_cast(settings.max_fixrate)); + time_limit = std::min(time_limit + f_t(2), static_cast(settings.max_time_limit)); } else if (branch_and_bound_status == dual_simplex::mip_status_t::INFEASIBLE) { CUOPT_LOG_DEBUG("RINS submip infeasible"); // do goldilocks update, decreasing fixrate - fixrate = std::max(fixrate - 0.05, settings.min_fixrate); + fixrate = std::max(fixrate - f_t(0.05), static_cast(settings.min_fixrate)); } else { CUOPT_LOG_DEBUG("RINS solution not found"); // do goldilocks update - fixrate = std::min(fixrate + 0.05, settings.max_fixrate); - time_limit = std::min(time_limit + 2, settings.max_time_limit); + fixrate = std::min(fixrate + f_t(0.05), static_cast(settings.max_fixrate)); + time_limit = std::min(time_limit + f_t(2), static_cast(settings.max_time_limit)); } cpu_fj_thread.stop_cpu_solver(); diff --git a/cpp/src/mip_heuristics/local_search/rounding/simple_rounding.cu b/cpp/src/mip_heuristics/local_search/rounding/simple_rounding.cu index c9a6dd0eda..4f3a015a6c 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/simple_rounding.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/simple_rounding.cu @@ -179,7 +179,7 @@ void invoke_correct_integers(solution_t& solution, f_t tol) template void invoke_correct_integers(solution_t & solution, \ F_TYPE tol); -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT INSTANTIATE(float) #endif diff --git a/cpp/src/mip_heuristics/mip_constants.hpp b/cpp/src/mip_heuristics/mip_constants.hpp index 66f5ebd273..47d3d22de4 100644 --- a/cpp/src/mip_heuristics/mip_constants.hpp +++ b/cpp/src/mip_heuristics/mip_constants.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -11,3 +11,5 @@ #define MIP_INSTANTIATE_FLOAT CUOPT_INSTANTIATE_FLOAT #define MIP_INSTANTIATE_DOUBLE CUOPT_INSTANTIATE_DOUBLE + +#define PDLP_INSTANTIATE_FLOAT 1 diff --git a/cpp/src/mip_heuristics/presolve/gf2_presolve.cpp b/cpp/src/mip_heuristics/presolve/gf2_presolve.cpp index 45ea4e420f..8ab0176cc4 100644 --- a/cpp/src/mip_heuristics/presolve/gf2_presolve.cpp +++ b/cpp/src/mip_heuristics/presolve/gf2_presolve.cpp @@ -247,7 +247,7 @@ papilo::PresolveStatus GF2Presolve::execute(const papilo::Problem& pro #define INSTANTIATE(F_TYPE) template class GF2Presolve; -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT INSTANTIATE(float) #endif diff --git a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp index 5a89393a6a..20a586f6fb 100644 --- a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp +++ b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp @@ -432,6 +432,7 @@ optimization_problem_t build_optimization_problem( const int* cols = constraint_matrix.getConstraintMatrix().getColumns(); const f_t* coeffs = constraint_matrix.getConstraintMatrix().getValues(); + op_problem.set_csr_constraint_matrix( &(coeffs[start]), nnz, &(cols[start]), nnz, offsets.data(), nrows + 1); @@ -535,7 +536,7 @@ void set_presolve_options(papilo::Presolve& presolver, problem_category_t category, f_t absolute_tolerance, f_t relative_tolerance, - double time_limit, + f_t time_limit, bool dual_postsolve, i_t num_cpu_threads) { @@ -572,24 +573,30 @@ template std::optional> third_party_presolve_t::apply_pslp( optimization_problem_t const& op_problem, const double time_limit) { - f_t original_obj_offset = op_problem.get_objective_offset(); - auto ctx = build_and_run_pslp_presolver(op_problem, maximize_, time_limit); + if constexpr (std::is_same_v) { + double original_obj_offset = op_problem.get_objective_offset(); + auto ctx = build_and_run_pslp_presolver(op_problem, maximize_, time_limit); - // Free previously allocated presolver and settings - if (pslp_presolver_ != nullptr) { free_presolver(pslp_presolver_); } - if (pslp_stgs_ != nullptr) { free_settings(pslp_stgs_); } + // Free previously allocated presolver and settings if they exist + if (pslp_presolver_ != nullptr) { free_presolver(pslp_presolver_); } + if (pslp_stgs_ != nullptr) { free_settings(pslp_stgs_); } - pslp_presolver_ = ctx.presolver; - pslp_stgs_ = ctx.settings; + pslp_presolver_ = ctx.presolver; + pslp_stgs_ = ctx.settings; - if (ctx.status == PresolveStatus_::INFEASIBLE || ctx.status == PresolveStatus_::UNBNDORINFEAS) { - return std::nullopt; - } + if (ctx.status == PresolveStatus_::INFEASIBLE || ctx.status == PresolveStatus_::UNBNDORINFEAS) { + return std::nullopt; + } - auto opt_problem = build_optimization_problem_from_pslp( - pslp_presolver_, op_problem.get_handle_ptr(), maximize_, original_obj_offset); + auto opt_problem = build_optimization_problem_from_pslp( + pslp_presolver_, op_problem.get_handle_ptr(), maximize_, original_obj_offset); - return std::make_optional(third_party_presolve_result_t{opt_problem, {}}); + return std::make_optional(third_party_presolve_result_t{opt_problem, {}}); + } else { + cuopt_expects( + false, error_type_t::ValidationError, "PSLP presolver only supports double precision"); + return std::nullopt; + } } template @@ -625,7 +632,7 @@ std::optional> third_party_presolve_t papilo_presolver; - set_presolve_methods(papilo_presolver, category, dual_postsolve); + set_presolve_methods(papilo_presolver, category, dual_postsolve); set_presolve_options(papilo_presolver, category, absolute_tolerance, @@ -633,7 +640,7 @@ std::optional> third_party_presolve_t( + set_presolve_parameters( papilo_presolver, category, op_problem.get_n_constraints(), op_problem.get_n_variables()); // Disable papilo logs @@ -697,12 +704,14 @@ void third_party_presolve_t::undo(rmm::device_uvector& primal_sol } if (status_to_skip) { return; } + std::vector primal_sol_vec_h(primal_solution.size()); raft::copy(primal_sol_vec_h.data(), primal_solution.data(), primal_solution.size(), stream_view); std::vector dual_sol_vec_h(dual_solution.size()); raft::copy(dual_sol_vec_h.data(), dual_solution.data(), dual_solution.size(), stream_view); std::vector reduced_costs_vec_h(reduced_costs.size()); raft::copy(reduced_costs_vec_h.data(), reduced_costs.data(), reduced_costs.size(), stream_view); + papilo::Solution reduced_sol(primal_sol_vec_h); if (dual_postsolve) { reduced_sol.dual = dual_sol_vec_h; @@ -734,26 +743,34 @@ void third_party_presolve_t::undo_pslp(rmm::device_uvector& prima rmm::device_uvector& reduced_costs, rmm::cuda_stream_view stream_view) { - std::vector h_primal_solution(primal_solution.size()); - std::vector h_dual_solution(dual_solution.size()); - std::vector h_reduced_costs(reduced_costs.size()); - raft::copy(h_primal_solution.data(), primal_solution.data(), primal_solution.size(), stream_view); - raft::copy(h_dual_solution.data(), dual_solution.data(), dual_solution.size(), stream_view); - raft::copy(h_reduced_costs.data(), reduced_costs.data(), reduced_costs.size(), stream_view); - - postsolve( - pslp_presolver_, h_primal_solution.data(), h_dual_solution.data(), h_reduced_costs.data()); - - auto uncrushed_sol = pslp_presolver_->sol; - int n_cols = uncrushed_sol->dim_x; - int n_rows = uncrushed_sol->dim_y; - - primal_solution.resize(n_cols, stream_view); - dual_solution.resize(n_rows, stream_view); - reduced_costs.resize(n_cols, stream_view); - raft::copy(primal_solution.data(), uncrushed_sol->x, n_cols, stream_view); - raft::copy(dual_solution.data(), uncrushed_sol->y, n_rows, stream_view); - raft::copy(reduced_costs.data(), uncrushed_sol->z, n_cols, stream_view); + if constexpr (std::is_same_v) { + // PSLP uses double internally, so we can use the data directly + std::vector h_primal_solution(primal_solution.size()); + std::vector h_dual_solution(dual_solution.size()); + std::vector h_reduced_costs(reduced_costs.size()); + raft::copy( + h_primal_solution.data(), primal_solution.data(), primal_solution.size(), stream_view); + raft::copy(h_dual_solution.data(), dual_solution.data(), dual_solution.size(), stream_view); + raft::copy(h_reduced_costs.data(), reduced_costs.data(), reduced_costs.size(), stream_view); + stream_view.synchronize(); + + postsolve( + pslp_presolver_, h_primal_solution.data(), h_dual_solution.data(), h_reduced_costs.data()); + + auto uncrushed_sol = pslp_presolver_->sol; + int n_cols = uncrushed_sol->dim_x; + int n_rows = uncrushed_sol->dim_y; + + primal_solution.resize(n_cols, stream_view); + dual_solution.resize(n_rows, stream_view); + reduced_costs.resize(n_cols, stream_view); + raft::copy(primal_solution.data(), uncrushed_sol->x, n_cols, stream_view); + raft::copy(dual_solution.data(), uncrushed_sol->y, n_rows, stream_view); + raft::copy(reduced_costs.data(), uncrushed_sol->z, n_cols, stream_view); + } else { + cuopt_expects( + false, error_type_t::ValidationError, "PSLP postsolve only supports double precision"); + } stream_view.synchronize(); } @@ -795,7 +812,7 @@ void papilo_postsolve_deleter::operator()(papilo::PostsolveStorage* pt delete ptr; } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template struct papilo_postsolve_deleter; template class third_party_presolve_t; #endif diff --git a/cpp/src/mip_heuristics/problem/presolve_data.cu b/cpp/src/mip_heuristics/problem/presolve_data.cu index b11f7b108a..bf05efa875 100644 --- a/cpp/src/mip_heuristics/problem/presolve_data.cu +++ b/cpp/src/mip_heuristics/problem/presolve_data.cu @@ -245,7 +245,7 @@ void presolve_data_t::papilo_uncrush_assignment( problem.handle_ptr->sync_stream(); } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class presolve_data_t; #endif diff --git a/cpp/src/mip_heuristics/problem/problem.cu b/cpp/src/mip_heuristics/problem/problem.cu index bc93a9d988..fadc00a850 100644 --- a/cpp/src/mip_heuristics/problem/problem.cu +++ b/cpp/src/mip_heuristics/problem/problem.cu @@ -2380,7 +2380,7 @@ void problem_t::update_variable_bounds(const std::vector& var_ind RAFT_CHECK_CUDA(handle_ptr->get_stream()); } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class problem_t; #endif diff --git a/cpp/src/mip_heuristics/solution/solution.cu b/cpp/src/mip_heuristics/solution/solution.cu index 5f1c13199b..531d54372c 100644 --- a/cpp/src/mip_heuristics/solution/solution.cu +++ b/cpp/src/mip_heuristics/solution/solution.cu @@ -660,7 +660,7 @@ mip_solution_t solution_t::get_solution(bool output_feasible } } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class solution_t; #endif diff --git a/cpp/src/mip_heuristics/solver_solution.cu b/cpp/src/mip_heuristics/solver_solution.cu index 60556884c9..e497a21c8f 100644 --- a/cpp/src/mip_heuristics/solver_solution.cu +++ b/cpp/src/mip_heuristics/solver_solution.cu @@ -209,8 +209,8 @@ void mip_solution_t::write_to_sol_file(std::string_view filename, status = "Infeasible"; } - double objective_value = get_objective_value(); - auto& var_names = get_variable_names(); + f_t objective_value = get_objective_value(); + auto& var_names = get_variable_names(); std::vector solution; solution.resize(solution_.size()); raft::copy(solution.data(), solution_.data(), solution_.size(), stream_view.value()); @@ -234,7 +234,7 @@ void mip_solution_t::log_summary() const CUOPT_LOG_INFO("Total Solve Time: %f", get_total_solve_time()); } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class mip_solution_t; #endif diff --git a/cpp/src/pdlp/cpu_pdlp_warm_start_data.cu b/cpp/src/pdlp/cpu_pdlp_warm_start_data.cu index f9a73dff06..b078bc4779 100644 --- a/cpp/src/pdlp/cpu_pdlp_warm_start_data.cu +++ b/cpp/src/pdlp/cpu_pdlp_warm_start_data.cu @@ -108,14 +108,14 @@ pdlp_warm_start_data_t convert_to_gpu_warmstart( return gpu_data; } -// Explicit template instantiations +#if MIP_INSTANTIATE_DOUBLE template cpu_pdlp_warm_start_data_t convert_to_cpu_warmstart( const pdlp_warm_start_data_t&, rmm::cuda_stream_view); - template pdlp_warm_start_data_t convert_to_gpu_warmstart( const cpu_pdlp_warm_start_data_t&, rmm::cuda_stream_view); +#endif -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template cpu_pdlp_warm_start_data_t convert_to_cpu_warmstart( const pdlp_warm_start_data_t&, rmm::cuda_stream_view); diff --git a/cpp/src/pdlp/cusparse_view.cu b/cpp/src/pdlp/cusparse_view.cu index ca36dde421..64ec44f5ef 100644 --- a/cpp/src/pdlp/cusparse_view.cu +++ b/cpp/src/pdlp/cusparse_view.cu @@ -21,6 +21,12 @@ #include #include +#include + +struct double_to_float_functor { + __host__ __device__ float operator()(double val) const { return static_cast(val); } +}; + namespace cuopt::linear_programming::detail { // cusparse_sp_mat_descr_wrapper_t implementation @@ -277,7 +283,8 @@ cusparse_view_t::cusparse_view_t( rmm::device_uvector& _potential_next_dual_solution, rmm::device_uvector& _reflected_primal_solution, const std::vector& climber_strategies, - const pdlp_hyper_params::pdlp_hyper_params_t& hyper_params) + const pdlp_hyper_params::pdlp_hyper_params_t& hyper_params, + bool enable_mixed_precision_spmv) : batch_mode_(climber_strategies.size() > 1), handle_ptr_(handle_ptr), A{}, @@ -304,7 +311,12 @@ cusparse_view_t::cusparse_view_t( A_{op_problem_scaled.coefficients}, A_offsets_{op_problem_scaled.offsets}, A_indices_{op_problem_scaled.variables}, - climber_strategies_(climber_strategies) + climber_strategies_(climber_strategies), + A_float_{0, handle_ptr->get_stream()}, + A_T_float_{0, handle_ptr->get_stream()}, + buffer_non_transpose_mixed_{0, handle_ptr->get_stream()}, + buffer_transpose_mixed_{0, handle_ptr->get_stream()}, + mixed_precision_enabled_{false} { raft::common::nvtx::range fun_scope("Initializing cuSparse view"); @@ -583,6 +595,92 @@ cusparse_view_t::cusparse_view_t( handle_ptr->get_stream()); } #endif + + if constexpr (std::is_same_v) { + if (enable_mixed_precision_spmv && !batch_mode_) { + mixed_precision_enabled_ = true; + + A_float_.resize(op_problem_scaled.nnz, handle_ptr->get_stream()); + A_T_float_.resize(op_problem_scaled.nnz, handle_ptr->get_stream()); + + RAFT_CUDA_TRY(cub::DeviceTransform::Transform(op_problem_scaled.coefficients.data(), + A_float_.data(), + op_problem_scaled.nnz, + double_to_float_functor{}, + handle_ptr->get_stream().value())); + + RAFT_CUDA_TRY(cub::DeviceTransform::Transform(A_T_.data(), + A_T_float_.data(), + op_problem_scaled.nnz, + double_to_float_functor{}, + handle_ptr->get_stream().value())); + + A_mixed_.create(op_problem_scaled.n_constraints, + op_problem_scaled.n_variables, + op_problem_scaled.nnz, + const_cast(op_problem_scaled.offsets.data()), + const_cast(op_problem_scaled.variables.data()), + A_float_.data()); + + A_T_mixed_.create(op_problem_scaled.n_variables, + op_problem_scaled.n_constraints, + op_problem_scaled.nnz, + const_cast(A_T_offsets_.data()), + const_cast(A_T_indices_.data()), + A_T_float_.data()); + + const rmm::device_scalar alpha_d{1.0, handle_ptr->get_stream()}; + const rmm::device_scalar beta_d{0.0, handle_ptr->get_stream()}; + + size_t buffer_size_non_transpose_mixed = + mixed_precision_spmv_buffersize(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + alpha_d.data(), + A_mixed_, + c, + beta_d.data(), + dual_solution, + CUSPARSE_SPMV_CSR_ALG2, + handle_ptr->get_stream()); + buffer_non_transpose_mixed_.resize(buffer_size_non_transpose_mixed, handle_ptr->get_stream()); + + size_t buffer_size_transpose_mixed = + mixed_precision_spmv_buffersize(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + alpha_d.data(), + A_T_mixed_, + dual_solution, + beta_d.data(), + c, + CUSPARSE_SPMV_CSR_ALG2, + handle_ptr->get_stream()); + buffer_transpose_mixed_.resize(buffer_size_transpose_mixed, handle_ptr->get_stream()); + +#if CUDA_VER_12_4_UP + mixed_precision_spmv_preprocess(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + alpha_d.data(), + A_mixed_, + c, + beta_d.data(), + dual_solution, + CUSPARSE_SPMV_CSR_ALG2, + buffer_non_transpose_mixed_.data(), + handle_ptr->get_stream()); + + mixed_precision_spmv_preprocess(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + alpha_d.data(), + A_T_mixed_, + dual_solution, + beta_d.data(), + c, + CUSPARSE_SPMV_CSR_ALG2, + buffer_transpose_mixed_.data(), + handle_ptr->get_stream()); +#endif + } + } } // Used by pdlp object for current and average termination condition @@ -625,7 +723,12 @@ cusparse_view_t::cusparse_view_t( A_{op_problem.coefficients}, A_offsets_{op_problem.offsets}, A_indices_{op_problem.variables}, - climber_strategies_(climber_strategies) + climber_strategies_(climber_strategies), + A_float_{0, handle_ptr->get_stream()}, + A_T_float_{0, handle_ptr->get_stream()}, + buffer_non_transpose_mixed_{0, handle_ptr->get_stream()}, + buffer_transpose_mixed_{0, handle_ptr->get_stream()}, + mixed_precision_enabled_{false} { #ifdef PDLP_DEBUG_MODE RAFT_CUDA_TRY(cudaDeviceSynchronize()); @@ -832,7 +935,12 @@ cusparse_view_t::cusparse_view_t( A_{existing_cusparse_view.A_}, A_offsets_{existing_cusparse_view.A_offsets_}, A_indices_{existing_cusparse_view.A_indices_}, - climber_strategies_(existing_cusparse_view.climber_strategies_) + climber_strategies_(existing_cusparse_view.climber_strategies_), + A_float_{0, handle_ptr->get_stream()}, + A_T_float_{0, handle_ptr->get_stream()}, + buffer_non_transpose_mixed_{0, handle_ptr->get_stream()}, + buffer_transpose_mixed_{0, handle_ptr->get_stream()}, + mixed_precision_enabled_{false} { #ifdef PDLP_DEBUG_MODE RAFT_CUDA_TRY(cudaDeviceSynchronize()); @@ -942,11 +1050,105 @@ cusparse_view_t::cusparse_view_t( A_(dummy_float), A_offsets_(dummy_int), A_indices_(dummy_int), - climber_strategies_(climber_strategies) + climber_strategies_(climber_strategies), + A_float_{0, handle_ptr->get_stream()}, + A_T_float_{0, handle_ptr->get_stream()}, + buffer_non_transpose_mixed_{0, handle_ptr->get_stream()}, + buffer_transpose_mixed_{0, handle_ptr->get_stream()}, + mixed_precision_enabled_{false} +{ +} + +// Update FP32 matrix copies after scaling (must be called after scale_problem()) +template +void cusparse_view_t::update_mixed_precision_matrices() +{ + if constexpr (std::is_same_v) { + if (!mixed_precision_enabled_) { return; } + + RAFT_CUDA_TRY(cub::DeviceTransform::Transform(A_.data(), + A_float_.data(), + A_.size(), + double_to_float_functor{}, + handle_ptr_->get_stream().value())); + + RAFT_CUDA_TRY(cub::DeviceTransform::Transform(A_T_.data(), + A_T_float_.data(), + A_T_.size(), + double_to_float_functor{}, + handle_ptr_->get_stream().value())); + + handle_ptr_->get_stream().synchronize(); + } +} + +// Mixed precision SpMV implementation: FP32 matrix with FP64 vectors and FP64 compute type +size_t mixed_precision_spmv_buffersize(cusparseHandle_t handle, + cusparseOperation_t opA, + const double* alpha, + cusparseSpMatDescr_t matA, // FP32 matrix + cusparseDnVecDescr_t vecX, // FP64 vector + const double* beta, + cusparseDnVecDescr_t vecY, // FP64 vector + cusparseSpMVAlg_t alg, + cudaStream_t stream) +{ + size_t bufferSize = 0; + RAFT_CUSPARSE_TRY(cusparseSetStream(handle, stream)); + RAFT_CUSPARSE_TRY(cusparseSpMV_bufferSize( + handle, opA, alpha, matA, vecX, beta, vecY, CUDA_R_64F, alg, &bufferSize)); + return bufferSize; +} + +void mixed_precision_spmv(cusparseHandle_t handle, + cusparseOperation_t opA, + const double* alpha, + cusparseSpMatDescr_t matA, // FP32 matrix + cusparseDnVecDescr_t vecX, // FP64 vector + const double* beta, + cusparseDnVecDescr_t vecY, // FP64 vector + cusparseSpMVAlg_t alg, + void* externalBuffer, + cudaStream_t stream) +{ + RAFT_CUSPARSE_TRY(cusparseSetStream(handle, stream)); + RAFT_CUSPARSE_TRY( + cusparseSpMV(handle, opA, alpha, matA, vecX, beta, vecY, CUDA_R_64F, alg, externalBuffer)); +} + +#if CUDA_VER_12_4_UP +void mixed_precision_spmv_preprocess(cusparseHandle_t handle, + cusparseOperation_t opA, + const double* alpha, + cusparseSpMatDescr_t matA, // FP32 matrix + cusparseDnVecDescr_t vecX, // FP64 vector + const double* beta, + cusparseDnVecDescr_t vecY, // FP64 vector + cusparseSpMVAlg_t alg, + void* externalBuffer, + cudaStream_t stream) +{ + static const auto func = + dynamic_load_runtime::function("cusparseSpMV_preprocess"); + if (func.has_value()) { + RAFT_CUSPARSE_TRY(cusparseSetStream(handle, stream)); + RAFT_CUSPARSE_TRY( + (*func)(handle, opA, alpha, matA, vecX, beta, vecY, CUDA_R_64F, alg, externalBuffer)); + } +} +#endif + +bool is_cusparse_runtime_mixed_precision_supported() { + int major = 0, minor = 0; + auto status = cusparseGetProperty(libraryPropertyType_t::MAJOR_VERSION, &major); + if (status != CUSPARSE_STATUS_SUCCESS) return false; + status = cusparseGetProperty(libraryPropertyType_t::MINOR_VERSION, &minor); + if (status != CUSPARSE_STATUS_SUCCESS) return false; + return (major > 12) || (major == 12 && minor >= 5); } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class cusparse_sp_mat_descr_wrapper_t; template class cusparse_dn_vec_descr_wrapper_t; template class cusparse_dn_mat_descr_wrapper_t; @@ -960,7 +1162,7 @@ template class cusparse_view_t; #endif #if CUDA_VER_12_4_UP -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template void my_cusparsespmm_preprocess(cusparseHandle_t, cusparseOperation_t, cusparseOperation_t, diff --git a/cpp/src/pdlp/cusparse_view.hpp b/cpp/src/pdlp/cusparse_view.hpp index cbbc856924..416a0b1e5f 100644 --- a/cpp/src/pdlp/cusparse_view.hpp +++ b/cpp/src/pdlp/cusparse_view.hpp @@ -90,7 +90,8 @@ class cusparse_view_t { rmm::device_uvector& _potential_next_dual_solution, rmm::device_uvector& _reflected_primal_solution, const std::vector& climber_strategies, - const pdlp_hyper_params::pdlp_hyper_params_t& hyper_params); + const pdlp_hyper_params::pdlp_hyper_params_t& hyper_params, + bool enable_mixed_precision_spmv); cusparse_view_t(raft::handle_t const* handle_ptr, const problem_t& op_problem, @@ -194,8 +195,56 @@ class cusparse_view_t { const rmm::device_uvector& A_indices_; const std::vector& climber_strategies_; + + // Mixed precision SpMV support (FP32 matrix with FP64 vectors/compute) + // Only used when mixed_precision_enabled_ is true and f_t = double + rmm::device_uvector A_float_; // FP32 copy of A values + rmm::device_uvector A_T_float_; // FP32 copy of A_T values + cusparse_sp_mat_descr_wrapper_t A_mixed_; // FP32 matrix descriptor for A + cusparse_sp_mat_descr_wrapper_t A_T_mixed_; // FP32 matrix descriptor for A_T + rmm::device_uvector buffer_non_transpose_mixed_; // SpMV buffer for mixed precision A + rmm::device_uvector buffer_transpose_mixed_; // SpMV buffer for mixed precision A_T + bool mixed_precision_enabled_{false}; + + // Update FP32 matrix copies after scaling (must be called after scale_problem()) + void update_mixed_precision_matrices(); }; +// Mixed precision SpMV: FP32 matrix with FP64 vectors and FP64 compute type +void mixed_precision_spmv(cusparseHandle_t handle, + cusparseOperation_t opA, + const double* alpha, + cusparseSpMatDescr_t matA, // FP32 matrix + cusparseDnVecDescr_t vecX, // FP64 vector + const double* beta, + cusparseDnVecDescr_t vecY, // FP64 vector + cusparseSpMVAlg_t alg, + void* externalBuffer, + cudaStream_t stream); + +size_t mixed_precision_spmv_buffersize(cusparseHandle_t handle, + cusparseOperation_t opA, + const double* alpha, + cusparseSpMatDescr_t matA, // FP32 matrix + cusparseDnVecDescr_t vecX, // FP64 vector + const double* beta, + cusparseDnVecDescr_t vecY, // FP64 vector + cusparseSpMVAlg_t alg, + cudaStream_t stream); + +#if CUDA_VER_12_4_UP +void mixed_precision_spmv_preprocess(cusparseHandle_t handle, + cusparseOperation_t opA, + const double* alpha, + cusparseSpMatDescr_t matA, // FP32 matrix + cusparseDnVecDescr_t vecX, // FP64 vector + const double* beta, + cusparseDnVecDescr_t vecY, // FP64 vector + cusparseSpMVAlg_t alg, + void* externalBuffer, + cudaStream_t stream); +#endif + #if CUDA_VER_12_4_UP template < typename T, @@ -213,4 +262,6 @@ void my_cusparsespmm_preprocess(cusparseHandle_t handle, cudaStream_t stream); #endif +bool is_cusparse_runtime_mixed_precision_supported(); + } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/pdlp/initial_scaling_strategy/initial_scaling.cu b/cpp/src/pdlp/initial_scaling_strategy/initial_scaling.cu index afa3ee5fb7..b618550f6e 100644 --- a/cpp/src/pdlp/initial_scaling_strategy/initial_scaling.cu +++ b/cpp/src/pdlp/initial_scaling_strategy/initial_scaling.cu @@ -858,7 +858,7 @@ pdlp_initial_scaling_strategy_t::view() int* A_T_offsets, \ int* A_T_indices); -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT INSTANTIATE(float) #endif diff --git a/cpp/src/pdlp/optimal_batch_size_handler/optimal_batch_size_handler.cu b/cpp/src/pdlp/optimal_batch_size_handler/optimal_batch_size_handler.cu index deb6b759aa..cbfb03618d 100644 --- a/cpp/src/pdlp/optimal_batch_size_handler/optimal_batch_size_handler.cu +++ b/cpp/src/pdlp/optimal_batch_size_handler/optimal_batch_size_handler.cu @@ -434,7 +434,7 @@ int optimal_batch_size_handler(const optimization_problem_t& op_proble return 0; } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template int optimal_batch_size_handler( const optimization_problem_t& op_problem, int max_batch_size); #endif diff --git a/cpp/src/pdlp/optimization_problem.cu b/cpp/src/pdlp/optimization_problem.cu index d0888dd3ac..9b3016a113 100644 --- a/cpp/src/pdlp/optimization_problem.cu +++ b/cpp/src/pdlp/optimization_problem.cu @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -1505,15 +1506,105 @@ void optimization_problem_t::copy_variable_types_to_host(var_t* output cudaMemcpy(output, variable_types_.data(), size * sizeof(var_t), cudaMemcpyDeviceToHost)); } +template +struct cast_op { + HDI To operator()(From val) const { return static_cast(val); } +}; + +template +rmm::device_uvector gpu_cast(const rmm::device_uvector& src, rmm::cuda_stream_view stream) +{ + rmm::device_uvector dst(src.size(), stream); + if (src.size() > 0) { + RAFT_CUDA_TRY(cub::DeviceTransform::Transform( + src.data(), dst.data(), src.size(), cast_op{}, stream.value())); + } + return dst; +} + +template rmm::device_uvector gpu_cast(const rmm::device_uvector&, + rmm::cuda_stream_view); +template rmm::device_uvector gpu_cast(const rmm::device_uvector&, + rmm::cuda_stream_view); + +template +template +optimization_problem_t optimization_problem_t::convert_to_other_prec( + rmm::cuda_stream_view stream) const +{ + optimization_problem_t other(handle_ptr_); + + other.set_maximize(maximize_); + other.set_objective_offset(static_cast(objective_offset_)); + other.set_objective_scaling_factor(static_cast(objective_scaling_factor_)); + + if (A_.size() > 0) { + auto other_A = gpu_cast(A_, stream); + other.set_csr_constraint_matrix(other_A.data(), + static_cast(other_A.size()), + A_indices_.data(), + static_cast(A_indices_.size()), + A_offsets_.data(), + static_cast(A_offsets_.size())); + } + + if (c_.size() > 0) { + auto other_c = gpu_cast(c_, stream); + other.set_objective_coefficients(other_c.data(), static_cast(other_c.size())); + } + + if (b_.size() > 0) { + auto other_b = gpu_cast(b_, stream); + other.set_constraint_bounds(other_b.data(), static_cast(other_b.size())); + } + + if (constraint_lower_bounds_.size() > 0) { + auto other_clb = gpu_cast(constraint_lower_bounds_, stream); + other.set_constraint_lower_bounds(other_clb.data(), static_cast(other_clb.size())); + } + + if (constraint_upper_bounds_.size() > 0) { + auto other_cub = gpu_cast(constraint_upper_bounds_, stream); + other.set_constraint_upper_bounds(other_cub.data(), static_cast(other_cub.size())); + } + + if (variable_lower_bounds_.size() > 0) { + auto other_vlb = gpu_cast(variable_lower_bounds_, stream); + other.set_variable_lower_bounds(other_vlb.data(), static_cast(other_vlb.size())); + } + + if (variable_upper_bounds_.size() > 0) { + auto other_vub = gpu_cast(variable_upper_bounds_, stream); + other.set_variable_upper_bounds(other_vub.data(), static_cast(other_vub.size())); + } + + if (variable_types_.size() > 0) { + other.set_variable_types(variable_types_.data(), static_cast(variable_types_.size())); + } + + other.set_variable_names(var_names_); + other.set_row_names(row_names_); + other.set_objective_name(objective_name_); + other.set_problem_category(problem_category_); + + return other; +} + // ============================================================================== // Template instantiations // ============================================================================== // Explicit template instantiations matching MIP constants -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class optimization_problem_t; #endif #if MIP_INSTANTIATE_DOUBLE template class optimization_problem_t; #endif +#if PDLP_INSTANTIATE_FLOAT || MIP_INSTANTIATE_FLOAT +template optimization_problem_t + optimization_problem_t::convert_to_other_prec( + rmm::cuda_stream_view) const; +#endif + } // namespace cuopt::linear_programming diff --git a/cpp/src/pdlp/pdhg.cu b/cpp/src/pdlp/pdhg.cu index 51e0b29381..74df7fee01 100644 --- a/cpp/src/pdlp/pdhg.cu +++ b/cpp/src/pdlp/pdhg.cu @@ -41,7 +41,8 @@ pdhg_solver_t::pdhg_solver_t( bool is_legacy_batch_mode, // Batch mode with streams const std::vector& climber_strategies, const pdlp_hyper_params::pdlp_hyper_params_t& hyper_params, - const std::vector>& new_bounds) + const std::vector>& new_bounds, + bool enable_mixed_precision_spmv) : batch_mode_(climber_strategies.size() > 1), handle_ptr_(handle_ptr), stream_view_(handle_ptr_->get_stream()), @@ -77,7 +78,8 @@ pdhg_solver_t::pdhg_solver_t( potential_next_dual_solution_, reflected_primal_, climber_strategies, - hyper_params}, + hyper_params, + enable_mixed_precision_spmv}, reusable_device_scalar_value_1_{1.0, stream_view_}, reusable_device_scalar_value_0_{0.0, stream_view_}, reusable_device_scalar_value_neg_1_{f_t(-1.0), stream_view_}, @@ -249,17 +251,33 @@ void pdhg_solver_t::compute_next_dual_solution(rmm::device_uvectorget_cusparse_handle(), - CUSPARSE_OPERATION_NON_TRANSPOSE, - reusable_device_scalar_value_1_.data(), // 1 - cusparse_view_.A, - cusparse_view_.tmp_primal, - reusable_device_scalar_value_0_.data(), // 1 - cusparse_view_.dual_gradient, - CUSPARSE_SPMV_CSR_ALG2, - (f_t*)cusparse_view_.buffer_non_transpose.data(), - stream_view_)); + if constexpr (std::is_same_v) { + if (cusparse_view_.mixed_precision_enabled_) { + mixed_precision_spmv(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + reusable_device_scalar_value_1_.data(), + cusparse_view_.A_mixed_, + cusparse_view_.tmp_primal, + reusable_device_scalar_value_0_.data(), + cusparse_view_.dual_gradient, + CUSPARSE_SPMV_CSR_ALG2, + cusparse_view_.buffer_non_transpose_mixed_.data(), + stream_view_); + } + } + if (!cusparse_view_.mixed_precision_enabled_) { + RAFT_CUSPARSE_TRY( + raft::sparse::detail::cusparsespmv(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + reusable_device_scalar_value_1_.data(), + cusparse_view_.A, + cusparse_view_.tmp_primal, + reusable_device_scalar_value_0_.data(), + cusparse_view_.dual_gradient, + CUSPARSE_SPMV_CSR_ALG2, + (f_t*)cusparse_view_.buffer_non_transpose.data(), + stream_view_)); + } // y - (sigma*dual_gradient) // max(min(0, sigma*constraint_upper+primal_product), sigma*constraint_lower+primal_product) @@ -287,17 +305,33 @@ void pdhg_solver_t::compute_At_y() // A_t @ y if (!batch_mode_) { - RAFT_CUSPARSE_TRY( - raft::sparse::detail::cusparsespmv(handle_ptr_->get_cusparse_handle(), - CUSPARSE_OPERATION_NON_TRANSPOSE, - reusable_device_scalar_value_1_.data(), - cusparse_view_.A_T, - cusparse_view_.dual_solution, - reusable_device_scalar_value_0_.data(), - cusparse_view_.current_AtY, - CUSPARSE_SPMV_CSR_ALG2, - (f_t*)cusparse_view_.buffer_transpose.data(), - stream_view_)); + if constexpr (std::is_same_v) { + if (cusparse_view_.mixed_precision_enabled_) { + mixed_precision_spmv(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + reusable_device_scalar_value_1_.data(), + cusparse_view_.A_T_mixed_, + cusparse_view_.dual_solution, + reusable_device_scalar_value_0_.data(), + cusparse_view_.current_AtY, + CUSPARSE_SPMV_CSR_ALG2, + cusparse_view_.buffer_transpose_mixed_.data(), + stream_view_); + } + } + if (!cusparse_view_.mixed_precision_enabled_) { + RAFT_CUSPARSE_TRY( + raft::sparse::detail::cusparsespmv(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + reusable_device_scalar_value_1_.data(), + cusparse_view_.A_T, + cusparse_view_.dual_solution, + reusable_device_scalar_value_0_.data(), + cusparse_view_.current_AtY, + CUSPARSE_SPMV_CSR_ALG2, + (f_t*)cusparse_view_.buffer_transpose.data(), + stream_view_)); + } } else { RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsespmm( handle_ptr_->get_cusparse_handle(), @@ -319,17 +353,33 @@ void pdhg_solver_t::compute_A_x() { // A @ x if (!batch_mode_) { - RAFT_CUSPARSE_TRY( - raft::sparse::detail::cusparsespmv(handle_ptr_->get_cusparse_handle(), - CUSPARSE_OPERATION_NON_TRANSPOSE, - reusable_device_scalar_value_1_.data(), - cusparse_view_.A, - cusparse_view_.reflected_primal_solution, - reusable_device_scalar_value_0_.data(), - cusparse_view_.dual_gradient, - CUSPARSE_SPMV_CSR_ALG2, - (f_t*)cusparse_view_.buffer_non_transpose.data(), - stream_view_)); + if constexpr (std::is_same_v) { + if (cusparse_view_.mixed_precision_enabled_) { + mixed_precision_spmv(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + reusable_device_scalar_value_1_.data(), + cusparse_view_.A_mixed_, + cusparse_view_.reflected_primal_solution, + reusable_device_scalar_value_0_.data(), + cusparse_view_.dual_gradient, + CUSPARSE_SPMV_CSR_ALG2, + cusparse_view_.buffer_non_transpose_mixed_.data(), + stream_view_); + } + } + if (!cusparse_view_.mixed_precision_enabled_) { + RAFT_CUSPARSE_TRY( + raft::sparse::detail::cusparsespmv(handle_ptr_->get_cusparse_handle(), + CUSPARSE_OPERATION_NON_TRANSPOSE, + reusable_device_scalar_value_1_.data(), + cusparse_view_.A, + cusparse_view_.reflected_primal_solution, + reusable_device_scalar_value_0_.data(), + cusparse_view_.dual_gradient, + CUSPARSE_SPMV_CSR_ALG2, + (f_t*)cusparse_view_.buffer_non_transpose.data(), + stream_view_)); + } } else { RAFT_CUSPARSE_TRY(raft::sparse::detail::cusparsespmm( handle_ptr_->get_cusparse_handle(), @@ -1196,7 +1246,7 @@ rmm::device_uvector& pdhg_solver_t::get_dual_solution() return current_saddle_point_state_.get_dual_solution(); } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class pdhg_solver_t; #endif #if MIP_INSTANTIATE_DOUBLE diff --git a/cpp/src/pdlp/pdhg.hpp b/cpp/src/pdlp/pdhg.hpp index 8ff45ac0ce..0a64e49efb 100644 --- a/cpp/src/pdlp/pdhg.hpp +++ b/cpp/src/pdlp/pdhg.hpp @@ -29,7 +29,8 @@ class pdhg_solver_t { bool is_legacy_batch_mode, const std::vector& climber_strategies, const pdlp_hyper_params::pdlp_hyper_params_t& hyper_params, - const std::vector>& new_bounds); + const std::vector>& new_bounds, + bool enable_mixed_precision_spmv = false); saddle_point_state_t& get_saddle_point_state(); cusparse_view_t& get_cusparse_view(); diff --git a/cpp/src/pdlp/pdlp.cu b/cpp/src/pdlp/pdlp.cu index cda60cf5ff..82e79098a7 100644 --- a/cpp/src/pdlp/pdlp.cu +++ b/cpp/src/pdlp/pdlp.cu @@ -43,6 +43,59 @@ namespace cuopt::linear_programming::detail { +// Templated wrapper for cuBLAS geam function +// cublasSgeam for float, cublasDgeam for double +template +inline cublasStatus_t cublasGeam(cublasHandle_t handle, + cublasOperation_t transa, + cublasOperation_t transb, + int m, + int n, + const T* alpha, + const T* A, + int lda, + const T* beta, + const T* B, + int ldb, + T* C, + int ldc); + +template <> +inline cublasStatus_t cublasGeam(cublasHandle_t handle, + cublasOperation_t transa, + cublasOperation_t transb, + int m, + int n, + const float* alpha, + const float* A, + int lda, + const float* beta, + const float* B, + int ldb, + float* C, + int ldc) +{ + return cublasSgeam(handle, transa, transb, m, n, alpha, A, lda, beta, B, ldb, C, ldc); +} + +template <> +inline cublasStatus_t cublasGeam(cublasHandle_t handle, + cublasOperation_t transa, + cublasOperation_t transb, + int m, + int n, + const double* alpha, + const double* A, + int lda, + const double* beta, + const double* B, + int ldb, + double* C, + int ldc) +{ + return cublasDgeam(handle, transa, transb, m, n, alpha, A, lda, beta, B, ldb, C, ldc); +} + template static size_t batch_size_handler(const problem_t& op_problem, const pdlp_solver_settings_t& settings) @@ -88,7 +141,8 @@ pdlp_solver_t::pdlp_solver_t(problem_t& op_problem, is_legacy_batch_mode, climber_strategies_, settings_.hyper_params, - settings_.new_bounds}, + settings_.new_bounds, + settings_.pdlp_precision == pdlp_precision_t::MixedPrecision}, initial_scaling_strategy_{handle_ptr_, op_problem_scaled_, settings_.hyper_params.default_l_inf_ruiz_iterations, @@ -1925,48 +1979,48 @@ void pdlp_solver_t::transpose_primal_dual_to_row( is_dual_slack_empty ? 0 : primal_size_h_ * climber_strategies_.size(), stream_view_); RAFT_CUBLAS_TRY(cublasSetStream(handle_ptr_->get_cublas_handle(), stream_view_)); - CUBLAS_CHECK(cublasDgeam(handle_ptr_->get_cublas_handle(), - CUBLAS_OP_T, - CUBLAS_OP_N, - climber_strategies_.size(), - primal_size_h_, - reusable_device_scalar_value_1_.data(), - primal_to_transpose.data(), - primal_size_h_, - reusable_device_scalar_value_0_.data(), - nullptr, - climber_strategies_.size(), - primal_transposed.data(), - climber_strategies_.size())); + CUBLAS_CHECK(cublasGeam(handle_ptr_->get_cublas_handle(), + CUBLAS_OP_T, + CUBLAS_OP_N, + climber_strategies_.size(), + primal_size_h_, + reusable_device_scalar_value_1_.data(), + primal_to_transpose.data(), + primal_size_h_, + reusable_device_scalar_value_0_.data(), + nullptr, + climber_strategies_.size(), + primal_transposed.data(), + climber_strategies_.size())); if (!is_dual_slack_empty) { - CUBLAS_CHECK(cublasDgeam(handle_ptr_->get_cublas_handle(), - CUBLAS_OP_T, - CUBLAS_OP_N, - climber_strategies_.size(), - primal_size_h_, - reusable_device_scalar_value_1_.data(), - dual_slack_to_transpose.data(), - primal_size_h_, - reusable_device_scalar_value_0_.data(), - nullptr, - climber_strategies_.size(), - dual_slack_transposed.data(), - climber_strategies_.size())); + CUBLAS_CHECK(cublasGeam(handle_ptr_->get_cublas_handle(), + CUBLAS_OP_T, + CUBLAS_OP_N, + climber_strategies_.size(), + primal_size_h_, + reusable_device_scalar_value_1_.data(), + dual_slack_to_transpose.data(), + primal_size_h_, + reusable_device_scalar_value_0_.data(), + nullptr, + climber_strategies_.size(), + dual_slack_transposed.data(), + climber_strategies_.size())); } - CUBLAS_CHECK(cublasDgeam(handle_ptr_->get_cublas_handle(), - CUBLAS_OP_T, - CUBLAS_OP_N, - climber_strategies_.size(), - dual_size_h_, - reusable_device_scalar_value_1_.data(), - dual_to_transpose.data(), - dual_size_h_, - reusable_device_scalar_value_0_.data(), - nullptr, - climber_strategies_.size(), - dual_transposed.data(), - climber_strategies_.size())); + CUBLAS_CHECK(cublasGeam(handle_ptr_->get_cublas_handle(), + CUBLAS_OP_T, + CUBLAS_OP_N, + climber_strategies_.size(), + dual_size_h_, + reusable_device_scalar_value_1_.data(), + dual_to_transpose.data(), + dual_size_h_, + reusable_device_scalar_value_0_.data(), + nullptr, + climber_strategies_.size(), + dual_transposed.data(), + climber_strategies_.size())); // Copy that holds the tranpose to the original vector raft::copy(primal_to_transpose.data(), @@ -2002,49 +2056,49 @@ void pdlp_solver_t::transpose_primal_dual_back_to_col( is_dual_slack_empty ? 0 : primal_size_h_ * climber_strategies_.size(), stream_view_); RAFT_CUBLAS_TRY(cublasSetStream(handle_ptr_->get_cublas_handle(), stream_view_)); - CUBLAS_CHECK(cublasDgeam(handle_ptr_->get_cublas_handle(), - CUBLAS_OP_T, - CUBLAS_OP_N, - primal_size_h_, - climber_strategies_.size(), - reusable_device_scalar_value_1_.data(), - primal_to_transpose.data(), - climber_strategies_.size(), - reusable_device_scalar_value_0_.data(), - nullptr, - primal_size_h_, - primal_transposed.data(), - primal_size_h_)); + CUBLAS_CHECK(cublasGeam(handle_ptr_->get_cublas_handle(), + CUBLAS_OP_T, + CUBLAS_OP_N, + primal_size_h_, + climber_strategies_.size(), + reusable_device_scalar_value_1_.data(), + primal_to_transpose.data(), + climber_strategies_.size(), + reusable_device_scalar_value_0_.data(), + nullptr, + primal_size_h_, + primal_transposed.data(), + primal_size_h_)); if (!is_dual_slack_empty) { - CUBLAS_CHECK(cublasDgeam(handle_ptr_->get_cublas_handle(), - CUBLAS_OP_T, - CUBLAS_OP_N, - primal_size_h_, - climber_strategies_.size(), - reusable_device_scalar_value_1_.data(), - dual_slack_to_transpose.data(), - climber_strategies_.size(), - reusable_device_scalar_value_0_.data(), - nullptr, - primal_size_h_, - dual_slack_transposed.data(), - primal_size_h_)); + CUBLAS_CHECK(cublasGeam(handle_ptr_->get_cublas_handle(), + CUBLAS_OP_T, + CUBLAS_OP_N, + primal_size_h_, + climber_strategies_.size(), + reusable_device_scalar_value_1_.data(), + dual_slack_to_transpose.data(), + climber_strategies_.size(), + reusable_device_scalar_value_0_.data(), + nullptr, + primal_size_h_, + dual_slack_transposed.data(), + primal_size_h_)); } - CUBLAS_CHECK(cublasDgeam(handle_ptr_->get_cublas_handle(), - CUBLAS_OP_T, - CUBLAS_OP_N, - dual_size_h_, - climber_strategies_.size(), - reusable_device_scalar_value_1_.data(), - dual_to_transpose.data(), - climber_strategies_.size(), - reusable_device_scalar_value_0_.data(), - nullptr, - dual_size_h_, - dual_transposed.data(), - dual_size_h_)); + CUBLAS_CHECK(cublasGeam(handle_ptr_->get_cublas_handle(), + CUBLAS_OP_T, + CUBLAS_OP_N, + dual_size_h_, + climber_strategies_.size(), + reusable_device_scalar_value_1_.data(), + dual_to_transpose.data(), + climber_strategies_.size(), + reusable_device_scalar_value_0_.data(), + nullptr, + dual_size_h_, + dual_transposed.data(), + dual_size_h_)); // Copy that holds the tranpose to the original vector raft::copy(primal_to_transpose.data(), @@ -2090,6 +2144,9 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co initial_scaling_strategy_.scale_problem(); + // Update FP32 matrix copies for mixed precision SpMV after scaling + pdhg_solver_.get_cusparse_view().update_mixed_precision_matrices(); + if (!settings_.hyper_params.compute_initial_step_size_before_scaling && !settings_.get_initial_step_size().has_value()) compute_initial_step_size(); @@ -2914,7 +2971,7 @@ pdlp_solver_t::get_current_termination_strategy() return current_termination_strategy_; } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class pdlp_solver_t; template __global__ void compute_weights_initial_primal_weight_from_squared_norms( diff --git a/cpp/src/pdlp/pdlp_warm_start_data.cu b/cpp/src/pdlp/pdlp_warm_start_data.cu index 66bfe66914..80abf015d8 100644 --- a/cpp/src/pdlp/pdlp_warm_start_data.cu +++ b/cpp/src/pdlp/pdlp_warm_start_data.cu @@ -178,7 +178,7 @@ void pdlp_warm_start_data_t::check_sizes() "All dual vectors should be of same size"); } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class pdlp_warm_start_data_t; #endif diff --git a/cpp/src/pdlp/restart_strategy/localized_duality_gap_container.cu b/cpp/src/pdlp/restart_strategy/localized_duality_gap_container.cu index 2f2e2c7333..bb79e5b6e6 100644 --- a/cpp/src/pdlp/restart_strategy/localized_duality_gap_container.cu +++ b/cpp/src/pdlp/restart_strategy/localized_duality_gap_container.cu @@ -144,7 +144,7 @@ localized_duality_gap_container_t::view() return v; } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template struct localized_duality_gap_container_t; #endif #if MIP_INSTANTIATE_DOUBLE diff --git a/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu b/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu index 8eacd4d246..149e99a431 100644 --- a/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu +++ b/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu @@ -2523,7 +2523,7 @@ bool pdlp_restart_strategy_t::get_last_restart_was_average() const const typename localized_duality_gap_container_t::view_t duality_gap_view, \ F_TYPE* primal_product); -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT INSTANTIATE(float) #endif diff --git a/cpp/src/pdlp/restart_strategy/weighted_average_solution.cu b/cpp/src/pdlp/restart_strategy/weighted_average_solution.cu index 03c76d79ae..70a448a9de 100644 --- a/cpp/src/pdlp/restart_strategy/weighted_average_solution.cu +++ b/cpp/src/pdlp/restart_strategy/weighted_average_solution.cu @@ -139,7 +139,7 @@ i_t weighted_average_solution_t::get_iterations_since_last_restart() c return iterations_since_last_restart_; } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template __global__ void add_weight_sums(const float* primal_weight, const float* dual_weight, float* sum_primal_solution_weights, diff --git a/cpp/src/pdlp/saddle_point.cu b/cpp/src/pdlp/saddle_point.cu index c516ab7355..157e7fa389 100644 --- a/cpp/src/pdlp/saddle_point.cu +++ b/cpp/src/pdlp/saddle_point.cu @@ -166,7 +166,7 @@ rmm::device_uvector& saddle_point_state_t::get_next_AtY() return next_AtY_; } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class saddle_point_state_t; #endif diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 5e1e25bbee..22fff31906 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -60,6 +60,10 @@ namespace cuopt::linear_programming { +template +extern rmm::device_uvector gpu_cast(const rmm::device_uvector& src, + rmm::cuda_stream_view stream); + // This serves as both a warm up but also a mandatory initial call to setup cuSparse and cuBLAS static void init_handler(const raft::handle_t* handle_ptr) { @@ -560,6 +564,122 @@ optimization_problem_solution_t run_dual_simplex( 0); } +#if PDLP_INSTANTIATE_FLOAT || CUOPT_INSTANTIATE_FLOAT + +template +static optimization_problem_solution_t run_pdlp_solver_in_fp32( + detail::problem_t& problem, + pdlp_solver_settings_t const& settings, + const timer_t& timer, + bool is_batch_mode) +{ + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "Running PDLP in FP32 precision"); + auto stream = problem.handle_ptr->get_stream(); + + // Convert the optimization problem stored inside problem_t to float + auto float_op = problem.original_problem_ptr->template convert_to_other_prec(stream); + float_op.set_objective_offset(static_cast(problem.presolve_data.objective_offset)); + float_op.set_objective_scaling_factor( + static_cast(problem.presolve_data.objective_scaling_factor)); + + detail::problem_t float_problem(float_op); + + auto objective_name = problem.objective_name; + auto var_names = problem.var_names; + auto row_names = problem.row_names; + // When crossover is off, free double-precision GPU memory to reduce peak usage. + // When crossover is on, run_pdlp needs the problem data after we return. + if (!settings.crossover) { + { + [[maybe_unused]] auto discard = detail::problem_t(std::move(problem)); + } + } + + // Create float settings from double settings + pdlp_solver_settings_t fs; + fs.tolerances.absolute_dual_tolerance = + static_cast(settings.tolerances.absolute_dual_tolerance); + fs.tolerances.relative_dual_tolerance = + static_cast(settings.tolerances.relative_dual_tolerance); + fs.tolerances.absolute_primal_tolerance = + static_cast(settings.tolerances.absolute_primal_tolerance); + fs.tolerances.relative_primal_tolerance = + static_cast(settings.tolerances.relative_primal_tolerance); + fs.tolerances.absolute_gap_tolerance = + static_cast(settings.tolerances.absolute_gap_tolerance); + fs.tolerances.relative_gap_tolerance = + static_cast(settings.tolerances.relative_gap_tolerance); + fs.tolerances.primal_infeasible_tolerance = + static_cast(settings.tolerances.primal_infeasible_tolerance); + fs.tolerances.dual_infeasible_tolerance = + static_cast(settings.tolerances.dual_infeasible_tolerance); + fs.detect_infeasibility = settings.detect_infeasibility; + fs.strict_infeasibility = settings.strict_infeasibility; + fs.iteration_limit = settings.iteration_limit; + fs.time_limit = static_cast(settings.time_limit); + fs.pdlp_solver_mode = settings.pdlp_solver_mode; + fs.log_to_console = settings.log_to_console; + fs.log_file = settings.log_file; + fs.per_constraint_residual = settings.per_constraint_residual; + fs.save_best_primal_so_far = settings.save_best_primal_so_far; + fs.first_primal_feasible = settings.first_primal_feasible; + fs.eliminate_dense_columns = settings.eliminate_dense_columns; + fs.pdlp_precision = pdlp_precision_t::DefaultPrecision; + fs.method = method_t::PDLP; + fs.inside_mip = settings.inside_mip; + fs.hyper_params = settings.hyper_params; + fs.presolver = settings.presolver; + fs.num_gpus = settings.num_gpus; + fs.concurrent_halt = settings.concurrent_halt; + + detail::pdlp_solver_t solver(float_problem, fs, is_batch_mode); + if (settings.inside_mip) { solver.set_inside_mip(true); } + auto float_sol = solver.run_solver(timer); + + // Convert float solution back to double on GPU (gpu_cast defined in optimization_problem.cu) + auto dev_primal = gpu_cast(float_sol.get_primal_solution(), stream); + auto dev_dual = gpu_cast(float_sol.get_dual_solution(), stream); + auto dev_reduced = gpu_cast(float_sol.get_reduced_cost(), stream); + + // Convert termination info (small host-side struct, stays on CPU) + auto float_term_infos = float_sol.get_additional_termination_informations(); + using double_term_info_t = + typename optimization_problem_solution_t::additional_termination_information_t; + std::vector term_infos; + for (auto& fi : float_term_infos) { + double_term_info_t di; + di.number_of_steps_taken = fi.number_of_steps_taken; + di.total_number_of_attempted_steps = fi.total_number_of_attempted_steps; + di.l2_primal_residual = static_cast(fi.l2_primal_residual); + di.l2_relative_primal_residual = static_cast(fi.l2_relative_primal_residual); + di.l2_dual_residual = static_cast(fi.l2_dual_residual); + di.l2_relative_dual_residual = static_cast(fi.l2_relative_dual_residual); + di.primal_objective = static_cast(fi.primal_objective); + di.dual_objective = static_cast(fi.dual_objective); + di.gap = static_cast(fi.gap); + di.relative_gap = static_cast(fi.relative_gap); + di.max_primal_ray_infeasibility = static_cast(fi.max_primal_ray_infeasibility); + di.primal_ray_linear_objective = static_cast(fi.primal_ray_linear_objective); + di.max_dual_ray_infeasibility = static_cast(fi.max_dual_ray_infeasibility); + di.dual_ray_linear_objective = static_cast(fi.dual_ray_linear_objective); + di.solve_time = fi.solve_time; + di.solved_by_pdlp = fi.solved_by_pdlp; + term_infos.push_back(di); + } + + auto status_vec = float_sol.get_terminations_status(); + + return optimization_problem_solution_t(dev_primal, + dev_dual, + dev_reduced, + objective_name, + var_names, + row_names, + std::move(term_infos), + std::move(status_vec)); +} +#endif + template static optimization_problem_solution_t run_pdlp_solver( detail::problem_t& problem, @@ -574,6 +694,13 @@ static optimization_problem_solution_t run_pdlp_solver( return optimization_problem_solution_t{pdlp_termination_status_t::NumericalError, problem.handle_ptr->get_stream()}; } +#if PDLP_INSTANTIATE_FLOAT || CUOPT_INSTANTIATE_FLOAT + if constexpr (std::is_same_v) { + if (settings.pdlp_precision == pdlp_precision_t::SinglePrecision) { + return run_pdlp_solver_in_fp32(problem, settings, timer, is_batch_mode); + } + } +#endif detail::pdlp_solver_t solver(problem, settings, is_batch_mode); if (settings.inside_mip) { solver.set_inside_mip(true); } return solver.run_solver(timer); @@ -585,6 +712,24 @@ optimization_problem_solution_t run_pdlp(detail::problem_t& const timer_t& timer, bool is_batch_mode) { + if constexpr (!std::is_same_v) { + cuopt_expects(!is_batch_mode, + error_type_t::ValidationError, + "PDLP batch mode is not supported for float precision. Use double precision."); + } + cuopt_expects(!(settings.pdlp_precision == pdlp_precision_t::MixedPrecision && + !detail::is_cusparse_runtime_mixed_precision_supported()), + error_type_t::ValidationError, + "Mixed-precision SpMV requires cuSPARSE runtime 12.5 or later."); + cuopt_expects( + !(is_batch_mode && settings.pdlp_precision == pdlp_precision_t::MixedPrecision), + error_type_t::ValidationError, + "Mixed-precision SpMV is not supported in batch mode. Set pdlp_precision=-1 (default) " + "or disable batch mode."); + cuopt_expects(!(settings.pdlp_precision == pdlp_precision_t::SinglePrecision && is_batch_mode), + error_type_t::ValidationError, + "Single-precision PDLP is not supported in batch mode."); + auto start_solver = std::chrono::high_resolution_clock::now(); timer_t timer_pdlp(timer.remaining_time()); auto sol = run_pdlp_solver(problem, settings, timer, is_batch_mode); @@ -606,82 +751,89 @@ optimization_problem_solution_t run_pdlp(detail::problem_t& sol.get_solve_time()); } - const bool do_crossover = settings.crossover; - i_t crossover_info = 0; - if (do_crossover && sol.get_termination_status() == pdlp_termination_status_t::Optimal) { - crossover_info = -1; - - dual_simplex::lp_problem_t lp(problem.handle_ptr, 1, 1, 1); - dual_simplex::lp_solution_t initial_solution(1, 1); - translate_to_crossover_problem(problem, sol, lp, initial_solution); - dual_simplex::simplex_solver_settings_t dual_simplex_settings; - dual_simplex_settings.time_limit = settings.time_limit; - dual_simplex_settings.iteration_limit = settings.iteration_limit; - dual_simplex_settings.concurrent_halt = settings.concurrent_halt; - dual_simplex::lp_solution_t vertex_solution(lp.num_rows, lp.num_cols); - std::vector vstatus(lp.num_cols); - dual_simplex::crossover_status_t crossover_status = dual_simplex::crossover( - lp, dual_simplex_settings, initial_solution, timer.get_tic_start(), vertex_solution, vstatus); - pdlp_termination_status_t termination_status = pdlp_termination_status_t::TimeLimit; - auto to_termination_status = [](dual_simplex::crossover_status_t status) { - switch (status) { - case dual_simplex::crossover_status_t::OPTIMAL: return pdlp_termination_status_t::Optimal; - case dual_simplex::crossover_status_t::PRIMAL_FEASIBLE: - return pdlp_termination_status_t::PrimalFeasible; - case dual_simplex::crossover_status_t::DUAL_FEASIBLE: - return pdlp_termination_status_t::NumericalError; - case dual_simplex::crossover_status_t::NUMERICAL_ISSUES: - return pdlp_termination_status_t::NumericalError; - case dual_simplex::crossover_status_t::CONCURRENT_LIMIT: - return pdlp_termination_status_t::ConcurrentLimit; - case dual_simplex::crossover_status_t::TIME_LIMIT: - return pdlp_termination_status_t::TimeLimit; - default: return pdlp_termination_status_t::NumericalError; - } - }; - termination_status = to_termination_status(crossover_status); - if (crossover_status == dual_simplex::crossover_status_t::OPTIMAL) { crossover_info = 0; } - rmm::device_uvector final_primal_solution = - cuopt::device_copy(vertex_solution.x, problem.handle_ptr->get_stream()); - rmm::device_uvector final_dual_solution = - cuopt::device_copy(vertex_solution.y, problem.handle_ptr->get_stream()); - rmm::device_uvector final_reduced_cost = - cuopt::device_copy(vertex_solution.z, problem.handle_ptr->get_stream()); - problem.handle_ptr->sync_stream(); - // Negate dual variables and reduced costs for maximization problems - if (problem.maximize) { - adjust_dual_solution_and_reduced_cost( - final_dual_solution, final_reduced_cost, problem.handle_ptr->get_stream()); + if constexpr (std::is_same_v) { + const bool do_crossover = settings.crossover; + i_t crossover_info = 0; + if (do_crossover && sol.get_termination_status() == pdlp_termination_status_t::Optimal) { + crossover_info = -1; + + dual_simplex::lp_problem_t lp(problem.handle_ptr, 1, 1, 1); + dual_simplex::lp_solution_t initial_solution(1, 1); + translate_to_crossover_problem(problem, sol, lp, initial_solution); + dual_simplex::simplex_solver_settings_t dual_simplex_settings; + dual_simplex_settings.time_limit = settings.time_limit; + dual_simplex_settings.iteration_limit = settings.iteration_limit; + dual_simplex_settings.concurrent_halt = settings.concurrent_halt; + dual_simplex::lp_solution_t vertex_solution(lp.num_rows, lp.num_cols); + std::vector vstatus(lp.num_cols); + dual_simplex::crossover_status_t crossover_status = + dual_simplex::crossover(lp, + dual_simplex_settings, + initial_solution, + timer.get_tic_start(), + vertex_solution, + vstatus); + pdlp_termination_status_t termination_status = pdlp_termination_status_t::TimeLimit; + auto to_termination_status = [](dual_simplex::crossover_status_t status) { + switch (status) { + case dual_simplex::crossover_status_t::OPTIMAL: return pdlp_termination_status_t::Optimal; + case dual_simplex::crossover_status_t::PRIMAL_FEASIBLE: + return pdlp_termination_status_t::PrimalFeasible; + case dual_simplex::crossover_status_t::DUAL_FEASIBLE: + return pdlp_termination_status_t::NumericalError; + case dual_simplex::crossover_status_t::NUMERICAL_ISSUES: + return pdlp_termination_status_t::NumericalError; + case dual_simplex::crossover_status_t::CONCURRENT_LIMIT: + return pdlp_termination_status_t::ConcurrentLimit; + case dual_simplex::crossover_status_t::TIME_LIMIT: + return pdlp_termination_status_t::TimeLimit; + default: return pdlp_termination_status_t::NumericalError; + } + }; + termination_status = to_termination_status(crossover_status); + if (crossover_status == dual_simplex::crossover_status_t::OPTIMAL) { crossover_info = 0; } + rmm::device_uvector final_primal_solution = + cuopt::device_copy(vertex_solution.x, problem.handle_ptr->get_stream()); + rmm::device_uvector final_dual_solution = + cuopt::device_copy(vertex_solution.y, problem.handle_ptr->get_stream()); + rmm::device_uvector final_reduced_cost = + cuopt::device_copy(vertex_solution.z, problem.handle_ptr->get_stream()); problem.handle_ptr->sync_stream(); - } + // Negate dual variables and reduced costs for maximization problems + if (problem.maximize) { + adjust_dual_solution_and_reduced_cost( + final_dual_solution, final_reduced_cost, problem.handle_ptr->get_stream()); + problem.handle_ptr->sync_stream(); + } - // Should be filled with more information from dual simplex - std::vector< - typename optimization_problem_solution_t::additional_termination_information_t> - info(1); - info[0].primal_objective = vertex_solution.user_objective; - info[0].number_of_steps_taken = vertex_solution.iterations; - auto crossover_end = std::chrono::high_resolution_clock::now(); - auto crossover_duration = - std::chrono::duration_cast(crossover_end - start_solver); - info[0].solve_time = crossover_duration.count() / 1000.0; - auto sol_crossover = optimization_problem_solution_t(final_primal_solution, - final_dual_solution, - final_reduced_cost, - problem.objective_name, - problem.var_names, - problem.row_names, - std::move(info), - {termination_status}); - sol.copy_from(problem.handle_ptr, sol_crossover); - CUOPT_LOG_CONDITIONAL_INFO( - !settings.inside_mip, "Crossover status %s", sol.get_termination_status_string().c_str()); - } - if (settings.method == method_t::Concurrent && settings.concurrent_halt != nullptr && - crossover_info == 0 && sol.get_termination_status() == pdlp_termination_status_t::Optimal) { - // We finished. Tell dual simplex to stop if it is still running. - CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "PDLP finished. Telling others to stop"); - *settings.concurrent_halt = 1; + // Should be filled with more information from dual simplex + std::vector< + typename optimization_problem_solution_t::additional_termination_information_t> + info(1); + info[0].primal_objective = vertex_solution.user_objective; + info[0].number_of_steps_taken = vertex_solution.iterations; + auto crossover_end = std::chrono::high_resolution_clock::now(); + auto crossover_duration = + std::chrono::duration_cast(crossover_end - start_solver); + info[0].solve_time = crossover_duration.count() / 1000.0; + auto sol_crossover = optimization_problem_solution_t(final_primal_solution, + final_dual_solution, + final_reduced_cost, + problem.objective_name, + problem.var_names, + problem.row_names, + std::move(info), + {termination_status}); + sol.copy_from(problem.handle_ptr, sol_crossover); + CUOPT_LOG_CONDITIONAL_INFO( + !settings.inside_mip, "Crossover status %s", sol.get_termination_status_string().c_str()); + } + if (settings.method == method_t::Concurrent && settings.concurrent_halt != nullptr && + crossover_info == 0 && sol.get_termination_status() == pdlp_termination_status_t::Optimal) { + // We finished. Tell dual simplex to stop if it is still running. + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "PDLP finished. Telling others to stop"); + *settings.concurrent_halt = 1; + } } return sol; } @@ -1117,13 +1269,22 @@ optimization_problem_solution_t solve_lp_with_method( const timer_t& timer, bool is_batch_mode) { - if (settings.method == method_t::DualSimplex) { - return run_dual_simplex(problem, settings, timer); - } else if (settings.method == method_t::Barrier) { - return run_barrier(problem, settings, timer); - } else if (settings.method == method_t::Concurrent) { - return run_concurrent(problem, settings, timer, is_batch_mode); + if constexpr (std::is_same_v) { + if (settings.method == method_t::DualSimplex) { + return run_dual_simplex(problem, settings, timer); + } else if (settings.method == method_t::Barrier) { + return run_barrier(problem, settings, timer); + } else if (settings.method == method_t::Concurrent) { + return run_concurrent(problem, settings, timer, is_batch_mode); + } else { + return run_pdlp(problem, settings, timer, is_batch_mode); + } } else { + // Float precision only supports PDLP without presolve/crossover + cuopt_expects(settings.method == method_t::PDLP, + error_type_t::ValidationError, + "Float precision only supports PDLP method. DualSimplex, Barrier, and Concurrent " + "require double precision."); return run_pdlp(problem, settings, timer, is_batch_mode); } } @@ -1199,9 +1360,6 @@ optimization_problem_solution_t solve_lp( std::unique_ptr> presolver; auto run_presolve = settings.presolver != presolver_t::None; run_presolve = run_presolve && settings.get_pdlp_warm_start_data().total_pdlp_iterations_ == -1; - if (!run_presolve && !settings_const.inside_mip) { - CUOPT_LOG_INFO("Third-party presolve is disabled, skipping"); - } // Declare result at outer scope so that result->reduced_problem (which may be // referenced by problem.original_problem_ptr) remains alive through the solve. diff --git a/cpp/src/pdlp/solver_settings.cu b/cpp/src/pdlp/solver_settings.cu index 560e40f302..7acfc7481c 100644 --- a/cpp/src/pdlp/solver_settings.cu +++ b/cpp/src/pdlp/solver_settings.cu @@ -382,7 +382,7 @@ pdlp_solver_settings_t::get_pdlp_warm_start_data_view() const noexcept return pdlp_warm_start_data_view_; } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class pdlp_solver_settings_t; #endif diff --git a/cpp/src/pdlp/solver_solution.cu b/cpp/src/pdlp/solver_solution.cu index a8001b91c1..10e6a80593 100644 --- a/cpp/src/pdlp/solver_solution.cu +++ b/cpp/src/pdlp/solver_solution.cu @@ -448,7 +448,7 @@ void optimization_problem_solution_t::write_to_sol_file( std::string(filename), status, objective_value, var_names_, solution); } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class optimization_problem_solution_t; #endif diff --git a/cpp/src/pdlp/step_size_strategy/adaptive_step_size_strategy.cu b/cpp/src/pdlp/step_size_strategy/adaptive_step_size_strategy.cu index 24ef29b243..d17a88dd29 100644 --- a/cpp/src/pdlp/step_size_strategy/adaptive_step_size_strategy.cu +++ b/cpp/src/pdlp/step_size_strategy/adaptive_step_size_strategy.cu @@ -578,7 +578,7 @@ adaptive_step_size_strategy_t::view() F_TYPE * dual_step_size, \ int* pdhg_iteration); -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT INSTANTIATE(float) #endif diff --git a/cpp/src/pdlp/termination_strategy/convergence_information.cu b/cpp/src/pdlp/termination_strategy/convergence_information.cu index 9b01608b47..ab0c921cc7 100644 --- a/cpp/src/pdlp/termination_strategy/convergence_information.cu +++ b/cpp/src/pdlp/termination_strategy/convergence_information.cu @@ -996,7 +996,7 @@ convergence_information_t::to_primal_quality_adapter( primal_objective_.element(0, stream_view_)}; } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class convergence_information_t; template __global__ void compute_remaining_stats_kernel( diff --git a/cpp/src/pdlp/termination_strategy/infeasibility_information.cu b/cpp/src/pdlp/termination_strategy/infeasibility_information.cu index 14114d306f..dbb35b732d 100644 --- a/cpp/src/pdlp/termination_strategy/infeasibility_information.cu +++ b/cpp/src/pdlp/termination_strategy/infeasibility_information.cu @@ -745,7 +745,7 @@ typename infeasibility_information_t::view_t infeasibility_information return v; } -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class infeasibility_information_t; template __global__ void compute_remaining_stats_kernel( diff --git a/cpp/src/pdlp/termination_strategy/termination_strategy.cu b/cpp/src/pdlp/termination_strategy/termination_strategy.cu index 033cbdbfda..7179df6a49 100644 --- a/cpp/src/pdlp/termination_strategy/termination_strategy.cu +++ b/cpp/src/pdlp/termination_strategy/termination_strategy.cu @@ -681,7 +681,7 @@ void pdlp_termination_strategy_t::print_termination_criteria(i_t itera bool per_constraint_residual, \ int batch_size); -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT INSTANTIATE(float) #endif diff --git a/cpp/src/pdlp/translate.hpp b/cpp/src/pdlp/translate.hpp index b8e0075733..b143a206d4 100644 --- a/cpp/src/pdlp/translate.hpp +++ b/cpp/src/pdlp/translate.hpp @@ -133,7 +133,7 @@ void translate_to_crossover_problem(const detail::problem_t& problem, std::vector slack(problem.n_constraints); std::vector tmp_x = cuopt::host_copy(sol.get_primal_solution(), stream); stream.synchronize(); - dual_simplex::matrix_vector_multiply(lp.A, 1.0, tmp_x, 0.0, slack); + dual_simplex::matrix_vector_multiply(lp.A, f_t(1.0), tmp_x, f_t(0.0), slack); CUOPT_LOG_DEBUG("Multiplied A and x"); lp.A.col_start.resize(problem.n_variables + problem.n_constraints + 1); diff --git a/cpp/src/pdlp/utilities/problem_checking.cu b/cpp/src/pdlp/utilities/problem_checking.cu index f970f8740d..b10850de27 100644 --- a/cpp/src/pdlp/utilities/problem_checking.cu +++ b/cpp/src/pdlp/utilities/problem_checking.cu @@ -340,7 +340,7 @@ bool problem_checking_t::has_crossing_bounds( #define INSTANTIATE(F_TYPE) template class problem_checking_t; -#if MIP_INSTANTIATE_FLOAT +#if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT INSTANTIATE(float) #endif diff --git a/cpp/tests/linear_programming/c_api_tests/c_api_test.c b/cpp/tests/linear_programming/c_api_tests/c_api_test.c index ecf610041c..996d60deae 100644 --- a/cpp/tests/linear_programming/c_api_tests/c_api_test.c +++ b/cpp/tests/linear_programming/c_api_tests/c_api_test.c @@ -2122,3 +2122,135 @@ cuopt_int_t test_cpu_only_mip_execution(const char* filename) cuOptDestroySolution(&solution); return status; } + +cuopt_int_t test_pdlp_precision_mixed(const char* filename, + cuopt_int_t* termination_status_ptr, + cuopt_float_t* objective_ptr) +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + cuopt_int_t status; + cuopt_int_t termination_status = -1; + cuopt_float_t objective_value; + + status = cuOptReadProblem(filename, &problem); + if (status != CUOPT_SUCCESS) { + printf("Error reading problem\n"); + goto DONE; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings\n"); + goto DONE; + } + + status = cuOptSetIntegerParameter(settings, CUOPT_METHOD, CUOPT_METHOD_PDLP); + if (status != CUOPT_SUCCESS) { + printf("Error setting method\n"); + goto DONE; + } + + status = cuOptSetIntegerParameter(settings, CUOPT_PDLP_PRECISION, CUOPT_PDLP_MIXED_PRECISION); + if (status != CUOPT_SUCCESS) { + printf("Error setting pdlp_precision\n"); + goto DONE; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem with pdlp_precision=mixed\n"); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status\n"); + goto DONE; + } + *termination_status_ptr = termination_status; + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value\n"); + goto DONE; + } + *objective_ptr = objective_value; + + printf("PDLP precision=mixed test passed: status=%s, objective=%f\n", + termination_status_to_string(termination_status), + objective_value); + +DONE: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return status; +} + +cuopt_int_t test_pdlp_precision_single(const char* filename, + cuopt_int_t* termination_status_ptr, + cuopt_float_t* objective_ptr) +{ + cuOptOptimizationProblem problem = NULL; + cuOptSolverSettings settings = NULL; + cuOptSolution solution = NULL; + cuopt_int_t status; + cuopt_int_t termination_status = -1; + cuopt_float_t objective_value; + + status = cuOptReadProblem(filename, &problem); + if (status != CUOPT_SUCCESS) { + printf("Error reading problem\n"); + goto DONE; + } + + status = cuOptCreateSolverSettings(&settings); + if (status != CUOPT_SUCCESS) { + printf("Error creating solver settings\n"); + goto DONE; + } + + status = cuOptSetIntegerParameter(settings, CUOPT_METHOD, CUOPT_METHOD_PDLP); + if (status != CUOPT_SUCCESS) { + printf("Error setting method\n"); + goto DONE; + } + + status = cuOptSetIntegerParameter(settings, CUOPT_PDLP_PRECISION, CUOPT_PDLP_SINGLE_PRECISION); + if (status != CUOPT_SUCCESS) { + printf("Error setting pdlp_precision\n"); + goto DONE; + } + + status = cuOptSolve(problem, settings, &solution); + if (status != CUOPT_SUCCESS) { + printf("Error solving problem with pdlp_precision=single\n"); + goto DONE; + } + + status = cuOptGetTerminationStatus(solution, &termination_status); + if (status != CUOPT_SUCCESS) { + printf("Error getting termination status\n"); + goto DONE; + } + *termination_status_ptr = termination_status; + + status = cuOptGetObjectiveValue(solution, &objective_value); + if (status != CUOPT_SUCCESS) { + printf("Error getting objective value\n"); + goto DONE; + } + *objective_ptr = objective_value; + + printf("PDLP precision=single test passed: status=%s, objective=%f\n", + termination_status_to_string(termination_status), + objective_value); + +DONE: + cuOptDestroyProblem(&problem); + cuOptDestroySolverSettings(&settings); + cuOptDestroySolution(&solution); + return status; +} diff --git a/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp b/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp index 33fb42cc9d..d39a970763 100644 --- a/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp +++ b/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp @@ -18,6 +18,10 @@ #include #include +namespace cuopt::linear_programming::detail { +bool is_cusparse_runtime_mixed_precision_supported(); +} + #include TEST(c_api, int_size) { EXPECT_EQ(test_int_size(), sizeof(int32_t)); } @@ -271,6 +275,43 @@ INSTANTIATE_TEST_SUITE_P(c_api, // Different instance std::make_tuple("/mip/bb_optimality.mps", 8, 60.0, 2))); +// ============================================================================= +// PDLP Precision Tests +// ============================================================================= + +TEST(c_api, pdlp_precision_single) +{ + const std::string& rapidsDatasetRootDir = cuopt::test::get_rapids_dataset_root_dir(); + std::string filename = rapidsDatasetRootDir + "/linear_programming/afiro_original.mps"; + cuopt_int_t termination_status; + cuopt_float_t objective; + EXPECT_EQ(test_pdlp_precision_single(filename.c_str(), &termination_status, &objective), + CUOPT_SUCCESS); + EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_NEAR(objective, -464.7531, 1e-1); +} + +TEST(c_api, pdlp_precision_mixed) +{ + using namespace cuopt::linear_programming::detail; + const std::string& rapidsDatasetRootDir = cuopt::test::get_rapids_dataset_root_dir(); + std::string filename = rapidsDatasetRootDir + "/linear_programming/afiro_original.mps"; + cuopt_int_t termination_status = -1; + cuopt_float_t objective; + if (!is_cusparse_runtime_mixed_precision_supported()) { + auto status = test_pdlp_precision_mixed(filename.c_str(), &termination_status, &objective); + bool solve_returned_error = (status != CUOPT_SUCCESS); + bool solve_returned_non_optimal = + (status == CUOPT_SUCCESS && termination_status != CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_TRUE(solve_returned_error || solve_returned_non_optimal); + return; + } + EXPECT_EQ(test_pdlp_precision_mixed(filename.c_str(), &termination_status, &objective), + CUOPT_SUCCESS); + EXPECT_EQ(termination_status, CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_NEAR(objective, -464.7531, 1e-1); +} + // ============================================================================= // Solution Interface Polymorphism Tests // ============================================================================= diff --git a/cpp/tests/linear_programming/c_api_tests/c_api_tests.h b/cpp/tests/linear_programming/c_api_tests/c_api_tests.h index e541316567..402c7d06a5 100644 --- a/cpp/tests/linear_programming/c_api_tests/c_api_tests.h +++ b/cpp/tests/linear_programming/c_api_tests/c_api_tests.h @@ -53,6 +53,14 @@ cuopt_int_t test_deterministic_bb(const char* filename, cuopt_int_t test_lp_solution_mip_methods(); cuopt_int_t test_mip_solution_lp_methods(); +cuopt_int_t test_pdlp_precision_single(const char* filename, + cuopt_int_t* termination_status_ptr, + cuopt_float_t* objective_ptr); + +cuopt_int_t test_pdlp_precision_mixed(const char* filename, + cuopt_int_t* termination_status_ptr, + cuopt_float_t* objective_ptr); + /* CPU-only execution tests (require env vars CUDA_VISIBLE_DEVICES="" and CUOPT_REMOTE_HOST) */ cuopt_int_t test_cpu_only_execution(const char* filename); cuopt_int_t test_cpu_only_mip_execution(const char* filename); diff --git a/cpp/tests/linear_programming/pdlp_test.cu b/cpp/tests/linear_programming/pdlp_test.cu index 8bf759367e..d5a8d69008 100644 --- a/cpp/tests/linear_programming/pdlp_test.cu +++ b/cpp/tests/linear_programming/pdlp_test.cu @@ -6,6 +6,7 @@ /* clang-format on */ #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -45,10 +47,10 @@ namespace cuopt::linear_programming::test { -constexpr double afiro_primal_objective = -464; - +constexpr double afiro_primal_objective = -464.0; // Accept a 1% error -static bool is_incorrect_objective(double reference, double objective) +template +static bool is_incorrect_objective(f_t reference, f_t objective) { if (reference == 0) { return std::abs(objective) > 0.01; } if (objective == 0) { return std::abs(reference) > 0.01; } @@ -73,6 +75,58 @@ TEST(pdlp_class, run_double) afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); } +TEST(pdlp_class, precision_mixed) +{ + using namespace cuopt::linear_programming::detail; + if (!is_cusparse_runtime_mixed_precision_supported()) { + const raft::handle_t handle_{}; + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + auto settings = pdlp_solver_settings_t{}; + settings.method = cuopt::linear_programming::method_t::PDLP; + settings.pdlp_precision = cuopt::linear_programming::pdlp_precision_t::MixedPrecision; + + optimization_problem_solution_t solution = + solve_lp(&handle_, op_problem, settings); + EXPECT_EQ(solution.get_error_status().get_error_type(), cuopt::error_type_t::ValidationError); + return; + } + + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + auto settings_mixed = pdlp_solver_settings_t{}; + settings_mixed.method = cuopt::linear_programming::method_t::PDLP; + settings_mixed.pdlp_precision = cuopt::linear_programming::pdlp_precision_t::MixedPrecision; + + optimization_problem_solution_t solution_mixed = + solve_lp(&handle_, op_problem, settings_mixed); + EXPECT_EQ((int)solution_mixed.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, + solution_mixed.get_additional_termination_information().primal_objective)); + + auto settings_full = pdlp_solver_settings_t{}; + settings_full.method = cuopt::linear_programming::method_t::PDLP; + settings_full.pdlp_precision = cuopt::linear_programming::pdlp_precision_t::DefaultPrecision; + + optimization_problem_solution_t solution_full = + solve_lp(&handle_, op_problem, settings_full); + EXPECT_EQ((int)solution_full.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, + solution_full.get_additional_termination_information().primal_objective)); + + EXPECT_NEAR(solution_mixed.get_additional_termination_information().primal_objective, + solution_full.get_additional_termination_information().primal_objective, + 1e-2); +} + TEST(pdlp_class, run_double_very_low_accuracy) { const raft::handle_t handle_{}; @@ -1888,6 +1942,107 @@ TEST(pdlp_class, some_climber_hit_iteration_limit) } } +TEST(pdlp_class, precision_single) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + auto solver_settings = pdlp_solver_settings_t{}; + solver_settings.method = cuopt::linear_programming::method_t::PDLP; + solver_settings.pdlp_precision = cuopt::linear_programming::pdlp_precision_t::SinglePrecision; + + optimization_problem_solution_t solution = + solve_lp(&handle_, op_problem, solver_settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +} + +TEST(pdlp_class, precision_single_crossover) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + auto solver_settings = pdlp_solver_settings_t{}; + solver_settings.method = cuopt::linear_programming::method_t::PDLP; + solver_settings.pdlp_precision = cuopt::linear_programming::pdlp_precision_t::SinglePrecision; + solver_settings.crossover = true; + + optimization_problem_solution_t solution = + solve_lp(&handle_, op_problem, solver_settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +} + +TEST(pdlp_class, precision_single_concurrent) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + auto solver_settings = pdlp_solver_settings_t{}; + solver_settings.method = cuopt::linear_programming::method_t::Concurrent; + solver_settings.pdlp_precision = cuopt::linear_programming::pdlp_precision_t::SinglePrecision; + + optimization_problem_solution_t solution = + solve_lp(&handle_, op_problem, solver_settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +} + +TEST(pdlp_class, precision_single_papilo_presolve) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + auto solver_settings = pdlp_solver_settings_t{}; + solver_settings.method = cuopt::linear_programming::method_t::PDLP; + solver_settings.pdlp_precision = cuopt::linear_programming::pdlp_precision_t::SinglePrecision; + solver_settings.presolver = cuopt::linear_programming::presolver_t::Papilo; + + optimization_problem_solution_t solution = + solve_lp(&handle_, op_problem, solver_settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +} + +TEST(pdlp_class, precision_single_pslp_presolve) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/afiro_original.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path, true); + + auto solver_settings = pdlp_solver_settings_t{}; + solver_settings.method = cuopt::linear_programming::method_t::PDLP; + solver_settings.pdlp_precision = cuopt::linear_programming::pdlp_precision_t::SinglePrecision; + solver_settings.presolver = cuopt::linear_programming::presolver_t::PSLP; + + optimization_problem_solution_t solution = + solve_lp(&handle_, op_problem, solver_settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_FALSE(is_incorrect_objective( + afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); +} + } // namespace cuopt::linear_programming::test CUOPT_TEST_PROGRAM_MAIN() diff --git a/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst b/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst index 43d15eca64..d9a42301cb 100644 --- a/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst +++ b/docs/cuopt/source/cuopt-c/lp-qp-milp/lp-qp-milp-c-api.rst @@ -187,6 +187,7 @@ These constants are used as parameter names in the :c:func:`cuOptSetParameter`, .. doxygendefine:: CUOPT_SOLUTION_FILE .. doxygendefine:: CUOPT_NUM_CPU_THREADS .. doxygendefine:: CUOPT_USER_PROBLEM_FILE +.. doxygendefine:: CUOPT_PDLP_PRECISION .. _pdlp-solver-mode-constants: @@ -201,6 +202,18 @@ These constants are used to configure `CUOPT_PDLP_SOLVER_MODE` via :c:func:`cuOp .. doxygendefine:: CUOPT_PDLP_SOLVER_MODE_METHODICAL1 .. doxygendefine:: CUOPT_PDLP_SOLVER_MODE_FAST1 +.. _pdlp-precision-constants: + +PDLP Precision Constants +------------------------ + +These constants are used to configure `CUOPT_PDLP_PRECISION` via :c:func:`cuOptSetIntegerParameter`. + +.. doxygendefine:: CUOPT_PDLP_DEFAULT_PRECISION +.. doxygendefine:: CUOPT_PDLP_SINGLE_PRECISION +.. doxygendefine:: CUOPT_PDLP_DOUBLE_PRECISION +.. doxygendefine:: CUOPT_PDLP_MIXED_PRECISION + .. _method-constants: Method Constants diff --git a/docs/cuopt/source/lp-qp-features.rst b/docs/cuopt/source/lp-qp-features.rst index 4bd178ed53..e3cbddbb05 100644 --- a/docs/cuopt/source/lp-qp-features.rst +++ b/docs/cuopt/source/lp-qp-features.rst @@ -157,6 +157,17 @@ Batch Mode Users can submit a set of problems which will be solved in a batch. Problems will be solved at the same time in parallel to fully utilize the GPU. Checkout :ref:`self-hosted client ` example in thin client. +PDLP Precision Modes +-------------------- + +By default, PDLP operates in the native precision of the problem type (FP64 for double-precision problems). The ``pdlp_precision`` parameter provides several modes: + +- **single**: Run PDLP internally in FP32, with automatic conversion of inputs and outputs. FP32 uses half the memory and allows PDHG iterations to be on average twice as fast, but may require more iterations to converge. Compatible with crossover (solution is converted back to FP64 before crossover) and concurrent mode (PDLP runs in FP32 while other solvers run in FP64). +- **mixed**: Use mixed precision SpMV during PDHG iterations. The constraint matrix is stored in FP32 while vectors and compute type remain in FP64, improving SpMV performance with limited impact on convergence. Convergence checking and restart logic always use the full FP64 matrix. +- **double**: Explicitly run in FP64 (same as default for double-precision problems). + +.. note:: The default precision is the native type of the problem (FP64 for double). + Multi-GPU Mode -------------- diff --git a/docs/cuopt/source/lp-qp-milp-settings.rst b/docs/cuopt/source/lp-qp-milp-settings.rst index bd1372f70e..29c27a4ac2 100644 --- a/docs/cuopt/source/lp-qp-milp-settings.rst +++ b/docs/cuopt/source/lp-qp-milp-settings.rst @@ -192,6 +192,27 @@ Per Constraint Residual .. note:: The default value is false. +PDLP Precision +^^^^^^^^^^^^^^ + +``CUOPT_PDLP_PRECISION`` controls the precision mode used by the PDLP solver. The following modes are +available: + +- **default** (-1): Use the native precision of the problem type (FP64 for double-precision problems). +- **single** (0): Run PDLP internally in FP32 (float). Inputs are converted from FP64 to FP32 before + solving and outputs are converted back to FP64. FP32 uses half the memory and allows PDHG iterations + to be on average twice as fast, but may require more iterations to converge due to reduced numerical + accuracy. Compatible with crossover (solution is converted back to FP64 before crossover runs) and + concurrent mode (the PDLP leg runs in FP32 while Dual Simplex and Barrier run in FP64). +- **double** (1): Explicitly run in FP64 (same as default for double-precision problems). +- **mixed** (2): Use mixed precision sparse matrix-vector products (SpMV) during PDHG iterations. The + constraint matrix and its transpose are stored in FP32 while vectors and the compute type remain in + FP64, improving SpMV performance. Convergence checking and restart logic always use the + full FP64 matrix, so this mode does not reduce overall memory usage. This provides a middle ground + between full FP64 and FP32: faster PDHG iterations with limited impact on convergence. + +.. note:: The default value is 0 (default precision). + Barrier Solver Settings ^^^^^^^^^^^^^^^^^^^^^^^^ 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 9f94916ff0..e284ffc0ab 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_lp_solver.py @@ -18,6 +18,7 @@ CUOPT_ITERATION_LIMIT, CUOPT_METHOD, CUOPT_MIP_HEURISTICS_ONLY, + CUOPT_PDLP_PRECISION, CUOPT_PDLP_SOLVER_MODE, CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, CUOPT_RELATIVE_DUAL_TOLERANCE, @@ -604,10 +605,10 @@ def test_barrier(): A_offsets = np.array([0, 2, 4]) data_model_obj.set_csr_constraint_matrix(A_values, A_indices, A_offsets) - b = np.array([200, 160]) + b = np.array([200.0, 160.0]) data_model_obj.set_constraint_bounds(b) - c = np.array([5, 20]) + c = np.array([5.0, 20.0]) data_model_obj.set_objective_coefficients(c) row_types = np.array(["L", "L"]) @@ -722,3 +723,43 @@ def test_write_files(): assert float(line.split()[-1]) == pytest.approx(80) os.remove("afiro.sol") + + +def test_pdlp_precision_single(): + file_path = ( + RAPIDS_DATASET_ROOT_DIR + "/linear_programming/afiro_original.mps" + ) + data_model_obj = cuopt_mps_parser.ParseMps(file_path) + + settings = solver_settings.SolverSettings() + settings.set_parameter(CUOPT_METHOD, SolverMethod.PDLP) + settings.set_parameter(CUOPT_PDLP_PRECISION, 0) # Single + settings.set_optimality_tolerance(1e-4) + + solution = solver.Solve(data_model_obj, settings) + + assert solution.get_termination_status() == LPTerminationStatus.Optimal + assert solution.get_primal_objective() == pytest.approx( + -464.7531, rel=1e-1 + ) + assert solution.get_solved_by_pdlp() + + +def test_pdlp_precision_single_crossover(): + file_path = ( + RAPIDS_DATASET_ROOT_DIR + "/linear_programming/afiro_original.mps" + ) + data_model_obj = cuopt_mps_parser.ParseMps(file_path) + + settings = solver_settings.SolverSettings() + settings.set_parameter(CUOPT_METHOD, SolverMethod.PDLP) + settings.set_parameter(CUOPT_PDLP_PRECISION, 1) # Single + settings.set_parameter("crossover", True) + settings.set_optimality_tolerance(1e-4) + + solution = solver.Solve(data_model_obj, settings) + + assert solution.get_termination_status() == LPTerminationStatus.Optimal + assert solution.get_primal_objective() == pytest.approx( + -464.7531, rel=1e-1 + ) From 9579cd139394f3149660efb1c37daf39149a6d71 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 7 Mar 2026 05:12:59 -0800 Subject: [PATCH 137/225] gpufj integration --- .../mip/solver_settings.hpp | 4 + cpp/src/branch_and_bound/branch_and_bound.cpp | 690 +++++++++++++++++- cpp/src/branch_and_bound/branch_and_bound.hpp | 34 + cpp/src/cuts/cuts.cpp | 168 +++++ cpp/src/dual_simplex/basis_updates.cpp | 4 +- cpp/src/dual_simplex/bb_worker_state.hpp | 2 +- .../bound_flipping_ratio_test.cpp | 2 +- .../bound_flipping_ratio_test.hpp | 2 +- .../diversity/diversity_manager.cu | 137 +++- .../mip_heuristics/diversity/population.cu | 102 ++- .../diversity/recombiners/recombiner.cuh | 59 +- .../feasibility_jump/feasibility_jump.cu | 32 +- .../mip_heuristics/feasibility_jump/fj_cpu.cu | 31 + .../local_search/local_search.cu | 13 +- .../local_search/rounding/bounds_repair.cu | 108 ++- .../local_search/rounding/constraint_prop.cu | 46 ++ cpp/src/mip_heuristics/solve.cu | 27 +- cpp/src/mip_heuristics/solver.cu | 41 +- cpp/src/mip_heuristics/solver_context.cuh | 6 + cpp/src/utilities/determinism_log.hpp | 35 + cpp/src/utilities/seed_generator.cuh | 4 +- cpp/src/utilities/work_limit_context.hpp | 122 +++- cpp/src/utilities/work_limit_timer.hpp | 14 +- cpp/src/utilities/work_unit_scheduler.cpp | 4 +- cpp/tests/mip/determinism_test.cu | 124 ++++ 25 files changed, 1716 insertions(+), 95 deletions(-) create mode 100644 cpp/src/utilities/determinism_log.hpp diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 01c4cb64ab..5756fcdadc 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -100,6 +100,10 @@ class mip_solver_settings_t { i_t mip_batch_pdlp_strong_branching = 0; i_t num_gpus = 1; bool log_to_console = true; + // Scales deterministic CPUFJ producer work units before they are exposed to B&B replay/sync. + f_t cpufj_work_unit_scale = 1.0; + // Scales deterministic GPU heuristic producer work units/timestamps exposed to B&B replay/sync. + f_t gpu_heur_work_unit_scale = 1.0; std::string log_file; std::string sol_file; diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 6ce9a4f4d0..2fe54c4c30 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -5,6 +5,8 @@ */ /* clang-format on */ +#include + #include #include #include @@ -261,6 +263,23 @@ branch_and_bound_t::branch_and_bound_t( dualize_info_t dualize_info; convert_user_problem(original_problem_, settings_, original_lp_, new_slacks_, dualize_info); full_variable_types(original_problem_, original_lp_, var_types_); + assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic LP init state: rows=%d cols=%d nnz=%zu slacks=%zu slack_hash=0x%x " + "rhs_hash=0x%x lower_hash=0x%x upper_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x " + "Aval_hash=0x%x\n", + original_lp_.num_rows, + original_lp_.num_cols, + original_lp_.A.x.size(), + new_slacks_.size(), + detail::compute_hash(new_slacks_), + detail::compute_hash(original_lp_.rhs), + detail::compute_hash(original_lp_.lower), + detail::compute_hash(original_lp_.upper), + detail::compute_hash(original_lp_.A.col_start), + detail::compute_hash(original_lp_.A.i), + detail::compute_hash(original_lp_.A.x)); // Check slack #ifdef CHECK_SLACKS @@ -444,7 +463,9 @@ void branch_and_bound_t::set_new_solution(const std::vector& solu std::vector crushed_solution; crush_primal_solution( original_problem_, original_lp_, solution, new_slacks_, crushed_solution); - f_t obj = compute_objective(original_lp_, crushed_solution); + f_t obj = compute_objective(original_lp_, crushed_solution); + const uint32_t host_hash = detail::compute_hash(solution); + const uint32_t crushed_hash = detail::compute_hash(crushed_solution); mutex_original_lp_.unlock(); bool is_feasible = false; bool attempt_repair = false; @@ -467,8 +488,17 @@ void branch_and_bound_t::set_new_solution(const std::vector& solu mutex_original_lp_.unlock(); mutex_upper_.lock(); if (is_feasible && obj < upper_bound_) { - upper_bound_ = obj; + const f_t previous_upper = upper_bound_; + upper_bound_ = obj; incumbent_.set_incumbent_solution(obj, crushed_solution); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic B&B incumbent update: source=external_direct prev_upper=%.16e " + "new_upper=%.16e obj=%.16e hash=0x%x\n", + previous_upper, + upper_bound_.load(), + obj, + detail::compute_hash(crushed_solution)); } else { attempt_repair = true; constexpr bool verbose = false; @@ -508,10 +538,40 @@ void branch_and_bound_t::queue_external_solution_deterministic( } mutex_original_lp_.lock(); + const size_t lp_nnz = original_lp_.A.x.size(); + const i_t active_cut_rows = std::max((i_t)0, original_lp_.num_rows - original_problem_.num_rows); + const uint32_t new_slacks_hash = detail::compute_hash(new_slacks_); + const uint32_t rhs_hash = detail::compute_hash(original_lp_.rhs); + const uint32_t lower_hash = detail::compute_hash(original_lp_.lower); + const uint32_t upper_hash = detail::compute_hash(original_lp_.upper); + const uint32_t a_col_hash = detail::compute_hash(original_lp_.A.col_start); + const uint32_t a_row_hash = detail::compute_hash(original_lp_.A.i); + const uint32_t a_val_hash = detail::compute_hash(original_lp_.A.x); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic external crush ctx: wut=%.6f lp_rows=%d lp_cols=%d lp_nnz=%zu " + "active_cut_rows=%d " + "slacks=%zu slack_hash=0x%x rhs_hash=0x%x lower_hash=0x%x upper_hash=0x%x " + "Acol_hash=0x%x Arow_hash=0x%x Aval_hash=0x%x\n", + work_unit_ts, + original_lp_.num_rows, + original_lp_.num_cols, + lp_nnz, + active_cut_rows, + new_slacks_.size(), + new_slacks_hash, + rhs_hash, + lower_hash, + upper_hash, + a_col_hash, + a_row_hash, + a_val_hash); std::vector crushed_solution; crush_primal_solution( original_problem_, original_lp_, solution, new_slacks_, crushed_solution); - f_t obj = compute_objective(original_lp_, crushed_solution); + f_t obj = compute_objective(original_lp_, crushed_solution); + const uint32_t host_hash = detail::compute_hash(solution); + const uint32_t crushed_hash = detail::compute_hash(crushed_solution); // Validate solution before queueing f_t primal_err; @@ -527,14 +587,37 @@ void branch_and_bound_t::queue_external_solution_deterministic( // (which may have gained slack columns from cuts added after this point). mutex_repair_.lock(); repair_queue_.push_back(solution); + const size_t repair_queue_size = repair_queue_.size(); mutex_repair_.unlock(); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic external reject_to_repair: wut=%.6f obj=%.16e host_hash=0x%x " + "crushed_hash=0x%x primal_err=%.6e bound_err=%.6e fractional=%d repair_q=%zu\n", + work_unit_ts, + obj, + host_hash, + crushed_hash, + primal_err, + bound_err, + num_fractional, + repair_queue_size); return; } // Queue the solution with its work unit timestamp mutex_heuristic_queue_.lock(); heuristic_solution_queue_.push_back({obj, std::move(crushed_solution), 0, -1, 0, work_unit_ts}); + const size_t heuristic_queue_size = heuristic_solution_queue_.size(); mutex_heuristic_queue_.unlock(); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic external queued: wut=%.6f obj=%.16e host_hash=0x%x crushed_hash=0x%x " + "heur_q=%zu\n", + work_unit_ts, + obj, + host_hash, + crushed_hash, + heuristic_queue_size); } template @@ -627,8 +710,17 @@ void branch_and_bound_t::repair_heuristic_solutions() mutex_upper_.lock(); if (repaired_obj < upper_bound_) { - upper_bound_ = repaired_obj; + const f_t previous_upper = upper_bound_; + upper_bound_ = repaired_obj; incumbent_.set_incumbent_solution(repaired_obj, repaired_solution); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic B&B incumbent update: source=repair_queue prev_upper=%.16e " + "new_upper=%.16e obj=%.16e hash=0x%x\n", + previous_upper, + upper_bound_.load(), + repaired_obj, + detail::compute_hash(repaired_solution)); report_heuristic(repaired_obj); if (settings_.solution_callback != nullptr) { @@ -649,8 +741,17 @@ void branch_and_bound_t::set_solution_at_root(mip_solution_t const cut_info_t& cut_info) { mutex_upper_.lock(); + const f_t previous_upper = upper_bound_; incumbent_.set_incumbent_solution(root_objective_, root_relax_soln_.x); upper_bound_ = root_objective_; + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic B&B incumbent update: source=root_solution prev_upper=%.16e new_upper=%.16e " + "obj=%.16e hash=0x%x\n", + previous_upper, + upper_bound_.load(), + root_objective_, + detail::compute_hash(root_relax_soln_.x)); mutex_upper_.unlock(); print_cut_info(settings_, cut_info); @@ -748,6 +849,17 @@ void branch_and_bound_t::set_final_solution(mip_solution_t& solution.lower_bound = lower_bound; solution.nodes_explored = exploration_stats_.nodes_explored; solution.simplex_iterations = exploration_stats_.total_lp_iters; + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic B&B final package: status=%d incumbent_obj=%.16e lower_bound=%.16e " + "incumbent_hash=0x%x final_hash=0x%x nodes=%d simplex_iterations=%d\n", + (int)solver_status_.load(), + solution.objective, + solution.lower_bound, + detail::compute_hash(incumbent_.x), + detail::compute_hash(solution.x), + solution.nodes_explored, + solution.simplex_iterations); } template @@ -764,8 +876,19 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, mutex_upper_.lock(); if (leaf_objective < upper_bound_) { + const f_t previous_upper = upper_bound_; incumbent_.set_incumbent_solution(leaf_objective, leaf_solution); upper_bound_ = leaf_objective; + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic B&B incumbent update: source=leaf prev_upper=%.16e new_upper=%.16e " + "obj=%.16e hash=0x%x depth=%d worker_type=%d\n", + previous_upper, + upper_bound_.load(), + leaf_objective, + detail::compute_hash(leaf_solution), + leaf_depth, + (int)thread_type); report(feasible_solution_symbol(thread_type), leaf_objective, get_lower_bound(), leaf_depth, 0); send_solution = true; } @@ -1002,9 +1125,31 @@ struct deterministic_bfs_policy_t const std::vector& x) override { if (obj < this->worker.local_upper_bound) { + const f_t previous_local_upper = this->worker.local_upper_bound; + const int previous_seq = this->worker.next_solution_seq; this->worker.local_upper_bound = obj; this->worker.integer_solutions.push_back( {obj, x, node->depth, this->worker.worker_id, this->worker.next_solution_seq++}); + if (this->bnb.deterministic_current_horizon_ <= + this->bnb.deterministic_horizon_step_ + 1e-9) { + CUOPT_DETERMINISM_LOG_PRINTF( + this->bnb.settings_.log, + "Deterministic BFS local integer queue: horizon=%.6f worker=%d node_id=%d packed=0x%llx " + "path_hash=0x%x depth=%d obj=%.16e sol_hash=0x%x local_upper_before=%.16e " + "local_upper_after=%.16e queue_seq=%d clock=%.6f\n", + this->bnb.deterministic_current_horizon_, + this->worker.worker_id, + node->creation_seq, + (unsigned long long)node->get_id_packed(), + node->compute_path_hash(), + node->depth, + obj, + detail::compute_hash(x), + previous_local_upper, + this->worker.local_upper_bound, + previous_seq, + this->worker.clock); + } } } @@ -1043,6 +1188,29 @@ struct deterministic_bfs_policy_t node->fractional_val); this->bnb.exploration_stats_.nodes_unexplored += 2; this->worker.enqueue_children_for_plunge(node->get_down_child(), node->get_up_child(), dir); + if (this->bnb.deterministic_current_horizon_ <= + this->bnb.deterministic_horizon_step_ + 1e-9) { + CUOPT_DETERMINISM_LOG_PRINTF( + this->bnb.settings_.log, + "Deterministic BFS branch create: horizon=%.6f worker=%d parent_packed=0x%llx " + "parent_path_hash=0x%x depth=%d branch_var=%d dir=%d frac=%.16e " + "down_packed=0x%llx down_path_hash=0x%x up_packed=0x%llx up_path_hash=0x%x " + "queue_size=%zu local_upper=%.16e\n", + this->bnb.deterministic_current_horizon_, + this->worker.worker_id, + (unsigned long long)node->get_id_packed(), + node->compute_path_hash(), + node->depth, + node->branch_var, + (int)dir, + node->fractional_val, + (unsigned long long)node->get_down_child()->get_id_packed(), + node->get_down_child()->compute_path_hash(), + (unsigned long long)node->get_up_child()->get_id_packed(), + node->get_up_child()->compute_path_hash(), + this->worker.queue_size(), + this->worker.local_upper_bound); + } break; case node_status_t::NUMERICAL: this->worker.record_numerical(node); break; default: break; @@ -1078,8 +1246,32 @@ struct deterministic_diving_policy_t const std::vector& x) override { if (obj < this->worker.local_upper_bound) { + const f_t previous_local_upper = this->worker.local_upper_bound; + const int previous_seq = this->worker.next_solution_seq; this->worker.local_upper_bound = obj; this->worker.queue_integer_solution(obj, x, node->depth); + if (this->bnb.deterministic_current_horizon_ <= + this->bnb.deterministic_horizon_step_ + 1e-9) { + CUOPT_DETERMINISM_LOG_PRINTF( + this->bnb.settings_.log, + "Deterministic diving local integer queue: horizon=%.6f worker=%d node_id=%d " + "packed=0x%llx " + "path_hash=0x%x depth=%d obj=%.16e sol_hash=0x%x local_upper_before=%.16e " + "local_upper_after=%.16e queue_seq=%d clock=%.6f type=%d\n", + this->bnb.deterministic_current_horizon_, + this->worker.worker_id, + node->creation_seq, + (unsigned long long)node->get_id_packed(), + node->compute_path_hash(), + node->depth, + obj, + detail::compute_hash(x), + previous_local_upper, + this->worker.local_upper_bound, + previous_seq, + this->worker.clock, + (int)this->worker.diving_type); + } } } @@ -1929,6 +2121,15 @@ lp_status_t branch_and_bound_t::solve_root_relaxation( settings_.log.printf("\n"); is_root_solution_set = true; + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root flag set: root_status=%d root_obj=%.16e recomputed_root_obj=%.16e " + "callback_flag=%d x_hash=0x%x\n", + (int)root_status, + root_objective_, + compute_objective(original_lp_, root_relax_soln_.x), + (int)root_crossover_solution_set_.load(std::memory_order_acquire), + detail::compute_hash(root_relax_soln_.x)); return root_status; } @@ -1959,8 +2160,17 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut if (feasible) { const f_t computed_obj = compute_objective(original_lp_, crushed_guess); mutex_upper_.lock(); + const f_t previous_upper = upper_bound_; incumbent_.set_incumbent_solution(computed_obj, crushed_guess); upper_bound_ = computed_obj; + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic B&B incumbent update: source=initial_guess prev_upper=%.16e " + "new_upper=%.16e obj=%.16e hash=0x%x\n", + previous_upper, + upper_bound_.load(), + computed_obj, + detail::compute_hash(crushed_guess)); mutex_upper_.unlock(); } } @@ -2040,7 +2250,18 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut assert(root_vstatus_.size() == original_lp_.num_cols); set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_); - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + { + const f_t previous_root_objective = root_objective_; + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root objective assign: source=post_root_solve old=%.16e new=%.16e " + "x_hash=0x%x obj_hash=0x%x\n", + previous_root_objective, + root_objective_, + detail::compute_hash(root_relax_soln_.x), + detail::compute_hash(original_lp_.objective)); + } if (settings_.set_simplex_solution_callback != nullptr) { std::vector original_x; @@ -2157,7 +2378,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut cut_pool_size = cut_pool.pool_size(); // Resolve the LP with the new cuts - settings_.log.debug( + settings_.log.printf( "Solving LP with %d cuts (%d cut nonzeros). Cuts in pool %d. Total constraints %d\n", num_cuts, cuts_to_add.row_start[cuts_to_add.m], @@ -2167,6 +2388,26 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut f_t add_cuts_start_time = tic(); mutex_original_lp_.lock(); + assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root LP before add_cuts: pass=%d fractional=%d num_cuts=%d rows=%d " + "cols=%d nnz=%zu slacks=%zu slack_hash=0x%x rhs_hash=0x%x lower_hash=0x%x " + "upper_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x Aval_hash=0x%x\n", + cut_pass, + num_fractional, + num_cuts, + original_lp_.num_rows, + original_lp_.num_cols, + original_lp_.A.x.size(), + new_slacks_.size(), + detail::compute_hash(new_slacks_), + detail::compute_hash(original_lp_.rhs), + detail::compute_hash(original_lp_.lower), + detail::compute_hash(original_lp_.upper), + detail::compute_hash(original_lp_.A.col_start), + detail::compute_hash(original_lp_.A.i), + detail::compute_hash(original_lp_.A.x)); i_t add_cuts_status = add_cuts(settings_, cuts_to_add, cut_rhs, @@ -2179,6 +2420,27 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_vstatus_, edge_norms_); var_types_.resize(original_lp_.num_cols, variable_type_t::CONTINUOUS); + assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root LP after add_cuts: pass=%d fractional=%d num_cuts=%d status=%d " + "rows=%d cols=%d nnz=%zu slacks=%zu slack_hash=0x%x rhs_hash=0x%x lower_hash=0x%x " + "upper_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x Aval_hash=0x%x\n", + cut_pass, + num_fractional, + num_cuts, + add_cuts_status, + original_lp_.num_rows, + original_lp_.num_cols, + original_lp_.A.x.size(), + new_slacks_.size(), + detail::compute_hash(new_slacks_), + detail::compute_hash(original_lp_.rhs), + detail::compute_hash(original_lp_.lower), + detail::compute_hash(original_lp_.upper), + detail::compute_hash(original_lp_.A.col_start), + detail::compute_hash(original_lp_.A.i), + detail::compute_hash(original_lp_.A.x)); mutex_original_lp_.unlock(); f_t add_cuts_time = toc(add_cuts_start_time); if (add_cuts_time > 1.0) { @@ -2248,7 +2510,19 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut iter, edge_norms_); exploration_stats_.total_lp_iters += iter; - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + { + const f_t previous_root_objective = root_objective_; + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root objective assign: source=cut_lp_resolve old=%.16e new=%.16e " + "pass=%d x_hash=0x%x obj_hash=0x%x\n", + previous_root_objective, + root_objective_, + cut_pass, + detail::compute_hash(root_relax_soln_.x), + detail::compute_hash(original_lp_.objective)); + } f_t dual_phase2_time = toc(dual_phase2_start_time); if (dual_phase2_time > 1.0) { settings_.log.debug("Dual phase2 time %.2f seconds\n", dual_phase2_time); @@ -2275,7 +2549,19 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut // We recovered cut_status = convert_lp_status_to_dual_status(scratch_status); exploration_stats_.total_lp_iters += root_relax_soln_.iterations; - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + { + const f_t previous_root_objective = root_objective_; + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root objective assign: source=cut_lp_scratch old=%.16e new=%.16e " + "pass=%d x_hash=0x%x obj_hash=0x%x\n", + previous_root_objective, + root_objective_, + cut_pass, + detail::compute_hash(root_relax_soln_.x), + detail::compute_hash(original_lp_.objective)); + } } else { settings_.log.printf("Cut status %s\n", dual::status_to_string(cut_status).c_str()); return mip_status_t::NUMERICAL; @@ -2284,6 +2570,38 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut f_t remove_cuts_start_time = tic(); mutex_original_lp_.lock(); + assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); + const f_t root_objective_before_remove = root_objective_; + const f_t root_objective_before_remove_recomputed = + compute_objective(original_lp_, root_relax_soln_.x); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root LP before remove_cuts: pass=%d fractional=%d rows=%d cols=%d " + "nnz=%zu original_rows=%d active_cut_rows=%d slacks=%zu slack_hash=0x%x rhs_hash=0x%x " + "lower_hash=0x%x upper_hash=0x%x obj_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x " + "Aval_hash=0x%x root_obj_before_remove=%.16e root_obj_before_remove_recomputed=%.16e " + "root_obj_before_remove_delta=%.16e callback_flag=%d root_flag=%d\n", + cut_pass, + num_fractional, + original_lp_.num_rows, + original_lp_.num_cols, + original_lp_.A.x.size(), + original_rows, + std::max((i_t)0, original_lp_.num_rows - original_rows), + new_slacks_.size(), + detail::compute_hash(new_slacks_), + detail::compute_hash(original_lp_.rhs), + detail::compute_hash(original_lp_.lower), + detail::compute_hash(original_lp_.upper), + detail::compute_hash(original_lp_.objective), + detail::compute_hash(original_lp_.A.col_start), + detail::compute_hash(original_lp_.A.i), + detail::compute_hash(original_lp_.A.x), + root_objective_before_remove, + root_objective_before_remove_recomputed, + root_objective_before_remove_recomputed - root_objective_before_remove, + (int)root_crossover_solution_set_.load(std::memory_order_acquire), + (int)is_root_solution_set); remove_cuts(original_lp_, settings_, exploration_stats_.start_time, @@ -2299,6 +2617,34 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut basic_list, nonbasic_list, basis_update); + assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); + const f_t root_objective_after_remove = compute_objective(original_lp_, root_relax_soln_.x); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root LP after remove_cuts: pass=%d fractional=%d rows=%d cols=%d " + "nnz=%zu original_rows=%d active_cut_rows=%d slacks=%zu slack_hash=0x%x rhs_hash=0x%x " + "lower_hash=0x%x upper_hash=0x%x obj_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x " + "Aval_hash=0x%x root_obj_before_remove=%.16e root_obj_after_remove=%.16e " + "root_obj_remove_delta=%.16e\n", + cut_pass, + num_fractional, + original_lp_.num_rows, + original_lp_.num_cols, + original_lp_.A.x.size(), + original_rows, + std::max((i_t)0, original_lp_.num_rows - original_rows), + new_slacks_.size(), + detail::compute_hash(new_slacks_), + detail::compute_hash(original_lp_.rhs), + detail::compute_hash(original_lp_.lower), + detail::compute_hash(original_lp_.upper), + detail::compute_hash(original_lp_.objective), + detail::compute_hash(original_lp_.A.col_start), + detail::compute_hash(original_lp_.A.i), + detail::compute_hash(original_lp_.A.x), + root_objective_before_remove, + root_objective_after_remove, + root_objective_after_remove - root_objective_before_remove); mutex_original_lp_.unlock(); f_t remove_cuts_time = toc(remove_cuts_start_time); if (remove_cuts_time > 1.0) { @@ -2306,11 +2652,50 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } fractional.clear(); num_fractional = fractional_variables(settings_, root_relax_soln_.x, var_types_, fractional); + assert(root_relax_soln_.x.size() == static_cast(original_lp_.num_cols)); + assert(root_relax_soln_.y.size() == static_cast(original_lp_.num_rows)); + assert(root_relax_soln_.z.size() == static_cast(original_lp_.num_cols)); + assert(basic_list.size() == static_cast(original_lp_.num_rows)); + assert(nonbasic_list.size() == + static_cast(original_lp_.num_cols - original_lp_.num_rows)); + assert(root_vstatus_.size() == static_cast(original_lp_.num_cols)); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root pass state: pass=%d root_obj=%.16e num_fractional=%d rel_gap=%.16e " + "abs_gap=%.16e x_hash=0x%x y_hash=0x%x z_hash=0x%x basic_hash=0x%x nonbasic_hash=0x%x " + "vstatus_hash=0x%x edge_norms_hash=0x%x root_obj_after_remove=%.16e " + "root_obj_remove_delta=%.16e root_obj_member_delta=%.16e callback_flag=%d root_flag=%d\n", + cut_pass, + root_objective_, + num_fractional, + user_relative_gap(original_lp_, upper_bound_.load(), root_objective_), + upper_bound_.load() - root_objective_, + detail::compute_hash(root_relax_soln_.x), + detail::compute_hash(root_relax_soln_.y), + detail::compute_hash(root_relax_soln_.z), + detail::compute_hash(basic_list), + detail::compute_hash(nonbasic_list), + detail::compute_hash(root_vstatus_), + detail::compute_hash(edge_norms_), + root_objective_after_remove, + root_objective_after_remove - root_objective_, + root_objective_ - root_objective_after_remove, + (int)root_crossover_solution_set_.load(std::memory_order_acquire), + (int)is_root_solution_set); if (num_fractional == 0) { - upper_bound_ = root_objective_; + const f_t previous_upper = upper_bound_; + upper_bound_ = root_objective_; mutex_upper_.lock(); incumbent_.set_incumbent_solution(root_objective_, root_relax_soln_.x); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic B&B incumbent update: source=root_integral_pass prev_upper=%.16e " + "new_upper=%.16e obj=%.16e hash=0x%x\n", + previous_upper, + upper_bound_.load(), + root_objective_, + detail::compute_hash(root_relax_soln_.x)); mutex_upper_.unlock(); } f_t obj = upper_bound_.load(); @@ -2328,12 +2713,32 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut const f_t factor = settings_.cut_change_threshold; const f_t min_objective = 1e-3; if (change_in_objective <= factor * std::max(min_objective, std::abs(root_relax_objective))) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root pass break: pass=%d change=%.16e threshold=%.16e last_obj=%.16e " + "root_relax_obj=%.16e root_obj=%.16e\n", + cut_pass, + change_in_objective, + factor * std::max(min_objective, std::abs(root_relax_objective)), + last_objective, + root_relax_objective, + root_objective_); settings_.log.debug( "Change in objective %.16e is less than 1e-3 of root relax objective %.16e\n", change_in_objective, root_relax_objective); break; } + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root pass continue: pass=%d change=%.16e threshold=%.16e last_obj=%.16e " + "root_relax_obj=%.16e root_obj=%.16e\n", + cut_pass, + change_in_objective, + factor * std::max(min_objective, std::abs(root_relax_objective)), + last_objective, + root_relax_objective, + root_objective_); last_objective = root_objective_; } } @@ -2802,10 +3207,43 @@ void branch_and_bound_t::run_deterministic_bfs_loop( if (node == nullptr) { continue; } worker.current_node = node; + if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9 && + worker.total_nodes_processed < 16) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic BFS dequeue: horizon=%.6f worker=%d node_id=%d packed=0x%llx " + "path_hash=0x%x depth=%d lower=%.16e local_upper=%.16e clock=%.6f queue_size=%zu\n", + deterministic_current_horizon_, + worker.worker_id, + node->creation_seq, + (unsigned long long)node->get_id_packed(), + node->compute_path_hash(), + node->depth, + node->lower_bound, + worker.local_upper_bound, + worker.clock, + worker.queue_size()); + } f_t upper_bound = worker.local_upper_bound; f_t rel_gap = user_relative_gap(original_lp_, upper_bound, node->lower_bound); if (node->lower_bound > upper_bound) { + if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic BFS prune vs local upper: horizon=%.6f worker=%d node_id=%d " + "packed=0x%llx " + "path_hash=0x%x depth=%d node_lower=%.16e local_upper=%.16e clock=%.6f\n", + deterministic_current_horizon_, + worker.worker_id, + node->creation_seq, + (unsigned long long)node->get_id_packed(), + node->compute_path_hash(), + node->depth, + node->lower_bound, + upper_bound, + worker.clock); + } worker.current_node = nullptr; worker.record_fathomed(node, node->lower_bound); search_tree.update(node, node_status_t::FATHOMED); @@ -2818,6 +3256,20 @@ void branch_and_bound_t::run_deterministic_bfs_loop( node_status_t status = solve_node_deterministic(worker, node, search_tree); worker.last_solved_node = node; + /* + settings_.log.printf( + "Deterministic BFS solved: worker=%d node_id=%d creation_seq=%d depth=%d status=%d " + "worker_clock=%.6f local_upper=%.16e global_upper=%.16e nodes_processed_h=%d\n", + worker.worker_id, + node->node_id, + node->creation_seq, + node->depth, + (int)status, + worker.clock, + worker.local_upper_bound, + upper_bound_.load(), + worker.nodes_processed_this_horizon); + */ worker.current_node = nullptr; continue; @@ -2845,9 +3297,59 @@ void branch_and_bound_t::deterministic_sync_callback() max_producer_wait_time_ = std::max(max_producer_wait_time_, wait_time); ++producer_wait_count_; - work_unit_context_.global_work_units_elapsed = horizon_end; + work_unit_context_.set_current_work(horizon_end, false); bb_event_batch_t all_events = deterministic_workers_->collect_and_sort_events(); + if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic sync start: horizon_idx=%d horizon_end=%.6f global_upper=%.16e " + "nodes_explored=%d nodes_unexplored=%lu event_count=%zu heuristic_queue=%zu\n", + deterministic_horizon_number_, + horizon_end, + upper_bound_.load(), + exploration_stats_.nodes_explored.load(), + exploration_stats_.nodes_unexplored.load(), + all_events.events.size(), + heuristic_solution_queue_.size()); + for (size_t i = 0; i < all_events.events.size(); ++i) { + const auto& event = all_events.events[i]; + if (event.type != bb_event_type_t::NODE_INTEGER) { continue; } + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic sync integer event[%zu]: wut=%.6f worker=%d node_id=%d event_seq=%d " + "obj=%.16e\n", + i, + event.work_timestamp, + event.worker_id, + event.node_id, + event.event_sequence, + event.payload.integer_solution.objective_value); + } + for (const auto& worker : *deterministic_workers_) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic BFS horizon summary: horizon=%.6f worker=%d clock=%.16e " + "nodes_processed_h=%d total_nodes=%d integer_queue=%zu next_creation_seq=%d " + "next_solution_seq=%d queue_size=%zu current_node=0x%llx last_solved=0x%llx " + "local_upper=%.16e\n", + deterministic_current_horizon_, + worker.worker_id, + worker.clock, + worker.nodes_processed_this_horizon, + worker.total_nodes_processed, + worker.integer_solutions.size(), + worker.next_creation_seq, + worker.next_solution_seq, + worker.queue_size(), + worker.current_node != nullptr ? (unsigned long long)worker.current_node->get_id_packed() + : 0ULL, + worker.last_solved_node != nullptr + ? (unsigned long long)worker.last_solved_node->get_id_packed() + : 0ULL, + worker.local_upper_bound); + } + } deterministic_sort_replay_events(all_events); @@ -3071,6 +3573,7 @@ node_status_t branch_and_bound_t::solve_node_deterministic( lp_status = convert_lp_status_to_dual_status(second_status); } + double clock_before = worker.clock; double work_performed = worker.work_context.global_work_units_elapsed - work_units_at_start; worker.clock += work_performed; @@ -3081,6 +3584,27 @@ node_status_t branch_and_bound_t::solve_node_deterministic( deterministic_bfs_policy_t policy{*this, worker}; auto [status, round_dir] = update_tree_impl(node_ptr, search_tree, &worker, lp_status, policy); + if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9 && + (status == node_status_t::HAS_CHILDREN || status == node_status_t::INTEGER_FEASIBLE)) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic BFS solve progress: horizon=%.6f worker=%d node_packed=0x%llx " + "node_path_hash=0x%x depth=%d lp_status=%d status=%d node_iter=%d work=%.16e " + "clock_before=%.16e clock_after=%.16e local_upper=%.16e node_lower=%.16e\n", + deterministic_current_horizon_, + worker.worker_id, + (unsigned long long)node_ptr->get_id_packed(), + node_ptr->compute_path_hash(), + node_ptr->depth, + (int)lp_status, + (int)status, + node_iter, + work_performed, + clock_before, + worker.clock, + worker.local_upper_bound, + node_ptr->lower_bound); + } return status; } @@ -3106,8 +3630,39 @@ void branch_and_bound_t::deterministic_process_worker_solutions( f_t deterministic_lower = deterministic_compute_lower_bound(); f_t current_upper = upper_bound_.load(); + if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic worker solution replay: candidates=%zu lower=%.16e upper_before=%.16e\n", + all_solutions.size(), + deterministic_lower, + current_upper); + for (size_t i = 0; i < all_solutions.size(); ++i) { + const auto* sol = all_solutions[i]; + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic worker solution[%zu]: obj=%.16e worker=%d seq=%d depth=%d sol_hash=0x%x\n", + i, + sol->objective, + sol->worker_id, + sol->sequence_id, + sol->depth, + detail::compute_hash(sol->solution)); + } + } for (const auto* sol : all_solutions) { + /* + settings_.log.printf( + "Deterministic worker candidate: obj=%.16e worker=%d seq=%d depth=%d current_upper=%.16e " + "lower=%.16e\n", + sol->objective, + sol->worker_id, + sol->sequence_id, + sol->depth, + current_upper, + deterministic_lower); + */ if (sol->objective < current_upper) { f_t user_obj = compute_user_objective(original_lp_, sol->objective); f_t user_lower = compute_user_objective(original_lp_, deterministic_lower); @@ -3124,17 +3679,53 @@ void branch_and_bound_t::deterministic_process_worker_solutions( bool improved = false; if (sol->objective < upper_bound_) { - upper_bound_ = sol->objective; + const f_t previous_upper = upper_bound_; + upper_bound_ = sol->objective; incumbent_.set_incumbent_solution(sol->objective, sol->solution); current_upper = sol->objective; improved = true; + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic B&B incumbent update: source=det_worker_replay prev_upper=%.16e " + "new_upper=%.16e obj=%.16e hash=0x%x worker=%d seq=%d depth=%d horizon=%.6f\n", + previous_upper, + upper_bound_.load(), + sol->objective, + detail::compute_hash(sol->solution), + sol->worker_id, + sol->sequence_id, + sol->depth, + deterministic_current_horizon_); } + /* + settings_.log.printf( + "Deterministic worker accept: improved=%d obj=%.16e worker=%d seq=%d depth=%d " + "upper_now=%.16e nodes=%d/%lu\n", + (int)improved, + sol->objective, + sol->worker_id, + sol->sequence_id, + sol->depth, + current_upper, + nodes_explored, + nodes_unexplored); + */ if (improved && settings_.solution_callback != nullptr) { std::vector original_x; uncrush_primal_solution(original_problem_, original_lp_, sol->solution, original_x); settings_.solution_callback(original_x, sol->objective); } + } else { + /* + settings_.log.printf( + "Deterministic worker reject: obj=%.16e worker=%d seq=%d depth=%d current_upper=%.16e\n", + sol->objective, + sol->worker_id, + sol->sequence_id, + sol->depth, + current_upper); + */ } } @@ -3192,8 +3783,10 @@ void branch_and_bound_t::deterministic_sort_replay_events( [](const std::vector& a, const std::vector& b) { return a < b; }); if (to_repair.size() > 0) { - settings_.log.debug("Deterministic sync: Attempting to repair %ld injected solutions\n", - to_repair.size()); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic sync: Attempting to repair %ld injected solutions\n", + to_repair.size()); for (const std::vector& uncrushed_solution : to_repair) { std::vector crushed_solution; crush_primal_solution( @@ -3229,6 +3822,25 @@ void branch_and_bound_t::deterministic_sort_replay_events( heuristic_solution_queue_ = std::move(future_solutions); } mutex_heuristic_queue_.unlock(); + if (!heuristic_solutions.empty() || !heuristic_solution_queue_.empty()) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic heuristic extract: horizon=%.6f now=%zu future=%zu upper=%.16e\n", + deterministic_current_horizon_, + heuristic_solutions.size(), + heuristic_solution_queue_.size(), + upper_bound_.load()); + } + /* + settings_.log.printf( + "Deterministic replay extract: horizon=%.6f bb_events=%zu heur_now=%zu heur_future=%zu " + "upper_before=%.16e\n", + deterministic_current_horizon_, + events.events.size(), + heuristic_solutions.size(), + heuristic_solution_queue_.size(), + upper_bound_.load()); + */ // sort by work unit timestamp, with objective and solution values as tie-breakers std::sort( @@ -3239,6 +3851,19 @@ void branch_and_bound_t::deterministic_sort_replay_events( if (a.objective != b.objective) { return a.objective < b.objective; } return a.solution < b.solution; // edge-case - lexicographical comparison }); + /* + for (size_t i = 0; i < std::min(heuristic_solutions.size(), 8); ++i) { + const auto& hsol = heuristic_solutions[i]; + settings_.log.printf( + "Deterministic replay heuristic[%zu]: wut=%.6f obj=%.16e worker=%d seq=%d depth=%d\n", + i, + hsol.work_timestamp, + hsol.objective, + hsol.worker_id, + hsol.sequence_id, + hsol.depth); + } + */ // Merge B&B events and heuristic solutions for unified timeline replay size_t event_idx = 0; @@ -3264,6 +3889,11 @@ void branch_and_bound_t::deterministic_sort_replay_events( if (process_event) { const auto& event = events.events[event_idx++]; + /* + settings_.log.printf("Deterministic replay event: type=%d wut=%.6f worker=%d node=%d + seq=%d\n", (int)event.type, event.work_timestamp, event.worker_id, event.node_id, + event.event_sequence); + */ switch (event.type) { case bb_event_type_t::NODE_INTEGER: case bb_event_type_t::NODE_BRANCHED: @@ -3276,21 +3906,39 @@ void branch_and_bound_t::deterministic_sort_replay_events( if (process_heuristic) { const auto& hsol = heuristic_solutions[heuristic_idx++]; - CUOPT_LOG_TRACE( - "Deterministic sync: Heuristic solution received at WUT %f with objective %g, current " - "horizon %f", - hsol.work_timestamp, - hsol.objective, - deterministic_current_horizon_); - // Process heuristic solution at its correct work unit timestamp position f_t new_upper = std::numeric_limits::infinity(); if (hsol.objective < upper_bound_) { - upper_bound_ = hsol.objective; + const f_t previous_upper = upper_bound_; + upper_bound_ = hsol.objective; incumbent_.set_incumbent_solution(hsol.objective, hsol.solution); new_upper = hsol.objective; + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic B&B incumbent update: source=det_heuristic_replay prev_upper=%.16e " + "new_upper=%.16e obj=%.16e hash=0x%x worker=%d seq=%d wut=%.6f horizon=%.6f\n", + previous_upper, + upper_bound_.load(), + hsol.objective, + detail::compute_hash(hsol.solution), + hsol.worker_id, + hsol.sequence_id, + hsol.work_timestamp, + deterministic_current_horizon_); } + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic heuristic replay: horizon=%.6f wut=%.6f obj=%.16e accepted=%d " + "upper_now=%.16e worker=%d seq=%d sol_hash=0x%x\n", + deterministic_current_horizon_, + hsol.work_timestamp, + hsol.objective, + (int)(new_upper < std::numeric_limits::infinity()), + upper_bound_.load(), + hsol.worker_id, + hsol.sequence_id, + detail::compute_hash(hsol.solution)); if (new_upper < std::numeric_limits::infinity()) { report_heuristic(new_upper); diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index a13d5cedcf..8d29808397 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -81,7 +82,22 @@ class branch_and_bound_t { f_t user_objective, i_t iterations) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root callback enter: guard=%d crossover_flag=%d current_root_obj=%.16e " + "callback_solver_obj=%.16e callback_user_obj=%.16e primal_size=%zu dual_size=%zu " + "reduced_cost_size=%zu iterations=%d\n", + (int)is_root_solution_set, + (int)root_crossover_solution_set_.load(std::memory_order_acquire), + root_objective_, + objective, + user_objective, + primal.size(), + dual.size(), + reduced_costs.size(), + iterations); if (!is_root_solution_set) { + const f_t previous_root_objective = root_objective_; root_crossover_soln_.x = primal; root_crossover_soln_.y = dual; root_crossover_soln_.z = reduced_costs; @@ -90,6 +106,24 @@ class branch_and_bound_t { root_crossover_soln_.user_objective = user_objective; root_crossover_soln_.iterations = iterations; root_crossover_solution_set_.store(true, std::memory_order_release); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root callback accept: root_obj_before=%.16e root_obj_after=%.16e " + "callback_solver_obj=%.16e callback_user_obj=%.16e iterations=%d\n", + previous_root_objective, + root_objective_, + objective, + user_objective, + iterations); + } else { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic root callback ignore: current_root_obj=%.16e callback_solver_obj=%.16e " + "callback_user_obj=%.16e iterations=%d\n", + root_objective_, + objective, + user_objective, + iterations); } } diff --git a/cpp/src/cuts/cuts.cpp b/cpp/src/cuts/cuts.cpp index cc2555611a..98c1c7a3c6 100644 --- a/cpp/src/cuts/cuts.cpp +++ b/cpp/src/cuts/cuts.cpp @@ -11,9 +11,91 @@ #include #include +#include +#include namespace cuopt::linear_programming::dual_simplex { +namespace { + +inline uint32_t hash_combine(uint32_t hash, uint32_t value) +{ + hash ^= value; + hash *= 16777619u; + return hash; +} + +template +uint32_t compute_sparse_cut_hash(const sparse_vector_t& cut, f_t rhs, cut_type_t cut_type) +{ + uint32_t hash = 2166136261u; + hash = hash_combine(hash, cuopt::linear_programming::detail::compute_hash(cut.i)); + hash = hash_combine(hash, cuopt::linear_programming::detail::compute_hash(cut.x)); + hash = hash_combine(hash, cuopt::linear_programming::detail::compute_hash(rhs)); + hash = hash_combine( + hash, cuopt::linear_programming::detail::compute_hash(static_cast(cut_type))); + return hash; +} + +template +uint32_t compute_stored_cut_hash(const csr_matrix_t& cut_storage, + const std::vector& rhs_storage, + const std::vector& cut_types, + i_t row) +{ + sparse_vector_t cut(cut_storage, row); + return compute_sparse_cut_hash(cut, rhs_storage[row], cut_types[row]); +} + +template +uint32_t compute_index_order_hash(const std::vector& ordered_indices) +{ + uint32_t hash = 2166136261u; + for (auto idx : ordered_indices) { + hash = hash_combine(hash, cuopt::linear_programming::detail::compute_hash(idx)); + } + return hash; +} + +uint32_t compute_ordered_hash_list_hash(const std::vector& ordered_hashes) +{ + uint32_t hash = 2166136261u; + for (auto cut_hash : ordered_hashes) { + hash = hash_combine(hash, cut_hash); + } + return hash; +} + +template +void log_lp_state_summary(const simplex_solver_settings_t& settings, + const char* label, + const lp_problem_t& lp, + const std::vector& new_slacks, + i_t cuts_delta) +{ + assert(new_slacks.size() == static_cast(lp.num_rows)); + + CUOPT_DETERMINISM_LOG_PRINTF( + settings.log, + "%s: cuts_delta=%d rows=%d cols=%d nnz=%zu slacks=%zu slack_hash=0x%x rhs_hash=0x%x " + "lower_hash=0x%x upper_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x Aval_hash=0x%x\n", + label, + cuts_delta, + lp.num_rows, + lp.num_cols, + lp.A.x.size(), + new_slacks.size(), + cuopt::linear_programming::detail::compute_hash(new_slacks), + cuopt::linear_programming::detail::compute_hash(lp.rhs), + cuopt::linear_programming::detail::compute_hash(lp.lower), + cuopt::linear_programming::detail::compute_hash(lp.upper), + cuopt::linear_programming::detail::compute_hash(lp.A.col_start), + cuopt::linear_programming::detail::compute_hash(lp.A.i), + cuopt::linear_programming::detail::compute_hash(lp.A.x)); +} + +} // namespace + template void cut_pool_t::add_cut(cut_type_t cut_type, const sparse_vector_t& cut, @@ -121,6 +203,40 @@ void cut_pool_t::score_cuts(std::vector& x_relax) std::vector sorted_indices; best_score_last_permutation(cut_distances_, sorted_indices); + if (!sorted_indices.empty()) { + std::vector candidate_order; + candidate_order.reserve(sorted_indices.size()); + std::vector candidate_cut_hashes; + candidate_cut_hashes.reserve(sorted_indices.size()); + for (auto it = sorted_indices.rbegin(); it != sorted_indices.rend(); ++it) { + const i_t cut_idx = *it; + candidate_order.push_back(cut_idx); + candidate_cut_hashes.push_back( + compute_stored_cut_hash(cut_storage_, rhs_storage_, cut_type_, cut_idx)); + } + + const uint32_t candidate_order_hash = compute_index_order_hash(candidate_order); + const uint32_t candidate_cut_hash = compute_ordered_hash_list_hash(candidate_cut_hashes); + CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, + "Deterministic cut candidate ranking: cuts=%zu order_hash=0x%x " + "cut_hash=0x%x top=", + candidate_order.size(), + candidate_order_hash, + candidate_cut_hash); + const size_t max_top_cuts = std::min((size_t)8, candidate_order.size()); + for (size_t k = 0; k < max_top_cuts; ++k) { + const i_t cut_idx = candidate_order[k]; + CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, + "%s[%d type=%d dist=%.16e hash=0x%x]", + k == 0 ? "" : " ", + cut_idx, + static_cast(cut_type_[cut_idx]), + cut_distances_[cut_idx], + candidate_cut_hashes[k]); + } + CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, "\n"); + } + const i_t max_cuts = 2000; const f_t min_orthogonality = settings_.cut_min_orthogonality; best_cuts_.reserve(std::min(max_cuts, cut_storage_.m)); @@ -151,6 +267,33 @@ void cut_pool_t::score_cuts(std::vector& x_relax) scored_cuts_++; } } + + if (!best_cuts_.empty()) { + std::vector selected_cut_hashes; + selected_cut_hashes.reserve(best_cuts_.size()); + for (auto cut_idx : best_cuts_) { + selected_cut_hashes.push_back( + compute_stored_cut_hash(cut_storage_, rhs_storage_, cut_type_, cut_idx)); + } + const uint32_t selected_cut_hash = compute_ordered_hash_list_hash(selected_cut_hashes); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic cut selection summary: selected=%zu order_hash=0x%x top=", + best_cuts_.size(), + selected_cut_hash); + const size_t max_selected_cuts = std::min((size_t)8, best_cuts_.size()); + for (size_t k = 0; k < max_selected_cuts; ++k) { + const i_t cut_idx = best_cuts_[k]; + CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, + "%s[%d type=%d dist=%.16e hash=0x%x]", + k == 0 ? "" : " ", + cut_idx, + static_cast(cut_type_[cut_idx]), + cut_distances_[cut_idx], + selected_cut_hashes[k]); + } + CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, "\n"); + } } template @@ -914,6 +1057,22 @@ void cut_generation_t::generate_mir_cuts( }); work_estimate += 10 * std::log2(10); + const uint32_t potential_row_hash = compute_index_order_hash(potential_rows); + CUOPT_DETERMINISM_LOG_PRINTF( + settings.log, + "Deterministic cut potential row ranking: pivot_var=%d rows=%zu " + "order_hash=0x%x top=", + max_off_bound_var, + potential_rows.size(), + potential_row_hash); + const size_t max_logged_rows = std::min((size_t)8, potential_rows.size()); + for (size_t k = 0; k < max_logged_rows; ++k) { + const i_t row = potential_rows[k]; + CUOPT_DETERMINISM_LOG_PRINTF( + settings.log, "%s[%d score=%.16e]", k == 0 ? "" : " ", row, score[row]); + } + CUOPT_DETERMINISM_LOG_PRINTF(settings.log, "\n"); + const i_t pivot_row = potential_rows[0]; sparse_vector_t pivot_row_inequality(Arow, pivot_row); @@ -2321,6 +2480,8 @@ i_t add_cuts(const simplex_solver_settings_t& settings, // by the current solution x* (i.e. C*x* > d), this function // adds the cuts into the LP and solves again. + log_lp_state_summary(settings, "Deterministic add_cuts state before", lp, new_slacks, cuts.m); + #ifdef CHECK_BASIS { csc_matrix_t Btest(lp.num_rows, lp.num_rows, 1); @@ -2497,6 +2658,8 @@ i_t add_cuts(const simplex_solver_settings_t& settings, solution.y.resize(lp.num_rows, 0.0); solution.z.resize(lp.num_cols, 0.0); + log_lp_state_summary(settings, "Deterministic add_cuts state after", lp, new_slacks, cuts.m); + return 0; } @@ -2517,6 +2680,8 @@ i_t remove_cuts(lp_problem_t& lp, std::vector& nonbasic_list, basis_update_mpf_t& basis_update) { + log_lp_state_summary(settings, "Deterministic remove_cuts state before", lp, new_slacks, 0); + std::vector cuts_to_remove; cuts_to_remove.reserve(lp.num_rows - original_rows); std::vector slacks_to_remove; @@ -2651,6 +2816,9 @@ i_t remove_cuts(lp_problem_t& lp, if (refactor_status == TIME_LIMIT_RETURN) { return TIME_LIMIT_RETURN; } } + log_lp_state_summary( + settings, "Deterministic remove_cuts state after", lp, new_slacks, (i_t)cuts_to_remove.size()); + return 0; } diff --git a/cpp/src/dual_simplex/basis_updates.cpp b/cpp/src/dual_simplex/basis_updates.cpp index 9c56ada50e..28a3845378 100644 --- a/cpp/src/dual_simplex/basis_updates.cpp +++ b/cpp/src/dual_simplex/basis_updates.cpp @@ -2202,7 +2202,7 @@ i_t basis_update_mpf_t::update(const sparse_vector_t& utilde // Ensure the workspace is sorted. Otherwise, the sparse dot will be incorrect. std::sort(xi_workspace_.begin() + m, xi_workspace_.begin() + m + nz, std::less()); - work_estimate_ += (m + nz) * std::log2(m + nz); + if ((m + nz) > 1) { work_estimate_ += (m + nz) * std::log2((f_t)(m + nz)); } // Gather the workspace into a column of S i_t S_start; @@ -2214,7 +2214,7 @@ i_t basis_update_mpf_t::update(const sparse_vector_t& utilde // Gather etilde into a column of S etilde.sort(); // Needs to be sorted for the sparse dot. TODO(CMM): Is etilde sorted on input? - work_estimate_ += etilde.i.size() * std::log2(etilde.i.size()); + if (etilde.i.size() > 1) { work_estimate_ += etilde.i.size() * std::log2((f_t)etilde.i.size()); } S_.append_column(etilde); work_estimate_ += 4 * etilde.i.size(); diff --git a/cpp/src/dual_simplex/bb_worker_state.hpp b/cpp/src/dual_simplex/bb_worker_state.hpp index 1714f42602..54cb5f71e5 100644 --- a/cpp/src/dual_simplex/bb_worker_state.hpp +++ b/cpp/src/dual_simplex/bb_worker_state.hpp @@ -187,7 +187,7 @@ struct bb_worker_state_t { nodes_processed_this_horizon = 0; work_units_this_horizon = 0.0; // Also sync work_context to match clock for consistent tracking - work_context.global_work_units_elapsed = horizon_start; + work_context.set_current_work(horizon_start, false); // Note: next_creation_seq is NOT reset - it's cumulative for unique identity // Initialize worker-local upper bound from global (for BSP determinism) diff --git a/cpp/src/dual_simplex/bound_flipping_ratio_test.cpp b/cpp/src/dual_simplex/bound_flipping_ratio_test.cpp index e30b067398..d9abc26fe1 100644 --- a/cpp/src/dual_simplex/bound_flipping_ratio_test.cpp +++ b/cpp/src/dual_simplex/bound_flipping_ratio_test.cpp @@ -235,7 +235,7 @@ void bound_flipping_ratio_test_t::heap_passes(const std::vector& // Remove minimum ratio from the heap and rebalance i_t heap_index = bare_idx.front(); std::pop_heap(bare_idx.begin(), bare_idx.end(), compare); - work_estimate_ += 2 * std::log2(bare_idx.size()); + if (bare_idx.size() > 1) { work_estimate_ += 2 * std::log2((f_t)bare_idx.size()); } bare_idx.pop_back(); nonbasic_entering = current_indicies[heap_index]; diff --git a/cpp/src/dual_simplex/bound_flipping_ratio_test.hpp b/cpp/src/dual_simplex/bound_flipping_ratio_test.hpp index 244ff334df..4b62c66771 100644 --- a/cpp/src/dual_simplex/bound_flipping_ratio_test.hpp +++ b/cpp/src/dual_simplex/bound_flipping_ratio_test.hpp @@ -100,7 +100,7 @@ class bound_flipping_ratio_test_t { i_t n_; i_t m_; - f_t work_estimate_; + f_t work_estimate_{0.0}; }; } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index ca3ea37855..b8c4d4e3a3 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -17,6 +17,7 @@ #include +#include #include #include @@ -85,6 +86,21 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::last_lm_config, + (int)mab_ls_config_t::last_ls_mab_option, + recombiner_t::enabled_recombiners.size()); // Read configuration ID from environment variable int max_config = -1; @@ -329,10 +345,31 @@ void diversity_manager_t::run_fj_alone(solution_t& solution) template void diversity_manager_t::run_fp_alone() { - CUOPT_LOG_DEBUG("Running FP alone!"); + CUOPT_DETERMINISM_LOG_INFO("Deterministic FP alone enter"); solution_t sol(population.best_feasible()); + sol.handle_ptr->sync_stream(); + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic FP alone input: hash=0x%x feasible=%d obj=%.16e excess=%.16e", + sol.get_hash(), + (int)sol.get_feasible(), + sol.get_user_objective(), + sol.get_total_excess()); ls.run_fp(sol, timer, &population, diversity_config.n_fp_iterations); - CUOPT_LOG_DEBUG("FP alone finished!"); + sol.handle_ptr->sync_stream(); + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic FP alone output: hash=0x%x feasible=%d obj=%.16e excess=%.16e", + sol.get_hash(), + (int)sol.get_feasible(), + sol.get_user_objective(), + sol.get_total_excess()); + auto& best_sol = population.best_feasible(); + best_sol.handle_ptr->sync_stream(); + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic FP alone population best after: hash=0x%x feasible=%d obj=%.16e excess=%.16e", + best_sol.get_hash(), + (int)best_sol.get_feasible(), + best_sol.get_user_objective(), + best_sol.get_total_excess()); } template @@ -355,6 +392,17 @@ solution_t diversity_manager_t::run_solver() // to automatically compute the solving time on scope exit auto timer_raii_guard = cuopt::scope_guard([&]() { stats.total_solve_time = timer.elapsed_time(); }); + auto log_return_solution = [&](const char* reason, solution_t& sol) { + sol.handle_ptr->sync_stream(); + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic run_solver return: reason=%s hash=0x%x feasible=%d " + "obj=%.16e excess=%.16e", + reason, + sol.get_hash(), + (int)sol.get_feasible(), + sol.get_user_objective(), + sol.get_total_excess()); + }; // Debug: Allow disabling GPU heuristics to test B&B tree determinism in isolation const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); @@ -377,7 +425,9 @@ solution_t diversity_manager_t::run_solver() ls.stop_cpufj_deterministic(); population.add_external_solutions_to_population(); - return population.best_feasible(); + auto& best_sol = population.best_feasible(); + log_return_solution("deterministic_cpufj", best_sol); + return best_sol; } if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); @@ -387,7 +437,25 @@ solution_t diversity_manager_t::run_solver() while (!check_b_b_preemption()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - return population.best_feasible(); + auto& best_sol = population.best_feasible(); + log_return_solution("heuristics_disabled", best_sol); + return best_sol; + } + + bool gpu_heuristic_producer_registered = false; + auto gpu_heuristic_producer_guard = cuopt::scope_guard([&]() { + if (!gpu_heuristic_producer_registered || context.branch_and_bound_ptr == nullptr) { return; } + auto& producer_sync = context.branch_and_bound_ptr->get_producer_sync(); + producer_sync.deregister_producer(context.gpu_heur_loop.producer_progress_ptr()); + context.gpu_heur_loop.detach_producer_sync(); + }); + if (is_gpu_heuristics_deterministic_mode(context.settings.determinism_mode) && + context.branch_and_bound_ptr != nullptr) { + auto& producer_sync = context.branch_and_bound_ptr->get_producer_sync(); + context.gpu_heur_loop.attach_producer_sync(&producer_sync); + producer_sync.register_producer(context.gpu_heur_loop.producer_progress_ptr()); + producer_sync.registration_complete(); + gpu_heuristic_producer_registered = true; } population.timer = timer; @@ -412,7 +480,11 @@ solution_t diversity_manager_t::run_solver() "The problem must not be ii"); population.initialize_population(); population.allocate_solutions(); - if (check_b_b_preemption()) { return population.best_feasible(); } + if (check_b_b_preemption()) { + auto& best_sol = population.best_feasible(); + log_return_solution("preempted_after_population_init", best_sol); + return best_sol; + } add_user_given_solutions(initial_sol_vector); CUOPT_LOG_DEBUG("DM bootstrap: initial_sol_vector size after user solutions = %lu", initial_sol_vector.size()); @@ -423,7 +495,11 @@ solution_t diversity_manager_t::run_solver() ls.start_cpufj_scratch_threads(population); } - if (check_b_b_preemption()) { return population.best_feasible(); } + if (check_b_b_preemption()) { + auto& best_sol = population.best_feasible(); + log_return_solution("preempted_before_lp", best_sol); + return best_sol; + } lp_state_t& lp_state = problem_ptr->lp_state; // resize because some constructor might be called before the presolve lp_state.resize(*problem_ptr, problem_ptr->handle_ptr->get_stream()); @@ -452,7 +528,7 @@ solution_t diversity_manager_t::run_solver() lp_settings.concurrent_halt = &global_concurrent_halt; lp_settings.work_context = &context.gpu_heur_loop; cuopt_assert(lp_settings.work_context != nullptr, "Missing deterministic work context"); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_DEBUG( "DM root LP config: dry_run=%d deterministic=%d work_limit=%.6f time_limit=%.6f", (int)diversity_config.dry_run, (int)timer.deterministic, @@ -472,7 +548,7 @@ solution_t diversity_manager_t::run_solver() pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; pdlp_settings.num_gpus = context.settings.num_gpus; pdlp_settings.presolver = presolver_t::None; - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_DEBUG( "DM root LP config: dry_run=%d deterministic=%d lp_time_limit=%.6f iter_limit=%d", (int)diversity_config.dry_run, (int)timer.deterministic, @@ -481,7 +557,7 @@ solution_t diversity_manager_t::run_solver() timer_t lp_timer(lp_time_limit); return solve_lp_with_method(*problem_ptr, pdlp_settings, lp_timer); }(); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_DEBUG( "DM root LP result: status=%d iters=%d user_obj=%.12f primal_hash=0x%x", (int)lp_result.get_termination_status(), lp_result.get_additional_termination_information().number_of_steps_taken, @@ -572,7 +648,7 @@ solution_t diversity_manager_t::run_solver() // in case the pdlp returned var boudns that are out of bounds clamp_within_var_bounds(lp_optimal_solution, problem_ptr, problem_ptr->handle_ptr); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_DEBUG( "DM root LP post-clamp: lp_optimal_solution hash=0x%x", detail::compute_hash(lp_optimal_solution, problem_ptr->handle_ptr->get_stream())); } @@ -606,32 +682,53 @@ solution_t diversity_manager_t::run_solver() } population.add_solutions_from_vec(std::move(initial_sol_vector)); - if (check_b_b_preemption()) { return population.best_feasible(); } + if (check_b_b_preemption()) { + auto& best_sol = population.best_feasible(); + log_return_solution("preempted_after_initial_population", best_sol); + return best_sol; + } if (context.settings.benchmark_info_ptr != nullptr) { context.settings.benchmark_info_ptr->objective_of_initial_population = population.best_feasible().get_user_objective(); } - if (diversity_config.dry_run) { return population.best_feasible(); } + if (diversity_config.dry_run) { + auto& best_sol = population.best_feasible(); + log_return_solution("dry_run", best_sol); + return best_sol; + } if (diversity_config.fj_only_run) { solution_t sol(*problem_ptr); run_fj_alone(sol); + log_return_solution("fj_only_run", sol); return sol; } if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { rins.enable(); } generate_solution(timer.remaining_time(), false); - if (diversity_config.initial_solution_only) { return population.best_feasible(); } + if (diversity_config.initial_solution_only) { + auto& best_sol = population.best_feasible(); + log_return_solution("initial_solution_only", best_sol); + return best_sol; + } if (work_limit_reached()) { population.add_external_solutions_to_population(); - return population.best_feasible(); + auto& best_sol = population.best_feasible(); + log_return_solution("work_limit_reached", best_sol); + return best_sol; + } + if (check_b_b_preemption()) { + auto& best_sol = population.best_feasible(); + log_return_solution("preempted_before_fp", best_sol); + return best_sol; } - if (check_b_b_preemption()) { return population.best_feasible(); } run_fp_alone(); population.add_external_solutions_to_population(); - return population.best_feasible(); + auto& best_sol = population.best_feasible(); + log_return_solution("post_fp_alone", best_sol); + return best_sol; }; template @@ -902,6 +999,14 @@ std::pair, bool> diversity_manager_t::recombine( } } } + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic recombiner selection: requested=%s selected_index=%d chosen=%s " + "enabled_size=%zu last_choice_before=%d", + recombiner_t::recombiner_name(recombiner_type), + (int)selected_index, + recombiner_t::recombiner_name(recombiner), + recombiner_t::enabled_recombiners.size(), + mab_recombiner.last_chosen_option); mab_recombiner.set_last_chosen_option(selected_index); recombine_stats.add_attempt((recombiner_enum_t)recombiner); recombine_stats.start_recombiner_time(); diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index f65362c9fc..a8d2cec6d4 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -179,6 +180,7 @@ void population_t::add_external_solution(const std::vector& solut template void population_t::add_external_solutions_to_population() { + if (is_deterministic_mode(context.settings.determinism_mode)) { return; } // don't do early exit checks here. mutex needs to be acquired to prevent race conditions auto new_sol_vector = get_external_solutions(); add_solutions_from_vec(std::move(new_sol_vector)); @@ -418,13 +420,21 @@ std::pair population_t::add_solution(solution_t&& // for hash computation, quality calculation, and similarity comparisons. sol.handle_ptr->sync_stream(); population_hash_map.insert(sol); - double sol_cost = sol.get_quality(weights); - bool best_updated = false; + double sol_cost = sol.get_quality(weights); + bool best_updated = false; + const bool had_best_feasible_before = solutions[0].first; + const uint32_t best_hash_before = had_best_feasible_before ? solutions[0].second.get_hash() : 0U; + const f_t best_user_objective_before = had_best_feasible_before + ? solutions[0].second.get_user_objective() + : std::numeric_limits::infinity(); + const f_t best_quality_before = + had_best_feasible_before ? indices[0].second : std::numeric_limits::infinity(); + const uint32_t candidate_hash = sol.get_hash(); CUOPT_LOG_DEBUG("Adding solution with quality %f and objective %f n_integers %d, hash %x!", sol_cost, sol.get_user_objective(), sol.n_assigned_integers, - sol.get_hash()); + candidate_hash); // We store the best feasible found so far at index 0. if (sol.get_feasible() && (solutions[0].first == false || sol_cost + OBJECTIVE_EPSILON < indices[0].second)) { @@ -436,11 +446,40 @@ std::pair population_t::add_solution(solution_t&& solutions[0].second = std::move(temp_sol); indices[0].second = sol_cost; best_updated = true; + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic population best update: candidate_hash=0x%x candidate_feasible=%d " + "candidate_obj=%.16e candidate_quality=%.16e best_hash_before=0x%x " + "best_obj_before=%.16e best_quality_before=%.16e best_hash_after=0x%x " + "best_obj_after=%.16e best_quality_after=%.16e", + candidate_hash, + (int)sol.get_feasible(), + sol.get_user_objective(), + sol_cost, + best_hash_before, + best_user_objective_before, + best_quality_before, + solutions[0].second.get_hash(), + solutions[0].second.get_user_objective(), + indices[0].second); } // Fast reject if (indices.size() == max_solutions && indices.back().second <= sol_cost + OBJECTIVE_EPSILON) { CUOPT_LOG_TRACE("Rejecting solution objective is not better!"); + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic population add decision: candidate_hash=0x%x candidate_feasible=%d " + "candidate_obj=%.16e candidate_quality=%.16e action=reject_fast best_updated=%d " + "best_hash_before=0x%x best_obj_before=%.16e best_quality_before=%.16e " + "worst_quality=%.16e", + candidate_hash, + (int)sol.get_feasible(), + sol.get_user_objective(), + sol_cost, + (int)best_updated, + best_hash_before, + best_user_objective_before, + best_quality_before, + indices.back().second); return std::make_pair(-1, best_updated); } @@ -471,6 +510,24 @@ std::pair population_t::add_solution(solution_t&& int inserted_pos = insert_index(std::pair((size_t)hint, sol_cost)); cuopt_assert(test_invariant(), "Population invariant doesn't hold"); test_invariant(); + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic population add decision: candidate_hash=0x%x candidate_feasible=%d " + "candidate_obj=%.16e candidate_quality=%.16e action=insert_new inserted_pos=%d " + "best_updated=%d best_hash_before=0x%x best_obj_before=%.16e best_quality_before=%.16e " + "best_hash_after=0x%x best_obj_after=%.16e best_quality_after=%.16e", + candidate_hash, + (int)solutions[hint].second.get_feasible(), + solutions[hint].second.get_user_objective(), + sol_cost, + inserted_pos, + (int)best_updated, + best_hash_before, + best_user_objective_before, + best_quality_before, + solutions[0].first ? solutions[0].second.get_hash() : 0U, + solutions[0].first ? solutions[0].second.get_user_objective() + : std::numeric_limits::infinity(), + solutions[0].first ? indices[0].second : std::numeric_limits::infinity()); return std::make_pair(inserted_pos, best_updated); } else if (sol_cost + OBJECTIVE_EPSILON < indices[index].second) { @@ -485,11 +542,50 @@ std::pair population_t::add_solution(solution_t&& int inserted_pos = insert_index(std::pair((size_t)free, sol_cost)); cuopt_assert(test_invariant(), "Population invariant doesn't hold"); test_invariant(); + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic population add decision: candidate_hash=0x%x candidate_feasible=%d " + "candidate_obj=%.16e candidate_quality=%.16e action=replace_similar similar_index=%zu " + "inserted_pos=%d best_updated=%d best_hash_before=0x%x best_obj_before=%.16e " + "best_quality_before=%.16e best_hash_after=0x%x best_obj_after=%.16e " + "best_quality_after=%.16e", + candidate_hash, + (int)solutions[free].second.get_feasible(), + solutions[free].second.get_user_objective(), + sol_cost, + index, + inserted_pos, + (int)best_updated, + best_hash_before, + best_user_objective_before, + best_quality_before, + solutions[0].first ? solutions[0].second.get_hash() : 0U, + solutions[0].first ? solutions[0].second.get_user_objective() + : std::numeric_limits::infinity(), + solutions[0].first ? indices[0].second : std::numeric_limits::infinity()); return std::make_pair(inserted_pos, best_updated); } CUOPT_LOG_TRACE("Adding solution failed!"); cuopt_assert(test_invariant(), "Population invariant doesn't hold"); test_invariant(); + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic population add decision: candidate_hash=0x%x candidate_feasible=%d " + "candidate_obj=%.16e candidate_quality=%.16e action=reject_similar_or_worse " + "similar_index=%zu best_updated=%d best_hash_before=0x%x best_obj_before=%.16e " + "best_quality_before=%.16e best_hash_after=0x%x best_obj_after=%.16e " + "best_quality_after=%.16e", + candidate_hash, + (int)sol.get_feasible(), + sol.get_user_objective(), + sol_cost, + index, + (int)best_updated, + best_hash_before, + best_user_objective_before, + best_quality_before, + solutions[0].first ? solutions[0].second.get_hash() : 0U, + solutions[0].first ? solutions[0].second.get_user_objective() + : std::numeric_limits::infinity(), + solutions[0].first ? indices[0].second : std::numeric_limits::infinity()); return std::make_pair(-1, best_updated); } diff --git a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh index fbb354e243..a4a9be59e2 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,18 @@ __global__ void assign_same_variables_kernel(typename solution_t::view template class recombiner_t { public: + static const char* recombiner_name(recombiner_enum_t recombiner) + { + switch (recombiner) { + case recombiner_enum_t::BOUND_PROP: return "BOUND_PROP"; + case recombiner_enum_t::FP: return "FP"; + case recombiner_enum_t::LINE_SEGMENT: return "LINE_SEGMENT"; + case recombiner_enum_t::SUB_MIP: return "SUB_MIP"; + case recombiner_enum_t::SIZE: return "SIZE"; + } + return "UNKNOWN"; + } + recombiner_t(mip_solver_context_t& context_, i_t n_integer_vars, const raft::handle_t* handle_ptr) @@ -214,25 +227,57 @@ class recombiner_t { const problem_t& problem) { std::unordered_set enabled_recombiners; + const bool disable_fp_and_submip_for_expensive_fix = problem.expensive_to_fix_vars; + const i_t n_continuous_vars = problem.n_variables - problem.n_integer_vars; + const bool disable_submip_for_continuous_limit = + n_continuous_vars > (i_t)sub_mip_recombiner_config_t::max_continuous_vars; + const bool disable_submip_for_determinism = + context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC || + context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; for (auto recombiner : recombiner_types) { enabled_recombiners.insert(recombiner); } - if (problem.expensive_to_fix_vars) { + if (disable_fp_and_submip_for_expensive_fix) { enabled_recombiners.erase(recombiner_enum_t::FP); enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } // check the size of the continous vars - if (problem.n_variables - problem.n_integer_vars > - (i_t)sub_mip_recombiner_config_t::max_continuous_vars) { + if (disable_submip_for_continuous_limit) { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } // submip not supported in deterministic mode yet - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC || - context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS) { - enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); - } + if (disable_submip_for_determinism) { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } recombiner_t::enabled_recombiners = std::vector(enabled_recombiners.begin(), enabled_recombiners.end()); + cuopt_assert(!recombiner_t::enabled_recombiners.empty(), "No recombiners enabled after init"); + const char* enabled_0 = recombiner_t::enabled_recombiners.size() > 0 + ? recombiner_name(recombiner_t::enabled_recombiners[0]) + : "NONE"; + const char* enabled_1 = recombiner_t::enabled_recombiners.size() > 1 + ? recombiner_name(recombiner_t::enabled_recombiners[1]) + : "NONE"; + const char* enabled_2 = recombiner_t::enabled_recombiners.size() > 2 + ? recombiner_name(recombiner_t::enabled_recombiners[2]) + : "NONE"; + const char* enabled_3 = recombiner_t::enabled_recombiners.size() > 3 + ? recombiner_name(recombiner_t::enabled_recombiners[3]) + : "NONE"; + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic recombiner init: expensive_to_fix=%d n_continuous=%d " + "max_continuous=%zu disable_fp_submip_expensive=%d " + "disable_submip_continuous=%d disable_submip_deterministic=%d size=%zu " + "order=[%s,%s,%s,%s]", + (int)problem.expensive_to_fix_vars, + (int)n_continuous_vars, + sub_mip_recombiner_config_t::max_continuous_vars, + (int)disable_fp_and_submip_for_expensive_fix, + (int)disable_submip_for_continuous_limit, + (int)disable_submip_for_determinism, + recombiner_t::enabled_recombiners.size(), + enabled_0, + enabled_1, + enabled_2, + enabled_3); } mip_solver_context_t& context; diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu index 9500614473..60f933e5b7 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -1062,6 +1063,14 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) { auto& data = *climbers[climber_idx]; auto v = data.view(); // == climber_views[climber_idx] + const bool post_incumbents_to_bb = + is_gpu_heuristics_deterministic_mode(context.settings.determinism_mode) && + context.branch_and_bound_ptr != nullptr; + const double work_units_at_start = context.gpu_heur_loop.current_work(); + const bool publish_progress = post_incumbents_to_bb && std::isfinite(settings.work_limit) && + settings.work_limit > 0.0 && settings.iteration_limit > 0 && + settings.iteration_limit != std::numeric_limits::max(); + f_t last_posted_objective = std::numeric_limits::infinity(); auto climber_stream = data.stream.view(); if (climber_idx == 0) climber_stream = handle_ptr->get_stream(); @@ -1138,6 +1147,12 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) } i_t iterations = data.iterations.value(climber_stream); + if (publish_progress) { + const double progress_ratio = + std::min(1.0, (double)iterations / (double)settings.iteration_limit); + const double published_work = work_units_at_start + settings.work_limit * progress_ratio; + context.gpu_heur_loop.set_current_work(published_work); + } // make sure we have the current incumbent saved (e.g. in the case of a timeout) update_best_solution_kernel<<<1, blocks_resetmoves, 0, climber_stream>>>(v); // check feasibility with the relative tolerance rather than the violation score @@ -1152,6 +1167,18 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) bool is_feasible = solution.compute_feasibility(); solution.handle_ptr->sync_stream(); + if (post_incumbents_to_bb && is_feasible && + solution.get_objective() < last_posted_objective) { + const double work_timestamp = context.gpu_heur_loop.current_producer_work(); + context.branch_and_bound_ptr->queue_external_solution_deterministic( + solution.get_host_assignment(), work_timestamp); + last_posted_objective = solution.get_objective(); + CUOPT_DETERMINISM_LOG_INFO("FJ deterministic post: obj=%f work_ts=%f hash=0x%x", + solution.get_user_objective(), + work_timestamp, + solution.get_hash()); + } + if (limit_reached) { break; } if (is_feasible) { @@ -1443,7 +1470,10 @@ i_t fj_t::solve(solution_t& solution) } CUOPT_LOG_DEBUG("FJ: recording work %fwu for %d iterations", work_to_record, iterations); - timer.record_work(work_to_record); + const double already_published_work = + std::max(0.0, context.gpu_heur_loop.current_work() - timer.work_units_at_start); + const double remaining_work_to_record = std::max(0.0, work_to_record - already_published_work); + timer.record_work(remaining_work_to_record); } CUOPT_LOG_DEBUG("FJ sol hash %x", solution.get_hash()); diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu index 6e30c48fd2..ff288414e9 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu @@ -11,11 +11,15 @@ #include "feasibility_jump_impl_common.cuh" #include "fj_cpu.cuh" +#include #include #include +#include #include +#include +#include #include #include #include @@ -38,6 +42,24 @@ namespace cuopt::linear_programming::detail { +namespace { + +double read_positive_work_unit_scale(const char* env_name) +{ + const char* env_value = std::getenv(env_name); + if (env_value == nullptr || env_value[0] == '\0') { return 1.0; } + + errno = 0; + char* end_ptr = nullptr; + const double parsed_value = std::strtod(env_value, &end_ptr); + const bool valid_value = errno == 0 && end_ptr != env_value && *end_ptr == '\0' && + std::isfinite(parsed_value) && parsed_value > 0.0; + cuopt_assert(valid_value, "Invalid CPUFJ work-unit scale env var"); + return parsed_value; +} + +} // namespace + template thrust::tuple get_mtm_for_bound(const typename fj_t::climber_data_t::view_t& fj, i_t var_idx, @@ -1308,6 +1330,15 @@ std::unique_ptr> fj_t::create_cpu_climber( // Initialize fj_cpu with all the data init_fj_cpu(*fj_cpu, solution, left_weights, right_weights, objective_weight); + const double cpu_work_unit_scale = + context.settings.cpufj_work_unit_scale != 1.0 + ? context.settings.cpufj_work_unit_scale + : read_positive_work_unit_scale("CUOPT_CPUFJ_WORK_UNIT_SCALE"); + fj_cpu->work_unit_bias *= cpu_work_unit_scale; + if (cpu_work_unit_scale != 1.0) { + CUOPT_DETERMINISM_LOG_DEBUG( + "CPUFJ using work-unit scale %f (bias=%f)", cpu_work_unit_scale, fj_cpu->work_unit_bias); + } fj_cpu->settings = settings; if (randomize_params) { auto rng = std::mt19937(cuopt::seed_generator::get_seed()); diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index f83139d9f2..6eac1c89ee 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,9 @@ namespace cuopt::linear_programming::detail { +static double local_search_best_obj = std::numeric_limits::max(); +static population_t* pop_ptr = nullptr; + template local_search_t::local_search_t(mip_solver_context_t& context_, rmm::device_uvector& lp_optimal_solution_) @@ -53,11 +57,14 @@ local_search_t::local_search_t(mip_solver_context_t& context cpu_fj.fj_ptr = &fj; } scratch_cpu_fj_on_lp_opt.fj_ptr = &fj; + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic solve start local_search state: seed_state=%lld " + "local_search_best_obj=%.16e pop_ptr_set=%d", + (long long)cuopt::seed_generator::peek_seed(), + local_search_best_obj, + (int)(pop_ptr != nullptr)); } -static double local_search_best_obj = std::numeric_limits::max(); -static population_t* pop_ptr = nullptr; - template void local_search_t::start_cpufj_scratch_threads(population_t& population) { diff --git a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu index d5cebd9dfa..aa421660a9 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu @@ -499,6 +499,18 @@ bool bounds_repair_t::repair_problem(problem_t& problem, if (timer.deterministic) { const double setup_work = estimate_bounds_repair_setup_work(problem); record_estimated_work(timer, &total_estimated_work, setup_work); + CUOPT_LOG_DEBUG( + "Repair entry: pb_hash=0x%x bounds_hash=0x%x violated_hash=0x%x n_violated=%d " + "best_violation=%.6f timer_rem=%.6f total_work=%.6f setup_work=%.6f", + problem.get_fingerprint(), + detail::compute_hash(make_span(problem.variable_bounds), handle_ptr->get_stream()), + detail::compute_hash(make_span(violated_constraints, 0, h_n_violated_cstr), + handle_ptr->get_stream()), + h_n_violated_cstr, + best_violation, + timer.remaining_time(), + total_estimated_work, + setup_work); } i_t no_candidate_in_a_row = 0; // TODO: do this better @@ -510,17 +522,46 @@ bool bounds_repair_t::repair_problem(problem_t& problem, h_n_violated_cstr, best_violation, curr_violation); + if (timer.deterministic) { + CUOPT_LOG_DEBUG( + "Repair iter entry: iter=%d pb_hash=0x%x bounds_hash=0x%x violated_hash=0x%x " + "n_violated=%d best_violation=%.6f curr_violation=%.6f timer_rem=%.6f total_work=%.6f", + repair_iterations, + problem.get_fingerprint(), + detail::compute_hash(make_span(problem.variable_bounds), handle_ptr->get_stream()), + detail::compute_hash(make_span(violated_constraints, 0, h_n_violated_cstr), + handle_ptr->get_stream()), + h_n_violated_cstr, + best_violation, + curr_violation, + timer.remaining_time(), + total_estimated_work); + } if (timer.check_time_limit()) { break; } i_t curr_cstr = get_random_cstr(); // best way would be to check a variable cycle, but this is easier and more performant bool is_cycle = detect_cycle(curr_cstr); if (is_cycle) { CUOPT_LOG_DEBUG("Repair: cycle detected at cstr %d", curr_cstr); } // in parallel compute the best shift and best respective damage - i_t n_candidates = compute_best_shift(problem, original_problem, curr_cstr); + i_t n_candidates = compute_best_shift(problem, original_problem, curr_cstr); + double shift_work = 0.0; if (timer.deterministic) { - const double shift_work = - estimate_bounds_repair_shift_work(problem, curr_cstr, n_candidates, is_cycle); + shift_work = estimate_bounds_repair_shift_work(problem, curr_cstr, n_candidates, is_cycle); record_estimated_work(timer, &total_estimated_work, shift_work); + CUOPT_LOG_DEBUG( + "Repair iter shift: iter=%d curr_cstr=%d cycle=%d n_candidates=%d cand_var_hash=0x%x " + "cand_shift_hash=0x%x shift_work=%.6f timer_rem=%.6f total_work=%.6f", + repair_iterations, + curr_cstr, + (int)is_cycle, + n_candidates, + detail::compute_hash(make_span(candidates.variable_index, 0, n_candidates), + handle_ptr->get_stream()), + detail::compute_hash(make_span(candidates.bound_shift, 0, n_candidates), + handle_ptr->get_stream()), + shift_work, + timer.remaining_time(), + total_estimated_work); } // if no candidate is there continue with another constraint if (n_candidates == 0) { @@ -536,9 +577,22 @@ bool bounds_repair_t::repair_problem(problem_t& problem, CUOPT_LOG_TRACE("Repair: number of candidates %d", n_candidates); // among the ones that have a valid shift value, compute the damage compute_damages(problem, n_candidates); + double damage_work = 0.0; if (timer.deterministic) { - const double damage_work = estimate_bounds_repair_damage_work(problem, n_candidates); + damage_work = estimate_bounds_repair_damage_work(problem, n_candidates); record_estimated_work(timer, &total_estimated_work, damage_work); + CUOPT_LOG_DEBUG( + "Repair iter damage: iter=%d curr_cstr=%d cand_cdelta_hash=0x%x cand_damage_hash=0x%x " + "damage_work=%.6f timer_rem=%.6f total_work=%.6f", + repair_iterations, + curr_cstr, + detail::compute_hash(make_span(candidates.cstr_delta, 0, n_candidates), + handle_ptr->get_stream()), + detail::compute_hash(make_span(candidates.damage, 0, n_candidates), + handle_ptr->get_stream()), + damage_work, + timer.remaining_time(), + total_estimated_work); } // get the best damage i_t best_cstr_delta = candidates.cstr_delta.front_element(handle_ptr->get_stream()); @@ -546,19 +600,40 @@ bool bounds_repair_t::repair_problem(problem_t& problem, CUOPT_LOG_TRACE( "Repair: best_cstr_delta value %d best_damage %f", best_cstr_delta, best_damage); i_t best_move_idx; + i_t n_of_eligible_candidates = -1; + const double random_draw = best_cstr_delta > 0 ? rand_double(0, 1, gen) : -1.0; + const bool choose_random_move = (best_cstr_delta > 0 && random_draw < p) || is_cycle; // if the best damage is positive and we are within the prop (paper uses 0.75) - if ((best_cstr_delta > 0 && rand_double(0, 1, gen) < p) || is_cycle) { + if (choose_random_move) { // pick a random move from the candidate list best_move_idx = get_random_idx(n_candidates); } else { // filter the moves with best_damage(it can be zero or not) and then pick a candidate among // them - i_t n_of_eligible_candidates = + n_of_eligible_candidates = find_cutoff_index(candidates, best_cstr_delta, best_damage, n_candidates); cuopt_assert(n_of_eligible_candidates > 0, ""); CUOPT_LOG_TRACE("n_of_eligible_candidates %d", n_of_eligible_candidates); best_move_idx = get_random_idx(n_of_eligible_candidates); } + if (timer.deterministic) { + CUOPT_LOG_DEBUG( + "Repair iter choice: iter=%d curr_cstr=%d best_cstr_delta=%d best_damage=%.6f " + "choose_random=%d random_draw=%.6f eligible=%d best_move_idx=%d move_var=%d " + "move_shift=%.6f move_cdelta=%d move_damage=%.6f", + repair_iterations, + curr_cstr, + best_cstr_delta, + best_damage, + (int)choose_random_move, + random_draw, + n_of_eligible_candidates, + best_move_idx, + candidates.variable_index.element(best_move_idx, handle_ptr->get_stream()), + candidates.bound_shift.element(best_move_idx, handle_ptr->get_stream()), + candidates.cstr_delta.element(best_move_idx, handle_ptr->get_stream()), + candidates.damage.element(best_move_idx, handle_ptr->get_stream())); + } CUOPT_LOG_TRACE("Repair: selected best_move_idx %d var id %d shift %f cstr_delta %d damage %f", best_move_idx, candidates.variable_index.element(best_move_idx, handle_ptr->get_stream()), @@ -570,11 +645,26 @@ bool bounds_repair_t::repair_problem(problem_t& problem, // TODO we might optimize this to only calculate the changed constraints curr_violation = get_ii_violation(problem); const bool improved_violation = curr_violation < best_violation; + double refresh_work = 0.0; if (timer.deterministic) { - const double refresh_work = - bounds_repair_move_base_work + - estimate_bounds_repair_violation_refresh_work(problem, improved_violation); + refresh_work = bounds_repair_move_base_work + + estimate_bounds_repair_violation_refresh_work(problem, improved_violation); record_estimated_work(timer, &total_estimated_work, refresh_work); + CUOPT_LOG_DEBUG( + "Repair iter post: iter=%d pb_hash=0x%x bounds_hash=0x%x violated_hash=0x%x " + "n_violated=%d curr_violation=%.6f improved=%d refresh_work=%.6f total_work=%.6f " + "timer_rem=%.6f", + repair_iterations, + problem.get_fingerprint(), + detail::compute_hash(make_span(problem.variable_bounds), handle_ptr->get_stream()), + detail::compute_hash(make_span(violated_constraints, 0, h_n_violated_cstr), + handle_ptr->get_stream()), + h_n_violated_cstr, + curr_violation, + (int)improved_violation, + refresh_work, + total_estimated_work, + timer.remaining_time()); CUOPT_LOG_DEBUG("Repair iter work: cstr=%d candidates=%d cycle=%d improved=%d total=%.6f", curr_cstr, n_candidates, diff --git a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu index 8208d1c613..4eebf2411b 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu @@ -1008,10 +1008,56 @@ bool constraint_prop_t::find_integer( // timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; work_limit_timer_t repair_timer(context.gpu_heur_loop, timer.remaining_time() / 5); save_bounds(sol); + auto remaining_unset = make_span(unset_integer_vars, set_count, unset_integer_vars.size()); + const auto orig_pb_fingerprint = orig_sol.problem_ptr->get_fingerprint(); + const auto curr_pb_fingerprint = sol.problem_ptr->get_fingerprint(); + CUOPT_LOG_DEBUG( + "CP repair entry: set_count=%lu unset_remaining=%lu unset_hash=0x%x assign_hash=0x%x " + "bounds_hash=0x%x orig_pb_hash=0x%x curr_pb_hash=0x%x ii_counts=%d/%d recovery=%d " + "rounding_ii=%d failed_repairs=%d interval=%d timer_rem=%.6f max_timer_rem=%.6f " + "repair_budget=%.6f", + set_count, + remaining_unset.size(), + detail::compute_hash(remaining_unset, sol.handle_ptr->get_stream()), + sol.get_hash(), + detail::compute_hash(make_span(sol.problem_ptr->variable_bounds), + sol.handle_ptr->get_stream()), + orig_pb_fingerprint, + curr_pb_fingerprint, + multi_probe.infeas_constraints_count_0, + multi_probe.infeas_constraints_count_1, + (int)recovery_mode, + (int)rounding_ii, + n_failed_repair_iterations, + bounds_prop_interval, + timer.remaining_time(), + max_timer.remaining_time(), + repair_timer.remaining_time()); // update bounds and run repair procedure repair_attempted = true; bounds_repaired = run_repair_procedure(*sol.problem_ptr, *orig_sol.problem_ptr, repair_timer, sol.handle_ptr); + remaining_unset = make_span(unset_integer_vars, set_count, unset_integer_vars.size()); + CUOPT_LOG_DEBUG( + "CP repair exit: success=%d set_count=%lu unset_remaining=%lu unset_hash=0x%x " + "assign_hash=0x%x bounds_hash=0x%x curr_pb_hash=0x%x ii_counts=%d/%d " + "infeas_after_bounds_update=%d timer_rem=%.6f max_timer_rem=%.6f repair_elapsed=%.6f " + "repair_remaining=%.6f", + (int)bounds_repaired, + set_count, + remaining_unset.size(), + detail::compute_hash(remaining_unset, sol.handle_ptr->get_stream()), + sol.get_hash(), + detail::compute_hash(make_span(sol.problem_ptr->variable_bounds), + sol.handle_ptr->get_stream()), + curr_pb_fingerprint, + multi_probe.infeas_constraints_count_0, + multi_probe.infeas_constraints_count_1, + bounds_update.infeas_constraints_count, + timer.remaining_time(), + max_timer.remaining_time(), + repair_timer.elapsed_time(), + repair_timer.remaining_time()); if (!bounds_repaired) { restore_bounds(sol); n_failed_repair_iterations++; diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 0db033a0f5..e4c131918d 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -219,6 +220,15 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, // Initialize seed generator if a specific seed is requested if (settings.seed >= 0) { cuopt::seed_generator::set_seed(settings.seed); } + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic solve start settings: seed=%lld seed_state=%lld det_mode=%d " + "work_limit=%.6f max_cut_passes=%d num_cpu_threads=%d", + (long long)settings.seed, + (long long)cuopt::seed_generator::peek_seed(), + (int)settings.determinism_mode, + (double)settings.work_limit, + settings.max_cut_passes, + settings.num_cpu_threads); raft::common::nvtx::range fun_scope("Running solver"); @@ -241,14 +251,13 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, op_problem.get_handle_ptr()->get_stream()); } - auto timer = timer_t(time_limit); + auto timer = timer_t(time_limit); + const bool deterministic_run = detail::is_deterministic_mode(settings.determinism_mode); double presolve_time = 0.0; std::unique_ptr> presolver; std::optional> presolve_result; - detail::problem_t problem(op_problem, - settings.get_tolerances(), - detail::is_deterministic_mode(settings.determinism_mode)); + detail::problem_t problem(op_problem, settings.get_tolerances(), deterministic_run); auto run_presolve = settings.presolver != presolver_t::None; run_presolve = run_presolve && settings.initial_solutions.size() == 0; @@ -273,9 +282,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, // allocate not more than 10% of the time limit to presolve. // Note that this is not the presolve time, but the time limit for presolve. double presolve_time_limit = std::min(0.1 * time_limit, 60.0); - if (detail::is_deterministic_mode(settings.determinism_mode)) { - presolve_time_limit = std::numeric_limits::infinity(); - } + if (deterministic_run) { presolve_time_limit = std::numeric_limits::infinity(); } presolver = std::make_unique>(); auto result = presolver->apply(op_problem, cuopt::linear_programming::problem_category_t::MIP, @@ -292,7 +299,8 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, } presolve_result.emplace(std::move(*result)); - problem = detail::problem_t(presolve_result->reduced_problem); + problem = detail::problem_t( + presolve_result->reduced_problem, settings.get_tolerances(), deterministic_run); problem.set_papilo_presolve_data(presolver.get(), presolve_result->reduced_to_original_map, presolve_result->original_to_reduced_map, @@ -335,7 +343,8 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, reduced_costs.data(), reduced_costs.data() + reduced_costs.size(), std::numeric_limits::signaling_NaN()); - detail::problem_t full_problem(op_problem); + detail::problem_t full_problem( + op_problem, settings.get_tolerances(), deterministic_run); detail::solution_t full_sol(full_problem); full_sol.copy_new_assignment( cuopt::host_copy(primal_solution, op_problem.get_handle_ptr()->get_stream())); diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index b8e1366d9c..454306d099 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,7 @@ struct branch_and_bound_solution_helper_t { void solution_callback(std::vector& solution, f_t objective) { + if (is_deterministic_mode(settings_.deterministic_mode)) { return; } dm->population.add_external_solution(solution, objective, solution_origin_t::BRANCH_AND_BOUND); dm->rins.new_best_incumbent_callback(solution); } @@ -287,15 +289,19 @@ solution_t mip_solver_t::run_solver() context.work_unit_scheduler_.register_context(branch_and_bound->get_work_unit_context()); - context.problem_ptr->set_root_relaxation_solution_callback = - std::bind(&dual_simplex::branch_and_bound_t::set_root_relaxation_solution, - branch_and_bound.get(), - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3, - std::placeholders::_4, - std::placeholders::_5, - std::placeholders::_6); + if (is_deterministic_mode(context.settings.determinism_mode)) { + context.problem_ptr->set_root_relaxation_solution_callback = nullptr; + } else { + context.problem_ptr->set_root_relaxation_solution_callback = + std::bind(&dual_simplex::branch_and_bound_t::set_root_relaxation_solution, + branch_and_bound.get(), + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6); + } if (timer_.check_time_limit()) { CUOPT_LOG_INFO("Time limit reached during B&B setup"); @@ -326,6 +332,23 @@ solution_t mip_solver_t::run_solver() context.problem_ptr->get_user_obj_from_solver_obj(branch_and_bound_solution.lower_bound)); } if (bb_status == dual_simplex::mip_status_t::INFEASIBLE) { sol.set_problem_fully_reduced(); } + if (is_deterministic_mode(context.settings.determinism_mode) && + std::isfinite(branch_and_bound_solution.objective)) { + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic solver B&B overwrite: bb_status=%d bb_obj=%.16e bb_lower=%.16e " + "bb_hash=0x%x dm_hash=0x%x nodes=%d simplex_iterations=%d", + (int)bb_status, + branch_and_bound_solution.objective, + branch_and_bound_solution.lower_bound, + detail::compute_hash(branch_and_bound_solution.x), + sol.get_hash(), + branch_and_bound_solution.nodes_explored, + branch_and_bound_solution.simplex_iterations); + solution_t bb_sol(*context.problem_ptr); + bb_sol.copy_new_assignment(branch_and_bound_solution.x); + bb_sol.compute_feasibility(); + sol = std::move(bb_sol); + } context.stats.num_nodes = branch_and_bound_solution.nodes_explored; context.stats.num_simplex_iterations = branch_and_bound_solution.simplex_iterations; } diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index 8f401fe514..530da15243 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -54,6 +54,12 @@ struct mip_solver_context_t { stats.set_solution_bound(problem_ptr->maximize ? std::numeric_limits::infinity() : -std::numeric_limits::infinity()); gpu_heur_loop.deterministic = is_deterministic_mode(settings.determinism_mode); + cuopt_assert(settings.cpufj_work_unit_scale > 0.0, "CPUFJ work-unit scale must be positive"); + cuopt_assert(settings.gpu_heur_work_unit_scale > 0.0, + "GPU heuristic work-unit scale must be positive"); + gpu_heur_loop.producer_progress_scale = settings.gpu_heur_work_unit_scale != 1.0 + ? settings.gpu_heur_work_unit_scale + : gpu_heur_loop.producer_progress_scale; } mip_solver_context_t(const mip_solver_context_t&) = delete; diff --git a/cpp/src/utilities/determinism_log.hpp b/cpp/src/utilities/determinism_log.hpp new file mode 100644 index 0000000000..03f31f414c --- /dev/null +++ b/cpp/src/utilities/determinism_log.hpp @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, 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 + +#ifndef CUOPT_DETERMINISM_LOG_PRINTF +#define CUOPT_DETERMINISM_LOG_PRINTF(logger, ...) \ + do { \ + } while (0) +#endif + +#ifndef CUOPT_DETERMINISM_LOG_INFO +#define CUOPT_DETERMINISM_LOG_INFO(...) \ + do { \ + } while (0) +#endif + +#ifndef CUOPT_DETERMINISM_LOG_DEBUG +#define CUOPT_DETERMINISM_LOG_DEBUG(...) \ + do { \ + } while (0) +#endif diff --git a/cpp/src/utilities/seed_generator.cuh b/cpp/src/utilities/seed_generator.cuh index 7692ff4676..8206fbcbfe 100644 --- a/cpp/src/utilities/seed_generator.cuh +++ b/cpp/src/utilities/seed_generator.cuh @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -43,6 +43,8 @@ class seed_generator { static int64_t get_seed() { return seed_++; } #endif + static int64_t peek_seed() { return seed_; } + public: seed_generator(seed_generator const&) = delete; void operator=(seed_generator const&) = delete; diff --git a/cpp/src/utilities/work_limit_context.hpp b/cpp/src/utilities/work_limit_context.hpp index d334cafbf5..51ce67a754 100644 --- a/cpp/src/utilities/work_limit_context.hpp +++ b/cpp/src/utilities/work_limit_context.hpp @@ -17,29 +17,147 @@ #pragma once #include +#include +#include +#include +#include +#include #include #include +#include +#include +#include "producer_sync.hpp" #include "timer.hpp" #include "work_unit_scheduler.hpp" namespace cuopt { +inline double read_work_unit_scale_env_or_default(const char* env_name, double default_value) +{ + const char* env_value = std::getenv(env_name); + if (env_value == nullptr || env_value[0] == '\0') { return default_value; } + + errno = 0; + char* end_ptr = nullptr; + const double parsed_value = std::strtod(env_value, &end_ptr); + const bool valid_value = errno == 0 && end_ptr != env_value && *end_ptr == '\0' && + std::isfinite(parsed_value) && parsed_value > 0.0; + cuopt_assert(valid_value, "Invalid work-unit scale env var"); + return parsed_value; +} + struct work_limit_context_t { double global_work_units_elapsed{0.0}; double total_sync_time{0.0}; // Total time spent waiting at sync barriers (seconds) bool deterministic{false}; work_unit_scheduler_t* scheduler{nullptr}; + producer_sync_t* producer_sync{nullptr}; std::string name; + std::unique_ptr> producer_work_units_elapsed{ + std::make_unique>(0.0)}; + double producer_progress_scale{ + read_work_unit_scale_env_or_default("CUOPT_GPU_HEUR_WORK_UNIT_SCALE", 1.0)}; work_limit_context_t(const std::string& name) : name(name) {} + work_limit_context_t(const work_limit_context_t& other) + : global_work_units_elapsed(other.global_work_units_elapsed), + total_sync_time(other.total_sync_time), + deterministic(other.deterministic), + scheduler(other.scheduler), + producer_sync(other.producer_sync), + name(other.name), + producer_work_units_elapsed(std::make_unique>( + other.producer_work_units_elapsed->load(std::memory_order_acquire))), + producer_progress_scale(other.producer_progress_scale) + { + } + + work_limit_context_t(work_limit_context_t&& other) noexcept + : global_work_units_elapsed(other.global_work_units_elapsed), + total_sync_time(other.total_sync_time), + deterministic(other.deterministic), + scheduler(other.scheduler), + producer_sync(other.producer_sync), + name(std::move(other.name)), + producer_work_units_elapsed(std::make_unique>( + other.producer_work_units_elapsed->load(std::memory_order_acquire))), + producer_progress_scale(other.producer_progress_scale) + { + } + + work_limit_context_t& operator=(const work_limit_context_t& other) + { + if (this == &other) { return *this; } + global_work_units_elapsed = other.global_work_units_elapsed; + total_sync_time = other.total_sync_time; + deterministic = other.deterministic; + scheduler = other.scheduler; + producer_sync = other.producer_sync; + name = other.name; + producer_work_units_elapsed = std::make_unique>( + other.producer_work_units_elapsed->load(std::memory_order_acquire)); + producer_progress_scale = other.producer_progress_scale; + return *this; + } + + work_limit_context_t& operator=(work_limit_context_t&& other) noexcept + { + if (this == &other) { return *this; } + global_work_units_elapsed = other.global_work_units_elapsed; + total_sync_time = other.total_sync_time; + deterministic = other.deterministic; + scheduler = other.scheduler; + producer_sync = other.producer_sync; + name = std::move(other.name); + producer_work_units_elapsed = std::make_unique>( + other.producer_work_units_elapsed->load(std::memory_order_acquire)); + producer_progress_scale = other.producer_progress_scale; + return *this; + } + + double current_work() const noexcept { return global_work_units_elapsed; } + + double current_producer_work() const noexcept { return current_work() * producer_progress_scale; } + + std::atomic* producer_progress_ptr() noexcept + { + return producer_work_units_elapsed.get(); + } + + void attach_producer_sync(producer_sync_t* producer_sync_) + { + producer_sync = producer_sync_; + producer_work_units_elapsed->store(current_producer_work(), std::memory_order_release); + if (producer_progress_scale != 1.0) { + CUOPT_DETERMINISM_LOG_DEBUG( + "[%s] Using producer work-unit scale %f", name.c_str(), producer_progress_scale); + } + } + + void detach_producer_sync() noexcept { producer_sync = nullptr; } + + void set_current_work(double total_work, bool notify_producer = true) + { + if (!deterministic) return; + cuopt_assert(total_work + 1e-12 >= global_work_units_elapsed, + "Deterministic work progress must be monotonic"); + global_work_units_elapsed = total_work; + producer_work_units_elapsed->store(current_producer_work(), std::memory_order_release); + if (notify_producer && producer_sync != nullptr) { producer_sync->notify_progress(); } + } + void record_work_sync_on_horizon(double work) { if (!deterministic) return; - global_work_units_elapsed += work; - if (scheduler) { scheduler->on_work_recorded(*this, global_work_units_elapsed); } + cuopt_assert(std::isfinite(work), "Recorded work must be finite"); + cuopt_assert(work >= 0.0, "Recorded work must be non-negative"); + const double total_work = global_work_units_elapsed + work; + set_current_work(total_work, false); + if (scheduler) { scheduler->on_work_recorded(*this, total_work); } + if (producer_sync != nullptr) { producer_sync->notify_progress(); } } void record_work(double work) { record_work_sync_on_horizon(work); } diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp index bd01aef977..4b9db4240d 100644 --- a/cpp/src/utilities/work_limit_timer.hpp +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -44,7 +44,7 @@ class work_limit_timer_t { work_limit(work_limit_), timer(work_limit_), work_context(&context), - work_units_at_start(context.deterministic ? context.global_work_units_elapsed : 0) + work_units_at_start(context.deterministic ? context.current_work() : 0) { } @@ -67,7 +67,7 @@ class work_limit_timer_t { if (deterministic) { if (!work_context) { return false; } // Check if global work has exceeded our budget (snapshot + limit) - double elapsed_since_start = work_context->global_work_units_elapsed - work_units_at_start; + double elapsed_since_start = work_context->current_work() - work_units_at_start; bool finished_now = elapsed_since_start >= work_limit; if (finished_now && !finished) { finished = true; @@ -82,7 +82,7 @@ class work_limit_timer_t { caller, actual_elapsed_time, work_limit, - work_context->global_work_units_elapsed, + work_context->current_work(), work_units_at_start); } } @@ -105,7 +105,7 @@ class work_limit_timer_t { caller, work_units, timer.elapsed_time(), - work_context->global_work_units_elapsed); + work_context->current_work()); work_context->record_work_sync_on_horizon(work_units); } } @@ -114,7 +114,7 @@ class work_limit_timer_t { { if (deterministic) { if (!work_context) { return work_limit; } - double elapsed_since_start = work_context->global_work_units_elapsed - work_units_at_start; + double elapsed_since_start = work_context->current_work() - work_units_at_start; return work_limit - elapsed_since_start; } else { return timer.remaining_time(); @@ -126,7 +126,7 @@ class work_limit_timer_t { double elapsed_time() const noexcept { if (deterministic) { - return work_context->global_work_units_elapsed - work_units_at_start; + return work_context->current_work() - work_units_at_start; } else { return timer.elapsed_time(); } @@ -143,7 +143,7 @@ class work_limit_timer_t { { if (deterministic) { if (!work_context) { return false; } - double elapsed_since_start = work_context->global_work_units_elapsed - work_units_at_start; + double elapsed_since_start = work_context->current_work() - work_units_at_start; return elapsed_since_start >= work_limit / 2; } else { return timer.check_half_time(); diff --git a/cpp/src/utilities/work_unit_scheduler.cpp b/cpp/src/utilities/work_unit_scheduler.cpp index b0e5c5f12f..84088b10e8 100644 --- a/cpp/src/utilities/work_unit_scheduler.cpp +++ b/cpp/src/utilities/work_unit_scheduler.cpp @@ -79,8 +79,8 @@ void work_unit_scheduler_t::wait_for_next_sync(work_limit_context_t& ctx) { if (is_shutdown()) return; - double next_sync = current_sync_target(); - ctx.global_work_units_elapsed = next_sync; + double next_sync = current_sync_target(); + ctx.set_current_work(next_sync, false); wait_at_sync_point(ctx, next_sync); } diff --git a/cpp/tests/mip/determinism_test.cu b/cpp/tests/mip/determinism_test.cu index 1e59fba649..f40eeead2b 100644 --- a/cpp/tests/mip/determinism_test.cu +++ b/cpp/tests/mip/determinism_test.cu @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -31,6 +32,34 @@ namespace cuopt::linear_programming::test { namespace { +class scoped_env_var_t { + public: + scoped_env_var_t(const char* name, const char* value) : name_(name) + { + cuopt_assert(name != nullptr, "Environment variable name must be non-null"); + cuopt_assert(value != nullptr, "Environment variable value must be non-null"); + const char* original_value = std::getenv(name_); + was_set_ = (original_value != nullptr); + if (was_set_) { original_value_ = original_value; } + const int status = setenv(name_, value, 1); + assert(status == 0); + } + + ~scoped_env_var_t() + { + const int status = was_set_ ? setenv(name_, original_value_.c_str(), 1) : unsetenv(name_); + assert(status == 0); + } + + scoped_env_var_t(const scoped_env_var_t&) = delete; + scoped_env_var_t& operator=(const scoped_env_var_t&) = delete; + + private: + const char* name_; + std::string original_value_; + bool was_set_{false}; +}; + void expect_solutions_bitwise_equal(const mip_solution_t& sol1, const mip_solution_t& sol2, raft::handle_t& handle, @@ -177,6 +206,99 @@ TEST_F(DeterministicBBTest, reproducible_solution_vector) expect_solutions_bitwise_equal(solution1, solution2, handle_); } +class DeterministicGpuHeuristicsInstanceTest : public ::testing::TestWithParam { + protected: + raft::handle_t handle_; +}; + +TEST_P(DeterministicGpuHeuristicsInstanceTest, reproducible_with_gpu_heuristics) +{ + auto path = make_path_absolute(GetParam()); + auto problem = mps_parser::parse_mps(path, false); + handle_.sync_stream(); + + mip_solver_settings_t settings; + settings.time_limit = 60.0; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; + settings.num_cpu_threads = 8; + settings.work_limit = 30; + + auto seed = std::random_device{}() & 0x7fffffff; + std::cout << "Tested with seed " << seed << "\n"; + settings.seed = seed; + + cuopt::seed_generator::set_seed(seed); + auto solution1 = solve_mip(&handle_, problem, settings); + cuopt::seed_generator::set_seed(seed); + auto solution2 = solve_mip(&handle_, problem, settings); + cuopt::seed_generator::set_seed(seed); + auto solution3 = solve_mip(&handle_, problem, settings); + + EXPECT_EQ(solution1.get_termination_status(), solution2.get_termination_status()); + EXPECT_EQ(solution1.get_termination_status(), solution3.get_termination_status()); + + EXPECT_DOUBLE_EQ(solution1.get_objective_value(), solution2.get_objective_value()); + EXPECT_DOUBLE_EQ(solution1.get_objective_value(), solution3.get_objective_value()); + + EXPECT_DOUBLE_EQ(solution1.get_solution_bound(), solution2.get_solution_bound()); + EXPECT_DOUBLE_EQ(solution1.get_solution_bound(), solution3.get_solution_bound()); + + expect_solutions_bitwise_equal(solution1, solution2, handle_, "GPU heur run 1 vs 2: "); + expect_solutions_bitwise_equal(solution1, solution3, handle_, "GPU heur run 1 vs 3: "); +} + +TEST_F(DeterministicBBTest, reproducible_with_gpu_heuristics_50v10_no_cuts) +{ + auto path = make_path_absolute("/mip/50v-10.mps"); + auto problem = mps_parser::parse_mps(path, false); + handle_.sync_stream(); + + mip_solver_settings_t settings; + settings.time_limit = 60.0; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; + settings.num_cpu_threads = 8; + settings.work_limit = 30; + // settings.max_cut_passes = 0; + + auto seed = std::random_device{}() & 0x7fffffff; + std::cout << "Tested with seed " << seed << "\n"; + settings.seed = seed; + + cuopt::seed_generator::set_seed(seed); + auto solution1 = solve_mip(&handle_, problem, settings); + cuopt::seed_generator::set_seed(seed); + auto solution2 = solve_mip(&handle_, problem, settings); + cuopt::seed_generator::set_seed(seed); + auto solution3 = solve_mip(&handle_, problem, settings); + + EXPECT_EQ(solution1.get_termination_status(), solution2.get_termination_status()); + EXPECT_EQ(solution1.get_termination_status(), solution3.get_termination_status()); + + EXPECT_DOUBLE_EQ(solution1.get_objective_value(), solution2.get_objective_value()); + EXPECT_DOUBLE_EQ(solution1.get_objective_value(), solution3.get_objective_value()); + + EXPECT_DOUBLE_EQ(solution1.get_solution_bound(), solution2.get_solution_bound()); + EXPECT_DOUBLE_EQ(solution1.get_solution_bound(), solution3.get_solution_bound()); + + expect_solutions_bitwise_equal(solution1, solution2, handle_, "GPU heur no-cuts run 1 vs 2: "); + expect_solutions_bitwise_equal(solution1, solution3, handle_, "GPU heur no-cuts run 1 vs 3: "); +} + +INSTANTIATE_TEST_SUITE_P( + DeterministicGpuHeuristics, + DeterministicGpuHeuristicsInstanceTest, + ::testing::Values(std::string("/mip/gen-ip054.mps"), + std::string("/mip/pk1.mps"), + // std::string("/mip/sct2.mps"), + // std::string("/mip/thor50dday.mps"), + std::string("/mip/50v-10.mps")), + [](const ::testing::TestParamInfo& info) { + std::string name = info.param.substr(info.param.rfind('/') + 1); + name = name.substr(0, name.rfind('.')); + std::replace(name.begin(), name.end(), '-', '_'); + return name; + }); + // Parameterized test for different problem instances class DeterministicBBInstanceTest : public ::testing::TestWithParam> { @@ -186,6 +308,7 @@ class DeterministicBBInstanceTest TEST_P(DeterministicBBInstanceTest, deterministic_across_runs) { + scoped_env_var_t gpu_fj_work_scale("CUOPT_GPU_HEUR_WORK_UNIT_SCALE", "0.1"); auto [instance_path, num_threads, time_limit, work_limit] = GetParam(); auto path = make_path_absolute(instance_path); auto problem = mps_parser::parse_mps(path, false); @@ -230,6 +353,7 @@ INSTANTIATE_TEST_SUITE_P( // Instance, threads, time_limit std::make_tuple("/mip/gen-ip054.mps", 4, 60.0, 4), std::make_tuple("/mip/swath1.mps", 8, 60.0, 4), + std::make_tuple("/mip/50v-10.mps", 8, 60.0, 30), std::make_tuple("/mip/gen-ip054.mps", 128, 120.0, 1), std::make_tuple("/mip/bb_optimality.mps", 4, 60.0, 4), std::make_tuple("/mip/neos5.mps", 16, 60.0, 1), From c8a98360e0cdc40241c51f7b659b1546aed57de7 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 7 Mar 2026 12:50:09 -0800 Subject: [PATCH 138/225] much in the way of fixes --- .../cuopt/linear_programming/cuopt_c.h | 53 +++ .../utilities/internals.hpp | 55 ++- cpp/src/branch_and_bound/branch_and_bound.cpp | 443 ++++++++---------- cpp/src/branch_and_bound/branch_and_bound.hpp | 37 +- .../deterministic_workers.hpp | 11 +- .../dual_simplex/simplex_solver_settings.hpp | 5 + .../diversity/diversity_manager.cu | 54 +-- .../mip_heuristics/diversity/population.cu | 147 +++++- .../mip_heuristics/diversity/population.cuh | 31 +- .../diversity/recombiners/sub_mip.cuh | 2 +- .../feasibility_jump/feasibility_jump.cu | 30 +- .../local_search/local_search.cu | 39 +- cpp/src/mip_heuristics/solve.cu | 36 +- cpp/src/mip_heuristics/solver.cu | 62 ++- cpp/src/pdlp/cuopt_c.cpp | 47 ++ cpp/src/utilities/work_limit_timer.hpp | 2 +- cpp/tests/mip/determinism_test.cu | 264 +++++++++++ 17 files changed, 945 insertions(+), 373 deletions(-) diff --git a/cpp/include/cuopt/linear_programming/cuopt_c.h b/cpp/include/cuopt/linear_programming/cuopt_c.h index 4c4d44c764..b0239e19f3 100644 --- a/cpp/include/cuopt/linear_programming/cuopt_c.h +++ b/cpp/include/cuopt/linear_programming/cuopt_c.h @@ -71,6 +71,27 @@ typedef int32_t cuopt_int_t; typedef int64_t cuopt_int_t; #endif +typedef enum { + CUOPT_MIP_SOLUTION_ORIGIN_UNKNOWN = 0, + CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND_NODE = 1, + CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND = CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND_NODE, + CUOPT_MIP_SOLUTION_ORIGIN_FEASIBILITY_JUMP = 2, + CUOPT_MIP_SOLUTION_ORIGIN_LOCAL_SEARCH = 3, + CUOPT_MIP_SOLUTION_ORIGIN_QUICK_FEASIBLE = 4, + CUOPT_MIP_SOLUTION_ORIGIN_USER_INITIAL = 5, + CUOPT_MIP_SOLUTION_ORIGIN_LP_ROUNDING = 6, + CUOPT_MIP_SOLUTION_ORIGIN_RECOMBINATION = 7, + CUOPT_MIP_SOLUTION_ORIGIN_SUB_MIP = 8, + CUOPT_MIP_SOLUTION_ORIGIN_CPU_FEASIBILITY_JUMP = 9, + CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND_DIVING = 10, +} cuOptMIPSolutionOrigin; + +typedef struct { + uint64_t struct_size; + cuOptMIPSolutionOrigin origin; + double work_timestamp; +} cuOptMIPSolutionCallbackInfo; + /** * @brief Get the size of the float type. * @@ -713,6 +734,25 @@ typedef void (*cuOptMIPGetSolutionCallback)(const cuopt_float_t* solution, const cuopt_float_t* solution_bound, void* user_data); +/** + * @brief Type of callback for receiving incumbent MIP solutions with extensible metadata. + * + * @param[in] solution - Pointer to incumbent solution values. + * @param[in] objective_value - Pointer to incumbent objective value. + * @param[in] solution_bound - Pointer to current solution (dual/user) bound. + * @param[in] callback_info - Pointer to callback metadata. `struct_size` is always set and can be + * used to detect future extensions safely. + * @param[in] user_data - Pointer to user data. + * @note All pointer 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. + */ +typedef void (*cuOptMIPGetSolutionCallbackExt)(const cuopt_float_t* solution, + const cuopt_float_t* objective_value, + const cuopt_float_t* solution_bound, + const cuOptMIPSolutionCallbackInfo* callback_info, + void* user_data); + /** * @brief Type of callback for injecting MIP solutions with user context. * @@ -748,6 +788,19 @@ cuopt_int_t cuOptSetMIPGetSolutionCallback(cuOptSolverSettings settings, cuOptMIPGetSolutionCallback callback, void* user_data); +/** + * @brief Register an extended callback to receive incumbent MIP solutions with origin metadata. + * + * @param[in] settings - The solver settings object. + * @param[in] callback - Callback function to receive incumbent solutions and callback metadata. + * @param[in] user_data - User-defined pointer passed through to the callback. + * + * @return A status code indicating success or failure. + */ +cuopt_int_t cuOptSetMIPGetSolutionCallbackExt(cuOptSolverSettings settings, + cuOptMIPGetSolutionCallbackExt callback, + void* user_data); + /** * @brief Register a callback to inject MIP solutions. * diff --git a/cpp/include/cuopt/linear_programming/utilities/internals.hpp b/cpp/include/cuopt/linear_programming/utilities/internals.hpp index fc90dec04f..20de029d34 100644 --- a/cpp/include/cuopt/linear_programming/utilities/internals.hpp +++ b/cpp/include/cuopt/linear_programming/utilities/internals.hpp @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -21,7 +22,46 @@ class Callback { virtual ~Callback() {} }; -enum class base_solution_callback_type { GET_SOLUTION, SET_SOLUTION }; +enum class mip_solution_origin_t : uint32_t { + UNKNOWN = 0, + BRANCH_AND_BOUND_NODE = 1, + BRANCH_AND_BOUND = BRANCH_AND_BOUND_NODE, + FEASIBILITY_JUMP = 2, + LOCAL_SEARCH = 3, + QUICK_FEASIBLE = 4, + USER_INITIAL = 5, + LP_ROUNDING = 6, + RECOMBINATION = 7, + SUB_MIP = 8, + CPU_FEASIBILITY_JUMP = 9, + BRANCH_AND_BOUND_DIVING = 10, +}; + +constexpr const char* mip_solution_origin_to_string(mip_solution_origin_t origin) +{ + switch (origin) { + case mip_solution_origin_t::UNKNOWN: return "unknown"; + case mip_solution_origin_t::BRANCH_AND_BOUND_NODE: return "branch_and_bound_node"; + case mip_solution_origin_t::BRANCH_AND_BOUND_DIVING: return "branch_and_bound_diving"; + case mip_solution_origin_t::FEASIBILITY_JUMP: return "feasibility_jump"; + case mip_solution_origin_t::LOCAL_SEARCH: return "local_search"; + case mip_solution_origin_t::QUICK_FEASIBLE: return "quick_feasible"; + case mip_solution_origin_t::USER_INITIAL: return "user_initial"; + case mip_solution_origin_t::LP_ROUNDING: return "lp_rounding"; + case mip_solution_origin_t::RECOMBINATION: return "recombination"; + case mip_solution_origin_t::SUB_MIP: return "sub_mip"; + case mip_solution_origin_t::CPU_FEASIBILITY_JUMP: return "cpu_feasibility_jump"; + default: return "unknown"; + } +} + +struct mip_solution_callback_info_t { + uint64_t struct_size{sizeof(mip_solution_callback_info_t)}; + mip_solution_origin_t origin{mip_solution_origin_t::UNKNOWN}; + double work_timestamp{-1.0}; +}; + +enum class base_solution_callback_type { GET_SOLUTION, GET_SOLUTION_EXT, SET_SOLUTION }; class base_solution_callback_t : public Callback { public: @@ -55,6 +95,19 @@ class get_solution_callback_t : public base_solution_callback_t { } }; +class get_solution_callback_ext_t : public base_solution_callback_t { + public: + virtual void get_solution(void* data, + void* objective_value, + void* solution_bound, + const mip_solution_callback_info_t* callback_info, + void* user_data) = 0; + base_solution_callback_type get_type() const override + { + return base_solution_callback_type::GET_SOLUTION_EXT; + } +}; + class set_solution_callback_t : public base_solution_callback_t { public: virtual void set_solution(void* data, diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 2fe54c4c30..7b2af9d6a5 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -452,6 +452,45 @@ void branch_and_bound_t::update_user_bound(f_t lower_bound) user_bound_callback_(user_lower); } +template +void branch_and_bound_t::emit_solution_callback( + std::vector& original_x, + f_t objective, + cuopt::internals::mip_solution_origin_t origin, + double work_timestamp) +{ + const size_t callback_count = (settings_.solution_callback != nullptr ? 1UL : 0UL) + + (settings_.solution_callback_ext != nullptr ? 1UL : 0UL); + settings_.log.debug("Publishing incumbent: obj=%g wut=%.6f origin=%s callbacks=%zu\n", + compute_user_objective(original_lp_, objective), + work_timestamp, + cuopt::internals::mip_solution_origin_to_string(origin), + callback_count); + if (settings_.solution_callback_ext != nullptr) { + cuopt::internals::mip_solution_callback_info_t callback_info{}; + callback_info.origin = origin; + callback_info.work_timestamp = work_timestamp; + settings_.solution_callback_ext(original_x, objective, callback_info, work_timestamp); + } else if (settings_.solution_callback != nullptr) { + settings_.solution_callback(original_x, objective); + } +} + +template +void branch_and_bound_t::emit_solution_callback_from_crushed( + const std::vector& crushed_solution, + f_t objective, + cuopt::internals::mip_solution_origin_t origin, + double work_timestamp) +{ + if (settings_.solution_callback == nullptr && settings_.solution_callback_ext == nullptr) { + return; + } + std::vector original_x; + uncrush_primal_solution(original_problem_, original_lp_, crushed_solution, original_x); + emit_solution_callback(original_x, objective, origin, work_timestamp); +} + template void branch_and_bound_t::set_new_solution(const std::vector& solution) { @@ -519,14 +558,16 @@ void branch_and_bound_t::set_new_solution(const std::vector& solu if (is_feasible) { report_heuristic(obj); } if (attempt_repair) { mutex_repair_.lock(); - repair_queue_.push_back(solution); + repair_queue_.push_back({solution, cuopt::internals::mip_solution_origin_t::UNKNOWN}); mutex_repair_.unlock(); } } template void branch_and_bound_t::queue_external_solution_deterministic( - const std::vector& solution, double work_unit_ts) + const std::vector& solution, + double work_unit_ts, + cuopt::internals::mip_solution_origin_t origin) { // In deterministic mode, queue the solution to be processed at the correct work unit timestamp // This ensures deterministic ordering of solution events @@ -586,7 +627,7 @@ void branch_and_bound_t::queue_external_solution_deterministic( // consumption time so that the crush reflects the current LP state // (which may have gained slack columns from cuts added after this point). mutex_repair_.lock(); - repair_queue_.push_back(solution); + repair_queue_.push_back({solution, origin}); const size_t repair_queue_size = repair_queue_.size(); mutex_repair_.unlock(); CUOPT_DETERMINISM_LOG_PRINTF( @@ -606,7 +647,8 @@ void branch_and_bound_t::queue_external_solution_deterministic( // Queue the solution with its work unit timestamp mutex_heuristic_queue_.lock(); - heuristic_solution_queue_.push_back({obj, std::move(crushed_solution), 0, -1, 0, work_unit_ts}); + heuristic_solution_queue_.push_back( + {obj, std::move(crushed_solution), 0, -1, 0, work_unit_ts, origin}); const size_t heuristic_queue_size = heuristic_solution_queue_.size(); mutex_heuristic_queue_.unlock(); CUOPT_DETERMINISM_LOG_PRINTF( @@ -688,7 +730,7 @@ void branch_and_bound_t::repair_heuristic_solutions() { raft::common::nvtx::range scope("BB::repair_heuristics"); // Check if there are any solutions to repair - std::vector> to_repair; + std::vector to_repair; mutex_repair_.lock(); if (repair_queue_.size() > 0) { to_repair = repair_queue_; @@ -698,7 +740,8 @@ void branch_and_bound_t::repair_heuristic_solutions() if (to_repair.size() > 0) { settings_.log.debug("Attempting to repair %ld injected solutions\n", to_repair.size()); - for (const std::vector& uncrushed_solution : to_repair) { + for (const auto& queued_solution : to_repair) { + const std::vector& uncrushed_solution = queued_solution.solution; std::vector crushed_solution; crush_primal_solution( original_problem_, original_lp_, uncrushed_solution, new_slacks_, crushed_solution); @@ -723,11 +766,8 @@ void branch_and_bound_t::repair_heuristic_solutions() detail::compute_hash(repaired_solution)); report_heuristic(repaired_obj); - if (settings_.solution_callback != nullptr) { - std::vector original_x; - uncrush_primal_solution(original_problem_, original_lp_, repaired_solution, original_x); - settings_.solution_callback(original_x, repaired_obj); - } + emit_solution_callback_from_crushed( + repaired_solution, repaired_obj, queued_solution.origin, -1.0); } mutex_upper_.unlock(); @@ -766,9 +806,8 @@ void branch_and_bound_t::set_solution_at_root(mip_solution_t compute_user_objective(original_lp_, root_objective_), toc(exploration_stats_.start_time)); - if (settings_.solution_callback != nullptr) { - settings_.solution_callback(solution.x, solution.objective); - } + emit_solution_callback( + solution.x, solution.objective, cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE); if (settings_.heuristic_preemption_callback != nullptr) { settings_.heuristic_preemption_callback(); } @@ -893,10 +932,12 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, send_solution = true; } - if (send_solution && settings_.solution_callback != nullptr) { - std::vector original_x; - uncrush_primal_solution(original_problem_, original_lp_, incumbent_.x, original_x); - settings_.solution_callback(original_x, upper_bound_); + if (send_solution) { + emit_solution_callback_from_crushed( + incumbent_.x, + upper_bound_, + cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE, + -1.0); } mutex_upper_.unlock(); } @@ -1129,7 +1170,13 @@ struct deterministic_bfs_policy_t const int previous_seq = this->worker.next_solution_seq; this->worker.local_upper_bound = obj; this->worker.integer_solutions.push_back( - {obj, x, node->depth, this->worker.worker_id, this->worker.next_solution_seq++}); + {obj, + x, + node->depth, + this->worker.worker_id, + this->worker.next_solution_seq++, + this->worker.clock, + cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE}); if (this->bnb.deterministic_current_horizon_ <= this->bnb.deterministic_horizon_step_ + 1e-9) { CUOPT_DETERMINISM_LOG_PRINTF( @@ -3351,20 +3398,19 @@ void branch_and_bound_t::deterministic_sync_callback() } } - deterministic_sort_replay_events(all_events); - - // deterministic_prune_worker_nodes_vs_incumbent(); + std::vector::deterministic_replay_solution_t> + replay_solutions; + deterministic_collect_worker_solutions( + *deterministic_workers_, + [](const deterministic_bfs_worker_pool_t&, int) { + return search_strategy_t::BEST_FIRST; + }, + replay_solutions); + deterministic_collect_diving_solutions_and_update_pseudocosts(replay_solutions); - deterministic_collect_diving_solutions_and_update_pseudocosts(); + deterministic_sort_replay_events(all_events, replay_solutions); - for (auto& worker : *deterministic_workers_) { - worker.integer_solutions.clear(); - } - if (deterministic_diving_workers_) { - for (auto& worker : *deterministic_diving_workers_) { - worker.integer_solutions.clear(); - } - } + // deterministic_prune_worker_nodes_vs_incumbent(); deterministic_populate_diving_heap(); @@ -3611,125 +3657,17 @@ node_status_t branch_and_bound_t::solve_node_deterministic( template template -void branch_and_bound_t::deterministic_process_worker_solutions( - PoolT& pool, WorkerTypeGetter get_worker_type) +void branch_and_bound_t::deterministic_collect_worker_solutions( + PoolT& pool, + WorkerTypeGetter get_worker_type, + std::vector::deterministic_replay_solution_t>& + replay_solutions) { - std::vector*> all_solutions; for (auto& worker : pool) { for (auto& sol : worker.integer_solutions) { - all_solutions.push_back(&sol); + const search_strategy_t strategy = get_worker_type(pool, sol.worker_id); + replay_solutions.push_back({std::move(sol), strategy}); } - } - - // relies on queued_integer_solution_t's operator< - // sorts based on objective first, then the tuple - std::sort(all_solutions.begin(), - all_solutions.end(), - [](const queued_integer_solution_t* a, - const queued_integer_solution_t* b) { return *a < *b; }); - - f_t deterministic_lower = deterministic_compute_lower_bound(); - f_t current_upper = upper_bound_.load(); - if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { - CUOPT_DETERMINISM_LOG_PRINTF( - settings_.log, - "Deterministic worker solution replay: candidates=%zu lower=%.16e upper_before=%.16e\n", - all_solutions.size(), - deterministic_lower, - current_upper); - for (size_t i = 0; i < all_solutions.size(); ++i) { - const auto* sol = all_solutions[i]; - CUOPT_DETERMINISM_LOG_PRINTF( - settings_.log, - "Deterministic worker solution[%zu]: obj=%.16e worker=%d seq=%d depth=%d sol_hash=0x%x\n", - i, - sol->objective, - sol->worker_id, - sol->sequence_id, - sol->depth, - detail::compute_hash(sol->solution)); - } - } - - for (const auto* sol : all_solutions) { - /* - settings_.log.printf( - "Deterministic worker candidate: obj=%.16e worker=%d seq=%d depth=%d current_upper=%.16e " - "lower=%.16e\n", - sol->objective, - sol->worker_id, - sol->sequence_id, - sol->depth, - current_upper, - deterministic_lower); - */ - if (sol->objective < current_upper) { - f_t user_obj = compute_user_objective(original_lp_, sol->objective); - f_t user_lower = compute_user_objective(original_lp_, deterministic_lower); - i_t nodes_explored = exploration_stats_.nodes_explored.load(); - i_t nodes_unexplored = exploration_stats_.nodes_unexplored.load(); - - search_strategy_t worker_type = get_worker_type(pool, sol->worker_id); - report(feasible_solution_symbol(worker_type), - sol->objective, - deterministic_lower, - sol->depth, - 0, - deterministic_current_horizon_); - - bool improved = false; - if (sol->objective < upper_bound_) { - const f_t previous_upper = upper_bound_; - upper_bound_ = sol->objective; - incumbent_.set_incumbent_solution(sol->objective, sol->solution); - current_upper = sol->objective; - improved = true; - CUOPT_DETERMINISM_LOG_PRINTF( - settings_.log, - "Deterministic B&B incumbent update: source=det_worker_replay prev_upper=%.16e " - "new_upper=%.16e obj=%.16e hash=0x%x worker=%d seq=%d depth=%d horizon=%.6f\n", - previous_upper, - upper_bound_.load(), - sol->objective, - detail::compute_hash(sol->solution), - sol->worker_id, - sol->sequence_id, - sol->depth, - deterministic_current_horizon_); - } - /* - settings_.log.printf( - "Deterministic worker accept: improved=%d obj=%.16e worker=%d seq=%d depth=%d " - "upper_now=%.16e nodes=%d/%lu\n", - (int)improved, - sol->objective, - sol->worker_id, - sol->sequence_id, - sol->depth, - current_upper, - nodes_explored, - nodes_unexplored); - */ - - if (improved && settings_.solution_callback != nullptr) { - std::vector original_x; - uncrush_primal_solution(original_problem_, original_lp_, sol->solution, original_x); - settings_.solution_callback(original_x, sol->objective); - } - } else { - /* - settings_.log.printf( - "Deterministic worker reject: obj=%.16e worker=%d seq=%d depth=%d current_upper=%.16e\n", - sol->objective, - sol->worker_id, - sol->sequence_id, - sol->depth, - current_upper); - */ - } - } - - for (auto& worker : pool) { worker.integer_solutions.clear(); } } @@ -3765,11 +3703,13 @@ void branch_and_bound_t::deterministic_broadcast_snapshots( template void branch_and_bound_t::deterministic_sort_replay_events( - const bb_event_batch_t& events) + const bb_event_batch_t& events, + std::vector::deterministic_replay_solution_t>& + replay_solutions) { // Infeasible solutions from GPU heuristics are queued for repair; process them now { - std::vector> to_repair; + std::vector to_repair; // TODO: support repair queue in deterministic mode // mutex_repair_.lock(); // if (repair_queue_.size() > 0) { @@ -3780,14 +3720,17 @@ void branch_and_bound_t::deterministic_sort_replay_events( std::sort(to_repair.begin(), to_repair.end(), - [](const std::vector& a, const std::vector& b) { return a < b; }); + [](const queued_repair_solution_t& a, const queued_repair_solution_t& b) { + return a.solution < b.solution; + }); if (to_repair.size() > 0) { CUOPT_DETERMINISM_LOG_PRINTF( settings_.log, "Deterministic sync: Attempting to repair %ld injected solutions\n", to_repair.size()); - for (const std::vector& uncrushed_solution : to_repair) { + for (const auto& queued_solution : to_repair) { + const std::vector& uncrushed_solution = queued_solution.solution; std::vector crushed_solution; crush_primal_solution( original_problem_, original_lp_, uncrushed_solution, new_slacks_, crushed_solution); @@ -3798,8 +3741,13 @@ void branch_and_bound_t::deterministic_sort_replay_events( if (success) { // Queue repaired solution with work unit timestamp (...workstamp?) mutex_heuristic_queue_.lock(); - heuristic_solution_queue_.push_back( - {repaired_obj, std::move(repaired_solution), 0, -1, 0, deterministic_current_horizon_}); + heuristic_solution_queue_.push_back({repaired_obj, + std::move(repaired_solution), + 0, + -1, + 0, + deterministic_current_horizon_, + queued_solution.origin}); mutex_heuristic_queue_.unlock(); } } @@ -3808,13 +3756,12 @@ void branch_and_bound_t::deterministic_sort_replay_events( // Extract heuristic solutions, keeping future solutions for next horizon // Use deterministic_current_horizon_ as the upper bound (horizon_end) - std::vector> heuristic_solutions; mutex_heuristic_queue_.lock(); { std::vector> future_solutions; for (auto& sol : heuristic_solution_queue_) { if (sol.work_timestamp < deterministic_current_horizon_) { - heuristic_solutions.push_back(std::move(sol)); + replay_solutions.push_back({std::move(sol), search_strategy_t::BEST_FIRST}); } else { future_solutions.push_back(std::move(sol)); } @@ -3822,68 +3769,79 @@ void branch_and_bound_t::deterministic_sort_replay_events( heuristic_solution_queue_ = std::move(future_solutions); } mutex_heuristic_queue_.unlock(); - if (!heuristic_solutions.empty() || !heuristic_solution_queue_.empty()) { + if (!replay_solutions.empty() || !heuristic_solution_queue_.empty()) { CUOPT_DETERMINISM_LOG_PRINTF( settings_.log, - "Deterministic heuristic extract: horizon=%.6f now=%zu future=%zu upper=%.16e\n", + "Deterministic replay extract: horizon=%.6f now=%zu future=%zu upper=%.16e\n", deterministic_current_horizon_, - heuristic_solutions.size(), + replay_solutions.size(), heuristic_solution_queue_.size(), upper_bound_.load()); } - /* - settings_.log.printf( - "Deterministic replay extract: horizon=%.6f bb_events=%zu heur_now=%zu heur_future=%zu " - "upper_before=%.16e\n", - deterministic_current_horizon_, - events.events.size(), - heuristic_solutions.size(), - heuristic_solution_queue_.size(), - upper_bound_.load()); - */ - - // sort by work unit timestamp, with objective and solution values as tie-breakers - std::sort( - heuristic_solutions.begin(), - heuristic_solutions.end(), - [](const queued_integer_solution_t& a, const queued_integer_solution_t& b) { - if (a.work_timestamp != b.work_timestamp) { return a.work_timestamp < b.work_timestamp; } - if (a.objective != b.objective) { return a.objective < b.objective; } - return a.solution < b.solution; // edge-case - lexicographical comparison - }); - /* - for (size_t i = 0; i < std::min(heuristic_solutions.size(), 8); ++i) { - const auto& hsol = heuristic_solutions[i]; - settings_.log.printf( - "Deterministic replay heuristic[%zu]: wut=%.6f obj=%.16e worker=%d seq=%d depth=%d\n", - i, - hsol.work_timestamp, - hsol.objective, - hsol.worker_id, - hsol.sequence_id, - hsol.depth); + + // Sort the full replay stream by work unit timestamp, with stable deterministic tie-breakers. + std::sort(replay_solutions.begin(), replay_solutions.end(), [](const auto& a, const auto& b) { + if (a.solution.work_timestamp != b.solution.work_timestamp) { + return a.solution.work_timestamp < b.solution.work_timestamp; + } + if (a.solution.objective != b.solution.objective) { + return a.solution.objective < b.solution.objective; + } + if (a.solution.origin != b.solution.origin) { return a.solution.origin < b.solution.origin; } + if (a.solution.worker_id != b.solution.worker_id) { + return a.solution.worker_id < b.solution.worker_id; + } + if (a.solution.sequence_id != b.solution.sequence_id) { + return a.solution.sequence_id < b.solution.sequence_id; + } + return a.solution.solution < b.solution.solution; + }); + + f_t deterministic_lower = deterministic_compute_lower_bound(); + f_t current_upper = upper_bound_.load(); + if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic solution replay: candidates=%zu lower=%.16e upper_before=%.16e\n", + replay_solutions.size(), + deterministic_lower, + current_upper); + for (size_t i = 0; i < replay_solutions.size(); ++i) { + const auto& replay = replay_solutions[i]; + const auto& sol = replay.solution; + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic replay solution[%zu]: wut=%.6f obj=%.16e origin=%s worker=%d seq=%d " + "depth=%d sol_hash=0x%x\n", + i, + sol.work_timestamp, + sol.objective, + cuopt::internals::mip_solution_origin_to_string(sol.origin), + sol.worker_id, + sol.sequence_id, + sol.depth, + detail::compute_hash(sol.solution)); + } } - */ - // Merge B&B events and heuristic solutions for unified timeline replay - size_t event_idx = 0; - size_t heuristic_idx = 0; + // Merge B&B events and all incumbent-producing solutions for unified timeline replay. + size_t event_idx = 0; + size_t solution_idx = 0; - while (event_idx < events.events.size() || heuristic_idx < heuristic_solutions.size()) { - bool process_event = false; - bool process_heuristic = false; + while (event_idx < events.events.size() || solution_idx < replay_solutions.size()) { + bool process_event = false; + bool process_solution = false; if (event_idx >= events.events.size()) { - process_heuristic = true; - } else if (heuristic_idx >= heuristic_solutions.size()) { + process_solution = true; + } else if (solution_idx >= replay_solutions.size()) { process_event = true; } else { - // Both have items - pick the one with smaller WUT if (events.events[event_idx].work_timestamp <= - heuristic_solutions[heuristic_idx].work_timestamp) { + replay_solutions[solution_idx].solution.work_timestamp) { process_event = true; } else { - process_heuristic = true; + process_solution = true; } } @@ -3903,61 +3861,62 @@ void branch_and_bound_t::deterministic_sort_replay_events( } } - if (process_heuristic) { - const auto& hsol = heuristic_solutions[heuristic_idx++]; - - // Process heuristic solution at its correct work unit timestamp position - f_t new_upper = std::numeric_limits::infinity(); + if (process_solution) { + const auto& replay = replay_solutions[solution_idx++]; + const auto& sol = replay.solution; + bool improved = false; - if (hsol.objective < upper_bound_) { + if (sol.objective < upper_bound_) { const f_t previous_upper = upper_bound_; - upper_bound_ = hsol.objective; - incumbent_.set_incumbent_solution(hsol.objective, hsol.solution); - new_upper = hsol.objective; + upper_bound_ = sol.objective; + incumbent_.set_incumbent_solution(sol.objective, sol.solution); + current_upper = sol.objective; + improved = true; CUOPT_DETERMINISM_LOG_PRINTF( settings_.log, - "Deterministic B&B incumbent update: source=det_heuristic_replay prev_upper=%.16e " + "Deterministic B&B incumbent update: source=det_replay prev_upper=%.16e " "new_upper=%.16e obj=%.16e hash=0x%x worker=%d seq=%d wut=%.6f horizon=%.6f\n", previous_upper, upper_bound_.load(), - hsol.objective, - detail::compute_hash(hsol.solution), - hsol.worker_id, - hsol.sequence_id, - hsol.work_timestamp, + sol.objective, + detail::compute_hash(sol.solution), + sol.worker_id, + sol.sequence_id, + sol.work_timestamp, deterministic_current_horizon_); } CUOPT_DETERMINISM_LOG_PRINTF( settings_.log, - "Deterministic heuristic replay: horizon=%.6f wut=%.6f obj=%.16e accepted=%d " + "Deterministic replay: horizon=%.6f wut=%.6f obj=%.16e origin=%s accepted=%d " "upper_now=%.16e worker=%d seq=%d sol_hash=0x%x\n", deterministic_current_horizon_, - hsol.work_timestamp, - hsol.objective, - (int)(new_upper < std::numeric_limits::infinity()), - upper_bound_.load(), - hsol.worker_id, - hsol.sequence_id, - detail::compute_hash(hsol.solution)); - - if (new_upper < std::numeric_limits::infinity()) { - report_heuristic(new_upper); - - if (settings_.solution_callback != nullptr) { - std::vector original_x; - uncrush_primal_solution(original_problem_, original_lp_, hsol.solution, original_x); - settings_.solution_callback(original_x, hsol.objective); + sol.work_timestamp, + sol.objective, + cuopt::internals::mip_solution_origin_to_string(sol.origin), + (int)improved, + current_upper, + sol.worker_id, + sol.sequence_id, + detail::compute_hash(sol.solution)); + + if (improved) { + if (sol.origin == cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE || + sol.origin == cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_DIVING) { + report(feasible_solution_symbol(replay.strategy), + sol.objective, + deterministic_lower, + sol.depth, + 0, + deterministic_current_horizon_); + } else { + report_heuristic(sol.objective); } + emit_solution_callback_from_crushed( + sol.solution, sol.objective, sol.origin, sol.work_timestamp); } } } - // Merge integer solutions from BFS workers and update global incumbent - deterministic_process_worker_solutions(*deterministic_workers_, - [](const deterministic_bfs_worker_pool_t&, int) { - return search_strategy_t::BEST_FIRST; - }); - // Merge and apply pseudo-cost updates from BFS workers deterministic_merge_pseudo_cost_updates(*deterministic_workers_); @@ -4178,16 +4137,18 @@ void branch_and_bound_t::deterministic_assign_diving_nodes() } template -void branch_and_bound_t::deterministic_collect_diving_solutions_and_update_pseudocosts() +void branch_and_bound_t::deterministic_collect_diving_solutions_and_update_pseudocosts( + std::vector::deterministic_replay_solution_t>& + replay_solutions) { if (!deterministic_diving_workers_) return; - // Collect integer solutions from diving workers and update global incumbent - deterministic_process_worker_solutions( + deterministic_collect_worker_solutions( *deterministic_diving_workers_, [](const deterministic_diving_worker_pool_t& pool, int worker_id) { return pool[worker_id].diving_type; - }); + }, + replay_solutions); // Merge pseudo-cost updates from diving workers deterministic_merge_pseudo_cost_updates(*deterministic_diving_workers_); diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index 8d29808397..9bc0e2d875 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -131,7 +131,10 @@ class branch_and_bound_t { void set_new_solution(const std::vector& solution); // This queues the solution to be processed at the correct work unit timestamp - void queue_external_solution_deterministic(const std::vector& solution, double work_unit_ts); + void queue_external_solution_deterministic(const std::vector& solution, + double work_unit_ts, + cuopt::internals::mip_solution_origin_t origin = + cuopt::internals::mip_solution_origin_t::UNKNOWN); void set_user_bound_callback(std::function callback) { @@ -214,7 +217,12 @@ class branch_and_bound_t { // Mutex for repair omp_mutex_t mutex_repair_; - std::vector> repair_queue_; + struct queued_repair_solution_t { + std::vector solution; + cuopt::internals::mip_solution_origin_t origin{ + cuopt::internals::mip_solution_origin_t::UNKNOWN}; + }; + std::vector repair_queue_; // Variables for the root node in the search tree. std::vector root_vstatus_; @@ -265,6 +273,14 @@ class branch_and_bound_t { i_t node_depth, i_t node_int_infeas, double work_time = -1); + void emit_solution_callback(std::vector& original_x, + f_t objective, + cuopt::internals::mip_solution_origin_t origin, + double work_timestamp = -1.0); + void emit_solution_callback_from_crushed(const std::vector& crushed_solution, + f_t objective, + cuopt::internals::mip_solution_origin_t origin, + double work_timestamp = -1.0); // Set the solution when found at the root node void set_solution_at_root(mip_solution_t& solution, @@ -337,7 +353,14 @@ class branch_and_bound_t { void run_deterministic_coordinator(const csr_matrix_t& Arow); // Gather all events generated, sort by WU timestamp, apply - void deterministic_sort_replay_events(const bb_event_batch_t& events); + struct deterministic_replay_solution_t { + queued_integer_solution_t solution; + search_strategy_t strategy{search_strategy_t::BEST_FIRST}; + }; + + void deterministic_sort_replay_events( + const bb_event_batch_t& events, + std::vector& replay_solutions); // Prune nodes held by workers based on new incumbent void deterministic_prune_worker_nodes_vs_incumbent(); @@ -370,10 +393,14 @@ class branch_and_bound_t { void deterministic_assign_diving_nodes(); // Collect and merge diving solutions at sync - void deterministic_collect_diving_solutions_and_update_pseudocosts(); + void deterministic_collect_diving_solutions_and_update_pseudocosts( + std::vector& replay_solutions); template - void deterministic_process_worker_solutions(PoolT& pool, WorkerTypeGetter get_worker_type); + void deterministic_collect_worker_solutions( + PoolT& pool, + WorkerTypeGetter get_worker_type, + std::vector& replay_solutions); template void deterministic_merge_pseudo_cost_updates(PoolT& pool); diff --git a/cpp/src/branch_and_bound/deterministic_workers.hpp b/cpp/src/branch_and_bound/deterministic_workers.hpp index 7a074051c6..a73c10301f 100644 --- a/cpp/src/branch_and_bound/deterministic_workers.hpp +++ b/cpp/src/branch_and_bound/deterministic_workers.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -44,6 +45,8 @@ struct queued_integer_solution_t { int worker_id{-1}; int sequence_id{0}; double work_timestamp{0.0}; + cuopt::internals::mip_solution_origin_t origin{ + cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE}; bool operator<(const queued_integer_solution_t& other) const { @@ -339,7 +342,13 @@ class deterministic_diving_worker_t void queue_integer_solution(f_t objective, const std::vector& solution, i_t depth) { this->integer_solutions.push_back( - {objective, solution, depth, this->worker_id, this->next_solution_seq++}); + {objective, + solution, + depth, + this->worker_id, + this->next_solution_seq++, + this->clock, + cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_DIVING}); ++this->total_integer_solutions; } diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 815e229232..79ff0331a9 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -109,6 +110,7 @@ struct simplex_solver_settings_t { inside_mip(0), sub_mip(0), solution_callback(nullptr), + solution_callback_ext(nullptr), heuristic_preemption_callback(nullptr), concurrent_halt(nullptr) { @@ -199,6 +201,9 @@ struct simplex_solver_settings_t { i_t sub_mip; // 0 if in regular MIP solve, 1 if in sub-MIP solve std::function&, f_t)> solution_callback; + std::function&, f_t, const cuopt::internals::mip_solution_callback_info_t&, double)> + solution_callback_ext; std::function&, f_t)> node_processed_callback; std::function heuristic_preemption_callback; std::function&, std::vector&, f_t)> set_simplex_solution_callback; diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index b8c4d4e3a3..650671896f 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -155,7 +155,7 @@ void diversity_manager_t::generate_solution(f_t time_limit, bool rando sol.compute_feasibility(); // if a feasible is found, it is added to the population ls.generate_solution(sol, random_start, &population, time_limit); - population.add_solution(std::move(sol)); + population.add_solution(std::move(sol), internals::mip_solution_origin_t::QUICK_FEASIBLE); } template @@ -192,7 +192,7 @@ void diversity_manager_t::add_user_given_solutions( is_feasible, sol.get_user_objective(), sol.get_total_excess()); - population.run_solution_callbacks(sol); + population.run_solution_callbacks(sol, internals::mip_solution_origin_t::USER_INITIAL); initial_sol_vector.emplace_back(std::move(sol)); } else { CUOPT_LOG_ERROR( @@ -298,13 +298,13 @@ void diversity_manager_t::generate_quick_feasible_solution() // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); if (solution.get_feasible()) { - population.run_solution_callbacks(solution); + population.run_solution_callbacks(solution, internals::mip_solution_origin_t::QUICK_FEASIBLE); initial_sol_vector.emplace_back(std::move(solution)); problem_ptr->handle_ptr->sync_stream(); solution_t searched_sol(initial_sol_vector.back()); ls_config_t ls_config; run_local_search(searched_sol, population.weights, sol_timer, ls_config); - population.run_solution_callbacks(searched_sol); + population.run_solution_callbacks(searched_sol, internals::mip_solution_origin_t::LOCAL_SEARCH); initial_sol_vector.emplace_back(std::move(searched_sol)); auto& feas_sol = initial_sol_vector.back().get_feasible() ? initial_sol_vector.back() @@ -406,29 +406,6 @@ solution_t diversity_manager_t::run_solver() // Debug: Allow disabling GPU heuristics to test B&B tree determinism in isolation const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); - if (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC) { - CUOPT_LOG_INFO("Running deterministic mode with CPUFJ heuristic"); - population.initialize_population(); - population.allocate_solutions(); - - // Start CPUFJ in deterministic mode with B&B integration - if (context.branch_and_bound_ptr != nullptr) { - ls.start_cpufj_deterministic(*context.branch_and_bound_ptr); - } - - while (!check_b_b_preemption()) { - if (timer.check_time_limit()) break; - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - - // Stop CPUFJ when B&B is done - ls.stop_cpufj_deterministic(); - - population.add_external_solutions_to_population(); - auto& best_sol = population.best_feasible(); - log_return_solution("deterministic_cpufj", best_sol); - return best_sol; - } if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); population.initialize_population(); @@ -449,7 +426,7 @@ solution_t diversity_manager_t::run_solver() producer_sync.deregister_producer(context.gpu_heur_loop.producer_progress_ptr()); context.gpu_heur_loop.detach_producer_sync(); }); - if (is_gpu_heuristics_deterministic_mode(context.settings.determinism_mode) && + if (is_deterministic_mode(context.settings.determinism_mode) && context.branch_and_bound_ptr != nullptr) { auto& producer_sync = context.branch_and_bound_ptr->get_producer_sync(); context.gpu_heur_loop.attach_producer_sync(&producer_sync); @@ -666,7 +643,8 @@ solution_t diversity_manager_t::run_solver() lp_rounded_sol.get_hash(), (int)lp_rounded_sol.get_feasible(), lp_rounded_sol.get_user_objective()); - population.add_solution(std::move(lp_rounded_sol)); + population.add_solution(std::move(lp_rounded_sol), + internals::mip_solution_origin_t::LP_ROUNDING); if (!diversity_config.dry_run && context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { ls.start_cpufj_lptopt_scratch_threads(population); @@ -680,7 +658,8 @@ solution_t diversity_manager_t::run_solver() (int)initial_sol_vector[i].get_feasible(), initial_sol_vector[i].get_user_objective()); } - population.add_solutions_from_vec(std::move(initial_sol_vector)); + population.add_solutions_from_vec(std::move(initial_sol_vector), + internals::mip_solution_origin_t::USER_INITIAL); if (check_b_b_preemption()) { auto& best_sol = population.best_feasible(); @@ -753,8 +732,10 @@ void diversity_manager_t::diversity_step(i_t max_iterations_without_im auto [sol1, sol2] = population.get_two_random(tournament); cuopt_assert(population.test_invariant(), ""); auto [lp_offspring, offspring] = recombine_and_local_search(sol1, sol2); - auto [inserted_pos_1, best_updated_1] = population.add_solution(std::move(lp_offspring)); - auto [inserted_pos_2, best_updated_2] = population.add_solution(std::move(offspring)); + auto [inserted_pos_1, best_updated_1] = population.add_solution( + std::move(lp_offspring), internals::mip_solution_origin_t::RECOMBINATION); + auto [inserted_pos_2, best_updated_2] = population.add_solution( + std::move(offspring), internals::mip_solution_origin_t::RECOMBINATION); if (best_updated_1 || best_updated_2) { recombine_stats.add_best_updated(); } cuopt_assert(population.test_invariant(), ""); if ((inserted_pos_1 != -1 && inserted_pos_1 <= 2) || @@ -796,10 +777,12 @@ void diversity_manager_t::recombine_and_ls_with_all(solution_t::recombine_and_ls_with_all( // add all solutions because time limit might have been consumed and we might have exited before for (auto& sol : solutions) { cuopt_func_call(sol.test_feasibility(true)); - population.add_solution(std::move(solution_t(sol))); + population.add_solution(std::move(solution_t(sol)), + internals::mip_solution_origin_t::BRANCH_AND_BOUND); } for (auto& sol : solutions) { if (work_limit_reached()) { return; } diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index a8d2cec6d4..7ec97925f9 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -8,6 +8,8 @@ #include "diversity_manager.cuh" #include "population.cuh" +#include + #include #include #include @@ -47,7 +49,8 @@ population_t::population_t(std::string const& name_, population_hash_map(*problem_ptr), timer(context.gpu_heur_loop, 0) { - best_feasible_objective = std::numeric_limits::max(); + best_feasible_objective = std::numeric_limits::max(); + best_callback_feasible_objective = std::numeric_limits::max(); } template @@ -126,11 +129,12 @@ std::pair, solution_t> population_t::ge } template -void population_t::add_solutions_from_vec(std::vector>&& solutions) +void population_t::add_solutions_from_vec( + std::vector>&& solutions, internals::mip_solution_origin_t callback_origin) { raft::common::nvtx::range fun_scope("add_solution_from_vec"); for (auto&& sol : solutions) { - add_solution(std::move(sol)); + add_solution(std::move(sol), callback_origin); } } @@ -292,29 +296,127 @@ void population_t::invoke_get_solution_callback( } template -void population_t::run_solution_callbacks(solution_t& sol) +void population_t::invoke_get_solution_callback_ext( + solution_t& sol, + internals::get_solution_callback_ext_t* callback, + const internals::mip_solution_callback_info_t& callback_info) +{ + f_t user_objective = sol.get_user_objective(); + f_t user_bound = context.stats.get_solution_bound(); + solution_t temp_sol(sol); + problem_ptr->post_process_assignment(temp_sol.assignment); + if (context.settings.mip_scaling) { + rmm::device_uvector dummy(0, temp_sol.handle_ptr->get_stream()); + context.scaling.unscale_solutions(temp_sol.assignment, dummy); + } + if (problem_ptr->has_papilo_presolve_data()) { + problem_ptr->papilo_uncrush_assignment(temp_sol.assignment); + } + + std::vector user_objective_vec(1); + std::vector user_bound_vec(1); + std::vector user_assignment_vec(temp_sol.assignment.size()); + user_objective_vec[0] = user_objective; + user_bound_vec[0] = user_bound; + raft::copy(user_assignment_vec.data(), + temp_sol.assignment.data(), + temp_sol.assignment.size(), + temp_sol.handle_ptr->get_stream()); + temp_sol.handle_ptr->sync_stream(); + callback->get_solution(user_assignment_vec.data(), + user_objective_vec.data(), + user_bound_vec.data(), + &callback_info, + callback->get_user_data()); +} + +template +void population_t::invoke_get_solution_callbacks( + solution_t& sol, + internals::mip_solution_origin_t callback_origin, + double work_timestamp) +{ + internals::mip_solution_callback_info_t callback_info{}; + auto user_callbacks = context.settings.get_mip_callbacks(); + if (work_timestamp < 0.0 && is_deterministic_mode(context.settings.determinism_mode)) { + work_timestamp = context.gpu_heur_loop.current_work(); + } + callback_info.origin = callback_origin; + callback_info.work_timestamp = work_timestamp; + CUOPT_LOG_DEBUG("Publishing incumbent: obj=%g wut=%.6f origin=%s callbacks=%zu", + sol.get_user_objective(), + work_timestamp, + internals::mip_solution_origin_to_string(callback_origin), + user_callbacks.size()); + for (auto callback : user_callbacks) { + if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { + auto get_sol_callback = static_cast(callback); + invoke_get_solution_callback(sol, get_sol_callback); + } else if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION_EXT) { + auto get_sol_callback_ext = static_cast(callback); + invoke_get_solution_callback_ext(sol, get_sol_callback_ext, callback_info); + } + } +} + +template +bool population_t::try_publish_new_best_feasible_to_get_callbacks( + solution_t& sol, + internals::mip_solution_origin_t callback_origin, + double work_timestamp) +{ + std::lock_guard lock(solution_callback_mutex); + if (!sol.get_feasible()) { return false; } + cuopt_assert(std::isfinite(sol.get_objective()), "Feasible incumbent objective must be finite"); + if (!(sol.get_objective() < best_callback_feasible_objective)) { return false; } + + if (context.settings.benchmark_info_ptr != nullptr) { + context.settings.benchmark_info_ptr->last_improvement_of_best_feasible = timer.elapsed_time(); + } + CUOPT_LOG_DEBUG("Population: Found new best solution %g", sol.get_user_objective()); + invoke_get_solution_callbacks(sol, callback_origin, work_timestamp); + + // Save the best objective here, because unscaling can make the callback-side solution infeasible. + best_callback_feasible_objective = sol.get_objective(); + return true; +} + +template +void population_t::run_solution_callbacks( + solution_t& sol, internals::mip_solution_origin_t callback_origin) { bool better_solution_found = is_better_than_best_feasible(sol); auto user_callbacks = context.settings.get_mip_callbacks(); if (better_solution_found) { - if (context.settings.benchmark_info_ptr != nullptr) { - context.settings.benchmark_info_ptr->last_improvement_of_best_feasible = timer.elapsed_time(); - } - CUOPT_LOG_DEBUG("Population: Found new best solution %g", sol.get_user_objective()); - if (problem_ptr->branch_and_bound_callback != nullptr) { - problem_ptr->branch_and_bound_callback(sol.get_host_assignment()); - } - for (auto callback : user_callbacks) { - if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { - auto get_sol_callback = static_cast(callback); - invoke_get_solution_callback(sol, get_sol_callback); + const bool deterministic_callback_owner_is_bb = + is_deterministic_mode(context.settings.determinism_mode) && + context.branch_and_bound_ptr != nullptr; + if (deterministic_callback_owner_is_bb) { + cuopt_assert(sol.get_feasible(), + "Deterministic heuristic posting requires a feasible solution"); + cuopt_assert(sol.get_objective() < best_feasible_objective, + "Deterministic heuristic posting must strictly improve best feasible objective"); + const double work_timestamp = context.gpu_heur_loop.current_producer_work(); + cuopt_assert(std::isfinite(work_timestamp), + "Deterministic heuristic work timestamp must be finite"); + CUOPT_LOG_DEBUG( + "Submitting deterministic heuristic incumbent: obj=%g wut=%.6f origin=%s hash=0x%x", + sol.get_user_objective(), + work_timestamp, + internals::mip_solution_origin_to_string(callback_origin), + sol.get_hash()); + context.branch_and_bound_ptr->queue_external_solution_deterministic( + sol.get_host_assignment(), work_timestamp, callback_origin); + // In deterministic mode, B&B replay is the single owner of GET_SOLUTION callback ordering. + best_feasible_objective = sol.get_objective(); + } else { + if (problem_ptr->branch_and_bound_callback != nullptr) { + problem_ptr->branch_and_bound_callback(sol.get_host_assignment()); } + const bool published = + try_publish_new_best_feasible_to_get_callbacks(sol, callback_origin, -1.0); + cuopt_assert(published, "New best feasible solution should publish to GET callbacks"); } - // save the best objective here, because we might not have been able to return the solution to - // the user because of the unscaling that causes infeasibility. - // This prevents an issue of repaired, or a fully feasible solution being reported in the call - // back in next run. - best_feasible_objective = sol.get_objective(); } for (auto callback : user_callbacks) { @@ -410,7 +512,8 @@ void population_t::adjust_weights_according_to_best_feasible() } template -std::pair population_t::add_solution(solution_t&& sol) +std::pair population_t::add_solution( + solution_t&& sol, internals::mip_solution_origin_t callback_origin) { std::lock_guard lock(write_mutex); raft::common::nvtx::range fun_scope("add_solution"); @@ -438,7 +541,7 @@ std::pair population_t::add_solution(solution_t&& // We store the best feasible found so far at index 0. if (sol.get_feasible() && (solutions[0].first == false || sol_cost + OBJECTIVE_EPSILON < indices[0].second)) { - run_solution_callbacks(sol); + run_solution_callbacks(sol, callback_origin); solutions[0].first = true; // we only have move assignment operator solution_t temp_sol(sol); diff --git a/cpp/src/mip_heuristics/diversity/population.cuh b/cpp/src/mip_heuristics/diversity/population.cuh index a0ac8a8262..29653ac66c 100644 --- a/cpp/src/mip_heuristics/diversity/population.cuh +++ b/cpp/src/mip_heuristics/diversity/population.cuh @@ -83,6 +83,8 @@ class population_t { a.first = false; indices[0].second = std::numeric_limits::max(); indices.erase(indices.begin() + 1, indices.end()); + best_feasible_objective = std::numeric_limits::max(); + best_callback_feasible_objective = std::numeric_limits::max(); } void clear_except_best_feasible() @@ -92,6 +94,8 @@ class population_t { } solutions[indices[0].first].first = true; indices.erase(indices.begin() + 1, indices.end()); + best_feasible_objective = solutions[indices[0].first].second.get_objective(); + best_callback_feasible_objective = best_feasible_objective; } // ------------------- @@ -103,7 +107,9 @@ class population_t { /*! \brief { Add a solution to population. Similar solutions may be ejected from the pool. } * \return { -1 = not inserted , others = inserted index} */ - std::pair add_solution(solution_t&& sol); + std::pair add_solution( + solution_t&& sol, + internals::mip_solution_origin_t callback_origin = internals::mip_solution_origin_t::UNKNOWN); void add_external_solution(const std::vector& solution, f_t objective, solution_origin_t origin); @@ -112,7 +118,9 @@ class population_t { size_t get_external_solution_size(); void preempt_heuristic_solver(); - void add_solutions_from_vec(std::vector>&& solutions); + void add_solutions_from_vec( + std::vector>&& solutions, + internals::mip_solution_origin_t callback_origin = internals::mip_solution_origin_t::UNKNOWN); // Updates the cstr weights according to the best solutions feasibility void compute_new_weights(); @@ -153,7 +161,9 @@ class population_t { std::vector> population_to_vector(); void halve_the_population(); - void run_solution_callbacks(solution_t& sol); + void run_solution_callbacks( + solution_t& sol, + internals::mip_solution_origin_t callback_origin = internals::mip_solution_origin_t::UNKNOWN); void adjust_weights_according_to_best_feasible(); @@ -161,8 +171,21 @@ class population_t { void diversity_step(i_t max_iterations_without_improvement); + bool try_publish_new_best_feasible_to_get_callbacks( + solution_t& sol, + internals::mip_solution_origin_t callback_origin = internals::mip_solution_origin_t::UNKNOWN, + double work_timestamp = -1.0); + + void invoke_get_solution_callbacks( + solution_t& sol, + internals::mip_solution_origin_t callback_origin = internals::mip_solution_origin_t::UNKNOWN, + double work_timestamp = -1.0); void invoke_get_solution_callback(solution_t& sol, internals::get_solution_callback_t* callback); + void invoke_get_solution_callback_ext( + solution_t& sol, + internals::get_solution_callback_ext_t* callback, + const internals::mip_solution_callback_info_t& callback_info); // does some consistency tests bool test_invariant(); @@ -205,9 +228,11 @@ class population_t { i_t update_iter = 0; std::recursive_mutex write_mutex; std::mutex solution_mutex; + std::mutex solution_callback_mutex; std::atomic early_exit_primal_generation = false; std::atomic solutions_in_external_queue_ = false; f_t best_feasible_objective = std::numeric_limits::max(); + f_t best_callback_feasible_objective = std::numeric_limits::max(); assignment_hash_map_t population_hash_map; cuopt::work_limit_timer_t timer; }; diff --git a/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh b/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh index 502e161536..ed3d3c47cf 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh @@ -182,7 +182,7 @@ class sub_mip_recombiner_t : public recombiner_t { sol.clamp_within_bounds(); // Scaling might bring some very slight variable bound violations sol.compute_feasibility(); cuopt_func_call(sol.test_variable_bounds()); - population.add_solution(std::move(sol)); + population.add_solution(std::move(sol), internals::mip_solution_origin_t::SUB_MIP); } bool better_cost_than_parents = offspring.get_quality(weights) < diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu index 60f933e5b7..e642285b51 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu @@ -1063,14 +1063,15 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) { auto& data = *climbers[climber_idx]; auto v = data.view(); // == climber_views[climber_idx] - const bool post_incumbents_to_bb = - is_gpu_heuristics_deterministic_mode(context.settings.determinism_mode) && + const bool deterministic_bnb_integration = + is_deterministic_mode(context.settings.determinism_mode) && context.branch_and_bound_ptr != nullptr; const double work_units_at_start = context.gpu_heur_loop.current_work(); - const bool publish_progress = post_incumbents_to_bb && std::isfinite(settings.work_limit) && - settings.work_limit > 0.0 && settings.iteration_limit > 0 && + const bool publish_progress = deterministic_bnb_integration && + std::isfinite(settings.work_limit) && settings.work_limit > 0.0 && + settings.iteration_limit > 0 && settings.iteration_limit != std::numeric_limits::max(); - f_t last_posted_objective = std::numeric_limits::infinity(); + f_t last_published_objective = std::numeric_limits::infinity(); auto climber_stream = data.stream.view(); if (climber_idx == 0) climber_stream = handle_ptr->get_stream(); @@ -1167,15 +1168,18 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) bool is_feasible = solution.compute_feasibility(); solution.handle_ptr->sync_stream(); - if (post_incumbents_to_bb && is_feasible && - solution.get_objective() < last_posted_objective) { - const double work_timestamp = context.gpu_heur_loop.current_producer_work(); - context.branch_and_bound_ptr->queue_external_solution_deterministic( - solution.get_host_assignment(), work_timestamp); - last_posted_objective = solution.get_objective(); - CUOPT_DETERMINISM_LOG_INFO("FJ deterministic post: obj=%f work_ts=%f hash=0x%x", + if (deterministic_bnb_integration && is_feasible && + solution.get_objective() < last_published_objective) { + cuopt_assert(context.diversity_manager_ptr != nullptr, + "Deterministic FJ publication requires diversity manager context"); + auto* population_ptr = context.diversity_manager_ptr->get_population_pointer(); + cuopt_assert(population_ptr != nullptr, + "Deterministic FJ publication requires a population"); + population_ptr->add_solution(solution_t(solution), + internals::mip_solution_origin_t::FEASIBILITY_JUMP); + last_published_objective = solution.get_objective(); + CUOPT_DETERMINISM_LOG_INFO("FJ deterministic publish via population: obj=%f hash=0x%x", solution.get_user_objective(), - work_timestamp, solution.get_hash()); } diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index 6eac1c89ee..e40d67ee05 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -68,6 +68,8 @@ local_search_t::local_search_t(mip_solver_context_t& context template void local_search_t::start_cpufj_scratch_threads(population_t& population) { + cuopt_assert(context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC, + "Scratch CPUFJ must remain opportunistic-only"); pop_ptr = &population; std::vector default_weights(context.problem_ptr->n_constraints, 1.); @@ -116,6 +118,8 @@ template void local_search_t::start_cpufj_lptopt_scratch_threads( population_t& population) { + cuopt_assert(context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC, + "LP-opt CPUFJ scratch must remain opportunistic-only"); pop_ptr = &population; std::vector default_weights(context.problem_ptr->n_constraints, 1.); @@ -186,7 +190,8 @@ void local_search_t::start_cpufj_deterministic( // Set up callback to send solutions to B&B with work unit timestamps deterministic_cpu_fj.fj_cpu->improvement_callback = [&bb](f_t obj, const std::vector& h_vec, double work_units) { - bb.queue_external_solution_deterministic(h_vec, work_units); + bb.queue_external_solution_deterministic( + h_vec, work_units, cuopt::internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP); }; deterministic_cpu_fj.start_cpu_solver(); @@ -214,6 +219,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, const std::string& source) { if (time_limit == 0.) return solution.get_feasible(); + const bool use_cpufj_local_search = context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC; work_limit_timer_t timer(context.gpu_heur_loop, time_limit); const auto old_n_cstr_weights = in_fj.cstr_weights.size(); @@ -235,22 +241,24 @@ bool local_search_t::do_fj_solve(solution_t& solution, } } - auto h_weights = cuopt::host_copy(in_fj.cstr_weights, solution.handle_ptr->get_stream()); - auto h_objective_weight = in_fj.objective_weight.value(solution.handle_ptr->get_stream()); - for (auto& cpu_fj : ls_cpu_fj) { - cpu_fj.fj_cpu = cpu_fj.fj_ptr->create_cpu_climber(solution, - h_weights, - h_weights, - h_objective_weight, - context.preempt_heuristic_solver_, - fj_settings_t{}, - true); + if (use_cpufj_local_search) { + auto h_weights = cuopt::host_copy(in_fj.cstr_weights, solution.handle_ptr->get_stream()); + auto h_objective_weight = in_fj.objective_weight.value(solution.handle_ptr->get_stream()); + for (auto& cpu_fj : ls_cpu_fj) { + cpu_fj.fj_cpu = cpu_fj.fj_ptr->create_cpu_climber(solution, + h_weights, + h_weights, + h_objective_weight, + context.preempt_heuristic_solver_, + fj_settings_t{}, + true); + } } auto solution_copy = solution; // Start CPU solver in background thread - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + if (use_cpufj_local_search) { for (auto& cpu_fj : ls_cpu_fj) { cpu_fj.start_cpu_solver(); } @@ -262,7 +270,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, in_fj.solve(solution); // Stop CPU solver - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + if (use_cpufj_local_search) { for (auto& cpu_fj : ls_cpu_fj) { cpu_fj.stop_cpu_solver(); } @@ -275,7 +283,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, f_t best_cpu_obj = std::numeric_limits::max(); // // Wait for CPU solver to finish - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + if (use_cpufj_local_search) { for (auto& cpu_fj : ls_cpu_fj) { bool cpu_sol_found = cpu_fj.wait_for_cpu_solver(); if (cpu_sol_found) { @@ -680,7 +688,8 @@ void local_search_t::reset_alpha_and_save_solution( solution_t solution_copy(solution); solution_copy.problem_ptr = old_problem_ptr; solution_copy.resize_to_problem(); - population_ptr->add_solution(std::move(solution_copy)); + population_ptr->add_solution(std::move(solution_copy), + internals::mip_solution_origin_t::LOCAL_SEARCH); population_ptr->add_external_solutions_to_population(); if (!cutting_plane_added_for_active_run) { solution.problem_ptr = &problem_with_objective_cut; diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index e4c131918d..caaa3d3c13 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -100,26 +100,34 @@ mip_solution_t run_mip(detail::problem_t& problem, // log the objective for scripts which need it CUOPT_LOG_INFO("Best feasible: %f", solution.get_user_objective()); for (auto callback : settings.get_mip_callbacks()) { + auto temp_sol(solution); + std::vector user_objective_vec(1); + std::vector user_bound_vec(1); + user_objective_vec[0] = solution.get_user_objective(); + user_bound_vec[0] = stats.get_solution_bound(); + if (problem.has_papilo_presolve_data()) { + problem.papilo_uncrush_assignment(temp_sol.assignment); + } + std::vector user_assignment_vec(temp_sol.assignment.size()); + raft::copy(user_assignment_vec.data(), + temp_sol.assignment.data(), + temp_sol.assignment.size(), + temp_sol.handle_ptr->get_stream()); + solution.handle_ptr->sync_stream(); if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { - auto temp_sol(solution); auto get_sol_callback = static_cast(callback); - std::vector user_objective_vec(1); - std::vector user_bound_vec(1); - user_objective_vec[0] = solution.get_user_objective(); - user_bound_vec[0] = stats.get_solution_bound(); - if (problem.has_papilo_presolve_data()) { - problem.papilo_uncrush_assignment(temp_sol.assignment); - } - std::vector user_assignment_vec(temp_sol.assignment.size()); - raft::copy(user_assignment_vec.data(), - temp_sol.assignment.data(), - temp_sol.assignment.size(), - temp_sol.handle_ptr->get_stream()); - solution.handle_ptr->sync_stream(); get_sol_callback->get_solution(user_assignment_vec.data(), user_objective_vec.data(), user_bound_vec.data(), get_sol_callback->get_user_data()); + } else if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION_EXT) { + internals::mip_solution_callback_info_t callback_info{}; + auto get_sol_callback_ext = static_cast(callback); + get_sol_callback_ext->get_solution(user_assignment_vec.data(), + user_objective_vec.data(), + user_bound_vec.data(), + &callback_info, + get_sol_callback_ext->get_user_data()); } } return solution.get_solution(true, stats, false); diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 454306d099..86d9c18178 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -63,9 +63,37 @@ struct branch_and_bound_solution_helper_t { void solution_callback(std::vector& solution, f_t objective) { - if (is_deterministic_mode(settings_.deterministic_mode)) { return; } - dm->population.add_external_solution(solution, objective, solution_origin_t::BRANCH_AND_BOUND); - dm->rins.new_best_incumbent_callback(solution); + if (!settings_.deterministic) { + dm->population.add_external_solution( + solution, objective, solution_origin_t::BRANCH_AND_BOUND); + dm->rins.new_best_incumbent_callback(solution); + } + } + + void solution_callback_ext(std::vector& solution, + f_t objective, + const internals::mip_solution_callback_info_t& callback_info, + double work_timestamp) + { + if (!settings_.deterministic) { + dm->population.add_external_solution( + solution, objective, solution_origin_t::BRANCH_AND_BOUND); + dm->rins.new_best_incumbent_callback(solution); + return; + } + + cuopt_assert(dm != nullptr, "Diversity manager pointer must be valid"); + cuopt_assert(dm->context.problem_ptr != nullptr, "Problem pointer must be valid"); + cuopt_assert(solution.size() == (size_t)dm->context.problem_ptr->n_variables, + "Deterministic B&B callback solution size mismatch"); + + solution_t incumbent(*dm->context.problem_ptr); + incumbent.copy_new_assignment(solution); + incumbent.compute_feasibility(); + cuopt_assert(incumbent.get_feasible(), + "Deterministic B&B callback must provide a feasible incumbent"); + dm->population.try_publish_new_best_feasible_to_get_callbacks( + incumbent, callback_info.origin, work_timestamp); } void set_simplex_solution(std::vector& solution, @@ -100,12 +128,7 @@ solution_t mip_solver_t::run_solver() CUOPT_LOG_INFO("Problem fully reduced in presolve"); solution_t sol(*context.problem_ptr); sol.set_problem_fully_reduced(); - for (auto callback : context.settings.get_mip_callbacks()) { - if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { - auto get_sol_callback = static_cast(callback); - dm.population.invoke_get_solution_callback(sol, get_sol_callback); - } - } + dm.population.invoke_get_solution_callbacks(sol); context.problem_ptr->post_process_solution(sol); return sol; } @@ -130,12 +153,7 @@ solution_t mip_solver_t::run_solver() CUOPT_LOG_INFO("Problem full reduced in presolve"); solution_t sol(*context.problem_ptr); sol.set_problem_fully_reduced(); - for (auto callback : context.settings.get_mip_callbacks()) { - if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { - auto get_sol_callback = static_cast(callback); - dm.population.invoke_get_solution_callback(sol, get_sol_callback); - } - } + dm.population.invoke_get_solution_callbacks(sol); context.problem_ptr->post_process_solution(sol); return sol; } @@ -168,12 +186,7 @@ solution_t mip_solver_t::run_solver() sol.set_problem_fully_reduced(); } if (opt_sol.get_termination_status() == pdlp_termination_status_t::Optimal) { - for (auto callback : context.settings.get_mip_callbacks()) { - if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { - auto get_sol_callback = static_cast(callback); - dm.population.invoke_get_solution_callback(sol, get_sol_callback); - } - } + dm.population.invoke_get_solution_callbacks(sol); } context.problem_ptr->post_process_solution(sol); return sol; @@ -244,6 +257,13 @@ solution_t mip_solver_t::run_solver() &solution_helper, std::placeholders::_1, std::placeholders::_2); + branch_and_bound_settings.solution_callback_ext = + std::bind(&branch_and_bound_solution_helper_t::solution_callback_ext, + &solution_helper, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4); // heuristic_preemption_callback is needed in both modes to properly stop the heuristic thread branch_and_bound_settings.heuristic_preemption_callback = std::bind( &branch_and_bound_solution_helper_t::preempt_heuristic_solver, &solution_helper); diff --git a/cpp/src/pdlp/cuopt_c.cpp b/cpp/src/pdlp/cuopt_c.cpp index ed2eab02f2..5b2a0a7ba5 100644 --- a/cpp/src/pdlp/cuopt_c.cpp +++ b/cpp/src/pdlp/cuopt_c.cpp @@ -49,6 +49,40 @@ class c_get_solution_callback_t : public cuopt::internals::get_solution_callback cuOptMIPGetSolutionCallback callback_; }; +class c_get_solution_callback_ext_t : public cuopt::internals::get_solution_callback_ext_t { + public: + explicit c_get_solution_callback_ext_t(cuOptMIPGetSolutionCallbackExt callback) + : callback_(callback) + { + } + + void get_solution(void* data, + void* objective_value, + void* solution_bound, + const cuopt::internals::mip_solution_callback_info_t* callback_info, + void* user_data) override + { + if (callback_ == nullptr) { return; } + cuOptMIPSolutionCallbackInfo c_callback_info{}; + c_callback_info.struct_size = sizeof(cuOptMIPSolutionCallbackInfo); + if (callback_info != nullptr) { + c_callback_info.origin = static_cast(callback_info->origin); + c_callback_info.work_timestamp = callback_info->work_timestamp; + } else { + c_callback_info.origin = CUOPT_MIP_SOLUTION_ORIGIN_UNKNOWN; + c_callback_info.work_timestamp = -1.0; + } + callback_(static_cast(data), + static_cast(objective_value), + static_cast(solution_bound), + &c_callback_info, + user_data); + } + + private: + cuOptMIPGetSolutionCallbackExt callback_; +}; + class c_set_solution_callback_t : public cuopt::internals::set_solution_callback_t { public: explicit c_set_solution_callback_t(cuOptMIPSetSolutionCallback callback) : callback_(callback) {} @@ -767,6 +801,19 @@ cuopt_int_t cuOptSetMIPGetSolutionCallback(cuOptSolverSettings settings, return CUOPT_SUCCESS; } +cuopt_int_t cuOptSetMIPGetSolutionCallbackExt(cuOptSolverSettings settings, + cuOptMIPGetSolutionCallbackExt callback, + void* user_data) +{ + if (settings == nullptr) { return CUOPT_INVALID_ARGUMENT; } + if (callback == nullptr) { return CUOPT_INVALID_ARGUMENT; } + solver_settings_handle_t* settings_handle = get_settings_handle(settings); + auto callback_wrapper = std::make_unique(callback); + settings_handle->settings->set_mip_callback(callback_wrapper.get(), user_data); + settings_handle->callbacks.push_back(std::move(callback_wrapper)); + return CUOPT_SUCCESS; +} + cuopt_int_t cuOptSetMIPSetSolutionCallback(cuOptSolverSettings settings, cuOptMIPSetSolutionCallback callback, void* user_data) diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp index 4b9db4240d..b81cb31d8d 100644 --- a/cpp/src/utilities/work_limit_timer.hpp +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -115,7 +115,7 @@ class work_limit_timer_t { if (deterministic) { if (!work_context) { return work_limit; } double elapsed_since_start = work_context->current_work() - work_units_at_start; - return work_limit - elapsed_since_start; + return std::max(0.0, work_limit - elapsed_since_start); } else { return timer.remaining_time(); } diff --git a/cpp/tests/mip/determinism_test.cu b/cpp/tests/mip/determinism_test.cu index f40eeead2b..c013b7a0d9 100644 --- a/cpp/tests/mip/determinism_test.cu +++ b/cpp/tests/mip/determinism_test.cu @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,118 @@ void expect_solutions_bitwise_equal(const mip_solution_t& sol1, } } +struct callback_solution_t { + std::vector assignment; + double objective{}; + double solution_bound{}; + internals::mip_solution_origin_t origin{internals::mip_solution_origin_t::UNKNOWN}; +}; + +class first_n_get_solution_callback_t : public cuopt::internals::get_solution_callback_ext_t { + public: + first_n_get_solution_callback_t(std::vector& solutions_in, + int n_variables_, + size_t max_solutions_, + void* expected_user_data_) + : solutions(solutions_in), + expected_user_data(expected_user_data_), + n_variables(n_variables_), + max_solutions(max_solutions_) + { + } + + void get_solution(void* data, + void* cost, + void* solution_bound, + const internals::mip_solution_callback_info_t* callback_info, + void* user_data) override + { + EXPECT_EQ(user_data, expected_user_data); + ASSERT_NE(callback_info, nullptr); + EXPECT_GE(callback_info->struct_size, sizeof(internals::mip_solution_callback_info_t)); + n_calls++; + + auto assignment_ptr = static_cast(data); + auto objective_ptr = static_cast(cost); + auto solution_bound_ptr = static_cast(solution_bound); + EXPECT_FALSE(std::isnan(objective_ptr[0])); + EXPECT_FALSE(std::isnan(solution_bound_ptr[0])); + + if (solutions.size() >= max_solutions) { return; } + + callback_solution_t callback_solution; + callback_solution.assignment.assign(assignment_ptr, assignment_ptr + n_variables); + callback_solution.objective = objective_ptr[0]; + callback_solution.solution_bound = solution_bound_ptr[0]; + callback_solution.origin = callback_info->origin; + solutions.push_back(std::move(callback_solution)); + } + + std::vector& solutions; + void* expected_user_data; + int n_calls{0}; + int n_variables; + size_t max_solutions; +}; + +bool is_gpu_callback_origin(internals::mip_solution_origin_t origin) +{ + switch (origin) { + case internals::mip_solution_origin_t::FEASIBILITY_JUMP: + case internals::mip_solution_origin_t::LOCAL_SEARCH: + case internals::mip_solution_origin_t::QUICK_FEASIBLE: + case internals::mip_solution_origin_t::LP_ROUNDING: + case internals::mip_solution_origin_t::RECOMBINATION: + case internals::mip_solution_origin_t::SUB_MIP: return true; + default: return false; + } +} + +size_t count_callbacks_with_origin(const std::vector& callbacks, + internals::mip_solution_origin_t origin) +{ + return std::count_if(callbacks.begin(), + callbacks.end(), + [origin](const callback_solution_t& sol) { return sol.origin == origin; }); +} + +size_t count_gpu_callbacks(const std::vector& callbacks) +{ + return std::count_if(callbacks.begin(), callbacks.end(), [](const callback_solution_t& sol) { + return is_gpu_callback_origin(sol.origin); + }); +} + +size_t count_branch_and_bound_callbacks(const std::vector& callbacks) +{ + return std::count_if(callbacks.begin(), callbacks.end(), [](const callback_solution_t& sol) { + return sol.origin == internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE || + sol.origin == internals::mip_solution_origin_t::BRANCH_AND_BOUND_DIVING; + }); +} + +void expect_callback_prefixes_bitwise_equal(const std::vector& lhs, + const std::vector& rhs, + size_t prefix_size, + const std::string& label) +{ + ASSERT_GE(lhs.size(), prefix_size) << label << "Left callback prefix missing entries"; + ASSERT_GE(rhs.size(), prefix_size) << label << "Right callback prefix missing entries"; + for (size_t i = 0; i < prefix_size; ++i) { + EXPECT_EQ(lhs[i].objective, rhs[i].objective) + << label << "Callback objective differs at index " << i; + EXPECT_EQ(lhs[i].solution_bound, rhs[i].solution_bound) + << label << "Callback bound differs at index " << i; + EXPECT_EQ(lhs[i].origin, rhs[i].origin) << label << "Callback origin differs at index " << i; + ASSERT_EQ(lhs[i].assignment.size(), rhs[i].assignment.size()) + << label << "Callback assignment size differs at index " << i; + for (size_t j = 0; j < lhs[i].assignment.size(); ++j) { + EXPECT_EQ(lhs[i].assignment[j], rhs[i].assignment[j]) + << label << "Callback assignment differs at callback " << i << " variable " << j; + } + } +} + } // namespace class DeterministicBBTest : public ::testing::Test { @@ -206,6 +319,157 @@ TEST_F(DeterministicBBTest, reproducible_solution_vector) expect_solutions_bitwise_equal(solution1, solution2, handle_); } +TEST_F(DeterministicBBTest, reproducible_with_gpu_pipeline_in_deterministic_mode) +{ + auto path = make_path_absolute("/mip/50v-10.mps"); + auto problem = mps_parser::parse_mps(path, false); + handle_.sync_stream(); + + mip_solver_settings_t settings; + settings.time_limit = 60.0; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.num_cpu_threads = 8; + settings.work_limit = 30; + settings.gpu_heur_work_unit_scale = 0.1; + settings.cpufj_work_unit_scale = 1.0; + + auto seed = std::random_device{}() & 0x7fffffff; + std::cout << "Tested with seed " << seed << "\n"; + settings.seed = seed; + + cuopt::seed_generator::set_seed(seed); + auto solution1 = solve_mip(&handle_, problem, settings); + cuopt::seed_generator::set_seed(seed); + auto solution2 = solve_mip(&handle_, problem, settings); + cuopt::seed_generator::set_seed(seed); + auto solution3 = solve_mip(&handle_, problem, settings); + + EXPECT_EQ(solution1.get_termination_status(), solution2.get_termination_status()); + EXPECT_EQ(solution1.get_termination_status(), solution3.get_termination_status()); + + EXPECT_DOUBLE_EQ(solution1.get_objective_value(), solution2.get_objective_value()); + EXPECT_DOUBLE_EQ(solution1.get_objective_value(), solution3.get_objective_value()); + + EXPECT_DOUBLE_EQ(solution1.get_solution_bound(), solution2.get_solution_bound()); + EXPECT_DOUBLE_EQ(solution1.get_solution_bound(), solution3.get_solution_bound()); + + expect_solutions_bitwise_equal( + solution1, solution2, handle_, "Deterministic GPU pipeline run 1 vs 2: "); + expect_solutions_bitwise_equal( + solution1, solution3, handle_, "Deterministic GPU pipeline run 1 vs 3: "); +} + +TEST_F(DeterministicBBTest, deterministic_gpu_pipeline_ignores_cpufj_work_scale) +{ + auto path = make_path_absolute("/mip/50v-10.mps"); + auto problem = mps_parser::parse_mps(path, false); + handle_.sync_stream(); + + mip_solver_settings_t base_settings; + base_settings.time_limit = 60.0; + base_settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + base_settings.num_cpu_threads = 8; + base_settings.work_limit = 30; + base_settings.gpu_heur_work_unit_scale = 0.1; + + auto seed = std::random_device{}() & 0x7fffffff; + std::cout << "Tested with seed " << seed << "\n"; + base_settings.seed = seed; + + auto settings_without_cpufj = base_settings; + settings_without_cpufj.cpufj_work_unit_scale = 1.0; + cuopt::seed_generator::set_seed(seed); + auto solution_without_cpufj = solve_mip(&handle_, problem, settings_without_cpufj); + + auto settings_with_cpufj_scale = base_settings; + settings_with_cpufj_scale.cpufj_work_unit_scale = 17.0; + cuopt::seed_generator::set_seed(seed); + auto solution_with_cpufj_scale = solve_mip(&handle_, problem, settings_with_cpufj_scale); + + EXPECT_EQ(solution_without_cpufj.get_termination_status(), + solution_with_cpufj_scale.get_termination_status()); + EXPECT_DOUBLE_EQ(solution_without_cpufj.get_objective_value(), + solution_with_cpufj_scale.get_objective_value()); + EXPECT_DOUBLE_EQ(solution_without_cpufj.get_solution_bound(), + solution_with_cpufj_scale.get_solution_bound()); + expect_solutions_bitwise_equal(solution_without_cpufj, + solution_with_cpufj_scale, + handle_, + "Deterministic GPU pipeline should ignore CPUFJ scale: "); +} + +TEST_F(DeterministicBBTest, deterministic_callback_sequence_reproducible_with_gpu_pipeline) +{ + constexpr size_t callback_compare_count = 5; + constexpr size_t callback_capture_limit = 32; + constexpr size_t min_gpu_callback_count = 3; + constexpr size_t min_bnb_callback_count = 3; + + auto path = make_path_absolute("/mip/50v-10.mps"); + auto problem = mps_parser::parse_mps(path, false); + handle_.sync_stream(); + + mip_solver_settings_t settings; + settings.time_limit = 360.0; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.num_cpu_threads = 2; + settings.work_limit = 60; + settings.gpu_heur_work_unit_scale = 0.05; + settings.cpufj_work_unit_scale = 1.0; + + auto seed = std::random_device{}() & 0x7fffffff; + std::cout << "Tested with seed " << seed << "\n"; + settings.seed = seed; + + const int n_variables = problem.get_variable_lower_bounds().size(); + int user_data = 7; + + std::vector callbacks_run1; + first_n_get_solution_callback_t callback_run1( + callbacks_run1, n_variables, callback_capture_limit, &user_data); + auto settings_run1 = settings; + settings_run1.set_mip_callback(&callback_run1, &user_data); + cuopt::seed_generator::set_seed(seed); + auto solution1 = solve_mip(&handle_, problem, settings_run1); + + std::vector callbacks_run2; + first_n_get_solution_callback_t callback_run2( + callbacks_run2, n_variables, callback_capture_limit, &user_data); + auto settings_run2 = settings; + settings_run2.set_mip_callback(&callback_run2, &user_data); + cuopt::seed_generator::set_seed(seed); + auto solution2 = solve_mip(&handle_, problem, settings_run2); + + std::vector callbacks_run3; + first_n_get_solution_callback_t callback_run3( + callbacks_run3, n_variables, callback_capture_limit, &user_data); + auto settings_run3 = settings; + settings_run3.set_mip_callback(&callback_run3, &user_data); + cuopt::seed_generator::set_seed(seed); + auto solution3 = solve_mip(&handle_, problem, settings_run3); + + EXPECT_EQ(solution1.get_termination_status(), solution2.get_termination_status()); + EXPECT_EQ(solution1.get_termination_status(), solution3.get_termination_status()); + EXPECT_GE(callback_run1.n_calls, (int)callback_compare_count); + EXPECT_GE(callback_run2.n_calls, (int)callback_compare_count); + EXPECT_GE(callback_run3.n_calls, (int)callback_compare_count); + ASSERT_GE(callbacks_run1.size(), callback_compare_count); + ASSERT_GE(callbacks_run2.size(), callback_compare_count); + ASSERT_GE(callbacks_run3.size(), callback_compare_count); + + EXPECT_GE(count_gpu_callbacks(callbacks_run1), min_gpu_callback_count); + EXPECT_GE(count_gpu_callbacks(callbacks_run2), min_gpu_callback_count); + EXPECT_GE(count_gpu_callbacks(callbacks_run3), min_gpu_callback_count); + EXPECT_GE(count_branch_and_bound_callbacks(callbacks_run1), min_bnb_callback_count); + EXPECT_GE(count_branch_and_bound_callbacks(callbacks_run2), min_bnb_callback_count); + EXPECT_GE(count_branch_and_bound_callbacks(callbacks_run3), min_bnb_callback_count); + + expect_callback_prefixes_bitwise_equal( + callbacks_run1, callbacks_run2, callback_compare_count, "Deterministic callback run 1 vs 2: "); + expect_callback_prefixes_bitwise_equal( + callbacks_run1, callbacks_run3, callback_compare_count, "Deterministic callback run 1 vs 3: "); +} + class DeterministicGpuHeuristicsInstanceTest : public ::testing::TestWithParam { protected: raft::handle_t handle_; From 5e13486f98fc054e69de54b269941e2ca669abd2 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 7 Mar 2026 12:50:44 -0800 Subject: [PATCH 139/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 28ab0e22bb..c639540178 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -211,7 +211,7 @@ int run_single_file(std::string file_path, settings.determinism_mode = CUOPT_MODE_OPPORTUNISTIC; } CUOPT_LOG_INFO( - "run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " + "1run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " "time_limit=%.6f work_limit=%.6f", (int)heuristics_only, (int)deterministic, From ec5152a861c963af8e726394864b17fe6ebdd242 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 7 Mar 2026 12:52:44 -0800 Subject: [PATCH 140/225] bump2 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index c639540178..28ab0e22bb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -211,7 +211,7 @@ int run_single_file(std::string file_path, settings.determinism_mode = CUOPT_MODE_OPPORTUNISTIC; } CUOPT_LOG_INFO( - "1run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " + "run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " "time_limit=%.6f work_limit=%.6f", (int)heuristics_only, (int)deterministic, From 0d70d96634501c017a934057ee706d0f8cd092ce Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 8 Mar 2026 05:39:19 -0700 Subject: [PATCH 141/225] refactor --- .../diversity/diversity_manager.cu | 16 +- .../mip_heuristics/diversity/population.cu | 193 +++++++----------- .../mip_heuristics/diversity/population.cuh | 26 +-- .../feasibility_jump/feasibility_jump.cu | 26 +-- cpp/src/mip_heuristics/solver.cu | 35 ++-- cpp/src/mip_heuristics/solver_context.cuh | 118 ++++++++++- 6 files changed, 228 insertions(+), 186 deletions(-) diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 650671896f..b7938617ee 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -192,7 +192,6 @@ void diversity_manager_t::add_user_given_solutions( is_feasible, sol.get_user_objective(), sol.get_total_excess()); - population.run_solution_callbacks(sol, internals::mip_solution_origin_t::USER_INITIAL); initial_sol_vector.emplace_back(std::move(sol)); } else { CUOPT_LOG_ERROR( @@ -237,8 +236,10 @@ bool diversity_manager_t::run_presolve(f_t time_limit, timer_t global_ const bool remap_cache_ids = true; if (!global_timer.check_time_limit()) { trivial_presolve(*problem_ptr, remap_cache_ids); } if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; } - if (!presolve_timer.check_time_limit() && !context.settings.heuristics_only && - !problem_ptr->empty) { + const bool run_clique_table = !presolve_timer.check_time_limit() && + !context.settings.heuristics_only && !problem_ptr->empty && + !is_deterministic_mode(context.settings.determinism_mode); + if (run_clique_table) { f_t time_limit_for_clique_table = std::min(3., presolve_timer.remaining_time() / 5); timer_t clique_timer(time_limit_for_clique_table); dual_simplex::user_problem_t host_problem(problem_ptr->handle_ptr); @@ -298,13 +299,11 @@ void diversity_manager_t::generate_quick_feasible_solution() // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); if (solution.get_feasible()) { - population.run_solution_callbacks(solution, internals::mip_solution_origin_t::QUICK_FEASIBLE); initial_sol_vector.emplace_back(std::move(solution)); problem_ptr->handle_ptr->sync_stream(); solution_t searched_sol(initial_sol_vector.back()); ls_config_t ls_config; run_local_search(searched_sol, population.weights, sol_timer, ls_config); - population.run_solution_callbacks(searched_sol, internals::mip_solution_origin_t::LOCAL_SEARCH); initial_sol_vector.emplace_back(std::move(searched_sol)); auto& feas_sol = initial_sol_vector.back().get_feasible() ? initial_sol_vector.back() @@ -338,6 +337,10 @@ void diversity_manager_t::run_fj_alone(solution_t& solution) ls.fj.settings.feasibility_run = false; ls.fj.settings.time_limit = timer.remaining_time(); ls.fj.solve(solution); + if (solution.get_feasible()) { + population.add_solution(std::move(solution), + internals::mip_solution_origin_t::FEASIBILITY_JUMP); + } CUOPT_LOG_INFO("FJ alone finished!"); } @@ -362,6 +365,9 @@ void diversity_manager_t::run_fp_alone() (int)sol.get_feasible(), sol.get_user_objective(), sol.get_total_excess()); + if (sol.get_feasible()) { + population.add_solution(std::move(sol), internals::mip_solution_origin_t::LOCAL_SEARCH); + } auto& best_sol = population.best_feasible(); best_sol.handle_ptr->sync_stream(); CUOPT_DETERMINISM_LOG_INFO( diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index 7ec97925f9..b3831de21e 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -29,6 +29,60 @@ constexpr double min_infeasibility_weight = 1.; constexpr double infeasibility_balance_ratio = 1.1; constexpr double halving_skip_ratio = 0.75; +template +void assert_solution_matches_population_problem(const char* location, + const char* role, + const problem_t* population_problem, + const solution_t& sol) +{ + cuopt_assert(population_problem != nullptr, "Population problem must not be null"); + cuopt_assert(sol.problem_ptr != nullptr, "Solution problem must not be null"); + const size_t assignment_size = sol.assignment.size(); + const size_t solution_vars = (size_t)sol.problem_ptr->n_variables; + const size_t population_vars = (size_t)population_problem->n_variables; + if (assignment_size != solution_vars || assignment_size != population_vars || + sol.problem_ptr != population_problem) { + CUOPT_LOG_ERROR( + "%s: %s shape mismatch assignment=%zu solution_vars=%zu population_vars=%zu sol_hash=0x%x " + "solution_problem=0x%x population_problem=0x%x", + location, + role, + assignment_size, + solution_vars, + population_vars, + sol.get_hash(), + sol.problem_ptr->get_fingerprint(), + population_problem->get_fingerprint()); + } + cuopt_assert(assignment_size == solution_vars, "Solution assignment must match its problem size"); + cuopt_assert(assignment_size == population_vars, + "Solution assignment must match the population problem size"); + cuopt_assert(sol.problem_ptr == population_problem, + "Solution problem must match the population problem"); +} + +template +void assert_solutions_compatible_for_similarity(const char* location, + const problem_t* population_problem, + const solution_t& lhs, + const char* lhs_role, + const solution_t& rhs, + const char* rhs_role) +{ + assert_solution_matches_population_problem(location, lhs_role, population_problem, lhs); + assert_solution_matches_population_problem(location, rhs_role, population_problem, rhs); + if (lhs.assignment.size() != rhs.assignment.size()) { + CUOPT_LOG_ERROR("%s: pair mismatch lhs_size=%zu rhs_size=%zu lhs_hash=0x%x rhs_hash=0x%x", + location, + lhs.assignment.size(), + rhs.assignment.size(), + lhs.get_hash(), + rhs.get_hash()); + } + cuopt_assert(lhs.assignment.size() == rhs.assignment.size(), + "Similarity check requires equal assignment sizes"); +} + template population_t::population_t(std::string const& name_, mip_solver_context_t& context_, @@ -49,8 +103,8 @@ population_t::population_t(std::string const& name_, population_hash_map(*problem_ptr), timer(context.gpu_heur_loop, 0) { - best_feasible_objective = std::numeric_limits::max(); - best_callback_feasible_objective = std::numeric_limits::max(); + best_feasible_objective = std::numeric_limits::max(); + context.solution_publication.reset_published_best(); } template @@ -263,124 +317,6 @@ bool population_t::is_better_than_best_feasible(solution_t& return obj_better && sol.get_feasible(); } -template -void population_t::invoke_get_solution_callback( - solution_t& sol, internals::get_solution_callback_t* callback) -{ - f_t user_objective = sol.get_user_objective(); - f_t user_bound = context.stats.get_solution_bound(); - solution_t temp_sol(sol); - problem_ptr->post_process_assignment(temp_sol.assignment); - if (context.settings.mip_scaling) { - rmm::device_uvector dummy(0, temp_sol.handle_ptr->get_stream()); - context.scaling.unscale_solutions(temp_sol.assignment, dummy); - } - if (problem_ptr->has_papilo_presolve_data()) { - problem_ptr->papilo_uncrush_assignment(temp_sol.assignment); - } - - std::vector user_objective_vec(1); - std::vector user_bound_vec(1); - std::vector user_assignment_vec(temp_sol.assignment.size()); - user_objective_vec[0] = user_objective; - user_bound_vec[0] = user_bound; - raft::copy(user_assignment_vec.data(), - temp_sol.assignment.data(), - temp_sol.assignment.size(), - temp_sol.handle_ptr->get_stream()); - temp_sol.handle_ptr->sync_stream(); - callback->get_solution(user_assignment_vec.data(), - user_objective_vec.data(), - user_bound_vec.data(), - callback->get_user_data()); -} - -template -void population_t::invoke_get_solution_callback_ext( - solution_t& sol, - internals::get_solution_callback_ext_t* callback, - const internals::mip_solution_callback_info_t& callback_info) -{ - f_t user_objective = sol.get_user_objective(); - f_t user_bound = context.stats.get_solution_bound(); - solution_t temp_sol(sol); - problem_ptr->post_process_assignment(temp_sol.assignment); - if (context.settings.mip_scaling) { - rmm::device_uvector dummy(0, temp_sol.handle_ptr->get_stream()); - context.scaling.unscale_solutions(temp_sol.assignment, dummy); - } - if (problem_ptr->has_papilo_presolve_data()) { - problem_ptr->papilo_uncrush_assignment(temp_sol.assignment); - } - - std::vector user_objective_vec(1); - std::vector user_bound_vec(1); - std::vector user_assignment_vec(temp_sol.assignment.size()); - user_objective_vec[0] = user_objective; - user_bound_vec[0] = user_bound; - raft::copy(user_assignment_vec.data(), - temp_sol.assignment.data(), - temp_sol.assignment.size(), - temp_sol.handle_ptr->get_stream()); - temp_sol.handle_ptr->sync_stream(); - callback->get_solution(user_assignment_vec.data(), - user_objective_vec.data(), - user_bound_vec.data(), - &callback_info, - callback->get_user_data()); -} - -template -void population_t::invoke_get_solution_callbacks( - solution_t& sol, - internals::mip_solution_origin_t callback_origin, - double work_timestamp) -{ - internals::mip_solution_callback_info_t callback_info{}; - auto user_callbacks = context.settings.get_mip_callbacks(); - if (work_timestamp < 0.0 && is_deterministic_mode(context.settings.determinism_mode)) { - work_timestamp = context.gpu_heur_loop.current_work(); - } - callback_info.origin = callback_origin; - callback_info.work_timestamp = work_timestamp; - CUOPT_LOG_DEBUG("Publishing incumbent: obj=%g wut=%.6f origin=%s callbacks=%zu", - sol.get_user_objective(), - work_timestamp, - internals::mip_solution_origin_to_string(callback_origin), - user_callbacks.size()); - for (auto callback : user_callbacks) { - if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { - auto get_sol_callback = static_cast(callback); - invoke_get_solution_callback(sol, get_sol_callback); - } else if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION_EXT) { - auto get_sol_callback_ext = static_cast(callback); - invoke_get_solution_callback_ext(sol, get_sol_callback_ext, callback_info); - } - } -} - -template -bool population_t::try_publish_new_best_feasible_to_get_callbacks( - solution_t& sol, - internals::mip_solution_origin_t callback_origin, - double work_timestamp) -{ - std::lock_guard lock(solution_callback_mutex); - if (!sol.get_feasible()) { return false; } - cuopt_assert(std::isfinite(sol.get_objective()), "Feasible incumbent objective must be finite"); - if (!(sol.get_objective() < best_callback_feasible_objective)) { return false; } - - if (context.settings.benchmark_info_ptr != nullptr) { - context.settings.benchmark_info_ptr->last_improvement_of_best_feasible = timer.elapsed_time(); - } - CUOPT_LOG_DEBUG("Population: Found new best solution %g", sol.get_user_objective()); - invoke_get_solution_callbacks(sol, callback_origin, work_timestamp); - - // Save the best objective here, because unscaling can make the callback-side solution infeasible. - best_callback_feasible_objective = sol.get_objective(); - return true; -} - template void population_t::run_solution_callbacks( solution_t& sol, internals::mip_solution_origin_t callback_origin) @@ -413,8 +349,8 @@ void population_t::run_solution_callbacks( if (problem_ptr->branch_and_bound_callback != nullptr) { problem_ptr->branch_and_bound_callback(sol.get_host_assignment()); } - const bool published = - try_publish_new_best_feasible_to_get_callbacks(sol, callback_origin, -1.0); + const bool published = context.solution_publication.publish_new_best_feasible( + sol, callback_origin, -1.0, timer.elapsed_time()); cuopt_assert(published, "New best feasible solution should publish to GET callbacks"); } } @@ -522,6 +458,8 @@ std::pair population_t::add_solution( // and we need those operations to complete before reading device data // for hash computation, quality calculation, and similarity comparisons. sol.handle_ptr->sync_stream(); + assert_solution_matches_population_problem( + "population.add_solution(candidate)", "candidate", problem_ptr, sol); population_hash_map.insert(sol); double sol_cost = sol.get_quality(weights); bool best_updated = false; @@ -816,6 +754,8 @@ template bool population_t::check_sols_similar(solution_t& sol1, solution_t& sol2) const { + assert_solutions_compatible_for_similarity( + "population.check_sols_similar", problem_ptr, sol1, "lhs", sol2, "rhs"); return sol1.calculate_similarity_radius(sol2) > var_threshold; } @@ -825,6 +765,15 @@ size_t population_t::best_similar_index(solution_t& sol) raft::common::nvtx::range fun_scope("best_similar_index"); if (indices.size() == 1) return max_solutions; for (size_t i = 1; i < indices.size(); i++) { + const size_t resident_idx = indices[i].first; + cuopt_assert(resident_idx < solutions.size(), "Population resident index out of bounds"); + cuopt_assert(solutions[resident_idx].first, "Population resident must be occupied"); + assert_solution_matches_population_problem( + "population.best_similar_index(candidate)", "candidate", problem_ptr, sol); + assert_solution_matches_population_problem("population.best_similar_index(resident)", + "resident", + problem_ptr, + solutions[resident_idx].second); if (check_sols_similar(sol, solutions[indices[i].first].second)) { return i; } } diff --git a/cpp/src/mip_heuristics/diversity/population.cuh b/cpp/src/mip_heuristics/diversity/population.cuh index 29653ac66c..817f568082 100644 --- a/cpp/src/mip_heuristics/diversity/population.cuh +++ b/cpp/src/mip_heuristics/diversity/population.cuh @@ -83,8 +83,8 @@ class population_t { a.first = false; indices[0].second = std::numeric_limits::max(); indices.erase(indices.begin() + 1, indices.end()); - best_feasible_objective = std::numeric_limits::max(); - best_callback_feasible_objective = std::numeric_limits::max(); + best_feasible_objective = std::numeric_limits::max(); + context.solution_publication.reset_published_best(); } void clear_except_best_feasible() @@ -94,8 +94,8 @@ class population_t { } solutions[indices[0].first].first = true; indices.erase(indices.begin() + 1, indices.end()); - best_feasible_objective = solutions[indices[0].first].second.get_objective(); - best_callback_feasible_objective = best_feasible_objective; + best_feasible_objective = solutions[indices[0].first].second.get_objective(); + context.solution_publication.reset_published_best(best_feasible_objective); } // ------------------- @@ -171,22 +171,6 @@ class population_t { void diversity_step(i_t max_iterations_without_improvement); - bool try_publish_new_best_feasible_to_get_callbacks( - solution_t& sol, - internals::mip_solution_origin_t callback_origin = internals::mip_solution_origin_t::UNKNOWN, - double work_timestamp = -1.0); - - void invoke_get_solution_callbacks( - solution_t& sol, - internals::mip_solution_origin_t callback_origin = internals::mip_solution_origin_t::UNKNOWN, - double work_timestamp = -1.0); - void invoke_get_solution_callback(solution_t& sol, - internals::get_solution_callback_t* callback); - void invoke_get_solution_callback_ext( - solution_t& sol, - internals::get_solution_callback_ext_t* callback, - const internals::mip_solution_callback_info_t& callback_info); - // does some consistency tests bool test_invariant(); @@ -228,11 +212,9 @@ class population_t { i_t update_iter = 0; std::recursive_mutex write_mutex; std::mutex solution_mutex; - std::mutex solution_callback_mutex; std::atomic early_exit_primal_generation = false; std::atomic solutions_in_external_queue_ = false; f_t best_feasible_objective = std::numeric_limits::max(); - f_t best_callback_feasible_objective = std::numeric_limits::max(); assignment_hash_map_t population_hash_map; cuopt::work_limit_timer_t timer; }; diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu index e642285b51..f6c96b012a 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu @@ -1061,17 +1061,14 @@ std::map fj_t::get_feature_vector(i_t climber_idx) template i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) { - auto& data = *climbers[climber_idx]; - auto v = data.view(); // == climber_views[climber_idx] - const bool deterministic_bnb_integration = - is_deterministic_mode(context.settings.determinism_mode) && - context.branch_and_bound_ptr != nullptr; + auto& data = *climbers[climber_idx]; + auto v = data.view(); // == climber_views[climber_idx] const double work_units_at_start = context.gpu_heur_loop.current_work(); - const bool publish_progress = deterministic_bnb_integration && + const bool publish_progress = is_deterministic_mode(context.settings.determinism_mode) && + context.branch_and_bound_ptr != nullptr && std::isfinite(settings.work_limit) && settings.work_limit > 0.0 && settings.iteration_limit > 0 && settings.iteration_limit != std::numeric_limits::max(); - f_t last_published_objective = std::numeric_limits::infinity(); auto climber_stream = data.stream.view(); if (climber_idx == 0) climber_stream = handle_ptr->get_stream(); @@ -1168,21 +1165,6 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) bool is_feasible = solution.compute_feasibility(); solution.handle_ptr->sync_stream(); - if (deterministic_bnb_integration && is_feasible && - solution.get_objective() < last_published_objective) { - cuopt_assert(context.diversity_manager_ptr != nullptr, - "Deterministic FJ publication requires diversity manager context"); - auto* population_ptr = context.diversity_manager_ptr->get_population_pointer(); - cuopt_assert(population_ptr != nullptr, - "Deterministic FJ publication requires a population"); - population_ptr->add_solution(solution_t(solution), - internals::mip_solution_origin_t::FEASIBILITY_JUMP); - last_published_objective = solution.get_objective(); - CUOPT_DETERMINISM_LOG_INFO("FJ deterministic publish via population: obj=%f hash=0x%x", - solution.get_user_objective(), - solution.get_hash()); - } - if (limit_reached) { break; } if (is_feasible) { diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 86d9c18178..0391bf325a 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -92,8 +92,8 @@ struct branch_and_bound_solution_helper_t { incumbent.compute_feasibility(); cuopt_assert(incumbent.get_feasible(), "Deterministic B&B callback must provide a feasible incumbent"); - dm->population.try_publish_new_best_feasible_to_get_callbacks( - incumbent, callback_info.origin, work_timestamp); + dm->context.solution_publication.publish_new_best_feasible( + incumbent, callback_info.origin, work_timestamp, dm->timer.elapsed_time()); } void set_simplex_solution(std::vector& solution, @@ -128,20 +128,25 @@ solution_t mip_solver_t::run_solver() CUOPT_LOG_INFO("Problem fully reduced in presolve"); solution_t sol(*context.problem_ptr); sol.set_problem_fully_reduced(); - dm.population.invoke_get_solution_callbacks(sol); + context.solution_publication.invoke_get_solution_callbacks( + sol, internals::mip_solution_origin_t::UNKNOWN, -1.0); context.problem_ptr->post_process_solution(sol); return sol; } - dm.timer = work_limit_timer_t(context.gpu_heur_loop, timer_.get_time_limit()); - const bool run_presolve = context.settings.presolver != presolver_t::None; - f_t time_limit = is_deterministic_mode(context.settings.determinism_mode) - ? std::numeric_limits::infinity() - : timer_.remaining_time(); + const bool deterministic_run = is_deterministic_mode(context.settings.determinism_mode); + const f_t gpu_heur_work_limit = + deterministic_run ? context.settings.work_limit : timer_.get_time_limit(); + if (deterministic_run) + cuopt_assert(gpu_heur_work_limit >= 0.0, + "Deterministic GPU heuristic work limit must be non-negative"); + dm.timer = work_limit_timer_t(context.gpu_heur_loop, gpu_heur_work_limit); + const bool run_presolve = context.settings.presolver != presolver_t::None; + f_t time_limit = + deterministic_run ? std::numeric_limits::infinity() : timer_.remaining_time(); double presolve_time_limit = std::min(0.1 * time_limit, 60.0); - presolve_time_limit = is_deterministic_mode(context.settings.determinism_mode) - ? std::numeric_limits::infinity() - : presolve_time_limit; - bool presolve_success = run_presolve ? dm.run_presolve(presolve_time_limit, timer_) : true; + presolve_time_limit = + deterministic_run ? std::numeric_limits::infinity() : presolve_time_limit; + bool presolve_success = run_presolve ? dm.run_presolve(presolve_time_limit, timer_) : true; if (!presolve_success) { CUOPT_LOG_INFO("Problem proven infeasible in presolve"); solution_t sol(*context.problem_ptr); @@ -153,7 +158,8 @@ solution_t mip_solver_t::run_solver() CUOPT_LOG_INFO("Problem full reduced in presolve"); solution_t sol(*context.problem_ptr); sol.set_problem_fully_reduced(); - dm.population.invoke_get_solution_callbacks(sol); + context.solution_publication.invoke_get_solution_callbacks( + sol, internals::mip_solution_origin_t::UNKNOWN, -1.0); context.problem_ptr->post_process_solution(sol); return sol; } @@ -186,7 +192,8 @@ solution_t mip_solver_t::run_solver() sol.set_problem_fully_reduced(); } if (opt_sol.get_termination_status() == pdlp_termination_status_t::Optimal) { - dm.population.invoke_get_solution_callbacks(sol); + context.solution_publication.invoke_get_solution_callbacks( + sol, internals::mip_solution_origin_t::UNKNOWN, -1.0); } context.problem_ptr->post_process_solution(sol); return sol; diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index 530da15243..cea620297f 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -10,11 +10,14 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include @@ -40,6 +43,114 @@ struct mip_solver_work_unit_predictors_t { template class diversity_manager_t; +template +class solution_publication_t { + public: + solution_publication_t(problem_t* problem_ptr_, + const mip_solver_settings_t& settings_, + pdlp_initial_scaling_strategy_t& scaling_, + solver_stats_t& stats_, + work_limit_context_t& gpu_heur_loop_) + : problem_ptr(problem_ptr_), + settings(settings_), + scaling(scaling_), + stats(stats_), + gpu_heur_loop(gpu_heur_loop_) + { + cuopt_assert(problem_ptr != nullptr, "Publication problem pointer must not be null"); + } + + void reset_published_best(f_t objective = std::numeric_limits::max()) + { + best_callback_feasible_objective_ = objective; + } + + void invoke_get_solution_callbacks(solution_t& sol, + internals::mip_solution_origin_t callback_origin, + double work_timestamp) + { + internals::mip_solution_callback_info_t callback_info{}; + auto user_callbacks = settings.get_mip_callbacks(); + if (work_timestamp < 0.0 && is_deterministic_mode(settings.determinism_mode)) { + work_timestamp = gpu_heur_loop.current_work(); + } + callback_info.origin = callback_origin; + callback_info.work_timestamp = work_timestamp; + CUOPT_LOG_DEBUG("Publishing incumbent: obj=%g wut=%.6f origin=%s callbacks=%zu", + sol.get_user_objective(), + work_timestamp, + internals::mip_solution_origin_to_string(callback_origin), + user_callbacks.size()); + + f_t user_objective = sol.get_user_objective(); + f_t user_bound = stats.get_solution_bound(); + solution_t temp_sol(sol); + problem_ptr->post_process_assignment(temp_sol.assignment); + if (settings.mip_scaling) { + rmm::device_uvector dummy(0, temp_sol.handle_ptr->get_stream()); + scaling.unscale_solutions(temp_sol.assignment, dummy); + } + if (problem_ptr->has_papilo_presolve_data()) { + problem_ptr->papilo_uncrush_assignment(temp_sol.assignment); + } + + std::vector user_objective_vec(1); + std::vector user_bound_vec(1); + std::vector user_assignment_vec(temp_sol.assignment.size()); + user_objective_vec[0] = user_objective; + user_bound_vec[0] = user_bound; + raft::copy(user_assignment_vec.data(), + temp_sol.assignment.data(), + temp_sol.assignment.size(), + temp_sol.handle_ptr->get_stream()); + temp_sol.handle_ptr->sync_stream(); + + for (auto callback : user_callbacks) { + if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION_EXT) { + auto get_sol_callback_ext = static_cast(callback); + get_sol_callback_ext->get_solution(user_assignment_vec.data(), + user_objective_vec.data(), + user_bound_vec.data(), + &callback_info, + get_sol_callback_ext->get_user_data()); + } else if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { + auto get_sol_callback = static_cast(callback); + get_sol_callback->get_solution(user_assignment_vec.data(), + user_objective_vec.data(), + user_bound_vec.data(), + get_sol_callback->get_user_data()); + } + } + } + + bool publish_new_best_feasible(solution_t& sol, + internals::mip_solution_origin_t callback_origin, + double work_timestamp, + double elapsed_time = -1.0) + { + std::lock_guard lock(solution_callback_mutex_); + if (!sol.get_feasible()) { return false; } + cuopt_assert(std::isfinite(sol.get_objective()), "Feasible incumbent objective must be finite"); + if (!(sol.get_objective() < best_callback_feasible_objective_)) { return false; } + + if (settings.benchmark_info_ptr != nullptr && elapsed_time >= 0.0) { + settings.benchmark_info_ptr->last_improvement_of_best_feasible = elapsed_time; + } + invoke_get_solution_callbacks(sol, callback_origin, work_timestamp); + best_callback_feasible_objective_ = sol.get_objective(); + return true; + } + + private: + problem_t* problem_ptr; + const mip_solver_settings_t& settings; + pdlp_initial_scaling_strategy_t& scaling; + solver_stats_t& stats; + work_limit_context_t& gpu_heur_loop; + std::mutex solution_callback_mutex_; + f_t best_callback_feasible_objective_{std::numeric_limits::max()}; +}; + // Aggregate structure containing the global context of the solving process for convenience: // The current problem, user settings, raft handle and statistics objects template @@ -48,7 +159,11 @@ struct mip_solver_context_t { problem_t* problem_ptr_, mip_solver_settings_t settings_, pdlp_initial_scaling_strategy_t& scaling) - : handle_ptr(handle_ptr_), problem_ptr(problem_ptr_), settings(settings_), scaling(scaling) + : handle_ptr(handle_ptr_), + problem_ptr(problem_ptr_), + settings(settings_), + scaling(scaling), + solution_publication(problem_ptr_, settings, scaling, stats, gpu_heur_loop) { cuopt_assert(problem_ptr != nullptr, "problem_ptr is nullptr"); stats.set_solution_bound(problem_ptr->maximize ? std::numeric_limits::infinity() @@ -78,6 +193,7 @@ struct mip_solver_context_t { // Work limit context for tracking work units in deterministic mode (shared across all timers in // GPU heuristic loop) work_limit_context_t gpu_heur_loop{"GPUHeur"}; + solution_publication_t solution_publication; // synchronization every 5 seconds for deterministic mode work_unit_scheduler_t work_unit_scheduler_{5.0}; From 2b0e5e4e0abb1a406bbc439f5dbac16a64d8c4b4 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 8 Mar 2026 05:42:15 -0700 Subject: [PATCH 142/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 28ab0e22bb..c639540178 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -211,7 +211,7 @@ int run_single_file(std::string file_path, settings.determinism_mode = CUOPT_MODE_OPPORTUNISTIC; } CUOPT_LOG_INFO( - "run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " + "1run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " "time_limit=%.6f work_limit=%.6f", (int)heuristics_only, (int)deterministic, From 21bf0a57e816d03e4296524314366a9dc5a5481b Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 8 Mar 2026 05:42:35 -0700 Subject: [PATCH 143/225] bump2 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index c639540178..28ab0e22bb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -211,7 +211,7 @@ int run_single_file(std::string file_path, settings.determinism_mode = CUOPT_MODE_OPPORTUNISTIC; } CUOPT_LOG_INFO( - "1run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " + "run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " "time_limit=%.6f work_limit=%.6f", (int)heuristics_only, (int)deterministic, From 75c942a5c9d7b586426247b7fe47835129b2f03c Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 8 Mar 2026 07:56:35 -0700 Subject: [PATCH 144/225] refactor --- .../mip_heuristics/diversity/population.cu | 12 +- cpp/src/mip_heuristics/solver.cu | 61 +++++--- cpp/src/mip_heuristics/solver_context.cuh | 140 ++++++++++-------- 3 files changed, 135 insertions(+), 78 deletions(-) diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index b3831de21e..d463c5da65 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -349,8 +349,16 @@ void population_t::run_solution_callbacks( if (problem_ptr->branch_and_bound_callback != nullptr) { problem_ptr->branch_and_bound_callback(sol.get_host_assignment()); } - const bool published = context.solution_publication.publish_new_best_feasible( - sol, callback_origin, -1.0, timer.elapsed_time()); + const auto payload = + make_solution_callback_payload_from_solution(problem_ptr, + context.settings, + context.scaling, + context.gpu_heur_loop, + sol, + callback_origin, + -1.0); + const bool published = + context.solution_publication.publish_new_best_feasible(payload, timer.elapsed_time()); cuopt_assert(published, "New best feasible solution should publish to GET callbacks"); } } diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 0391bf325a..69a82afbbd 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -86,14 +86,16 @@ struct branch_and_bound_solution_helper_t { cuopt_assert(dm->context.problem_ptr != nullptr, "Problem pointer must be valid"); cuopt_assert(solution.size() == (size_t)dm->context.problem_ptr->n_variables, "Deterministic B&B callback solution size mismatch"); - - solution_t incumbent(*dm->context.problem_ptr); - incumbent.copy_new_assignment(solution); - incumbent.compute_feasibility(); - cuopt_assert(incumbent.get_feasible(), - "Deterministic B&B callback must provide a feasible incumbent"); - dm->context.solution_publication.publish_new_best_feasible( - incumbent, callback_info.origin, work_timestamp, dm->timer.elapsed_time()); + cuopt_assert(std::isfinite(objective), "Deterministic B&B callback objective must be finite"); + const auto payload = + make_solution_callback_payload_from_host_solution(dm->context.problem_ptr, + dm->context.settings, + dm->context.gpu_heur_loop, + solution, + objective, + callback_info.origin, + work_timestamp); + dm->context.solution_publication.publish_new_best_feasible(payload, dm->timer.elapsed_time()); } void set_simplex_solution(std::vector& solution, @@ -128,8 +130,15 @@ solution_t mip_solver_t::run_solver() CUOPT_LOG_INFO("Problem fully reduced in presolve"); solution_t sol(*context.problem_ptr); sol.set_problem_fully_reduced(); - context.solution_publication.invoke_get_solution_callbacks( - sol, internals::mip_solution_origin_t::UNKNOWN, -1.0); + const auto payload = make_solution_callback_payload_from_solution( + context.problem_ptr, + context.settings, + context.scaling, + context.gpu_heur_loop, + sol, + internals::mip_solution_origin_t::UNKNOWN, + -1.0); + context.solution_publication.invoke_get_solution_callbacks(payload); context.problem_ptr->post_process_solution(sol); return sol; } @@ -158,8 +167,15 @@ solution_t mip_solver_t::run_solver() CUOPT_LOG_INFO("Problem full reduced in presolve"); solution_t sol(*context.problem_ptr); sol.set_problem_fully_reduced(); - context.solution_publication.invoke_get_solution_callbacks( - sol, internals::mip_solution_origin_t::UNKNOWN, -1.0); + const auto payload = make_solution_callback_payload_from_solution( + context.problem_ptr, + context.settings, + context.scaling, + context.gpu_heur_loop, + sol, + internals::mip_solution_origin_t::UNKNOWN, + -1.0); + context.solution_publication.invoke_get_solution_callbacks(payload); context.problem_ptr->post_process_solution(sol); return sol; } @@ -192,8 +208,15 @@ solution_t mip_solver_t::run_solver() sol.set_problem_fully_reduced(); } if (opt_sol.get_termination_status() == pdlp_termination_status_t::Optimal) { - context.solution_publication.invoke_get_solution_callbacks( - sol, internals::mip_solution_origin_t::UNKNOWN, -1.0); + const auto payload = make_solution_callback_payload_from_solution( + context.problem_ptr, + context.settings, + context.scaling, + context.gpu_heur_loop, + sol, + internals::mip_solution_origin_t::UNKNOWN, + -1.0); + context.solution_publication.invoke_get_solution_callbacks(payload); } context.problem_ptr->post_process_solution(sol); return sol; @@ -342,10 +365,12 @@ solution_t mip_solver_t::run_solver() // std::async and std::future allow us to get the return value of bb::solve() // without having to manually manage the thread // std::future.get() performs a join() operation to wait until the return status is available - branch_and_bound_status_future = std::async(std::launch::async, - &dual_simplex::branch_and_bound_t::solve, - branch_and_bound.get(), - std::ref(branch_and_bound_solution)); + int bb_device_id = context.handle_ptr->get_device(); + branch_and_bound_status_future = + std::async(std::launch::async, [&branch_and_bound, &branch_and_bound_solution, bb_device_id] { + RAFT_CUDA_TRY(cudaSetDevice(bb_device_id)); + return branch_and_bound->solve(branch_and_bound_solution); + }); } // Start the primal heuristics diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index cea620297f..27edfcc05c 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -43,21 +43,76 @@ struct mip_solver_work_unit_predictors_t { template class diversity_manager_t; +template +struct solution_callback_payload_t { + std::vector assignment{}; + f_t user_objective{}; + f_t solver_objective{}; + internals::mip_solution_callback_info_t callback_info{}; +}; + +template +solution_callback_payload_t make_solution_callback_payload_from_solution( + problem_t* problem_ptr, + const mip_solver_settings_t& settings, + pdlp_initial_scaling_strategy_t& scaling, + work_limit_context_t& gpu_heur_loop, + solution_t& sol, + internals::mip_solution_origin_t callback_origin, + double work_timestamp) +{ + cuopt_assert(problem_ptr != nullptr, "Callback payload problem pointer must not be null"); + if (work_timestamp < 0.0 && is_deterministic_mode(settings.determinism_mode)) { + work_timestamp = gpu_heur_loop.current_work(); + } + solution_callback_payload_t payload{}; + payload.user_objective = sol.get_user_objective(); + payload.solver_objective = sol.get_objective(); + payload.callback_info.origin = callback_origin; + payload.callback_info.work_timestamp = work_timestamp; + solution_t temp_sol(sol); + problem_ptr->post_process_assignment(temp_sol.assignment); + if (settings.mip_scaling) { + rmm::device_uvector dummy(0, temp_sol.handle_ptr->get_stream()); + scaling.unscale_solutions(temp_sol.assignment, dummy); + } + if (problem_ptr->has_papilo_presolve_data()) { + problem_ptr->papilo_uncrush_assignment(temp_sol.assignment); + } + payload.assignment = temp_sol.get_host_assignment(); + return payload; +} + +template +solution_callback_payload_t make_solution_callback_payload_from_host_solution( + problem_t* problem_ptr, + const mip_solver_settings_t& settings, + work_limit_context_t& gpu_heur_loop, + const std::vector& assignment, + f_t solver_objective, + internals::mip_solution_origin_t callback_origin, + double work_timestamp) +{ + cuopt_assert(problem_ptr != nullptr, "Callback payload problem pointer must not be null"); + if (work_timestamp < 0.0 && is_deterministic_mode(settings.determinism_mode)) { + work_timestamp = gpu_heur_loop.current_work(); + } + solution_callback_payload_t payload{}; + payload.assignment = assignment; + payload.user_objective = problem_ptr->get_user_obj_from_solver_obj(solver_objective); + payload.solver_objective = solver_objective; + payload.callback_info.origin = callback_origin; + payload.callback_info.work_timestamp = work_timestamp; + return payload; +} + template class solution_publication_t { public: - solution_publication_t(problem_t* problem_ptr_, - const mip_solver_settings_t& settings_, - pdlp_initial_scaling_strategy_t& scaling_, - solver_stats_t& stats_, - work_limit_context_t& gpu_heur_loop_) - : problem_ptr(problem_ptr_), - settings(settings_), - scaling(scaling_), - stats(stats_), - gpu_heur_loop(gpu_heur_loop_) + solution_publication_t(const mip_solver_settings_t& settings_, + solver_stats_t& stats_) + : settings(settings_), stats(stats_) { - cuopt_assert(problem_ptr != nullptr, "Publication problem pointer must not be null"); } void reset_published_best(f_t objective = std::numeric_limits::max()) @@ -65,57 +120,31 @@ class solution_publication_t { best_callback_feasible_objective_ = objective; } - void invoke_get_solution_callbacks(solution_t& sol, - internals::mip_solution_origin_t callback_origin, - double work_timestamp) + void invoke_get_solution_callbacks(const solution_callback_payload_t& payload) { - internals::mip_solution_callback_info_t callback_info{}; auto user_callbacks = settings.get_mip_callbacks(); - if (work_timestamp < 0.0 && is_deterministic_mode(settings.determinism_mode)) { - work_timestamp = gpu_heur_loop.current_work(); - } - callback_info.origin = callback_origin; - callback_info.work_timestamp = work_timestamp; CUOPT_LOG_DEBUG("Publishing incumbent: obj=%g wut=%.6f origin=%s callbacks=%zu", - sol.get_user_objective(), - work_timestamp, - internals::mip_solution_origin_to_string(callback_origin), + payload.user_objective, + payload.callback_info.work_timestamp, + internals::mip_solution_origin_to_string(payload.callback_info.origin), user_callbacks.size()); - f_t user_objective = sol.get_user_objective(); - f_t user_bound = stats.get_solution_bound(); - solution_t temp_sol(sol); - problem_ptr->post_process_assignment(temp_sol.assignment); - if (settings.mip_scaling) { - rmm::device_uvector dummy(0, temp_sol.handle_ptr->get_stream()); - scaling.unscale_solutions(temp_sol.assignment, dummy); - } - if (problem_ptr->has_papilo_presolve_data()) { - problem_ptr->papilo_uncrush_assignment(temp_sol.assignment); - } - std::vector user_objective_vec(1); std::vector user_bound_vec(1); - std::vector user_assignment_vec(temp_sol.assignment.size()); - user_objective_vec[0] = user_objective; - user_bound_vec[0] = user_bound; - raft::copy(user_assignment_vec.data(), - temp_sol.assignment.data(), - temp_sol.assignment.size(), - temp_sol.handle_ptr->get_stream()); - temp_sol.handle_ptr->sync_stream(); + user_objective_vec[0] = payload.user_objective; + user_bound_vec[0] = stats.get_solution_bound(); for (auto callback : user_callbacks) { if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION_EXT) { auto get_sol_callback_ext = static_cast(callback); - get_sol_callback_ext->get_solution(user_assignment_vec.data(), + get_sol_callback_ext->get_solution(const_cast(payload.assignment.data()), user_objective_vec.data(), user_bound_vec.data(), - &callback_info, + &payload.callback_info, get_sol_callback_ext->get_user_data()); } else if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { auto get_sol_callback = static_cast(callback); - get_sol_callback->get_solution(user_assignment_vec.data(), + get_sol_callback->get_solution(const_cast(payload.assignment.data()), user_objective_vec.data(), user_bound_vec.data(), get_sol_callback->get_user_data()); @@ -123,30 +152,25 @@ class solution_publication_t { } } - bool publish_new_best_feasible(solution_t& sol, - internals::mip_solution_origin_t callback_origin, - double work_timestamp, + bool publish_new_best_feasible(const solution_callback_payload_t& payload, double elapsed_time = -1.0) { std::lock_guard lock(solution_callback_mutex_); - if (!sol.get_feasible()) { return false; } - cuopt_assert(std::isfinite(sol.get_objective()), "Feasible incumbent objective must be finite"); - if (!(sol.get_objective() < best_callback_feasible_objective_)) { return false; } + cuopt_assert(std::isfinite(payload.solver_objective), + "Feasible incumbent objective must be finite"); + if (!(payload.solver_objective < best_callback_feasible_objective_)) { return false; } if (settings.benchmark_info_ptr != nullptr && elapsed_time >= 0.0) { settings.benchmark_info_ptr->last_improvement_of_best_feasible = elapsed_time; } - invoke_get_solution_callbacks(sol, callback_origin, work_timestamp); - best_callback_feasible_objective_ = sol.get_objective(); + invoke_get_solution_callbacks(payload); + best_callback_feasible_objective_ = payload.solver_objective; return true; } private: - problem_t* problem_ptr; const mip_solver_settings_t& settings; - pdlp_initial_scaling_strategy_t& scaling; solver_stats_t& stats; - work_limit_context_t& gpu_heur_loop; std::mutex solution_callback_mutex_; f_t best_callback_feasible_objective_{std::numeric_limits::max()}; }; @@ -163,7 +187,7 @@ struct mip_solver_context_t { problem_ptr(problem_ptr_), settings(settings_), scaling(scaling), - solution_publication(problem_ptr_, settings, scaling, stats, gpu_heur_loop) + solution_publication(settings, stats) { cuopt_assert(problem_ptr != nullptr, "problem_ptr is nullptr"); stats.set_solution_bound(problem_ptr->maximize ? std::numeric_limits::infinity() From 9db457b6aaeba66f9a9372ee82814a9984523532 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 8 Mar 2026 07:56:57 -0700 Subject: [PATCH 145/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 28ab0e22bb..c639540178 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -211,7 +211,7 @@ int run_single_file(std::string file_path, settings.determinism_mode = CUOPT_MODE_OPPORTUNISTIC; } CUOPT_LOG_INFO( - "run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " + "1run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " "time_limit=%.6f work_limit=%.6f", (int)heuristics_only, (int)deterministic, From c3e7ff61a22bcfc430f75f9bda0ad12a987de2fb Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 8 Mar 2026 07:57:20 -0700 Subject: [PATCH 146/225] bump2 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index c639540178..28ab0e22bb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -211,7 +211,7 @@ int run_single_file(std::string file_path, settings.determinism_mode = CUOPT_MODE_OPPORTUNISTIC; } CUOPT_LOG_INFO( - "1run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " + "run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " "time_limit=%.6f work_limit=%.6f", (int)heuristics_only, (int)deterministic, From 3ed12cfc1a87b6a4b9821725cf6452ff19ead317 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 8 Mar 2026 11:01:22 -0700 Subject: [PATCH 147/225] remove sigILL --- cpp/src/utilities/timer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/utilities/timer.hpp b/cpp/src/utilities/timer.hpp index 7ee849c727..6bf8795fca 100644 --- a/cpp/src/utilities/timer.hpp +++ b/cpp/src/utilities/timer.hpp @@ -46,7 +46,7 @@ class timer_t { line, caller); // assert(false && "unexpected timer"); - __builtin_trap(); + //__builtin_trap(); } return elapsed; } From 1e5807b35855d18dcdce427716358ddde32a0934 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sun, 8 Mar 2026 11:01:39 -0700 Subject: [PATCH 148/225] bump --- cpp/src/utilities/timer.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/src/utilities/timer.hpp b/cpp/src/utilities/timer.hpp index 6bf8795fca..41153cbfb6 100644 --- a/cpp/src/utilities/timer.hpp +++ b/cpp/src/utilities/timer.hpp @@ -45,7 +45,6 @@ class timer_t { file, line, caller); - // assert(false && "unexpected timer"); //__builtin_trap(); } return elapsed; From 033f1ce1cf1fd382f73022707b82d110be2b707e Mon Sep 17 00:00:00 2001 From: anandhkb Date: Sun, 8 Mar 2026 17:54:35 -0700 Subject: [PATCH 149/225] Add MIP gap to heuristic log during dual-simplex root relaxation - Add root_lp_progress_callback to simplex_solver_settings (optional) - Invoke callback in dual_phase2 with current dual objective when inside_mip=1 - B&B: store root LP lower bound via callback, use in report_heuristic - Append 'Gap: X%' to log line when heuristic finds solution during root solve Addresses issue when solver times out during root relaxation: gap can now be computed from last dual feasible solution and heuristic incumbent. --- cpp/src/branch_and_bound/branch_and_bound.cpp | 25 +++++++++++++++---- cpp/src/branch_and_bound/branch_and_bound.hpp | 1 + cpp/src/dual_simplex/phase2.cpp | 3 +++ .../dual_simplex/simplex_solver_settings.hpp | 3 +++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 6ce9a4f4d0..f09158e7f2 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -282,8 +282,9 @@ branch_and_bound_t::branch_and_bound_t( } #endif - upper_bound_ = inf; - root_objective_ = std::numeric_limits::quiet_NaN(); + upper_bound_ = inf; + root_lp_current_lower_bound_ = -inf; + root_objective_ = std::numeric_limits::quiet_NaN(); } template @@ -318,9 +319,19 @@ void branch_and_bound_t::report_heuristic(f_t obj) user_gap.c_str(), toc(exploration_stats_.start_time)); } else { - settings_.log.printf("New solution from primal heuristics. Objective %+.6e. Time %.2f\n", - compute_user_objective(original_lp_, obj), - toc(exploration_stats_.start_time)); + f_t user_obj = compute_user_objective(original_lp_, obj); + f_t user_lower = get_lower_bound(); + if (!std::isfinite(user_lower)) { + f_t root_lower = root_lp_current_lower_bound_.load(); + if (std::isfinite(root_lower)) { user_lower = root_lower; } + } + std::string gap_str = std::isfinite(user_lower) + ? (". Gap: " + user_mip_gap(user_obj, user_lower) + "\n") + : "\n"; + settings_.log.printf("New solution from primal heuristics. Objective %+.6e. Time %.2f%s", + user_obj, + toc(exploration_stats_.start_time), + gap_str.c_str()); } } @@ -1943,6 +1954,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut log.log_prefix = settings_.log.log_prefix; solver_status_ = mip_status_t::UNSET; is_running_ = false; + root_lp_current_lower_bound_ = -inf; exploration_stats_.nodes_unexplored = 0; exploration_stats_.nodes_explored = 0; original_lp_.A.to_compressed_row(Arow_); @@ -1972,6 +1984,9 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut lp_settings.inside_mip = 1; lp_settings.scale_columns = false; lp_settings.concurrent_halt = get_root_concurrent_halt(); + lp_settings.root_lp_progress_callback = [this](f_t user_obj) { + root_lp_current_lower_bound_.store(user_obj); + }; std::vector basic_list(original_lp_.num_rows); std::vector nonbasic_list; basis_update_mpf_t basis_update(original_lp_.num_rows, settings_.refactor_frequency); diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index a13d5cedcf..ec73792502 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -190,6 +190,7 @@ class branch_and_bound_t { lp_solution_t root_crossover_soln_; std::vector edge_norms_; std::atomic root_crossover_solution_set_{false}; + omp_atomic_t root_lp_current_lower_bound_; bool enable_concurrent_lp_root_solve_{false}; std::atomic root_concurrent_halt_{0}; bool is_root_solution_set{false}; diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 426d9a7535..8c575f884f 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -3520,6 +3520,9 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, primal_infeasibility_squared, sum_perturb, now); + if (phase == 2 && settings.inside_mip == 1 && settings.root_lp_progress_callback) { + settings.root_lp_progress_callback(compute_user_objective(lp, obj)); + } } if (obj >= settings.cut_off) { diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 815e229232..bdf392d813 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -110,6 +110,7 @@ struct simplex_solver_settings_t { sub_mip(0), solution_callback(nullptr), heuristic_preemption_callback(nullptr), + root_lp_progress_callback(nullptr), concurrent_halt(nullptr) { } @@ -202,6 +203,8 @@ struct simplex_solver_settings_t { std::function&, f_t)> node_processed_callback; std::function heuristic_preemption_callback; std::function&, std::vector&, f_t)> set_simplex_solution_callback; + std::function + root_lp_progress_callback; // Called with current dual obj during root LP mutable logger_t log; std::atomic* concurrent_halt; // if nullptr ignored, if !nullptr, 0 if solver should // continue, 1 if solver should halt From a7e4ad3f5925776877e09a03bd551e0a672101b7 Mon Sep 17 00:00:00 2001 From: anandhkb Date: Sun, 8 Mar 2026 18:08:45 -0700 Subject: [PATCH 150/225] Address CodeRabbit review: user-space conversion, root_objective_ reset, callback every iteration - Convert get_lower_bound() to user space before user_mip_gap in report_heuristic - Reset root_objective_ at solve start to avoid stale bound on B&B reuse - Invoke root_lp_progress_callback every iteration, not just at log points --- cpp/src/branch_and_bound/branch_and_bound.cpp | 11 +++++------ cpp/src/dual_simplex/phase2.cpp | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index f09158e7f2..bd4b3e2e38 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -319,12 +319,10 @@ void branch_and_bound_t::report_heuristic(f_t obj) user_gap.c_str(), toc(exploration_stats_.start_time)); } else { - f_t user_obj = compute_user_objective(original_lp_, obj); - f_t user_lower = get_lower_bound(); - if (!std::isfinite(user_lower)) { - f_t root_lower = root_lp_current_lower_bound_.load(); - if (std::isfinite(root_lower)) { user_lower = root_lower; } - } + f_t user_obj = compute_user_objective(original_lp_, obj); + f_t lower = get_lower_bound(); + f_t user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) + : root_lp_current_lower_bound_.load(); std::string gap_str = std::isfinite(user_lower) ? (". Gap: " + user_mip_gap(user_obj, user_lower) + "\n") : "\n"; @@ -1955,6 +1953,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut solver_status_ = mip_status_t::UNSET; is_running_ = false; root_lp_current_lower_bound_ = -inf; + root_objective_ = std::numeric_limits::quiet_NaN(); exploration_stats_.nodes_unexplored = 0; exploration_stats_.nodes_explored = 0; original_lp_.A.to_compressed_row(Arow_); diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 8c575f884f..07fc944333 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -3520,9 +3520,9 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, primal_infeasibility_squared, sum_perturb, now); - if (phase == 2 && settings.inside_mip == 1 && settings.root_lp_progress_callback) { - settings.root_lp_progress_callback(compute_user_objective(lp, obj)); - } + } + if (phase == 2 && settings.inside_mip == 1 && settings.root_lp_progress_callback) { + settings.root_lp_progress_callback(compute_user_objective(lp, obj)); } if (obj >= settings.cut_off) { From 7de698551765cb2757cf525f4f8866f5d0148e69 Mon Sep 17 00:00:00 2001 From: anandhkb Date: Sun, 8 Mar 2026 19:14:42 -0700 Subject: [PATCH 151/225] Use root_lp_progress_callback bound during root cut passes (CodeRabbit comment 4) When nodes_explored==0, prefer root_lp_current_lower_bound_ over get_lower_bound() when it provides a tighter bound. Fixes stale gap for heuristics during cut-pass solve. --- cpp/src/branch_and_bound/branch_and_bound.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index bd4b3e2e38..70baa3e0a0 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -308,8 +308,16 @@ template void branch_and_bound_t::report_heuristic(f_t obj) { if (is_running_) { - f_t user_obj = compute_user_objective(original_lp_, obj); - f_t user_lower = compute_user_objective(original_lp_, get_lower_bound()); + f_t user_obj = compute_user_objective(original_lp_, obj); + f_t lower = get_lower_bound(); + f_t user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) : -inf; + if (exploration_stats_.nodes_explored == 0) { + f_t root_progress = root_lp_current_lower_bound_.load(); + if (std::isfinite(root_progress) && + (!std::isfinite(user_lower) || root_progress > user_lower)) { + user_lower = root_progress; + } + } std::string user_gap = user_mip_gap(user_obj, user_lower); settings_.log.printf( From 2322ad18d680b794be30f23be8272e95cca476a2 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 9 Mar 2026 04:18:03 -0700 Subject: [PATCH 152/225] gpu scale tuning --- .../linear_programming/cuopt/run_mip.cpp | 1 + cpp/src/branch_and_bound/branch_and_bound.cpp | 9 ++--- .../diversity/diversity_manager.cu | 14 ++++---- .../diversity/diversity_manager.cuh | 5 +-- cpp/src/mip_heuristics/diversity/lns/rins.cu | 5 +-- .../mip_heuristics/diversity/population.cu | 27 ++++++++------- .../mip_heuristics/diversity/population.cuh | 34 +++++++++---------- .../local_search/local_search.cu | 6 ++-- cpp/src/mip_heuristics/solver.cu | 4 +-- 9 files changed, 58 insertions(+), 47 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 28ab0e22bb..11f9cda679 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -223,6 +223,7 @@ int run_single_file(std::string file_path, settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; settings.seed = 42; + settings.gpu_heur_work_unit_scale = 0.1; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 7b2af9d6a5..62e16f995b 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -3077,8 +3077,8 @@ void branch_and_bound_t::run_deterministic_coordinator(const csr_matri deterministic_horizon_step_ = 0.50; - // Compute worker counts using the same formula as reliability-branching scheduler - const i_t num_workers = 2 * settings_.num_threads; + // In deterministic mode, honor the user-visible B&B thread budget exactly. + const i_t num_workers = settings_.num_threads; std::vector search_strategies = get_search_strategies(settings_.diving_settings); std::array max_num_workers = @@ -3134,8 +3134,9 @@ void branch_and_bound_t::run_deterministic_coordinator(const csr_matri int actual_diving_workers = deterministic_diving_workers_ ? (int)deterministic_diving_workers_->size() : 0; settings_.log.printf( - "Deterministic Mode: %d BFS workers + %d diving workers, horizon step = %.2f work " - "units\n", + "Deterministic Mode: %d total threads split as %d BFS workers + %d diving workers, " + "horizon step = %.2f work units\n", + num_workers, num_bfs_workers, actual_diving_workers, deterministic_horizon_step_); diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index b7938617ee..618e247766 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -155,7 +155,7 @@ void diversity_manager_t::generate_solution(f_t time_limit, bool rando sol.compute_feasibility(); // if a feasible is found, it is added to the population ls.generate_solution(sol, random_start, &population, time_limit); - population.add_solution(std::move(sol), internals::mip_solution_origin_t::QUICK_FEASIBLE); + population.add_solution(std::move(sol), internals::mip_solution_origin_t::LOCAL_SEARCH); } template @@ -798,18 +798,20 @@ void diversity_manager_t::recombine_and_ls_with_all(solution_t void diversity_manager_t::recombine_and_ls_with_all( - std::vector>& solutions, bool add_only_feasible) + std::vector::drained_external_solution_t>& solutions, + bool add_only_feasible) { raft::common::nvtx::range fun_scope("recombine_and_ls_with_all"); if (solutions.size() > 0) { CUOPT_LOG_DEBUG("Running recombiners on B&B solutions with size %lu", solutions.size()); // add all solutions because time limit might have been consumed and we might have exited before - for (auto& sol : solutions) { + for (auto& drained_sol : solutions) { + auto& sol = drained_sol.solution; cuopt_func_call(sol.test_feasibility(true)); - population.add_solution(std::move(solution_t(sol)), - internals::mip_solution_origin_t::BRANCH_AND_BOUND); + population.add_solution(std::move(solution_t(sol)), drained_sol.origin); } - for (auto& sol : solutions) { + for (auto& drained_sol : solutions) { + auto& sol = drained_sol.solution; if (work_limit_reached()) { return; } solution_t ls_solution(sol); ls_config_t ls_config; diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh index 8d41b61f46..d0a45c4373 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh @@ -49,8 +49,9 @@ class diversity_manager_t { void diversity_step(i_t max_iterations_without_improvement); void add_user_given_solutions(std::vector>& initial_sol_vector); population_t* get_population_pointer() { return &population; } - void recombine_and_ls_with_all(std::vector>& solutions, - bool add_only_feasible = false); + void recombine_and_ls_with_all( + std::vector::drained_external_solution_t>& solutions, + bool add_only_feasible = false); void recombine_and_ls_with_all(solution_t& solution, bool add_only_feasible = false); std::pair, solution_t> recombine_and_local_search( solution_t& a, diff --git a/cpp/src/mip_heuristics/diversity/lns/rins.cu b/cpp/src/mip_heuristics/diversity/lns/rins.cu index 7fd8533f82..73b527a77b 100644 --- a/cpp/src/mip_heuristics/diversity/lns/rins.cu +++ b/cpp/src/mip_heuristics/diversity/lns/rins.cu @@ -342,8 +342,9 @@ void rins_t::run_rins() cuopt_assert(best_sol.assignment.size() == sol_size_before_rins, "Assignment size mismatch"); cuopt_assert(best_sol.assignment.size() == problem_copy->n_variables, "Assignment size mismatch"); - dm.population.add_external_solution( - best_sol.get_host_assignment(), best_sol.get_objective(), solution_origin_t::RINS); + dm.population.add_external_solution(best_sol.get_host_assignment(), + best_sol.get_objective(), + internals::mip_solution_origin_t::LOCAL_SEARCH); } } diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index d463c5da65..94a4c5ee24 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -202,11 +202,11 @@ size_t population_t::get_external_solution_size() template void population_t::add_external_solution(const std::vector& solution, f_t objective, - solution_origin_t origin) + internals::mip_solution_origin_t origin) { std::lock_guard lock(solution_mutex); - if (origin == solution_origin_t::CPUFJ) { + if (origin == internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP) { external_solution_queue_cpufj.emplace_back(solution, objective, origin); } else { external_solution_queue.emplace_back(solution, objective, origin); @@ -224,7 +224,7 @@ void population_t::add_external_solution(const std::vector& solut } CUOPT_LOG_DEBUG("%s added a solution to population, solution queue size %lu with objective %g", - solution_origin_to_string(origin), + internals::mip_solution_origin_to_string(origin), external_solution_queue.size(), problem_ptr->get_user_obj_from_solver_obj(objective)); if (objective < best_feasible_objective) { @@ -241,7 +241,9 @@ void population_t::add_external_solutions_to_population() if (is_deterministic_mode(context.settings.determinism_mode)) { return; } // don't do early exit checks here. mutex needs to be acquired to prevent race conditions auto new_sol_vector = get_external_solutions(); - add_solutions_from_vec(std::move(new_sol_vector)); + for (auto& drained_sol : new_sol_vector) { + add_solution(std::move(drained_sol.solution), drained_sol.origin); + } } // normally we would need a lock here but these are boolean types and race conditions are not @@ -254,10 +256,11 @@ void population_t::preempt_heuristic_solver() } template -std::vector> population_t::get_external_solutions() +std::vector::drained_external_solution_t> +population_t::get_external_solutions() { std::lock_guard lock(solution_mutex); - std::vector> return_vector; + std::vector return_vector; i_t counter = 0; f_t new_best_feasible_objective = best_feasible_objective; f_t longest_wait_time = 0; @@ -265,10 +268,10 @@ std::vector> population_t::get_external_solutions for (auto& h_entry : queue) { // ignore CPUFJ solutions if they're not better than the best feasible. // It seems they worsen results on some instances despite the potential for improved diversity - if (h_entry.origin == solution_origin_t::CPUFJ && + if (h_entry.origin == internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP && h_entry.objective > new_best_feasible_objective) { continue; - } else if (h_entry.origin != solution_origin_t::CPUFJ && + } else if (h_entry.origin != internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP && h_entry.objective > new_best_feasible_objective) { new_best_feasible_objective = h_entry.objective; } @@ -293,7 +296,7 @@ std::vector> population_t::get_external_solutions problem_ptr->n_integer_vars); } sol.handle_ptr->sync_stream(); - return_vector.emplace_back(std::move(sol)); + return_vector.emplace_back(std::move(sol), h_entry.origin); counter++; } } @@ -418,7 +421,7 @@ void population_t::run_solution_callbacks( "External solution objective mismatch"); auto h_outside_sol = outside_sol.get_host_assignment(); add_external_solution( - h_outside_sol, outside_sol.get_objective(), solution_origin_t::EXTERNAL); + h_outside_sol, outside_sol.get_objective(), internals::mip_solution_origin_t::USER_INITIAL); } } } @@ -1028,8 +1031,8 @@ void population_t::print() template void population_t::run_all_recombiners(solution_t& sol) { - std::vector> sol_vec; - sol_vec.emplace_back(std::move(solution_t(sol))); + std::vector::drained_external_solution_t> sol_vec; + sol_vec.emplace_back(solution_t(sol), internals::mip_solution_origin_t::LOCAL_SEARCH); dm.recombine_and_ls_with_all(sol_vec, true); } diff --git a/cpp/src/mip_heuristics/diversity/population.cuh b/cpp/src/mip_heuristics/diversity/population.cuh index 817f568082..b92b7f1a97 100644 --- a/cpp/src/mip_heuristics/diversity/population.cuh +++ b/cpp/src/mip_heuristics/diversity/population.cuh @@ -25,22 +25,20 @@ namespace cuopt::linear_programming::detail { template class diversity_manager_t; -enum class solution_origin_t { BRANCH_AND_BOUND, CPUFJ, RINS, EXTERNAL }; - -constexpr const char* solution_origin_to_string(solution_origin_t origin) -{ - switch (origin) { - case solution_origin_t::BRANCH_AND_BOUND: return "B&B"; - case solution_origin_t::CPUFJ: return "CPUFJ"; - case solution_origin_t::RINS: return "RINS"; - case solution_origin_t::EXTERNAL: return "injected"; - default: return "unknown"; - } -} - template class population_t { public: + struct drained_external_solution_t { + drained_external_solution_t(solution_t&& solution_, + internals::mip_solution_origin_t origin_) + : solution(std::move(solution_)), origin(origin_) + { + } + + solution_t solution; + internals::mip_solution_origin_t origin; + }; + population_t(std::string const& name, mip_solver_context_t& context, diversity_manager_t& dm, @@ -112,8 +110,8 @@ class population_t { internals::mip_solution_origin_t callback_origin = internals::mip_solution_origin_t::UNKNOWN); void add_external_solution(const std::vector& solution, f_t objective, - solution_origin_t origin); - std::vector> get_external_solutions(); + internals::mip_solution_origin_t origin); + std::vector get_external_solutions(); void add_external_solutions_to_population(); size_t get_external_solution_size(); void preempt_heuristic_solver(); @@ -193,7 +191,9 @@ class population_t { struct external_solution_t { external_solution_t() = default; - external_solution_t(const std::vector& solution, f_t objective, solution_origin_t origin) + external_solution_t(const std::vector& solution, + f_t objective, + internals::mip_solution_origin_t origin) : solution(solution), objective(objective), origin(origin), @@ -202,7 +202,7 @@ class population_t { } std::vector solution; f_t objective; - solution_origin_t origin; + internals::mip_solution_origin_t origin; timer_t timer; // debug timer to track how long a solution has lingered in the queue }; diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index e40d67ee05..70369f824c 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -95,7 +95,8 @@ void local_search_t::start_cpufj_scratch_threads(population_timprovement_callback = [&population, problem_ptr = context.problem_ptr]( f_t obj, const std::vector& h_vec, double /*work_units*/) { - population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); + population.add_external_solution( + h_vec, obj, internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP); (void)problem_ptr; if (obj < local_search_best_obj) { CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", @@ -133,7 +134,8 @@ void local_search_t::start_cpufj_lptopt_scratch_threads( scratch_cpu_fj_on_lp_opt.fj_cpu->log_prefix = "******* scratch on LP optimal: "; scratch_cpu_fj_on_lp_opt.fj_cpu->improvement_callback = [this, &population](f_t obj, const std::vector& h_vec, double /*work_units*/) { - population.add_external_solution(h_vec, obj, solution_origin_t::CPUFJ); + population.add_external_solution( + h_vec, obj, internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP); if (obj < local_search_best_obj) { CUOPT_LOG_DEBUG("******* New local search best obj %g, best overall %g", context.problem_ptr->get_user_obj_from_solver_obj(obj), diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 69a82afbbd..fede9d8fb3 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -65,7 +65,7 @@ struct branch_and_bound_solution_helper_t { { if (!settings_.deterministic) { dm->population.add_external_solution( - solution, objective, solution_origin_t::BRANCH_AND_BOUND); + solution, objective, internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE); dm->rins.new_best_incumbent_callback(solution); } } @@ -77,7 +77,7 @@ struct branch_and_bound_solution_helper_t { { if (!settings_.deterministic) { dm->population.add_external_solution( - solution, objective, solution_origin_t::BRANCH_AND_BOUND); + solution, objective, internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE); dm->rins.new_best_incumbent_callback(solution); return; } From e532bcda747ed1be65af45caab450f60479c0f16 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 9 Mar 2026 04:19:11 -0700 Subject: [PATCH 153/225] tuning 1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 11f9cda679..272fe44905 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -223,7 +223,7 @@ int run_single_file(std::string file_path, settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; settings.seed = 42; - settings.gpu_heur_work_unit_scale = 0.1; + settings.gpu_heur_work_unit_scale = 0.4; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); From f80c73369669f8d4ae45698891cf50f1c5e94ce4 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 9 Mar 2026 04:19:30 -0700 Subject: [PATCH 154/225] tuning 2 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 272fe44905..7b71c8c5bd 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -223,7 +223,7 @@ int run_single_file(std::string file_path, settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; settings.seed = 42; - settings.gpu_heur_work_unit_scale = 0.4; + settings.gpu_heur_work_unit_scale = 0.05; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); From e3e37ad55f4c6d3da35fd2b9e1148b283909a379 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 9 Mar 2026 06:34:23 -0700 Subject: [PATCH 155/225] logging fixes --- cpp/src/branch_and_bound/branch_and_bound.cpp | 42 +++++++++++++------ cpp/src/branch_and_bound/branch_and_bound.hpp | 3 +- .../mip_heuristics/diversity/population.cu | 6 --- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 62e16f995b..e40800d89d 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -323,19 +323,30 @@ f_t branch_and_bound_t::get_lower_bound() } template -void branch_and_bound_t::report_heuristic(f_t obj) +void branch_and_bound_t::report_heuristic(f_t obj, double work_time) { if (is_running_) { f_t user_obj = compute_user_objective(original_lp_, obj); f_t user_lower = compute_user_objective(original_lp_, get_lower_bound()); std::string user_gap = user_mip_gap(user_obj, user_lower); - - settings_.log.printf( - "H %+13.6e %+10.6e %s %9.2f\n", - user_obj, - user_lower, - user_gap.c_str(), - toc(exploration_stats_.start_time)); + if (settings_.deterministic) { + const double reported_work = work_time >= 0.0 ? work_time : work_unit_context_.current_work(); + settings_.log.printf( + "H %+13.6e %+10.6e %s " + "%9.2f %9.2f\n", + user_obj, + user_lower, + user_gap.c_str(), + reported_work, + toc(exploration_stats_.start_time)); + } else { + settings_.log.printf( + "H %+13.6e %+10.6e %s %9.2f\n", + user_obj, + user_lower, + user_gap.c_str(), + toc(exploration_stats_.start_time)); + } } else { settings_.log.printf("New solution from primal heuristics. Objective %+.6e. Time %.2f\n", compute_user_objective(original_lp_, obj), @@ -577,6 +588,14 @@ void branch_and_bound_t::queue_external_solution_deterministic( "Solution size mismatch %ld %d\n", solution.size(), original_problem_.num_cols); return; } + const double bnb_work_total = work_unit_context_.current_work(); + const uint32_t host_hash = detail::compute_hash(solution); + settings_.log.printf( + "Queueing deterministic external incumbent: wut=%.3f (bnb_wut=%.3f) origin=%s hash=0x%x\n", + work_unit_ts, + bnb_work_total, + cuopt::internals::mip_solution_origin_to_string(origin), + host_hash); mutex_original_lp_.lock(); const size_t lp_nnz = original_lp_.A.x.size(); @@ -611,7 +630,6 @@ void branch_and_bound_t::queue_external_solution_deterministic( crush_primal_solution( original_problem_, original_lp_, solution, new_slacks_, crushed_solution); f_t obj = compute_objective(original_lp_, crushed_solution); - const uint32_t host_hash = detail::compute_hash(solution); const uint32_t crushed_hash = detail::compute_hash(crushed_solution); // Validate solution before queueing @@ -627,7 +645,7 @@ void branch_and_bound_t::queue_external_solution_deterministic( // consumption time so that the crush reflects the current LP state // (which may have gained slack columns from cuts added after this point). mutex_repair_.lock(); - repair_queue_.push_back({solution, origin}); + repair_queue_.push_back({solution, origin, work_unit_ts}); const size_t repair_queue_size = repair_queue_.size(); mutex_repair_.unlock(); CUOPT_DETERMINISM_LOG_PRINTF( @@ -764,7 +782,7 @@ void branch_and_bound_t::repair_heuristic_solutions() upper_bound_.load(), repaired_obj, detail::compute_hash(repaired_solution)); - report_heuristic(repaired_obj); + report_heuristic(repaired_obj, queued_solution.work_timestamp); emit_solution_callback_from_crushed( repaired_solution, repaired_obj, queued_solution.origin, -1.0); @@ -3910,7 +3928,7 @@ void branch_and_bound_t::deterministic_sort_replay_events( 0, deterministic_current_horizon_); } else { - report_heuristic(sol.objective); + report_heuristic(sol.objective, sol.work_timestamp); } emit_solution_callback_from_crushed( sol.solution, sol.objective, sol.origin, sol.work_timestamp); diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index 9bc0e2d875..e441033f8a 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -221,6 +221,7 @@ class branch_and_bound_t { std::vector solution; cuopt::internals::mip_solution_origin_t origin{ cuopt::internals::mip_solution_origin_t::UNKNOWN}; + double work_timestamp{-1.0}; }; std::vector repair_queue_; @@ -266,7 +267,7 @@ class branch_and_bound_t { omp_atomic_t lower_bound_ceiling_; std::function user_bound_callback_; - void report_heuristic(f_t obj); + void report_heuristic(f_t obj, double work_time = -1.0); void report(char symbol, f_t obj, f_t lower_bound, diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index 94a4c5ee24..bedaf31ca3 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -338,12 +338,6 @@ void population_t::run_solution_callbacks( const double work_timestamp = context.gpu_heur_loop.current_producer_work(); cuopt_assert(std::isfinite(work_timestamp), "Deterministic heuristic work timestamp must be finite"); - CUOPT_LOG_DEBUG( - "Submitting deterministic heuristic incumbent: obj=%g wut=%.6f origin=%s hash=0x%x", - sol.get_user_objective(), - work_timestamp, - internals::mip_solution_origin_to_string(callback_origin), - sol.get_hash()); context.branch_and_bound_ptr->queue_external_solution_deterministic( sol.get_host_assignment(), work_timestamp, callback_origin); // In deterministic mode, B&B replay is the single owner of GET_SOLUTION callback ordering. From c36ae1d31ca167706aa3901f312ec0169641deef Mon Sep 17 00:00:00 2001 From: Chris Maes Date: Mon, 9 Mar 2026 12:12:27 -0700 Subject: [PATCH 156/225] Fix incorrect errors with x + x*x, +x, -x in Python API (#936) ## Issue Authors: - Chris Maes (https://github.com/chris-maes) - Ishika Roy (https://github.com/Iroy30) Approvers: - Ishika Roy (https://github.com/Iroy30) URL: https://github.com/NVIDIA/cuopt/pull/936 --- python/cuopt/cuopt/linear_programming/problem.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/python/cuopt/cuopt/linear_programming/problem.py b/python/cuopt/cuopt/linear_programming/problem.py index baf9716191..80cb83ee6f 100644 --- a/python/cuopt/cuopt/linear_programming/problem.py +++ b/python/cuopt/cuopt/linear_programming/problem.py @@ -195,6 +195,12 @@ def getVariableName(self): """ return self.VariableName + def __neg__(self): + return LinearExpression([self], [-1.0], 0.0) + + def __pos__(self): + return self + def __add__(self, other): match other: case int() | float(): @@ -204,6 +210,8 @@ def __add__(self, other): return LinearExpression([self, other], [1.0, 1.0], 0.0) case LinearExpression(): return other + self + case QuadraticExpression(): + return other + self case _: raise ValueError( "Cannot add type %s to variable" % type(other).__name__ @@ -221,6 +229,8 @@ def __sub__(self, other): case LinearExpression(): # self - other -> other * -1.0 + self return other * -1.0 + self + case QuadraticExpression(): + return other * -1.0 + self case _: raise ValueError( "Cannot subtract type %s from variable" From 4c9f4945a7f924b93841a02b5ad035e32550c38b Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 9 Mar 2026 13:07:42 -0700 Subject: [PATCH 157/225] attempts at fixing failed repairs --- .../linear_programming/cuopt/run_mip.cpp | 2 +- cpp/src/branch_and_bound/branch_and_bound.cpp | 278 +++++++++++------- cpp/src/branch_and_bound/branch_and_bound.hpp | 17 +- cpp/src/branch_and_bound/pseudo_costs.cpp | 39 ++- cpp/src/branch_and_bound/pseudo_costs.hpp | 4 +- cpp/src/dual_simplex/phase2.cpp | 6 +- .../diversity/diversity_manager.cu | 3 - .../mip_heuristics/diversity/population.cu | 3 +- .../local_search/local_search.cu | 2 +- 9 files changed, 230 insertions(+), 124 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 7b71c8c5bd..272fe44905 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -223,7 +223,7 @@ int run_single_file(std::string file_path, settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; settings.seed = 42; - settings.gpu_heur_work_unit_scale = 0.05; + settings.gpu_heur_work_unit_scale = 0.4; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index e40800d89d..65827fd188 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -43,6 +43,13 @@ #include #include +// uncomment to enable detailed detemrinism logs +#undef CUOPT_DETERMINISM_LOG_PRINTF +#define CUOPT_DETERMINISM_LOG_PRINTF(logger, ...) \ + do { \ + logger.printf(__VA_ARGS__); \ + } while (0) + namespace cuopt::linear_programming::dual_simplex { namespace { @@ -577,11 +584,12 @@ void branch_and_bound_t::set_new_solution(const std::vector& solu template void branch_and_bound_t::queue_external_solution_deterministic( const std::vector& solution, + f_t user_objective, double work_unit_ts, cuopt::internals::mip_solution_origin_t origin) { - // In deterministic mode, queue the solution to be processed at the correct work unit timestamp - // This ensures deterministic ordering of solution events + // In deterministic mode, external solutions remain raw until their retirement + // horizon so that feasibility and repair use the retirement LP state. if (solution.size() != original_problem_.num_cols) { settings_.log.printf( @@ -591,7 +599,9 @@ void branch_and_bound_t::queue_external_solution_deterministic( const double bnb_work_total = work_unit_context_.current_work(); const uint32_t host_hash = detail::compute_hash(solution); settings_.log.printf( - "Queueing deterministic external incumbent: wut=%.3f (bnb_wut=%.3f) origin=%s hash=0x%x\n", + "Queueing deterministic external incumbent: obj=%g wut=%.3f (bnb_wut=%.3f) origin=%s " + "hash=0x%x\n", + user_objective, work_unit_ts, bnb_work_total, cuopt::internals::mip_solution_origin_to_string(origin), @@ -626,57 +636,19 @@ void branch_and_bound_t::queue_external_solution_deterministic( a_col_hash, a_row_hash, a_val_hash); - std::vector crushed_solution; - crush_primal_solution( - original_problem_, original_lp_, solution, new_slacks_, crushed_solution); - f_t obj = compute_objective(original_lp_, crushed_solution); - const uint32_t crushed_hash = detail::compute_hash(crushed_solution); - - // Validate solution before queueing - f_t primal_err; - f_t bound_err; - i_t num_fractional; - bool is_feasible = check_guess( - original_lp_, settings_, var_types_, crushed_solution, primal_err, bound_err, num_fractional); mutex_original_lp_.unlock(); - if (!is_feasible) { - // Queue the uncrushed solution for repair; it will be crushed at - // consumption time so that the crush reflects the current LP state - // (which may have gained slack columns from cuts added after this point). - mutex_repair_.lock(); - repair_queue_.push_back({solution, origin, work_unit_ts}); - const size_t repair_queue_size = repair_queue_.size(); - mutex_repair_.unlock(); - CUOPT_DETERMINISM_LOG_PRINTF( - settings_.log, - "Deterministic external reject_to_repair: wut=%.6f obj=%.16e host_hash=0x%x " - "crushed_hash=0x%x primal_err=%.6e bound_err=%.6e fractional=%d repair_q=%zu\n", - work_unit_ts, - obj, - host_hash, - crushed_hash, - primal_err, - bound_err, - num_fractional, - repair_queue_size); - return; - } - - // Queue the solution with its work unit timestamp mutex_heuristic_queue_.lock(); - heuristic_solution_queue_.push_back( - {obj, std::move(crushed_solution), 0, -1, 0, work_unit_ts, origin}); + heuristic_solution_queue_.push_back({solution, user_objective, work_unit_ts, origin}); const size_t heuristic_queue_size = heuristic_solution_queue_.size(); mutex_heuristic_queue_.unlock(); CUOPT_DETERMINISM_LOG_PRINTF( settings_.log, - "Deterministic external queued: wut=%.6f obj=%.16e host_hash=0x%x crushed_hash=0x%x " + "Deterministic external queued_for_retirement: wut=%.6f user_obj=%.16e host_hash=0x%x " "heur_q=%zu\n", work_unit_ts, - obj, + user_objective, host_hash, - crushed_hash, heuristic_queue_size); } @@ -720,24 +692,33 @@ bool branch_and_bound_t::repair_solution(const std::vector& edge_ f_t primal_error; f_t bound_error; i_t num_fractional; - feasible = check_guess(original_lp_, + feasible = check_guess(original_lp_, settings_, var_types_, lp_solution.x, primal_error, bound_error, num_fractional); - repaired_obj = compute_objective(original_lp_, repaired_solution); - constexpr bool verbose = false; - if (verbose) { + repaired_obj = compute_objective(original_lp_, repaired_solution); + if (!feasible) { settings_.log.printf( - "After repair: feasible %d primal error %e bound error %e fractional %d. Objective %e\n", - feasible, + "Repair LP optimal but check_guess failed: primal_err=%e bound_err=%e fractional=%d " + "obj=%e iters=%d time=%.3fs\n", primal_error, bound_error, num_fractional, - repaired_obj); + repaired_obj, + iter, + toc(lp_start_time)); } + } else { + settings_.log.printf( + "Repair LP failed: status=%s iters=%d time=%.3fs time_limit=%.3f cut_off=%e\n", + dual::status_to_string(lp_status).c_str(), + iter, + toc(lp_start_time), + lp_settings.time_limit, + lp_settings.cut_off); } return feasible; @@ -2213,6 +2194,16 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut exploration_stats_.nodes_explored = 0; original_lp_.A.to_compressed_row(Arow_); + work_unit_scheduler_t* saved_scheduler = work_unit_context_.scheduler; + if (settings_.deterministic) { + work_unit_context_.deterministic = true; + // Detach the scheduler during the serial root/cuts/SB phase. + // record_work_sync_on_horizon still accumulates global_work_units_elapsed, + // but avoids scheduler->on_work_recorded whose OMP directives + // perturb FP state in a single-thread context. + work_unit_context_.scheduler = nullptr; + } + if (guess_.size() != 0) { raft::common::nvtx::range scope_guess("BB::check_initial_guess"); std::vector crushed_guess; @@ -2263,7 +2254,8 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut basic_list, nonbasic_list, root_vstatus_, - edge_norms_); + edge_norms_, + &work_unit_context_); } else { settings_.log.printf("\nSolving LP root relaxation in concurrent mode\n"); root_status = solve_root_relaxation(lp_settings, @@ -2560,8 +2552,30 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut i_t iter = 0; bool initialize_basis = false; lp_settings.concurrent_halt = NULL; - f_t dual_phase2_start_time = tic(); - dual::status_t cut_status = dual_phase2_with_advanced_basis(2, + CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, + "Cut loop LP warm-start: pass=%d rows=%d cols=%d " + "lower_hash=0x%x upper_hash=0x%x " + "x_hash=0x%x y_hash=0x%x z_hash=0x%x " + "basic_hash=0x%x nonbasic_hash=0x%x " + "vstatus_hash=0x%x edge_norms_hash=0x%x " + "cut_off=%.16e work_limit=%.16e time_limit=%.16e\n", + cut_pass, + original_lp_.num_rows, + original_lp_.num_cols, + detail::compute_hash(original_lp_.lower), + detail::compute_hash(original_lp_.upper), + detail::compute_hash(root_relax_soln_.x), + detail::compute_hash(root_relax_soln_.y), + detail::compute_hash(root_relax_soln_.z), + detail::compute_hash(basic_list), + detail::compute_hash(nonbasic_list), + detail::compute_hash(root_vstatus_), + detail::compute_hash(edge_norms_), + lp_settings.cut_off, + lp_settings.work_limit, + lp_settings.time_limit); + f_t dual_phase2_start_time = tic(); + dual::status_t cut_status = dual_phase2_with_advanced_basis(2, 0, initialize_basis, exploration_stats_.start_time, @@ -2573,7 +2587,8 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut nonbasic_list, root_relax_soln_, iter, - edge_norms_); + edge_norms_, + &work_unit_context_); exploration_stats_.total_lp_iters += iter; { const f_t previous_root_objective = root_objective_; @@ -2598,6 +2613,12 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut return solver_status_; } + if (cut_status == dual::status_t::WORK_LIMIT) { + solver_status_ = mip_status_t::WORK_LIMIT; + set_final_solution(solution, root_objective_); + return solver_status_; + } + if (cut_status != dual::status_t::OPTIMAL) { settings_.log.printf("Numerical issue at root node. Resolving from scratch\n"); lp_status_t scratch_status = @@ -2609,7 +2630,8 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut basic_list, nonbasic_list, root_vstatus_, - edge_norms_); + edge_norms_, + &work_unit_context_); if (scratch_status == lp_status_t::OPTIMAL) { // We recovered cut_status = convert_lp_status_to_dual_status(scratch_status); @@ -2833,7 +2855,8 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_objective_, root_vstatus_, edge_norms_, - pc_); + pc_, + &work_unit_context_); } if (toc(exploration_stats_.start_time) > settings_.time_limit) { @@ -2914,6 +2937,9 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut calculate_variable_locks(original_lp_, var_up_locks_, var_down_locks_); } if (settings_.deterministic) { + pre_exploration_work_ = work_unit_context_.current_work(); + work_unit_context_.scheduler = saved_scheduler; + settings_.log.printf("Pre-exploration work: %.2f work units\n", pre_exploration_work_); settings_.log.printf( " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " "| Gap | Work | Time |\n"); @@ -3109,7 +3135,7 @@ void branch_and_bound_t::run_deterministic_coordinator(const csr_matri } deterministic_mode_enabled_ = true; - deterministic_current_horizon_ = deterministic_horizon_step_; + deterministic_current_horizon_ = pre_exploration_work_ + deterministic_horizon_step_; deterministic_horizon_number_ = 0; deterministic_global_termination_status_ = mip_status_t::UNSET; @@ -3726,68 +3752,110 @@ void branch_and_bound_t::deterministic_sort_replay_events( std::vector::deterministic_replay_solution_t>& replay_solutions) { - // Infeasible solutions from GPU heuristics are queued for repair; process them now + // Retire external solutions that have reached the current horizon. Feasibility + // classification and repair happen only here in deterministic mode. { - std::vector to_repair; - // TODO: support repair queue in deterministic mode - // mutex_repair_.lock(); - // if (repair_queue_.size() > 0) { - // to_repair = repair_queue_; - // repair_queue_.clear(); - // } - // mutex_repair_.unlock(); - - std::sort(to_repair.begin(), - to_repair.end(), - [](const queued_repair_solution_t& a, const queued_repair_solution_t& b) { + std::vector due_solutions; + mutex_heuristic_queue_.lock(); + { + std::vector future_solutions; + for (auto& sol : heuristic_solution_queue_) { + if (sol.work_timestamp < deterministic_current_horizon_) { + due_solutions.push_back(std::move(sol)); + } else { + future_solutions.push_back(std::move(sol)); + } + } + heuristic_solution_queue_ = std::move(future_solutions); + } + mutex_heuristic_queue_.unlock(); + + std::sort(due_solutions.begin(), + due_solutions.end(), + [](const queued_external_solution_t& a, const queued_external_solution_t& b) { + if (a.work_timestamp != b.work_timestamp) { + return a.work_timestamp < b.work_timestamp; + } + if (a.user_objective != b.user_objective) { + return a.user_objective < b.user_objective; + } + if (a.origin != b.origin) { return a.origin < b.origin; } return a.solution < b.solution; }); - if (to_repair.size() > 0) { - CUOPT_DETERMINISM_LOG_PRINTF( - settings_.log, - "Deterministic sync: Attempting to repair %ld injected solutions\n", - to_repair.size()); - for (const auto& queued_solution : to_repair) { - const std::vector& uncrushed_solution = queued_solution.solution; + if (!due_solutions.empty()) { + CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, + "Deterministic sync: retiring %ld external solutions\n", + due_solutions.size()); + for (const auto& queued_solution : due_solutions) { std::vector crushed_solution; + f_t obj; + f_t primal_err; + f_t bound_err; + i_t num_fractional; + bool is_feasible = false; + mutex_original_lp_.lock(); crush_primal_solution( - original_problem_, original_lp_, uncrushed_solution, new_slacks_, crushed_solution); + original_problem_, original_lp_, queued_solution.solution, new_slacks_, crushed_solution); + obj = compute_objective(original_lp_, crushed_solution); + is_feasible = check_guess(original_lp_, + settings_, + var_types_, + crushed_solution, + primal_err, + bound_err, + num_fractional); + mutex_original_lp_.unlock(); + + if (is_feasible) { + replay_solutions.push_back({{obj, + std::move(crushed_solution), + 0, + -1, + 0, + queued_solution.work_timestamp, + queued_solution.origin}, + search_strategy_t::BEST_FIRST}); + CUOPT_DETERMINISM_LOG_PRINTF( + settings_.log, + "Deterministic retirement accepted: wut=%.6f obj=%.16e origin=%s primal_err=%.6e " + "bound_err=%.6e fractional=%d\n", + queued_solution.work_timestamp, + obj, + cuopt::internals::mip_solution_origin_to_string(queued_solution.origin), + primal_err, + bound_err, + num_fractional); + continue; + } + std::vector repaired_solution; f_t repaired_obj; bool success = repair_solution(edge_norms_, crushed_solution, repaired_obj, repaired_solution); if (success) { - // Queue repaired solution with work unit timestamp (...workstamp?) - mutex_heuristic_queue_.lock(); - heuristic_solution_queue_.push_back({repaired_obj, - std::move(repaired_solution), - 0, - -1, - 0, - deterministic_current_horizon_, - queued_solution.origin}); - mutex_heuristic_queue_.unlock(); + settings_.log.printf( + "Deterministic repair success: wut=%.3f obj=%.16e origin=%s\n", + queued_solution.work_timestamp, + repaired_obj, + cuopt::internals::mip_solution_origin_to_string(queued_solution.origin)); + replay_solutions.push_back({{repaired_obj, + std::move(repaired_solution), + 0, + -1, + 0, + queued_solution.work_timestamp, + queued_solution.origin}, + search_strategy_t::BEST_FIRST}); + } else { + settings_.log.printf( + "Deterministic repair FAILED: wut=%.3f origin=%s\n", + queued_solution.work_timestamp, + cuopt::internals::mip_solution_origin_to_string(queued_solution.origin)); } } } } - - // Extract heuristic solutions, keeping future solutions for next horizon - // Use deterministic_current_horizon_ as the upper bound (horizon_end) - mutex_heuristic_queue_.lock(); - { - std::vector> future_solutions; - for (auto& sol : heuristic_solution_queue_) { - if (sol.work_timestamp < deterministic_current_horizon_) { - replay_solutions.push_back({std::move(sol), search_strategy_t::BEST_FIRST}); - } else { - future_solutions.push_back(std::move(sol)); - } - } - heuristic_solution_queue_ = std::move(future_solutions); - } - mutex_heuristic_queue_.unlock(); if (!replay_solutions.empty() || !heuristic_solution_queue_.empty()) { CUOPT_DETERMINISM_LOG_PRINTF( settings_.log, diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index e441033f8a..08ba10fade 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -132,6 +132,7 @@ class branch_and_bound_t { // This queues the solution to be processed at the correct work unit timestamp void queue_external_solution_deterministic(const std::vector& solution, + f_t user_objective, double work_unit_ts, cuopt::internals::mip_solution_origin_t origin = cuopt::internals::mip_solution_origin_t::UNKNOWN); @@ -180,6 +181,7 @@ class branch_and_bound_t { const simplex_solver_settings_t settings_; work_limit_context_t work_unit_context_{"B&B"}; + double pre_exploration_work_{0.0}; // Initial guess. std::vector guess_; @@ -432,10 +434,19 @@ class branch_and_bound_t { double max_producer_wait_time_{0.0}; i_t producer_wait_count_{0}; - // Determinism heuristic solution queue - solutions received from GPU heuristics - // Stored with work unit timestamp for deterministic ordering + struct queued_external_solution_t { + std::vector solution; + f_t user_objective{std::numeric_limits::infinity()}; + double work_timestamp{0.0}; + cuopt::internals::mip_solution_origin_t origin{ + cuopt::internals::mip_solution_origin_t::UNKNOWN}; + }; + + // Deterministic pending external solution queue. + // External solutions stay raw until their retirement horizon, where they are + // crushed, checked, and repaired immediately if needed. omp_mutex_t mutex_heuristic_queue_; - std::vector> heuristic_solution_queue_; + std::vector heuristic_solution_queue_; // ============================================================================ // Determinism Diving state diff --git a/cpp/src/branch_and_bound/pseudo_costs.cpp b/cpp/src/branch_and_bound/pseudo_costs.cpp index ee7e2f7803..b26d185598 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.cpp +++ b/cpp/src/branch_and_bound/pseudo_costs.cpp @@ -34,7 +34,8 @@ void strong_branch_helper(i_t start, const std::vector& root_soln, const std::vector& root_vstatus, const std::vector& edge_norms, - pseudo_costs_t& pc) + pseudo_costs_t& pc, + cuopt::work_limit_context_t* work_unit_context) { raft::common::nvtx::range scope("BB::strong_branch_helper"); lp_problem_t child_problem = original_lp; @@ -74,7 +75,8 @@ void strong_branch_helper(i_t start, vstatus, solution, iter, - child_edge_norms); + child_edge_norms, + work_unit_context); f_t obj = std::numeric_limits::quiet_NaN(); if (status == dual::status_t::DUAL_UNBOUNDED) { @@ -307,7 +309,8 @@ void strong_branching(const user_problem_t& original_problem, f_t root_obj, const std::vector& root_vstatus, const std::vector& edge_norms, - pseudo_costs_t& pc) + pseudo_costs_t& pc, + cuopt::work_limit_context_t* work_unit_context) { pc.resize(original_lp.num_cols); pc.strong_branch_down.assign(fractional.size(), 0); @@ -400,12 +403,23 @@ void strong_branching(const user_problem_t& original_problem, fractional.size()); f_t strong_branching_start_time = tic(); + const bool use_work_accounting = work_unit_context && work_unit_context->deterministic; + std::vector thread_work_contexts; + if (use_work_accounting) { + thread_work_contexts.reserve(settings.num_threads); + for (i_t t = 0; t < settings.num_threads; ++t) { + thread_work_contexts.emplace_back("sb_thread_" + std::to_string(t)); + thread_work_contexts.back().deterministic = true; + } + } + #pragma omp parallel num_threads(settings.num_threads) { i_t n = std::min(4 * settings.num_threads, fractional.size()); - // Here we are creating more tasks than the number of threads - // such that they can be scheduled dynamically to the threads. + cuopt::work_limit_context_t* thread_ctx = + use_work_accounting ? &thread_work_contexts[omp_get_thread_num()] : nullptr; + #pragma omp for schedule(dynamic, 1) for (i_t k = 0; k < n; k++) { i_t start = std::floor(k * fractional.size() / n); @@ -432,9 +446,19 @@ void strong_branching(const user_problem_t& original_problem, root_soln, root_vstatus, edge_norms, - pc); + pc, + thread_ctx); } } + + if (use_work_accounting) { + double max_work = 0.0; + for (const auto& ctx : thread_work_contexts) { + max_work = std::max(max_work, ctx.current_work()); + } + work_unit_context->record_work_sync_on_horizon(max_work); + } + settings.log.printf("Strong branching completed in %.2fs\n", toc(strong_branching_start_time)); } @@ -786,7 +810,8 @@ template void strong_branching(const user_problem_t& o double root_obj, const std::vector& root_vstatus, const std::vector& edge_norms, - pseudo_costs_t& pc); + pseudo_costs_t& pc, + cuopt::work_limit_context_t* work_unit_context); #endif diff --git a/cpp/src/branch_and_bound/pseudo_costs.hpp b/cpp/src/branch_and_bound/pseudo_costs.hpp index 6b6c6917b6..ccd5b5d0c1 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.hpp +++ b/cpp/src/branch_and_bound/pseudo_costs.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -527,6 +528,7 @@ void strong_branching(const user_problem_t& original_problem, f_t root_obj, const std::vector& root_vstatus, const std::vector& edge_norms, - pseudo_costs_t& pc); + pseudo_costs_t& pc, + cuopt::work_limit_context_t* work_unit_context = nullptr); } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 426d9a7535..55c044d6e7 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -3497,12 +3497,14 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, f_t now = toc(start_time); // Feature logging for regression training (every FEATURE_LOG_INTERVAL iterations) - if ((iter % FEATURE_LOG_INTERVAL) == 0 && work_unit_context) { + if ((iter % FEATURE_LOG_INTERVAL) == 0) { [[maybe_unused]] i_t iters_elapsed = iter - last_feature_log_iter; phase2_work_estimate += ft.work_estimate(); ft.clear_work_estimate(); - work_unit_context->record_work_sync_on_horizon(phase2_work_estimate / 1e8); + if (work_unit_context) { + work_unit_context->record_work_sync_on_horizon(phase2_work_estimate / 1e8); + } phase2_work_estimate = 0.0; last_feature_log_iter = iter; diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 618e247766..865b6dca50 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -220,9 +220,6 @@ bool diversity_manager_t::run_presolve(f_t time_limit, timer_t global_ ls.constraint_prop.bounds_update.set_updated_bounds(*problem_ptr); } bool run_probing_cache = !fj_only_run; - // Don't run probing cache in deterministic mode yet as neither B&B nor CPUFJ need it - // and it doesn't make use of work units yet - if (is_deterministic_mode(context.settings.determinism_mode)) { run_probing_cache = false; } if (run_probing_cache) { // Run probing cache before trivial presolve to discover variable implications const f_t max_time_on_probing = diversity_config.max_time_on_probing; diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index bedaf31ca3..16f61b0e00 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -339,7 +339,7 @@ void population_t::run_solution_callbacks( cuopt_assert(std::isfinite(work_timestamp), "Deterministic heuristic work timestamp must be finite"); context.branch_and_bound_ptr->queue_external_solution_deterministic( - sol.get_host_assignment(), work_timestamp, callback_origin); + sol.get_host_assignment(), sol.get_objective(), work_timestamp, callback_origin); // In deterministic mode, B&B replay is the single owner of GET_SOLUTION callback ordering. best_feasible_objective = sol.get_objective(); } else { @@ -357,6 +357,7 @@ void population_t::run_solution_callbacks( const bool published = context.solution_publication.publish_new_best_feasible(payload, timer.elapsed_time()); cuopt_assert(published, "New best feasible solution should publish to GET callbacks"); + best_feasible_objective = sol.get_objective(); } } diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index 70369f824c..ac317ce38f 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -193,7 +193,7 @@ void local_search_t::start_cpufj_deterministic( deterministic_cpu_fj.fj_cpu->improvement_callback = [&bb](f_t obj, const std::vector& h_vec, double work_units) { bb.queue_external_solution_deterministic( - h_vec, work_units, cuopt::internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP); + h_vec, obj, work_units, cuopt::internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP); }; deterministic_cpu_fj.start_cpu_solver(); From 971f2d1f27d13acc0e2370d3548b11598af946aa Mon Sep 17 00:00:00 2001 From: anandhkb Date: Mon, 9 Mar 2026 14:47:14 -0700 Subject: [PATCH 158/225] CodeRabbit/chris-maes: objective-sense aware bound, reset lower_bound_ceiling_, time last in heuristic log --- cpp/src/branch_and_bound/branch_and_bound.cpp | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 70baa3e0a0..93bf2c2aa6 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -312,9 +312,11 @@ void branch_and_bound_t::report_heuristic(f_t obj) f_t lower = get_lower_bound(); f_t user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) : -inf; if (exploration_stats_.nodes_explored == 0) { - f_t root_progress = root_lp_current_lower_bound_.load(); + f_t root_progress = root_lp_current_lower_bound_.load(); + const bool is_maximization = original_lp_.obj_scale < 0.0; if (std::isfinite(root_progress) && - (!std::isfinite(user_lower) || root_progress > user_lower)) { + (!std::isfinite(user_lower) || + (is_maximization ? root_progress < user_lower : root_progress > user_lower))) { user_lower = root_progress; } } @@ -327,17 +329,16 @@ void branch_and_bound_t::report_heuristic(f_t obj) user_gap.c_str(), toc(exploration_stats_.start_time)); } else { - f_t user_obj = compute_user_objective(original_lp_, obj); - f_t lower = get_lower_bound(); - f_t user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) - : root_lp_current_lower_bound_.load(); - std::string gap_str = std::isfinite(user_lower) - ? (". Gap: " + user_mip_gap(user_obj, user_lower) + "\n") - : "\n"; - settings_.log.printf("New solution from primal heuristics. Objective %+.6e. Time %.2f%s", + f_t user_obj = compute_user_objective(original_lp_, obj); + f_t lower = get_lower_bound(); + f_t user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) + : root_lp_current_lower_bound_.load(); + std::string gap_str = + std::isfinite(user_lower) ? (". Gap: " + user_mip_gap(user_obj, user_lower)) : ""; + settings_.log.printf("New solution from primal heuristics. Objective %+.6e%s. Time %.2f\n", user_obj, - toc(exploration_stats_.start_time), - gap_str.c_str()); + gap_str.c_str(), + toc(exploration_stats_.start_time)); } } @@ -1960,6 +1961,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut log.log_prefix = settings_.log.log_prefix; solver_status_ = mip_status_t::UNSET; is_running_ = false; + lower_bound_ceiling_ = inf; root_lp_current_lower_bound_ = -inf; root_objective_ = std::numeric_limits::quiet_NaN(); exploration_stats_.nodes_unexplored = 0; From d7ddec79e6c8031e91b0e9b8c7e5f74aff70bf8c Mon Sep 17 00:00:00 2001 From: anandhkb Date: Mon, 9 Mar 2026 15:13:02 -0700 Subject: [PATCH 159/225] Chris Maes: dual_simplex_objective_callback, solving_root_relaxation_, unify report_heuristic --- cpp/src/branch_and_bound/branch_and_bound.cpp | 67 +++++++++++-------- cpp/src/branch_and_bound/branch_and_bound.hpp | 1 + cpp/src/dual_simplex/phase2.cpp | 9 +-- .../dual_simplex/simplex_solver_settings.hpp | 5 +- 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 93bf2c2aa6..a1c5d775ef 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -290,6 +290,10 @@ branch_and_bound_t::branch_and_bound_t( template f_t branch_and_bound_t::get_lower_bound() { + if (solving_root_relaxation_.load()) { + f_t root = root_lp_current_lower_bound_.load(); + return std::isfinite(root) ? root : -inf; + } f_t lower_bound = lower_bound_ceiling_.load(); f_t heap_lower_bound = node_queue_.get_lower_bound(); lower_bound = std::min(heap_lower_bound, lower_bound); @@ -307,21 +311,21 @@ f_t branch_and_bound_t::get_lower_bound() template void branch_and_bound_t::report_heuristic(f_t obj) { - if (is_running_) { - f_t user_obj = compute_user_objective(original_lp_, obj); - f_t lower = get_lower_bound(); - f_t user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) : -inf; - if (exploration_stats_.nodes_explored == 0) { - f_t root_progress = root_lp_current_lower_bound_.load(); - const bool is_maximization = original_lp_.obj_scale < 0.0; - if (std::isfinite(root_progress) && - (!std::isfinite(user_lower) || - (is_maximization ? root_progress < user_lower : root_progress > user_lower))) { - user_lower = root_progress; - } + const f_t user_obj = compute_user_objective(original_lp_, obj); + f_t lower = get_lower_bound(); + f_t user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) : -inf; + if (!solving_root_relaxation_.load() && exploration_stats_.nodes_explored == 0) { + f_t root_progress = root_lp_current_lower_bound_.load(); + const bool is_maximization = original_lp_.obj_scale < 0.0; + if (std::isfinite(root_progress) && + (!std::isfinite(user_lower) || + (is_maximization ? root_progress < user_lower : root_progress > user_lower))) { + user_lower = root_progress; } - std::string user_gap = user_mip_gap(user_obj, user_lower); + } + if (is_running_) { + std::string user_gap = user_mip_gap(user_obj, user_lower); settings_.log.printf( "H %+13.6e %+10.6e %s %9.2f\n", user_obj, @@ -329,10 +333,6 @@ void branch_and_bound_t::report_heuristic(f_t obj) user_gap.c_str(), toc(exploration_stats_.start_time)); } else { - f_t user_obj = compute_user_objective(original_lp_, obj); - f_t lower = get_lower_bound(); - f_t user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) - : root_lp_current_lower_bound_.load(); std::string gap_str = std::isfinite(user_lower) ? (". Gap: " + user_mip_gap(user_obj, user_lower)) : ""; settings_.log.printf("New solution from primal heuristics. Objective %+.6e%s. Time %.2f\n", @@ -1961,6 +1961,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut log.log_prefix = settings_.log.log_prefix; solver_status_ = mip_status_t::UNSET; is_running_ = false; + solving_root_relaxation_ = false; lower_bound_ceiling_ = inf; root_lp_current_lower_bound_ = -inf; root_objective_ = std::numeric_limits::quiet_NaN(); @@ -1988,12 +1989,13 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_relax_soln_.resize(original_lp_.num_rows, original_lp_.num_cols); - i_t original_rows = original_lp_.num_rows; - simplex_solver_settings_t lp_settings = settings_; - lp_settings.inside_mip = 1; - lp_settings.scale_columns = false; - lp_settings.concurrent_halt = get_root_concurrent_halt(); - lp_settings.root_lp_progress_callback = [this](f_t user_obj) { + solving_root_relaxation_ = true; + i_t original_rows = original_lp_.num_rows; + simplex_solver_settings_t lp_settings = settings_; + lp_settings.inside_mip = 1; + lp_settings.scale_columns = false; + lp_settings.concurrent_halt = get_root_concurrent_halt(); + lp_settings.dual_simplex_objective_callback = [this](f_t user_obj) { root_lp_current_lower_bound_.store(user_obj); }; std::vector basic_list(original_lp_.num_rows); @@ -2027,6 +2029,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut exploration_stats_.total_lp_solve_time = toc(exploration_stats_.start_time); if (root_status == lp_status_t::INFEASIBLE) { + solving_root_relaxation_ = false; settings_.log.printf("MIP Infeasible\n"); // FIXME: rarely dual simplex detects infeasible whereas it is feasible. // to add a small safety net, check if there is a primal solution already. @@ -2037,6 +2040,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut return mip_status_t::INFEASIBLE; } if (root_status == lp_status_t::UNBOUNDED) { + solving_root_relaxation_ = false; settings_.log.printf("MIP Unbounded\n"); if (settings_.heuristic_preemption_callback != nullptr) { settings_.heuristic_preemption_callback(); @@ -2044,19 +2048,22 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut return mip_status_t::UNBOUNDED; } if (root_status == lp_status_t::TIME_LIMIT) { - solver_status_ = mip_status_t::TIME_LIMIT; + solving_root_relaxation_ = false; + solver_status_ = mip_status_t::TIME_LIMIT; set_final_solution(solution, -inf); return solver_status_; } if (root_status == lp_status_t::WORK_LIMIT) { - solver_status_ = mip_status_t::WORK_LIMIT; + solving_root_relaxation_ = false; + solver_status_ = mip_status_t::WORK_LIMIT; set_final_solution(solution, -inf); return solver_status_; } if (root_status == lp_status_t::NUMERICAL_ISSUES) { - solver_status_ = mip_status_t::NUMERICAL; + solving_root_relaxation_ = false; + solver_status_ = mip_status_t::NUMERICAL; set_final_solution(solution, -inf); return solver_status_; } @@ -2087,6 +2094,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut cut_info_t cut_info; if (num_fractional == 0) { + solving_root_relaxation_ = false; set_solution_at_root(solution, cut_info); return mip_status_t::OPTIMAL; } @@ -2117,6 +2125,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut i_t cut_pool_size = 0; for (i_t cut_pass = 0; cut_pass < settings_.max_cut_passes; cut_pass++) { if (num_fractional == 0) { + solving_root_relaxation_ = false; set_solution_at_root(solution, cut_info); return mip_status_t::OPTIMAL; } else { @@ -2278,7 +2287,8 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut settings_.log.debug("Dual phase2 time %.2f seconds\n", dual_phase2_time); } if (cut_status == dual::status_t::TIME_LIMIT) { - solver_status_ = mip_status_t::TIME_LIMIT; + solving_root_relaxation_ = false; + solver_status_ = mip_status_t::TIME_LIMIT; set_final_solution(solution, root_objective_); return solver_status_; } @@ -2301,6 +2311,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut exploration_stats_.total_lp_iters += root_relax_soln_.iterations; root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); } else { + solving_root_relaxation_ = false; settings_.log.printf("Cut status %s\n", dual::status_to_string(cut_status).c_str()); return mip_status_t::NUMERICAL; } @@ -2343,6 +2354,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut f_t rel_gap = user_relative_gap(original_lp_, upper_bound_.load(), root_objective_); f_t abs_gap = upper_bound_.load() - root_objective_; if (rel_gap < settings_.relative_mip_gap_tol || abs_gap < settings_.absolute_mip_gap_tol) { + solving_root_relaxation_ = false; set_solution_at_root(solution, cut_info); set_final_solution(solution, root_objective_); return mip_status_t::OPTIMAL; @@ -2362,6 +2374,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } } + solving_root_relaxation_ = false; print_cut_info(settings_, cut_info); if (cut_info.has_cuts()) { diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index ec73792502..8b083acd6e 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -191,6 +191,7 @@ class branch_and_bound_t { std::vector edge_norms_; std::atomic root_crossover_solution_set_{false}; omp_atomic_t root_lp_current_lower_bound_; + omp_atomic_t solving_root_relaxation_{false}; bool enable_concurrent_lp_root_solve_{false}; std::atomic root_concurrent_halt_{0}; bool is_root_solution_set{false}; diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 07fc944333..111500fbee 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -3510,19 +3510,20 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, if ((iter - start_iter) < settings.first_iteration_log || (iter % settings.iteration_log_frequency) == 0) { + const f_t user_obj = compute_user_objective(lp, obj); if (phase == 1 && iter == 1) { settings.log.printf(" Iter Objective Num Inf. Sum Inf. Perturb Time\n"); } settings.log.printf("%5d %+.16e %7d %.8e %.2e %.2f\n", iter, - compute_user_objective(lp, obj), + user_obj, infeasibility_indices.size(), primal_infeasibility_squared, sum_perturb, now); - } - if (phase == 2 && settings.inside_mip == 1 && settings.root_lp_progress_callback) { - settings.root_lp_progress_callback(compute_user_objective(lp, obj)); + if (phase == 2 && settings.inside_mip == 1 && settings.dual_simplex_objective_callback) { + settings.dual_simplex_objective_callback(user_obj); + } } if (obj >= settings.cut_off) { diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index bdf392d813..3f04f8b0a6 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -110,7 +110,7 @@ struct simplex_solver_settings_t { sub_mip(0), solution_callback(nullptr), heuristic_preemption_callback(nullptr), - root_lp_progress_callback(nullptr), + dual_simplex_objective_callback(nullptr), concurrent_halt(nullptr) { } @@ -203,8 +203,7 @@ struct simplex_solver_settings_t { std::function&, f_t)> node_processed_callback; std::function heuristic_preemption_callback; std::function&, std::vector&, f_t)> set_simplex_solution_callback; - std::function - root_lp_progress_callback; // Called with current dual obj during root LP + std::function dual_simplex_objective_callback; // Called with current dual obj mutable logger_t log; std::atomic* concurrent_halt; // if nullptr ignored, if !nullptr, 0 if solver should // continue, 1 if solver should halt From 2b211181912fa6629883a3c90eb2fbe67e820ac8 Mon Sep 17 00:00:00 2001 From: Ishika Roy <41401566+Iroy30@users.noreply.github.com> Date: Mon, 9 Mar 2026 19:47:17 -0500 Subject: [PATCH 160/225] update exceptions and guards (#912) Closes #870 ## Issue Authors: - Ishika Roy (https://github.com/Iroy30) Approvers: - Trevor McKay (https://github.com/tmckayus) URL: https://github.com/NVIDIA/cuopt/pull/912 --- .../cuopt/cuopt/linear_programming/problem.py | 8 ++- .../linear_programming/solution/solution.py | 65 ++++++++++++------- .../utils/linear_programming/solver.py | 6 +- 3 files changed, 51 insertions(+), 28 deletions(-) diff --git a/python/cuopt/cuopt/linear_programming/problem.py b/python/cuopt/cuopt/linear_programming/problem.py index 80cb83ee6f..5821fd3a93 100644 --- a/python/cuopt/cuopt/linear_programming/problem.py +++ b/python/cuopt/cuopt/linear_programming/problem.py @@ -1910,7 +1910,11 @@ def relax(self): def populate_solution(self, solution): self.Status = solution.get_termination_status() self.SolveTime = solution.get_solve_time() - self.warmstart_data = solution.get_pdlp_warm_start_data() + self.warmstart_data = ( + solution.get_pdlp_warm_start_data() + if solution.problem_category == 0 + else None + ) IsMIP = False if solution.problem_category == 0: @@ -1919,7 +1923,7 @@ def populate_solution(self, solution): IsMIP = True self.SolutionStats = self.dict_to_object(solution.get_milp_stats()) primal_sol = solution.get_primal_solution() - reduced_cost = solution.get_reduced_cost() + reduced_cost = solution.get_reduced_cost() if not IsMIP else None if len(primal_sol) > 0: for var in self.vars: var.Value = primal_sol[var.index] diff --git a/python/cuopt/cuopt/linear_programming/solution/solution.py b/python/cuopt/cuopt/linear_programming/solution/solution.py index 849c907d06..e2533da8c1 100644 --- a/python/cuopt/cuopt/linear_programming/solution/solution.py +++ b/python/cuopt/cuopt/linear_programming/solution/solution.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 from cuopt.linear_programming.solver.solver_wrapper import ( @@ -167,25 +167,28 @@ def __init__( self.problem_category = problem_category self.primal_solution = primal_solution self.dual_solution = dual_solution - self.pdlp_warm_start_data = PDLPWarmStartData( - current_primal_solution, - current_dual_solution, - initial_primal_average, - initial_dual_average, - current_ATY, - sum_primal_solutions, - sum_dual_solutions, - last_restart_duality_gap_primal_solution, - last_restart_duality_gap_dual_solution, - initial_primal_weight, - initial_step_size, - total_pdlp_iterations, - total_pdhg_iterations, - last_candidate_kkt_score, - last_restart_kkt_score, - sum_solution_weight, - iterations_since_last_restart, - ) + if problem_category == ProblemCategory.LP: + self.pdlp_warm_start_data = PDLPWarmStartData( + current_primal_solution, + current_dual_solution, + initial_primal_average, + initial_dual_average, + current_ATY, + sum_primal_solutions, + sum_dual_solutions, + last_restart_duality_gap_primal_solution, + last_restart_duality_gap_dual_solution, + initial_primal_weight, + initial_step_size, + total_pdlp_iterations, + total_pdhg_iterations, + last_candidate_kkt_score, + last_restart_kkt_score, + sum_solution_weight, + iterations_since_last_restart, + ) + else: + self.pdlp_warm_start_data = None self._set_termination_status(termination_status) self.error_status = error_status self.error_message = error_message @@ -216,8 +219,17 @@ def __init__( def _set_termination_status(self, ts): if self.problem_category == ProblemCategory.LP: self.termination_status = LPTerminationStatus(ts) - else: + elif self.problem_category in ( + ProblemCategory.MIP, + ProblemCategory.IP, + ): self.termination_status = MILPTerminationStatus(ts) + else: + raise ValueError( + f"Unknown problem_category: {self.problem_category!r}. " + "Expected one of ProblemCategory.LP, ProblemCategory.MIP, " + "ProblemCategory.IP." + ) def raise_if_milp_solution(self, function_name): if self.problem_category in (ProblemCategory.MIP, ProblemCategory.IP): @@ -242,7 +254,7 @@ def get_dual_solution(self): Note: Applicable to only LP Returns the dual solution as numpy.array with float64 type. """ - self.raise_if_milp_solution(__name__) + self.raise_if_milp_solution("get_dual_solution") return self.dual_solution def get_primal_objective(self): @@ -256,7 +268,7 @@ def get_dual_objective(self): Note: Applicable to only LP Returns the dual objective as a float64. """ - self.raise_if_milp_solution(__name__) + self.raise_if_milp_solution("get_dual_objective") return self.dual_objective def get_termination_status(self): @@ -325,14 +337,16 @@ def get_lp_stats(self): Number of iterations the LP solver did before converging. """ - self.raise_if_milp_solution(__name__) + self.raise_if_milp_solution("get_lp_stats") return self.lp_stats def get_reduced_cost(self): """ + Note: Applicable to only LP Returns the reduced cost as numpy.array with float64 type. """ + self.raise_if_milp_solution("get_reduced_cost") return self.reduced_cost def get_pdlp_warm_start_data(self): @@ -343,6 +357,7 @@ def get_pdlp_warm_start_data(self): See `SolverSettings.set_pdlp_warm_start_data` for more details. """ + self.raise_if_milp_solution("get_pdlp_warm_start_data") return self.pdlp_warm_start_data def get_milp_stats(self): @@ -386,7 +401,7 @@ def get_milp_stats(self): Number of simplex iterations performed during the MIP solve """ - self.raise_if_lp_solution(__name__) + self.raise_if_lp_solution("get_milp_stats") return self.milp_stats diff --git a/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py b/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py index 6eeaafbde5..87524f8715 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/solver.py @@ -304,6 +304,9 @@ def create_solution(sol): milp_stats = get_if_attribute_is_valid_else_none( sol.get_milp_stats ) + pdlpwarmstart_data = get_if_attribute_is_valid_else_none( + sol.get_pdlp_warm_start_data + ) solution["problem_category"] = sol.get_problem_category().name solution["primal_solution"] = primal_solution solution["dual_solution"] = dual_solution @@ -318,8 +321,9 @@ def create_solution(sol): solution["vars"] = sol.get_vars() solution["lp_statistics"] = {} if lp_stats is None else lp_stats solution["reduced_cost"] = reduced_cost + solution["pdlpwarmstart_data"] = extract_pdlpwarmstart_data( - sol.get_pdlp_warm_start_data() + pdlpwarmstart_data ) solution["milp_statistics"] = ( {} if milp_stats is None else milp_stats From 8e9a2fc69fb5f4b5652477ac3a25cabd8eebc3a7 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 10 Mar 2026 07:31:57 -0700 Subject: [PATCH 161/225] B&B bugfixing --- cpp/src/branch_and_bound/branch_and_bound.cpp | 83 ++++++++++--------- .../deterministic_workers.hpp | 10 +-- cpp/src/branch_and_bound/pseudo_costs.cpp | 20 +++++ .../diversity/diversity_manager.cu | 5 ++ 4 files changed, 71 insertions(+), 47 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 65827fd188..24c58a2bf7 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -26,6 +26,7 @@ #include #include +#include #include @@ -2184,6 +2185,11 @@ template mip_status_t branch_and_bound_t::solve(mip_solution_t& solution) { raft::common::nvtx::range scope("BB::solve"); + auto heuristic_preemption_guard = cuopt::scope_guard([this]() { + if (settings_.heuristic_preemption_callback != nullptr) { + settings_.heuristic_preemption_callback(); + } + }); logger_t log; log.log = false; @@ -3453,6 +3459,14 @@ void branch_and_bound_t::deterministic_sync_callback() replay_solutions); deterministic_collect_diving_solutions_and_update_pseudocosts(replay_solutions); + if (deterministic_diving_workers_) { + for (auto& worker : *deterministic_diving_workers_) { + i_t delta = worker.total_nodes_explored - worker.nodes_explored_last_sync; + worker.nodes_explored_last_sync = worker.total_nodes_explored; + exploration_stats_.nodes_explored += delta; + } + } + deterministic_sort_replay_events(all_events, replay_solutions); // deterministic_prune_worker_nodes_vs_incumbent(); @@ -3600,9 +3614,15 @@ node_status_t branch_and_bound_t::solve_node_deterministic( simplex_solver_settings_t lp_settings = settings_; lp_settings.set_log(false); - lp_settings.cut_off = worker.local_upper_bound + settings_.dual_tol; + if (original_lp_.objective_is_integral) { + lp_settings.cut_off = + std::ceil(worker.local_upper_bound - settings_.integer_tol) + settings_.dual_tol; + } else { + lp_settings.cut_off = worker.local_upper_bound + settings_.dual_tol; + } lp_settings.inside_mip = 2; lp_settings.time_limit = remaining_time; + lp_settings.work_limit = std::numeric_limits::infinity(); lp_settings.scale_columns = false; bool feasible = true; @@ -3612,7 +3632,6 @@ node_status_t branch_and_bound_t::solve_node_deterministic( lp_settings, worker.bounds_changed, worker.leaf_problem.lower, worker.leaf_problem.upper); if (settings_.deterministic) { - // TEMP APPROXIMATION; worker.work_context.record_work_sync_on_horizon(worker.node_presolver.last_nnz_processed / 1e8); } #endif @@ -4057,34 +4076,12 @@ void branch_and_bound_t::deterministic_balance_worker_loads() const size_t num_workers = deterministic_workers_->size(); if (num_workers <= 1) return; - constexpr bool force_rebalance_every_sync = false; - - // Count work for each worker: current_node (if any) + plunge_stack + backlog - std::vector work_counts(num_workers); - size_t total_work = 0; - size_t max_work = 0; - size_t min_work = std::numeric_limits::max(); - - for (size_t w = 0; w < num_workers; ++w) { - auto& worker = (*deterministic_workers_)[w]; - work_counts[w] = worker.queue_size(); - total_work += work_counts[w]; - max_work = std::max(max_work, work_counts[w]); - min_work = std::min(min_work, work_counts[w]); - } - if (total_work == 0) return; - - bool needs_balance; - if (force_rebalance_every_sync) { - needs_balance = (total_work > 1); - } else { - needs_balance = (min_work == 0 && max_work >= 2) || (min_work > 0 && max_work > 4 * min_work); - } - - if (!needs_balance) return; - std::vector*> all_nodes; for (auto& worker : *deterministic_workers_) { + for (auto* node : worker.plunge_stack) { + all_nodes.push_back(node); + } + worker.plunge_stack.clear(); for (auto* node : worker.backlog.data()) { all_nodes.push_back(node); } @@ -4093,16 +4090,17 @@ void branch_and_bound_t::deterministic_balance_worker_loads() if (all_nodes.empty()) return; - auto deterministic_less = [](const mip_node_t* a, const mip_node_t* b) { - if (a->origin_worker_id != b->origin_worker_id) { - return a->origin_worker_id < b->origin_worker_id; - } - return a->creation_seq < b->creation_seq; - }; - std::sort(all_nodes.begin(), all_nodes.end(), deterministic_less); - - // Distribute nodes - for (size_t i = 0; i < all_nodes.size(); ++i) { + std::sort(all_nodes.begin(), + all_nodes.end(), + [](const mip_node_t* a, const mip_node_t* b) { + if (a->lower_bound != b->lower_bound) { return a->lower_bound < b->lower_bound; } + if (a->origin_worker_id != b->origin_worker_id) { + return a->origin_worker_id < b->origin_worker_id; + } + return a->creation_seq < b->creation_seq; + }); + + for (int i = (int)all_nodes.size() - 1; i >= 0; --i) { size_t worker_idx = i % num_workers; (*deterministic_workers_)[worker_idx].enqueue_node(all_nodes[i]); } @@ -4314,9 +4312,15 @@ void branch_and_bound_t::deterministic_dive( // Setup LP settings simplex_solver_settings_t lp_settings = settings_; lp_settings.set_log(false); - lp_settings.cut_off = worker.local_upper_bound + settings_.dual_tol; + if (original_lp_.objective_is_integral) { + lp_settings.cut_off = + std::ceil(worker.local_upper_bound - settings_.integer_tol) + settings_.dual_tol; + } else { + lp_settings.cut_off = worker.local_upper_bound + settings_.dual_tol; + } lp_settings.inside_mip = 2; lp_settings.time_limit = remaining_time; + lp_settings.work_limit = std::numeric_limits::infinity(); lp_settings.scale_columns = false; #ifndef DETERMINISM_DISABLE_BOUNDS_STRENGTHENING @@ -4324,7 +4328,6 @@ void branch_and_bound_t::deterministic_dive( lp_settings, worker.bounds_changed, worker.leaf_problem.lower, worker.leaf_problem.upper); if (settings_.deterministic) { - // TEMP APPROXIMATION; worker.work_context.record_work_sync_on_horizon(worker.node_presolver.last_nnz_processed / 1e8); } diff --git a/cpp/src/branch_and_bound/deterministic_workers.hpp b/cpp/src/branch_and_bound/deterministic_workers.hpp index a73c10301f..2e94c13a3a 100644 --- a/cpp/src/branch_and_bound/deterministic_workers.hpp +++ b/cpp/src/branch_and_bound/deterministic_workers.hpp @@ -161,11 +161,6 @@ class deterministic_bfs_worker_t mip_node_t* up_child, rounding_direction_t preferred_direction) { - if (!plunge_stack.empty()) { - backlog.push(plunge_stack.back()); - plunge_stack.pop_back(); - } - down_child->origin_worker_id = this->worker_id; down_child->creation_seq = next_creation_seq++; up_child->origin_worker_id = this->worker_id; @@ -173,11 +168,11 @@ class deterministic_bfs_worker_t mip_node_t* first_child; if (preferred_direction == rounding_direction_t::UP) { - plunge_stack.push_front(down_child); + backlog.push(down_child); plunge_stack.push_front(up_child); first_child = up_child; } else { - plunge_stack.push_front(up_child); + backlog.push(up_child); plunge_stack.push_front(down_child); first_child = down_child; } @@ -291,6 +286,7 @@ class deterministic_diving_worker_t // Diving statistics i_t total_nodes_explored{0}; + i_t nodes_explored_last_sync{0}; i_t total_dives{0}; i_t lp_iters_this_dive{0}; diff --git a/cpp/src/branch_and_bound/pseudo_costs.cpp b/cpp/src/branch_and_bound/pseudo_costs.cpp index b26d185598..67c185bf27 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.cpp +++ b/cpp/src/branch_and_bound/pseudo_costs.cpp @@ -639,6 +639,20 @@ i_t pseudo_costs_t::reliable_variable_selection( assert(num_candidates > 0); assert(num_tasks > 0); + settings.log.debug( + "Reliability branching: node=%d depth=%d fractional=%zu unreliable=%zu candidates=%d " + "threshold=%d sb_iters=%d bnb_iters=%lld explored=%lld tasks=%d\n", + node_ptr->node_id, + node_ptr->depth, + fractional.size(), + unreliable_list.size(), + num_candidates, + reliable_threshold, + strong_branching_lp_iter.load(), + (long long)branch_and_bound_lp_iters, + (long long)branch_and_bound_explored, + num_tasks); + log.printf( "RB iters = %d, B&B iters = %d, unreliable = %d, num_tasks = %d, reliable_threshold = %d\n", strong_branching_lp_iter.load(), @@ -735,6 +749,12 @@ i_t pseudo_costs_t::reliable_variable_selection( score_mutex.unlock(); } + settings.log.debug("Reliability branching result: node=%d branch_var=%d value=%e score=%e\n", + node_ptr->node_id, + branch_var, + solution[branch_var], + max_score); + log.printf( "pc branching on %d. Value %e. Score %e\n", branch_var, solution[branch_var], max_score); diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 865b6dca50..36370bc945 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -411,6 +411,11 @@ solution_t diversity_manager_t::run_solver() const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); + if (is_deterministic_mode(context.settings.determinism_mode) && + context.branch_and_bound_ptr != nullptr) { + auto& producer_sync = context.branch_and_bound_ptr->get_producer_sync(); + producer_sync.registration_complete(); + } population.initialize_population(); population.allocate_solutions(); From c2179229a1e95de9348711f42c5a8b1ddece3c71 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 10 Mar 2026 08:10:49 -0700 Subject: [PATCH 162/225] reliability branching for determinism --- cpp/src/branch_and_bound/branch_and_bound.cpp | 55 +- cpp/src/branch_and_bound/pseudo_costs.cpp | 493 +++++++++++------- cpp/src/branch_and_bound/pseudo_costs.hpp | 37 +- 3 files changed, 381 insertions(+), 204 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 24c58a2bf7..3ee8546610 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -45,11 +45,11 @@ #include // uncomment to enable detailed detemrinism logs -#undef CUOPT_DETERMINISM_LOG_PRINTF -#define CUOPT_DETERMINISM_LOG_PRINTF(logger, ...) \ - do { \ - logger.printf(__VA_ARGS__); \ - } while (0) +// #undef CUOPT_DETERMINISM_LOG_PRINTF +// #define CUOPT_DETERMINISM_LOG_PRINTF(logger, ...) \ +// do { \ +// logger.printf(__VA_ARGS__); \ +// } while (0) namespace cuopt::linear_programming::dual_simplex { @@ -1200,11 +1200,41 @@ struct deterministic_bfs_policy_t } } - branch_variable_t select_branch_variable(mip_node_t*, + branch_variable_t select_branch_variable(mip_node_t* node, const std::vector& fractional, const std::vector& x) override { - i_t var = this->worker.pc_snapshot.variable_selection(fractional, x); + i_t var; + if (this->bnb.settings_.reliability_branching != 0 && + this->bnb.exploration_stats_.nodes_explored > 0) { + var = reliable_variable_selection_core(node, + fractional, + x, + this->bnb.settings_, + this->bnb.var_types_, + this->worker.leaf_problem, + this->worker.leaf_edge_norms, + this->worker.basis_factors, + this->worker.basic_list, + this->worker.nonbasic_list, + this->worker.pc_snapshot.sum_down_.data(), + this->worker.pc_snapshot.sum_up_.data(), + this->worker.pc_snapshot.num_down_.data(), + this->worker.pc_snapshot.num_up_.data(), + this->worker.pc_snapshot.n_vars(), + this->worker.pc_snapshot.strong_branching_lp_iter_, + this->worker.local_upper_bound, + this->bnb.exploration_stats_.total_lp_iters, + this->bnb.exploration_stats_.nodes_explored, + this->bnb.exploration_stats_.start_time, + this->bnb.pc_.reliability_branching_settings, + 1, + nullptr, + nullptr, + nullptr); + } else { + var = this->worker.pc_snapshot.variable_selection(fractional, x); + } auto dir = martin_criteria(x[var], this->bnb.root_relax_soln_.x[var]); return {var, dir}; } @@ -3651,7 +3681,7 @@ node_status_t branch_and_bound_t::solve_node_deterministic( std::vector& leaf_vstatus = node_ptr->vstatus; i_t node_iter = 0; f_t lp_start_time = tic(); - std::vector leaf_edge_norms = edge_norms_; + worker.leaf_edge_norms = edge_norms_; dual::status_t lp_status = dual_phase2_with_advanced_basis(2, 0, @@ -3665,7 +3695,7 @@ node_status_t branch_and_bound_t::solve_node_deterministic( worker.nonbasic_list, worker.leaf_solution, node_iter, - leaf_edge_norms, + worker.leaf_edge_norms, &worker.work_context); if (lp_status == dual::status_t::NUMERICAL) { @@ -3678,7 +3708,7 @@ node_status_t branch_and_bound_t::solve_node_deterministic( worker.basic_list, worker.nonbasic_list, leaf_vstatus, - leaf_edge_norms, + worker.leaf_edge_norms, &worker.work_context); lp_status = convert_lp_status_to_dual_status(second_status); } @@ -3741,12 +3771,17 @@ template void branch_and_bound_t::deterministic_merge_pseudo_cost_updates(PoolT& pool) { std::vector> all_pc_updates; + int64_t sb_iter_delta = 0; for (auto& worker : pool) { auto updates = worker.pc_snapshot.take_updates(); all_pc_updates.insert(all_pc_updates.end(), updates.begin(), updates.end()); + int64_t snapshot_sb = worker.pc_snapshot.strong_branching_lp_iter_; + int64_t base_sb = pc_.strong_branching_lp_iter.load(); + if (snapshot_sb > base_sb) { sb_iter_delta += snapshot_sb - base_sb; } } std::sort(all_pc_updates.begin(), all_pc_updates.end()); pc_.merge_updates(all_pc_updates); + if (sb_iter_delta > 0) { pc_.strong_branching_lp_iter += sb_iter_delta; } } template diff --git a/cpp/src/branch_and_bound/pseudo_costs.cpp b/cpp/src/branch_and_bound/pseudo_costs.cpp index 67c185bf27..4b0aef022d 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.cpp +++ b/cpp/src/branch_and_bound/pseudo_costs.cpp @@ -218,8 +218,239 @@ f_t trial_branching(const lp_problem_t& original_lp, } } +// Overload of trial_branching that takes a plain int64_t& counter (for deterministic/serial use). +template +f_t trial_branching_plain(const lp_problem_t& original_lp, + const simplex_solver_settings_t& settings, + const std::vector& var_types, + const std::vector& vstatus, + const std::vector& edge_norms, + const basis_update_mpf_t& basis_factors, + const std::vector& basic_list, + const std::vector& nonbasic_list, + i_t branch_var, + f_t branch_var_lower, + f_t branch_var_upper, + f_t upper_bound, + i_t bnb_lp_iter_per_node, + f_t start_time, + i_t upper_max_lp_iter, + i_t lower_max_lp_iter, + int64_t& total_lp_iter) +{ + omp_atomic_t atomic_iter{0}; + f_t result = trial_branching(original_lp, + settings, + var_types, + vstatus, + edge_norms, + basis_factors, + basic_list, + nonbasic_list, + branch_var, + branch_var_lower, + branch_var_upper, + upper_bound, + bnb_lp_iter_per_node, + start_time, + upper_max_lp_iter, + lower_max_lp_iter, + atomic_iter); + total_lp_iter += atomic_iter.load(); + return result; +} + } // namespace +template +i_t reliable_variable_selection_core(mip_node_t* node_ptr, + const std::vector& fractional, + const std::vector& solution, + const simplex_solver_settings_t& settings, + const std::vector& var_types, + const lp_problem_t& leaf_problem, + const std::vector& edge_norms, + const basis_update_mpf_t& basis_factors, + const std::vector& basic_list, + const std::vector& nonbasic_list, + f_t* sum_down, + f_t* sum_up, + i_t* num_down, + i_t* num_up, + i_t n_vars, + int64_t& strong_branching_lp_iter, + f_t upper_bound, + int64_t bnb_lp_iters, + int64_t bnb_nodes_explored, + f_t start_time, + const reliability_branching_settings_t& rb_settings, + int num_tasks, + omp_mutex_t* var_mutex_down, + omp_mutex_t* var_mutex_up, + pcgenerator_t* rng) +{ + constexpr f_t eps = 1e-6; + i_t branch_var = fractional[0]; + f_t max_score = -1; + + auto avgs = compute_pseudo_cost_averages(sum_down, sum_up, num_down, num_up, (size_t)n_vars); + f_t pseudo_cost_down_avg = avgs.down_avg; + f_t pseudo_cost_up_avg = avgs.up_avg; + + const i_t bnb_lp_iter_per_node = + bnb_nodes_explored > 0 ? (i_t)(bnb_lp_iters / bnb_nodes_explored) : 0; + + i_t reliable_threshold = settings.reliability_branching; + if (reliable_threshold < 0) { + const int64_t alpha = (int64_t)(rb_settings.bnb_lp_factor * bnb_lp_iters); + const int64_t max_reliability_iter = alpha + rb_settings.bnb_lp_offset; + + f_t iter_fraction = + (max_reliability_iter - strong_branching_lp_iter) / (strong_branching_lp_iter + 1.0); + iter_fraction = std::min(1.0, iter_fraction); + iter_fraction = std::max((alpha - strong_branching_lp_iter) / (strong_branching_lp_iter + 1.0), + iter_fraction); + reliable_threshold = (int)((1 - iter_fraction) * rb_settings.min_reliable_threshold + + iter_fraction * rb_settings.max_reliable_threshold); + reliable_threshold = strong_branching_lp_iter < max_reliability_iter ? reliable_threshold : 0; + } + + std::vector unreliable_list; + + for (i_t j : fractional) { + if (num_down[j] < reliable_threshold || num_up[j] < reliable_threshold) { + unreliable_list.push_back(j); + continue; + } + f_t pc_down = num_down[j] > 0 ? sum_down[j] / num_down[j] : pseudo_cost_down_avg; + f_t pc_up = num_up[j] > 0 ? sum_up[j] / num_up[j] : pseudo_cost_up_avg; + f_t f_down = solution[j] - std::floor(solution[j]); + f_t f_up = std::ceil(solution[j]) - solution[j]; + f_t score = std::max(f_down * pc_down, eps) * std::max(f_up * pc_up, eps); + if (score > max_score) { + max_score = score; + branch_var = j; + } + } + + if (unreliable_list.empty()) { return branch_var; } + + const i_t max_num_candidates = rb_settings.max_num_candidates; + const i_t num_candidates = std::min(unreliable_list.size(), max_num_candidates); + + settings.log.debug( + "Reliability branching: node=%d depth=%d fractional=%zu unreliable=%zu candidates=%d " + "threshold=%d sb_iters=%lld bnb_iters=%lld explored=%lld tasks=%d\n", + node_ptr->node_id, + node_ptr->depth, + fractional.size(), + unreliable_list.size(), + num_candidates, + reliable_threshold, + (long long)strong_branching_lp_iter, + (long long)bnb_lp_iters, + (long long)bnb_nodes_explored, + num_tasks); + + if (rng != nullptr && unreliable_list.size() > (size_t)max_num_candidates) { + rng->shuffle(unreliable_list); + } + + if (toc(start_time) > settings.time_limit) { return branch_var; } + + omp_mutex_t score_mutex; + const int task_priority = rb_settings.task_priority; + +#pragma omp taskloop if (num_tasks > 1) priority(task_priority) num_tasks(num_tasks) \ + shared(score_mutex) + for (i_t i = 0; i < num_candidates; ++i) { + const i_t j = unreliable_list[i]; + + if (toc(start_time) > settings.time_limit) { continue; } + + if (var_mutex_down) { var_mutex_down[j].lock(); } + if (num_down[j] < reliable_threshold) { + f_t obj = trial_branching_plain(leaf_problem, + settings, + var_types, + node_ptr->vstatus, + edge_norms, + basis_factors, + basic_list, + nonbasic_list, + j, + leaf_problem.lower[j], + std::floor(solution[j]), + upper_bound, + bnb_lp_iter_per_node, + start_time, + rb_settings.upper_max_lp_iter, + rb_settings.lower_max_lp_iter, + strong_branching_lp_iter); + if (!std::isnan(obj)) { + f_t change_in_obj = std::max(obj - node_ptr->lower_bound, eps); + f_t change_in_x = solution[j] - std::floor(solution[j]); + sum_down[j] += change_in_obj / change_in_x; + num_down[j]++; + } + } + if (var_mutex_down) { var_mutex_down[j].unlock(); } + + if (toc(start_time) > settings.time_limit) { continue; } + + if (var_mutex_up) { var_mutex_up[j].lock(); } + if (num_up[j] < reliable_threshold) { + f_t obj = trial_branching_plain(leaf_problem, + settings, + var_types, + node_ptr->vstatus, + edge_norms, + basis_factors, + basic_list, + nonbasic_list, + j, + std::ceil(solution[j]), + leaf_problem.upper[j], + upper_bound, + bnb_lp_iter_per_node, + start_time, + rb_settings.upper_max_lp_iter, + rb_settings.lower_max_lp_iter, + strong_branching_lp_iter); + if (!std::isnan(obj)) { + f_t change_in_obj = std::max(obj - node_ptr->lower_bound, eps); + f_t change_in_x = std::ceil(solution[j]) - solution[j]; + sum_up[j] += change_in_obj / change_in_x; + num_up[j]++; + } + } + if (var_mutex_up) { var_mutex_up[j].unlock(); } + + if (toc(start_time) > settings.time_limit) { continue; } + + f_t pc_down = num_down[j] > 0 ? sum_down[j] / num_down[j] : pseudo_cost_down_avg; + f_t pc_up = num_up[j] > 0 ? sum_up[j] / num_up[j] : pseudo_cost_up_avg; + f_t f_down = solution[j] - std::floor(solution[j]); + f_t f_up = std::ceil(solution[j]) - solution[j]; + f_t score = std::max(f_down * pc_down, eps) * std::max(f_up * pc_up, eps); + + score_mutex.lock(); + if (score > max_score) { + max_score = score; + branch_var = j; + } + score_mutex.unlock(); + } + + settings.log.debug("Reliability branching result: node=%d branch_var=%d value=%e score=%e\n", + node_ptr->node_id, + branch_var, + solution[branch_var], + max_score); + + return branch_var; +} + template static cuopt::mps_parser::mps_data_model_t simplex_problem_to_mps_data_model( const dual_simplex::user_problem_t& user_problem) @@ -564,201 +795,52 @@ i_t pseudo_costs_t::reliable_variable_selection( int max_num_tasks, logger_t& log) { - constexpr f_t eps = 1e-6; - f_t start_time = bnb_stats.start_time; - i_t branch_var = fractional[0]; - f_t max_score = -1; - i_t num_initialized_down; - i_t num_initialized_up; - f_t pseudo_cost_down_avg; - f_t pseudo_cost_up_avg; - - initialized(num_initialized_down, num_initialized_up, pseudo_cost_down_avg, pseudo_cost_up_avg); - - log.printf("PC: num initialized down %d up %d avg down %e up %e\n", - num_initialized_down, - num_initialized_up, - pseudo_cost_down_avg, - pseudo_cost_up_avg); - - const int64_t branch_and_bound_lp_iters = bnb_stats.total_lp_iters; - const int64_t branch_and_bound_explored = bnb_stats.nodes_explored; - const i_t branch_and_bound_lp_iter_per_node = - branch_and_bound_lp_iters / bnb_stats.nodes_explored; - - i_t reliable_threshold = settings.reliability_branching; - if (reliable_threshold < 0) { - const i_t max_threshold = reliability_branching_settings.max_reliable_threshold; - const i_t min_threshold = reliability_branching_settings.min_reliable_threshold; - const f_t iter_factor = reliability_branching_settings.bnb_lp_factor; - const i_t iter_offset = reliability_branching_settings.bnb_lp_offset; - const int64_t alpha = iter_factor * branch_and_bound_lp_iters; - const int64_t max_reliability_iter = alpha + reliability_branching_settings.bnb_lp_offset; - - f_t iter_fraction = - (max_reliability_iter - strong_branching_lp_iter) / (strong_branching_lp_iter + 1.0); - iter_fraction = std::min(1.0, iter_fraction); - iter_fraction = std::max((alpha - strong_branching_lp_iter) / (strong_branching_lp_iter + 1.0), - iter_fraction); - reliable_threshold = (1 - iter_fraction) * min_threshold + iter_fraction * max_threshold; - reliable_threshold = strong_branching_lp_iter < max_reliability_iter ? reliable_threshold : 0; - } - - std::vector unreliable_list; - omp_mutex_t score_mutex; - - for (i_t j : fractional) { - if (pseudo_cost_num_down[j] < reliable_threshold || - pseudo_cost_num_up[j] < reliable_threshold) { - unreliable_list.push_back(j); - continue; - } - - f_t score = calculate_pseudocost_score(j, solution, pseudo_cost_up_avg, pseudo_cost_down_avg); - - if (score > max_score) { - max_score = score; - branch_var = j; - } + const i_t n = (i_t)pseudo_cost_sum_down.size(); + std::vector sd(n), su(n); + std::vector nd(n), nu(n); + for (i_t j = 0; j < n; ++j) { + sd[j] = pseudo_cost_sum_down[j]; + su[j] = pseudo_cost_sum_up[j]; + nd[j] = pseudo_cost_num_down[j]; + nu[j] = pseudo_cost_num_up[j]; } - - if (unreliable_list.empty()) { - log.printf( - "pc branching on %d. Value %e. Score %e\n", branch_var, solution[branch_var], max_score); - - return branch_var; + int64_t sb_iter = strong_branching_lp_iter.load(); + + i_t result = reliable_variable_selection_core(node_ptr, + fractional, + solution, + settings, + var_types, + worker->leaf_problem, + worker->leaf_edge_norms, + worker->basis_factors, + worker->basic_list, + worker->nonbasic_list, + sd.data(), + su.data(), + nd.data(), + nu.data(), + n, + sb_iter, + upper_bound, + bnb_stats.total_lp_iters, + bnb_stats.nodes_explored, + bnb_stats.start_time, + reliability_branching_settings, + std::max(max_num_tasks, 1), + pseudo_cost_mutex_down.data(), + pseudo_cost_mutex_up.data(), + &worker->rng); + + for (i_t j = 0; j < n; ++j) { + pseudo_cost_sum_down[j] = sd[j]; + pseudo_cost_sum_up[j] = su[j]; + pseudo_cost_num_down[j] = nd[j]; + pseudo_cost_num_up[j] = nu[j]; } + strong_branching_lp_iter = sb_iter; - const int num_tasks = std::max(max_num_tasks, 1); - const int task_priority = reliability_branching_settings.task_priority; - const i_t max_num_candidates = reliability_branching_settings.max_num_candidates; - const i_t num_candidates = std::min(unreliable_list.size(), max_num_candidates); - - assert(task_priority > 0); - assert(max_num_candidates > 0); - assert(num_candidates > 0); - assert(num_tasks > 0); - - settings.log.debug( - "Reliability branching: node=%d depth=%d fractional=%zu unreliable=%zu candidates=%d " - "threshold=%d sb_iters=%d bnb_iters=%lld explored=%lld tasks=%d\n", - node_ptr->node_id, - node_ptr->depth, - fractional.size(), - unreliable_list.size(), - num_candidates, - reliable_threshold, - strong_branching_lp_iter.load(), - (long long)branch_and_bound_lp_iters, - (long long)branch_and_bound_explored, - num_tasks); - - log.printf( - "RB iters = %d, B&B iters = %d, unreliable = %d, num_tasks = %d, reliable_threshold = %d\n", - strong_branching_lp_iter.load(), - branch_and_bound_lp_iters, - unreliable_list.size(), - num_tasks, - reliable_threshold); - - // Shuffle the unreliable list so every variable has the same chance to be selected. - if (unreliable_list.size() > max_num_candidates) { worker->rng.shuffle(unreliable_list); } - - if (toc(start_time) > settings.time_limit) { - log.printf("Time limit reached"); - return branch_var; - } - -#pragma omp taskloop if (num_tasks > 1) priority(task_priority) num_tasks(num_tasks) \ - shared(score_mutex) - for (i_t i = 0; i < num_candidates; ++i) { - const i_t j = unreliable_list[i]; - - if (toc(start_time) > settings.time_limit) { continue; } - - pseudo_cost_mutex_down[j].lock(); - if (pseudo_cost_num_down[j] < reliable_threshold) { - // Do trial branching on the down branch - f_t obj = trial_branching(worker->leaf_problem, - settings, - var_types, - node_ptr->vstatus, - worker->leaf_edge_norms, - worker->basis_factors, - worker->basic_list, - worker->nonbasic_list, - j, - worker->leaf_problem.lower[j], - std::floor(solution[j]), - upper_bound, - branch_and_bound_lp_iter_per_node, - start_time, - reliability_branching_settings.upper_max_lp_iter, - reliability_branching_settings.lower_max_lp_iter, - strong_branching_lp_iter); - - if (!std::isnan(obj)) { - f_t change_in_obj = std::max(obj - node_ptr->lower_bound, eps); - f_t change_in_x = solution[j] - std::floor(solution[j]); - pseudo_cost_sum_down[j] += change_in_obj / change_in_x; - pseudo_cost_num_down[j]++; - } - } - pseudo_cost_mutex_down[j].unlock(); - - if (toc(start_time) > settings.time_limit) { continue; } - - pseudo_cost_mutex_up[j].lock(); - if (pseudo_cost_num_up[j] < reliable_threshold) { - f_t obj = trial_branching(worker->leaf_problem, - settings, - var_types, - node_ptr->vstatus, - worker->leaf_edge_norms, - worker->basis_factors, - worker->basic_list, - worker->nonbasic_list, - j, - std::ceil(solution[j]), - worker->leaf_problem.upper[j], - upper_bound, - branch_and_bound_lp_iter_per_node, - start_time, - reliability_branching_settings.upper_max_lp_iter, - reliability_branching_settings.lower_max_lp_iter, - strong_branching_lp_iter); - - if (!std::isnan(obj)) { - f_t change_in_obj = std::max(obj - node_ptr->lower_bound, eps); - f_t change_in_x = std::ceil(solution[j]) - solution[j]; - pseudo_cost_sum_up[j] += change_in_obj / change_in_x; - pseudo_cost_num_up[j]++; - } - } - pseudo_cost_mutex_up[j].unlock(); - - if (toc(start_time) > settings.time_limit) { continue; } - - f_t score = calculate_pseudocost_score(j, solution, pseudo_cost_up_avg, pseudo_cost_down_avg); - - score_mutex.lock(); - if (score > max_score) { - max_score = score; - branch_var = j; - } - score_mutex.unlock(); - } - - settings.log.debug("Reliability branching result: node=%d branch_var=%d value=%e score=%e\n", - node_ptr->node_id, - branch_var, - solution[branch_var], - max_score); - - log.printf( - "pc branching on %d. Value %e. Score %e\n", branch_var, solution[branch_var], max_score); - - return branch_var; + return result; } template @@ -820,6 +902,33 @@ void pseudo_costs_t::update_pseudo_costs_from_strong_branching( template class pseudo_costs_t; +template int reliable_variable_selection_core( + mip_node_t*, + const std::vector&, + const std::vector&, + const simplex_solver_settings_t&, + const std::vector&, + const lp_problem_t&, + const std::vector&, + const basis_update_mpf_t&, + const std::vector&, + const std::vector&, + double*, + double*, + int*, + int*, + int, + int64_t&, + double, + int64_t, + int64_t, + double, + const reliability_branching_settings_t&, + int, + omp_mutex_t*, + omp_mutex_t*, + pcgenerator_t*); + template void strong_branching(const user_problem_t& original_problem, const lp_problem_t& original_lp, const simplex_solver_settings_t& settings, diff --git a/cpp/src/branch_and_bound/pseudo_costs.hpp b/cpp/src/branch_and_bound/pseudo_costs.hpp index ccd5b5d0c1..e8fe6b976f 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.hpp +++ b/cpp/src/branch_and_bound/pseudo_costs.hpp @@ -368,6 +368,7 @@ class pseudo_cost_snapshot_t { std::vector sum_up_; std::vector num_down_; std::vector num_up_; + int64_t strong_branching_lp_iter_{0}; private: std::vector> updates_; @@ -432,8 +433,10 @@ class pseudo_costs_t { nd[j] = pseudo_cost_num_down[j]; nu[j] = pseudo_cost_num_up[j]; } - return pseudo_cost_snapshot_t( - std::move(sd), std::move(su), std::move(nd), std::move(nu)); + auto snap = + pseudo_cost_snapshot_t(std::move(sd), std::move(su), std::move(nd), std::move(nu)); + snap.strong_branching_lp_iter_ = strong_branching_lp_iter.load(); + return snap; } void merge_updates(const std::vector>& updates) @@ -517,6 +520,36 @@ class pseudo_costs_t { omp_atomic_t strong_branching_lp_iter = 0; }; +// Core reliability branching loop usable by both opportunistic and deterministic paths. +// When num_tasks == 1, runs serially with no locking (deterministic). +// When num_tasks > 1 with mutexes/rng, uses OMP taskloop (opportunistic). +template +i_t reliable_variable_selection_core(mip_node_t* node_ptr, + const std::vector& fractional, + const std::vector& solution, + const simplex_solver_settings_t& settings, + const std::vector& var_types, + const lp_problem_t& leaf_problem, + const std::vector& edge_norms, + const basis_update_mpf_t& basis_factors, + const std::vector& basic_list, + const std::vector& nonbasic_list, + f_t* sum_down, + f_t* sum_up, + i_t* num_down, + i_t* num_up, + i_t n_vars, + int64_t& strong_branching_lp_iter, + f_t upper_bound, + int64_t bnb_lp_iters, + int64_t bnb_nodes_explored, + f_t start_time, + const reliability_branching_settings_t& rb_settings, + int num_tasks, + omp_mutex_t* var_mutex_down, + omp_mutex_t* var_mutex_up, + pcgenerator_t* rng); + template void strong_branching(const user_problem_t& original_problem, const lp_problem_t& original_lp, From 90f184cb2143f045a7760d78eee53437b20ce003 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 10 Mar 2026 08:45:33 -0700 Subject: [PATCH 163/225] cpufj in local search --- .../mip_heuristics/feasibility_jump/fj_cpu.cu | 4 ++ .../feasibility_jump/fj_cpu.cuh | 3 +- .../local_search/local_search.cu | 42 +++++++++---------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu index ff288414e9..6c4fb4e039 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu @@ -1496,6 +1496,10 @@ bool fj_t::cpu_solve(fj_cpu_climber_t& fj_cpu, f_t in_time_l CUOPT_LOG_TRACE("CPUFJ work units: %f incumbent %g", fj_cpu.work_units_elapsed.load(std::memory_order_relaxed), fj_cpu.pb_ptr->get_user_obj_from_solver_obj(fj_cpu.h_best_objective)); + + if (fj_cpu.work_units_elapsed.load(std::memory_order_relaxed) >= fj_cpu.work_budget) { + break; + } } cuopt_func_call(sanity_checks(fj_cpu)); diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cuh b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cuh index 7dcc8d39b0..d1684a2774 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cuh +++ b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cuh @@ -154,7 +154,8 @@ struct fj_cpu_climber_t { // Work unit tracking for deterministic synchronization std::atomic work_units_elapsed{0.0}; - double work_unit_bias{1.5}; // Bias factor to keep CPUFJ ahead of B&B + double work_unit_bias{1.5}; // Bias factor to keep CPUFJ ahead of B&B + double work_budget{std::numeric_limits::infinity()}; producer_sync_t* producer_sync{nullptr}; // Optional sync utility for notifying progress std::atomic halted{false}; diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index ac317ce38f..8097b784d9 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -221,7 +221,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, const std::string& source) { if (time_limit == 0.) return solution.get_feasible(); - const bool use_cpufj_local_search = context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC; + const bool deterministic = is_deterministic_mode(context.settings.determinism_mode); work_limit_timer_t timer(context.gpu_heur_loop, time_limit); const auto old_n_cstr_weights = in_fj.cstr_weights.size(); @@ -243,7 +243,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, } } - if (use_cpufj_local_search) { + { auto h_weights = cuopt::host_copy(in_fj.cstr_weights, solution.handle_ptr->get_stream()); auto h_objective_weight = in_fj.objective_weight.value(solution.handle_ptr->get_stream()); for (auto& cpu_fj : ls_cpu_fj) { @@ -254,16 +254,18 @@ bool local_search_t::do_fj_solve(solution_t& solution, context.preempt_heuristic_solver_, fj_settings_t{}, true); + if (deterministic) { + cpu_fj.fj_cpu->work_units_elapsed = 0.0; + cpu_fj.fj_cpu->work_budget = time_limit; + } } } auto solution_copy = solution; // Start CPU solver in background thread - if (use_cpufj_local_search) { - for (auto& cpu_fj : ls_cpu_fj) { - cpu_fj.start_cpu_solver(); - } + for (auto& cpu_fj : ls_cpu_fj) { + cpu_fj.start_cpu_solver(); } // Run GPU solver and measure execution time @@ -271,11 +273,10 @@ bool local_search_t::do_fj_solve(solution_t& solution, in_fj.settings.time_limit = timer.remaining_time(); in_fj.solve(solution); - // Stop CPU solver - if (use_cpufj_local_search) { - for (auto& cpu_fj : ls_cpu_fj) { - cpu_fj.stop_cpu_solver(); - } + // Stop CPU solver (in opportunistic mode this halts immediately; + // in deterministic mode CPUFJ self-halts via work_budget) + for (auto& cpu_fj : ls_cpu_fj) { + cpu_fj.stop_cpu_solver(); } auto gpu_fj_end = std::chrono::high_resolution_clock::now(); @@ -284,17 +285,14 @@ bool local_search_t::do_fj_solve(solution_t& solution, solution_t solution_cpu(*solution.problem_ptr); f_t best_cpu_obj = std::numeric_limits::max(); - // // Wait for CPU solver to finish - if (use_cpufj_local_search) { - for (auto& cpu_fj : ls_cpu_fj) { - bool cpu_sol_found = cpu_fj.wait_for_cpu_solver(); - if (cpu_sol_found) { - f_t cpu_obj = cpu_fj.fj_cpu->h_best_objective; - if (cpu_obj < best_cpu_obj) { - best_cpu_obj = cpu_obj; - solution_cpu.copy_new_assignment(cpu_fj.fj_cpu->h_best_assignment); - solution_cpu.compute_feasibility(); - } + for (auto& cpu_fj : ls_cpu_fj) { + bool cpu_sol_found = cpu_fj.wait_for_cpu_solver(); + if (cpu_sol_found) { + f_t cpu_obj = cpu_fj.fj_cpu->h_best_objective; + if (cpu_obj < best_cpu_obj) { + best_cpu_obj = cpu_obj; + solution_cpu.copy_new_assignment(cpu_fj.fj_cpu->h_best_assignment); + solution_cpu.compute_feasibility(); } } } From ec4239b7088cf457a5e032105ba40c53aea28c79 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 10 Mar 2026 10:50:49 -0700 Subject: [PATCH 164/225] presolve determinsitc, longer gpu heur wokr scale --- .../linear_programming/cuopt/run_mip.cpp | 2 +- .../presolve/third_party_presolve.cpp | 25 +++++++++++-------- .../presolve/third_party_presolve.hpp | 6 +++++ cpp/src/mip_heuristics/solve.cu | 3 ++- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 272fe44905..a759103f27 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -223,7 +223,7 @@ int run_single_file(std::string file_path, settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; settings.seed = 42; - settings.gpu_heur_work_unit_scale = 0.4; + settings.gpu_heur_work_unit_scale = 0.9; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp index 5a89393a6a..d0d5b759a4 100644 --- a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp +++ b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp @@ -552,17 +552,19 @@ template void set_presolve_parameters(papilo::Presolve& presolver, problem_category_t category, int nrows, - int ncols) + int ncols, + bool deterministic = false) { - // It looks like a copy. But this copy has the pointers to relevant variables in papilo auto params = presolver.getParameters(); if (category == problem_category_t::MIP) { - // Papilo has work unit measurements for probing. Because of this when the first batch fails to - // produce any reductions, the algorithm stops. To avoid stopping the algorithm, we set a - // minimum badge size to a huge value. The time limit makes sure that we exit if it takes too - // long - int min_badgesize = std::max(ncols / 2, 32); - params.setParameter("probing.minbadgesize", min_badgesize); + if (!deterministic) { + // Papilo has work unit measurements for probing. Because of this when the first batch fails + // to produce any reductions, the algorithm stops. To avoid stopping the algorithm, we set a + // minimum badge size to a huge value. The time limit makes sure that we exit if it takes too + // long + int min_badgesize = std::max(ncols / 2, 32); + params.setParameter("probing.minbadgesize", min_badgesize); + } params.setParameter("cliquemerging.enabled", true); params.setParameter("cliquemerging.maxcalls", 50); } @@ -633,8 +635,11 @@ std::optional> third_party_presolve_t( - papilo_presolver, category, op_problem.get_n_constraints(), op_problem.get_n_variables()); + set_presolve_parameters(papilo_presolver, + category, + op_problem.get_n_constraints(), + op_problem.get_n_variables(), + deterministic_); // Disable papilo logs papilo_presolver.setVerbosityLevel(papilo::VerbosityLevel::kQuiet); diff --git a/cpp/src/mip_heuristics/presolve/third_party_presolve.hpp b/cpp/src/mip_heuristics/presolve/third_party_presolve.hpp index ee273b6497..156c546742 100644 --- a/cpp/src/mip_heuristics/presolve/third_party_presolve.hpp +++ b/cpp/src/mip_heuristics/presolve/third_party_presolve.hpp @@ -82,6 +82,12 @@ class third_party_presolve_t { rmm::device_uvector& reduced_costs, rmm::cuda_stream_view stream_view); + bool deterministic_ = false; + + public: + void set_deterministic(bool d) { deterministic_ = d; } + + private: bool maximize_ = false; cuopt::linear_programming::presolver_t presolver_ = cuopt::linear_programming::presolver_t::PSLP; // PSLP settings diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index caaa3d3c13..38c25b977c 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -291,7 +291,8 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, // Note that this is not the presolve time, but the time limit for presolve. double presolve_time_limit = std::min(0.1 * time_limit, 60.0); if (deterministic_run) { presolve_time_limit = std::numeric_limits::infinity(); } - presolver = std::make_unique>(); + presolver = std::make_unique>(); + presolver->set_deterministic(deterministic_run); auto result = presolver->apply(op_problem, cuopt::linear_programming::problem_category_t::MIP, settings.presolver, From bc0927097c9e8abe81aad28f971a02df906d263b Mon Sep 17 00:00:00 2001 From: anandhkb Date: Tue, 10 Mar 2026 21:10:13 -0700 Subject: [PATCH 165/225] Chris Maes: RAII guard for solving_root_relaxation_, simplify report_heuristic - Add solving_root_relaxation_guard_t for single-point reset - Move solving_root_relaxation_ = true to just before root LP solve - Remove early return in get_lower_bound(); handle in report_heuristic - Refactor report_heuristic: shared user_obj/user_lower, simplified else branch --- cpp/src/branch_and_bound/branch_and_bound.cpp | 903 +++++++++--------- 1 file changed, 455 insertions(+), 448 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index a1c5d775ef..08ca97ba1a 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -235,6 +235,15 @@ inline char feasible_solution_symbol(search_strategy_t strategy) } #endif +// RAII guard: sets solving_root_relaxation_ to false on destruction. +struct solving_root_relaxation_guard_t { + omp_atomic_t* flag_; + explicit solving_root_relaxation_guard_t(omp_atomic_t& flag) : flag_(&flag) {} + ~solving_root_relaxation_guard_t() { flag_->store(false); } + solving_root_relaxation_guard_t(const solving_root_relaxation_guard_t&) = delete; + solving_root_relaxation_guard_t& operator=(const solving_root_relaxation_guard_t&) = delete; +}; + } // namespace template @@ -290,10 +299,6 @@ branch_and_bound_t::branch_and_bound_t( template f_t branch_and_bound_t::get_lower_bound() { - if (solving_root_relaxation_.load()) { - f_t root = root_lp_current_lower_bound_.load(); - return std::isfinite(root) ? root : -inf; - } f_t lower_bound = lower_bound_ceiling_.load(); f_t heap_lower_bound = node_queue_.get_lower_bound(); lower_bound = std::min(heap_lower_bound, lower_bound); @@ -312,16 +317,12 @@ template void branch_and_bound_t::report_heuristic(f_t obj) { const f_t user_obj = compute_user_objective(original_lp_, obj); - f_t lower = get_lower_bound(); - f_t user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) : -inf; - if (!solving_root_relaxation_.load() && exploration_stats_.nodes_explored == 0) { - f_t root_progress = root_lp_current_lower_bound_.load(); - const bool is_maximization = original_lp_.obj_scale < 0.0; - if (std::isfinite(root_progress) && - (!std::isfinite(user_lower) || - (is_maximization ? root_progress < user_lower : root_progress > user_lower))) { - user_lower = root_progress; - } + f_t user_lower; + if (solving_root_relaxation_.load()) { + user_lower = root_lp_current_lower_bound_.load(); + } else { + const f_t lower = get_lower_bound(); + user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) : -inf; } if (is_running_) { @@ -333,12 +334,19 @@ void branch_and_bound_t::report_heuristic(f_t obj) user_gap.c_str(), toc(exploration_stats_.start_time)); } else { - std::string gap_str = - std::isfinite(user_lower) ? (". Gap: " + user_mip_gap(user_obj, user_lower)) : ""; - settings_.log.printf("New solution from primal heuristics. Objective %+.6e%s. Time %.2f\n", - user_obj, - gap_str.c_str(), - toc(exploration_stats_.start_time)); + if (solving_root_relaxation_.load()) { + std::string gap_str = + std::isfinite(user_lower) ? user_mip_gap(user_obj, user_lower) : " - "; + settings_.log.printf( + "New solution from primal heuristics. Objective %+.6e. Gap %s. Time %.2f\n", + user_obj, + gap_str.c_str(), + toc(exploration_stats_.start_time)); + } else { + settings_.log.printf("New solution from primal heuristics. Objective %+.6e. Time %.2f\n", + user_obj, + toc(exploration_stats_.start_time)); + } } } @@ -1989,7 +1997,6 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_relax_soln_.resize(original_lp_.num_rows, original_lp_.num_cols); - solving_root_relaxation_ = true; i_t original_rows = original_lp_.num_rows; simplex_solver_settings_t lp_settings = settings_; lp_settings.inside_mip = 1; @@ -2003,491 +2010,491 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut basis_update_mpf_t basis_update(original_lp_.num_rows, settings_.refactor_frequency); lp_status_t root_status; - if (!enable_concurrent_lp_root_solve()) { - // RINS/SUBMIP path - settings_.log.printf("\nSolving LP root relaxation with dual simplex\n"); - root_status = solve_linear_program_with_advanced_basis(original_lp_, - exploration_stats_.start_time, - lp_settings, - root_relax_soln_, - basis_update, - basic_list, - nonbasic_list, - root_vstatus_, - edge_norms_); - } else { - settings_.log.printf("\nSolving LP root relaxation in concurrent mode\n"); - root_status = solve_root_relaxation(lp_settings, - root_relax_soln_, - root_vstatus_, - basis_update, - basic_list, - nonbasic_list, - edge_norms_); - } - exploration_stats_.total_lp_iters = root_relax_soln_.iterations; - exploration_stats_.total_lp_solve_time = toc(exploration_stats_.start_time); - - if (root_status == lp_status_t::INFEASIBLE) { - solving_root_relaxation_ = false; - settings_.log.printf("MIP Infeasible\n"); - // FIXME: rarely dual simplex detects infeasible whereas it is feasible. - // to add a small safety net, check if there is a primal solution already. - // Uncomment this if the issue with cost266-UUE is resolved - // if (settings.heuristic_preemption_callback != nullptr) { - // settings.heuristic_preemption_callback(); - // } - return mip_status_t::INFEASIBLE; - } - if (root_status == lp_status_t::UNBOUNDED) { - solving_root_relaxation_ = false; - settings_.log.printf("MIP Unbounded\n"); - if (settings_.heuristic_preemption_callback != nullptr) { - settings_.heuristic_preemption_callback(); + { + solving_root_relaxation_ = true; + solving_root_relaxation_guard_t root_guard(solving_root_relaxation_); + + if (!enable_concurrent_lp_root_solve()) { + // RINS/SUBMIP path + settings_.log.printf("\nSolving LP root relaxation with dual simplex\n"); + root_status = solve_linear_program_with_advanced_basis(original_lp_, + exploration_stats_.start_time, + lp_settings, + root_relax_soln_, + basis_update, + basic_list, + nonbasic_list, + root_vstatus_, + edge_norms_); + } else { + settings_.log.printf("\nSolving LP root relaxation in concurrent mode\n"); + root_status = solve_root_relaxation(lp_settings, + root_relax_soln_, + root_vstatus_, + basis_update, + basic_list, + nonbasic_list, + edge_norms_); + } + exploration_stats_.total_lp_iters = root_relax_soln_.iterations; + exploration_stats_.total_lp_solve_time = toc(exploration_stats_.start_time); + + if (root_status == lp_status_t::INFEASIBLE) { + settings_.log.printf("MIP Infeasible\n"); + // FIXME: rarely dual simplex detects infeasible whereas it is feasible. + // to add a small safety net, check if there is a primal solution already. + // Uncomment this if the issue with cost266-UUE is resolved + // if (settings.heuristic_preemption_callback != nullptr) { + // settings.heuristic_preemption_callback(); + // } + return mip_status_t::INFEASIBLE; + } + if (root_status == lp_status_t::UNBOUNDED) { + settings_.log.printf("MIP Unbounded\n"); + if (settings_.heuristic_preemption_callback != nullptr) { + settings_.heuristic_preemption_callback(); + } + return mip_status_t::UNBOUNDED; + } + if (root_status == lp_status_t::TIME_LIMIT) { + solver_status_ = mip_status_t::TIME_LIMIT; + set_final_solution(solution, -inf); + return solver_status_; } - return mip_status_t::UNBOUNDED; - } - if (root_status == lp_status_t::TIME_LIMIT) { - solving_root_relaxation_ = false; - solver_status_ = mip_status_t::TIME_LIMIT; - set_final_solution(solution, -inf); - return solver_status_; - } - if (root_status == lp_status_t::WORK_LIMIT) { - solving_root_relaxation_ = false; - solver_status_ = mip_status_t::WORK_LIMIT; - set_final_solution(solution, -inf); - return solver_status_; - } + if (root_status == lp_status_t::WORK_LIMIT) { + solver_status_ = mip_status_t::WORK_LIMIT; + set_final_solution(solution, -inf); + return solver_status_; + } - if (root_status == lp_status_t::NUMERICAL_ISSUES) { - solving_root_relaxation_ = false; - solver_status_ = mip_status_t::NUMERICAL; - set_final_solution(solution, -inf); - return solver_status_; - } + if (root_status == lp_status_t::NUMERICAL_ISSUES) { + solver_status_ = mip_status_t::NUMERICAL; + set_final_solution(solution, -inf); + return solver_status_; + } - assert(root_vstatus_.size() == original_lp_.num_cols); - set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_); + assert(root_vstatus_.size() == original_lp_.num_cols); + set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_); - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); - if (settings_.set_simplex_solution_callback != nullptr) { - std::vector original_x; - uncrush_primal_solution(original_problem_, original_lp_, root_relax_soln_.x, original_x); - std::vector original_dual; - std::vector original_z; - uncrush_dual_solution(original_problem_, - original_lp_, - root_relax_soln_.y, - root_relax_soln_.z, - original_dual, - original_z); - settings_.set_simplex_solution_callback( - original_x, original_dual, compute_user_objective(original_lp_, root_objective_)); - } + if (settings_.set_simplex_solution_callback != nullptr) { + std::vector original_x; + uncrush_primal_solution(original_problem_, original_lp_, root_relax_soln_.x, original_x); + std::vector original_dual; + std::vector original_z; + uncrush_dual_solution(original_problem_, + original_lp_, + root_relax_soln_.y, + root_relax_soln_.z, + original_dual, + original_z); + settings_.set_simplex_solution_callback( + original_x, original_dual, compute_user_objective(original_lp_, root_objective_)); + } - std::vector fractional; - i_t num_fractional = fractional_variables(settings_, root_relax_soln_.x, var_types_, fractional); + std::vector fractional; + i_t num_fractional = + fractional_variables(settings_, root_relax_soln_.x, var_types_, fractional); - cut_info_t cut_info; + cut_info_t cut_info; - if (num_fractional == 0) { - solving_root_relaxation_ = false; - set_solution_at_root(solution, cut_info); - return mip_status_t::OPTIMAL; - } + if (num_fractional == 0) { + set_solution_at_root(solution, cut_info); + return mip_status_t::OPTIMAL; + } - is_running_ = true; - lower_bound_ceiling_ = inf; + is_running_ = true; + lower_bound_ceiling_ = inf; - if (num_fractional != 0 && settings_.max_cut_passes > 0) { - settings_.log.printf( - " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node | " - "Gap " - "| Time |\n"); - } + if (num_fractional != 0 && settings_.max_cut_passes > 0) { + settings_.log.printf( + " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node | " + " " + "Gap " + "| Time |\n"); + } - cut_pool_t cut_pool(original_lp_.num_cols, settings_); - cut_generation_t cut_generation( - cut_pool, original_lp_, settings_, Arow_, new_slacks_, var_types_); + cut_pool_t cut_pool(original_lp_.num_cols, settings_); + cut_generation_t cut_generation( + cut_pool, original_lp_, settings_, Arow_, new_slacks_, var_types_); - std::vector saved_solution; + std::vector saved_solution; #ifdef CHECK_CUTS_AGAINST_SAVED_SOLUTION - read_saved_solution_for_cut_verification(original_lp_, settings_, saved_solution); + read_saved_solution_for_cut_verification(original_lp_, settings_, saved_solution); #endif - f_t last_upper_bound = std::numeric_limits::infinity(); - f_t last_objective = root_objective_; - f_t root_relax_objective = root_objective_; + f_t last_upper_bound = std::numeric_limits::infinity(); + f_t last_objective = root_objective_; + f_t root_relax_objective = root_objective_; - i_t cut_pool_size = 0; - for (i_t cut_pass = 0; cut_pass < settings_.max_cut_passes; cut_pass++) { - if (num_fractional == 0) { - solving_root_relaxation_ = false; - set_solution_at_root(solution, cut_info); - return mip_status_t::OPTIMAL; - } else { + i_t cut_pool_size = 0; + for (i_t cut_pass = 0; cut_pass < settings_.max_cut_passes; cut_pass++) { + if (num_fractional == 0) { + set_solution_at_root(solution, cut_info); + return mip_status_t::OPTIMAL; + } else { #ifdef PRINT_FRACTIONAL_INFO - settings_.log.printf( - "Found %d fractional variables on cut pass %d\n", num_fractional, cut_pass); - for (i_t j : fractional) { - settings_.log.printf("Fractional variable %d lower %e value %e upper %e\n", - j, - original_lp_.lower[j], - root_relax_soln_.x[j], - original_lp_.upper[j]); - } + settings_.log.printf( + "Found %d fractional variables on cut pass %d\n", num_fractional, cut_pass); + for (i_t j : fractional) { + settings_.log.printf("Fractional variable %d lower %e value %e upper %e\n", + j, + original_lp_.lower[j], + root_relax_soln_.x[j], + original_lp_.upper[j]); + } #endif - // Generate cuts and add them to the cut pool - f_t cut_start_time = tic(); - cut_generation.generate_cuts(original_lp_, - settings_, - Arow_, - new_slacks_, - var_types_, - basis_update, - root_relax_soln_.x, - basic_list, - nonbasic_list); - f_t cut_generation_time = toc(cut_start_time); - if (cut_generation_time > 1.0) { - settings_.log.debug("Cut generation time %.2f seconds\n", cut_generation_time); - } - // Score the cuts - f_t score_start_time = tic(); - cut_pool.score_cuts(root_relax_soln_.x); - f_t score_time = toc(score_start_time); - if (score_time > 1.0) { settings_.log.debug("Cut scoring time %.2f seconds\n", score_time); } - // Get the best cuts from the cut pool - csr_matrix_t cuts_to_add(0, original_lp_.num_cols, 0); - std::vector cut_rhs; - std::vector cut_types; - i_t num_cuts = cut_pool.get_best_cuts(cuts_to_add, cut_rhs, cut_types); - if (num_cuts == 0) { break; } - cut_info.record_cut_types(cut_types); + // Generate cuts and add them to the cut pool + f_t cut_start_time = tic(); + cut_generation.generate_cuts(original_lp_, + settings_, + Arow_, + new_slacks_, + var_types_, + basis_update, + root_relax_soln_.x, + basic_list, + nonbasic_list); + f_t cut_generation_time = toc(cut_start_time); + if (cut_generation_time > 1.0) { + settings_.log.debug("Cut generation time %.2f seconds\n", cut_generation_time); + } + // Score the cuts + f_t score_start_time = tic(); + cut_pool.score_cuts(root_relax_soln_.x); + f_t score_time = toc(score_start_time); + if (score_time > 1.0) { + settings_.log.debug("Cut scoring time %.2f seconds\n", score_time); + } + // Get the best cuts from the cut pool + csr_matrix_t cuts_to_add(0, original_lp_.num_cols, 0); + std::vector cut_rhs; + std::vector cut_types; + i_t num_cuts = cut_pool.get_best_cuts(cuts_to_add, cut_rhs, cut_types); + if (num_cuts == 0) { break; } + cut_info.record_cut_types(cut_types); #ifdef PRINT_CUT_POOL_TYPES - cut_pool.print_cutpool_types(); - print_cut_types("In LP ", cut_types, settings_); - printf("Cut pool size: %d\n", cut_pool.pool_size()); + cut_pool.print_cutpool_types(); + print_cut_types("In LP ", cut_types, settings_); + printf("Cut pool size: %d\n", cut_pool.pool_size()); #endif #ifdef CHECK_CUT_MATRIX - if (cuts_to_add.check_matrix() != 0) { - settings_.log.printf("Bad cuts matrix\n"); - for (i_t i = 0; i < static_cast(cut_types.size()); ++i) { - settings_.log.printf("row %d cut type %d\n", i, cut_types[i]); + if (cuts_to_add.check_matrix() != 0) { + settings_.log.printf("Bad cuts matrix\n"); + for (i_t i = 0; i < static_cast(cut_types.size()); ++i) { + settings_.log.printf("row %d cut type %d\n", i, cut_types[i]); + } + return mip_status_t::NUMERICAL; } - return mip_status_t::NUMERICAL; - } #endif - // Check against saved solution + // Check against saved solution #ifdef CHECK_CUTS_AGAINST_SAVED_SOLUTION - verify_cuts_against_saved_solution(cuts_to_add, cut_rhs, saved_solution); + verify_cuts_against_saved_solution(cuts_to_add, cut_rhs, saved_solution); #endif - cut_pool_size = cut_pool.pool_size(); - - // Resolve the LP with the new cuts - settings_.log.debug( - "Solving LP with %d cuts (%d cut nonzeros). Cuts in pool %d. Total constraints %d\n", - num_cuts, - cuts_to_add.row_start[cuts_to_add.m], - cut_pool.pool_size(), - cuts_to_add.m + original_lp_.num_rows); - lp_settings.log.log = false; - - f_t add_cuts_start_time = tic(); - mutex_original_lp_.lock(); - i_t add_cuts_status = add_cuts(settings_, - cuts_to_add, - cut_rhs, - original_lp_, - new_slacks_, - root_relax_soln_, - basis_update, - basic_list, - nonbasic_list, - root_vstatus_, - edge_norms_); - var_types_.resize(original_lp_.num_cols, variable_type_t::CONTINUOUS); - mutex_original_lp_.unlock(); - f_t add_cuts_time = toc(add_cuts_start_time); - if (add_cuts_time > 1.0) { - settings_.log.debug("Add cuts time %.2f seconds\n", add_cuts_time); - } - if (add_cuts_status != 0) { - settings_.log.printf("Failed to add cuts\n"); - return mip_status_t::NUMERICAL; - } + cut_pool_size = cut_pool.pool_size(); - if (settings_.reduced_cost_strengthening >= 1 && upper_bound_.load() < last_upper_bound) { - mutex_upper_.lock(); - last_upper_bound = upper_bound_.load(); - std::vector lower_bounds; - std::vector upper_bounds; - find_reduced_cost_fixings(upper_bound_.load(), lower_bounds, upper_bounds); - mutex_upper_.unlock(); + // Resolve the LP with the new cuts + settings_.log.debug( + "Solving LP with %d cuts (%d cut nonzeros). Cuts in pool %d. Total constraints %d\n", + num_cuts, + cuts_to_add.row_start[cuts_to_add.m], + cut_pool.pool_size(), + cuts_to_add.m + original_lp_.num_rows); + lp_settings.log.log = false; + + f_t add_cuts_start_time = tic(); mutex_original_lp_.lock(); - original_lp_.lower = lower_bounds; - original_lp_.upper = upper_bounds; + i_t add_cuts_status = add_cuts(settings_, + cuts_to_add, + cut_rhs, + original_lp_, + new_slacks_, + root_relax_soln_, + basis_update, + basic_list, + nonbasic_list, + root_vstatus_, + edge_norms_); + var_types_.resize(original_lp_.num_cols, variable_type_t::CONTINUOUS); mutex_original_lp_.unlock(); - } + f_t add_cuts_time = toc(add_cuts_start_time); + if (add_cuts_time > 1.0) { + settings_.log.debug("Add cuts time %.2f seconds\n", add_cuts_time); + } + if (add_cuts_status != 0) { + settings_.log.printf("Failed to add cuts\n"); + return mip_status_t::NUMERICAL; + } - // Try to do bound strengthening - std::vector bounds_changed(original_lp_.num_cols, true); - std::vector row_sense; + if (settings_.reduced_cost_strengthening >= 1 && upper_bound_.load() < last_upper_bound) { + mutex_upper_.lock(); + last_upper_bound = upper_bound_.load(); + std::vector lower_bounds; + std::vector upper_bounds; + find_reduced_cost_fixings(upper_bound_.load(), lower_bounds, upper_bounds); + mutex_upper_.unlock(); + mutex_original_lp_.lock(); + original_lp_.lower = lower_bounds; + original_lp_.upper = upper_bounds; + mutex_original_lp_.unlock(); + } + + // Try to do bound strengthening + std::vector bounds_changed(original_lp_.num_cols, true); + std::vector row_sense; #ifdef CHECK_MATRICES - settings_.log.printf("Before A check\n"); - original_lp_.A.check_matrix(); + settings_.log.printf("Before A check\n"); + original_lp_.A.check_matrix(); #endif - original_lp_.A.to_compressed_row(Arow_); - - f_t node_presolve_start_time = tic(); - bounds_strengthening_t node_presolve(original_lp_, Arow_, row_sense, var_types_); - std::vector new_lower = original_lp_.lower; - std::vector new_upper = original_lp_.upper; - bool feasible = - node_presolve.bounds_strengthening(settings_, bounds_changed, new_lower, new_upper); - mutex_original_lp_.lock(); - original_lp_.lower = new_lower; - original_lp_.upper = new_upper; - mutex_original_lp_.unlock(); - f_t node_presolve_time = toc(node_presolve_start_time); - if (node_presolve_time > 1.0) { - settings_.log.debug("Node presolve time %.2f seconds\n", node_presolve_time); - } - if (!feasible) { - settings_.log.printf("Bound strengthening detected infeasibility\n"); - return mip_status_t::INFEASIBLE; - } + original_lp_.A.to_compressed_row(Arow_); + + f_t node_presolve_start_time = tic(); + bounds_strengthening_t node_presolve(original_lp_, Arow_, row_sense, var_types_); + std::vector new_lower = original_lp_.lower; + std::vector new_upper = original_lp_.upper; + bool feasible = + node_presolve.bounds_strengthening(settings_, bounds_changed, new_lower, new_upper); + mutex_original_lp_.lock(); + original_lp_.lower = new_lower; + original_lp_.upper = new_upper; + mutex_original_lp_.unlock(); + f_t node_presolve_time = toc(node_presolve_start_time); + if (node_presolve_time > 1.0) { + settings_.log.debug("Node presolve time %.2f seconds\n", node_presolve_time); + } + if (!feasible) { + settings_.log.printf("Bound strengthening detected infeasibility\n"); + return mip_status_t::INFEASIBLE; + } - i_t iter = 0; - bool initialize_basis = false; - lp_settings.concurrent_halt = NULL; - f_t dual_phase2_start_time = tic(); - dual::status_t cut_status = dual_phase2_with_advanced_basis(2, - 0, - initialize_basis, - exploration_stats_.start_time, - original_lp_, - lp_settings, - root_vstatus_, - basis_update, - basic_list, - nonbasic_list, - root_relax_soln_, - iter, - edge_norms_); - exploration_stats_.total_lp_iters += iter; - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); - f_t dual_phase2_time = toc(dual_phase2_start_time); - if (dual_phase2_time > 1.0) { - settings_.log.debug("Dual phase2 time %.2f seconds\n", dual_phase2_time); - } - if (cut_status == dual::status_t::TIME_LIMIT) { - solving_root_relaxation_ = false; - solver_status_ = mip_status_t::TIME_LIMIT; - set_final_solution(solution, root_objective_); - return solver_status_; - } + i_t iter = 0; + bool initialize_basis = false; + lp_settings.concurrent_halt = NULL; + f_t dual_phase2_start_time = tic(); + dual::status_t cut_status = dual_phase2_with_advanced_basis(2, + 0, + initialize_basis, + exploration_stats_.start_time, + original_lp_, + lp_settings, + root_vstatus_, + basis_update, + basic_list, + nonbasic_list, + root_relax_soln_, + iter, + edge_norms_); + exploration_stats_.total_lp_iters += iter; + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + f_t dual_phase2_time = toc(dual_phase2_start_time); + if (dual_phase2_time > 1.0) { + settings_.log.debug("Dual phase2 time %.2f seconds\n", dual_phase2_time); + } + if (cut_status == dual::status_t::TIME_LIMIT) { + solver_status_ = mip_status_t::TIME_LIMIT; + set_final_solution(solution, root_objective_); + return solver_status_; + } - if (cut_status != dual::status_t::OPTIMAL) { - settings_.log.printf("Numerical issue at root node. Resolving from scratch\n"); - lp_status_t scratch_status = - solve_linear_program_with_advanced_basis(original_lp_, - exploration_stats_.start_time, - lp_settings, - root_relax_soln_, - basis_update, - basic_list, - nonbasic_list, - root_vstatus_, - edge_norms_); - if (scratch_status == lp_status_t::OPTIMAL) { - // We recovered - cut_status = convert_lp_status_to_dual_status(scratch_status); - exploration_stats_.total_lp_iters += root_relax_soln_.iterations; - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); - } else { - solving_root_relaxation_ = false; - settings_.log.printf("Cut status %s\n", dual::status_to_string(cut_status).c_str()); - return mip_status_t::NUMERICAL; + if (cut_status != dual::status_t::OPTIMAL) { + settings_.log.printf("Numerical issue at root node. Resolving from scratch\n"); + lp_status_t scratch_status = + solve_linear_program_with_advanced_basis(original_lp_, + exploration_stats_.start_time, + lp_settings, + root_relax_soln_, + basis_update, + basic_list, + nonbasic_list, + root_vstatus_, + edge_norms_); + if (scratch_status == lp_status_t::OPTIMAL) { + // We recovered + cut_status = convert_lp_status_to_dual_status(scratch_status); + exploration_stats_.total_lp_iters += root_relax_soln_.iterations; + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + } else { + settings_.log.printf("Cut status %s\n", dual::status_to_string(cut_status).c_str()); + return mip_status_t::NUMERICAL; + } } - } - f_t remove_cuts_start_time = tic(); - mutex_original_lp_.lock(); - remove_cuts(original_lp_, - settings_, - exploration_stats_.start_time, - Arow_, - new_slacks_, - original_rows, - var_types_, - root_vstatus_, - edge_norms_, - root_relax_soln_.x, - root_relax_soln_.y, - root_relax_soln_.z, - basic_list, - nonbasic_list, - basis_update); - mutex_original_lp_.unlock(); - f_t remove_cuts_time = toc(remove_cuts_start_time); - if (remove_cuts_time > 1.0) { - settings_.log.debug("Remove cuts time %.2f seconds\n", remove_cuts_time); - } - fractional.clear(); - num_fractional = fractional_variables(settings_, root_relax_soln_.x, var_types_, fractional); + f_t remove_cuts_start_time = tic(); + mutex_original_lp_.lock(); + remove_cuts(original_lp_, + settings_, + exploration_stats_.start_time, + Arow_, + new_slacks_, + original_rows, + var_types_, + root_vstatus_, + edge_norms_, + root_relax_soln_.x, + root_relax_soln_.y, + root_relax_soln_.z, + basic_list, + nonbasic_list, + basis_update); + mutex_original_lp_.unlock(); + f_t remove_cuts_time = toc(remove_cuts_start_time); + if (remove_cuts_time > 1.0) { + settings_.log.debug("Remove cuts time %.2f seconds\n", remove_cuts_time); + } + fractional.clear(); + num_fractional = + fractional_variables(settings_, root_relax_soln_.x, var_types_, fractional); + + if (num_fractional == 0) { + upper_bound_ = root_objective_; + mutex_upper_.lock(); + incumbent_.set_incumbent_solution(root_objective_, root_relax_soln_.x); + mutex_upper_.unlock(); + } + f_t obj = upper_bound_.load(); + report(' ', obj, root_objective_, 0, num_fractional); + + f_t rel_gap = user_relative_gap(original_lp_, upper_bound_.load(), root_objective_); + f_t abs_gap = upper_bound_.load() - root_objective_; + if (rel_gap < settings_.relative_mip_gap_tol || abs_gap < settings_.absolute_mip_gap_tol) { + set_solution_at_root(solution, cut_info); + set_final_solution(solution, root_objective_); + return mip_status_t::OPTIMAL; + } - if (num_fractional == 0) { - upper_bound_ = root_objective_; - mutex_upper_.lock(); - incumbent_.set_incumbent_solution(root_objective_, root_relax_soln_.x); - mutex_upper_.unlock(); + f_t change_in_objective = root_objective_ - last_objective; + const f_t factor = settings_.cut_change_threshold; + const f_t min_objective = 1e-3; + if (change_in_objective <= + factor * std::max(min_objective, std::abs(root_relax_objective))) { + settings_.log.debug( + "Change in objective %.16e is less than 1e-3 of root relax objective %.16e\n", + change_in_objective, + root_relax_objective); + break; + } + last_objective = root_objective_; } - f_t obj = upper_bound_.load(); - report(' ', obj, root_objective_, 0, num_fractional); + } - f_t rel_gap = user_relative_gap(original_lp_, upper_bound_.load(), root_objective_); - f_t abs_gap = upper_bound_.load() - root_objective_; - if (rel_gap < settings_.relative_mip_gap_tol || abs_gap < settings_.absolute_mip_gap_tol) { - solving_root_relaxation_ = false; - set_solution_at_root(solution, cut_info); - set_final_solution(solution, root_objective_); - return mip_status_t::OPTIMAL; - } + print_cut_info(settings_, cut_info); - f_t change_in_objective = root_objective_ - last_objective; - const f_t factor = settings_.cut_change_threshold; - const f_t min_objective = 1e-3; - if (change_in_objective <= factor * std::max(min_objective, std::abs(root_relax_objective))) { - settings_.log.debug( - "Change in objective %.16e is less than 1e-3 of root relax objective %.16e\n", - change_in_objective, - root_relax_objective); - break; - } - last_objective = root_objective_; + if (cut_info.has_cuts()) { + settings_.log.printf("Cut pool size : %d\n", cut_pool_size); + settings_.log.printf("Size with cuts : %d constraints, %d variables, %d nonzeros\n", + original_lp_.num_rows, + original_lp_.num_cols, + original_lp_.A.col_start[original_lp_.A.n]); } - } - solving_root_relaxation_ = false; - print_cut_info(settings_, cut_info); + set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_); - if (cut_info.has_cuts()) { - settings_.log.printf("Cut pool size : %d\n", cut_pool_size); - settings_.log.printf("Size with cuts : %d constraints, %d variables, %d nonzeros\n", - original_lp_.num_rows, - original_lp_.num_cols, - original_lp_.A.col_start[original_lp_.A.n]); - } + pc_.resize(original_lp_.num_cols); + { + raft::common::nvtx::range scope_sb("BB::strong_branching"); + strong_branching(original_problem_, + original_lp_, + settings_, + exploration_stats_.start_time, + var_types_, + root_relax_soln_.x, + fractional, + root_objective_, + root_vstatus_, + edge_norms_, + pc_); + } - set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_); + if (toc(exploration_stats_.start_time) > settings_.time_limit) { + solver_status_ = mip_status_t::TIME_LIMIT; + set_final_solution(solution, root_objective_); + return solver_status_; + } - pc_.resize(original_lp_.num_cols); - { - raft::common::nvtx::range scope_sb("BB::strong_branching"); - strong_branching(original_problem_, - original_lp_, - settings_, - exploration_stats_.start_time, - var_types_, - root_relax_soln_.x, - fractional, - root_objective_, - root_vstatus_, - edge_norms_, - pc_); - } + if (settings_.reduced_cost_strengthening >= 2 && upper_bound_.load() < last_upper_bound) { + std::vector lower_bounds; + std::vector upper_bounds; + i_t num_fixed = find_reduced_cost_fixings(upper_bound_.load(), lower_bounds, upper_bounds); + if (num_fixed > 0) { + std::vector bounds_changed(original_lp_.num_cols, true); + std::vector row_sense; - if (toc(exploration_stats_.start_time) > settings_.time_limit) { - solver_status_ = mip_status_t::TIME_LIMIT; - set_final_solution(solution, root_objective_); - return solver_status_; - } - - if (settings_.reduced_cost_strengthening >= 2 && upper_bound_.load() < last_upper_bound) { - std::vector lower_bounds; - std::vector upper_bounds; - i_t num_fixed = find_reduced_cost_fixings(upper_bound_.load(), lower_bounds, upper_bounds); - if (num_fixed > 0) { - std::vector bounds_changed(original_lp_.num_cols, true); - std::vector row_sense; - - bounds_strengthening_t node_presolve(original_lp_, Arow_, row_sense, var_types_); - - mutex_original_lp_.lock(); - original_lp_.lower = lower_bounds; - original_lp_.upper = upper_bounds; - bool feasible = node_presolve.bounds_strengthening( - settings_, bounds_changed, original_lp_.lower, original_lp_.upper); - mutex_original_lp_.unlock(); - if (!feasible) { - settings_.log.printf("Bound strengthening failed\n"); - return mip_status_t::NUMERICAL; // We had a feasible integer solution, but bound - // strengthening thinks we are infeasible. - } - // Go through and check the fractional variables and remove any that are now fixed to their - // bounds - std::vector to_remove(fractional.size(), 0); - i_t num_to_remove = 0; - for (i_t k = 0; k < fractional.size(); k++) { - const i_t j = fractional[k]; - if (std::abs(original_lp_.upper[j] - original_lp_.lower[j]) < settings_.fixed_tol) { - to_remove[k] = 1; - num_to_remove++; + bounds_strengthening_t node_presolve(original_lp_, Arow_, row_sense, var_types_); + + mutex_original_lp_.lock(); + original_lp_.lower = lower_bounds; + original_lp_.upper = upper_bounds; + bool feasible = node_presolve.bounds_strengthening( + settings_, bounds_changed, original_lp_.lower, original_lp_.upper); + mutex_original_lp_.unlock(); + if (!feasible) { + settings_.log.printf("Bound strengthening failed\n"); + return mip_status_t::NUMERICAL; // We had a feasible integer solution, but bound + // strengthening thinks we are infeasible. } - } - if (num_to_remove > 0) { - std::vector new_fractional; - new_fractional.reserve(fractional.size() - num_to_remove); + // Go through and check the fractional variables and remove any that are now fixed to their + // bounds + std::vector to_remove(fractional.size(), 0); + i_t num_to_remove = 0; for (i_t k = 0; k < fractional.size(); k++) { - if (!to_remove[k]) { new_fractional.push_back(fractional[k]); } + const i_t j = fractional[k]; + if (std::abs(original_lp_.upper[j] - original_lp_.lower[j]) < settings_.fixed_tol) { + to_remove[k] = 1; + num_to_remove++; + } + } + if (num_to_remove > 0) { + std::vector new_fractional; + new_fractional.reserve(fractional.size() - num_to_remove); + for (i_t k = 0; k < fractional.size(); k++) { + if (!to_remove[k]) { new_fractional.push_back(fractional[k]); } + } + fractional = new_fractional; + num_fractional = fractional.size(); } - fractional = new_fractional; - num_fractional = fractional.size(); } } - } - // Choose variable to branch on - i_t branch_var = pc_.variable_selection(fractional, root_relax_soln_.x, log); + // Choose variable to branch on + i_t branch_var = pc_.variable_selection(fractional, root_relax_soln_.x, log); - search_tree_.root = std::move(mip_node_t(root_objective_, root_vstatus_)); - search_tree_.num_nodes = 0; - search_tree_.graphviz_node(settings_.log, &search_tree_.root, "lower bound", root_objective_); - search_tree_.branch(&search_tree_.root, - branch_var, - root_relax_soln_.x[branch_var], - num_fractional, - root_vstatus_, - original_lp_, - log); - node_queue_.push(search_tree_.root.get_down_child()); - node_queue_.push(search_tree_.root.get_up_child()); + search_tree_.root = std::move(mip_node_t(root_objective_, root_vstatus_)); + search_tree_.num_nodes = 0; + search_tree_.graphviz_node(settings_.log, &search_tree_.root, "lower bound", root_objective_); + search_tree_.branch(&search_tree_.root, + branch_var, + root_relax_soln_.x[branch_var], + num_fractional, + root_vstatus_, + original_lp_, + log); + node_queue_.push(search_tree_.root.get_down_child()); + node_queue_.push(search_tree_.root.get_up_child()); - settings_.log.printf("Exploring the B&B tree using %d threads\n\n", settings_.num_threads); + settings_.log.printf("Exploring the B&B tree using %d threads\n\n", settings_.num_threads); - exploration_stats_.nodes_explored = 0; - exploration_stats_.nodes_unexplored = 2; - exploration_stats_.nodes_since_last_log = 0; - exploration_stats_.last_log = tic(); - min_node_queue_size_ = 2 * settings_.num_threads; + exploration_stats_.nodes_explored = 0; + exploration_stats_.nodes_unexplored = 2; + exploration_stats_.nodes_since_last_log = 0; + exploration_stats_.last_log = tic(); + min_node_queue_size_ = 2 * settings_.num_threads; - if (settings_.diving_settings.coefficient_diving != 0) { - calculate_variable_locks(original_lp_, var_up_locks_, var_down_locks_); - } - if (settings_.deterministic) { - settings_.log.printf( - " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " - "| Gap | Work | Time |\n"); - } else { - settings_.log.printf( - " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " - "| Gap | Time |\n"); + if (settings_.diving_settings.coefficient_diving != 0) { + calculate_variable_locks(original_lp_, var_up_locks_, var_down_locks_); + } + if (settings_.deterministic) { + settings_.log.printf( + " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " + "| Gap | Work | Time |\n"); + } else { + settings_.log.printf( + " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " + "| Gap | Time |\n"); + } } if (settings_.deterministic) { From d862480ff1a0f507912741aac2d094802a55635a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Akif=20=C3=87=C3=96RD=C3=9CK?= Date: Wed, 11 Mar 2026 11:10:02 +0100 Subject: [PATCH 166/225] Add clique cuts (#937) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds clique cuts to the set of cuts we have. It uses the ideas from Preprocessing and Cutting Planes with Conflict Graphs, Brito et.al. In a previous PR, I have added the clique table generation as a preprocessing step (currently doesn't change the problem). This PR builds on that by using the extended cliques during cut passes. At each cut pass, a subgraph induced by the fractinoal variables on the current relaxation solution is used to generate clique cuts. We want to generate the set of violated clique inequalities that have at least min violation (1e-6). The maximal cliques in the fractional subgraph is found by Bron Kerbosh dynamic programming algorithm. The found maximal cliques are extended with the suitable integer valued variables on the original conflict graph. The extension is guided by reduced cost of the variables at the current relaxation optimal. We try to extend the cliques with variables that have lower reduced cost. The reason is that it disturbs dual simplex less (causes fewer refactors and iterations for resolve). Also the variables with lower reduced costs are likely to be active in the next resolve. This also results in more dominant cliques preventing multiple smaller sized cliques in the next iterations. Benchmark results: MIP GAP wins/losses defined as at least 10% difference: main wins (6): Instance dir1 gap dir2 gap diff app1-2 0.8077 1.1818 +0.3741 bab2 0.4148 0.6148 +0.2001 glass4 0.3333 0.4483 +0.1149 map16715-04 0.9269 1.4085 +0.4816 neos-4413714-turia 0.7027 0.8454 +0.1427 rail01 0.5476 0.7934 +0.2458 clique cuts wins (11): Instance dir1 gap dir2 gap diff 30n20b8 0.7688 0.6667 +0.1021 bab6 0.9255 0.3611 +0.5643 buildingenergy 0.1706 0.0036 +0.1670 co-100 0.8410 0.7089 +0.1321 neos-3754480-nidda 20.3512 19.6495 +0.7017 neos-5188808-nattai 0.2032 0.0000 +0.2032 netdiversion 0.3710 0.1429 +0.2281 physiciansched3-3 0.2444 0.0269 +0.2175 rocII-5-11 1.0893 0.7762 +0.3131 satellites2-60-fs 13.5000 1.4167 +12.0833 sorrell3 9.5625 9.4375 +0.1250 Optimality wins/loses: Wins graphdraw-domain (443.68s) neos-5188808-nattai (462.84s) supportcase26 (537.66s) Losses dano3_3 (121.30s) Time to optimality geomean (shifted geomean by 1.0): on 50 common optimal instances Common optimal instances: 50 Geometric mean of ratios (main/clique): 1.1925 Authors: - Akif ÇÖRDÜK (https://github.com/akifcorduk) - Alice Boucher (https://github.com/aliceb-nv) Approvers: - Chris Maes (https://github.com/chris-maes) URL: https://github.com/NVIDIA/cuopt/pull/937 --- .../linear_programming/cuopt/run_mip.cpp | 16 +- .../cuopt/linear_programming/constants.h | 1 + .../mip/solver_settings.hpp | 1 + cpp/src/branch_and_bound/branch_and_bound.cpp | 96 +- cpp/src/branch_and_bound/branch_and_bound.hpp | 16 +- cpp/src/cuts/cuts.cpp | 769 ++++++++++- cpp/src/cuts/cuts.hpp | 101 +- .../dual_simplex/simplex_solver_settings.hpp | 2 + cpp/src/math_optimization/solver_settings.cu | 1 + .../diversity/diversity_manager.cu | 80 +- .../diversity/diversity_manager.cuh | 3 +- cpp/src/mip_heuristics/diversity/lns/rins.cu | 1 + .../diversity/recombiners/sub_mip.cuh | 1 + .../presolve/conflict_graph/clique_table.cu | 664 +++++---- .../presolve/conflict_graph/clique_table.cuh | 16 +- .../presolve/third_party_presolve.cpp | 3 +- cpp/src/mip_heuristics/problem/problem.cu | 4 + cpp/src/mip_heuristics/problem/problem.cuh | 6 + cpp/src/mip_heuristics/solver.cu | 6 +- cpp/src/pdlp/solve.cu | 5 +- cpp/tests/mip/cuts_test.cu | 1216 +++++++++++++++++ datasets/mip/download_miplib_test_dataset.sh | 1 + 22 files changed, 2649 insertions(+), 360 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 9b79dff8af..e01e533a65 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -183,6 +183,10 @@ int run_single_file(std::string file_path, CUOPT_LOG_ERROR("Parsing MPS failed exiting!"); return -1; } + // Use the benchmark filename for downstream instance-level reporting. + // This keeps per-instance metrics aligned with the run list even if the MPS NAME card differs. + mps_data_model.set_problem_name(base_filename); + if (initial_solution_dir.has_value()) { auto initial_solutions = read_solution_from_dir( initial_solution_dir.value(), base_filename, mps_data_model.get_variable_names()); @@ -209,6 +213,7 @@ int run_single_file(std::string file_path, settings.tolerances.absolute_tolerance = 1e-6; settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; + settings.clique_cuts = -1; settings.seed = 42; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; @@ -413,7 +418,16 @@ int main(int argc, char* argv[]) int reliability_branching = program.get("--reliability-branching"); bool deterministic = program.get("--determinism"); - if (num_cpu_threads < 0) { num_cpu_threads = omp_get_max_threads() / n_gpus; } + if (num_cpu_threads < 0) { + num_cpu_threads = omp_get_max_threads() / n_gpus; + // std::ifstream smt_file("/sys/devices/system/cpu/smt/active"); + // if (smt_file.is_open()) { + // int smt_active = 0; + // smt_file >> smt_active; + // if (smt_active) { num_cpu_threads /= 2; } + // } + num_cpu_threads = std::max(num_cpu_threads, 1); + } if (program.is_used("--out-dir")) { out_dir = program.get("--out-dir"); diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index d9dfbce16d..86becfe06d 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -64,6 +64,7 @@ #define CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS "mip_mixed_integer_rounding_cuts" #define CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS "mip_mixed_integer_gomory_cuts" #define CUOPT_MIP_KNAPSACK_CUTS "mip_knapsack_cuts" +#define CUOPT_MIP_CLIQUE_CUTS "mip_clique_cuts" #define CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS "mip_strong_chvatal_gomory_cuts" #define CUOPT_MIP_REDUCED_COST_STRENGTHENING "mip_reduced_cost_strengthening" #define CUOPT_MIP_CUT_CHANGE_THRESHOLD "mip_cut_change_threshold" diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 6d32cd5ed9..a9e404d14e 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -93,6 +93,7 @@ class mip_solver_settings_t { i_t mir_cuts = -1; i_t mixed_integer_gomory_cuts = -1; i_t knapsack_cuts = -1; + i_t clique_cuts = -1; i_t strong_chvatal_gomory_cuts = -1; i_t reduced_cost_strengthening = -1; f_t cut_change_threshold = 1e-3; diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 6ce9a4f4d0..41d23bc0ff 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -241,9 +242,11 @@ template branch_and_bound_t::branch_and_bound_t( const user_problem_t& user_problem, const simplex_solver_settings_t& solver_settings, - f_t start_time) + f_t start_time, + std::shared_ptr> clique_table) : original_problem_(user_problem), settings_(solver_settings), + clique_table_(std::move(clique_table)), original_lp_(user_problem.handle_ptr, 1, 1, 1), Arow_(1, 1, 0), incumbent_(1), @@ -1967,6 +1970,31 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_relax_soln_.resize(original_lp_.num_rows, original_lp_.num_cols); + if (settings_.clique_cuts != 0 && clique_table_ == nullptr) { + signal_extend_cliques_.store(false, std::memory_order_release); + typename ::cuopt::linear_programming::mip_solver_settings_t::tolerances_t + tolerances_for_clique{}; + tolerances_for_clique.presolve_absolute_tolerance = settings_.primal_tol; + tolerances_for_clique.absolute_tolerance = settings_.primal_tol; + tolerances_for_clique.relative_tolerance = settings_.zero_tol; + tolerances_for_clique.integrality_tolerance = settings_.integer_tol; + tolerances_for_clique.absolute_mip_gap = settings_.absolute_mip_gap_tol; + tolerances_for_clique.relative_mip_gap = settings_.relative_mip_gap_tol; + auto* signal_ptr = &signal_extend_cliques_; + clique_table_future_ = + std::async(std::launch::async, + [this, + tolerances_for_clique, + signal_ptr]() -> std::shared_ptr> { + user_problem_t problem_copy = original_problem_; + cuopt::timer_t timer(std::numeric_limits::infinity()); + std::shared_ptr> table; + detail::find_initial_cliques( + problem_copy, tolerances_for_clique, &table, timer, false, signal_ptr); + return table; + }); + } + i_t original_rows = original_lp_.num_rows; simplex_solver_settings_t lp_settings = settings_; lp_settings.inside_mip = 1; @@ -2002,14 +2030,16 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut exploration_stats_.total_lp_iters = root_relax_soln_.iterations; exploration_stats_.total_lp_solve_time = toc(exploration_stats_.start_time); + auto finish_clique_thread = [this]() { + if (clique_table_future_.valid()) { + signal_extend_cliques_.store(true, std::memory_order_release); + clique_table_ = clique_table_future_.get(); + } + }; + if (root_status == lp_status_t::INFEASIBLE) { settings_.log.printf("MIP Infeasible\n"); - // FIXME: rarely dual simplex detects infeasible whereas it is feasible. - // to add a small safety net, check if there is a primal solution already. - // Uncomment this if the issue with cost266-UUE is resolved - // if (settings.heuristic_preemption_callback != nullptr) { - // settings.heuristic_preemption_callback(); - // } + finish_clique_thread(); return mip_status_t::INFEASIBLE; } if (root_status == lp_status_t::UNBOUNDED) { @@ -2017,23 +2047,27 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut if (settings_.heuristic_preemption_callback != nullptr) { settings_.heuristic_preemption_callback(); } + finish_clique_thread(); return mip_status_t::UNBOUNDED; } if (root_status == lp_status_t::TIME_LIMIT) { solver_status_ = mip_status_t::TIME_LIMIT; set_final_solution(solution, -inf); + finish_clique_thread(); return solver_status_; } if (root_status == lp_status_t::WORK_LIMIT) { solver_status_ = mip_status_t::WORK_LIMIT; set_final_solution(solution, -inf); + finish_clique_thread(); return solver_status_; } if (root_status == lp_status_t::NUMERICAL_ISSUES) { solver_status_ = mip_status_t::NUMERICAL; set_final_solution(solution, -inf); + finish_clique_thread(); return solver_status_; } @@ -2064,6 +2098,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut if (num_fractional == 0) { set_solution_at_root(solution, cut_info); + finish_clique_thread(); return mip_status_t::OPTIMAL; } @@ -2078,8 +2113,16 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } cut_pool_t cut_pool(original_lp_.num_cols, settings_); - cut_generation_t cut_generation( - cut_pool, original_lp_, settings_, Arow_, new_slacks_, var_types_); + cut_generation_t cut_generation(cut_pool, + original_lp_, + settings_, + Arow_, + new_slacks_, + var_types_, + original_problem_, + clique_table_, + &clique_table_future_, + &signal_extend_cliques_); std::vector saved_solution; #ifdef CHECK_CUTS_AGAINST_SAVED_SOLUTION @@ -2090,7 +2133,8 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut f_t last_objective = root_objective_; f_t root_relax_objective = root_objective_; - i_t cut_pool_size = 0; + f_t cut_generation_start_time = tic(); + i_t cut_pool_size = 0; for (i_t cut_pass = 0; cut_pass < settings_.max_cut_passes; cut_pass++) { if (num_fractional == 0) { set_solution_at_root(solution, cut_info); @@ -2109,16 +2153,25 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut #endif // Generate cuts and add them to the cut pool - f_t cut_start_time = tic(); - cut_generation.generate_cuts(original_lp_, - settings_, - Arow_, - new_slacks_, - var_types_, - basis_update, - root_relax_soln_.x, - basic_list, - nonbasic_list); + f_t cut_start_time = tic(); + bool problem_feasible = cut_generation.generate_cuts(original_lp_, + settings_, + Arow_, + new_slacks_, + var_types_, + basis_update, + root_relax_soln_.x, + root_relax_soln_.z, + basic_list, + nonbasic_list, + exploration_stats_.start_time); + if (!problem_feasible) { + if (settings_.heuristic_preemption_callback != nullptr) { + settings_.heuristic_preemption_callback(); + } + finish_clique_thread(); + return mip_status_t::INFEASIBLE; + } f_t cut_generation_time = toc(cut_start_time); if (cut_generation_time > 1.0) { settings_.log.debug("Cut generation time %.2f seconds\n", cut_generation_time); @@ -2339,8 +2392,9 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } print_cut_info(settings_, cut_info); - + f_t cut_generation_time = toc(cut_generation_start_time); if (cut_info.has_cuts()) { + settings_.log.printf("Cut generation time: %.2f seconds\n", cut_generation_time); settings_.log.printf("Cut pool size : %d\n", cut_pool_size); settings_.log.printf("Size with cuts : %d constraints, %d variables, %d nonzeros\n", original_lp_.num_rows, diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index a13d5cedcf..f44142f37f 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -32,9 +32,17 @@ #include +#include #include +#include +#include #include +namespace cuopt::linear_programming::detail { +template +struct clique_table_t; +} + namespace cuopt::linear_programming::dual_simplex { enum class mip_status_t { @@ -68,7 +76,8 @@ class branch_and_bound_t { public: branch_and_bound_t(const user_problem_t& user_problem, const simplex_solver_settings_t& solver_settings, - f_t start_time); + f_t start_time, + std::shared_ptr> clique_table = nullptr); // Set an initial guess based on the user_problem. This should be called before solve. void set_initial_guess(const std::vector& user_guess) { guess_ = user_guess; } @@ -106,8 +115,6 @@ class branch_and_bound_t { void set_concurrent_lp_root_solve(bool enable) { enable_concurrent_lp_root_solve_ = enable; } - bool stop_for_time_limit(mip_solution_t& solution); - // Repair a low-quality solution from the heuristics. bool repair_solution(const std::vector& leaf_edge_norms, const std::vector& potential_solution, @@ -141,6 +148,9 @@ class branch_and_bound_t { private: const user_problem_t& original_problem_; const simplex_solver_settings_t settings_; + std::shared_ptr> clique_table_; + std::future>> clique_table_future_; + std::atomic signal_extend_cliques_{false}; work_limit_context_t work_unit_context_{"B&B"}; diff --git a/cpp/src/cuts/cuts.cpp b/cpp/src/cuts/cuts.cpp index cc2555611a..f4b3106112 100644 --- a/cpp/src/cuts/cuts.cpp +++ b/cpp/src/cuts/cuts.cpp @@ -9,11 +9,486 @@ #include #include +#include +#include + +#include +#include +#include +#include #include namespace cuopt::linear_programming::dual_simplex { +namespace { + +#define DEBUG_CLIQUE_CUTS 0 +#define CHECK_WORKSPACE 0 + +enum class clique_cut_build_status_t : int8_t { NO_CUT = 0, CUT_ADDED = 1, INFEASIBLE = 2 }; + +#if DEBUG_CLIQUE_CUTS +#define CLIQUE_CUTS_DEBUG(...) \ + do { \ + std::fprintf(stderr, "[DEBUG_CLIQUE_CUTS] "); \ + std::fprintf(stderr, __VA_ARGS__); \ + std::fprintf(stderr, "\n"); \ + } while (0) +#else +#define CLIQUE_CUTS_DEBUG(...) \ + do { \ + } while (0) +#endif + +template +clique_cut_build_status_t build_clique_cut(const std::vector& clique_vertices, + i_t num_vars, + const std::vector& var_types, + const std::vector& lower_bounds, + const std::vector& upper_bounds, + const std::vector& xstar, + f_t bound_tol, + f_t min_violation, + sparse_vector_t& cut, + f_t& cut_rhs, + f_t* work_estimate, + f_t max_work_estimate) +{ + if (clique_vertices.size() < 2) { return clique_cut_build_status_t::NO_CUT; } + const f_t clique_size = static_cast(clique_vertices.size()); + CLIQUE_CUTS_DEBUG("build_clique_cut start clique_size=%lld", + static_cast(clique_vertices.size())); + const f_t sort_work = clique_size > 0.0 ? 2.0 * clique_size * std::log2(clique_size + 1.0) : 0.0; + const f_t dot_work = 2.0 * clique_size; + const f_t estimated_work = 9.0 * clique_size + sort_work + dot_work; + if (add_work_estimate(estimated_work, work_estimate, max_work_estimate)) { + CLIQUE_CUTS_DEBUG("build_clique_cut skip work_limit clique_size=%lld work=%g limit=%g", + static_cast(clique_vertices.size()), + work_estimate == nullptr ? -1.0 : static_cast(*work_estimate), + static_cast(max_work_estimate)); + return clique_cut_build_status_t::NO_CUT; + } + + cuopt_assert(num_vars > 0, "Clique cut num_vars must be positive"); + cuopt_assert(static_cast(num_vars) <= lower_bounds.size(), + "Clique cut lower bounds size mismatch"); + cuopt_assert(static_cast(num_vars) <= xstar.size(), "Clique cut xstar size mismatch"); + + cut.i.clear(); + cut.x.clear(); + i_t num_complements = 0; + std::unordered_set seen_original; + std::unordered_set seen_complement; + seen_original.reserve(clique_vertices.size()); + seen_complement.reserve(clique_vertices.size()); + for (const auto vertex_idx : clique_vertices) { + cuopt_assert(vertex_idx >= 0 && vertex_idx < 2 * num_vars, "Clique vertex out of range"); + const i_t var_idx = vertex_idx % num_vars; + const bool complement = vertex_idx >= num_vars; + const f_t lower_bound = lower_bounds[var_idx]; + const f_t upper_bound = upper_bounds[var_idx]; + + cuopt_assert(var_types[var_idx] != variable_type_t::CONTINUOUS, + "Clique contains continuous variable"); + cuopt_assert(lower_bound >= -bound_tol, "Clique variable lower bound below zero"); + cuopt_assert(upper_bound <= 1 + bound_tol, "Clique variable upper bound above one"); + + // we store the cut in the form of >= 1, for easy violation check with dot product + // that's why compelements have 1 as coeff and normal vars have -1 + if (complement) { + if (seen_original.count(var_idx) > 0) { + // FIXME: this is temporary, fix all the vars of all other vars in the clique + return clique_cut_build_status_t::NO_CUT; + CLIQUE_CUTS_DEBUG("build_clique_cut infeasible var=%lld appears as variable and complement", + static_cast(var_idx)); + return clique_cut_build_status_t::INFEASIBLE; + } + cuopt_assert(seen_complement.count(var_idx) == 0, "Duplicate complement in clique"); + seen_complement.insert(var_idx); + num_complements++; + cut.i.push_back(var_idx); + cut.x.push_back(1.0); + } else { + if (seen_complement.count(var_idx) > 0) { + // FIXME: this is temporary, fix all the vars of all other vars in the clique + return clique_cut_build_status_t::NO_CUT; + CLIQUE_CUTS_DEBUG("build_clique_cut infeasible var=%lld appears as variable and complement", + static_cast(var_idx)); + return clique_cut_build_status_t::INFEASIBLE; + } + cuopt_assert(seen_original.count(var_idx) == 0, "Duplicate variable in clique"); + seen_original.insert(var_idx); + cut.i.push_back(var_idx); + cut.x.push_back(-1.0); + } + } + + if (cut.i.empty()) { + CLIQUE_CUTS_DEBUG("build_clique_cut no_cut empty support"); + return clique_cut_build_status_t::NO_CUT; + } + + cut_rhs = static_cast(num_complements - 1); + cut.sort(); + + const f_t dot = cut.dot(xstar); + const f_t violation = cut_rhs - dot; + if (violation > min_violation) { + CLIQUE_CUTS_DEBUG( + "build_clique_cut accepted nz=%lld rhs=%g dot=%g violation=%g threshold=%g complements=%lld", + static_cast(cut.i.size()), + static_cast(cut_rhs), + static_cast(dot), + static_cast(violation), + static_cast(min_violation), + static_cast(num_complements)); + return clique_cut_build_status_t::CUT_ADDED; + } + CLIQUE_CUTS_DEBUG( + "build_clique_cut rejected nz=%lld rhs=%g dot=%g violation=%g threshold=%g complements=%lld", + static_cast(cut.i.size()), + static_cast(cut_rhs), + static_cast(dot), + static_cast(violation), + static_cast(min_violation), + static_cast(num_complements)); + return clique_cut_build_status_t::NO_CUT; +} + +template +struct bk_bitset_context_t { + const std::vector>& adj; + const std::vector& weights; + f_t min_weight; + i_t max_calls; + f_t start_time; + f_t time_limit; + size_t words; + f_t* work_estimate; + f_t max_work_estimate; + i_t num_calls{0}; + bool work_limit_reached{false}; + bool call_limit_reached{false}; + std::vector> cliques; + + bool add_work(f_t accesses) + { + return add_work_estimate(accesses, work_estimate, max_work_estimate, &work_limit_reached); + } + + bool over_work_limit() const + { + if (work_limit_reached) { return true; } + if (work_estimate == nullptr) { return false; } + return *work_estimate > max_work_estimate; + } + + bool over_call_limit() const { return call_limit_reached || num_calls >= max_calls; } +}; + +inline size_t bitset_words(size_t n) { return (n + 63) / 64; } + +inline bool bitset_any(const std::vector& bs) +{ + for (auto word : bs) { + if (word != 0) { return true; } + } + return false; +} + +inline void bitset_set(std::vector& bs, size_t idx) +{ + bs[idx >> 6] |= (uint64_t(1) << (idx & 63)); +} + +inline void bitset_clear(std::vector& bs, size_t idx) +{ + bs[idx >> 6] &= ~(uint64_t(1) << (idx & 63)); +} + +template +f_t sum_weights_bitset(const std::vector& bs, const std::vector& weights) +{ + f_t sum = 0.0; + for (size_t w = 0; w < bs.size(); ++w) { + uint64_t word = bs[w]; + while (word) { + const int bit = __builtin_ctzll(word); + const size_t idx = w * 64 + static_cast(bit); + sum += weights[idx]; + word &= (word - 1); + } + } + return sum; +} + +template +void bron_kerbosch(bk_bitset_context_t& ctx, + std::vector& R, // current clique + std::vector& P, // potential candidates + std::vector& X, // already in the clique + f_t weight_R) +{ + if (ctx.over_work_limit() || ctx.over_call_limit()) { return; } + if (toc(ctx.start_time) >= ctx.time_limit) { return; } + ctx.num_calls++; + // stop the recursion, for perf reasons + if (ctx.num_calls > ctx.max_calls) { + ctx.call_limit_reached = true; + return; + } + if (ctx.add_work(static_cast(4 * ctx.words))) { return; } + + // if P and X are empty, we are at maximal clique + if (!bitset_any(P) && !bitset_any(X)) { + // if the weight is enough, add and exit + if (weight_R >= ctx.min_weight) { + ctx.add_work(static_cast(R.size())); + ctx.cliques.push_back(R); + } + return; + } + + const f_t sumP = sum_weights_bitset(P, ctx.weights); + // check if all P is added to clique, would we exceed the weight? + if (weight_R + sumP < ctx.min_weight) { return; } + + i_t pivot = -1; + i_t max_deg = -1; + i_t pivot_vertices_examined = 0; + // pivoting rule according to the highest degree vertex + // TODO try other pivoting strategies, we can also implement some online learning like MAB + for (size_t w = 0; w < ctx.words; ++w) { + // union of P and X + uint64_t word = P[w] | X[w]; + while (word) { + pivot_vertices_examined++; + // least significant set bit idnex + const int bit = __builtin_ctzll(word); + // overall vertex index + const i_t v = static_cast(w * 64 + static_cast(bit)); + // clear the least significant set bit (v) + word &= (word - 1); + i_t count = 0; + // count the number of neighbors of v in P + for (size_t k = 0; k < ctx.words; ++k) { + count += __builtin_popcountll(P[k] & ctx.adj[v][k]); + } + // chose the highest degree v as the pivot + // we choose the highest degree as the pivot to reduce the recursion size + // later in this function we recurse on the candidate P / N(v) + // so it is good to maximize P n N(v) + if (count > max_deg) { + max_deg = count; + pivot = v; + } + } + } + ctx.add_work(static_cast(2 * ctx.words) + + static_cast(pivot_vertices_examined) * static_cast(2 * ctx.words)); + + std::vector candidates; + candidates.reserve(ctx.weights.size()); + cuopt_assert(pivot >= 0, "Pivot must be valid when P or X is non-empty"); + for (size_t w = 0; w < ctx.words; ++w) { + // P / N(pivot) + uint64_t word = P[w] & ~ctx.adj[pivot][w]; + while (word) { + const int bit = __builtin_ctzll(word); + const i_t v = static_cast(w * 64 + static_cast(bit)); + word &= (word - 1); + candidates.push_back(v); + } + } + const i_t num_candidates = static_cast(candidates.size()); + ctx.add_work(static_cast(2 * ctx.words + num_candidates)); + ctx.add_work(static_cast(num_candidates) * static_cast(7 * ctx.words + 6)); + // note that candidates will include pivot if it is in P + for (auto v : candidates) { + if (ctx.over_call_limit()) { + ctx.call_limit_reached = true; + return; + } + if (toc(ctx.start_time) >= ctx.time_limit) { return; } + + R.push_back(v); + std::vector P_next(ctx.words, 0); + std::vector X_next(ctx.words, 0); + for (size_t k = 0; k < ctx.words; ++k) { + P_next[k] = P[k] & ctx.adj[v][k]; + X_next[k] = X[k] & ctx.adj[v][k]; + } + + bron_kerbosch(ctx, R, P_next, X_next, weight_R + ctx.weights[v]); + if (ctx.over_work_limit()) { return; } + if (ctx.over_call_limit()) { + ctx.call_limit_reached = true; + return; + } + R.pop_back(); + bitset_clear(P, static_cast(v)); + bitset_set(X, static_cast(v)); + } +} + +template +void extend_clique_vertices(std::vector& clique_vertices, + detail::clique_table_t& graph, + const std::vector& xstar, + const std::vector& reduced_costs, + i_t num_vars, + f_t integer_tol, + f_t start_time, + f_t time_limit, + f_t* work_estimate, + f_t max_work_estimate) +{ + if (toc(start_time) >= time_limit) { return; } + if (clique_vertices.empty()) { return; } +#if DEBUG_CLIQUE_CUTS + const size_t initial_clique_vertices = clique_vertices.size(); +#endif + CLIQUE_CUTS_DEBUG("extend_clique_vertices start size=%lld", + static_cast(clique_vertices.size())); + const f_t initial_clique_size = static_cast(clique_vertices.size()); + + i_t smallest_degree = std::numeric_limits::max(); + i_t smallest_degree_var = -1; + for (auto v : clique_vertices) { + if (toc(start_time) >= time_limit) { return; } + i_t degree = graph.get_degree_of_var(v); + if (degree < smallest_degree) { + smallest_degree = degree; + smallest_degree_var = v; + } + } + + auto adj_set = graph.get_adj_set_of_var(smallest_degree_var); + std::unordered_set clique_members(clique_vertices.begin(), clique_vertices.end()); + std::vector candidates; + candidates.reserve(adj_set.size()); + // the candidate list if only the integer valued vertices + for (const auto& candidate : adj_set) { + if (toc(start_time) >= time_limit) { return; } + if (clique_members.count(candidate) != 0) { continue; } + i_t var_idx = candidate % num_vars; + f_t value = candidate >= num_vars ? (1.0 - xstar[var_idx]) : xstar[var_idx]; + if (std::abs(value - std::round(value)) <= integer_tol) { candidates.push_back(candidate); } + } + CLIQUE_CUTS_DEBUG( + "extend_clique_vertices anchor=%lld degree=%lld adj_size=%lld integer_candidates=%lld", + static_cast(smallest_degree_var), + static_cast(smallest_degree), + static_cast(adj_set.size()), + static_cast(candidates.size())); + const f_t candidate_size = static_cast(candidates.size()); + const f_t sort_work = + candidate_size > 0.0 ? 2.0 * candidate_size * std::log2(candidate_size + 1.0) : 0.0; + const f_t adj_set_build_cost = 2.0 * static_cast(adj_set.size()); + const f_t adj_check_cost = 5.0; + const f_t estimated_preloop_work = 2.0 * initial_clique_size + adj_set_build_cost + + 3.0 * static_cast(adj_set.size()) + sort_work + + 2.0 * candidate_size; + if (add_work_estimate(estimated_preloop_work, work_estimate, max_work_estimate)) { + CLIQUE_CUTS_DEBUG("extend_clique_vertices skip work_limit work=%g limit=%g", + work_estimate == nullptr ? -1.0 : static_cast(*work_estimate), + static_cast(max_work_estimate)); + return; + } + + // sort the candidates by reduced cost. + // smaller reduce cost disturbs dual simplex less + // less refactors and less iterations after resolve. + // it also increases the cut's effectiveness by keeping xstar not disturbed much + // if it is disturbed too much, the cut might become non-binding + auto reduced_cost = [&](i_t vertex_idx) -> f_t { + i_t var_idx = vertex_idx % num_vars; + cuopt_assert(var_idx >= 0 && var_idx < static_cast(reduced_costs.size()), + "Variable index out of range"); + f_t rc = reduced_costs[var_idx]; + if (!std::isfinite(rc)) { rc = 0.0; } + return vertex_idx >= num_vars ? -rc : rc; + }; + + std::sort(candidates.begin(), candidates.end(), [&](i_t a, i_t b) { + return reduced_cost(a) < reduced_cost(b); + }); + + for (const auto candidate : candidates) { + bool add = true; + i_t checks = 0; + for (const auto v : clique_vertices) { + checks++; + if (!graph.check_adjacency(candidate, v)) { + add = false; + break; + } + } + if (add_work_estimate( + adj_check_cost * static_cast(checks), work_estimate, max_work_estimate)) { + break; + } + if (add) { + clique_vertices.push_back(candidate); + clique_members.insert(candidate); + } + } + CLIQUE_CUTS_DEBUG("extend_clique_vertices done start=%lld final=%lld added=%lld", + static_cast(initial_clique_vertices), + static_cast(clique_vertices.size()), + static_cast(clique_vertices.size() - initial_clique_vertices)); +} + +} // namespace + +// This function is only used in tests +std::vector> find_maximal_cliques_for_test( + const std::vector>& adjacency_list, + const std::vector& weights, + double min_weight, + int max_calls, + double time_limit) +{ + const size_t n_vertices = adjacency_list.size(); + if (n_vertices == 0) { return {}; } + cuopt_assert(weights.size() == n_vertices, "Weights size mismatch in clique test helper"); + cuopt_assert(max_calls > 0, "max_calls must be positive in clique test helper"); + + const size_t words = bitset_words(n_vertices); + std::vector> adj_bitset(n_vertices, std::vector(words, 0)); + for (size_t v = 0; v < n_vertices; ++v) { + for (const auto& nbr : adjacency_list[v]) { + cuopt_assert(nbr >= 0 && static_cast(nbr) < n_vertices, + "Neighbor index out of range in clique test helper"); + bitset_set(adj_bitset[v], static_cast(nbr)); + } + } + + double work_estimate = 0.0; + const double max_work_estimate = std::numeric_limits::infinity(); + const double start_time = tic(); + + bk_bitset_context_t ctx{adj_bitset, + weights, + min_weight, + max_calls, + start_time, + time_limit, + words, + &work_estimate, + max_work_estimate}; + + std::vector R; + std::vector P(words, 0); + std::vector X(words, 0); + for (size_t idx = 0; idx < n_vertices; ++idx) { + bitset_set(P, idx); + } + bron_kerbosch(ctx, R, P, X, 0.0); + return ctx.cliques; +} + template void cut_pool_t::add_cut(cut_type_t cut_type, const sparse_vector_t& cut, @@ -553,15 +1028,17 @@ f_t knapsack_generation_t::solve_knapsack_problem(const std::vector -void cut_generation_t::generate_cuts(const lp_problem_t& lp, +bool cut_generation_t::generate_cuts(const lp_problem_t& lp, const simplex_solver_settings_t& settings, csr_matrix_t& Arow, const std::vector& new_slacks, const std::vector& var_types, basis_update_mpf_t& basis_update, const std::vector& xstar, + const std::vector& reduced_costs, const std::vector& basic_list, - const std::vector& nonbasic_list) + const std::vector& nonbasic_list, + f_t start_time) { // Generate Gomory and CG Cuts if (settings.mixed_integer_gomory_cuts != 0 || settings.strong_chvatal_gomory_cuts != 0) { @@ -593,6 +1070,21 @@ void cut_generation_t::generate_cuts(const lp_problem_t& lp, settings.log.debug("MIR and CG cut generation time %.2f seconds\n", cut_generation_time); } } + + // Generate Clique cuts (last to give background clique table generation maximum time) + if (settings.clique_cuts != 0) { + f_t cut_start_time = tic(); + bool feasible = generate_clique_cuts(lp, settings, var_types, xstar, reduced_costs, start_time); + if (!feasible) { + settings.log.printf("Clique cuts proved infeasible\n"); + return false; + } + f_t cut_generation_time = toc(cut_start_time); + if (cut_generation_time > 1.0) { + settings.log.debug("Clique cut generation time %.2f seconds\n", cut_generation_time); + } + } + return true; } template @@ -615,6 +1107,279 @@ void cut_generation_t::generate_knapsack_cuts( } } +template +bool cut_generation_t::generate_clique_cuts( + const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + const std::vector& var_types, + const std::vector& xstar, + const std::vector& reduced_costs, + f_t start_time) +{ + if (settings.clique_cuts == 0) { return true; } + if (toc(start_time) >= settings.time_limit) { return true; } + + const i_t num_vars = user_problem_.num_cols; + CLIQUE_CUTS_DEBUG("generate_clique_cuts start num_vars=%lld time_limit=%g elapsed=%g", + static_cast(num_vars), + static_cast(settings.time_limit), + static_cast(toc(start_time))); + + if (clique_table_ == nullptr && clique_table_future_ != nullptr && + clique_table_future_->valid()) { + CLIQUE_CUTS_DEBUG("generate_clique_cuts signaling background thread and waiting"); + if (signal_extend_) { signal_extend_->store(true, std::memory_order_release); } + clique_table_ = clique_table_future_->get(); + clique_table_future_ = nullptr; + if (clique_table_) { + CLIQUE_CUTS_DEBUG("generate_clique_cuts received clique table first=%lld addtl=%lld", + static_cast(clique_table_->first.size()), + static_cast(clique_table_->addtl_cliques.size())); + } + } + + if (clique_table_ == nullptr) { + CLIQUE_CUTS_DEBUG("generate_clique_cuts no clique table available, skipping"); + return true; + } + CLIQUE_CUTS_DEBUG("generate_clique_cuts using clique table first=%lld addtl=%lld", + static_cast(clique_table_->first.size()), + static_cast(clique_table_->addtl_cliques.size())); + + if (clique_table_->first.empty() && clique_table_->addtl_cliques.empty()) { + CLIQUE_CUTS_DEBUG("generate_clique_cuts empty clique table, nothing to separate"); + return true; + } + + cuopt_assert(clique_table_->n_variables == num_vars, "Clique table variable count mismatch"); + cuopt_assert(static_cast(num_vars) <= xstar.size(), "Clique cut xstar size mismatch"); + + const f_t min_violation = std::max(settings.primal_tol, static_cast(1e-6)); + const f_t bound_tol = settings.primal_tol; + const f_t min_weight = 1.0 + min_violation; + // TODO this can be problem dependent + const i_t max_calls = 100000; + f_t work_estimate = 0.0; + const f_t max_work_estimate = 1e8; + + cuopt_assert(user_problem_.var_types.size() == static_cast(num_vars), + "User problem var_types size mismatch"); + + std::vector vertices; + std::vector weights; + vertices.reserve(num_vars * 2); + weights.reserve(num_vars * 2); + + // create the sub graph induced by fractional binary variables + for (i_t j = 0; j < num_vars; ++j) { + if (user_problem_.var_types[j] == variable_type_t::CONTINUOUS) { continue; } + const f_t lower_bound = user_problem_.lower[j]; + const f_t upper_bound = user_problem_.upper[j]; + if (lower_bound < -bound_tol || upper_bound > 1 + bound_tol) { continue; } + const f_t xj = xstar[j]; + if (std::abs(xj - std::round(xj)) <= settings.integer_tol) { continue; } + vertices.push_back(j); + weights.push_back(xj); + vertices.push_back(j + num_vars); + weights.push_back(1.0 - xj); + } + // Coarse loop estimate: variable scans + selected vertex/weight writes + work_estimate += 4.0 * static_cast(num_vars) + 2.0 * static_cast(vertices.size()); + if (work_estimate > max_work_estimate) { return true; } + + if (vertices.empty()) { + CLIQUE_CUTS_DEBUG("generate_clique_cuts no fractional binary vertices"); + return true; + } + CLIQUE_CUTS_DEBUG("generate_clique_cuts fractional subgraph vertices=%lld (literals=%lld)", + static_cast(vertices.size() / 2), + static_cast(vertices.size())); + + std::vector vertex_to_local(2 * num_vars, -1); + std::vector in_subgraph(2 * num_vars, 0); + for (size_t idx = 0; idx < vertices.size(); ++idx) { + if (toc(start_time) >= settings.time_limit) { return true; } + const i_t vertex_idx = vertices[idx]; + vertex_to_local[vertex_idx] = static_cast(idx); + in_subgraph[vertex_idx] = 1; + } + work_estimate += 3.0 * static_cast(vertices.size()); + if (work_estimate > max_work_estimate) { return true; } + + std::vector> adj_local(vertices.size()); + size_t total_adj_entries = 0; + size_t kept_adj_entries = 0; + for (size_t idx = 0; idx < vertices.size(); ++idx) { + if (toc(start_time) >= settings.time_limit) { return true; } + i_t vertex_idx = vertices[idx]; + // returns the complement as well + auto adj_set = clique_table_->get_adj_set_of_var(vertex_idx); + total_adj_entries += adj_set.size(); + auto& adj = adj_local[idx]; + adj.reserve(adj_set.size()); + for (const auto neighbor : adj_set) { + if (toc(start_time) >= settings.time_limit) { return true; } + cuopt_assert(neighbor >= 0 && neighbor < 2 * num_vars, "Neighbor out of range"); + if (!in_subgraph[neighbor]) { continue; } + i_t local_neighbor = vertex_to_local[neighbor]; + cuopt_assert(local_neighbor >= 0, "Local neighbor out of range"); + adj.push_back(local_neighbor); + } + kept_adj_entries += adj.size(); +#ifdef ASSERT_MODE + { + std::unordered_set adj_global; + adj_global.reserve(adj.size()); + for (const auto neighbor : adj) { + i_t v = vertices[neighbor]; + cuopt_assert(adj_global.insert(v).second, "Duplicate neighbor in adjacency list"); + i_t complement = (v >= num_vars) ? (v - num_vars) : (v + num_vars); + cuopt_assert(adj_global.find(complement) == adj_global.end(), + "Adjacency list contains complementing variable"); + } + } +#endif + } + work_estimate += static_cast(vertices.size()) + static_cast(total_adj_entries) + + 2.0 * static_cast(kept_adj_entries); + if (work_estimate > max_work_estimate) { return true; } + CLIQUE_CUTS_DEBUG("generate_clique_cuts adjacency raw_entries=%lld kept_entries=%lld", + static_cast(total_adj_entries), + static_cast(kept_adj_entries)); + + const size_t words = bitset_words(vertices.size()); + std::vector> adj_bitset(vertices.size(), std::vector(words, 0)); + size_t local_adj_entries = 0; + for (size_t v = 0; v < adj_local.size(); ++v) { + local_adj_entries += adj_local[v].size(); + for (const auto neighbor : adj_local[v]) { + bitset_set(adj_bitset[v], static_cast(neighbor)); + } + } + work_estimate += static_cast(adj_local.size()) + 3.0 * static_cast(local_adj_entries); + if (work_estimate > max_work_estimate) { return true; } + CLIQUE_CUTS_DEBUG("generate_clique_cuts bitset graph words=%lld local_entries=%lld", + static_cast(words), + static_cast(local_adj_entries)); + + bk_bitset_context_t ctx{adj_bitset, + weights, + min_weight, + max_calls, + start_time, + settings.time_limit, + words, + &work_estimate, + max_work_estimate}; + std::vector R; + std::vector P(words, 0); + std::vector X(words, 0); + for (size_t idx = 0; idx < vertices.size(); ++idx) { + bitset_set(P, idx); + } + work_estimate += 2.0 * static_cast(vertices.size()); + if (work_estimate > max_work_estimate) { return true; } + bron_kerbosch(ctx, R, P, X, 0.0); + CLIQUE_CUTS_DEBUG( + "generate_clique_cuts maximal cliques found=%lld bk_calls=%lld work=%g work_limit=%d " + "call_limit=%d", + static_cast(ctx.cliques.size()), + static_cast(ctx.num_calls), + static_cast(work_estimate), + ctx.over_work_limit() ? 1 : 0, + ctx.over_call_limit() ? 1 : 0); + if (ctx.over_call_limit()) { return true; } + if (ctx.over_work_limit()) { return true; } + if (toc(start_time) >= settings.time_limit) { return true; } + if (work_estimate > max_work_estimate) { return true; } + + sparse_vector_t cut(lp.num_cols, 0); + f_t cut_rhs = 0.0; +#if DEBUG_CLIQUE_CUTS + size_t candidate_cliques = 0; + size_t added_cuts = 0; + size_t rejected_cliques = 0; + size_t extension_gain = 0; +#endif + for (auto& clique_local : ctx.cliques) { + if (toc(start_time) >= settings.time_limit) { return true; } +#if DEBUG_CLIQUE_CUTS + candidate_cliques++; +#endif + std::vector clique_vertices; + clique_vertices.reserve(clique_local.size()); + for (auto local_idx : clique_local) { + clique_vertices.push_back(vertices[local_idx]); + } + work_estimate += 3.0 * static_cast(clique_local.size()); + if (work_estimate > max_work_estimate) { return true; } +#if DEBUG_CLIQUE_CUTS + const size_t size_before_extension = clique_vertices.size(); +#endif + extend_clique_vertices(clique_vertices, + *clique_table_, + xstar, + reduced_costs, + num_vars, + settings.integer_tol, + start_time, + settings.time_limit, + &work_estimate, + max_work_estimate); +#if DEBUG_CLIQUE_CUTS + extension_gain += clique_vertices.size() - size_before_extension; +#endif + if (work_estimate > max_work_estimate) { return true; } + if (toc(start_time) >= settings.time_limit) { return true; } + const auto build_status = build_clique_cut(clique_vertices, + num_vars, + var_types, + user_problem_.lower, + user_problem_.upper, + xstar, + bound_tol, + min_violation, + cut, + cut_rhs, + &work_estimate, + max_work_estimate); + if (work_estimate > max_work_estimate) { return true; } + if (build_status == clique_cut_build_status_t::INFEASIBLE) { + settings.log.debug("Detected contradictory variable/complement clique\n"); + CLIQUE_CUTS_DEBUG( + "generate_clique_cuts infeasible clique detected after processing=%lld cliques", + static_cast(candidate_cliques)); + return false; + } + if (build_status == clique_cut_build_status_t::CUT_ADDED) { + cut_pool_.add_cut(cut_type_t::CLIQUE, cut, cut_rhs); +#if DEBUG_CLIQUE_CUTS + added_cuts++; + CLIQUE_CUTS_DEBUG("generate_clique_cuts added cut nz=%lld rhs=%g clique_size=%lld", + static_cast(cut.i.size()), + static_cast(cut_rhs), + static_cast(clique_vertices.size())); +#endif + } +#if DEBUG_CLIQUE_CUTS + else { + rejected_cliques++; + } +#endif + } +#if DEBUG_CLIQUE_CUTS + CLIQUE_CUTS_DEBUG( + "generate_clique_cuts done candidate_cliques=%lld added=%lld rejected=%lld extension_gain=%lld " + "final_work=%g", + static_cast(candidate_cliques), + static_cast(added_cuts), + static_cast(rejected_cliques), + static_cast(extension_gain), + static_cast(work_estimate)); +#endif + return true; +} + template void cut_generation_t::generate_mir_cuts( const lp_problem_t& lp, diff --git a/cpp/src/cuts/cuts.hpp b/cpp/src/cuts/cuts.hpp index 4f55e96e4d..fffa4b10fe 100644 --- a/cpp/src/cuts/cuts.hpp +++ b/cpp/src/cuts/cuts.hpp @@ -15,6 +15,9 @@ #include #include +#include +#include +#include #include #include #include @@ -22,6 +25,11 @@ #include #include +namespace cuopt::linear_programming::detail { +template +struct clique_table_t; +} + namespace cuopt::linear_programming::dual_simplex { enum cut_type_t : int8_t { @@ -29,9 +37,31 @@ enum cut_type_t : int8_t { MIXED_INTEGER_ROUNDING = 1, KNAPSACK = 2, CHVATAL_GOMORY = 3, - MAX_CUT_TYPE = 4 + CLIQUE = 4, + MAX_CUT_TYPE = 5 +}; + +template +struct cut_gap_closure_t { + f_t initial_gap{0.0}; + f_t final_gap{0.0}; + f_t gap_closed{0.0}; + f_t gap_closed_ratio{0.0}; }; +template +cut_gap_closure_t compute_cut_gap_closure(f_t objective_reference, + f_t objective_before_cuts, + f_t objective_after_cuts) +{ + const f_t initial_gap = std::abs(objective_reference - objective_before_cuts); + const f_t final_gap = std::abs(objective_reference - objective_after_cuts); + const f_t gap_closed = initial_gap - final_gap; + constexpr f_t eps = static_cast(1e-12); + const f_t gap_closed_ratio = initial_gap > eps ? gap_closed / initial_gap : static_cast(0.0); + return {initial_gap, final_gap, gap_closed, gap_closed_ratio}; +} + template struct cut_info_t { bool has_cuts() const @@ -48,8 +78,9 @@ struct cut_info_t { num_cuts[static_cast(cut_type)]++; } } - const char* cut_type_names[MAX_CUT_TYPE] = {"Gomory ", "MIR ", "Knapsack ", "Strong CG"}; - std::array num_cuts = {0}; + const char* cut_type_names[MAX_CUT_TYPE] = { + "Gomory ", "MIR ", "Knapsack ", "Strong CG", "Clique "}; + std::array num_cuts = {0}; }; template @@ -84,6 +115,19 @@ f_t fractional_part(f_t a) return a - std::floor(a); } +template +bool add_work_estimate(f_t accesses, + f_t* work_estimate, + f_t max_work_estimate, + bool* work_limit_reached = nullptr) +{ + if (work_estimate == nullptr) { return false; } + *work_estimate += accesses; + const bool over_work_limit = *work_estimate > max_work_estimate; + if (over_work_limit && work_limit_reached != nullptr) { *work_limit_reached = true; } + return over_work_limit; +} + // Computes a permutation of a score vector that puts the highest scores first template void best_score_first_permutation(std::vector& scores, std::vector& permutation) @@ -119,6 +163,15 @@ void verify_cuts_against_saved_solution(const csr_matrix_t& cuts, const std::vector& cut_rhs, const std::vector& saved_solution); +// Test-only helper to run the production maximal-clique algorithm used by clique cuts. +// adjacency_list must contain local vertex indices in [0, n_vertices). +std::vector> find_maximal_cliques_for_test( + const std::vector>& adjacency_list, + const std::vector& weights, + double min_weight, + int max_calls, + double time_limit); + template class cut_pool_t { public: @@ -221,25 +274,37 @@ class mixed_integer_rounding_cut_t; template class cut_generation_t { public: - cut_generation_t(cut_pool_t& cut_pool, - const lp_problem_t& lp, - const simplex_solver_settings_t& settings, - csr_matrix_t& Arow, - const std::vector& new_slacks, - const std::vector& var_types) - : cut_pool_(cut_pool), knapsack_generation_(lp, settings, Arow, new_slacks, var_types) + cut_generation_t( + cut_pool_t& cut_pool, + const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + csr_matrix_t& Arow, + const std::vector& new_slacks, + const std::vector& var_types, + const user_problem_t& user_problem, + std::shared_ptr> clique_table = nullptr, + std::future>>* clique_table_future = nullptr, + std::atomic* signal_extend = nullptr) + : cut_pool_(cut_pool), + knapsack_generation_(lp, settings, Arow, new_slacks, var_types), + user_problem_(user_problem), + clique_table_(std::move(clique_table)), + clique_table_future_(clique_table_future), + signal_extend_(signal_extend) { } - void generate_cuts(const lp_problem_t& lp, + bool generate_cuts(const lp_problem_t& lp, const simplex_solver_settings_t& settings, csr_matrix_t& Arow, const std::vector& new_slacks, const std::vector& var_types, basis_update_mpf_t& basis_update, const std::vector& xstar, + const std::vector& reduced_costs, const std::vector& basic_list, - const std::vector& nonbasic_list); + const std::vector& nonbasic_list, + f_t start_time); private: // Generate all mixed integer gomory cuts @@ -269,8 +334,20 @@ class cut_generation_t { const std::vector& var_types, const std::vector& xstar); + // Generate clique cuts from conflict graph cliques + bool generate_clique_cuts(const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + const std::vector& var_types, + const std::vector& xstar, + const std::vector& reduced_costs, + f_t start_time); + cut_pool_t& cut_pool_; knapsack_generation_t knapsack_generation_; + const user_problem_t& user_problem_; + std::shared_ptr> clique_table_; + std::future>>* clique_table_future_{nullptr}; + std::atomic* signal_extend_{nullptr}; }; template diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 815e229232..a8369a1858 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -100,6 +100,7 @@ struct simplex_solver_settings_t { mir_cuts(-1), mixed_integer_gomory_cuts(-1), knapsack_cuts(-1), + clique_cuts(-1), strong_chvatal_gomory_cuts(-1), reduced_cost_strengthening(-1), cut_change_threshold(1e-3), @@ -178,6 +179,7 @@ struct simplex_solver_settings_t { i_t mixed_integer_gomory_cuts; // -1 automatic, 0 to disable, >0 to enable mixed integer Gomory // cuts i_t knapsack_cuts; // -1 automatic, 0 to disable, >0 to enable knapsack cuts + i_t clique_cuts; // -1 automatic, 0 to disable, >0 to enable clique cuts i_t strong_chvatal_gomory_cuts; // -1 automatic, 0 to disable, >0 to enable strong Chvatal Gomory // cuts i_t reduced_cost_strengthening; // -1 automatic, 0 to disable, >0 to enable reduced cost diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index 7435bb37fa..f7d0408aba 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -94,6 +94,7 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS, &mip_settings.mir_cuts, -1, 1, -1}, {CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS, &mip_settings.mixed_integer_gomory_cuts, -1, 1, -1}, {CUOPT_MIP_KNAPSACK_CUTS, &mip_settings.knapsack_cuts, -1, 1, -1}, + {CUOPT_MIP_CLIQUE_CUTS, &mip_settings.clique_cuts, -1, 1, -1}, {CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS, &mip_settings.strong_chvatal_gomory_cuts, -1, 1, -1}, {CUOPT_MIP_REDUCED_COST_STRENGTHENING, &mip_settings.reduced_cost_strengthening, -1, std::numeric_limits::max(), -1}, {CUOPT_NUM_GPUS, &pdlp_settings.num_gpus, 1, 2, 1}, diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index ed165fe610..0ded8337d8 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -9,6 +9,7 @@ #include "diversity_manager.cuh" #include + #include #include #include @@ -78,9 +79,8 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::n_of_arms, cuopt::seed_generator::get_seed(), ls_alpha, "ls"), ls_hash_map(*context.problem_ptr) { - // Read configuration ID from environment variable - int max_config = -1; - // Read max configuration value from environment variable + int max_config = -1; + int env_config_id = -1; const char* env_max_config = std::getenv("CUOPT_MAX_CONFIG"); if (env_max_config != nullptr) { try { @@ -90,17 +90,21 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t 1) { - [[maybe_unused]] int config_id = -1; // Default value - const char* env_config_id = std::getenv("CUOPT_CONFIG_ID"); - if (env_config_id != nullptr) { - try { - config_id = std::stoi(env_config_id); - CUOPT_LOG_INFO("Using configuration ID from environment: %d", config_id); - } catch (const std::exception& e) { - CUOPT_LOG_WARN("Failed to parse CUOPT_CONFIG_ID environment variable: %s", e.what()); - } - } + + const char* env_config_id_raw = std::getenv("CUOPT_CONFIG_ID"); + if (env_config_id_raw == nullptr) { return; } + + try { + env_config_id = std::stoi(env_config_id_raw); + } catch (const std::exception& e) { + CUOPT_LOG_WARN("Failed to parse CUOPT_CONFIG_ID environment variable: %s", e.what()); + return; + } + + if (max_config > 0 && env_config_id >= max_config) { + CUOPT_LOG_WARN( + "CUOPT_CONFIG_ID=%d is outside [0, %d). Ignoring cut override.", env_config_id, max_config); + return; } } @@ -205,28 +209,32 @@ bool diversity_manager_t::run_presolve(f_t time_limit, timer_t global_ const bool remap_cache_ids = true; if (!global_timer.check_time_limit()) { trivial_presolve(*problem_ptr, remap_cache_ids); } if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; } - if (!presolve_timer.check_time_limit() && !context.settings.heuristics_only && - !problem_ptr->empty) { - f_t time_limit_for_clique_table = std::min(3., presolve_timer.remaining_time() / 5); - timer_t clique_timer(time_limit_for_clique_table); - dual_simplex::user_problem_t host_problem(problem_ptr->handle_ptr); - problem_ptr->get_host_user_problem(host_problem); - std::shared_ptr> clique_table; - constexpr bool modify_problem_with_cliques = false; - find_initial_cliques( - host_problem, context.settings.tolerances, clique_timer, modify_problem_with_cliques); - if (modify_problem_with_cliques) { - problem_ptr->set_constraints_from_host_user_problem(host_problem); - cuopt_assert(host_problem.lower.size() == static_cast(problem_ptr->n_variables), - "host lower bound size mismatch"); - cuopt_assert(host_problem.upper.size() == static_cast(problem_ptr->n_variables), - "host upper bound size mismatch"); - std::vector all_var_indices(problem_ptr->n_variables); - std::iota(all_var_indices.begin(), all_var_indices.end(), 0); - problem_ptr->update_variable_bounds(all_var_indices, host_problem.lower, host_problem.upper); - trivial_presolve(*problem_ptr, remap_cache_ids); - } - } + // if (!presolve_timer.check_time_limit() && !context.settings.heuristics_only && + // !problem_ptr->empty) { + // f_t time_limit_for_clique_table = std::min(3., presolve_timer.remaining_time() / 5); + // timer_t clique_timer(time_limit_for_clique_table); + // dual_simplex::user_problem_t host_problem(problem_ptr->handle_ptr); + // problem_ptr->get_host_user_problem(host_problem); + // std::shared_ptr> clique_table; + // constexpr bool modify_problem_with_cliques = false; + // find_initial_cliques(host_problem, + // context.settings.tolerances, + // &clique_table, + // clique_timer, + // modify_problem_with_cliques, + // nullptr); + // if (modify_problem_with_cliques) { + // problem_ptr->set_constraints_from_host_user_problem(host_problem); + // cuopt_assert(host_problem.lower.size() == static_cast(problem_ptr->n_variables), + // "host lower bound size mismatch"); + // cuopt_assert(host_problem.upper.size() == static_cast(problem_ptr->n_variables), + // "host upper bound size mismatch"); + // std::vector all_var_indices(problem_ptr->n_variables); + // std::iota(all_var_indices.begin(), all_var_indices.end(), 0); + // problem_ptr->update_variable_bounds(all_var_indices, host_problem.lower, + // host_problem.upper); trivial_presolve(*problem_ptr, remap_cache_ids); + // } + // } // May overconstrain if Papilo presolve has been run before if (context.settings.presolver == presolver_t::None) { if (!problem_ptr->empty) { diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh index d4e24bdeaf..4f86192db8 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh @@ -27,6 +27,8 @@ #include #include +#include + namespace cuopt::linear_programming::detail { template @@ -69,7 +71,6 @@ class diversity_manager_t { void set_simplex_solution(const std::vector& solution, const std::vector& dual_solution, f_t objective); - mip_solver_context_t& context; dual_simplex::branch_and_bound_t* branch_and_bound_ptr; problem_t* problem_ptr; diff --git a/cpp/src/mip_heuristics/diversity/lns/rins.cu b/cpp/src/mip_heuristics/diversity/lns/rins.cu index 2fbe79ba34..d7d7601014 100644 --- a/cpp/src/mip_heuristics/diversity/lns/rins.cu +++ b/cpp/src/mip_heuristics/diversity/lns/rins.cu @@ -266,6 +266,7 @@ void rins_t::run_rins() branch_and_bound_settings.num_threads = 1; branch_and_bound_settings.reliability_branching = 0; branch_and_bound_settings.max_cut_passes = 0; + branch_and_bound_settings.clique_cuts = 0; branch_and_bound_settings.sub_mip = 1; branch_and_bound_settings.log.log = false; branch_and_bound_settings.log.log_prefix = "[RINS] "; diff --git a/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh b/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh index b2f7f80066..a867141d0a 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh @@ -108,6 +108,7 @@ class sub_mip_recombiner_t : public recombiner_t { branch_and_bound_settings.num_threads = 1; branch_and_bound_settings.reliability_branching = 0; branch_and_bound_settings.max_cut_passes = 0; + branch_and_bound_settings.clique_cuts = 0; branch_and_bound_settings.sub_mip = 1; branch_and_bound_settings.solution_callback = [this](std::vector& solution, f_t objective) { diff --git a/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cu b/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cu index e21af7f69f..70855267df 100644 --- a/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cu +++ b/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cu @@ -15,7 +15,7 @@ * limitations under the License. */ -#define DEBUG_KNAPSACK_CONSTRAINTS 1 +#define DEBUG_KNAPSACK_CONSTRAINTS 0 #include "clique_table.cuh" @@ -34,11 +34,13 @@ namespace cuopt::linear_programming::detail { // do constraints with only binary variables. template void find_cliques_from_constraint(const knapsack_constraint_t& kc, - clique_table_t& clique_table) + clique_table_t& clique_table, + cuopt::timer_t& timer) { i_t size = kc.entries.size(); cuopt_assert(size > 1, "Constraint has not enough variables"); if (kc.entries[size - 1].val + kc.entries[size - 2].val <= kc.rhs) { return; } + std::vector clique; i_t k = size - 1; // find the first clique, which is the largest @@ -55,6 +57,7 @@ void find_cliques_from_constraint(const knapsack_constraint_t& kc, // find the additional cliques k--; while (k >= 0) { + if (timer.check_time_limit()) { return; } f_t curr_val = kc.entries[k].val; i_t curr_col = kc.entries[k].col; // do a binary search in the clique coefficients to find f, such that coeff_k + coeff_f > rhs @@ -210,13 +213,14 @@ void fill_knapsack_constraints(const dual_simplex::user_problem_t& pro } template -void remove_small_cliques(clique_table_t& clique_table) +void remove_small_cliques(clique_table_t& clique_table, cuopt::timer_t& timer) { i_t num_removed_first = 0; i_t num_removed_addtl = 0; std::vector to_delete(clique_table.first.size(), false); // if a clique is small, we remove it from the cliques and add it to adjlist for (size_t clique_idx = 0; clique_idx < clique_table.first.size(); clique_idx++) { + if (timer.check_time_limit()) { return; } const auto& clique = clique_table.first[clique_idx]; if (clique.size() <= (size_t)clique_table.min_clique_size) { for (size_t i = 0; i < clique.size(); i++) { @@ -316,15 +320,16 @@ std::unordered_set clique_table_t::get_adj_set_of_var(i_t var_idx addtl_cliques[addtl_clique_idx].start_pos_on_clique, first[addtl_cliques[addtl_clique_idx].clique_idx].end()); } - // Memory-neutral reverse lookup for additional cliques: + // Reverse lookup for additional cliques using position map: // if var_idx is in first[clique_idx][start_pos_on_clique:], it is adjacent to vertex_idx. for (const auto& addtl : addtl_cliques) { if (addtl.vertex_idx == var_idx) { continue; } - const auto& clique = first[addtl.clique_idx]; - size_t start_pos = static_cast(addtl.start_pos_on_clique); - if (start_pos < clique.size() && - std::find(clique.begin() + start_pos, clique.end(), var_idx) != clique.end()) { - adj_set.insert(addtl.vertex_idx); + if (static_cast(addtl.clique_idx) < first_var_positions.size()) { + const auto& pos_map = first_var_positions[addtl.clique_idx]; + auto it = pos_map.find(var_idx); + if (it != pos_map.end() && it->second >= addtl.start_pos_on_clique) { + adj_set.insert(addtl.vertex_idx); + } } } @@ -349,44 +354,39 @@ i_t clique_table_t::get_degree_of_var(i_t var_idx) template bool clique_table_t::check_adjacency(i_t var_idx1, i_t var_idx2) { - // if passed same variable if (var_idx1 == var_idx2) { return false; } - // in case they are complements of each other if (var_idx1 % n_variables == var_idx2 % n_variables) { return true; } - if (adj_list_small_cliques[var_idx1].count(var_idx2) > 0) { return true; } - // Check first cliques: var_clique_map_first stores clique indices - for (const auto& clique_idx : var_clique_map_first[var_idx1]) { - const auto& clique = first[clique_idx]; - // TODO: we can also keep a set of the clique if the memory allows, instead of doing linear - // search - if (std::find(clique.begin(), clique.end(), var_idx2) != clique.end()) { return true; } + + { + auto it = adj_list_small_cliques.find(var_idx1); + if (it != adj_list_small_cliques.end() && it->second.count(var_idx2) > 0) { return true; } } - // Check additional cliques: var_clique_map_addtl stores indices into addtl_cliques - for (const auto& addtl_idx : var_clique_map_addtl[var_idx1]) { - const auto& addtl = addtl_cliques[addtl_idx]; - const auto& clique = first[addtl.clique_idx]; - // addtl clique is: vertex_idx + first[clique_idx][start_pos_on_clique:] - if (addtl.vertex_idx == var_idx2) { return true; } - if (addtl.start_pos_on_clique < static_cast(clique.size())) { - if (std::find(clique.begin() + addtl.start_pos_on_clique, clique.end(), var_idx2) != - clique.end()) { - return true; - } + // Iterate whichever variable belongs to fewer first-cliques + { + i_t probe_var = var_idx1; + i_t target_var = var_idx2; + if (var_clique_map_first[var_idx1].size() > var_clique_map_first[var_idx2].size()) { + probe_var = var_idx2; + target_var = var_idx1; + } + for (const auto& clique_idx : var_clique_map_first[probe_var]) { + if (first_var_positions[clique_idx].count(target_var) > 0) { return true; } } } - // var_clique_map_addtl is keyed by addtl.vertex_idx, so also check the reverse direction. + for (const auto& addtl_idx : var_clique_map_addtl[var_idx1]) { + const auto& addtl = addtl_cliques[addtl_idx]; + const auto& pos_map = first_var_positions[addtl.clique_idx]; + auto it = pos_map.find(var_idx2); + if (it != pos_map.end() && it->second >= addtl.start_pos_on_clique) { return true; } + } + for (const auto& addtl_idx : var_clique_map_addtl[var_idx2]) { - const auto& addtl = addtl_cliques[addtl_idx]; - const auto& clique = first[addtl.clique_idx]; - if (addtl.vertex_idx == var_idx1) { return true; } - if (addtl.start_pos_on_clique < static_cast(clique.size())) { - if (std::find(clique.begin() + addtl.start_pos_on_clique, clique.end(), var_idx1) != - clique.end()) { - return true; - } - } + const auto& addtl = addtl_cliques[addtl_idx]; + const auto& pos_map = first_var_positions[addtl.clique_idx]; + auto it = pos_map.find(var_idx1); + if (it != pos_map.end() && it->second >= addtl.start_pos_on_clique) { return true; } } return false; @@ -548,6 +548,232 @@ bool extend_clique(const std::vector& clique, return new_clique.size() > clique.size(); } +template +struct clique_sig_t { + i_t knapsack_idx; + i_t size; + long long signature; +}; + +template +struct extension_candidate_t { + i_t knapsack_idx; + i_t estimated_gain; + i_t clique_size; +}; + +template +bool compare_clique_sig(const clique_sig_t& a, const clique_sig_t& b) +{ + if (a.signature != b.signature) { return a.signature < b.signature; } + return a.size < b.size; +} + +template +bool compare_signature_value(long long value, const clique_sig_t& a) +{ + return value < a.signature; +} + +template +bool compare_extension_candidate(const extension_candidate_t& a, + const extension_candidate_t& b) +{ + if (a.estimated_gain != b.estimated_gain) { return a.estimated_gain > b.estimated_gain; } + if (a.clique_size != b.clique_size) { return a.clique_size < b.clique_size; } + return a.knapsack_idx < b.knapsack_idx; +} + +template +bool is_sorted_subset(const std::vector& a, const std::vector& b) +{ + size_t i = 0; + size_t j = 0; + while (i < a.size() && j < b.size()) { + if (a[i] == b[j]) { + i++; + j++; + } else if (a[i] > b[j]) { + j++; + } else { + return false; + } + } + return i == a.size(); +} + +template +void fix_difference(const std::vector& superset, + const std::vector& subset, + dual_simplex::user_problem_t& problem) +{ + cuopt_assert(std::is_sorted(subset.begin(), subset.end()), + "subset vector passed to fix_difference is not sorted"); + for (auto var_idx : superset) { + if (std::binary_search(subset.begin(), subset.end(), var_idx)) { continue; } + if (var_idx >= problem.num_cols) { + i_t orig_idx = var_idx - problem.num_cols; + CUOPT_LOG_DEBUG("Fixing variable %d", orig_idx); + cuopt_assert(problem.lower[orig_idx] != 0 || problem.upper[orig_idx] != 0, + "Variable is fixed to other side"); + problem.lower[orig_idx] = 1; + problem.upper[orig_idx] = 1; + } else { + CUOPT_LOG_DEBUG("Fixing variable %d", var_idx); + cuopt_assert(problem.lower[var_idx] != 1 || problem.upper[var_idx] != 1, + "Variable is fixed to other side"); + problem.lower[var_idx] = 0; + problem.upper[var_idx] = 0; + } + } +} + +template +void remove_marked_elements(std::vector& vec, const std::vector& removal_marker) +{ + size_t write_idx = 0; + for (size_t i = 0; i < vec.size(); i++) { + if (!removal_marker[i]) { + if (write_idx != i) { vec[write_idx] = std::move(vec[i]); } + write_idx++; + } + } + vec.resize(write_idx); +} + +template +void remove_dominated_cliques_in_problem_for_single_extended_clique( + const std::vector& curr_clique, + f_t coeff_scale, + i_t remaining_rows_budget, + i_t remaining_nnz_budget, + i_t& inserted_row_nnz, + const std::vector>& sp_sigs, + const std::vector>& cstr_vars, + const std::vector>& knapsack_constraints, + std::vector& original_to_current_row_idx, + dual_simplex::user_problem_t& problem, + dual_simplex::csr_matrix_t& A, + cuopt::timer_t& timer) +{ + inserted_row_nnz = 0; + if (curr_clique.empty() || sp_sigs.empty()) { return; } + std::vector curr_clique_vars(curr_clique.begin(), curr_clique.end()); + std::sort(curr_clique_vars.begin(), curr_clique_vars.end()); + curr_clique_vars.erase(std::unique(curr_clique_vars.begin(), curr_clique_vars.end()), + curr_clique_vars.end()); + long long signature = 0; + for (auto v : curr_clique_vars) { + signature += static_cast(v); + } + constexpr size_t dominance_window = 20000; + auto end_it = + std::upper_bound(sp_sigs.begin(), sp_sigs.end(), signature, compare_signature_value); + size_t end = static_cast(std::distance(sp_sigs.begin(), end_it)); + size_t start = (end > dominance_window) ? (end - dominance_window) : 0; + std::vector rows_to_remove; + bool covering_clique_implied_by_partitioning = false; + for (size_t idx = end; idx > start; idx--) { + if (timer.check_time_limit()) { break; } + const auto& sp = sp_sigs[idx - 1]; + const auto& vars_sp = cstr_vars[sp.knapsack_idx]; + if (vars_sp.size() > curr_clique_vars.size()) { continue; } + cuopt_assert(std::is_sorted(vars_sp.begin(), vars_sp.end()), + "vars_sp vector passed to is_sorted_subset is not sorted"); + if (!is_sorted_subset(vars_sp, curr_clique_vars)) { continue; } + if (knapsack_constraints[sp.knapsack_idx].is_set_partitioning) { + if (vars_sp.size() != curr_clique_vars.size()) { + fix_difference(curr_clique_vars, vars_sp, problem); + covering_clique_implied_by_partitioning = true; + } + continue; + } + i_t original_row_idx = knapsack_constraints[sp.knapsack_idx].cstr_idx; + if (original_row_idx < 0) { continue; } + cuopt_assert(original_row_idx < static_cast(original_to_current_row_idx.size()), + "Invalid original row index in knapsack constraint"); + i_t current_row_idx = original_to_current_row_idx[original_row_idx]; + if (current_row_idx < 0) { continue; } + cuopt_assert(current_row_idx < static_cast(problem.row_sense.size()), + "Invalid current row index in row mapping"); + rows_to_remove.push_back(current_row_idx); + } + if (rows_to_remove.empty()) { return; } + std::sort(rows_to_remove.begin(), rows_to_remove.end()); + rows_to_remove.erase(std::unique(rows_to_remove.begin(), rows_to_remove.end()), + rows_to_remove.end()); + if (!covering_clique_implied_by_partitioning) { + if (remaining_rows_budget <= 0 || + remaining_nnz_budget < static_cast(curr_clique_vars.size())) { + return; + } + insert_clique_into_problem(curr_clique_vars, problem, A, coeff_scale); + inserted_row_nnz = static_cast(curr_clique_vars.size()); + } + std::vector removal_marker(problem.row_sense.size(), 0); + for (auto row_idx : rows_to_remove) { + cuopt_assert(row_idx >= 0 && row_idx < static_cast(removal_marker.size()), + "Invalid dominated row index"); + CUOPT_LOG_DEBUG("Removing dominated row %d", row_idx); + removal_marker[row_idx] = true; + } + dual_simplex::csr_matrix_t A_removed(0, 0, 0); + A.remove_rows(removal_marker, A_removed); + A = std::move(A_removed); + problem.num_rows = A.m; + remove_marked_elements(problem.row_sense, removal_marker); + remove_marked_elements(problem.rhs, removal_marker); + remove_marked_elements(problem.row_names, removal_marker); + cuopt_assert(problem.rhs.size() == problem.row_sense.size(), "rhs and row sense size mismatch"); + cuopt_assert(problem.row_names.size() == problem.rhs.size(), "row names and rhs size mismatch"); + cuopt_assert(problem.num_rows == static_cast(problem.rhs.size()), + "matrix and num rows mismatch after removal"); + if (!problem.range_rows.empty()) { + std::vector old_to_new_indices; + old_to_new_indices.reserve(removal_marker.size()); + i_t new_idx = 0; + for (size_t i = 0; i < removal_marker.size(); ++i) { + if (!removal_marker[i]) { + old_to_new_indices.push_back(new_idx++); + } else { + old_to_new_indices.push_back(-1); + } + } + std::vector new_range_rows; + std::vector new_range_values; + for (size_t i = 0; i < problem.range_rows.size(); ++i) { + i_t old_row = problem.range_rows[i]; + cuopt_assert(old_row >= 0 && old_row < static_cast(removal_marker.size()), + "Invalid row index in range_rows"); + if (!removal_marker[old_row]) { + i_t new_row = old_to_new_indices[old_row]; + cuopt_assert(new_row != -1, "Invalid new row index for ranged row renumbering"); + new_range_rows.push_back(new_row); + new_range_values.push_back(problem.range_value[i]); + } + } + problem.range_rows = std::move(new_range_rows); + problem.range_value = std::move(new_range_values); + } + problem.num_range_rows = static_cast(problem.range_rows.size()); + std::vector removed_prefix(removal_marker.size() + 1, 0); + for (size_t row_idx = 0; row_idx < removal_marker.size(); row_idx++) { + removed_prefix[row_idx + 1] = + removed_prefix[row_idx] + static_cast(removal_marker[row_idx]); + } + for (i_t row_idx = 0; row_idx < static_cast(original_to_current_row_idx.size()); row_idx++) { + i_t current_row_idx = original_to_current_row_idx[row_idx]; + if (current_row_idx < 0) { continue; } + cuopt_assert(current_row_idx < static_cast(removal_marker.size()), + "Row index map is out of bounds"); + if (removal_marker[current_row_idx]) { + original_to_current_row_idx[row_idx] = -1; + } else { + original_to_current_row_idx[row_idx] = current_row_idx - removed_prefix[current_row_idx]; + } + } +} + // Also known as clique merging. Infer larger clique constraints which allows inclusion of vars from // other constraints. This only extends the original cliques in the formulation for now. // TODO: consider a heuristic on how much of the cliques derived from knapsacks to include here @@ -558,12 +784,17 @@ i_t extend_cliques(const std::vector>& knapsack_ dual_simplex::user_problem_t& problem, dual_simplex::csr_matrix_t& A, bool modify_problem, - cuopt::timer_t& timer) + cuopt::timer_t& timer, + double* work_estimate_out, + double max_work_estimate) { constexpr i_t min_extension_gain = 2; constexpr i_t extension_yield_window = 64; constexpr i_t min_successes_per_window = 1; + double local_work = 0.0; + double& work = work_estimate_out ? *work_estimate_out : local_work; + i_t base_rows = A.m; i_t base_nnz = A.row_start[A.m]; i_t max_added_rows = std::max(8, base_rows / 50); @@ -579,12 +810,7 @@ i_t extend_cliques(const std::vector>& knapsack_ max_added_rows, max_added_nnz); std::vector> cstr_vars(knapsack_constraints.size()); - struct clique_sig_t { - i_t knapsack_idx; - i_t size; - long long signature; - }; - std::vector sp_sigs; + std::vector> sp_sigs; sp_sigs.reserve(set_packing_constraints.size()); for (const auto knapsack_idx : set_packing_constraints) { cuopt_assert(knapsack_idx >= 0 && knapsack_idx < static_cast(knapsack_constraints.size()), @@ -603,207 +829,20 @@ i_t extend_cliques(const std::vector>& knapsack_ signature += static_cast(v); } sp_sigs.push_back({knapsack_idx, static_cast(cstr_vars[knapsack_idx].size()), signature}); + work += cstr_vars[knapsack_idx].size(); } - std::sort(sp_sigs.begin(), sp_sigs.end(), [](const auto& a, const auto& b) { - if (a.signature != b.signature) { return a.signature < b.signature; } - return a.size < b.size; - }); + if (work > max_work_estimate) { return 0; } + std::sort(sp_sigs.begin(), sp_sigs.end(), compare_clique_sig); std::vector original_to_current_row_idx(problem.row_sense.size(), -1); for (i_t row_idx = 0; row_idx < static_cast(original_to_current_row_idx.size()); row_idx++) { original_to_current_row_idx[row_idx] = row_idx; } - auto is_subset = [](const std::vector& a, const std::vector& b) { - size_t i = 0; - size_t j = 0; - while (i < a.size() && j < b.size()) { - if (a[i] == b[j]) { - i++; - j++; - } else if (a[i] > b[j]) { - j++; - } else { - return false; - } - } - return i == a.size(); - }; - auto fix_difference = [&](const std::vector& superset, const std::vector& subset) { - if (!modify_problem) { return; } - cuopt_assert(std::is_sorted(subset.begin(), subset.end()), - "subset vector passed to fix_difference is not sorted"); - for (auto var_idx : superset) { - if (std::binary_search(subset.begin(), subset.end(), var_idx)) { continue; } - if (var_idx >= problem.num_cols) { - i_t orig_idx = var_idx - problem.num_cols; - CUOPT_LOG_DEBUG("Fixing variable %d", orig_idx); - cuopt_assert(problem.lower[orig_idx] != 0 || problem.upper[orig_idx] != 0, - "Variable is fixed to other side"); - problem.lower[orig_idx] = 1; - problem.upper[orig_idx] = 1; - } else { - CUOPT_LOG_DEBUG("Fixing variable %d", var_idx); - cuopt_assert(problem.lower[var_idx] != 1 || problem.upper[var_idx] != 1, - "Variable is fixed to other side"); - problem.lower[var_idx] = 0; - problem.upper[var_idx] = 0; - } - } - }; - auto remove_dominated_cliques_in_problem_for_single_extended_clique = - [&](const std::vector& curr_clique, - f_t coeff_scale, - i_t remaining_rows_budget, - i_t remaining_nnz_budget, - i_t& inserted_row_nnz) { - inserted_row_nnz = 0; - if (curr_clique.empty() || sp_sigs.empty()) { return; } - std::vector curr_clique_vars(curr_clique.begin(), curr_clique.end()); - std::sort(curr_clique_vars.begin(), curr_clique_vars.end()); - curr_clique_vars.erase(std::unique(curr_clique_vars.begin(), curr_clique_vars.end()), - curr_clique_vars.end()); - long long signature = 0; - for (auto v : curr_clique_vars) { - signature += static_cast(v); - } - constexpr size_t dominance_window = 20000; - auto end_it = std::upper_bound( - sp_sigs.begin(), sp_sigs.end(), signature, [](long long value, const auto& a) { - return value < a.signature; - }); - size_t end = static_cast(std::distance(sp_sigs.begin(), end_it)); - size_t start = (end > dominance_window) ? (end - dominance_window) : 0; - std::vector rows_to_remove; - bool covering_clique_implied_by_partitioning = false; - for (size_t idx = end; idx > start; idx--) { - if (timer.check_time_limit()) { break; } - const auto& sp = sp_sigs[idx - 1]; - const auto& vars_sp = cstr_vars[sp.knapsack_idx]; - if (vars_sp.size() > curr_clique_vars.size()) { continue; } - cuopt_assert(std::is_sorted(vars_sp.begin(), vars_sp.end()), - "vars_sp vector passed to is_subset is not sorted"); - if (!is_subset(vars_sp, curr_clique_vars)) { continue; } - if (knapsack_constraints[sp.knapsack_idx].is_set_partitioning) { - if (vars_sp.size() != curr_clique_vars.size()) { - fix_difference(curr_clique_vars, vars_sp); - covering_clique_implied_by_partitioning = true; - } - continue; - } - i_t original_row_idx = knapsack_constraints[sp.knapsack_idx].cstr_idx; - if (original_row_idx < 0) { continue; } - cuopt_assert(original_row_idx < static_cast(original_to_current_row_idx.size()), - "Invalid original row index in knapsack constraint"); - i_t current_row_idx = original_to_current_row_idx[original_row_idx]; - if (current_row_idx < 0) { continue; } - cuopt_assert(current_row_idx < static_cast(problem.row_sense.size()), - "Invalid current row index in row mapping"); - rows_to_remove.push_back(current_row_idx); - } - if (rows_to_remove.empty()) { return; } - if (!modify_problem) { return; } - std::sort(rows_to_remove.begin(), rows_to_remove.end()); - rows_to_remove.erase(std::unique(rows_to_remove.begin(), rows_to_remove.end()), - rows_to_remove.end()); - if (!covering_clique_implied_by_partitioning) { - if (remaining_rows_budget <= 0 || - remaining_nnz_budget < static_cast(curr_clique_vars.size())) { - return; - } - // Replace dominated rows with this stronger clique row. - insert_clique_into_problem(curr_clique_vars, problem, A, coeff_scale); - inserted_row_nnz = static_cast(curr_clique_vars.size()); - } - std::vector removal_marker(problem.row_sense.size(), 0); - for (auto row_idx : rows_to_remove) { - cuopt_assert(row_idx >= 0 && row_idx < static_cast(removal_marker.size()), - "Invalid dominated row index"); - CUOPT_LOG_DEBUG("Removing dominated row %d", row_idx); - removal_marker[row_idx] = true; - } - dual_simplex::csr_matrix_t A_removed(0, 0, 0); - A.remove_rows(removal_marker, A_removed); - A = std::move(A_removed); - problem.num_rows = A.m; - i_t n = 0; - auto new_end = std::remove_if( - problem.row_sense.begin(), problem.row_sense.end(), [&removal_marker, &n](char) mutable { - return removal_marker[n++]; - }); - problem.row_sense.erase(new_end, problem.row_sense.end()); - n = 0; - auto new_end_rhs = - std::remove_if(problem.rhs.begin(), problem.rhs.end(), [&removal_marker, &n](f_t) mutable { - return removal_marker[n++]; - }); - problem.rhs.erase(new_end_rhs, problem.rhs.end()); - n = 0; - auto new_end_row_names = std::remove_if( - problem.row_names.begin(), - problem.row_names.end(), - [&removal_marker, &n](const std::string&) mutable { return removal_marker[n++]; }); - problem.row_names.erase(new_end_row_names, problem.row_names.end()); - cuopt_assert(problem.rhs.size() == problem.row_sense.size(), - "rhs and row sense size mismatch"); - cuopt_assert(problem.row_names.size() == problem.rhs.size(), - "row names and rhs size mismatch"); - cuopt_assert(problem.num_rows == static_cast(problem.rhs.size()), - "matrix and num rows mismatch after removal"); - if (!problem.range_rows.empty()) { - std::vector old_to_new_indices; - old_to_new_indices.reserve(removal_marker.size()); - i_t new_idx = 0; - for (size_t i = 0; i < removal_marker.size(); ++i) { - if (!removal_marker[i]) { - old_to_new_indices.push_back(new_idx++); - } else { - old_to_new_indices.push_back(-1); - } - } - std::vector new_range_rows; - std::vector new_range_values; - for (size_t i = 0; i < problem.range_rows.size(); ++i) { - i_t old_row = problem.range_rows[i]; - cuopt_assert(old_row >= 0 && old_row < static_cast(removal_marker.size()), - "Invalid row index in range_rows"); - if (!removal_marker[old_row]) { - i_t new_row = old_to_new_indices[old_row]; - cuopt_assert(new_row != -1, "Invalid new row index for ranged row renumbering"); - new_range_rows.push_back(new_row); - new_range_values.push_back(problem.range_value[i]); - } - } - problem.range_rows = std::move(new_range_rows); - problem.range_value = std::move(new_range_values); - } - problem.num_range_rows = static_cast(problem.range_rows.size()); - std::vector removed_prefix(removal_marker.size() + 1, 0); - for (size_t row_idx = 0; row_idx < removal_marker.size(); row_idx++) { - removed_prefix[row_idx + 1] = - removed_prefix[row_idx] + static_cast(removal_marker[row_idx]); - } - for (i_t row_idx = 0; row_idx < static_cast(original_to_current_row_idx.size()); - row_idx++) { - i_t current_row_idx = original_to_current_row_idx[row_idx]; - if (current_row_idx < 0) { continue; } - cuopt_assert(current_row_idx < static_cast(removal_marker.size()), - "Row index map is out of bounds"); - if (removal_marker[current_row_idx]) { - original_to_current_row_idx[row_idx] = -1; - } else { - original_to_current_row_idx[row_idx] = current_row_idx - removed_prefix[current_row_idx]; - } - } - }; - struct extension_candidate_t { - i_t knapsack_idx; - i_t estimated_gain; - i_t clique_size; - }; - std::vector extension_worklist; + std::vector> extension_worklist; extension_worklist.reserve(knapsack_constraints.size()); for (i_t knapsack_idx = 0; knapsack_idx < static_cast(knapsack_constraints.size()); knapsack_idx++) { if (timer.check_time_limit()) { break; } + if (work > max_work_estimate) { break; } const auto& knapsack_constraint = knapsack_constraints[knapsack_idx]; if (!knapsack_constraint.is_set_packing) { continue; } i_t clique_size = static_cast(knapsack_constraint.entries.size()); @@ -812,26 +851,19 @@ i_t extend_cliques(const std::vector>& knapsack_ for (const auto& entry : knapsack_constraint.entries) { smallest_degree = std::min(smallest_degree, clique_table.get_degree_of_var(entry.col)); } - // The smallest-degree vertex upper-bounds how many new literals can be added. i_t estimated_gain = std::max(0, smallest_degree - (clique_size - 1)); if (estimated_gain < min_extension_gain) { continue; } extension_worklist.push_back({knapsack_idx, estimated_gain, clique_size}); + work += knapsack_constraint.entries.size(); } - std::stable_sort(extension_worklist.begin(), - extension_worklist.end(), - [](const extension_candidate_t& a, const extension_candidate_t& b) { - if (a.estimated_gain != b.estimated_gain) { - return a.estimated_gain > b.estimated_gain; - } - if (a.clique_size != b.clique_size) { return a.clique_size < b.clique_size; } - return a.knapsack_idx < b.knapsack_idx; - }); + std::stable_sort( + extension_worklist.begin(), extension_worklist.end(), compare_extension_candidate); CUOPT_LOG_DEBUG("Clique extension candidates after scoring: %zu", extension_worklist.size()); i_t n_extended_cliques = 0; - // Try highest estimated gain candidates first so budget is spent on promising rows. for (const auto& candidate : extension_worklist) { if (timer.check_time_limit()) { break; } + if (work > max_work_estimate) { break; } if (added_rows >= max_added_rows || added_nnz >= max_added_nnz) { CUOPT_LOG_DEBUG( "Stopping clique extension: budget reached (rows=%d nnz=%d)", added_rows, added_nnz); @@ -855,14 +887,24 @@ i_t extend_cliques(const std::vector>& knapsack_ max_added_rows - added_rows, max_added_nnz - added_nnz, inserted_row_nnz); + work += clique.size() * clique.size(); if (extended_clique) { n_extended_cliques++; i_t replacement_row_nnz = 0; - remove_dominated_cliques_in_problem_for_single_extended_clique(clique_table.first.back(), - coeff_scale, - max_added_rows - added_rows, - max_added_nnz - added_nnz, - replacement_row_nnz); + if (modify_problem) { + remove_dominated_cliques_in_problem_for_single_extended_clique(clique_table.first.back(), + coeff_scale, + max_added_rows - added_rows, + max_added_nnz - added_nnz, + replacement_row_nnz, + sp_sigs, + cstr_vars, + knapsack_constraints, + original_to_current_row_idx, + problem, + A, + timer); + } if (replacement_row_nnz > 0) { window_successes++; added_rows++; @@ -890,11 +932,15 @@ i_t extend_cliques(const std::vector>& knapsack_ template void fill_var_clique_maps(clique_table_t& clique_table) { + clique_table.first_var_positions.resize(clique_table.first.size()); for (size_t clique_idx = 0; clique_idx < clique_table.first.size(); clique_idx++) { const auto& clique = clique_table.first[clique_idx]; + auto& pos_map = clique_table.first_var_positions[clique_idx]; + pos_map.reserve(clique.size()); for (size_t idx = 0; idx < clique.size(); idx++) { i_t var_idx = clique[idx]; clique_table.var_clique_map_first[var_idx].insert(clique_idx); + pos_map[var_idx] = static_cast(idx); } } for (size_t addtl_c = 0; addtl_c < clique_table.addtl_cliques.size(); addtl_c++) { @@ -903,6 +949,37 @@ void fill_var_clique_maps(clique_table_t& clique_table) } } +template +void build_clique_table(const dual_simplex::user_problem_t& problem, + clique_table_t& clique_table, + typename mip_solver_settings_t::tolerances_t tolerances, + bool remove_small_cliques_flag, + bool fill_var_clique_maps_flag, + cuopt::timer_t& timer) +{ + if (timer.check_time_limit()) { return; } + cuopt_assert(clique_table.n_variables == problem.num_cols, "Clique table size mismatch"); + cuopt_assert(problem.var_types.size() == static_cast(problem.num_cols), + "Problem variable types size mismatch"); + std::vector> knapsack_constraints; + std::unordered_set set_packing_constraints; + dual_simplex::csr_matrix_t A(problem.num_rows, problem.num_cols, 0); + problem.A.to_compressed_row(A); + fill_knapsack_constraints(problem, knapsack_constraints, A); + make_coeff_positive_knapsack_constraint( + problem, knapsack_constraints, set_packing_constraints, tolerances); + sort_csr_by_constraint_coefficients(knapsack_constraints); + clique_table.tolerances = tolerances; + for (const auto& knapsack_constraint : knapsack_constraints) { + if (timer.check_time_limit()) { return; } + find_cliques_from_constraint(knapsack_constraint, clique_table, timer); + } + if (timer.check_time_limit()) { return; } + if (remove_small_cliques_flag) { remove_small_cliques(clique_table, timer); } + if (timer.check_time_limit()) { return; } + if (fill_var_clique_maps_flag) { fill_var_clique_maps(clique_table); } +} + template void print_knapsack_constraints( const std::vector>& knapsack_constraints, @@ -946,8 +1023,10 @@ void print_clique_table(const clique_table_t& clique_table) template void find_initial_cliques(dual_simplex::user_problem_t& problem, typename mip_solver_settings_t::tolerances_t tolerances, + std::shared_ptr>* clique_table_out, cuopt::timer_t& timer, - bool modify_problem) + bool modify_problem, + std::atomic* signal_extend) { cuopt::timer_t stage_timer(std::numeric_limits::infinity()); #ifdef DEBUG_CLIQUE_TABLE @@ -977,43 +1056,61 @@ void find_initial_cliques(dual_simplex::user_problem_t& problem, #ifdef DEBUG_CLIQUE_TABLE t_sort = stage_timer.elapsed_time(); #endif - // print_knapsack_constraints(knapsack_constraints); - // TODO think about getting min_clique_size according to some problem property clique_config_t clique_config; - clique_table_t clique_table(2 * problem.num_cols, - clique_config.min_clique_size, - clique_config.max_clique_size_for_extension); - clique_table.tolerances = tolerances; + std::shared_ptr> clique_table_shared; + clique_table_t clique_table_local(2 * problem.num_cols, + clique_config.min_clique_size, + clique_config.max_clique_size_for_extension); + clique_table_t* clique_table_ptr = &clique_table_local; + if (clique_table_out != nullptr) { + clique_table_shared = + std::make_shared>(2 * problem.num_cols, + clique_config.min_clique_size, + clique_config.max_clique_size_for_extension); + clique_table_ptr = clique_table_shared.get(); + } + clique_table_ptr->tolerances = tolerances; + double time_limit_for_additional_cliques = timer.remaining_time() / 2; + cuopt::timer_t additional_cliques_timer(time_limit_for_additional_cliques); + double find_work_estimate = 0.0; for (const auto& knapsack_constraint : knapsack_constraints) { if (timer.check_time_limit()) { break; } - find_cliques_from_constraint(knapsack_constraint, clique_table); + if (signal_extend && signal_extend->load(std::memory_order_acquire)) { break; } + find_cliques_from_constraint(knapsack_constraint, *clique_table_ptr, additional_cliques_timer); + find_work_estimate += knapsack_constraint.entries.size(); } - if (timer.check_time_limit()) { return; } #ifdef DEBUG_CLIQUE_TABLE t_find = stage_timer.elapsed_time(); #endif - CUOPT_LOG_DEBUG("Number of cliques: %d, additional cliques: %d", - clique_table.first.size(), - clique_table.addtl_cliques.size()); - // print_clique_table(clique_table); - // remove small cliques and add them to adj_list - remove_small_cliques(clique_table); + CUOPT_LOG_DEBUG("Number of cliques: %d, additional cliques: %d, find_work=%.0f", + clique_table_ptr->first.size(), + clique_table_ptr->addtl_cliques.size(), + find_work_estimate); + remove_small_cliques(*clique_table_ptr, timer); #ifdef DEBUG_CLIQUE_TABLE t_small = stage_timer.elapsed_time(); #endif - // fill var clique maps - fill_var_clique_maps(clique_table); + fill_var_clique_maps(*clique_table_ptr); #ifdef DEBUG_CLIQUE_TABLE t_maps = stage_timer.elapsed_time(); #endif - extend_cliques( - knapsack_constraints, set_packing_constraints, clique_table, problem, A, modify_problem, timer); + if (clique_table_out != nullptr) { *clique_table_out = std::move(clique_table_shared); } + double extend_work = 0.0; + constexpr double max_extend_work = 2e9; + i_t n_extended_cliques = extend_cliques(knapsack_constraints, + set_packing_constraints, + *clique_table_ptr, + problem, + A, + modify_problem, + timer, + &extend_work, + max_extend_work); #ifdef DEBUG_CLIQUE_TABLE t_extend = stage_timer.elapsed_time(); - t_remove = t_extend; CUOPT_LOG_DEBUG( "Clique table timing (s): fill=%.6f coeff=%.6f sort=%.6f find=%.6f small=%.6f maps=%.6f " - "extend=%.6f remove=%.6f total=%.6f", + "extend=%.6f total=%.6f find_work=%.0f extend_work=%.0f", t_fill, t_coeff - t_fill, t_sort - t_coeff, @@ -1021,8 +1118,9 @@ void find_initial_cliques(dual_simplex::user_problem_t& problem, t_small - t_find, t_maps - t_small, t_extend - t_maps, - t_remove - t_extend, - t_remove); + t_extend, + find_work_estimate, + extend_work); #endif } @@ -1030,8 +1128,18 @@ void find_initial_cliques(dual_simplex::user_problem_t& problem, template void find_initial_cliques( \ dual_simplex::user_problem_t & problem, \ typename mip_solver_settings_t::tolerances_t tolerances, \ + std::shared_ptr> * clique_table_out, \ cuopt::timer_t & timer, \ - bool modify_problem); + bool modify_problem, \ + std::atomic* signal_extend); \ + template void build_clique_table( \ + const dual_simplex::user_problem_t& problem, \ + clique_table_t& clique_table, \ + typename mip_solver_settings_t::tolerances_t tolerances, \ + bool remove_small_cliques_flag, \ + bool fill_var_clique_maps_flag, \ + cuopt::timer_t& timer); \ + template class clique_table_t; #if MIP_INSTANTIATE_FLOAT INSTANTIATE(float) diff --git a/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cuh b/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cuh index a8261685ab..944241b4f0 100644 --- a/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cuh +++ b/cpp/src/mip_heuristics/presolve/conflict_graph/clique_table.cuh @@ -20,8 +20,10 @@ #include #include +#include #include +#include #include #include #include @@ -83,6 +85,8 @@ struct clique_table_t { std::vector> var_clique_map_first; // keeps the indices of additional cliques that contain variable x std::vector> var_clique_map_addtl; + // var_idx -> position mapping for each first clique, enabling O(1) membership/position checks + std::vector> first_var_positions; // adjacency list to keep small cliques, this basically keeps the vars share a small clique // constraint std::unordered_map> adj_list_small_cliques; @@ -98,8 +102,18 @@ struct clique_table_t { template void find_initial_cliques(dual_simplex::user_problem_t& problem, typename mip_solver_settings_t::tolerances_t tolerances, + std::shared_ptr>* clique_table_out, cuopt::timer_t& timer, - bool modify_problem); + bool modify_problem, + std::atomic* signal_extend = nullptr); + +template +void build_clique_table(const dual_simplex::user_problem_t& problem, + clique_table_t& clique_table, + typename mip_solver_settings_t::tolerances_t tolerances, + bool remove_small_cliques, + bool fill_var_clique_maps, + cuopt::timer_t& timer); } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp index 20a586f6fb..2fa10f421b 100644 --- a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp +++ b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp @@ -590,7 +590,7 @@ std::optional> third_party_presolve_t( pslp_presolver_, op_problem.get_handle_ptr(), maximize_, original_obj_offset); - + opt_problem.set_problem_name(op_problem.get_problem_name()); return std::make_optional(third_party_presolve_result_t{opt_problem, {}}); } else { cuopt_expects( @@ -669,6 +669,7 @@ std::optional> third_party_presolve_t( papilo_problem, op_problem.get_handle_ptr(), category, maximize_); + opt_problem.set_problem_name(op_problem.get_problem_name()); auto col_flags = papilo_problem.getColFlags(); std::vector implied_integer_indices; for (size_t i = 0; i < col_flags.size(); i++) { diff --git a/cpp/src/mip_heuristics/problem/problem.cu b/cpp/src/mip_heuristics/problem/problem.cu index fadc00a850..90d80f5948 100644 --- a/cpp/src/mip_heuristics/problem/problem.cu +++ b/cpp/src/mip_heuristics/problem/problem.cu @@ -143,6 +143,7 @@ problem_t::problem_t( objective_offset(problem_.get_objective_offset()), lp_state(*this, problem_.get_handle_ptr()->get_stream()), fixing_helpers(n_constraints, n_variables, handle_ptr), + clique_table(nullptr), Q_offsets(problem_.get_quadratic_objective_offsets()), Q_indices(problem_.get_quadratic_objective_indices()), Q_values(problem_.get_quadratic_objective_values()) @@ -199,6 +200,7 @@ problem_t::problem_t(const problem_t& problem_) objective_is_integral(problem_.objective_is_integral), lp_state(problem_.lp_state), fixing_helpers(problem_.fixing_helpers, handle_ptr), + clique_table(problem_.clique_table), vars_with_objective_coeffs(problem_.vars_with_objective_coeffs), expensive_to_fix_vars(problem_.expensive_to_fix_vars), Q_offsets(problem_.Q_offsets), @@ -255,6 +257,7 @@ problem_t::problem_t(const problem_t& problem_, objective_is_integral(problem_.objective_is_integral), lp_state(problem_.lp_state, handle_ptr), fixing_helpers(problem_.fixing_helpers, handle_ptr), + clique_table(problem_.clique_table), vars_with_objective_coeffs(problem_.vars_with_objective_coeffs), expensive_to_fix_vars(problem_.expensive_to_fix_vars), Q_offsets(problem_.Q_offsets), @@ -279,6 +282,7 @@ problem_t::problem_t(const problem_t& problem_, bool no_deep maximize(problem_.maximize), empty(problem_.empty), is_binary_pb(problem_.is_binary_pb), + clique_table(problem_.clique_table), // Copy constructor used by PDLP and MIP // PDLP uses the version with no_deep_copy = false which deep copy some fields but doesn't // allocate others that are not needed in PDLP diff --git a/cpp/src/mip_heuristics/problem/problem.cuh b/cpp/src/mip_heuristics/problem/problem.cuh index b9ca420820..9771bab568 100644 --- a/cpp/src/mip_heuristics/problem/problem.cuh +++ b/cpp/src/mip_heuristics/problem/problem.cuh @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -37,6 +38,9 @@ namespace cuopt { namespace linear_programming::detail { +template +struct clique_table_t; + template class solution_t; @@ -119,6 +123,8 @@ class problem_t { bool is_integer(f_t val) const; bool integer_equal(f_t val1, f_t val2) const; + std::shared_ptr> clique_table; + void get_host_user_problem( cuopt::linear_programming::dual_simplex::user_problem_t& user_problem) const; void set_constraints_from_host_user_problem( diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index e6f6d50b62..da6c7c0b81 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -220,6 +220,7 @@ solution_t mip_solver_t::run_solver() branch_and_bound_settings.mixed_integer_gomory_cuts = context.settings.mixed_integer_gomory_cuts; branch_and_bound_settings.knapsack_cuts = context.settings.knapsack_cuts; + branch_and_bound_settings.clique_cuts = context.settings.clique_cuts; branch_and_bound_settings.strong_chvatal_gomory_cuts = context.settings.strong_chvatal_gomory_cuts; branch_and_bound_settings.reduced_cost_strengthening = @@ -261,7 +262,10 @@ solution_t mip_solver_t::run_solver() // Create the branch and bound object branch_and_bound = std::make_unique>( - branch_and_bound_problem, branch_and_bound_settings, timer_.get_tic_start()); + branch_and_bound_problem, + branch_and_bound_settings, + timer_.get_tic_start(), + context.problem_ptr->clique_table); context.branch_and_bound_ptr = branch_and_bound.get(); auto* stats_ptr = &context.stats; branch_and_bound->set_user_bound_callback( diff --git a/cpp/src/pdlp/solve.cu b/cpp/src/pdlp/solve.cu index 22fff31906..2fc9ec08d5 100644 --- a/cpp/src/pdlp/solve.cu +++ b/cpp/src/pdlp/solve.cu @@ -1571,9 +1571,8 @@ cuopt::linear_programming::optimization_problem_t mps_data_model_to_op if (data_model.get_objective_name().size() != 0) { op_problem.set_objective_name(data_model.get_objective_name()); } - if (data_model.get_problem_name().size() != 0) { - op_problem.set_problem_name(data_model.get_problem_name().data()); - } + auto problem_name = data_model.get_problem_name(); + op_problem.set_problem_name(problem_name); if (data_model.get_variable_names().size() != 0) { op_problem.set_variable_names(data_model.get_variable_names()); } diff --git a/cpp/tests/mip/cuts_test.cu b/cpp/tests/mip/cuts_test.cu index 1a360b41eb..67baab5f12 100644 --- a/cpp/tests/mip/cuts_test.cu +++ b/cpp/tests/mip/cuts_test.cu @@ -8,25 +8,829 @@ #include "../linear_programming/utilities/pdlp_test_utilities.cuh" #include "mip_utils.cuh" +#include +#include #include +#include +#include +#include #include #include +#include #include +#include #include #include #include +#include #include #include #include +#include +#include +#include #include #include +#include #include namespace cuopt::linear_programming::test { +namespace { + +constexpr double kCliqueTestTol = 1e-6; + +mps_parser::mps_data_model_t create_pairwise_triangle_set_packing_problem() +{ + // Maximize x0 + x1 + x2 via minimizing -x0 - x1 - x2. + // Pairwise conflicts: + // x0 + x1 <= 1 + // x1 + x2 <= 1 + // x0 + x2 <= 1 + mps_parser::mps_data_model_t problem; + std::vector offsets = {0, 2, 4, 6}; + std::vector indices = {0, 1, 1, 2, 0, 2}; + std::vector coefficients = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + problem.set_csr_constraint_matrix(coefficients.data(), + coefficients.size(), + indices.data(), + indices.size(), + offsets.data(), + offsets.size()); + std::vector lower_bounds = {-std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + -std::numeric_limits::infinity()}; + std::vector upper_bounds = {1.0, 1.0, 1.0}; + problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); + problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); + std::vector var_lower_bounds = {0.0, 0.0, 0.0}; + std::vector var_upper_bounds = {1.0, 1.0, 1.0}; + problem.set_variable_lower_bounds(var_lower_bounds.data(), var_lower_bounds.size()); + problem.set_variable_upper_bounds(var_upper_bounds.data(), var_upper_bounds.size()); + std::vector objective_coefficients = {-1.0, -1.0, -1.0}; + problem.set_objective_coefficients(objective_coefficients.data(), objective_coefficients.size()); + std::vector variable_types = {'I', 'I', 'I'}; + problem.set_variable_types(variable_types); + problem.set_maximize(false); + return problem; +} + +mps_parser::mps_data_model_t create_pairwise_triangle_with_isolated_variable_problem() +{ + // Same triangle conflicts as create_pairwise_triangle_set_packing_problem(), + // plus an isolated binary variable x3 with no conflict rows. + mps_parser::mps_data_model_t problem; + std::vector offsets = {0, 2, 4, 6}; + std::vector indices = {0, 1, 1, 2, 0, 2}; + std::vector coefficients = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0}; + problem.set_csr_constraint_matrix(coefficients.data(), + coefficients.size(), + indices.data(), + indices.size(), + offsets.data(), + offsets.size()); + std::vector lower_bounds = {-std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + -std::numeric_limits::infinity()}; + std::vector upper_bounds = {1.0, 1.0, 1.0}; + problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); + problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); + std::vector var_lower_bounds = {0.0, 0.0, 0.0, 0.0}; + std::vector var_upper_bounds = {1.0, 1.0, 1.0, 1.0}; + problem.set_variable_lower_bounds(var_lower_bounds.data(), var_lower_bounds.size()); + problem.set_variable_upper_bounds(var_upper_bounds.data(), var_upper_bounds.size()); + std::vector objective_coefficients = {-1.0, -1.0, -1.0, 0.0}; + problem.set_objective_coefficients(objective_coefficients.data(), objective_coefficients.size()); + std::vector variable_types = {'I', 'I', 'I', 'I'}; + problem.set_variable_types(variable_types); + problem.set_maximize(false); + return problem; +} + +mps_parser::mps_data_model_t create_binary_continuous_mixed_conflict_problem() +{ + // x0 + y1 <= 1 (must be ignored for clique graph because y1 is continuous) + // x0 + x2 <= 1 (must generate a conflict edge) + mps_parser::mps_data_model_t problem; + std::vector offsets = {0, 2, 4}; + std::vector indices = {0, 1, 0, 2}; + std::vector coefficients = {1.0, 1.0, 1.0, 1.0}; + problem.set_csr_constraint_matrix(coefficients.data(), + coefficients.size(), + indices.data(), + indices.size(), + offsets.data(), + offsets.size()); + std::vector lower_bounds = {-std::numeric_limits::infinity(), + -std::numeric_limits::infinity()}; + std::vector upper_bounds = {1.0, 1.0}; + problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); + problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); + std::vector var_lower_bounds = {0.0, 0.0, 0.0}; + std::vector var_upper_bounds = {1.0, 1.0, 1.0}; + problem.set_variable_lower_bounds(var_lower_bounds.data(), var_lower_bounds.size()); + problem.set_variable_upper_bounds(var_upper_bounds.data(), var_upper_bounds.size()); + std::vector objective_coefficients = {0.0, 0.0, 0.0}; + problem.set_objective_coefficients(objective_coefficients.data(), objective_coefficients.size()); + std::vector variable_types = {'I', 'C', 'I'}; + problem.set_variable_types(variable_types); + problem.set_maximize(false); + return problem; +} + +mps_parser::mps_data_model_t create_near_binary_bound_conflict_problem() +{ + // x0 + x1 <= 1 but x1 has upper bound 0.9999999, so this row should not be + // treated as a binary conflict row. + mps_parser::mps_data_model_t problem; + std::vector offsets = {0, 2}; + std::vector indices = {0, 1}; + std::vector coefficients = {1.0, 1.0}; + problem.set_csr_constraint_matrix(coefficients.data(), + coefficients.size(), + indices.data(), + indices.size(), + offsets.data(), + offsets.size()); + std::vector lower_bounds = {-std::numeric_limits::infinity()}; + std::vector upper_bounds = {1.0}; + problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); + problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); + std::vector var_lower_bounds = {0.0, 0.0}; + std::vector var_upper_bounds = {1.0, 0.9999999}; + problem.set_variable_lower_bounds(var_lower_bounds.data(), var_lower_bounds.size()); + problem.set_variable_upper_bounds(var_upper_bounds.data(), var_upper_bounds.size()); + std::vector objective_coefficients = {0.0, 0.0}; + problem.set_objective_coefficients(objective_coefficients.data(), objective_coefficients.size()); + std::vector variable_types = {'I', 'I'}; + problem.set_variable_types(variable_types); + problem.set_maximize(false); + return problem; +} + +mps_parser::mps_data_model_t create_weighted_addtl_conflict_problem() +{ + // One weighted binary knapsack row: + // 1*x0 + 2*x1 + 3*x2 + 4*x3 <= 5 + // This creates base clique {x2, x3} and additional clique inducing conflict {x1, x3}. + mps_parser::mps_data_model_t problem; + std::vector offsets = {0, 4}; + std::vector indices = {0, 1, 2, 3}; + std::vector coefficients = {1.0, 2.0, 3.0, 4.0}; + problem.set_csr_constraint_matrix(coefficients.data(), + coefficients.size(), + indices.data(), + indices.size(), + offsets.data(), + offsets.size()); + std::vector lower_bounds = {-std::numeric_limits::infinity()}; + std::vector upper_bounds = {5.0}; + problem.set_constraint_lower_bounds(lower_bounds.data(), lower_bounds.size()); + problem.set_constraint_upper_bounds(upper_bounds.data(), upper_bounds.size()); + std::vector var_lower_bounds = {0.0, 0.0, 0.0, 0.0}; + std::vector var_upper_bounds = {1.0, 1.0, 1.0, 1.0}; + problem.set_variable_lower_bounds(var_lower_bounds.data(), var_lower_bounds.size()); + problem.set_variable_upper_bounds(var_upper_bounds.data(), var_upper_bounds.size()); + std::vector objective_coefficients = {0.0, 0.0, 0.0, 0.0}; + problem.set_objective_coefficients(objective_coefficients.data(), objective_coefficients.size()); + std::vector variable_types = {'I', 'I', 'I', 'I'}; + problem.set_variable_types(variable_types); + problem.set_maximize(false); + return problem; +} + +detail::clique_table_t build_clique_table_for_model_with_min_size( + const raft::handle_t& handle, + const mps_parser::mps_data_model_t& model, + int min_clique_size) +{ + auto op_problem = mps_data_model_to_optimization_problem(&handle, model); + detail::problem_t mip_problem(op_problem); + dual_simplex::user_problem_t host_problem(op_problem.get_handle_ptr()); + mip_problem.get_host_user_problem(host_problem); + + detail::clique_config_t clique_config; + clique_config.min_clique_size = min_clique_size; + detail::clique_table_t clique_table(2 * host_problem.num_cols, + clique_config.min_clique_size, + clique_config.max_clique_size_for_extension); + + mip_solver_settings_t settings; + cuopt::timer_t timer(std::numeric_limits::infinity()); + detail::build_clique_table(host_problem, clique_table, settings.tolerances, true, true, timer); + return clique_table; +} + +detail::clique_table_t build_clique_table_for_model( + const raft::handle_t& handle, const mps_parser::mps_data_model_t& model) +{ + return build_clique_table_for_model_with_min_size(handle, model, 1); +} + +mps_parser::mps_data_model_t& get_neos8_model_cached() +{ + static std::once_flag init_flag; + static std::unique_ptr> model_ptr; + std::call_once(init_flag, []() { + const auto neos8_path = make_path_absolute("mip/neos8.mps"); + auto neos8_model = cuopt::mps_parser::parse_mps(neos8_path, false); + model_ptr = std::make_unique>(std::move(neos8_model)); + }); + cuopt_assert(model_ptr != nullptr, "Failed to initialize cached neos8 model"); + return *model_ptr; +} + +detail::clique_table_t& get_neos8_clique_table_cached() +{ + static std::once_flag init_flag; + static std::unique_ptr> clique_table_ptr; + std::call_once(init_flag, []() { + const raft::handle_t handle{}; + auto& neos8_model = get_neos8_model_cached(); + auto clique_table = build_clique_table_for_model(handle, neos8_model); + clique_table_ptr = + std::make_unique>(std::move(clique_table)); + }); + cuopt_assert(clique_table_ptr != nullptr, "Failed to initialize cached neos8 clique table"); + return *clique_table_ptr; +} + +std::vector> build_original_adjacency_matrix( + detail::clique_table_t& clique_table, int num_vars) +{ + std::vector> adj(num_vars, std::vector(num_vars, 0)); + for (int i = 0; i < num_vars; ++i) { + for (int j = i + 1; j < num_vars; ++j) { + if (clique_table.check_adjacency(i, j)) { + adj[i][j] = 1; + adj[j][i] = 1; + } + } + } + return adj; +} + +std::vector> maximal_cliques_bruteforce(const std::vector>& adj) +{ + const int n = static_cast(adj.size()); + if (n <= 0 || n > 20) { return {}; } + const uint64_t total_masks = (uint64_t{1} << n); + std::vector> maximal_cliques; + + auto is_mask_clique = [&](uint64_t mask) { + for (int i = 0; i < n; ++i) { + if ((mask & (uint64_t{1} << i)) == 0) { continue; } + for (int j = i + 1; j < n; ++j) { + if ((mask & (uint64_t{1} << j)) == 0) { continue; } + if (!adj[i][j]) { return false; } + } + } + return true; + }; + + for (uint64_t mask = 1; mask < total_masks; ++mask) { + if (!is_mask_clique(mask)) { continue; } + bool is_maximal = true; + for (int v = 0; v < n && is_maximal; ++v) { + if (mask & (uint64_t{1} << v)) { continue; } + bool can_extend = true; + for (int u = 0; u < n; ++u) { + if ((mask & (uint64_t{1} << u)) == 0) { continue; } + if (!adj[v][u]) { + can_extend = false; + break; + } + } + if (can_extend) { is_maximal = false; } + } + if (!is_maximal) { continue; } + std::vector clique; + for (int u = 0; u < n; ++u) { + if (mask & (uint64_t{1} << u)) { clique.push_back(u); } + } + maximal_cliques.push_back(std::move(clique)); + } + return maximal_cliques; +} + +std::vector> canonicalize_cliques(std::vector> cliques) +{ + for (auto& clique : cliques) { + std::sort(clique.begin(), clique.end()); + } + std::sort(cliques.begin(), cliques.end(), [](const auto& a, const auto& b) { + if (a.size() != b.size()) { return a.size() < b.size(); } + return a < b; + }); + cliques.erase(std::unique(cliques.begin(), cliques.end()), cliques.end()); + return cliques; +} + +std::vector> adjacency_matrix_to_list(const std::vector>& adj) +{ + const int n = static_cast(adj.size()); + std::vector> adj_list(n); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + if (adj[i][j]) { adj_list[i].push_back(j); } + } + } + return adj_list; +} + +std::vector> maximal_cliques_from_production_algorithm( + const std::vector>& adj) +{ + const auto adj_list = adjacency_matrix_to_list(adj); + std::vector weights(adj_list.size(), 1.0); + auto cliques = dual_simplex::find_maximal_cliques_for_test( + adj_list, weights, 0.0, 100000, std::numeric_limits::infinity()); + return canonicalize_cliques(std::move(cliques)); +} + +double original_clique_sum(const std::vector& clique_vars, + const std::vector& assignment) +{ + double lhs = 0.0; + for (const auto var : clique_vars) { + lhs += assignment[var]; + } + return lhs; +} + +std::string format_phase2_panic_dump(const mps_parser::mps_data_model_t& problem, + const std::vector& clique_vars, + const std::vector& x_star) +{ + std::ostringstream out; + const auto& var_lb = problem.get_variable_lower_bounds(); + const auto& var_ub = problem.get_variable_upper_bounds(); + out << "\nClique vars:"; + for (auto v : clique_vars) { + out << " x" << v << "(value=" << x_star[v] << ", lb=" << var_lb[v] << ", ub=" << var_ub[v] + << ")"; + } + + std::unordered_set clique_var_set(clique_vars.begin(), clique_vars.end()); + const auto& values = problem.get_constraint_matrix_values(); + const auto& cols = problem.get_constraint_matrix_indices(); + const auto& rows = problem.get_constraint_matrix_offsets(); + const auto& clb = problem.get_constraint_lower_bounds(); + const auto& cub = problem.get_constraint_upper_bounds(); + + out << "\nRelated constraints:"; + for (size_t row = 0; row + 1 < rows.size(); ++row) { + bool touches_clique = false; + for (int p = rows[row]; p < rows[row + 1]; ++p) { + if (clique_var_set.count(cols[p]) > 0) { + touches_clique = true; + break; + } + } + if (!touches_clique) { continue; } + out << "\n row " << row << ": "; + for (int p = rows[row]; p < rows[row + 1]; ++p) { + if (p > rows[row]) { out << " + "; } + out << values[p] << "*x" << cols[p]; + } + out << " in [" << clb[row] << ", " << cub[row] << "]"; + } + return out.str(); +} + +void disable_non_clique_cuts(mip_solver_settings_t& settings) +{ + settings.clique_cuts = 1; + settings.max_cut_passes = 10; + settings.mixed_integer_gomory_cuts = 0; + settings.knapsack_cuts = 0; + settings.mir_cuts = 0; + settings.strong_chvatal_gomory_cuts = 0; +} + +void disable_all_cuts(mip_solver_settings_t& settings) +{ + settings.max_cut_passes = 0; + settings.clique_cuts = 0; + settings.mixed_integer_gomory_cuts = 0; + settings.knapsack_cuts = 0; + settings.mir_cuts = 0; + settings.strong_chvatal_gomory_cuts = 0; +} + +bool cut_is_invalid_for_incumbent(const std::vector& cut_vars, + const std::vector& incumbent, + double tol) +{ + return original_clique_sum(cut_vars, incumbent) > 1.0 + tol; +} + +bool prefix_has_invalid_cut(const std::vector>& dumped_cuts, + size_t prefix_end_exclusive, + const std::vector& incumbent, + double tol) +{ + for (size_t i = 0; i < prefix_end_exclusive; ++i) { + if (cut_is_invalid_for_incumbent(dumped_cuts[i], incumbent, tol)) { return true; } + } + return false; +} + +std::optional isolate_first_invalid_cut_by_bisection( + const std::vector>& dumped_cuts, + const std::vector& incumbent, + double tol) +{ + if (!prefix_has_invalid_cut(dumped_cuts, dumped_cuts.size(), incumbent, tol)) { + return std::nullopt; + } + size_t lo = 0; + size_t hi = dumped_cuts.size() - 1; + while (lo < hi) { + const size_t mid = lo + (hi - lo) / 2; + if (prefix_has_invalid_cut(dumped_cuts, mid + 1, incumbent, tol)) { + hi = mid; + } else { + lo = mid + 1; + } + } + return lo; +} + +struct neos8_mip_solution_cache_t { + mip_termination_status_t status; + std::vector primal; + double objective; +}; + +struct neos8_lp_solution_cache_t { + pdlp_termination_status_t status; + std::vector primal; +}; + +neos8_mip_solution_cache_t& get_neos8_optimal_solution_no_cuts_cached() +{ + static std::once_flag init_flag; + static std::unique_ptr solution_ptr; + std::call_once(init_flag, []() { + const raft::handle_t handle{}; + auto& neos8_model = get_neos8_model_cached(); + mip_solver_settings_t settings; + settings.time_limit = 120.0; + settings.presolver = presolver_t::None; + disable_all_cuts(settings); + + auto mip_solution = solve_mip(&handle, neos8_model, settings); + auto cache = std::make_unique(); + cache->status = mip_solution.get_termination_status(); + cache->objective = mip_solution.get_objective_value(); + cache->primal = cuopt::host_copy(mip_solution.get_solution(), handle.get_stream()); + solution_ptr = std::move(cache); + }); + cuopt_assert(solution_ptr != nullptr, "Failed to initialize cached neos8 no-cut MIP solution"); + return *solution_ptr; +} + +neos8_lp_solution_cache_t& get_neos8_lp_relaxation_solution_cached() +{ + static std::once_flag init_flag; + static std::unique_ptr solution_ptr; + std::call_once(init_flag, []() { + const raft::handle_t handle{}; + auto lp_relaxation = get_neos8_model_cached(); + std::vector all_continuous(lp_relaxation.get_n_variables(), 'C'); + lp_relaxation.set_variable_types(all_continuous); + + pdlp_solver_settings_t lp_settings{}; + lp_settings.time_limit = 120.0; + lp_settings.presolver = presolver_t::None; + lp_settings.set_optimality_tolerance(1e-8); + + auto lp_solution = solve_lp(&handle, lp_relaxation, lp_settings); + auto cache = std::make_unique(); + cache->status = lp_solution.get_termination_status(); + cache->primal = cuopt::host_copy(lp_solution.get_primal_solution(), handle.get_stream()); + solution_ptr = std::move(cache); + }); + cuopt_assert(solution_ptr != nullptr, "Failed to initialize cached neos8 LP relaxation solution"); + return *solution_ptr; +} + +bool is_binary_var_for_clique_literals(const mps_parser::mps_data_model_t& problem, + int var_idx, + double bound_tol) +{ + const auto& var_types = problem.get_variable_types(); + const auto& var_lb = problem.get_variable_lower_bounds(); + const auto& var_ub = problem.get_variable_upper_bounds(); + return var_types[var_idx] != 'C' && var_lb[var_idx] >= -bound_tol && + var_ub[var_idx] <= 1.0 + bound_tol; +} + +std::vector> build_fractional_literal_cliques_for_assignment( + const mps_parser::mps_data_model_t& problem, + detail::clique_table_t& clique_table, + const std::vector& assignment, + double integer_tol, + double bound_tol, + int max_calls) +{ + const int num_vars = problem.get_n_variables(); + cuopt_assert(static_cast(assignment.size()) >= num_vars, + "Assignment size mismatch in fractional literal clique builder"); + + std::vector vertices; + std::vector weights; + vertices.reserve(2 * num_vars); + weights.reserve(2 * num_vars); + for (int j = 0; j < num_vars; ++j) { + if (!is_binary_var_for_clique_literals(problem, j, bound_tol)) { continue; } + const double xj = assignment[j]; + if (std::abs(xj - std::round(xj)) <= integer_tol) { continue; } + vertices.push_back(j); + weights.push_back(xj); + vertices.push_back(j + num_vars); + weights.push_back(1.0 - xj); + } + if (vertices.empty()) { return {}; } + + std::vector vertex_to_local(2 * num_vars, -1); + std::vector in_subgraph(2 * num_vars, 0); + for (size_t idx = 0; idx < vertices.size(); ++idx) { + vertex_to_local[vertices[idx]] = static_cast(idx); + in_subgraph[vertices[idx]] = 1; + } + + std::vector> adj_local(vertices.size()); + for (size_t idx = 0; idx < vertices.size(); ++idx) { + const auto vertex_idx = vertices[idx]; + auto adj_set = clique_table.get_adj_set_of_var(vertex_idx); + auto& adj = adj_local[idx]; + adj.reserve(adj_set.size()); + for (const auto neighbor : adj_set) { + cuopt_assert(neighbor >= 0 && neighbor < 2 * num_vars, + "Neighbor out of range in fractional literal clique builder"); + if (!in_subgraph[neighbor]) { continue; } + const auto local_neighbor = vertex_to_local[neighbor]; + if (local_neighbor >= 0) { adj.push_back(local_neighbor); } + } + } + + auto cliques_local = dual_simplex::find_maximal_cliques_for_test( + adj_local, weights, 1.0 + kCliqueTestTol, max_calls, std::numeric_limits::infinity()); + std::vector> cliques_global; + cliques_global.reserve(cliques_local.size()); + for (auto& local_clique : cliques_local) { + std::vector global_clique; + global_clique.reserve(local_clique.size()); + for (const auto local_idx : local_clique) { + cuopt_assert(local_idx >= 0 && static_cast(local_idx) < vertices.size(), + "Local clique index out of range"); + global_clique.push_back(vertices[local_idx]); + } + cliques_global.push_back(std::move(global_clique)); + } + return canonicalize_cliques(std::move(cliques_global)); +} + +std::vector>& get_neos8_fractional_literal_cliques_cached() +{ + static std::once_flag init_flag; + static std::unique_ptr>> cliques_ptr; + std::call_once(init_flag, []() { + auto& neos8_model = get_neos8_model_cached(); + auto& clique_table = get_neos8_clique_table_cached(); + auto& lp_relaxation = get_neos8_lp_relaxation_solution_cached(); + auto cliques = build_fractional_literal_cliques_for_assignment( + neos8_model, clique_table, lp_relaxation.primal, kCliqueTestTol, kCliqueTestTol, 100000); + cliques_ptr = std::make_unique>>(std::move(cliques)); + }); + cuopt_assert(cliques_ptr != nullptr, "Failed to initialize cached neos8 dumped literal cliques"); + return *cliques_ptr; +} + +double literal_clique_cut_violation(const std::vector& literal_clique, + const std::vector& assignment, + int num_vars) +{ + cuopt_assert(static_cast(assignment.size()) >= num_vars, + "Assignment size mismatch in literal clique violation"); + double dot = 0.0; + int num_complement_vars = 0; + for (const auto literal : literal_clique) { + cuopt_assert(literal >= 0 && literal < 2 * num_vars, "Literal out of range"); + const int var_idx = literal % num_vars; + const bool is_complement = literal >= num_vars; + if (is_complement) { + num_complement_vars++; + dot += assignment[var_idx]; + } else { + dot -= assignment[var_idx]; + } + } + const double rhs = static_cast(num_complement_vars - 1); + return rhs - dot; +} + +std::string format_phase2_literal_panic_dump(const std::vector& literal_clique, + const std::vector& incumbent, + int num_vars) +{ + std::ostringstream out; + out << "\nLiteral clique:"; + for (const auto literal : literal_clique) { + const bool is_complement = literal >= num_vars; + const int var_idx = literal % num_vars; + out << " " << (is_complement ? "~x" : "x") << var_idx << "(value=" << incumbent[var_idx] << ")"; + } + out << "\nViolation: " << literal_clique_cut_violation(literal_clique, incumbent, num_vars); + return out.str(); +} + +bool literal_cut_is_invalid_for_incumbent(const std::vector& literal_clique, + const std::vector& incumbent, + int num_vars, + double tol) +{ + return literal_clique_cut_violation(literal_clique, incumbent, num_vars) > tol; +} + +bool prefix_has_invalid_literal_cut(const std::vector>& dumped_cuts, + size_t prefix_end_exclusive, + const std::vector& incumbent, + int num_vars, + double tol) +{ + for (size_t i = 0; i < prefix_end_exclusive; ++i) { + if (literal_cut_is_invalid_for_incumbent(dumped_cuts[i], incumbent, num_vars, tol)) { + return true; + } + } + return false; +} + +std::optional isolate_first_invalid_literal_cut_by_bisection( + const std::vector>& dumped_cuts, + const std::vector& incumbent, + int num_vars, + double tol) +{ + if (!prefix_has_invalid_literal_cut(dumped_cuts, dumped_cuts.size(), incumbent, num_vars, tol)) { + return std::nullopt; + } + size_t lo = 0; + size_t hi = dumped_cuts.size() - 1; + while (lo < hi) { + const size_t mid = lo + (hi - lo) / 2; + if (prefix_has_invalid_literal_cut(dumped_cuts, mid + 1, incumbent, num_vars, tol)) { + hi = mid; + } else { + lo = mid + 1; + } + } + return lo; +} + +mps_parser::mps_data_model_t& get_neos8_lp_relaxation_model_cached() +{ + static std::once_flag init_flag; + static std::unique_ptr> model_ptr; + std::call_once(init_flag, []() { + auto lp_relaxation = get_neos8_model_cached(); + std::vector all_continuous(lp_relaxation.get_n_variables(), 'C'); + lp_relaxation.set_variable_types(all_continuous); + model_ptr = + std::make_unique>(std::move(lp_relaxation)); + }); + cuopt_assert(model_ptr != nullptr, "Failed to initialize cached neos8 LP relaxation model"); + return *model_ptr; +} + +mps_parser::mps_data_model_t append_literal_cut_prefix_to_lp_model( + const mps_parser::mps_data_model_t& base_lp_model, + const std::vector>& dumped_cuts, + size_t prefix_end_exclusive, + int num_vars) +{ + auto model_with_cuts = base_lp_model; + if (prefix_end_exclusive == 0) { return model_with_cuts; } + + std::vector matrix_values = base_lp_model.get_constraint_matrix_values(); + std::vector matrix_indices = base_lp_model.get_constraint_matrix_indices(); + std::vector matrix_offsets = base_lp_model.get_constraint_matrix_offsets(); + std::vector constraint_lbs = base_lp_model.get_constraint_lower_bounds(); + std::vector constraint_ubs = base_lp_model.get_constraint_upper_bounds(); + std::vector row_names = base_lp_model.get_row_names(); + if (matrix_offsets.empty()) { matrix_offsets.push_back(0); } + + const size_t cuts_to_apply = std::min(prefix_end_exclusive, dumped_cuts.size()); + for (size_t cut_idx = 0; cut_idx < cuts_to_apply; ++cut_idx) { + const auto& literal_cut = dumped_cuts[cut_idx]; + + std::vector row_vars; + std::vector row_coeffs; + row_vars.reserve(literal_cut.size()); + row_coeffs.reserve(literal_cut.size()); + + int num_complements = 0; + for (const auto literal : literal_cut) { + cuopt_assert(literal >= 0 && literal < 2 * num_vars, + "Literal out of range for LP cut append"); + const int var_idx = literal % num_vars; + const bool is_complement = literal >= num_vars; + if (is_complement) { num_complements++; } + const double coeff = is_complement ? 1.0 : -1.0; + + bool found = false; + for (size_t t = 0; t < row_vars.size(); ++t) { + if (row_vars[t] == var_idx) { + row_coeffs[t] += coeff; + found = true; + break; + } + } + if (!found) { + row_vars.push_back(var_idx); + row_coeffs.push_back(coeff); + } + } + + std::vector order(row_vars.size()); + std::iota(order.begin(), order.end(), 0); + std::sort(order.begin(), order.end(), [&](int a, int b) { return row_vars[a] < row_vars[b]; }); + for (const auto pos : order) { + const double coeff = row_coeffs[pos]; + if (std::abs(coeff) <= 1e-12) { continue; } + matrix_indices.push_back(row_vars[pos]); + matrix_values.push_back(coeff); + } + matrix_offsets.push_back(static_cast(matrix_indices.size())); + constraint_lbs.push_back(static_cast(num_complements - 1)); + constraint_ubs.push_back(std::numeric_limits::infinity()); + row_names.push_back("literal_cut_" + std::to_string(cut_idx)); + } + + model_with_cuts.set_csr_constraint_matrix(matrix_values.data(), + matrix_values.size(), + matrix_indices.data(), + matrix_indices.size(), + matrix_offsets.data(), + matrix_offsets.size()); + model_with_cuts.set_constraint_lower_bounds(constraint_lbs.data(), constraint_lbs.size()); + model_with_cuts.set_constraint_upper_bounds(constraint_ubs.data(), constraint_ubs.size()); + model_with_cuts.set_row_names(row_names); + return model_with_cuts; +} + +pdlp_termination_status_t solve_lp_with_literal_cut_prefix( + const std::vector>& dumped_cuts, size_t prefix_end_exclusive, int num_vars) +{ + const raft::handle_t handle{}; + auto& base_lp_model = get_neos8_lp_relaxation_model_cached(); + auto model_with_cuts = append_literal_cut_prefix_to_lp_model( + base_lp_model, dumped_cuts, prefix_end_exclusive, num_vars); + + pdlp_solver_settings_t lp_settings{}; + lp_settings.time_limit = 120.0; + lp_settings.presolver = presolver_t::None; + lp_settings.set_optimality_tolerance(1e-8); + + auto lp_solution = solve_lp(&handle, model_with_cuts, lp_settings); + return lp_solution.get_termination_status(); +} + +bool prefix_makes_lp_relaxation_infeasible(const std::vector>& dumped_cuts, + size_t prefix_end_exclusive, + int num_vars) +{ + const auto status = solve_lp_with_literal_cut_prefix(dumped_cuts, prefix_end_exclusive, num_vars); + return status == pdlp_termination_status_t::PrimalInfeasible; +} + +std::optional isolate_first_lp_infeasible_literal_cut_by_bisection( + const std::vector>& dumped_cuts, int num_vars) +{ + if (!prefix_makes_lp_relaxation_infeasible(dumped_cuts, dumped_cuts.size(), num_vars)) { + return std::nullopt; + } + size_t lo = 0; + size_t hi = dumped_cuts.size() - 1; + while (lo < hi) { + const size_t mid = lo + (hi - lo) / 2; + if (prefix_makes_lp_relaxation_infeasible(dumped_cuts, mid + 1, num_vars)) { + hi = mid; + } else { + lo = mid + 1; + } + } + return lo; +} + +} // namespace + // Problem data for the mixed integer linear programming problem mps_parser::mps_data_model_t create_cuts_problem_1() { @@ -165,4 +969,416 @@ TEST(cuts, test_cuts_2) EXPECT_EQ(solution.get_num_nodes(), 0); } +TEST(cuts, clique_phase1_smoke_conflict_graph_edges) +{ + const raft::handle_t handle{}; + auto problem = create_pairwise_triangle_with_isolated_variable_problem(); + auto clique_table = build_clique_table_for_model(handle, problem); + + // Positive edges from triangle. + EXPECT_TRUE(clique_table.check_adjacency(0, 1)); + EXPECT_TRUE(clique_table.check_adjacency(1, 0)); + EXPECT_TRUE(clique_table.check_adjacency(1, 2)); + EXPECT_TRUE(clique_table.check_adjacency(2, 1)); + EXPECT_TRUE(clique_table.check_adjacency(0, 2)); + EXPECT_TRUE(clique_table.check_adjacency(2, 0)); + + // Negative edges to isolated x3. + EXPECT_FALSE(clique_table.check_adjacency(0, 3)); + EXPECT_FALSE(clique_table.check_adjacency(3, 0)); + EXPECT_FALSE(clique_table.check_adjacency(1, 3)); + EXPECT_FALSE(clique_table.check_adjacency(3, 1)); + EXPECT_FALSE(clique_table.check_adjacency(2, 3)); + EXPECT_FALSE(clique_table.check_adjacency(3, 2)); + + // Self is never an edge. + EXPECT_FALSE(clique_table.check_adjacency(3, 3)); +} + +TEST(cuts, clique_phase1_unit_maximal_clique_finder_hardcoded_adj) +{ + // Hardcoded graph: + // triangle (0,1,2) and an extra edge (2,3) + std::vector> adj = { + {0, 1, 1, 0}, + {1, 0, 1, 0}, + {1, 1, 0, 1}, + {0, 0, 1, 0}, + }; + + auto maximal_bruteforce = canonicalize_cliques(maximal_cliques_bruteforce(adj)); + auto maximal_internal = maximal_cliques_from_production_algorithm(adj); + EXPECT_EQ(maximal_internal, maximal_bruteforce); + bool found_triangle = false; + for (const auto& clique : maximal_internal) { + if (clique.size() == 3 && clique[0] == 0 && clique[1] == 1 && clique[2] == 2) { + found_triangle = true; + break; + } + } + EXPECT_TRUE(found_triangle); +} + +TEST(cuts, clique_phase1_addtl_conflict_symmetry_and_reverse_lookup) +{ + const raft::handle_t handle{}; + auto problem = create_weighted_addtl_conflict_problem(); + auto clique_table = build_clique_table_for_model_with_min_size(handle, problem, 1); + + ASSERT_FALSE(clique_table.addtl_cliques.empty()); + + // Conflict introduced through additional clique path must be symmetric. + EXPECT_TRUE(clique_table.check_adjacency(1, 3)); + EXPECT_TRUE(clique_table.check_adjacency(3, 1)); + + // get_adj_set_of_var() must also include reverse lookup for addtl membership. + auto adj_of_1 = clique_table.get_adj_set_of_var(1); + auto adj_of_3 = clique_table.get_adj_set_of_var(3); + EXPECT_TRUE(adj_of_1.count(3) > 0); + EXPECT_TRUE(adj_of_3.count(1) > 0); +} + +TEST(cuts, clique_phase1_remove_small_cliques_preserves_addtl_conflicts) +{ + const raft::handle_t handle{}; + auto problem = create_weighted_addtl_conflict_problem(); + // Force base clique {x2,x3} to be considered "small" and removed. + auto clique_table = build_clique_table_for_model_with_min_size(handle, problem, 2); + + EXPECT_TRUE(clique_table.first.empty()); + EXPECT_TRUE(clique_table.addtl_cliques.empty()); + + // Conflicts must remain materialized in adj_list_small_cliques after removals. + EXPECT_TRUE(clique_table.check_adjacency(1, 3)); + EXPECT_TRUE(clique_table.check_adjacency(3, 1)); + EXPECT_TRUE(clique_table.check_adjacency(2, 3)); + EXPECT_TRUE(clique_table.check_adjacency(3, 2)); + EXPECT_FALSE(clique_table.check_adjacency(0, 3)); +} + +TEST(cuts, clique_phase2_no_cut_off_optimal_solution_validation) +{ + const raft::handle_t handle{}; + auto problem = create_pairwise_triangle_set_packing_problem(); + + mip_solver_settings_t settings; + settings.time_limit = 10.0; + settings.presolver = presolver_t::None; + disable_all_cuts(settings); + + auto mip_solution = solve_mip(&handle, problem, settings); + ASSERT_EQ(mip_solution.get_termination_status(), mip_termination_status_t::Optimal); + auto x_star = cuopt::host_copy(mip_solution.get_solution(), handle.get_stream()); + + auto clique_table = build_clique_table_for_model(handle, problem); + auto adj = build_original_adjacency_matrix(clique_table, problem.get_n_variables()); + auto maximal = maximal_cliques_bruteforce(adj); + ASSERT_FALSE(maximal.empty()); + + for (const auto& clique_vars : maximal) { + if (clique_vars.size() < 2) { continue; } + const double lhs = original_clique_sum(clique_vars, x_star); + ASSERT_LE(lhs, 1.0 + kCliqueTestTol) << format_phase2_panic_dump(problem, clique_vars, x_star); + } +} + +TEST(cuts, clique_phase3_fractional_separation_must_cut_off) +{ + const raft::handle_t handle{}; + auto mip_problem = create_pairwise_triangle_set_packing_problem(); + + auto lp_relaxation = mip_problem; + std::vector all_continuous(lp_relaxation.get_n_variables(), 'C'); + lp_relaxation.set_variable_types(all_continuous); + + pdlp_solver_settings_t lp_settings{}; + lp_settings.time_limit = 10.0; + lp_settings.presolver = presolver_t::None; + lp_settings.set_optimality_tolerance(1e-8); + + auto lp_solution = solve_lp(&handle, lp_relaxation, lp_settings); + ASSERT_EQ(lp_solution.get_termination_status(), pdlp_termination_status_t::Optimal); + auto x_bar = cuopt::host_copy(lp_solution.get_primal_solution(), handle.get_stream()); + + auto clique_table = build_clique_table_for_model(handle, mip_problem); + auto adj = build_original_adjacency_matrix(clique_table, mip_problem.get_n_variables()); + auto maximal = maximal_cliques_from_production_algorithm(adj); + + bool found_separating_clique = false; + for (const auto& clique_vars : maximal) { + if (clique_vars.size() < 2) { continue; } + const double lhs = original_clique_sum(clique_vars, x_bar); + if (lhs > 1.0 + kCliqueTestTol) { + found_separating_clique = true; + break; + } + } + EXPECT_TRUE(found_separating_clique); +} + +TEST(cuts, clique_phase4_fault_isolation_binary_search) +{ + // Simulated incumbent x* and dumped cuts. + // First invalid cut is at index 2: {0,1} gives 2 > 1. + const std::vector incumbent = {1.0, 1.0, 0.0, 0.0}; + const std::vector> dumped_cuts = { + {0, 2}, // valid + {1, 3}, // valid + {0, 1}, // invalid + {2, 3}, // valid + }; + + auto first_invalid = + isolate_first_invalid_cut_by_bisection(dumped_cuts, incumbent, kCliqueTestTol); + ASSERT_TRUE(first_invalid.has_value()); + EXPECT_EQ(first_invalid.value(), 2); +} + +TEST(cuts, clique_phase4_tree_depth_limit_smoke) +{ + const raft::handle_t handle{}; + auto problem = create_pairwise_triangle_set_packing_problem(); + + mip_solver_settings_t root_only_settings; + root_only_settings.time_limit = 10.0; + root_only_settings.presolver = presolver_t::None; + root_only_settings.node_limit = 0; + disable_non_clique_cuts(root_only_settings); + + mip_solver_settings_t deeper_settings = root_only_settings; + deeper_settings.node_limit = 100; + + auto root_only_solution = solve_mip(&handle, problem, root_only_settings); + auto deeper_solution = solve_mip(&handle, problem, deeper_settings); + + EXPECT_EQ(deeper_solution.get_termination_status(), mip_termination_status_t::Optimal); + EXPECT_NE(root_only_solution.get_termination_status(), mip_termination_status_t::Infeasible); + if (root_only_solution.get_termination_status() == mip_termination_status_t::Optimal) { + EXPECT_NEAR( + root_only_solution.get_objective_value(), deeper_solution.get_objective_value(), 1e-6); + } +} + +TEST(cuts, clique_phase5_ignores_non_binary_variables) +{ + const raft::handle_t handle{}; + auto problem = create_binary_continuous_mixed_conflict_problem(); + auto clique_table = build_clique_table_for_model(handle, problem); + + EXPECT_TRUE(clique_table.check_adjacency(0, 2)); + EXPECT_FALSE(clique_table.check_adjacency(0, 1)); + EXPECT_FALSE(clique_table.check_adjacency(1, 2)); +} + +TEST(cuts, clique_phase5_ignores_fractional_binary_bounds) +{ + const raft::handle_t handle{}; + auto problem = create_near_binary_bound_conflict_problem(); + auto clique_table = build_clique_table_for_model(handle, problem); + + EXPECT_FALSE(clique_table.check_adjacency(0, 1)); +} + +TEST(cuts, clique_neos8_phase1_addtl_indices_and_nonempty_graph) +{ + auto& clique_table = get_neos8_clique_table_cached(); + EXPECT_TRUE(!clique_table.first.empty() || !clique_table.addtl_cliques.empty()); + + const size_t max_addtl_to_check = std::min(clique_table.addtl_cliques.size(), 400); + for (size_t k = 0; k < max_addtl_to_check; ++k) { + const auto& addtl = clique_table.addtl_cliques[k]; + ASSERT_GE(addtl.clique_idx, 0); + ASSERT_LT(static_cast(addtl.clique_idx), clique_table.first.size()); + const auto& base = clique_table.first[addtl.clique_idx]; + ASSERT_GE(addtl.start_pos_on_clique, 0); + ASSERT_LE(static_cast(addtl.start_pos_on_clique), base.size()); + } +} + +TEST(cuts, clique_neos8_phase1_addtl_suffix_conflicts_materialized) +{ + auto& clique_table = get_neos8_clique_table_cached(); + if (clique_table.addtl_cliques.empty()) { + GTEST_SKIP() << "neos8 produced no additional cliques in this configuration"; + } + + size_t checked_addtl = 0; + const size_t max_addtl_to_check = std::min(clique_table.addtl_cliques.size(), 200); + for (size_t k = 0; k < max_addtl_to_check; ++k) { + const auto& addtl = clique_table.addtl_cliques[k]; + if (addtl.clique_idx < 0 || + static_cast(addtl.clique_idx) >= clique_table.first.size()) { + continue; + } + const auto& base = clique_table.first[addtl.clique_idx]; + const size_t start_at = static_cast(addtl.start_pos_on_clique); + if (start_at >= base.size()) { continue; } + + const size_t end_at = std::min(base.size(), start_at + 8); + for (size_t p = start_at; p < end_at; ++p) { + EXPECT_TRUE(clique_table.check_adjacency(addtl.vertex_idx, base[p])); + EXPECT_TRUE(clique_table.check_adjacency(base[p], addtl.vertex_idx)); + } + checked_addtl++; + } + EXPECT_GT(checked_addtl, 0); +} + +TEST(cuts, clique_neos8_phase1_symmetry_and_degree_cache_consistency) +{ + auto& clique_table = get_neos8_clique_table_cached(); + const int n_vertices = static_cast(clique_table.var_clique_map_first.size()); + ASSERT_GT(n_vertices, 0); + + const int sample_size = std::min(n_vertices, 24); + const int stride = std::max(1, n_vertices / sample_size); + std::vector sampled_vertices(sample_size); + for (int i = 0; i < sample_size; ++i) { + sampled_vertices[i] = (i * stride) % n_vertices; + } + + for (const auto v : sampled_vertices) { + const auto deg_cached = clique_table.get_degree_of_var(v); + const auto adj_set = clique_table.get_adj_set_of_var(v); + EXPECT_EQ(deg_cached, static_cast(adj_set.size())); + EXPECT_EQ(deg_cached, clique_table.get_degree_of_var(v)); + } + + for (int i = 0; i < sample_size; ++i) { + for (int j = i + 1; j < sample_size; ++j) { + const auto v1 = sampled_vertices[i]; + const auto v2 = sampled_vertices[j]; + EXPECT_EQ(clique_table.check_adjacency(v1, v2), clique_table.check_adjacency(v2, v1)); + } + } +} + +TEST(cuts, clique_neos8_phase2_no_cut_off_optimal_solution_validation) +{ + auto& no_cut_mip = get_neos8_optimal_solution_no_cuts_cached(); + ASSERT_EQ(no_cut_mip.status, mip_termination_status_t::Optimal); + + auto& lp_relaxation = get_neos8_lp_relaxation_solution_cached(); + ASSERT_EQ(lp_relaxation.status, pdlp_termination_status_t::Optimal); + + auto& dumped_literal_cuts = get_neos8_fractional_literal_cliques_cached(); + if (dumped_literal_cuts.empty()) { + GTEST_SKIP() << "neos8 produced no candidate literal cliques from LP relaxation"; + } + + const int num_vars = get_neos8_model_cached().get_n_variables(); + for (size_t i = 0; i < dumped_literal_cuts.size(); ++i) { + const double violation = + literal_clique_cut_violation(dumped_literal_cuts[i], no_cut_mip.primal, num_vars); + ASSERT_LE(violation, kCliqueTestTol) + << "Invalid clique cut at index " << i + << format_phase2_literal_panic_dump(dumped_literal_cuts[i], no_cut_mip.primal, num_vars); + } +} + +TEST(cuts, clique_neos8_phase3_fractional_separation_must_cut_off) +{ + auto& lp_relaxation = get_neos8_lp_relaxation_solution_cached(); + ASSERT_EQ(lp_relaxation.status, pdlp_termination_status_t::Optimal); + + auto& dumped_literal_cuts = get_neos8_fractional_literal_cliques_cached(); + if (dumped_literal_cuts.empty()) { + GTEST_SKIP() << "neos8 produced no candidate literal cliques from LP relaxation"; + } + + const int num_vars = get_neos8_model_cached().get_n_variables(); + for (size_t i = 0; i < dumped_literal_cuts.size(); ++i) { + const double violation = + literal_clique_cut_violation(dumped_literal_cuts[i], lp_relaxation.primal, num_vars); + ASSERT_GT(violation, kCliqueTestTol) + << "Non-separating clique cut at index " << i + << format_phase2_literal_panic_dump(dumped_literal_cuts[i], lp_relaxation.primal, num_vars); + } +} + +TEST(cuts, clique_neos8_phase4_fault_isolation_binary_search) +{ + auto& no_cut_mip = get_neos8_optimal_solution_no_cuts_cached(); + ASSERT_EQ(no_cut_mip.status, mip_termination_status_t::Optimal); + + auto& dumped_literal_cuts = get_neos8_fractional_literal_cliques_cached(); + if (dumped_literal_cuts.empty()) { + GTEST_SKIP() << "neos8 produced no candidate literal cliques from LP relaxation"; + } + + const auto& model = get_neos8_model_cached(); + const int num_vars = model.get_n_variables(); + + // Real dumped cuts should not invalidate the no-cut incumbent. + EXPECT_FALSE(prefix_has_invalid_literal_cut( + dumped_literal_cuts, dumped_literal_cuts.size(), no_cut_mip.primal, num_vars, kCliqueTestTol)); + + // Inject a known-invalid cut and verify bisection isolates it. + std::vector incumbent_ones; + incumbent_ones.reserve(2); + for (int j = 0; j < num_vars && incumbent_ones.size() < 2; ++j) { + if (!is_binary_var_for_clique_literals(model, j, kCliqueTestTol)) { continue; } + if (no_cut_mip.primal[j] >= 1.0 - kCliqueTestTol) { incumbent_ones.push_back(j); } + } + if (incumbent_ones.size() < 2) { + GTEST_SKIP() << "Could not find two binary variables fixed to one in neos8 incumbent"; + } + + auto cuts_with_injected_bug = dumped_literal_cuts; + const size_t injected_index = cuts_with_injected_bug.size(); + cuts_with_injected_bug.push_back({incumbent_ones[0], incumbent_ones[1]}); + + auto first_invalid = isolate_first_invalid_literal_cut_by_bisection( + cuts_with_injected_bug, no_cut_mip.primal, num_vars, kCliqueTestTol); + ASSERT_TRUE(first_invalid.has_value()); + EXPECT_EQ(first_invalid.value(), injected_index); +} + +TEST(cuts, clique_neos8_phase4_lp_infeasibility_binary_search) +{ + auto& dumped_literal_cuts = get_neos8_fractional_literal_cliques_cached(); + if (dumped_literal_cuts.empty()) { + GTEST_SKIP() << "neos8 produced no candidate literal cliques from LP relaxation"; + } + + const auto& model = get_neos8_model_cached(); + const int num_vars = model.get_n_variables(); + + std::vector> cuts_for_lp_search; + const size_t max_real_cuts = std::min(dumped_literal_cuts.size(), 64); + cuts_for_lp_search.insert(cuts_for_lp_search.end(), + dumped_literal_cuts.begin(), + dumped_literal_cuts.begin() + max_real_cuts); + + int inject_var = -1; + for (int j = 0; j < num_vars; ++j) { + if (is_binary_var_for_clique_literals(model, j, kCliqueTestTol)) { + inject_var = j; + break; + } + } + if (inject_var < 0) { + GTEST_SKIP() << "Could not find a binary variable for LP infeasibility injection"; + } + + const size_t injected_index = cuts_for_lp_search.size(); + cuts_for_lp_search.push_back( + {inject_var, inject_var, inject_var + num_vars, inject_var + num_vars}); + + // Prefix before injected cut should remain LP-feasible. + const auto status_before_injection = + solve_lp_with_literal_cut_prefix(cuts_for_lp_search, injected_index, num_vars); + EXPECT_NE(status_before_injection, pdlp_termination_status_t::PrimalInfeasible); + + // Full prefix should be LP-infeasible due to injected contradictory cut. + const auto status_with_injection = + solve_lp_with_literal_cut_prefix(cuts_for_lp_search, cuts_for_lp_search.size(), num_vars); + EXPECT_EQ(status_with_injection, pdlp_termination_status_t::PrimalInfeasible); + + auto first_infeasible = + isolate_first_lp_infeasible_literal_cut_by_bisection(cuts_for_lp_search, num_vars); + ASSERT_TRUE(first_infeasible.has_value()); + EXPECT_EQ(first_infeasible.value(), injected_index); +} + } // namespace cuopt::linear_programming::test diff --git a/datasets/mip/download_miplib_test_dataset.sh b/datasets/mip/download_miplib_test_dataset.sh index dc2dd79662..3040f0f543 100755 --- a/datasets/mip/download_miplib_test_dataset.sh +++ b/datasets/mip/download_miplib_test_dataset.sh @@ -20,6 +20,7 @@ INSTANCES=( "thor50dday" "stein9inf" "neos5" + "neos8" "swath1" "enlight_hard" "enlight11" From 014dc7242cc5a683807c9bd4f2e0345c2d0264e7 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 11 Mar 2026 04:30:09 -0700 Subject: [PATCH 167/225] bugfixes, reject infeasible itneger incumbents in B&B , lower probing cache work for determinism --- .../linear_programming/cuopt/run_mip.cpp | 1 + cpp/src/branch_and_bound/branch_and_bound.cpp | 189 ++++++++++++------ cpp/src/branch_and_bound/branch_and_bound.hpp | 6 +- cpp/src/cuts/cuts.cpp | 61 +++--- .../mip_heuristics/diversity/population.cu | 2 +- .../local_search/local_search.cu | 6 +- .../local_search/rounding/bounds_repair.cu | 23 ++- .../local_search/rounding/constraint_prop.cu | 10 +- .../mip_heuristics/presolve/probing_cache.cu | 6 + cpp/src/mip_heuristics/solve.cu | 1 + cpp/src/mip_heuristics/solver.cu | 61 +++++- cpp/src/utilities/determinism_log.hpp | 6 +- 12 files changed, 252 insertions(+), 120 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index a759103f27..b8b41f8f56 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -224,6 +224,7 @@ int run_single_file(std::string file_path, settings.reliability_branching = reliability_branching; settings.seed = 42; settings.gpu_heur_work_unit_scale = 0.9; + settings.mip_scaling = false; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 3ee8546610..debbd0800e 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -45,8 +45,8 @@ #include // uncomment to enable detailed detemrinism logs -// #undef CUOPT_DETERMINISM_LOG_PRINTF -// #define CUOPT_DETERMINISM_LOG_PRINTF(logger, ...) \ +// #undef CUOPT_DETERMINISM_LOG +// #define CUOPT_DETERMINISM_LOG(logger, ...) \ // do { \ // logger.printf(__VA_ARGS__); \ // } while (0) @@ -272,7 +272,7 @@ branch_and_bound_t::branch_and_bound_t( convert_user_problem(original_problem_, settings_, original_lp_, new_slacks_, dualize_info); full_variable_types(original_problem_, original_lp_, var_types_); assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic LP init state: rows=%d cols=%d nnz=%zu slacks=%zu slack_hash=0x%x " "rhs_hash=0x%x lower_hash=0x%x upper_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x " @@ -549,7 +549,7 @@ void branch_and_bound_t::set_new_solution(const std::vector& solu const f_t previous_upper = upper_bound_; upper_bound_ = obj; incumbent_.set_incumbent_solution(obj, crushed_solution); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic B&B incumbent update: source=external_direct prev_upper=%.16e " "new_upper=%.16e obj=%.16e hash=0x%x\n", @@ -618,7 +618,7 @@ void branch_and_bound_t::queue_external_solution_deterministic( const uint32_t a_col_hash = detail::compute_hash(original_lp_.A.col_start); const uint32_t a_row_hash = detail::compute_hash(original_lp_.A.i); const uint32_t a_val_hash = detail::compute_hash(original_lp_.A.x); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic external crush ctx: wut=%.6f lp_rows=%d lp_cols=%d lp_nnz=%zu " "active_cut_rows=%d " @@ -643,7 +643,7 @@ void branch_and_bound_t::queue_external_solution_deterministic( heuristic_solution_queue_.push_back({solution, user_objective, work_unit_ts, origin}); const size_t heuristic_queue_size = heuristic_solution_queue_.size(); mutex_heuristic_queue_.unlock(); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic external queued_for_retirement: wut=%.6f user_obj=%.16e host_hash=0x%x " "heur_q=%zu\n", @@ -756,7 +756,7 @@ void branch_and_bound_t::repair_heuristic_solutions() const f_t previous_upper = upper_bound_; upper_bound_ = repaired_obj; incumbent_.set_incumbent_solution(repaired_obj, repaired_solution); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic B&B incumbent update: source=repair_queue prev_upper=%.16e " "new_upper=%.16e obj=%.16e hash=0x%x\n", @@ -784,7 +784,7 @@ void branch_and_bound_t::set_solution_at_root(mip_solution_t const f_t previous_upper = upper_bound_; incumbent_.set_incumbent_solution(root_objective_, root_relax_soln_.x); upper_bound_ = root_objective_; - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic B&B incumbent update: source=root_solution prev_upper=%.16e new_upper=%.16e " "obj=%.16e hash=0x%x\n", @@ -888,7 +888,7 @@ void branch_and_bound_t::set_final_solution(mip_solution_t& solution.lower_bound = lower_bound; solution.nodes_explored = exploration_stats_.nodes_explored; solution.simplex_iterations = exploration_stats_.total_lp_iters; - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic B&B final package: status=%d incumbent_obj=%.16e lower_bound=%.16e " "incumbent_hash=0x%x final_hash=0x%x nodes=%d simplex_iterations=%d\n", @@ -918,7 +918,7 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, const f_t previous_upper = upper_bound_; incumbent_.set_incumbent_solution(leaf_objective, leaf_solution); upper_bound_ = leaf_objective; - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic B&B incumbent update: source=leaf prev_upper=%.16e new_upper=%.16e " "obj=%.16e hash=0x%x depth=%d worker_type=%d\n", @@ -1073,6 +1073,23 @@ struct nondeterministic_policy_t : tree_update_policy_t { f_t obj, const std::vector& x) override { + f_t primal_err; + f_t bound_err; + i_t num_fractional; + bool cg = check_guess( + bnb.original_lp_, bnb.settings_, bnb.var_types_, x, primal_err, bound_err, num_fractional); + if (!cg) { + bnb.settings_.log.printf( + "Rejecting infeasible integer solution: node=%d depth=%d " + "obj=%.6e primal_err=%.6e bound_err=%.6e fractional=%d\n", + node->node_id, + node->depth, + obj, + primal_err, + bound_err, + num_fractional); + return; + } bnb.add_feasible_solution(obj, x, node->depth, worker->search_strategy); } @@ -1166,6 +1183,29 @@ struct deterministic_bfs_policy_t const std::vector& x) override { if (obj < this->worker.local_upper_bound) { + f_t primal_err; + f_t bound_err; + i_t num_fractional; + bool cg = check_guess(this->bnb.original_lp_, + this->bnb.settings_, + this->bnb.var_types_, + x, + primal_err, + bound_err, + num_fractional); + if (!cg) { + this->bnb.settings_.log.printf( + "Rejecting infeasible integer solution: worker=%d node=%d depth=%d " + "obj=%.6e primal_err=%.6e bound_err=%.6e fractional=%d\n", + this->worker.worker_id, + node->creation_seq, + node->depth, + obj, + primal_err, + bound_err, + num_fractional); + return; + } const f_t previous_local_upper = this->worker.local_upper_bound; const int previous_seq = this->worker.next_solution_seq; this->worker.local_upper_bound = obj; @@ -1179,7 +1219,7 @@ struct deterministic_bfs_policy_t cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE}); if (this->bnb.deterministic_current_horizon_ <= this->bnb.deterministic_horizon_step_ + 1e-9) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( this->bnb.settings_.log, "Deterministic BFS local integer queue: horizon=%.6f worker=%d node_id=%d packed=0x%llx " "path_hash=0x%x depth=%d obj=%.16e sol_hash=0x%x local_upper_before=%.16e " @@ -1267,7 +1307,7 @@ struct deterministic_bfs_policy_t this->worker.enqueue_children_for_plunge(node->get_down_child(), node->get_up_child(), dir); if (this->bnb.deterministic_current_horizon_ <= this->bnb.deterministic_horizon_step_ + 1e-9) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( this->bnb.settings_.log, "Deterministic BFS branch create: horizon=%.6f worker=%d parent_packed=0x%llx " "parent_path_hash=0x%x depth=%d branch_var=%d dir=%d frac=%.16e " @@ -1323,13 +1363,36 @@ struct deterministic_diving_policy_t const std::vector& x) override { if (obj < this->worker.local_upper_bound) { + f_t primal_err; + f_t bound_err; + i_t num_fractional; + bool cg = check_guess(this->bnb.original_lp_, + this->bnb.settings_, + this->bnb.var_types_, + x, + primal_err, + bound_err, + num_fractional); + if (!cg) { + this->bnb.settings_.log.printf( + "Rejecting infeasible diving integer solution: worker=%d node=%d depth=%d " + "obj=%.6e primal_err=%.6e bound_err=%.6e fractional=%d\n", + this->worker.worker_id, + node->creation_seq, + node->depth, + obj, + primal_err, + bound_err, + num_fractional); + return; + } const f_t previous_local_upper = this->worker.local_upper_bound; const int previous_seq = this->worker.next_solution_seq; this->worker.local_upper_bound = obj; this->worker.queue_integer_solution(obj, x, node->depth); if (this->bnb.deterministic_current_horizon_ <= this->bnb.deterministic_horizon_step_ + 1e-9) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( this->bnb.settings_.log, "Deterministic diving local integer queue: horizon=%.6f worker=%d node_id=%d " "packed=0x%llx " @@ -2198,7 +2261,7 @@ lp_status_t branch_and_bound_t::solve_root_relaxation( settings_.log.printf("\n"); is_root_solution_set = true; - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root flag set: root_status=%d root_obj=%.16e recomputed_root_obj=%.16e " "callback_flag=%d x_hash=0x%x\n", @@ -2255,7 +2318,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut const f_t previous_upper = upper_bound_; incumbent_.set_incumbent_solution(computed_obj, crushed_guess); upper_bound_ = computed_obj; - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic B&B incumbent update: source=initial_guess prev_upper=%.16e " "new_upper=%.16e obj=%.16e hash=0x%x\n", @@ -2346,7 +2409,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut { const f_t previous_root_objective = root_objective_; root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root objective assign: source=post_root_solve old=%.16e new=%.16e " "x_hash=0x%x obj_hash=0x%x\n", @@ -2482,7 +2545,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut f_t add_cuts_start_time = tic(); mutex_original_lp_.lock(); assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root LP before add_cuts: pass=%d fractional=%d num_cuts=%d rows=%d " "cols=%d nnz=%zu slacks=%zu slack_hash=0x%x rhs_hash=0x%x lower_hash=0x%x " @@ -2514,7 +2577,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut edge_norms_); var_types_.resize(original_lp_.num_cols, variable_type_t::CONTINUOUS); assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root LP after add_cuts: pass=%d fractional=%d num_cuts=%d status=%d " "rows=%d cols=%d nnz=%zu slacks=%zu slack_hash=0x%x rhs_hash=0x%x lower_hash=0x%x " @@ -2588,28 +2651,28 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut i_t iter = 0; bool initialize_basis = false; lp_settings.concurrent_halt = NULL; - CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, - "Cut loop LP warm-start: pass=%d rows=%d cols=%d " - "lower_hash=0x%x upper_hash=0x%x " - "x_hash=0x%x y_hash=0x%x z_hash=0x%x " - "basic_hash=0x%x nonbasic_hash=0x%x " - "vstatus_hash=0x%x edge_norms_hash=0x%x " - "cut_off=%.16e work_limit=%.16e time_limit=%.16e\n", - cut_pass, - original_lp_.num_rows, - original_lp_.num_cols, - detail::compute_hash(original_lp_.lower), - detail::compute_hash(original_lp_.upper), - detail::compute_hash(root_relax_soln_.x), - detail::compute_hash(root_relax_soln_.y), - detail::compute_hash(root_relax_soln_.z), - detail::compute_hash(basic_list), - detail::compute_hash(nonbasic_list), - detail::compute_hash(root_vstatus_), - detail::compute_hash(edge_norms_), - lp_settings.cut_off, - lp_settings.work_limit, - lp_settings.time_limit); + CUOPT_DETERMINISM_LOG(settings_.log, + "Cut loop LP warm-start: pass=%d rows=%d cols=%d " + "lower_hash=0x%x upper_hash=0x%x " + "x_hash=0x%x y_hash=0x%x z_hash=0x%x " + "basic_hash=0x%x nonbasic_hash=0x%x " + "vstatus_hash=0x%x edge_norms_hash=0x%x " + "cut_off=%.16e work_limit=%.16e time_limit=%.16e\n", + cut_pass, + original_lp_.num_rows, + original_lp_.num_cols, + detail::compute_hash(original_lp_.lower), + detail::compute_hash(original_lp_.upper), + detail::compute_hash(root_relax_soln_.x), + detail::compute_hash(root_relax_soln_.y), + detail::compute_hash(root_relax_soln_.z), + detail::compute_hash(basic_list), + detail::compute_hash(nonbasic_list), + detail::compute_hash(root_vstatus_), + detail::compute_hash(edge_norms_), + lp_settings.cut_off, + lp_settings.work_limit, + lp_settings.time_limit); f_t dual_phase2_start_time = tic(); dual::status_t cut_status = dual_phase2_with_advanced_basis(2, 0, @@ -2629,7 +2692,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut { const f_t previous_root_objective = root_objective_; root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root objective assign: source=cut_lp_resolve old=%.16e new=%.16e " "pass=%d x_hash=0x%x obj_hash=0x%x\n", @@ -2675,7 +2738,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut { const f_t previous_root_objective = root_objective_; root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root objective assign: source=cut_lp_scratch old=%.16e new=%.16e " "pass=%d x_hash=0x%x obj_hash=0x%x\n", @@ -2697,7 +2760,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut const f_t root_objective_before_remove = root_objective_; const f_t root_objective_before_remove_recomputed = compute_objective(original_lp_, root_relax_soln_.x); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root LP before remove_cuts: pass=%d fractional=%d rows=%d cols=%d " "nnz=%zu original_rows=%d active_cut_rows=%d slacks=%zu slack_hash=0x%x rhs_hash=0x%x " @@ -2742,7 +2805,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut basis_update); assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); const f_t root_objective_after_remove = compute_objective(original_lp_, root_relax_soln_.x); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root LP after remove_cuts: pass=%d fractional=%d rows=%d cols=%d " "nnz=%zu original_rows=%d active_cut_rows=%d slacks=%zu slack_hash=0x%x rhs_hash=0x%x " @@ -2782,7 +2845,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut assert(nonbasic_list.size() == static_cast(original_lp_.num_cols - original_lp_.num_rows)); assert(root_vstatus_.size() == static_cast(original_lp_.num_cols)); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root pass state: pass=%d root_obj=%.16e num_fractional=%d rel_gap=%.16e " "abs_gap=%.16e x_hash=0x%x y_hash=0x%x z_hash=0x%x basic_hash=0x%x nonbasic_hash=0x%x " @@ -2811,7 +2874,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut upper_bound_ = root_objective_; mutex_upper_.lock(); incumbent_.set_incumbent_solution(root_objective_, root_relax_soln_.x); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic B&B incumbent update: source=root_integral_pass prev_upper=%.16e " "new_upper=%.16e obj=%.16e hash=0x%x\n", @@ -2836,7 +2899,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut const f_t factor = settings_.cut_change_threshold; const f_t min_objective = 1e-3; if (change_in_objective <= factor * std::max(min_objective, std::abs(root_relax_objective))) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root pass break: pass=%d change=%.16e threshold=%.16e last_obj=%.16e " "root_relax_obj=%.16e root_obj=%.16e\n", @@ -2852,7 +2915,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_relax_objective); break; } - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root pass continue: pass=%d change=%.16e threshold=%.16e last_obj=%.16e " "root_relax_obj=%.16e root_obj=%.16e\n", @@ -3337,7 +3400,7 @@ void branch_and_bound_t::run_deterministic_bfs_loop( worker.current_node = node; if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9 && worker.total_nodes_processed < 16) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic BFS dequeue: horizon=%.6f worker=%d node_id=%d packed=0x%llx " "path_hash=0x%x depth=%d lower=%.16e local_upper=%.16e clock=%.6f queue_size=%zu\n", @@ -3357,7 +3420,7 @@ void branch_and_bound_t::run_deterministic_bfs_loop( f_t rel_gap = user_relative_gap(original_lp_, upper_bound, node->lower_bound); if (node->lower_bound > upper_bound) { if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic BFS prune vs local upper: horizon=%.6f worker=%d node_id=%d " "packed=0x%llx " @@ -3429,7 +3492,7 @@ void branch_and_bound_t::deterministic_sync_callback() bb_event_batch_t all_events = deterministic_workers_->collect_and_sort_events(); if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic sync start: horizon_idx=%d horizon_end=%.6f global_upper=%.16e " "nodes_explored=%d nodes_unexplored=%lu event_count=%zu heuristic_queue=%zu\n", @@ -3443,7 +3506,7 @@ void branch_and_bound_t::deterministic_sync_callback() for (size_t i = 0; i < all_events.events.size(); ++i) { const auto& event = all_events.events[i]; if (event.type != bb_event_type_t::NODE_INTEGER) { continue; } - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic sync integer event[%zu]: wut=%.6f worker=%d node_id=%d event_seq=%d " "obj=%.16e\n", @@ -3455,7 +3518,7 @@ void branch_and_bound_t::deterministic_sync_callback() event.payload.integer_solution.objective_value); } for (const auto& worker : *deterministic_workers_) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic BFS horizon summary: horizon=%.6f worker=%d clock=%.16e " "nodes_processed_h=%d total_nodes=%d integer_queue=%zu next_creation_seq=%d " @@ -3726,7 +3789,7 @@ node_status_t branch_and_bound_t::solve_node_deterministic( auto [status, round_dir] = update_tree_impl(node_ptr, search_tree, &worker, lp_status, policy); if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9 && (status == node_status_t::HAS_CHILDREN || status == node_status_t::INTEGER_FEASIBLE)) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic BFS solve progress: horizon=%.6f worker=%d node_packed=0x%llx " "node_path_hash=0x%x depth=%d lp_status=%d status=%d node_iter=%d work=%.16e " @@ -3838,9 +3901,9 @@ void branch_and_bound_t::deterministic_sort_replay_events( }); if (!due_solutions.empty()) { - CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, - "Deterministic sync: retiring %ld external solutions\n", - due_solutions.size()); + CUOPT_DETERMINISM_LOG(settings_.log, + "Deterministic sync: retiring %ld external solutions\n", + due_solutions.size()); for (const auto& queued_solution : due_solutions) { std::vector crushed_solution; f_t obj; @@ -3870,7 +3933,7 @@ void branch_and_bound_t::deterministic_sort_replay_events( queued_solution.work_timestamp, queued_solution.origin}, search_strategy_t::BEST_FIRST}); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic retirement accepted: wut=%.6f obj=%.16e origin=%s primal_err=%.6e " "bound_err=%.6e fractional=%d\n", @@ -3911,7 +3974,7 @@ void branch_and_bound_t::deterministic_sort_replay_events( } } if (!replay_solutions.empty() || !heuristic_solution_queue_.empty()) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic replay extract: horizon=%.6f now=%zu future=%zu upper=%.16e\n", deterministic_current_horizon_, @@ -3941,7 +4004,7 @@ void branch_and_bound_t::deterministic_sort_replay_events( f_t deterministic_lower = deterministic_compute_lower_bound(); f_t current_upper = upper_bound_.load(); if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic solution replay: candidates=%zu lower=%.16e upper_before=%.16e\n", replay_solutions.size(), @@ -3950,7 +4013,7 @@ void branch_and_bound_t::deterministic_sort_replay_events( for (size_t i = 0; i < replay_solutions.size(); ++i) { const auto& replay = replay_solutions[i]; const auto& sol = replay.solution; - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic replay solution[%zu]: wut=%.6f obj=%.16e origin=%s worker=%d seq=%d " "depth=%d sol_hash=0x%x\n", @@ -4013,7 +4076,7 @@ void branch_and_bound_t::deterministic_sort_replay_events( incumbent_.set_incumbent_solution(sol.objective, sol.solution); current_upper = sol.objective; improved = true; - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic B&B incumbent update: source=det_replay prev_upper=%.16e " "new_upper=%.16e obj=%.16e hash=0x%x worker=%d seq=%d wut=%.6f horizon=%.6f\n", @@ -4026,7 +4089,7 @@ void branch_and_bound_t::deterministic_sort_replay_events( sol.work_timestamp, deterministic_current_horizon_); } - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic replay: horizon=%.6f wut=%.6f obj=%.16e origin=%s accepted=%d " "upper_now=%.16e worker=%d seq=%d sol_hash=0x%x\n", diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index 08ba10fade..5bf4fc062a 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -82,7 +82,7 @@ class branch_and_bound_t { f_t user_objective, i_t iterations) { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root callback enter: guard=%d crossover_flag=%d current_root_obj=%.16e " "callback_solver_obj=%.16e callback_user_obj=%.16e primal_size=%zu dual_size=%zu " @@ -106,7 +106,7 @@ class branch_and_bound_t { root_crossover_soln_.user_objective = user_objective; root_crossover_soln_.iterations = iterations; root_crossover_solution_set_.store(true, std::memory_order_release); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root callback accept: root_obj_before=%.16e root_obj_after=%.16e " "callback_solver_obj=%.16e callback_user_obj=%.16e iterations=%d\n", @@ -116,7 +116,7 @@ class branch_and_bound_t { user_objective, iterations); } else { - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings_.log, "Deterministic root callback ignore: current_root_obj=%.16e callback_solver_obj=%.16e " "callback_user_obj=%.16e iterations=%d\n", diff --git a/cpp/src/cuts/cuts.cpp b/cpp/src/cuts/cuts.cpp index 98c1c7a3c6..a3ed0a3e37 100644 --- a/cpp/src/cuts/cuts.cpp +++ b/cpp/src/cuts/cuts.cpp @@ -75,7 +75,7 @@ void log_lp_state_summary(const simplex_solver_settings_t& settings, { assert(new_slacks.size() == static_cast(lp.num_rows)); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings.log, "%s: cuts_delta=%d rows=%d cols=%d nnz=%zu slacks=%zu slack_hash=0x%x rhs_hash=0x%x " "lower_hash=0x%x upper_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x Aval_hash=0x%x\n", @@ -217,24 +217,24 @@ void cut_pool_t::score_cuts(std::vector& x_relax) const uint32_t candidate_order_hash = compute_index_order_hash(candidate_order); const uint32_t candidate_cut_hash = compute_ordered_hash_list_hash(candidate_cut_hashes); - CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, - "Deterministic cut candidate ranking: cuts=%zu order_hash=0x%x " - "cut_hash=0x%x top=", - candidate_order.size(), - candidate_order_hash, - candidate_cut_hash); + CUOPT_DETERMINISM_LOG(settings_.log, + "Deterministic cut candidate ranking: cuts=%zu order_hash=0x%x " + "cut_hash=0x%x top=", + candidate_order.size(), + candidate_order_hash, + candidate_cut_hash); const size_t max_top_cuts = std::min((size_t)8, candidate_order.size()); for (size_t k = 0; k < max_top_cuts; ++k) { const i_t cut_idx = candidate_order[k]; - CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, - "%s[%d type=%d dist=%.16e hash=0x%x]", - k == 0 ? "" : " ", - cut_idx, - static_cast(cut_type_[cut_idx]), - cut_distances_[cut_idx], - candidate_cut_hashes[k]); + CUOPT_DETERMINISM_LOG(settings_.log, + "%s[%d type=%d dist=%.16e hash=0x%x]", + k == 0 ? "" : " ", + cut_idx, + static_cast(cut_type_[cut_idx]), + cut_distances_[cut_idx], + candidate_cut_hashes[k]); } - CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, "\n"); + CUOPT_DETERMINISM_LOG(settings_.log, "\n"); } const i_t max_cuts = 2000; @@ -276,23 +276,22 @@ void cut_pool_t::score_cuts(std::vector& x_relax) compute_stored_cut_hash(cut_storage_, rhs_storage_, cut_type_, cut_idx)); } const uint32_t selected_cut_hash = compute_ordered_hash_list_hash(selected_cut_hashes); - CUOPT_DETERMINISM_LOG_PRINTF( - settings_.log, - "Deterministic cut selection summary: selected=%zu order_hash=0x%x top=", - best_cuts_.size(), - selected_cut_hash); + CUOPT_DETERMINISM_LOG(settings_.log, + "Deterministic cut selection summary: selected=%zu order_hash=0x%x top=", + best_cuts_.size(), + selected_cut_hash); const size_t max_selected_cuts = std::min((size_t)8, best_cuts_.size()); for (size_t k = 0; k < max_selected_cuts; ++k) { const i_t cut_idx = best_cuts_[k]; - CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, - "%s[%d type=%d dist=%.16e hash=0x%x]", - k == 0 ? "" : " ", - cut_idx, - static_cast(cut_type_[cut_idx]), - cut_distances_[cut_idx], - selected_cut_hashes[k]); + CUOPT_DETERMINISM_LOG(settings_.log, + "%s[%d type=%d dist=%.16e hash=0x%x]", + k == 0 ? "" : " ", + cut_idx, + static_cast(cut_type_[cut_idx]), + cut_distances_[cut_idx], + selected_cut_hashes[k]); } - CUOPT_DETERMINISM_LOG_PRINTF(settings_.log, "\n"); + CUOPT_DETERMINISM_LOG(settings_.log, "\n"); } } @@ -1058,7 +1057,7 @@ void cut_generation_t::generate_mir_cuts( work_estimate += 10 * std::log2(10); const uint32_t potential_row_hash = compute_index_order_hash(potential_rows); - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings.log, "Deterministic cut potential row ranking: pivot_var=%d rows=%zu " "order_hash=0x%x top=", @@ -1068,10 +1067,10 @@ void cut_generation_t::generate_mir_cuts( const size_t max_logged_rows = std::min((size_t)8, potential_rows.size()); for (size_t k = 0; k < max_logged_rows; ++k) { const i_t row = potential_rows[k]; - CUOPT_DETERMINISM_LOG_PRINTF( + CUOPT_DETERMINISM_LOG( settings.log, "%s[%d score=%.16e]", k == 0 ? "" : " ", row, score[row]); } - CUOPT_DETERMINISM_LOG_PRINTF(settings.log, "\n"); + CUOPT_DETERMINISM_LOG(settings.log, "\n"); const i_t pivot_row = potential_rows[0]; diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index 16f61b0e00..597979fc75 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -339,7 +339,7 @@ void population_t::run_solution_callbacks( cuopt_assert(std::isfinite(work_timestamp), "Deterministic heuristic work timestamp must be finite"); context.branch_and_bound_ptr->queue_external_solution_deterministic( - sol.get_host_assignment(), sol.get_objective(), work_timestamp, callback_origin); + sol.get_host_assignment(), sol.get_user_objective(), work_timestamp, callback_origin); // In deterministic mode, B&B replay is the single owner of GET_SOLUTION callback ordering. best_feasible_objective = sol.get_objective(); } else { diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index 8097b784d9..ffa9e948f1 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -191,9 +191,11 @@ void local_search_t::start_cpufj_deterministic( // Set up callback to send solutions to B&B with work unit timestamps deterministic_cpu_fj.fj_cpu->improvement_callback = - [&bb](f_t obj, const std::vector& h_vec, double work_units) { + [&bb, problem_ptr = context.problem_ptr]( + f_t obj, const std::vector& h_vec, double work_units) { + f_t user_obj = problem_ptr->get_user_obj_from_solver_obj(obj); bb.queue_external_solution_deterministic( - h_vec, obj, work_units, cuopt::internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP); + h_vec, user_obj, work_units, cuopt::internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP); }; deterministic_cpu_fj.start_cpu_solver(); diff --git a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu index aa421660a9..f42ee148ca 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu @@ -523,7 +523,7 @@ bool bounds_repair_t::repair_problem(problem_t& problem, best_violation, curr_violation); if (timer.deterministic) { - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "Repair iter entry: iter=%d pb_hash=0x%x bounds_hash=0x%x violated_hash=0x%x " "n_violated=%d best_violation=%.6f curr_violation=%.6f timer_rem=%.6f total_work=%.6f", repair_iterations, @@ -548,7 +548,7 @@ bool bounds_repair_t::repair_problem(problem_t& problem, if (timer.deterministic) { shift_work = estimate_bounds_repair_shift_work(problem, curr_cstr, n_candidates, is_cycle); record_estimated_work(timer, &total_estimated_work, shift_work); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "Repair iter shift: iter=%d curr_cstr=%d cycle=%d n_candidates=%d cand_var_hash=0x%x " "cand_shift_hash=0x%x shift_work=%.6f timer_rem=%.6f total_work=%.6f", repair_iterations, @@ -581,7 +581,7 @@ bool bounds_repair_t::repair_problem(problem_t& problem, if (timer.deterministic) { damage_work = estimate_bounds_repair_damage_work(problem, n_candidates); record_estimated_work(timer, &total_estimated_work, damage_work); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "Repair iter damage: iter=%d curr_cstr=%d cand_cdelta_hash=0x%x cand_damage_hash=0x%x " "damage_work=%.6f timer_rem=%.6f total_work=%.6f", repair_iterations, @@ -617,7 +617,7 @@ bool bounds_repair_t::repair_problem(problem_t& problem, best_move_idx = get_random_idx(n_of_eligible_candidates); } if (timer.deterministic) { - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "Repair iter choice: iter=%d curr_cstr=%d best_cstr_delta=%d best_damage=%.6f " "choose_random=%d random_draw=%.6f eligible=%d best_move_idx=%d move_var=%d " "move_shift=%.6f move_cdelta=%d move_damage=%.6f", @@ -650,7 +650,7 @@ bool bounds_repair_t::repair_problem(problem_t& problem, refresh_work = bounds_repair_move_base_work + estimate_bounds_repair_violation_refresh_work(problem, improved_violation); record_estimated_work(timer, &total_estimated_work, refresh_work); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "Repair iter post: iter=%d pb_hash=0x%x bounds_hash=0x%x violated_hash=0x%x " "n_violated=%d curr_violation=%.6f improved=%d refresh_work=%.6f total_work=%.6f " "timer_rem=%.6f", @@ -665,12 +665,13 @@ bool bounds_repair_t::repair_problem(problem_t& problem, refresh_work, total_estimated_work, timer.remaining_time()); - CUOPT_LOG_DEBUG("Repair iter work: cstr=%d candidates=%d cycle=%d improved=%d total=%.6f", - curr_cstr, - n_candidates, - (int)is_cycle, - (int)improved_violation, - total_estimated_work); + CUOPT_DETERMINISM_LOG( + "Repair iter work: cstr=%d candidates=%d cycle=%d improved=%d total=%.6f", + curr_cstr, + n_candidates, + (int)is_cycle, + (int)improved_violation, + total_estimated_work); } if (improved_violation) { diff --git a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu index 4eebf2411b..a3d6355157 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu @@ -941,7 +941,7 @@ bool constraint_prop_t::find_integer( while (set_count < unset_integer_vars.size()) { auto iter_start_time = std::chrono::high_resolution_clock::now(); CUOPT_LOG_TRACE("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); - CUOPT_LOG_DEBUG("unset_integer_vars size %lu", unset_integer_vars.size()); + CUOPT_LOG_TRACE("unset_integer_vars size %lu", unset_integer_vars.size()); const size_t set_count_before = set_count; update_host_assignment(sol); auto after_update_host_assignment = std::chrono::high_resolution_clock::now(); @@ -1000,9 +1000,9 @@ bool constraint_prop_t::find_integer( (int)set_count, detail::compute_hash(make_span(unset_integer_vars), sol.handle_ptr->get_stream()), unset_integer_vars.size()); - bool repair_attempted = false; - bool bounds_repaired = false; - i_t n_fixed_vars = 0; + [[maybe_unused]] bool repair_attempted = false; + bool bounds_repaired = false; + i_t n_fixed_vars = 0; if (!(n_failed_repair_iterations >= max_n_failed_repair_iterations) && rounding_ii && !timeout_happened) { // timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; @@ -1099,7 +1099,7 @@ bool constraint_prop_t::find_integer( std::chrono::duration(after_repair - after_probe).count(); const double iter_total_ms = std::chrono::duration(after_repair - iter_start_time).count(); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "CP iter: set_count=%lu->%lu unset=%d interval=%d n_vars_to_set=%d sort=%d recovery=%d " "rounding_ii=%d probe_ii=%d timeout=%d repair_attempted=%d repair_success=%d " "repair_fixed=%d t_ms(update=%.3f sort=%.3f gen=%.3f probe=%.3f repair=%.3f total=%.3f)", diff --git a/cpp/src/mip_heuristics/presolve/probing_cache.cu b/cpp/src/mip_heuristics/presolve/probing_cache.cu index 90eb1fe8d7..b50bd2a34b 100644 --- a/cpp/src/mip_heuristics/presolve/probing_cache.cu +++ b/cpp/src/mip_heuristics/presolve/probing_cache.cu @@ -857,6 +857,12 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, bound_presolve.settings.iteration_limit = 50; bound_presolve.settings.time_limit = timer.remaining_time(); + // TODO: proper work unit accounting in deterministic mode for the probing cache + if (is_deterministic_mode(bound_presolve.context.settings.determinism_mode)) { + bound_presolve.settings.iteration_limit = 1; + priority_indices.resize(std::min(priority_indices.size(), 2048)); + } + size_t num_threads = bound_presolve.settings.num_threads < 0 ? 0.2 * omp_get_max_threads() : bound_presolve.settings.num_threads; diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 38c25b977c..cca03eca40 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -194,6 +194,7 @@ mip_solution_t run_mip(detail::problem_t& problem, int hidesol = std::getenv("CUOPT_MIP_HIDE_SOLUTION") ? atoi(std::getenv("CUOPT_MIP_HIDE_SOLUTION")) : 0; + hidesol = 0; if (!hidesol) { detail::print_solution(scaled_problem.handle_ptr, sol.get_solution()); } return sol; } diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index fede9d8fb3..bb3ffc2a5b 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -19,6 +19,9 @@ #include #include +#undef CUOPT_DETERMINISM_LOG_INFO +#define CUOPT_DETERMINISM_LOG_INFO(...) CUOPT_LOG_INFO(__VA_ARGS__) + #include #include @@ -388,18 +391,74 @@ solution_t mip_solver_t::run_solver() std::isfinite(branch_and_bound_solution.objective)) { CUOPT_DETERMINISM_LOG_INFO( "Deterministic solver B&B overwrite: bb_status=%d bb_obj=%.16e bb_lower=%.16e " + "bb_x_size=%zu bb_has_incumbent=%d " "bb_hash=0x%x dm_hash=0x%x nodes=%d simplex_iterations=%d", (int)bb_status, branch_and_bound_solution.objective, branch_and_bound_solution.lower_bound, + branch_and_bound_solution.x.size(), + (int)branch_and_bound_solution.has_incumbent, detail::compute_hash(branch_and_bound_solution.x), sol.get_hash(), branch_and_bound_solution.nodes_explored, branch_and_bound_solution.simplex_iterations); solution_t bb_sol(*context.problem_ptr); bb_sol.copy_new_assignment(branch_and_bound_solution.x); - bb_sol.compute_feasibility(); + bool bb_feasible = bb_sol.compute_feasibility(); + f_t max_cstr_vio = bb_sol.compute_max_constraint_violation(); + f_t max_int_vio = bb_sol.compute_max_int_violation(); + f_t max_bnd_vio = bb_sol.compute_max_variable_violation(); + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic B&B overwrite feasibility: feasible=%d obj=%.16e user_obj=%.16e " + "max_cstr_vio=%.6e max_int_vio=%.6e max_bnd_vio=%.6e " + "n_integers=%d n_int_vars=%d n_feas_cstr=%d n_cstr=%d hash=0x%x", + (int)bb_feasible, + bb_sol.get_objective(), + bb_sol.get_user_objective(), + max_cstr_vio, + max_int_vio, + max_bnd_vio, + bb_sol.n_assigned_integers, + context.problem_ptr->n_integer_vars, + bb_sol.n_feasible_constraints.value(bb_sol.handle_ptr->get_stream()), + context.problem_ptr->n_constraints, + bb_sol.get_hash()); + if (!bb_feasible) { + auto stream = context.problem_ptr->handle_ptr->get_stream(); + auto h_clb = cuopt::host_copy(context.problem_ptr->constraint_lower_bounds, stream); + auto h_cub = cuopt::host_copy(context.problem_ptr->constraint_upper_bounds, stream); + auto h_coef = cuopt::host_copy(context.problem_ptr->coefficients, stream); + auto h_vars = cuopt::host_copy(context.problem_ptr->variables, stream); + auto h_offs = cuopt::host_copy(context.problem_ptr->offsets, stream); + const auto& x = branch_and_bound_solution.x; + for (i_t row = 0; row < context.problem_ptr->n_constraints; ++row) { + f_t ax = 0; + for (i_t p = h_offs[row]; p < h_offs[row + 1]; ++p) { + ax += h_coef[p] * x[h_vars[p]]; + } + f_t lo_vio = std::max(0.0, (double)(h_clb[row] - ax)); + f_t hi_vio = std::max(0.0, (double)(ax - h_cub[row])); + if (lo_vio > 1e-6 || hi_vio > 1e-6) { + CUOPT_DETERMINISM_LOG_INFO( + " Constraint %d violated: Ax=%.16e lb=%.16e ub=%.16e lo_vio=%.6e hi_vio=%.6e " + "nnz=%d", + row, + ax, + h_clb[row], + h_cub[row], + lo_vio, + hi_vio, + h_offs[row + 1] - h_offs[row]); + } + } + } sol = std::move(bb_sol); + } else if (is_deterministic_mode(context.settings.determinism_mode)) { + CUOPT_DETERMINISM_LOG_INFO( + "Deterministic B&B overwrite skipped: bb_obj=%.16e bb_obj_finite=%d bb_has_incumbent=%d", + branch_and_bound_solution.objective, + (int)std::isfinite(branch_and_bound_solution.objective), + (int)branch_and_bound_solution.has_incumbent); } context.stats.num_nodes = branch_and_bound_solution.nodes_explored; context.stats.num_simplex_iterations = branch_and_bound_solution.simplex_iterations; diff --git a/cpp/src/utilities/determinism_log.hpp b/cpp/src/utilities/determinism_log.hpp index 03f31f414c..d2e5acae48 100644 --- a/cpp/src/utilities/determinism_log.hpp +++ b/cpp/src/utilities/determinism_log.hpp @@ -16,9 +16,9 @@ */ #pragma once -#ifndef CUOPT_DETERMINISM_LOG_PRINTF -#define CUOPT_DETERMINISM_LOG_PRINTF(logger, ...) \ - do { \ +#ifndef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(logger, ...) \ + do { \ } while (0) #endif From 58c6c4bed6ea6062a891af9f8a340a21d5ad311b Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 11 Mar 2026 09:57:31 -0700 Subject: [PATCH 168/225] switch to hierarchical termination checkers --- .../linear_programming/cuopt/run_mip.cpp | 2 +- .../mip/solver_settings.hpp | 3 + cpp/src/branch_and_bound/branch_and_bound.cpp | 3 + .../dual_simplex/simplex_solver_settings.hpp | 1 + .../diversity/diversity_manager.cu | 13 +- .../diversity/diversity_manager.cuh | 2 +- .../mip_heuristics/diversity/population.cu | 2 +- .../recombiners/bound_prop_recombiner.cuh | 6 +- .../diversity/recombiners/fp_recombiner.cuh | 4 +- .../recombiners/line_segment_recombiner.cuh | 4 +- .../feasibility_jump/feasibility_jump.cu | 4 +- .../feasibility_pump/feasibility_pump.cu | 5 +- .../local_search/local_search.cu | 23 +- .../local_search/rounding/bounds_repair.cu | 3 +- .../local_search/rounding/constraint_prop.cu | 9 +- .../local_search/rounding/lb_bounds_repair.cu | 3 +- .../rounding/lb_constraint_prop.cu | 6 +- cpp/src/mip_heuristics/solve.cu | 5 +- cpp/src/mip_heuristics/solver.cu | 6 +- cpp/src/mip_heuristics/solver.cuh | 7 +- cpp/src/mip_heuristics/solver_context.cuh | 4 + cpp/src/utilities/termination_checker.hpp | 219 ++++++++++++++++++ cpp/src/utilities/work_limit_timer.hpp | 188 +-------------- cpp/tests/mip/diversity_test.cu | 13 +- cpp/tests/mip/load_balancing_test.cu | 3 +- cpp/tests/mip/local_search_test.cu | 15 +- cpp/tests/mip/mip_utils.cuh | 2 +- cpp/tests/mip/multi_probe_test.cu | 3 +- cpp/tests/mip/presolve_test.cu | 7 +- 29 files changed, 319 insertions(+), 246 deletions(-) create mode 100644 cpp/src/utilities/termination_checker.hpp diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index b8b41f8f56..845572d765 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -223,7 +223,7 @@ int run_single_file(std::string file_path, settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; settings.seed = 42; - settings.gpu_heur_work_unit_scale = 0.9; + settings.gpu_heur_work_unit_scale = 0.4; settings.mip_scaling = false; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 5756fcdadc..26caf2ad5f 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -104,6 +104,9 @@ class mip_solver_settings_t { f_t cpufj_work_unit_scale = 1.0; // Scales deterministic GPU heuristic producer work units/timestamps exposed to B&B replay/sync. f_t gpu_heur_work_unit_scale = 1.0; + // Scales deterministic B&B work units (LP iterations) exposed to the shared deterministic + // timeline. + f_t bnb_work_unit_scale = 1.0; std::string log_file; std::string sol_file; diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index debbd0800e..086ac0b26b 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -2296,6 +2296,9 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut work_unit_scheduler_t* saved_scheduler = work_unit_context_.scheduler; if (settings_.deterministic) { work_unit_context_.deterministic = true; + cuopt_assert(settings_.bnb_work_unit_scale > 0.0, "B&B work-unit scale must be positive"); + // TODO: support bnb work unit scale... + // Detach the scheduler during the serial root/cuts/SB phase. // record_work_sync_on_horizon still accumulates global_work_units_elapsed, // but avoids scheduler->on_work_recorded whose OMP directives diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 79ff0331a9..6dad39792c 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -188,6 +188,7 @@ struct simplex_solver_settings_t { f_t cut_min_orthogonality; // minimum orthogonality for cuts i_t mip_batch_pdlp_strong_branching{0}; // 0 if not using batch PDLP for strong branching, 1 if // using batch PDLP for strong branching + f_t bnb_work_unit_scale{1.0}; diving_heuristics_settings_t diving_settings; // Settings for the diving heuristics diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 36370bc945..8ef718e98a 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -56,7 +56,7 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_thandle_ptr->get_stream()), ls(context, lp_optimal_solution), rins(context, *this), - timer(context.gpu_heur_loop, diversity_config.default_time_limit), + timer(0.0, cuopt::termination_checker_t::root_tag_t{}), bound_prop_recombiner(context, context.problem_ptr->n_variables, ls.constraint_prop, @@ -205,12 +205,13 @@ void diversity_manager_t::add_user_given_solutions( } template -bool diversity_manager_t::run_presolve(f_t time_limit, timer_t global_timer) +bool diversity_manager_t::run_presolve(f_t time_limit, + cuopt::termination_checker_t& global_timer) { raft::common::nvtx::range fun_scope("run_presolve"); CUOPT_LOG_INFO("Running presolve!"); CUOPT_LOG_INFO("Problem fingerprint before DM presolve: 0x%x", problem_ptr->get_fingerprint()); - work_limit_timer_t presolve_timer(context.gpu_heur_loop, time_limit); + work_limit_timer_t presolve_timer(context.gpu_heur_loop, time_limit, *context.termination); auto term_crit = ls.constraint_prop.bounds_update.solve(*problem_ptr); if (ls.constraint_prop.bounds_update.infeas_constraints_count > 0) { stats.presolve_time = timer.elapsed_time(); @@ -224,7 +225,8 @@ bool diversity_manager_t::run_presolve(f_t time_limit, timer_t global_ // Run probing cache before trivial presolve to discover variable implications const f_t max_time_on_probing = diversity_config.max_time_on_probing; f_t time_for_probing_cache = std::min(max_time_on_probing, time_limit); - work_limit_timer_t probing_timer(context.gpu_heur_loop, time_for_probing_cache); + work_limit_timer_t probing_timer( + context.gpu_heur_loop, time_for_probing_cache, *context.termination); // this function computes probing cache, finds singletons, substitutions and changes the problem bool problem_is_infeasible = compute_probing_cache(ls.constraint_prop.bounds_update, *problem_ptr, probing_timer); @@ -292,7 +294,8 @@ void diversity_manager_t::generate_quick_feasible_solution() // min 1 second, max 10 seconds const f_t generate_fast_solution_time = std::min(diversity_config.max_fast_sol_time, std::max(1., timer.remaining_time() / 20.)); - work_limit_timer_t sol_timer(context.gpu_heur_loop, generate_fast_solution_time); + work_limit_timer_t sol_timer( + context.gpu_heur_loop, generate_fast_solution_time, *context.termination); // do very short LP run to get somewhere close to the optimal point ls.generate_fast_solution(solution, sol_timer); if (solution.get_feasible()) { diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh index d0a45c4373..7c90ceff0c 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cuh +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cuh @@ -34,7 +34,7 @@ template class diversity_manager_t { public: diversity_manager_t(mip_solver_context_t& context); - bool run_presolve(f_t time_limit, timer_t global_timer); + bool run_presolve(f_t time_limit, cuopt::termination_checker_t& global_timer); solution_t run_solver(); void generate_solution(f_t time_limit, bool random_start = true); void run_fj_alone(solution_t& solution); diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index 597979fc75..53d6e8aa1d 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -101,7 +101,7 @@ population_t::population_t(std::string const& name_, rng(cuopt::seed_generator::get_seed()), early_exit_primal_generation(false), population_hash_map(*problem_ptr), - timer(context.gpu_heur_loop, 0) + timer(0.0, cuopt::termination_checker_t::root_tag_t{}) { best_feasible_objective = std::numeric_limits::max(); context.solution_publication.reset_published_best(); diff --git a/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh index edcfa5c717..3660927d7b 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh @@ -208,7 +208,8 @@ class bound_prop_recombiner_t : public recombiner_t { fixed_problem.get_fingerprint(), variable_map.size()); work_limit_timer_t timer(this->context.gpu_heur_loop, - bp_recombiner_config_t::bounds_prop_time_limit); + bp_recombiner_config_t::bounds_prop_time_limit, + *this->context.termination); rmm::device_uvector old_assignment(offspring.assignment, offspring.handle_ptr->get_stream()); offspring.handle_ptr->sync_stream(); @@ -253,7 +254,8 @@ class bound_prop_recombiner_t : public recombiner_t { } else { CUOPT_LOG_TRACE("BP_DET: Taking INFEASIBLE path (no variable fixing)"); work_limit_timer_t timer(this->context.gpu_heur_loop, - bp_recombiner_config_t::bounds_prop_time_limit); + bp_recombiner_config_t::bounds_prop_time_limit, + *this->context.termination); get_probing_values_for_infeasible( guiding_solution, other_solution, offspring, probing_values, n_vars_from_other); probing_config.probing_values = host_copy(probing_values, offspring.handle_ptr->get_stream()); diff --git a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh index 1fe14e0149..1e8bb1f1c6 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh @@ -127,7 +127,9 @@ class fp_recombiner_t : public recombiner_t { detail::compute_hash(offspring.assignment, offspring.handle_ptr->get_stream()), fixed_problem.get_fingerprint(), fixed_problem.n_integer_vars); - work_limit_timer_t timer(this->context.gpu_heur_loop, fp_recombiner_config_t::fp_time_limit); + work_limit_timer_t timer(this->context.gpu_heur_loop, + fp_recombiner_config_t::fp_time_limit, + *this->context.termination); fp.timer = timer; fp.cycle_queue.reset(offspring); fp.reset(); diff --git a/cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh index 8dba2a2599..50c63405d8 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh @@ -76,8 +76,8 @@ class line_segment_recombiner_t : public recombiner_t { auto& other_solution = a.get_feasible() ? b : a; // copy the solution from A solution_t offspring(guiding_solution); - work_limit_timer_t line_segment_timer{this->context.gpu_heur_loop, - ls_recombiner_config_t::time_limit}; + work_limit_timer_t line_segment_timer{ + this->context.gpu_heur_loop, ls_recombiner_config_t::time_limit, *this->context.termination}; // TODO after we have the conic combination, detect the lambda change // (i.e. the integral variables flip on line segment) i_t n_points_to_search = ls_recombiner_config_t::n_points_to_search; diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu index f6c96b012a..cb53f7879e 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu @@ -1083,7 +1083,7 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) data.incumbent_quality.set_value_async(obj, handle_ptr->get_stream()); - work_limit_timer_t timer(context.gpu_heur_loop, settings.time_limit); + work_limit_timer_t timer(context.gpu_heur_loop, settings.time_limit, *context.termination); i_t steps; bool limit_reached = false; for (steps = 0; steps < std::numeric_limits::max(); steps += iterations_per_graph) { @@ -1308,7 +1308,7 @@ i_t fj_t::solve(solution_t& solution) CUOPT_LOG_DEBUG("FJ: skipping solve due to exhausted deterministic work budget"); return solution.compute_feasibility(); } - work_limit_timer_t timer(context.gpu_heur_loop, settings.time_limit); + work_limit_timer_t timer(context.gpu_heur_loop, settings.time_limit, *context.termination); pb_ptr->check_problem_representation(true); resize_vectors(solution.handle_ptr); diff --git a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu index f13dd3af90..ba0e2d2e56 100644 --- a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu @@ -52,7 +52,7 @@ feasibility_pump_t::feasibility_pump_t( context.problem_ptr->handle_ptr->get_stream()), lp_optimal_solution(lp_optimal_solution_), rng(cuopt::seed_generator::get_seed()), - timer(20.) + timer(20., *context.termination) { } @@ -302,7 +302,8 @@ bool feasibility_pump_t::round(solution_t& solution) } else { bounds_prop_time_limit = std::max((f_t)0.05, bounds_prop_time_limit); } - work_limit_timer_t bounds_prop_timer(context.gpu_heur_loop, bounds_prop_time_limit); + work_limit_timer_t bounds_prop_timer( + context.gpu_heur_loop, bounds_prop_time_limit, *context.termination); const f_t lp_run_time_after_feasible = 0.; bool old_var = constraint_prop.round_all_vars; f_t old_time = constraint_prop.max_time_for_bounds_prop; diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index ffa9e948f1..68a7257fd7 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -225,7 +225,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, if (time_limit == 0.) return solution.get_feasible(); const bool deterministic = is_deterministic_mode(context.settings.determinism_mode); - work_limit_timer_t timer(context.gpu_heur_loop, time_limit); + work_limit_timer_t timer(context.gpu_heur_loop, time_limit, *context.termination); const auto old_n_cstr_weights = in_fj.cstr_weights.size(); const auto expected_n_cstr_weights = static_cast(solution.problem_ptr->n_constraints); // in case this is the first time run, resize @@ -349,8 +349,8 @@ void local_search_t::generate_fast_solution(solution_t& solu while ((context.diversity_manager_ptr == nullptr || !context.diversity_manager_ptr->check_b_b_preemption()) && !timer.check_time_limit()) { - work_limit_timer_t constr_prop_timer = - work_limit_timer_t(context.gpu_heur_loop, std::min(timer.remaining_time(), 2.)); + work_limit_timer_t constr_prop_timer = work_limit_timer_t( + context.gpu_heur_loop, std::min(timer.remaining_time(), 2.), *context.termination); // do constraint prop on lp optimal solution constraint_prop.apply_round(solution, 1., constr_prop_timer); if (solution.compute_feasibility()) { return; } @@ -377,11 +377,10 @@ bool local_search_t::run_local_search(solution_t& solution, if (!solution.get_feasible()) { if (ls_config.at_least_one_parent_feasible) { fj_settings.time_limit = 0.5; - timer = work_limit_timer_t(context.gpu_heur_loop, fj_settings.time_limit); } else { fj_settings.time_limit = 0.25; - timer = work_limit_timer_t(context.gpu_heur_loop, fj_settings.time_limit); } + timer = work_limit_timer_t(context.gpu_heur_loop, fj_settings.time_limit, *context.termination); } else { fj_settings.time_limit = std::min(1., timer.remaining_time()); } @@ -499,9 +498,9 @@ bool local_search_t::check_fj_on_lp_optimal(solution_t& solu solution.assign_random_within_bounds(perturbation_ratio); } cuopt_func_call(solution.test_variable_bounds(false)); - f_t lp_run_time_after_feasible = std::min(1., timer.remaining_time()); - work_limit_timer_t bounds_prop_timer = - work_limit_timer_t(context.gpu_heur_loop, std::min(timer.remaining_time(), 10.)); + f_t lp_run_time_after_feasible = std::min(1., timer.remaining_time()); + work_limit_timer_t bounds_prop_timer = work_limit_timer_t( + context.gpu_heur_loop, std::min(timer.remaining_time(), 10.), *context.termination); bool is_feasible = constraint_prop.apply_round(solution, lp_run_time_after_feasible, bounds_prop_timer); if (!is_feasible) { @@ -578,7 +577,8 @@ bool local_search_t::run_staged_fp(solution_t& solution, } CUOPT_LOG_DEBUG("Running staged FP from beginning it %d", i); fp.relax_general_integers(solution); - work_limit_timer_t binary_timer(context.gpu_heur_loop, timer.remaining_time() / 3); + work_limit_timer_t binary_timer( + context.gpu_heur_loop, timer.remaining_time() / 3, *context.termination); i_t binary_it_counter = 0; for (; binary_it_counter < 100; ++binary_it_counter) { population_ptr->add_external_solutions_to_population(); @@ -756,7 +756,8 @@ bool local_search_t::run_fp(solution_t& solution, is_feasible ? solution.get_objective() : std::numeric_limits::max(); rmm::device_uvector best_solution(solution.assignment, solution.handle_ptr->get_stream()); problem_t* old_problem_ptr = solution.problem_ptr; - fp.timer = work_limit_timer_t(context.gpu_heur_loop, timer.remaining_time()); + fp.timer = + work_limit_timer_t(context.gpu_heur_loop, timer.remaining_time(), *context.termination); // if it has not been initialized yet, create a new problem and move it to the cut problem if (!problem_with_objective_cut.cutting_plane_added) { problem_with_objective_cut = std::move(problem_t(*old_problem_ptr)); @@ -857,7 +858,7 @@ bool local_search_t::generate_solution(solution_t& solution, { raft::common::nvtx::range fun_scope("generate_solution"); cuopt_assert(population_ptr != nullptr, "Population pointer must not be null"); - work_limit_timer_t timer(context.gpu_heur_loop, time_limit); + work_limit_timer_t timer(context.gpu_heur_loop, time_limit, *context.termination); auto n_vars = solution.problem_ptr->n_variables; auto n_binary_vars = solution.problem_ptr->get_n_binary_variables(); auto n_integer_vars = solution.problem_ptr->n_integer_vars; diff --git a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu index f42ee148ca..d6686bc671 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu @@ -117,7 +117,8 @@ bounds_repair_t::bounds_repair_t(const problem_t& pb, violated_cstr_map(0, pb.handle_ptr->get_stream()), total_vio(pb.handle_ptr->get_stream()), gen(cuopt::seed_generator::get_seed()), - cycle_vector(MAX_CYCLE_SEQUENCE, -1) + cycle_vector(MAX_CYCLE_SEQUENCE, -1), + timer(0.0, cuopt::termination_checker_t::root_tag_t{}) { } diff --git a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu index a3d6355157..bad90fe0bd 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu @@ -41,7 +41,8 @@ constraint_prop_t::constraint_prop_t(mip_solver_context_t& c ub_restore(context.problem_ptr->n_variables, context.problem_ptr->handle_ptr->get_stream()), assignment_restore(context.problem_ptr->n_variables, context.problem_ptr->handle_ptr->get_stream()), - rng(cuopt::seed_generator::get_seed(), 0, 0) + rng(cuopt::seed_generator::get_seed(), 0, 0), + max_timer(0.0, cuopt::termination_checker_t::root_tag_t{}) { } @@ -1006,7 +1007,8 @@ bool constraint_prop_t::find_integer( if (!(n_failed_repair_iterations >= max_n_failed_repair_iterations) && rounding_ii && !timeout_happened) { // timer_t repair_timer{std::min(timer.remaining_time() / 5, timer.elapsed_time() / 3)}; - work_limit_timer_t repair_timer(context.gpu_heur_loop, timer.remaining_time() / 5); + work_limit_timer_t repair_timer( + context.gpu_heur_loop, timer.remaining_time() / 5, *context.termination); save_bounds(sol); auto remaining_unset = make_span(unset_integer_vars, set_count, unset_integer_vars.size()); const auto orig_pb_fingerprint = orig_sol.problem_ptr->get_fingerprint(); @@ -1212,7 +1214,8 @@ bool constraint_prop_t::apply_round( // lp_run_time_after_feasible); // === CONSTRAINT PROP PREDICTOR FEATURES - END === - max_timer = work_limit_timer_t{context.gpu_heur_loop, max_time_for_bounds_prop}; + max_timer = + work_limit_timer_t{context.gpu_heur_loop, max_time_for_bounds_prop, *context.termination}; if (check_brute_force_rounding(sol)) { auto cp_end_time = std::chrono::high_resolution_clock::now(); auto cp_elapsed_ms = diff --git a/cpp/src/mip_heuristics/local_search/rounding/lb_bounds_repair.cu b/cpp/src/mip_heuristics/local_search/rounding/lb_bounds_repair.cu index 9d0943410a..b3d53f43b2 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/lb_bounds_repair.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/lb_bounds_repair.cu @@ -26,7 +26,8 @@ lb_bounds_repair_t::lb_bounds_repair_t(const raft::handle_t* handle_pt violated_cstr_map(0, handle_ptr->get_stream()), total_vio(handle_ptr->get_stream()), gen(cuopt::seed_generator::get_seed()), - cycle_vector(MAX_CYCLE_SEQUENCE, -1) + cycle_vector(MAX_CYCLE_SEQUENCE, -1), + timer(0.0, cuopt::termination_checker_t::root_tag_t{}) { } diff --git a/cpp/src/mip_heuristics/local_search/rounding/lb_constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/lb_constraint_prop.cu index b7e01b1b12..d8e3bcc040 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/lb_constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/lb_constraint_prop.cu @@ -33,7 +33,8 @@ lb_constraint_prop_t::lb_constraint_prop_t(mip_solver_context_thandle_ptr->get_stream()), assignment_restore(context.problem_ptr->n_variables, context.problem_ptr->handle_ptr->get_stream()), - rng(cuopt::seed_generator::get_seed(), 0, 0) + rng(cuopt::seed_generator::get_seed(), 0, 0), + max_timer(0.0, cuopt::termination_checker_t::root_tag_t{}) { } @@ -707,7 +708,8 @@ bool lb_constraint_prop_t::apply_round( // this is second timer that can continue but without recovery mode const f_t max_time_for_bounds_prop = 5.; - max_timer = work_limit_timer_t{context.gpu_heur_loop, max_time_for_bounds_prop}; + max_timer = + work_limit_timer_t{context.gpu_heur_loop, max_time_for_bounds_prop, *context.termination}; if (check_brute_force_rounding(sol)) { return true; } recovery_mode = false; rounding_ii = false; diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index cca03eca40..e2004ded46 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -63,7 +63,7 @@ static void init_handler(const raft::handle_t* handle_ptr) template mip_solution_t run_mip(detail::problem_t& problem, mip_solver_settings_t const& settings, - timer_t& timer) + cuopt::termination_checker_t& timer) { raft::common::nvtx::range fun_scope("run_mip"); auto constexpr const running_mip = true; @@ -260,7 +260,8 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, op_problem.get_handle_ptr()->get_stream()); } - auto timer = timer_t(time_limit); + auto timer = + cuopt::termination_checker_t(time_limit, cuopt::termination_checker_t::root_tag_t{}); const bool deterministic_run = detail::is_deterministic_mode(settings.determinism_mode); double presolve_time = 0.0; diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index bb3ffc2a5b..e11aaffdb7 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -46,7 +46,7 @@ template mip_solver_t::mip_solver_t(const problem_t& op_problem, const mip_solver_settings_t& solver_settings, pdlp_initial_scaling_strategy_t& scaling, - timer_t timer) + cuopt::termination_checker_t& timer) : op_problem_(op_problem), solver_settings_(solver_settings), context(op_problem.handle_ptr, @@ -55,6 +55,7 @@ mip_solver_t::mip_solver_t(const problem_t& op_problem, scaling), timer_(timer) { + context.termination = &timer_; init_handler(op_problem.handle_ptr); } @@ -151,7 +152,7 @@ solution_t mip_solver_t::run_solver() if (deterministic_run) cuopt_assert(gpu_heur_work_limit >= 0.0, "Deterministic GPU heuristic work limit must be non-negative"); - dm.timer = work_limit_timer_t(context.gpu_heur_loop, gpu_heur_work_limit); + dm.timer = cuopt::termination_checker_t(context.gpu_heur_loop, gpu_heur_work_limit, timer_); const bool run_presolve = context.settings.presolver != presolver_t::None; f_t time_limit = deterministic_run ? std::numeric_limits::infinity() : timer_.remaining_time(); @@ -276,6 +277,7 @@ solution_t mip_solver_t::run_solver() branch_and_bound_settings.cut_min_orthogonality = context.settings.cut_min_orthogonality; branch_and_bound_settings.mip_batch_pdlp_strong_branching = context.settings.mip_batch_pdlp_strong_branching; + branch_and_bound_settings.bnb_work_unit_scale = solver_settings_.bnb_work_unit_scale; if (context.settings.num_cpu_threads < 0) { branch_and_bound_settings.num_threads = std::max(1, omp_get_max_threads() - 1); diff --git a/cpp/src/mip_heuristics/solver.cuh b/cpp/src/mip_heuristics/solver.cuh index 1b5fe17244..58f2d4ff42 100644 --- a/cpp/src/mip_heuristics/solver.cuh +++ b/cpp/src/mip_heuristics/solver.cuh @@ -10,7 +10,7 @@ #include #include #include -#include +#include #pragma once namespace cuopt::linear_programming::detail { @@ -21,16 +21,15 @@ class mip_solver_t { explicit mip_solver_t(const problem_t& op_problem, const mip_solver_settings_t& solver_settings, pdlp_initial_scaling_strategy_t& scaling, - timer_t timer); + cuopt::termination_checker_t& timer); solution_t run_solver(); solver_stats_t& get_solver_stats() { return context.stats; } mip_solver_context_t context; - // reference to the original problem const problem_t& op_problem_; const mip_solver_settings_t& solver_settings_; - timer_t timer_; + cuopt::termination_checker_t& timer_; }; } // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index 27edfcc05c..ab84a31ca3 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -219,6 +219,10 @@ struct mip_solver_context_t { work_limit_context_t gpu_heur_loop{"GPUHeur"}; solution_publication_t solution_publication; + // Root termination checker — set by mip_solver_t after construction. + // All sub-timers should use this as parent for wall-clock safety. + cuopt::termination_checker_t* termination{nullptr}; + // synchronization every 5 seconds for deterministic mode work_unit_scheduler_t work_unit_scheduler_{5.0}; }; diff --git a/cpp/src/utilities/termination_checker.hpp b/cpp/src/utilities/termination_checker.hpp new file mode 100644 index 0000000000..180b194608 --- /dev/null +++ b/cpp/src/utilities/termination_checker.hpp @@ -0,0 +1,219 @@ +/* clang-format off */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* clang-format on */ + +#pragma once + +#include +#include +#include + +#include + +#include "timer.hpp" +#include "work_limit_context.hpp" + +namespace cuopt { + +/** + * Unified termination checker that subsumes timer_t and work_limit_timer_t. + * + * In non-deterministic mode: checks wall-clock time. + * In deterministic mode: checks work units via work_limit_context_t. + * In BOTH modes: checks parent chain (inheriting root wall-clock limit) and user callbacks. + * + * This is the single timer type used throughout the solver. It replaces work_limit_timer_t. + */ +class termination_checker_t { + public: + struct root_tag_t {}; + + // Root constructor (top-level solver, wall-clock only) + explicit termination_checker_t(double time_limit, root_tag_t) + : deterministic(false), + work_limit(time_limit), + timer(time_limit), + work_context(nullptr), + work_units_at_start(0), + parent_(nullptr) + { + } + + // Non-deterministic constructor with parent + termination_checker_t(double time_limit_, const termination_checker_t& parent) + : deterministic(false), + work_limit(time_limit_), + timer(time_limit_), + work_context(nullptr), + work_units_at_start(0), + parent_(&parent) + { + } + + // Deterministic constructor with parent (inherits parent's termination) + termination_checker_t(work_limit_context_t& context, + double work_limit_, + const termination_checker_t& parent) + : deterministic(context.deterministic), + work_limit(work_limit_), + timer(work_limit_), + work_context(&context), + work_units_at_start(context.deterministic ? context.current_work() : 0), + parent_(&parent) + { + } + + void set_parent(const termination_checker_t* parent) { parent_ = parent; } + const termination_checker_t* get_parent() const { return parent_; } + + void set_termination_callback(bool (*cb)(void*), void* data) + { + termination_callback_ = cb; + termination_callback_data_ = data; + } + + bool check(const char* caller = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) const noexcept + { + if (termination_callback_ != nullptr && termination_callback_(termination_callback_data_)) { + return true; + } + + if (parent_ != nullptr && parent_->check()) { return true; } + + if (deterministic) { + if (!work_context) { return false; } + double elapsed_since_start = work_context->current_work() - work_units_at_start; + bool finished_now = elapsed_since_start >= work_limit; + if (finished_now && !finished) { + finished = true; + double actual_elapsed_time = timer.elapsed_time(); + + if (work_limit > 0 && std::abs(actual_elapsed_time - work_limit) / work_limit > 0.10) { + CUOPT_LOG_ERROR( + "%s:%d: %s(): Work limit timer finished with a large discrepancy: %fs for %fwu " + "(global: %g, start: %g)", + file, + line, + caller, + actual_elapsed_time, + work_limit, + work_context->current_work(), + work_units_at_start); + } + } + return finished; + } else { + return timer.check_time_limit(); + } + } + + // Aliases for compatibility with work_limit_timer_t and timer_t interfaces + bool check_time_limit(const char* caller = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) const noexcept + { + return check(caller, file, line); + } + + bool check_limit(const char* caller = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) const noexcept + { + return check(caller, file, line); + } + + void record_work(double work_units, + const char* caller = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), + int line = __builtin_LINE()) + { + if (deterministic && work_context) { + // debugging info + double parent_elapsed_time = parent_ != nullptr ? parent_->timer.elapsed_time() : 0.0; + double parent_time_limit = parent_ != nullptr ? parent_->timer.get_time_limit() : 0.0; + + CUOPT_LOG_DEBUG("%s:%d: %s(): Recorded %f work units in %fs, total %f (parent time: %g/%g)", + file, + line, + caller, + work_units, + timer.elapsed_time(), + work_context->current_work(), + parent_elapsed_time, + parent_time_limit); + work_context->record_work_sync_on_horizon(work_units); + } + } + + double remaining_units() const noexcept + { + if (deterministic) { + if (!work_context) { return work_limit; } + double elapsed_since_start = work_context->current_work() - work_units_at_start; + return std::max(0.0, work_limit - elapsed_since_start); + } else { + return timer.remaining_time(); + } + } + + double remaining_time() const noexcept { return remaining_units(); } + + double elapsed_time() const noexcept + { + if (deterministic) { + if (!work_context) { return 0.0; } + return work_context->current_work() - work_units_at_start; + } else { + return timer.elapsed_time(); + } + } + + bool check_half_time() const noexcept + { + if (deterministic) { + if (!work_context) { return false; } + double elapsed_since_start = work_context->current_work() - work_units_at_start; + return elapsed_since_start >= work_limit / 2; + } else { + return timer.check_half_time(); + } + } + + double clamp_remaining_time(double desired_time) const noexcept + { + return std::min(desired_time, remaining_time()); + } + + double get_time_limit() const noexcept + { + if (deterministic) { + return work_limit; + } else { + return timer.get_time_limit(); + } + } + + double get_tic_start() const noexcept { return timer.get_tic_start(); } + + timer_t timer; + double work_limit{}; + mutable bool finished{false}; + bool deterministic{false}; + work_limit_context_t* work_context{nullptr}; + double work_units_at_start{0}; + + private: + const termination_checker_t* parent_{nullptr}; + bool (*termination_callback_)(void*) = nullptr; + void* termination_callback_data_ = nullptr; +}; + +// Backward compatibility +using work_limit_timer_t = termination_checker_t; + +} // namespace cuopt diff --git a/cpp/src/utilities/work_limit_timer.hpp b/cpp/src/utilities/work_limit_timer.hpp index b81cb31d8d..801a3e5ee9 100644 --- a/cpp/src/utilities/work_limit_timer.hpp +++ b/cpp/src/utilities/work_limit_timer.hpp @@ -1,189 +1,11 @@ +/* clang-format off */ /* * SPDX-FileCopyrightText: Copyright (c) 2025-2026, 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. */ +/* clang-format on */ #pragma once -#include -#include - -#include - -#include "timer.hpp" -#include "work_limit_context.hpp" - -namespace cuopt { - -// In determinism mode, relies on a work limit accumulator; otherwise rely on a timer -// in non-determinism mode: 1s = 1wu -// In deterministic mode, all timers share a global work units counter (via work_limit_context_t), -// and each timer records the snapshot at construction time to determine when its own limit is -// reached. -class work_limit_timer_t { - public: - work_limit_timer_t() - : deterministic(false), work_limit(0), timer(0), work_context(nullptr), work_units_at_start(0) - { - } - - // Constructor taking work limit context reference (for deterministic mode) - work_limit_timer_t(work_limit_context_t& context, double work_limit_) - : deterministic(context.deterministic), - work_limit(work_limit_), - timer(work_limit_), - work_context(&context), - work_units_at_start(context.deterministic ? context.current_work() : 0) - { - } - - // Compatibility constructor for non-deterministic contexts. - explicit work_limit_timer_t(double time_limit_) - : deterministic(false), - work_limit(time_limit_), - timer(time_limit_), - work_context(nullptr), - work_units_at_start(0) - { - } - - bool check_limit(const char* caller = __builtin_FUNCTION(), - const char* file = __builtin_FILE(), - int line = __builtin_LINE()) const noexcept - { - // TODO check against global timer - - if (deterministic) { - if (!work_context) { return false; } - // Check if global work has exceeded our budget (snapshot + limit) - double elapsed_since_start = work_context->current_work() - work_units_at_start; - bool finished_now = elapsed_since_start >= work_limit; - if (finished_now && !finished) { - finished = true; - double actual_elapsed_time = timer.elapsed_time(); - // 10% timing error - if (work_limit > 0 && abs(actual_elapsed_time - work_limit) / work_limit > 0.10) { - CUOPT_LOG_ERROR( - "%s:%d: %s(): Work limit timer finished with a large discrepancy: %fs for %fwu " - "(global: %f, start: %f)", - file, - line, - caller, - actual_elapsed_time, - work_limit, - work_context->current_work(), - work_units_at_start); - } - } - return finished; - } else { - return timer.check_time_limit(); - } - } - - // in determinism mode, add the work units to the global work limit accumulator - void record_work(double work_units, - const char* caller = __builtin_FUNCTION(), - const char* file = __builtin_FILE(), - int line = __builtin_LINE()) - { - if (deterministic && work_context) { - CUOPT_LOG_DEBUG("%s:%d: %s(): Recorded %f work units in %fs, total %f", - file, - line, - caller, - work_units, - timer.elapsed_time(), - work_context->current_work()); - work_context->record_work_sync_on_horizon(work_units); - } - } - - double remaining_units() const noexcept - { - if (deterministic) { - if (!work_context) { return work_limit; } - double elapsed_since_start = work_context->current_work() - work_units_at_start; - return std::max(0.0, work_limit - elapsed_since_start); - } else { - return timer.remaining_time(); - } - } - - double remaining_time() const noexcept { return remaining_units(); } - - double elapsed_time() const noexcept - { - if (deterministic) { - return work_context->current_work() - work_units_at_start; - } else { - return timer.elapsed_time(); - } - } - - bool check_time_limit(const char* caller = __builtin_FUNCTION(), - const char* file = __builtin_FILE(), - int line = __builtin_LINE()) const noexcept - { - return check_limit(caller, file, line); - } - - bool check_half_time() const noexcept - { - if (deterministic) { - if (!work_context) { return false; } - double elapsed_since_start = work_context->current_work() - work_units_at_start; - return elapsed_since_start >= work_limit / 2; - } else { - return timer.check_half_time(); - } - } - - double clamp_remaining_time(double desired_time) const noexcept - { - return std::min(desired_time, remaining_time()); - } - - double get_time_limit() const noexcept - { - if (deterministic) { - return work_limit; - } else { - return timer.get_time_limit(); - } - } - - void print_debug(std::string msg) const - { - if (deterministic) { - printf("%s work_limit: %f remaining_work: %f elapsed_work: %f \n", - msg.c_str(), - work_limit, - remaining_time(), - elapsed_time()); - } else { - timer.print_debug(msg); - } - } - - timer_t timer; - double work_limit{}; - mutable bool finished{false}; - bool deterministic{false}; - // Pointer to work limit context (shared across all timers in deterministic mode) - work_limit_context_t* work_context{nullptr}; - // Snapshot of global work units when this timer was created - double work_units_at_start{0}; -}; -} // namespace cuopt +// work_limit_timer_t is now an alias for termination_checker_t. +// This header exists for backward compatibility. +#include "termination_checker.hpp" diff --git a/cpp/tests/mip/diversity_test.cu b/cpp/tests/mip/diversity_test.cu index 4fc4a84f97..d41dcc83cb 100644 --- a/cpp/tests/mip/diversity_test.cu +++ b/cpp/tests/mip/diversity_test.cu @@ -105,13 +105,14 @@ static uint32_t test_full_run_determinism(std::string path, settings.work_limit = work_limit; settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; settings.heuristics_only = true; - auto timer = cuopt::timer_t(3000); + auto timer = cuopt::termination_checker_t(3000.0, cuopt::termination_checker_t::root_tag_t{}); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); solver.context.gpu_heur_loop.deterministic = true; - diversity_manager.timer = work_limit_timer_t(solver.context.gpu_heur_loop, settings.work_limit); + diversity_manager.timer = + work_limit_timer_t(solver.context.gpu_heur_loop, settings.work_limit, timer); diversity_manager.run_solver(); std::vector hashes; @@ -163,14 +164,14 @@ static uint32_t test_initial_solution_determinism(std::string path, settings.time_limit = 3000.; settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; settings.heuristics_only = true; - auto timer = cuopt::timer_t(3000); + auto timer = cuopt::termination_checker_t(3000.0, cuopt::termination_checker_t::root_tag_t{}); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); work_limit_context_t work_limit_context("DiversityManager"); work_limit_context.deterministic = true; - diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); + diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000, timer); diversity_manager.diversity_config.initial_solution_only = true; diversity_manager.run_solver(); @@ -223,14 +224,14 @@ static uint32_t test_recombiners_determinism(std::string path, settings.time_limit = 3000.; settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; settings.heuristics_only = true; - auto timer = cuopt::timer_t(3000); + auto timer = cuopt::termination_checker_t(3000.0, cuopt::termination_checker_t::root_tag_t{}); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); detail::diversity_manager_t diversity_manager(solver.context); work_limit_context_t work_limit_context("DiversityManager"); work_limit_context.deterministic = true; - diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000); + diversity_manager.timer = work_limit_timer_t(work_limit_context, 60000, timer); diversity_manager.diversity_config.dry_run = true; diversity_manager.run_solver(); diff --git a/cpp/tests/mip/load_balancing_test.cu b/cpp/tests/mip/load_balancing_test.cu index 413e840b3c..11e518e892 100644 --- a/cpp/tests/mip/load_balancing_test.cu +++ b/cpp/tests/mip/load_balancing_test.cu @@ -141,7 +141,8 @@ void test_multi_probe(std::string path) nullptr, hyper_params, true); - detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); + auto timer = cuopt::termination_checker_t(0.0, cuopt::termination_checker_t::root_tag_t{}); + detail::mip_solver_t solver(problem, default_settings, scaling, timer); detail::load_balanced_problem_t lb_problem(problem); detail::load_balanced_bounds_presolve_t lb_prs(lb_problem, solver.context); diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index 32b5d024e4..18a99a4233 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -110,7 +110,7 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) auto settings = mip_solver_settings_t{}; settings.time_limit = 30.; settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; - auto timer = cuopt::timer_t(30); + auto timer = cuopt::termination_checker_t(30.0, cuopt::termination_checker_t::root_tag_t{}); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); @@ -148,7 +148,7 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) printf("running mode: %d\n", mode); work_limit_context_t work_limit_context("LocalSearch"); - local_search.fp.timer = work_limit_timer_t(work_limit_context, 6000); + local_search.fp.timer = work_limit_timer_t(work_limit_context, 6000, timer); detail::ls_config_t ls_config{}; @@ -158,12 +158,9 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) while (true) { is_feasible = local_search.fp.run_single_fp_descent(solution); printf("fp_loop it %d, is_feasible %d\n", iterations, is_feasible); - // if feasible return true if (is_feasible) { break; - } - // if not feasible, it means it is a cycle - else { + } else { is_feasible = local_search.fp.restart_fp(solution); if (is_feasible) { break; } } @@ -171,12 +168,12 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) } } else if (mode == local_search_mode_t::FJ_LINE_SEGMENT) { local_search.run_fj_line_segment( - solution, work_limit_timer_t(work_limit_context, 6000), ls_config); + solution, work_limit_timer_t(work_limit_context, 6000, timer), ls_config); } else if (mode == local_search_mode_t::FJ_ON_ZERO) { - local_search.run_fj_on_zero(solution, work_limit_timer_t(work_limit_context, 6000)); + local_search.run_fj_on_zero(solution, work_limit_timer_t(work_limit_context, 6000, timer)); } else if (mode == local_search_mode_t::FJ_ANNEALING) { local_search.run_fj_annealing( - solution, work_limit_timer_t(work_limit_context, 6000), ls_config); + solution, work_limit_timer_t(work_limit_context, 6000, timer), ls_config); } std::vector hashes; diff --git a/cpp/tests/mip/mip_utils.cuh b/cpp/tests/mip/mip_utils.cuh index d2bd2832f9..b0af0fecf8 100644 --- a/cpp/tests/mip/mip_utils.cuh +++ b/cpp/tests/mip/mip_utils.cuh @@ -218,7 +218,7 @@ static fj_state_t run_fj(detail::problem_t& problem, auto settings = mip_solver_settings_t{}; settings.time_limit = 30.; settings.determinism_mode = determinism_mode; - auto timer = timer_t(30); + auto timer = cuopt::termination_checker_t(30.0, cuopt::termination_checker_t::root_tag_t{}); detail::mip_solver_t solver(problem, settings, scaling, timer); detail::solution_t solution(*solver.context.problem_ptr); diff --git a/cpp/tests/mip/multi_probe_test.cu b/cpp/tests/mip/multi_probe_test.cu index 64bbed34ab..b50fb2848c 100644 --- a/cpp/tests/mip/multi_probe_test.cu +++ b/cpp/tests/mip/multi_probe_test.cu @@ -161,7 +161,8 @@ uint32_t test_multi_probe(std::string path, unsigned long seed = std::random_dev nullptr, hyper_params, true); - detail::mip_solver_t solver(problem, default_settings, scaling, timer_t(0)); + auto timer = cuopt::termination_checker_t(0.0, cuopt::termination_checker_t::root_tag_t{}); + detail::mip_solver_t solver(problem, default_settings, scaling, timer); detail::bound_presolve_t bnd_prb_0(solver.context); detail::bound_presolve_t bnd_prb_1(solver.context); detail::multi_probe_t multi_probe_prs(solver.context); diff --git a/cpp/tests/mip/presolve_test.cu b/cpp/tests/mip/presolve_test.cu index e437c35b78..2fa1cf9dc4 100644 --- a/cpp/tests/mip/presolve_test.cu +++ b/cpp/tests/mip/presolve_test.cu @@ -118,13 +118,16 @@ uint32_t test_probing_cache_determinism(std::string path, nullptr, hyper_params, true); - detail::mip_solver_t solver(problem, default_settings, scaling, cuopt::timer_t(0)); + auto timer = cuopt::termination_checker_t(0.0, cuopt::termination_checker_t::root_tag_t{}); + detail::mip_solver_t solver(problem, default_settings, scaling, timer); detail::bound_presolve_t bnd_prb(solver.context); work_limit_context_t work_limit_context("ProbingCache"); // rely on the iteration limit compute_probing_cache( - bnd_prb, problem, work_limit_timer_t(work_limit_context, std::numeric_limits::max())); + bnd_prb, + problem, + work_limit_timer_t(work_limit_context, std::numeric_limits::max(), timer)); std::vector, 2>>> cached_values( bnd_prb.probing_cache.probing_cache.begin(), bnd_prb.probing_cache.probing_cache.end()); std::sort(cached_values.begin(), cached_values.end(), [](const auto& a, const auto& b) { From 5e5d285cfd37bb783a992737868ec0f628466d3f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 11 Mar 2026 10:12:40 -0700 Subject: [PATCH 169/225] scaled down --- benchmarks/linear_programming/cuopt/run_mip.cpp | 1 + cpp/src/branch_and_bound/branch_and_bound.cpp | 2 +- cpp/src/mip_heuristics/solver_context.cuh | 4 +--- cpp/src/utilities/work_limit_context.hpp | 17 +++++++++++------ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 845572d765..b95e12267a 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -223,6 +223,7 @@ int run_single_file(std::string file_path, settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; settings.seed = 42; + settings.bnb_work_unit_scale = 0.1; settings.gpu_heur_work_unit_scale = 0.4; settings.mip_scaling = false; cuopt::linear_programming::benchmark_info_t benchmark_info; diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 086ac0b26b..866fca420d 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -2297,7 +2297,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut if (settings_.deterministic) { work_unit_context_.deterministic = true; cuopt_assert(settings_.bnb_work_unit_scale > 0.0, "B&B work-unit scale must be positive"); - // TODO: support bnb work unit scale... + work_unit_context_.work_unit_scale = settings_.bnb_work_unit_scale; // Detach the scheduler during the serial root/cuts/SB phase. // record_work_sync_on_horizon still accumulates global_work_units_elapsed, diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index ab84a31ca3..5fea7be96d 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -196,9 +196,7 @@ struct mip_solver_context_t { cuopt_assert(settings.cpufj_work_unit_scale > 0.0, "CPUFJ work-unit scale must be positive"); cuopt_assert(settings.gpu_heur_work_unit_scale > 0.0, "GPU heuristic work-unit scale must be positive"); - gpu_heur_loop.producer_progress_scale = settings.gpu_heur_work_unit_scale != 1.0 - ? settings.gpu_heur_work_unit_scale - : gpu_heur_loop.producer_progress_scale; + gpu_heur_loop.work_unit_scale = settings.gpu_heur_work_unit_scale; } mip_solver_context_t(const mip_solver_context_t&) = delete; diff --git a/cpp/src/utilities/work_limit_context.hpp b/cpp/src/utilities/work_limit_context.hpp index 51ce67a754..513f7dd176 100644 --- a/cpp/src/utilities/work_limit_context.hpp +++ b/cpp/src/utilities/work_limit_context.hpp @@ -59,6 +59,7 @@ struct work_limit_context_t { std::make_unique>(0.0)}; double producer_progress_scale{ read_work_unit_scale_env_or_default("CUOPT_GPU_HEUR_WORK_UNIT_SCALE", 1.0)}; + double work_unit_scale{1.0}; work_limit_context_t(const std::string& name) : name(name) {} @@ -71,7 +72,8 @@ struct work_limit_context_t { name(other.name), producer_work_units_elapsed(std::make_unique>( other.producer_work_units_elapsed->load(std::memory_order_acquire))), - producer_progress_scale(other.producer_progress_scale) + producer_progress_scale(other.producer_progress_scale), + work_unit_scale(other.work_unit_scale) { } @@ -84,7 +86,8 @@ struct work_limit_context_t { name(std::move(other.name)), producer_work_units_elapsed(std::make_unique>( other.producer_work_units_elapsed->load(std::memory_order_acquire))), - producer_progress_scale(other.producer_progress_scale) + producer_progress_scale(other.producer_progress_scale), + work_unit_scale(other.work_unit_scale) { } @@ -100,6 +103,7 @@ struct work_limit_context_t { producer_work_units_elapsed = std::make_unique>( other.producer_work_units_elapsed->load(std::memory_order_acquire)); producer_progress_scale = other.producer_progress_scale; + work_unit_scale = other.work_unit_scale; return *this; } @@ -115,6 +119,7 @@ struct work_limit_context_t { producer_work_units_elapsed = std::make_unique>( other.producer_work_units_elapsed->load(std::memory_order_acquire)); producer_progress_scale = other.producer_progress_scale; + work_unit_scale = other.work_unit_scale; return *this; } @@ -131,9 +136,8 @@ struct work_limit_context_t { { producer_sync = producer_sync_; producer_work_units_elapsed->store(current_producer_work(), std::memory_order_release); - if (producer_progress_scale != 1.0) { - CUOPT_DETERMINISM_LOG_DEBUG( - "[%s] Using producer work-unit scale %f", name.c_str(), producer_progress_scale); + if (work_unit_scale != 1.0) { + CUOPT_DETERMINISM_LOG_DEBUG("[%s] Using work-unit scale %f", name.c_str(), work_unit_scale); } } @@ -154,7 +158,8 @@ struct work_limit_context_t { if (!deterministic) return; cuopt_assert(std::isfinite(work), "Recorded work must be finite"); cuopt_assert(work >= 0.0, "Recorded work must be non-negative"); - const double total_work = global_work_units_elapsed + work; + const double scaled_work = work * work_unit_scale; + const double total_work = global_work_units_elapsed + scaled_work; set_current_work(total_work, false); if (scheduler) { scheduler->on_work_recorded(*this, total_work); } if (producer_sync != nullptr) { producer_sync->notify_progress(); } From f10d720ab9961cce372e6aa87ed4151f6ebe5cb6 Mon Sep 17 00:00:00 2001 From: anandhkb Date: Wed, 11 Mar 2026 10:13:38 -0700 Subject: [PATCH 170/225] Add dual-simplex callback and reset root LP bound at solve start - Set dual_simplex_objective_callback on lp_settings before root solve to populate root_lp_current_lower_bound_ during dual-simplex iterations - Reset root_lp_current_lower_bound_ to -inf at beginning of solve() with solver_status_, is_running_, etc. to avoid stale bound on reused solver --- cpp/src/branch_and_bound/branch_and_bound.cpp | 882 +++++++++--------- 1 file changed, 428 insertions(+), 454 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 08ca97ba1a..6baa14bc6a 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -235,15 +235,6 @@ inline char feasible_solution_symbol(search_strategy_t strategy) } #endif -// RAII guard: sets solving_root_relaxation_ to false on destruction. -struct solving_root_relaxation_guard_t { - omp_atomic_t* flag_; - explicit solving_root_relaxation_guard_t(omp_atomic_t& flag) : flag_(&flag) {} - ~solving_root_relaxation_guard_t() { flag_->store(false); } - solving_root_relaxation_guard_t(const solving_root_relaxation_guard_t&) = delete; - solving_root_relaxation_guard_t& operator=(const solving_root_relaxation_guard_t&) = delete; -}; - } // namespace template @@ -292,8 +283,8 @@ branch_and_bound_t::branch_and_bound_t( #endif upper_bound_ = inf; - root_lp_current_lower_bound_ = -inf; root_objective_ = std::numeric_limits::quiet_NaN(); + root_lp_current_lower_bound_ = -inf; } template @@ -316,17 +307,11 @@ f_t branch_and_bound_t::get_lower_bound() template void branch_and_bound_t::report_heuristic(f_t obj) { - const f_t user_obj = compute_user_objective(original_lp_, obj); - f_t user_lower; - if (solving_root_relaxation_.load()) { - user_lower = root_lp_current_lower_bound_.load(); - } else { - const f_t lower = get_lower_bound(); - user_lower = std::isfinite(lower) ? compute_user_objective(original_lp_, lower) : -inf; - } - if (is_running_) { + f_t user_obj = compute_user_objective(original_lp_, obj); + f_t user_lower = compute_user_objective(original_lp_, get_lower_bound()); std::string user_gap = user_mip_gap(user_obj, user_lower); + settings_.log.printf( "H %+13.6e %+10.6e %s %9.2f\n", user_obj, @@ -335,16 +320,17 @@ void branch_and_bound_t::report_heuristic(f_t obj) toc(exploration_stats_.start_time)); } else { if (solving_root_relaxation_.load()) { - std::string gap_str = - std::isfinite(user_lower) ? user_mip_gap(user_obj, user_lower) : " - "; + f_t user_obj = compute_user_objective(original_lp_, obj); + f_t user_lower = root_lp_current_lower_bound_.load(); + std::string user_gap = user_mip_gap(user_obj, user_lower); settings_.log.printf( "New solution from primal heuristics. Objective %+.6e. Gap %s. Time %.2f\n", user_obj, - gap_str.c_str(), + user_gap.c_str(), toc(exploration_stats_.start_time)); } else { settings_.log.printf("New solution from primal heuristics. Objective %+.6e. Time %.2f\n", - user_obj, + compute_user_objective(original_lp_, obj), toc(exploration_stats_.start_time)); } } @@ -1969,10 +1955,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut log.log_prefix = settings_.log.log_prefix; solver_status_ = mip_status_t::UNSET; is_running_ = false; - solving_root_relaxation_ = false; - lower_bound_ceiling_ = inf; root_lp_current_lower_bound_ = -inf; - root_objective_ = std::numeric_limits::quiet_NaN(); exploration_stats_.nodes_unexplored = 0; exploration_stats_.nodes_explored = 0; original_lp_.A.to_compressed_row(Arow_); @@ -2009,492 +1992,483 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut std::vector nonbasic_list; basis_update_mpf_t basis_update(original_lp_.num_rows, settings_.refactor_frequency); lp_status_t root_status; - - { - solving_root_relaxation_ = true; - solving_root_relaxation_guard_t root_guard(solving_root_relaxation_); - - if (!enable_concurrent_lp_root_solve()) { - // RINS/SUBMIP path - settings_.log.printf("\nSolving LP root relaxation with dual simplex\n"); - root_status = solve_linear_program_with_advanced_basis(original_lp_, - exploration_stats_.start_time, - lp_settings, - root_relax_soln_, - basis_update, - basic_list, - nonbasic_list, - root_vstatus_, - edge_norms_); - } else { - settings_.log.printf("\nSolving LP root relaxation in concurrent mode\n"); - root_status = solve_root_relaxation(lp_settings, - root_relax_soln_, - root_vstatus_, - basis_update, - basic_list, - nonbasic_list, - edge_norms_); - } - exploration_stats_.total_lp_iters = root_relax_soln_.iterations; - exploration_stats_.total_lp_solve_time = toc(exploration_stats_.start_time); - - if (root_status == lp_status_t::INFEASIBLE) { - settings_.log.printf("MIP Infeasible\n"); - // FIXME: rarely dual simplex detects infeasible whereas it is feasible. - // to add a small safety net, check if there is a primal solution already. - // Uncomment this if the issue with cost266-UUE is resolved - // if (settings.heuristic_preemption_callback != nullptr) { - // settings.heuristic_preemption_callback(); - // } - return mip_status_t::INFEASIBLE; - } - if (root_status == lp_status_t::UNBOUNDED) { - settings_.log.printf("MIP Unbounded\n"); - if (settings_.heuristic_preemption_callback != nullptr) { - settings_.heuristic_preemption_callback(); - } - return mip_status_t::UNBOUNDED; - } - if (root_status == lp_status_t::TIME_LIMIT) { - solver_status_ = mip_status_t::TIME_LIMIT; - set_final_solution(solution, -inf); - return solver_status_; + solving_root_relaxation_ = true; + + if (!enable_concurrent_lp_root_solve()) { + // RINS/SUBMIP path + settings_.log.printf("\nSolving LP root relaxation with dual simplex\n"); + root_status = solve_linear_program_with_advanced_basis(original_lp_, + exploration_stats_.start_time, + lp_settings, + root_relax_soln_, + basis_update, + basic_list, + nonbasic_list, + root_vstatus_, + edge_norms_); + } else { + settings_.log.printf("\nSolving LP root relaxation in concurrent mode\n"); + root_status = solve_root_relaxation(lp_settings, + root_relax_soln_, + root_vstatus_, + basis_update, + basic_list, + nonbasic_list, + edge_norms_); + } + solving_root_relaxation_ = false; + exploration_stats_.total_lp_iters = root_relax_soln_.iterations; + exploration_stats_.total_lp_solve_time = toc(exploration_stats_.start_time); + + if (root_status == lp_status_t::INFEASIBLE) { + settings_.log.printf("MIP Infeasible\n"); + // FIXME: rarely dual simplex detects infeasible whereas it is feasible. + // to add a small safety net, check if there is a primal solution already. + // Uncomment this if the issue with cost266-UUE is resolved + // if (settings.heuristic_preemption_callback != nullptr) { + // settings.heuristic_preemption_callback(); + // } + return mip_status_t::INFEASIBLE; + } + if (root_status == lp_status_t::UNBOUNDED) { + settings_.log.printf("MIP Unbounded\n"); + if (settings_.heuristic_preemption_callback != nullptr) { + settings_.heuristic_preemption_callback(); } + return mip_status_t::UNBOUNDED; + } + if (root_status == lp_status_t::TIME_LIMIT) { + solver_status_ = mip_status_t::TIME_LIMIT; + set_final_solution(solution, -inf); + return solver_status_; + } - if (root_status == lp_status_t::WORK_LIMIT) { - solver_status_ = mip_status_t::WORK_LIMIT; - set_final_solution(solution, -inf); - return solver_status_; - } + if (root_status == lp_status_t::WORK_LIMIT) { + solver_status_ = mip_status_t::WORK_LIMIT; + set_final_solution(solution, -inf); + return solver_status_; + } - if (root_status == lp_status_t::NUMERICAL_ISSUES) { - solver_status_ = mip_status_t::NUMERICAL; - set_final_solution(solution, -inf); - return solver_status_; - } + if (root_status == lp_status_t::NUMERICAL_ISSUES) { + solver_status_ = mip_status_t::NUMERICAL; + set_final_solution(solution, -inf); + return solver_status_; + } - assert(root_vstatus_.size() == original_lp_.num_cols); - set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_); + assert(root_vstatus_.size() == original_lp_.num_cols); + set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_); - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); - if (settings_.set_simplex_solution_callback != nullptr) { - std::vector original_x; - uncrush_primal_solution(original_problem_, original_lp_, root_relax_soln_.x, original_x); - std::vector original_dual; - std::vector original_z; - uncrush_dual_solution(original_problem_, - original_lp_, - root_relax_soln_.y, - root_relax_soln_.z, - original_dual, - original_z); - settings_.set_simplex_solution_callback( - original_x, original_dual, compute_user_objective(original_lp_, root_objective_)); - } + if (settings_.set_simplex_solution_callback != nullptr) { + std::vector original_x; + uncrush_primal_solution(original_problem_, original_lp_, root_relax_soln_.x, original_x); + std::vector original_dual; + std::vector original_z; + uncrush_dual_solution(original_problem_, + original_lp_, + root_relax_soln_.y, + root_relax_soln_.z, + original_dual, + original_z); + settings_.set_simplex_solution_callback( + original_x, original_dual, compute_user_objective(original_lp_, root_objective_)); + } - std::vector fractional; - i_t num_fractional = - fractional_variables(settings_, root_relax_soln_.x, var_types_, fractional); + std::vector fractional; + i_t num_fractional = fractional_variables(settings_, root_relax_soln_.x, var_types_, fractional); - cut_info_t cut_info; + cut_info_t cut_info; - if (num_fractional == 0) { - set_solution_at_root(solution, cut_info); - return mip_status_t::OPTIMAL; - } + if (num_fractional == 0) { + set_solution_at_root(solution, cut_info); + return mip_status_t::OPTIMAL; + } - is_running_ = true; - lower_bound_ceiling_ = inf; + is_running_ = true; + lower_bound_ceiling_ = inf; - if (num_fractional != 0 && settings_.max_cut_passes > 0) { - settings_.log.printf( - " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node | " - " " - "Gap " - "| Time |\n"); - } + if (num_fractional != 0 && settings_.max_cut_passes > 0) { + settings_.log.printf( + " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node | " + "Gap " + "| Time |\n"); + } - cut_pool_t cut_pool(original_lp_.num_cols, settings_); - cut_generation_t cut_generation( - cut_pool, original_lp_, settings_, Arow_, new_slacks_, var_types_); + cut_pool_t cut_pool(original_lp_.num_cols, settings_); + cut_generation_t cut_generation( + cut_pool, original_lp_, settings_, Arow_, new_slacks_, var_types_); - std::vector saved_solution; + std::vector saved_solution; #ifdef CHECK_CUTS_AGAINST_SAVED_SOLUTION - read_saved_solution_for_cut_verification(original_lp_, settings_, saved_solution); + read_saved_solution_for_cut_verification(original_lp_, settings_, saved_solution); #endif - f_t last_upper_bound = std::numeric_limits::infinity(); - f_t last_objective = root_objective_; - f_t root_relax_objective = root_objective_; + f_t last_upper_bound = std::numeric_limits::infinity(); + f_t last_objective = root_objective_; + f_t root_relax_objective = root_objective_; - i_t cut_pool_size = 0; - for (i_t cut_pass = 0; cut_pass < settings_.max_cut_passes; cut_pass++) { - if (num_fractional == 0) { - set_solution_at_root(solution, cut_info); - return mip_status_t::OPTIMAL; - } else { + i_t cut_pool_size = 0; + for (i_t cut_pass = 0; cut_pass < settings_.max_cut_passes; cut_pass++) { + if (num_fractional == 0) { + set_solution_at_root(solution, cut_info); + return mip_status_t::OPTIMAL; + } else { #ifdef PRINT_FRACTIONAL_INFO - settings_.log.printf( - "Found %d fractional variables on cut pass %d\n", num_fractional, cut_pass); - for (i_t j : fractional) { - settings_.log.printf("Fractional variable %d lower %e value %e upper %e\n", - j, - original_lp_.lower[j], - root_relax_soln_.x[j], - original_lp_.upper[j]); - } + settings_.log.printf( + "Found %d fractional variables on cut pass %d\n", num_fractional, cut_pass); + for (i_t j : fractional) { + settings_.log.printf("Fractional variable %d lower %e value %e upper %e\n", + j, + original_lp_.lower[j], + root_relax_soln_.x[j], + original_lp_.upper[j]); + } #endif - // Generate cuts and add them to the cut pool - f_t cut_start_time = tic(); - cut_generation.generate_cuts(original_lp_, - settings_, - Arow_, - new_slacks_, - var_types_, - basis_update, - root_relax_soln_.x, - basic_list, - nonbasic_list); - f_t cut_generation_time = toc(cut_start_time); - if (cut_generation_time > 1.0) { - settings_.log.debug("Cut generation time %.2f seconds\n", cut_generation_time); - } - // Score the cuts - f_t score_start_time = tic(); - cut_pool.score_cuts(root_relax_soln_.x); - f_t score_time = toc(score_start_time); - if (score_time > 1.0) { - settings_.log.debug("Cut scoring time %.2f seconds\n", score_time); - } - // Get the best cuts from the cut pool - csr_matrix_t cuts_to_add(0, original_lp_.num_cols, 0); - std::vector cut_rhs; - std::vector cut_types; - i_t num_cuts = cut_pool.get_best_cuts(cuts_to_add, cut_rhs, cut_types); - if (num_cuts == 0) { break; } - cut_info.record_cut_types(cut_types); + // Generate cuts and add them to the cut pool + f_t cut_start_time = tic(); + cut_generation.generate_cuts(original_lp_, + settings_, + Arow_, + new_slacks_, + var_types_, + basis_update, + root_relax_soln_.x, + basic_list, + nonbasic_list); + f_t cut_generation_time = toc(cut_start_time); + if (cut_generation_time > 1.0) { + settings_.log.debug("Cut generation time %.2f seconds\n", cut_generation_time); + } + // Score the cuts + f_t score_start_time = tic(); + cut_pool.score_cuts(root_relax_soln_.x); + f_t score_time = toc(score_start_time); + if (score_time > 1.0) { settings_.log.debug("Cut scoring time %.2f seconds\n", score_time); } + // Get the best cuts from the cut pool + csr_matrix_t cuts_to_add(0, original_lp_.num_cols, 0); + std::vector cut_rhs; + std::vector cut_types; + i_t num_cuts = cut_pool.get_best_cuts(cuts_to_add, cut_rhs, cut_types); + if (num_cuts == 0) { break; } + cut_info.record_cut_types(cut_types); #ifdef PRINT_CUT_POOL_TYPES - cut_pool.print_cutpool_types(); - print_cut_types("In LP ", cut_types, settings_); - printf("Cut pool size: %d\n", cut_pool.pool_size()); + cut_pool.print_cutpool_types(); + print_cut_types("In LP ", cut_types, settings_); + printf("Cut pool size: %d\n", cut_pool.pool_size()); #endif #ifdef CHECK_CUT_MATRIX - if (cuts_to_add.check_matrix() != 0) { - settings_.log.printf("Bad cuts matrix\n"); - for (i_t i = 0; i < static_cast(cut_types.size()); ++i) { - settings_.log.printf("row %d cut type %d\n", i, cut_types[i]); - } - return mip_status_t::NUMERICAL; + if (cuts_to_add.check_matrix() != 0) { + settings_.log.printf("Bad cuts matrix\n"); + for (i_t i = 0; i < static_cast(cut_types.size()); ++i) { + settings_.log.printf("row %d cut type %d\n", i, cut_types[i]); } + return mip_status_t::NUMERICAL; + } #endif - // Check against saved solution + // Check against saved solution #ifdef CHECK_CUTS_AGAINST_SAVED_SOLUTION - verify_cuts_against_saved_solution(cuts_to_add, cut_rhs, saved_solution); + verify_cuts_against_saved_solution(cuts_to_add, cut_rhs, saved_solution); #endif - cut_pool_size = cut_pool.pool_size(); + cut_pool_size = cut_pool.pool_size(); + + // Resolve the LP with the new cuts + settings_.log.debug( + "Solving LP with %d cuts (%d cut nonzeros). Cuts in pool %d. Total constraints %d\n", + num_cuts, + cuts_to_add.row_start[cuts_to_add.m], + cut_pool.pool_size(), + cuts_to_add.m + original_lp_.num_rows); + lp_settings.log.log = false; + + f_t add_cuts_start_time = tic(); + mutex_original_lp_.lock(); + i_t add_cuts_status = add_cuts(settings_, + cuts_to_add, + cut_rhs, + original_lp_, + new_slacks_, + root_relax_soln_, + basis_update, + basic_list, + nonbasic_list, + root_vstatus_, + edge_norms_); + var_types_.resize(original_lp_.num_cols, variable_type_t::CONTINUOUS); + mutex_original_lp_.unlock(); + f_t add_cuts_time = toc(add_cuts_start_time); + if (add_cuts_time > 1.0) { + settings_.log.debug("Add cuts time %.2f seconds\n", add_cuts_time); + } + if (add_cuts_status != 0) { + settings_.log.printf("Failed to add cuts\n"); + return mip_status_t::NUMERICAL; + } - // Resolve the LP with the new cuts - settings_.log.debug( - "Solving LP with %d cuts (%d cut nonzeros). Cuts in pool %d. Total constraints %d\n", - num_cuts, - cuts_to_add.row_start[cuts_to_add.m], - cut_pool.pool_size(), - cuts_to_add.m + original_lp_.num_rows); - lp_settings.log.log = false; - - f_t add_cuts_start_time = tic(); + if (settings_.reduced_cost_strengthening >= 1 && upper_bound_.load() < last_upper_bound) { + mutex_upper_.lock(); + last_upper_bound = upper_bound_.load(); + std::vector lower_bounds; + std::vector upper_bounds; + find_reduced_cost_fixings(upper_bound_.load(), lower_bounds, upper_bounds); + mutex_upper_.unlock(); mutex_original_lp_.lock(); - i_t add_cuts_status = add_cuts(settings_, - cuts_to_add, - cut_rhs, - original_lp_, - new_slacks_, - root_relax_soln_, - basis_update, - basic_list, - nonbasic_list, - root_vstatus_, - edge_norms_); - var_types_.resize(original_lp_.num_cols, variable_type_t::CONTINUOUS); + original_lp_.lower = lower_bounds; + original_lp_.upper = upper_bounds; mutex_original_lp_.unlock(); - f_t add_cuts_time = toc(add_cuts_start_time); - if (add_cuts_time > 1.0) { - settings_.log.debug("Add cuts time %.2f seconds\n", add_cuts_time); - } - if (add_cuts_status != 0) { - settings_.log.printf("Failed to add cuts\n"); - return mip_status_t::NUMERICAL; - } - - if (settings_.reduced_cost_strengthening >= 1 && upper_bound_.load() < last_upper_bound) { - mutex_upper_.lock(); - last_upper_bound = upper_bound_.load(); - std::vector lower_bounds; - std::vector upper_bounds; - find_reduced_cost_fixings(upper_bound_.load(), lower_bounds, upper_bounds); - mutex_upper_.unlock(); - mutex_original_lp_.lock(); - original_lp_.lower = lower_bounds; - original_lp_.upper = upper_bounds; - mutex_original_lp_.unlock(); - } + } - // Try to do bound strengthening - std::vector bounds_changed(original_lp_.num_cols, true); - std::vector row_sense; + // Try to do bound strengthening + std::vector bounds_changed(original_lp_.num_cols, true); + std::vector row_sense; #ifdef CHECK_MATRICES - settings_.log.printf("Before A check\n"); - original_lp_.A.check_matrix(); + settings_.log.printf("Before A check\n"); + original_lp_.A.check_matrix(); #endif - original_lp_.A.to_compressed_row(Arow_); - - f_t node_presolve_start_time = tic(); - bounds_strengthening_t node_presolve(original_lp_, Arow_, row_sense, var_types_); - std::vector new_lower = original_lp_.lower; - std::vector new_upper = original_lp_.upper; - bool feasible = - node_presolve.bounds_strengthening(settings_, bounds_changed, new_lower, new_upper); - mutex_original_lp_.lock(); - original_lp_.lower = new_lower; - original_lp_.upper = new_upper; - mutex_original_lp_.unlock(); - f_t node_presolve_time = toc(node_presolve_start_time); - if (node_presolve_time > 1.0) { - settings_.log.debug("Node presolve time %.2f seconds\n", node_presolve_time); - } - if (!feasible) { - settings_.log.printf("Bound strengthening detected infeasibility\n"); - return mip_status_t::INFEASIBLE; - } + original_lp_.A.to_compressed_row(Arow_); + + f_t node_presolve_start_time = tic(); + bounds_strengthening_t node_presolve(original_lp_, Arow_, row_sense, var_types_); + std::vector new_lower = original_lp_.lower; + std::vector new_upper = original_lp_.upper; + bool feasible = + node_presolve.bounds_strengthening(settings_, bounds_changed, new_lower, new_upper); + mutex_original_lp_.lock(); + original_lp_.lower = new_lower; + original_lp_.upper = new_upper; + mutex_original_lp_.unlock(); + f_t node_presolve_time = toc(node_presolve_start_time); + if (node_presolve_time > 1.0) { + settings_.log.debug("Node presolve time %.2f seconds\n", node_presolve_time); + } + if (!feasible) { + settings_.log.printf("Bound strengthening detected infeasibility\n"); + return mip_status_t::INFEASIBLE; + } - i_t iter = 0; - bool initialize_basis = false; - lp_settings.concurrent_halt = NULL; - f_t dual_phase2_start_time = tic(); - dual::status_t cut_status = dual_phase2_with_advanced_basis(2, - 0, - initialize_basis, - exploration_stats_.start_time, - original_lp_, - lp_settings, - root_vstatus_, - basis_update, - basic_list, - nonbasic_list, - root_relax_soln_, - iter, - edge_norms_); - exploration_stats_.total_lp_iters += iter; - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); - f_t dual_phase2_time = toc(dual_phase2_start_time); - if (dual_phase2_time > 1.0) { - settings_.log.debug("Dual phase2 time %.2f seconds\n", dual_phase2_time); - } - if (cut_status == dual::status_t::TIME_LIMIT) { - solver_status_ = mip_status_t::TIME_LIMIT; - set_final_solution(solution, root_objective_); - return solver_status_; - } + i_t iter = 0; + bool initialize_basis = false; + lp_settings.concurrent_halt = NULL; + f_t dual_phase2_start_time = tic(); + dual::status_t cut_status = dual_phase2_with_advanced_basis(2, + 0, + initialize_basis, + exploration_stats_.start_time, + original_lp_, + lp_settings, + root_vstatus_, + basis_update, + basic_list, + nonbasic_list, + root_relax_soln_, + iter, + edge_norms_); + exploration_stats_.total_lp_iters += iter; + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + f_t dual_phase2_time = toc(dual_phase2_start_time); + if (dual_phase2_time > 1.0) { + settings_.log.debug("Dual phase2 time %.2f seconds\n", dual_phase2_time); + } + if (cut_status == dual::status_t::TIME_LIMIT) { + solver_status_ = mip_status_t::TIME_LIMIT; + set_final_solution(solution, root_objective_); + return solver_status_; + } - if (cut_status != dual::status_t::OPTIMAL) { - settings_.log.printf("Numerical issue at root node. Resolving from scratch\n"); - lp_status_t scratch_status = - solve_linear_program_with_advanced_basis(original_lp_, - exploration_stats_.start_time, - lp_settings, - root_relax_soln_, - basis_update, - basic_list, - nonbasic_list, - root_vstatus_, - edge_norms_); - if (scratch_status == lp_status_t::OPTIMAL) { - // We recovered - cut_status = convert_lp_status_to_dual_status(scratch_status); - exploration_stats_.total_lp_iters += root_relax_soln_.iterations; - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); - } else { - settings_.log.printf("Cut status %s\n", dual::status_to_string(cut_status).c_str()); - return mip_status_t::NUMERICAL; - } + if (cut_status != dual::status_t::OPTIMAL) { + settings_.log.printf("Numerical issue at root node. Resolving from scratch\n"); + lp_status_t scratch_status = + solve_linear_program_with_advanced_basis(original_lp_, + exploration_stats_.start_time, + lp_settings, + root_relax_soln_, + basis_update, + basic_list, + nonbasic_list, + root_vstatus_, + edge_norms_); + if (scratch_status == lp_status_t::OPTIMAL) { + // We recovered + cut_status = convert_lp_status_to_dual_status(scratch_status); + exploration_stats_.total_lp_iters += root_relax_soln_.iterations; + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); + } else { + settings_.log.printf("Cut status %s\n", dual::status_to_string(cut_status).c_str()); + return mip_status_t::NUMERICAL; } + } - f_t remove_cuts_start_time = tic(); - mutex_original_lp_.lock(); - remove_cuts(original_lp_, - settings_, - exploration_stats_.start_time, - Arow_, - new_slacks_, - original_rows, - var_types_, - root_vstatus_, - edge_norms_, - root_relax_soln_.x, - root_relax_soln_.y, - root_relax_soln_.z, - basic_list, - nonbasic_list, - basis_update); - mutex_original_lp_.unlock(); - f_t remove_cuts_time = toc(remove_cuts_start_time); - if (remove_cuts_time > 1.0) { - settings_.log.debug("Remove cuts time %.2f seconds\n", remove_cuts_time); - } - fractional.clear(); - num_fractional = - fractional_variables(settings_, root_relax_soln_.x, var_types_, fractional); - - if (num_fractional == 0) { - upper_bound_ = root_objective_; - mutex_upper_.lock(); - incumbent_.set_incumbent_solution(root_objective_, root_relax_soln_.x); - mutex_upper_.unlock(); - } - f_t obj = upper_bound_.load(); - report(' ', obj, root_objective_, 0, num_fractional); - - f_t rel_gap = user_relative_gap(original_lp_, upper_bound_.load(), root_objective_); - f_t abs_gap = upper_bound_.load() - root_objective_; - if (rel_gap < settings_.relative_mip_gap_tol || abs_gap < settings_.absolute_mip_gap_tol) { - set_solution_at_root(solution, cut_info); - set_final_solution(solution, root_objective_); - return mip_status_t::OPTIMAL; - } + f_t remove_cuts_start_time = tic(); + mutex_original_lp_.lock(); + remove_cuts(original_lp_, + settings_, + exploration_stats_.start_time, + Arow_, + new_slacks_, + original_rows, + var_types_, + root_vstatus_, + edge_norms_, + root_relax_soln_.x, + root_relax_soln_.y, + root_relax_soln_.z, + basic_list, + nonbasic_list, + basis_update); + mutex_original_lp_.unlock(); + f_t remove_cuts_time = toc(remove_cuts_start_time); + if (remove_cuts_time > 1.0) { + settings_.log.debug("Remove cuts time %.2f seconds\n", remove_cuts_time); + } + fractional.clear(); + num_fractional = fractional_variables(settings_, root_relax_soln_.x, var_types_, fractional); - f_t change_in_objective = root_objective_ - last_objective; - const f_t factor = settings_.cut_change_threshold; - const f_t min_objective = 1e-3; - if (change_in_objective <= - factor * std::max(min_objective, std::abs(root_relax_objective))) { - settings_.log.debug( - "Change in objective %.16e is less than 1e-3 of root relax objective %.16e\n", - change_in_objective, - root_relax_objective); - break; - } - last_objective = root_objective_; + if (num_fractional == 0) { + upper_bound_ = root_objective_; + mutex_upper_.lock(); + incumbent_.set_incumbent_solution(root_objective_, root_relax_soln_.x); + mutex_upper_.unlock(); } - } + f_t obj = upper_bound_.load(); + report(' ', obj, root_objective_, 0, num_fractional); - print_cut_info(settings_, cut_info); + f_t rel_gap = user_relative_gap(original_lp_, upper_bound_.load(), root_objective_); + f_t abs_gap = upper_bound_.load() - root_objective_; + if (rel_gap < settings_.relative_mip_gap_tol || abs_gap < settings_.absolute_mip_gap_tol) { + set_solution_at_root(solution, cut_info); + set_final_solution(solution, root_objective_); + return mip_status_t::OPTIMAL; + } - if (cut_info.has_cuts()) { - settings_.log.printf("Cut pool size : %d\n", cut_pool_size); - settings_.log.printf("Size with cuts : %d constraints, %d variables, %d nonzeros\n", - original_lp_.num_rows, - original_lp_.num_cols, - original_lp_.A.col_start[original_lp_.A.n]); + f_t change_in_objective = root_objective_ - last_objective; + const f_t factor = settings_.cut_change_threshold; + const f_t min_objective = 1e-3; + if (change_in_objective <= factor * std::max(min_objective, std::abs(root_relax_objective))) { + settings_.log.debug( + "Change in objective %.16e is less than 1e-3 of root relax objective %.16e\n", + change_in_objective, + root_relax_objective); + break; + } + last_objective = root_objective_; } + } - set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_); - - pc_.resize(original_lp_.num_cols); - { - raft::common::nvtx::range scope_sb("BB::strong_branching"); - strong_branching(original_problem_, - original_lp_, - settings_, - exploration_stats_.start_time, - var_types_, - root_relax_soln_.x, - fractional, - root_objective_, - root_vstatus_, - edge_norms_, - pc_); - } + print_cut_info(settings_, cut_info); - if (toc(exploration_stats_.start_time) > settings_.time_limit) { - solver_status_ = mip_status_t::TIME_LIMIT; - set_final_solution(solution, root_objective_); - return solver_status_; - } + if (cut_info.has_cuts()) { + settings_.log.printf("Cut pool size : %d\n", cut_pool_size); + settings_.log.printf("Size with cuts : %d constraints, %d variables, %d nonzeros\n", + original_lp_.num_rows, + original_lp_.num_cols, + original_lp_.A.col_start[original_lp_.A.n]); + } - if (settings_.reduced_cost_strengthening >= 2 && upper_bound_.load() < last_upper_bound) { - std::vector lower_bounds; - std::vector upper_bounds; - i_t num_fixed = find_reduced_cost_fixings(upper_bound_.load(), lower_bounds, upper_bounds); - if (num_fixed > 0) { - std::vector bounds_changed(original_lp_.num_cols, true); - std::vector row_sense; + set_uninitialized_steepest_edge_norms(original_lp_, basic_list, edge_norms_); - bounds_strengthening_t node_presolve(original_lp_, Arow_, row_sense, var_types_); + pc_.resize(original_lp_.num_cols); + { + raft::common::nvtx::range scope_sb("BB::strong_branching"); + strong_branching(original_problem_, + original_lp_, + settings_, + exploration_stats_.start_time, + var_types_, + root_relax_soln_.x, + fractional, + root_objective_, + root_vstatus_, + edge_norms_, + pc_); + } - mutex_original_lp_.lock(); - original_lp_.lower = lower_bounds; - original_lp_.upper = upper_bounds; - bool feasible = node_presolve.bounds_strengthening( - settings_, bounds_changed, original_lp_.lower, original_lp_.upper); - mutex_original_lp_.unlock(); - if (!feasible) { - settings_.log.printf("Bound strengthening failed\n"); - return mip_status_t::NUMERICAL; // We had a feasible integer solution, but bound - // strengthening thinks we are infeasible. + if (toc(exploration_stats_.start_time) > settings_.time_limit) { + solver_status_ = mip_status_t::TIME_LIMIT; + set_final_solution(solution, root_objective_); + return solver_status_; + } + + if (settings_.reduced_cost_strengthening >= 2 && upper_bound_.load() < last_upper_bound) { + std::vector lower_bounds; + std::vector upper_bounds; + i_t num_fixed = find_reduced_cost_fixings(upper_bound_.load(), lower_bounds, upper_bounds); + if (num_fixed > 0) { + std::vector bounds_changed(original_lp_.num_cols, true); + std::vector row_sense; + + bounds_strengthening_t node_presolve(original_lp_, Arow_, row_sense, var_types_); + + mutex_original_lp_.lock(); + original_lp_.lower = lower_bounds; + original_lp_.upper = upper_bounds; + bool feasible = node_presolve.bounds_strengthening( + settings_, bounds_changed, original_lp_.lower, original_lp_.upper); + mutex_original_lp_.unlock(); + if (!feasible) { + settings_.log.printf("Bound strengthening failed\n"); + return mip_status_t::NUMERICAL; // We had a feasible integer solution, but bound + // strengthening thinks we are infeasible. + } + // Go through and check the fractional variables and remove any that are now fixed to their + // bounds + std::vector to_remove(fractional.size(), 0); + i_t num_to_remove = 0; + for (i_t k = 0; k < fractional.size(); k++) { + const i_t j = fractional[k]; + if (std::abs(original_lp_.upper[j] - original_lp_.lower[j]) < settings_.fixed_tol) { + to_remove[k] = 1; + num_to_remove++; } - // Go through and check the fractional variables and remove any that are now fixed to their - // bounds - std::vector to_remove(fractional.size(), 0); - i_t num_to_remove = 0; + } + if (num_to_remove > 0) { + std::vector new_fractional; + new_fractional.reserve(fractional.size() - num_to_remove); for (i_t k = 0; k < fractional.size(); k++) { - const i_t j = fractional[k]; - if (std::abs(original_lp_.upper[j] - original_lp_.lower[j]) < settings_.fixed_tol) { - to_remove[k] = 1; - num_to_remove++; - } - } - if (num_to_remove > 0) { - std::vector new_fractional; - new_fractional.reserve(fractional.size() - num_to_remove); - for (i_t k = 0; k < fractional.size(); k++) { - if (!to_remove[k]) { new_fractional.push_back(fractional[k]); } - } - fractional = new_fractional; - num_fractional = fractional.size(); + if (!to_remove[k]) { new_fractional.push_back(fractional[k]); } } + fractional = new_fractional; + num_fractional = fractional.size(); } } + } - // Choose variable to branch on - i_t branch_var = pc_.variable_selection(fractional, root_relax_soln_.x, log); + // Choose variable to branch on + i_t branch_var = pc_.variable_selection(fractional, root_relax_soln_.x, log); - search_tree_.root = std::move(mip_node_t(root_objective_, root_vstatus_)); - search_tree_.num_nodes = 0; - search_tree_.graphviz_node(settings_.log, &search_tree_.root, "lower bound", root_objective_); - search_tree_.branch(&search_tree_.root, - branch_var, - root_relax_soln_.x[branch_var], - num_fractional, - root_vstatus_, - original_lp_, - log); - node_queue_.push(search_tree_.root.get_down_child()); - node_queue_.push(search_tree_.root.get_up_child()); + search_tree_.root = std::move(mip_node_t(root_objective_, root_vstatus_)); + search_tree_.num_nodes = 0; + search_tree_.graphviz_node(settings_.log, &search_tree_.root, "lower bound", root_objective_); + search_tree_.branch(&search_tree_.root, + branch_var, + root_relax_soln_.x[branch_var], + num_fractional, + root_vstatus_, + original_lp_, + log); + node_queue_.push(search_tree_.root.get_down_child()); + node_queue_.push(search_tree_.root.get_up_child()); - settings_.log.printf("Exploring the B&B tree using %d threads\n\n", settings_.num_threads); + settings_.log.printf("Exploring the B&B tree using %d threads\n\n", settings_.num_threads); - exploration_stats_.nodes_explored = 0; - exploration_stats_.nodes_unexplored = 2; - exploration_stats_.nodes_since_last_log = 0; - exploration_stats_.last_log = tic(); - min_node_queue_size_ = 2 * settings_.num_threads; + exploration_stats_.nodes_explored = 0; + exploration_stats_.nodes_unexplored = 2; + exploration_stats_.nodes_since_last_log = 0; + exploration_stats_.last_log = tic(); + min_node_queue_size_ = 2 * settings_.num_threads; - if (settings_.diving_settings.coefficient_diving != 0) { - calculate_variable_locks(original_lp_, var_up_locks_, var_down_locks_); - } - if (settings_.deterministic) { - settings_.log.printf( - " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " - "| Gap | Work | Time |\n"); - } else { - settings_.log.printf( - " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " - "| Gap | Time |\n"); - } + if (settings_.diving_settings.coefficient_diving != 0) { + calculate_variable_locks(original_lp_, var_up_locks_, var_down_locks_); + } + if (settings_.deterministic) { + settings_.log.printf( + " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " + "| Gap | Work | Time |\n"); + } else { + settings_.log.printf( + " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " + "| Gap | Time |\n"); } if (settings_.deterministic) { From 068c6991da4b31df489ff1cb33674a6c74c955d1 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 11 Mar 2026 11:53:23 -0700 Subject: [PATCH 171/225] output csv in run_mip from user callback hooks --- .../linear_programming/cuopt/run_mip.cpp | 65 ++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index b95e12267a..6d76a9fb03 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -137,6 +138,58 @@ std::vector> read_solution_from_dir(const std::string file_p return initial_solutions; } +struct incumbent_record_t { + double objective; + double work_timestamp; + double wall_time; + cuopt::internals::mip_solution_origin_t origin; +}; + +class incumbent_tracker_t : public cuopt::internals::get_solution_callback_ext_t { + public: + incumbent_tracker_t(std::chrono::high_resolution_clock::time_point start_time) + : start_time_(start_time) + { + } + + void get_solution(void* data, + void* cost, + void* solution_bound, + const cuopt::internals::mip_solution_callback_info_t* info, + void* user_data) override + { + double obj = *static_cast(cost); + double wt = (info != nullptr) ? info->work_timestamp : -1.0; + auto origin = + (info != nullptr) ? info->origin : cuopt::internals::mip_solution_origin_t::UNKNOWN; + auto now = std::chrono::high_resolution_clock::now(); + double wall_s = std::chrono::duration(now - start_time_).count(); + records_.push_back({obj, wt, wall_s, origin}); + } + + void write_csv(const std::string& path) const + { + std::ofstream f(path); + if (!f.is_open()) { + fprintf(stderr, "Failed to open incumbent CSV: %s\n", path.c_str()); + return; + } + f << "index,objective,work_timestamp,wall_time_s,origin\n"; + for (size_t i = 0; i < records_.size(); ++i) { + auto& r = records_[i]; + f << i << "," << std::setprecision(15) << r.objective << "," << r.work_timestamp << "," + << std::setprecision(6) << r.wall_time << "," + << cuopt::internals::mip_solution_origin_to_string(r.origin) << "\n"; + } + } + + size_t size() const { return records_.size(); } + + private: + std::chrono::high_resolution_clock::time_point start_time_; + std::vector records_; +}; + int run_single_file(std::string file_path, int device, int batch_id, @@ -223,12 +276,14 @@ int run_single_file(std::string file_path, settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; settings.seed = 42; - settings.bnb_work_unit_scale = 0.1; + settings.bnb_work_unit_scale = 1; settings.gpu_heur_work_unit_scale = 0.4; settings.mip_scaling = false; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); + incumbent_tracker_t incumbent_tracker(start_run_solver); + settings.set_mip_callback(&incumbent_tracker); auto solution = cuopt::linear_programming::solve_mip(&handle_, mps_data_model, settings); CUOPT_LOG_INFO( "first obj: %f last improvement of best feasible: %f last improvement after recombination: %f", @@ -264,7 +319,13 @@ int run_single_file(std::string file_path, << benchmark_info.last_improvement_after_recombination << "," << mip_gap << "," << is_optimal << "\n"; write_to_output_file(out_dir, base_filename, device, n_gpus, batch_id, ss.str()); - CUOPT_LOG_INFO("Results written to the file %s", base_filename.c_str()); + if (!out_dir.empty() && incumbent_tracker.size() > 0) { + std::string mps_stem = base_filename.substr(0, base_filename.find(".mps")); + std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; + incumbent_tracker.write_csv(csv_path); + CUOPT_LOG_INFO( + "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + } return sol_found; } From 7d0fd31877dc24a1d525d7d2630390d6117ff5ca Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 11 Mar 2026 12:43:12 -0700 Subject: [PATCH 172/225] fix false optimality claim --- cpp/src/branch_and_bound/branch_and_bound.cpp | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 866fca420d..3515b4119a 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -1330,9 +1330,12 @@ struct deterministic_bfs_policy_t } break; case node_status_t::NUMERICAL: this->worker.record_numerical(node); break; + case node_status_t::PENDING: this->worker.plunge_stack.push_back(node); break; default: break; } - if (status != node_status_t::HAS_CHILDREN) { this->worker.recompute_bounds_and_basis = true; } + if (status != node_status_t::HAS_CHILDREN && status != node_status_t::PENDING) { + this->worker.recompute_bounds_and_basis = true; + } } void on_numerical_issue(mip_node_t* node) override @@ -3448,25 +3451,16 @@ void branch_and_bound_t::run_deterministic_bfs_loop( bool is_child = (node->parent == worker.last_solved_node); worker.recompute_bounds_and_basis = !is_child; - node_status_t status = solve_node_deterministic(worker, node, search_tree); - worker.last_solved_node = node; - /* - settings_.log.printf( - "Deterministic BFS solved: worker=%d node_id=%d creation_seq=%d depth=%d status=%d " - "worker_clock=%.6f local_upper=%.16e global_upper=%.16e nodes_processed_h=%d\n", - worker.worker_id, - node->node_id, - node->creation_seq, - node->depth, - (int)status, - worker.clock, - worker.local_upper_bound, - upper_bound_.load(), - worker.nodes_processed_this_horizon); - */ + node_status_t status = solve_node_deterministic(worker, node, search_tree); + worker.current_node = nullptr; - worker.current_node = nullptr; - continue; + if (status == node_status_t::PENDING) { + // LP didn't finish (TIME_LIMIT/WORK_LIMIT). Node was re-enqueued by on_node_completed. + // Fall through to sync barrier instead of immediately retrying. + } else { + worker.last_solved_node = node; + continue; + } } // No work - advance to sync point to participate in barrier @@ -3785,8 +3779,13 @@ node_status_t branch_and_bound_t::solve_node_deterministic( exploration_stats_.total_lp_solve_time += toc(lp_start_time); exploration_stats_.total_lp_iters += node_iter; - ++exploration_stats_.nodes_explored; - --exploration_stats_.nodes_unexplored; + + bool lp_conclusive = + (lp_status != dual::status_t::TIME_LIMIT && lp_status != dual::status_t::WORK_LIMIT); + if (lp_conclusive) { + ++exploration_stats_.nodes_explored; + --exploration_stats_.nodes_unexplored; + } deterministic_bfs_policy_t policy{*this, worker}; auto [status, round_dir] = update_tree_impl(node_ptr, search_tree, &worker, lp_status, policy); @@ -4482,10 +4481,7 @@ void branch_and_bound_t::deterministic_dive( lp_status = convert_lp_status_to_dual_status(second_status); } - ++nodes_this_dive; - ++worker.total_nodes_explored; worker.lp_iters_this_dive += node_iter; - worker.clock = worker.work_context.global_work_units_elapsed; if (lp_status == dual::status_t::TIME_LIMIT || lp_status == dual::status_t::WORK_LIMIT || @@ -4493,6 +4489,9 @@ void branch_and_bound_t::deterministic_dive( break; } + ++nodes_this_dive; + ++worker.total_nodes_explored; + deterministic_diving_policy_t policy{*this, worker, stack, max_backtrack_depth}; update_tree_impl(node_ptr, dive_tree, &worker, lp_status, policy); } From 2be4f5b35c0424e0f4c53633b568816996317947 Mon Sep 17 00:00:00 2001 From: anandhkb Date: Wed, 11 Mar 2026 17:32:44 -0700 Subject: [PATCH 173/225] Log root LP dual objective in final status for gap verification Add 'Root LP dual objective (last)' when finite so reviewers can confirm the heuristic gap calculation used the correct dual simplex objective. --- cpp/src/branch_and_bound/branch_and_bound.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index f9d9112763..23bb5e1e6a 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -724,6 +724,12 @@ void branch_and_bound_t::set_final_solution(mip_solution_t& obj, is_maximization ? "Upper" : "Lower", user_bound); + { + const f_t root_lp_obj = root_lp_current_lower_bound_.load(); + if (std::isfinite(root_lp_obj)) { + settings_.log.printf("Root LP dual objective (last): %.16e\n", root_lp_obj); + } + } if (gap <= settings_.absolute_mip_gap_tol || gap_rel <= settings_.relative_mip_gap_tol) { solver_status_ = mip_status_t::OPTIMAL; From 6a0d60beb58de6034167190b7d255390a80a909e Mon Sep 17 00:00:00 2001 From: anandhkb Date: Wed, 11 Mar 2026 17:34:02 -0700 Subject: [PATCH 174/225] Style: fix formatting in solve() --- cpp/src/branch_and_bound/branch_and_bound.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 23bb5e1e6a..d8fab6ec52 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -1989,7 +1989,6 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_relax_soln_.resize(original_lp_.num_rows, original_lp_.num_cols); - if (settings_.clique_cuts != 0 && clique_table_ == nullptr) { signal_extend_cliques_.store(false, std::memory_order_release); typename ::cuopt::linear_programming::mip_solver_settings_t::tolerances_t @@ -2015,11 +2014,11 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut }); } - i_t original_rows = original_lp_.num_rows; - simplex_solver_settings_t lp_settings = settings_; - lp_settings.inside_mip = 1; - lp_settings.scale_columns = false; - lp_settings.concurrent_halt = get_root_concurrent_halt(); + i_t original_rows = original_lp_.num_rows; + simplex_solver_settings_t lp_settings = settings_; + lp_settings.inside_mip = 1; + lp_settings.scale_columns = false; + lp_settings.concurrent_halt = get_root_concurrent_halt(); lp_settings.dual_simplex_objective_callback = [this](f_t user_obj) { root_lp_current_lower_bound_.store(user_obj); }; From 41967367b510f2e2d8ddb775a425a343072260f6 Mon Sep 17 00:00:00 2001 From: Chris Maes Date: Wed, 11 Mar 2026 17:48:49 -0700 Subject: [PATCH 175/225] Fix MIR cut aggregation. Complement integer variables to find MIR cuts. Use Variable Lower/Upper Bounds in MIR cuts. (#905) This PR includes the following changes to try and find more MIR cuts (these changes follow Kati Wolter's thesis): 0) Fix a bug in row aggregation in MIR cuts. This bug was causing us to fail to generate cuts when trying to aggregate most rows. 1) Compute variable lower bounds (VLB) and variable upper bounds (VUB), i.e y_j <= gamma * x_i + alpha is a variable upper bound for the continuous variable y_j in terms of the integer variable x_i. We use constraint activations to derive VLB/VUBs from the rows of the LP relaxation. 2) New scoring of rows based on dual variable value associated with the row. 3) New complemented_mir_t class with bound substition and VLB/VUBs. 4) Try complementing integer variables off their bounds in the base inequality if we are unable to find a cut. Store new upper bounds for transformed variables. 5) Try dividing base inequality by delta in { | a_j | : j in I, 0 < x_j < new_upper_j } 6) New scratch_pad_t class to abstract out collecting nonzero coefficients from a cut 7) New inequality_t class to abstract out a sparse_vector_t and rhs. Results from a 5 minute MIPLIB benchmark run on GH200 are shown below. TLDR: Time to optimality improves by 5%. 6 more problems are declared optimal. ``` Geomean MIP GAP A / (B = basline): 1.00 Geomean Time to Optimal A/B: 0.95 A optimal 65 B optimal 59 A problems with feasible solutions 223 B problems with feasible solutions 223 A wins neos5 34.95 48.95 binkar10_1 94.57 300.00 cbs-cta 31.61 300.00 neos-4738912-atrato 18.21 40.02 eil33-2 8.24 9.68 fhnw-binpack4-48 0.48 0.57 gen-ip002 44.87 56.03 map10 138.82 300.00 mas74 32.05 53.34 graphdraw-domain 0.71 175.61 neos-662469 156.25 300.00 n5-3 7.88 74.45 neos-1122047 6.39 8.29 neos-1171448 3.06 3.67 neos-1582420 1.70 11.22 neos-957323 55.15 66.44 ns1116954 128.69 300.00 neos-960392 57.16 300.00 nursesched-sprint02 56.29 300.00 p200x1188c 95.35 300.00 piperout-08 8.80 10.23 piperout-27 6.29 10.30 supportcase26 23.77 103.93 supportcase40 42.28 50.26 swath1 1.31 2.58 supportcase7 18.48 22.42 swath3 17.78 22.30 A wins 27 A losses nu25-pr12 9.50 1.42 app1-1 8.78 3.66 istanbul-no-cutoff 23.73 16.52 dano3_3 67.90 51.04 dano3_5 81.09 57.90 fastxgemm-n2r6s0t2 300.00 252.59 gen-ip054 93.65 63.32 neos-5188808-nattai 162.75 119.39 mas76 5.14 3.52 mcsched 57.55 46.66 mik-250-20-75-4 229.36 89.91 neos-827175 31.42 13.29 neos859080 1.72 0.81 neos-860300 3.50 3.11 neos8 1.01 0.84 ns1952667 300.00 132.69 pg5_34 300.00 48.44 supportcase6 99.21 82.68 uccase12 162.60 113.66 trento1 286.07 104.24 A losses 20 ``` Authors: - Chris Maes (https://github.com/chris-maes) Approvers: - Alice Boucher (https://github.com/aliceb-nv) URL: https://github.com/NVIDIA/cuopt/pull/905 --- .../mip/solver_settings.hpp | 2 +- cpp/src/branch_and_bound/branch_and_bound.cpp | 23 +- cpp/src/cuts/cuts.cpp | 2294 ++++++++++------- cpp/src/cuts/cuts.hpp | 430 ++- cpp/src/dual_simplex/phase2.cpp | 64 +- cpp/src/dual_simplex/presolve.hpp | 114 + cpp/src/dual_simplex/sparse_matrix.hpp | 8 + cpp/src/dual_simplex/sparse_vector.cpp | 9 + cpp/src/dual_simplex/sparse_vector.hpp | 4 + cpp/src/math_optimization/solver_settings.cu | 2 +- cpp/src/mip_heuristics/solver.cu | 4 + cpp/tests/mip/cuts_test.cu | 2 +- 12 files changed, 1932 insertions(+), 1024 deletions(-) diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index a9e404d14e..2c92d26231 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -96,7 +96,7 @@ class mip_solver_settings_t { i_t clique_cuts = -1; i_t strong_chvatal_gomory_cuts = -1; i_t reduced_cost_strengthening = -1; - f_t cut_change_threshold = 1e-3; + f_t cut_change_threshold = -1.0; f_t cut_min_orthogonality = 0.5; i_t mip_batch_pdlp_strong_branching = 0; i_t num_gpus = 1; diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 41d23bc0ff..2899aa3b30 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -1950,6 +1950,12 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut exploration_stats_.nodes_explored = 0; original_lp_.A.to_compressed_row(Arow_); + settings_.log.printf("Reduced cost strengthening enabled: %d\n", + settings_.reduced_cost_strengthening); + + variable_bounds_t variable_bounds( + original_lp_, settings_, var_types_, Arow_, new_slacks_); + if (guess_.size() != 0) { raft::common::nvtx::range scope_guess("BB::check_initial_guess"); std::vector crushed_guess; @@ -2161,9 +2167,11 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut var_types_, basis_update, root_relax_soln_.x, + root_relax_soln_.y, root_relax_soln_.z, basic_list, nonbasic_list, + variable_bounds, exploration_stats_.start_time); if (!problem_feasible) { if (settings_.heuristic_preemption_callback != nullptr) { @@ -2232,6 +2240,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_vstatus_, edge_norms_); var_types_.resize(original_lp_.num_cols, variable_type_t::CONTINUOUS); + variable_bounds.resize(original_lp_.num_cols); mutex_original_lp_.unlock(); f_t add_cuts_time = toc(add_cuts_start_time); if (add_cuts_time > 1.0) { @@ -2280,6 +2289,9 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } if (!feasible) { settings_.log.printf("Bound strengthening detected infeasibility\n"); +#ifdef WRITE_BOUND_STRENGTHENING_INFEASIBLE_MPS + original_lp_.write_mps("bound_strengthening_infeasible.mps"); +#endif return mip_status_t::INFEASIBLE; } @@ -2301,7 +2313,6 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut iter, edge_norms_); exploration_stats_.total_lp_iters += iter; - root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); f_t dual_phase2_time = toc(dual_phase2_start_time); if (dual_phase2_time > 1.0) { settings_.log.debug("Dual phase2 time %.2f seconds\n", dual_phase2_time); @@ -2331,9 +2342,13 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); } else { settings_.log.printf("Cut status %s\n", dual::status_to_string(cut_status).c_str()); +#ifdef WRITE_CUT_INFEASIBLE_MPS + original_lp_.write_mps("cut_infeasible.mps"); +#endif return mip_status_t::NUMERICAL; } } + root_objective_ = compute_objective(original_lp_, root_relax_soln_.x); f_t remove_cuts_start_time = tic(); mutex_original_lp_.lock(); @@ -2352,6 +2367,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut basic_list, nonbasic_list, basis_update); + variable_bounds.resize(original_lp_.num_cols); mutex_original_lp_.unlock(); f_t remove_cuts_time = toc(remove_cuts_start_time); if (remove_cuts_time > 1.0) { @@ -2380,8 +2396,9 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut f_t change_in_objective = root_objective_ - last_objective; const f_t factor = settings_.cut_change_threshold; const f_t min_objective = 1e-3; - if (change_in_objective <= factor * std::max(min_objective, std::abs(root_relax_objective))) { - settings_.log.debug( + if (factor > 0.0 && + change_in_objective <= factor * std::max(min_objective, std::abs(root_relax_objective))) { + settings_.log.printf( "Change in objective %.16e is less than 1e-3 of root relax objective %.16e\n", change_in_objective, root_relax_objective); diff --git a/cpp/src/cuts/cuts.cpp b/cpp/src/cuts/cuts.cpp index f4b3106112..c74a12b36b 100644 --- a/cpp/src/cuts/cuts.cpp +++ b/cpp/src/cuts/cuts.cpp @@ -19,6 +19,9 @@ #include +#include +#include + namespace cuopt::linear_programming::dual_simplex { namespace { @@ -490,14 +493,12 @@ std::vector> find_maximal_cliques_for_test( } template -void cut_pool_t::add_cut(cut_type_t cut_type, - const sparse_vector_t& cut, - f_t rhs) +void cut_pool_t::add_cut(cut_type_t cut_type, const inequality_t& cut) { // TODO: Need to deduplicate cuts and only add if the cut is not already in the pool - for (i_t p = 0; p < cut.i.size(); p++) { - const i_t j = cut.i[p]; + for (i_t p = 0; p < cut.size(); p++) { + const i_t j = cut.index(p); if (j >= original_vars_) { settings_.log.printf( "Cut has variable %d that is greater than original_vars_ %d\n", j, original_vars_); @@ -505,14 +506,14 @@ void cut_pool_t::add_cut(cut_type_t cut_type, } } - sparse_vector_t cut_squeezed; + inequality_t cut_squeezed; cut.squeeze(cut_squeezed); - if (cut_squeezed.i.size() == 0) { + if (cut_squeezed.size() == 0) { settings_.log.printf("Cut has no coefficients\n"); return; } - cut_storage_.append_row(cut_squeezed); - rhs_storage_.push_back(rhs); + cut_storage_.append_row(cut_squeezed.vector); + rhs_storage_.push_back(cut_squeezed.rhs); cut_type_.push_back(cut_type); cut_age_.push_back(0); } @@ -574,7 +575,6 @@ f_t cut_pool_t::cut_orthogonality(i_t i, i_t j) template void cut_pool_t::score_cuts(std::vector& x_relax) { - const f_t min_cut_distance = 1e-4; cut_distances_.resize(cut_storage_.m, 0.0); cut_norms_.resize(cut_storage_.m, 0.0); @@ -582,7 +582,7 @@ void cut_pool_t::score_cuts(std::vector& x_relax) for (i_t i = 0; i < cut_storage_.m; i++) { f_t violation; f_t cut_dist = cut_distance(i, x_relax, violation, cut_norms_[i]); - cut_distances_[i] = cut_dist <= min_cut_distance ? 0.0 : cut_dist; + cut_distances_[i] = cut_dist <= min_cut_distance_ ? 0.0 : cut_dist; if (verbose) { settings_.log.printf("Cut %d type %d distance %+e violation %+e cut_norm %e\n", i, @@ -613,7 +613,7 @@ void cut_pool_t::score_cuts(std::vector& x_relax) const i_t i = sorted_indices.back(); sorted_indices.pop_back(); - if (cut_distances_[i] <= min_cut_distance) { break; } + if (cut_distances_[i] <= min_cut_distance_) { break; } f_t cut_ortho = 1.0; const i_t best_cuts_size = best_cuts_.size(); @@ -646,6 +646,7 @@ i_t cut_pool_t::get_best_cuts(csr_matrix_t& best_cuts, best_cut_types.reserve(scored_cuts_); for (i_t i : best_cuts_) { + if (cut_distances_[i] <= min_cut_distance_) { continue; } sparse_vector_t cut(cut_storage_, i); cut.negate(); best_cuts.append_row(cut); @@ -655,7 +656,7 @@ i_t cut_pool_t::get_best_cuts(csr_matrix_t& best_cuts, age_cuts(); - return static_cast(best_cuts_.size()); + return static_cast(best_rhs.size()); } template @@ -749,8 +750,7 @@ i_t knapsack_generation_t::generate_knapsack_cuts( const std::vector& var_types, const std::vector& xstar, i_t knapsack_row, - sparse_vector_t& cut, - f_t& cut_rhs) + inequality_t& cut) { const bool verbose = false; // Get the row associated with the knapsack constraint @@ -821,44 +821,31 @@ i_t knapsack_generation_t::generate_knapsack_cuts( if (solution[k] == 0.0) { cover_size++; } } - cut.i.clear(); - cut.x.clear(); - cut.i.reserve(cover_size); - cut.x.reserve(cover_size); + cut.reserve(cover_size); + cut.clear(); h = 0; for (i_t k = 0; k < knapsack_inequality.i.size(); k++) { const i_t j = knapsack_inequality.i[k]; if (!is_slack_[j]) { - if (solution[h] == 0.0) { - cut.i.push_back(j); - cut.x.push_back(-1.0); - } + if (solution[h] == 0.0) { cut.push_back(j, -1.0); } h++; } } - cut_rhs = -cover_size + 1; + cut.rhs = -cover_size + 1; cut.sort(); // The cut is in the form: - sum_{j in cover} x_j >= -cover_size + 1 // Which is equivalent to: sum_{j in cover} x_j <= cover_size - 1 // Verify the cut is violated - f_t dot = cut.dot(xstar); - f_t violation = dot - cut_rhs; + f_t dot = cut.vector.dot(xstar); + f_t violation = dot - cut.rhs; if (verbose) { settings.log.printf("Knapsack cut %d violation %e < 0\n", knapsack_row, violation); } if (violation >= -tol) { return -1; } - -#ifdef PRINT_KNAPSACK_CUT - settings.log.printf("knapsack cut (cover %d): \n", cover_size); - for (i_t k = 0; k < cut.i.size(); k++) { - settings.log.printf("x%d coeff %g value %g\n", cut.i[k], -cut.x[k], xstar[cut.i[k]]); - } - settings.log.printf("cut_rhs %g\n", -cut_rhs); -#endif return 0; } @@ -1035,9 +1022,11 @@ bool cut_generation_t::generate_cuts(const lp_problem_t& lp, const std::vector& var_types, basis_update_mpf_t& basis_update, const std::vector& xstar, - const std::vector& reduced_costs, + const std::vector& ystar, + const std::vector& zstar, const std::vector& basic_list, const std::vector& nonbasic_list, + variable_bounds_t& variable_bounds, f_t start_time) { // Generate Gomory and CG Cuts @@ -1064,7 +1053,7 @@ bool cut_generation_t::generate_cuts(const lp_problem_t& lp, // Generate MIR and CG cuts if (settings.mir_cuts != 0 || settings.strong_chvatal_gomory_cuts != 0) { f_t cut_start_time = tic(); - generate_mir_cuts(lp, settings, Arow, new_slacks, var_types, xstar); + generate_mir_cuts(lp, settings, Arow, new_slacks, var_types, xstar, ystar, variable_bounds); f_t cut_generation_time = toc(cut_start_time); if (cut_generation_time > 1.0) { settings.log.debug("MIR and CG cut generation time %.2f seconds\n", cut_generation_time); @@ -1074,7 +1063,7 @@ bool cut_generation_t::generate_cuts(const lp_problem_t& lp, // Generate Clique cuts (last to give background clique table generation maximum time) if (settings.clique_cuts != 0) { f_t cut_start_time = tic(); - bool feasible = generate_clique_cuts(lp, settings, var_types, xstar, reduced_costs, start_time); + bool feasible = generate_clique_cuts(lp, settings, var_types, xstar, zstar, start_time); if (!feasible) { settings.log.printf("Clique cuts proved infeasible\n"); return false; @@ -1098,11 +1087,10 @@ void cut_generation_t::generate_knapsack_cuts( { if (knapsack_generation_.num_knapsack_constraints() > 0) { for (i_t knapsack_row : knapsack_generation_.get_knapsack_constraints()) { - sparse_vector_t cut(lp.num_cols, 0); - f_t cut_rhs; + inequality_t cut(lp.num_cols); i_t knapsack_status = knapsack_generation_.generate_knapsack_cuts( - lp, settings, Arow, new_slacks, var_types, xstar, knapsack_row, cut, cut_rhs); - if (knapsack_status == 0) { cut_pool_.add_cut(cut_type_t::KNAPSACK, cut, cut_rhs); } + lp, settings, Arow, new_slacks, var_types, xstar, knapsack_row, cut); + if (knapsack_status == 0) { cut_pool_.add_cut(cut_type_t::KNAPSACK, cut); } } } } @@ -1352,7 +1340,10 @@ bool cut_generation_t::generate_clique_cuts( return false; } if (build_status == clique_cut_build_status_t::CUT_ADDED) { - cut_pool_.add_cut(cut_type_t::CLIQUE, cut, cut_rhs); + inequality_t cut_inequality; + cut_inequality.vector = cut; + cut_inequality.rhs = cut_rhs; + cut_pool_.add_cut(cut_type_t::CLIQUE, cut_inequality); #if DEBUG_CLIQUE_CUTS added_cuts++; CLIQUE_CUTS_DEBUG("generate_clique_cuts added cut nz=%lld rhs=%g clique_size=%lld", @@ -1387,127 +1378,96 @@ void cut_generation_t::generate_mir_cuts( csr_matrix_t& Arow, const std::vector& new_slacks, const std::vector& var_types, - const std::vector& xstar) + const std::vector& xstar, + const std::vector& ystar, + variable_bounds_t& variable_bounds) { - f_t mir_start_time = tic(); - mixed_integer_rounding_cut_t mir(lp, settings, new_slacks, xstar); + f_t mir_start_time = tic(); + constexpr bool verbose = false; + complemented_mixed_integer_rounding_cut_t complemented_mir(lp, settings, new_slacks); strong_cg_cut_t cg(lp, var_types, xstar); - std::vector slack_map(lp.num_rows, -1); - for (i_t slack : new_slacks) { - const i_t col_start = lp.A.col_start[slack]; - const i_t col_end = lp.A.col_start[slack + 1]; - const i_t col_len = col_end - col_start; - assert(col_len == 1); - const i_t i = lp.A.i[col_start]; - slack_map[i] = slack; - } + std::vector scores; + complemented_mir.compute_initial_scores_for_rows(lp, settings, Arow, xstar, ystar, scores); - // Compute initial scores for all rows - std::vector score(lp.num_rows, 0.0); + // Push all the scores onto the priority queue + std::priority_queue> score_queue; for (i_t i = 0; i < lp.num_rows; i++) { - const i_t row_start = Arow.row_start[i]; - const i_t row_end = Arow.row_start[i + 1]; - - const i_t row_nz = row_end - row_start; - i_t num_integer_in_row = 0; - i_t num_continuous_in_row = 0; - for (i_t p = row_start; p < row_end; p++) { - const i_t j = Arow.j[p]; - if (var_types[j] == variable_type_t::INTEGER) { - num_integer_in_row++; - } else { - num_continuous_in_row++; - } - } - - if (num_integer_in_row == 0) { - score[i] = 0.0; - - } else { - f_t nz_score = lp.num_cols - row_nz; - - const i_t slack = slack_map[i]; - assert(slack >= 0); - const f_t slack_value = xstar[slack]; - - f_t slack_score = -std::log10(1e-16 + std::abs(slack_value)); - - const f_t nz_weight = 1.0; - const f_t slack_weight = 1.0; - const f_t integer_weight = 1.0; - - score[i] = - nz_weight * nz_score + slack_weight * slack_score + integer_weight * num_integer_in_row; - } + score_queue.push(std::make_pair(scores[i], i)); } - // Sort the rows by score - std::vector sorted_indices; - best_score_last_permutation(score, sorted_indices); - // These data structures are used to track the rows that have been aggregated // The invariant is that aggregated_rows is empty and aggregated_mark is all zeros // at the beginning of each iteration of the for loop below std::vector aggregated_rows; std::vector aggregated_mark(lp.num_rows, 0); - const i_t max_cuts = std::min(lp.num_rows, 1000); - f_t work_estimate = 0.0; - for (i_t h = 0; h < max_cuts; h++) { - // Get the row with the highest score - const i_t i = sorted_indices.back(); - sorted_indices.pop_back(); - const f_t max_score = score[i]; + // Transform the relaxation solution + std::vector transformed_xstar; + complemented_mir.bound_substitution(lp, variable_bounds, var_types, xstar, transformed_xstar); - const i_t row_nz = Arow.row_start[i + 1] - Arow.row_start[i]; - const i_t slack = slack_map[i]; + const i_t max_cuts = std::min(lp.num_rows, 100000); + f_t work_estimate = 0.0; + i_t num_cuts = 0; + while (num_cuts < max_cuts && !score_queue.empty()) { + // Get the row with the highest score from the queue + auto [max_score, i] = score_queue.top(); + score_queue.pop(); + // skip stale score entries + if (max_score != scores[i]) { continue; } + + // Add the current row to the aggregated set + aggregated_mark[i] = 1; + aggregated_rows.push_back(i); + + const i_t row_nz = Arow.row_length(i); + const i_t slack = complemented_mir.slack_cols(i); const f_t slack_value = xstar[slack]; if (max_score <= 0.0) { break; } if (work_estimate > 2e9) { break; } - sparse_vector_t inequality(Arow, i); - work_estimate += inequality.i.size(); - f_t inequality_rhs = lp.rhs[i]; + inequality_t inequality(Arow, i, lp.rhs[i]); + work_estimate += inequality.size(); + const bool generate_cg_cut = settings.strong_chvatal_gomory_cuts != 0; - f_t fractional_part_rhs = fractional_part(inequality_rhs); + f_t fractional_part_rhs = fractional_part(inequality.rhs); if (generate_cg_cut && fractional_part_rhs > 1e-6 && fractional_part_rhs < (1 - 1e-6)) { // Try to generate a CG cut - sparse_vector_t cg_inequality = inequality; - f_t cg_inequality_rhs = inequality_rhs; - if (fractional_part(inequality_rhs) < 0.5) { + + inequality_t cg_inequality = inequality; + if (fractional_part(inequality.rhs) < 0.5) { // Multiply by -1 to force the fractional part to be greater than 0.5 - cg_inequality_rhs *= -1; cg_inequality.negate(); } - sparse_vector_t cg_cut(lp.num_cols, 0); - f_t cg_cut_rhs; - i_t cg_status = cg.generate_strong_cg_cut( - lp, settings, var_types, cg_inequality, cg_inequality_rhs, xstar, cg_cut, cg_cut_rhs); - if (cg_status == 0) { cut_pool_.add_cut(cut_type_t::CHVATAL_GOMORY, cg_cut, cg_cut_rhs); } + inequality_t cg_cut; + i_t cg_status = + cg.generate_strong_cg_cut(lp, settings, var_types, cg_inequality, xstar, cg_cut); + if (cg_status == 0) { cut_pool_.add_cut(cut_type_t::CHVATAL_GOMORY, cg_cut); } } + if (settings.mir_cuts == 0) { continue; } + // Remove the slack from the equality to get an inequality - work_estimate += inequality.i.size(); + work_estimate += inequality.size(); i_t negate_inequality = 1; - for (i_t k = 0; k < inequality.i.size(); k++) { - const i_t j = inequality.i[k]; + for (i_t k = 0; k < inequality.size(); k++) { + const i_t j = inequality.index(k); if (j == slack) { - if (inequality.x[k] != 1.0) { - if (inequality.x[k] == -1.0 && lp.lower[j] >= 0.0) { + if (inequality.coeff(k) != 1.0) { + if (inequality.coeff(k) == -1.0 && lp.lower[j] >= 0.0) { negate_inequality = 0; } else { settings.log.debug("Bad slack %d in inequality: aj %e lo %e up %e\n", j, - inequality.x[k], + inequality.coeff(k), lp.lower[j], lp.upper[j]); negate_inequality = -1; break; } } - inequality.x[k] = 0.0; + inequality.vector.x[k] = 0.0; } } @@ -1516,112 +1476,56 @@ void cut_generation_t::generate_mir_cuts( if (negate_inequality) { // inequaility'*x <= inequality_rhs // But for MIR we need: inequality'*x >= inequality_rhs - inequality_rhs *= -1; inequality.negate(); - work_estimate += inequality.i.size(); + work_estimate += inequality.size(); } // We should now have: inequality'*x >= inequality_rhs - // Transform the relaxation solution - std::vector transformed_xstar; - mir.relaxation_to_nonnegative(lp, xstar, transformed_xstar); - work_estimate += transformed_xstar.size(); + for (i_t k = 0; k < inequality.size(); k++) { + const i_t j = inequality.index(k); + if (var_types[j] == variable_type_t::INTEGER) { + if (transformed_xstar[j] > complemented_mir.new_upper(j) / 2.0) { + settings.log.printf("!!!!!! j %d transformed x_j %e new_upper_j/2.0 %e\n", + j, + transformed_xstar[j], + complemented_mir.new_upper(j) / 2.0); + } + } + } - sparse_vector_t cut(lp.num_cols, 0); - f_t cut_rhs; bool add_cut = false; i_t num_aggregated = 0; const i_t max_aggregated = 6; + f_t min_abs_multiplier = 1.0; + f_t max_abs_multiplier = 1.0; work_estimate += lp.num_cols; while (!add_cut && num_aggregated < max_aggregated) { - sparse_vector_t transformed_inequality; + inequality_t transformed_inequality; inequality.squeeze(transformed_inequality); - f_t transformed_rhs = inequality_rhs; - work_estimate += transformed_inequality.i.size(); - - mir.to_nonnegative(lp, transformed_inequality, transformed_rhs); - work_estimate += transformed_inequality.i.size(); - std::vector> transformed_cuts; - std::vector transformed_cut_rhs; - std::vector transformed_violations; - - // Generate cut for delta = 1 - { - sparse_vector_t cut_1(lp.num_cols, 0); - f_t cut_1_rhs; - mir.generate_cut_nonnegative( - transformed_inequality, transformed_rhs, var_types, cut_1, cut_1_rhs); - f_t cut_1_violation = mir.compute_violation(cut_1, cut_1_rhs, transformed_xstar); - if (cut_1_violation > 1e-6) { - transformed_cuts.push_back(cut_1); - transformed_cut_rhs.push_back(cut_1_rhs); - transformed_violations.push_back(cut_1_violation); - } - work_estimate += transformed_inequality.i.size(); - } - - // Generate a cut for delta = max { |a_j|, j in I} - { - f_t max_coeff = 0.0; - for (i_t k = 0; k < transformed_inequality.i.size(); k++) { - const i_t j = transformed_inequality.i[k]; - if (var_types[j] == variable_type_t::INTEGER) { - const f_t abs_aj = std::abs(transformed_inequality.x[k]); - if (abs_aj > max_coeff) { max_coeff = abs_aj; } - } - } - work_estimate += transformed_inequality.i.size(); - - if (max_coeff > 1e-6 && max_coeff != 1.0) { - sparse_vector_t scaled_inequality = transformed_inequality; - const i_t nz = transformed_inequality.i.size(); - for (i_t k = 0; k < nz; k++) { - scaled_inequality.x[k] /= max_coeff; - } - const f_t scaled_rhs = transformed_rhs / max_coeff; - sparse_vector_t cut_2(lp.num_cols, 0); - f_t cut_2_rhs; - mir.generate_cut_nonnegative(scaled_inequality, scaled_rhs, var_types, cut_2, cut_2_rhs); - f_t cut_2_violation = mir.compute_violation(cut_2, cut_2_rhs, transformed_xstar); - if (cut_2_violation > 1e-6) { - transformed_cuts.push_back(cut_2); - transformed_cut_rhs.push_back(cut_2_rhs); - transformed_violations.push_back(cut_2_violation); - } - work_estimate += 5 * transformed_inequality.i.size(); - } - } - - if (!transformed_violations.empty()) { - std::vector permuted(transformed_violations.size()); - std::iota(permuted.begin(), permuted.end(), 0); - std::sort(permuted.begin(), permuted.end(), [&](i_t i, i_t j) { - return transformed_violations[i] > transformed_violations[j]; - }); - work_estimate += transformed_violations.size() * std::log2(transformed_violations.size()); - // Get the biggest violation - const i_t best_index = permuted[0]; - f_t max_viol = transformed_violations[best_index]; - cut = transformed_cuts[best_index]; - cut_rhs = transformed_cut_rhs[best_index]; - - if (max_viol > 1e-6) { - // TODO: Divide by 1/2*violation, 1/4*violation, 1/8*violation - // Transform back to the original variables - mir.to_original(lp, cut, cut_rhs); - mir.remove_small_coefficients(lp.lower, lp.upper, cut, cut_rhs); - mir.substitute_slacks(lp, Arow, cut, cut_rhs); - f_t viol = mir.compute_violation(cut, cut_rhs, xstar); - work_estimate += 10 * cut.i.size(); - add_cut = true; - } + work_estimate += transformed_inequality.size(); + + complemented_mir.transform_inequality(variable_bounds, var_types, transformed_inequality); + work_estimate += transformed_inequality.size(); + + inequality_t cut; + bool cut_found = complemented_mir.cut_generation_heuristic( + transformed_inequality, var_types, transformed_xstar, cut, work_estimate); + // Note cut is in the transformed variables + + if (cut_found) { + // Transform back to the original variables + complemented_mir.untransform_inequality(variable_bounds, var_types, cut); + complemented_mir.remove_small_coefficients(lp.lower, lp.upper, cut); + complemented_mir.substitute_slacks(lp, Arow, cut); + complemented_mir.remove_small_coefficients(lp.lower, lp.upper, cut); + f_t viol = complemented_mir.compute_violation(cut, xstar); + work_estimate += 10 * cut.size(); + if (viol > 1e-6) { add_cut = true; } } if (add_cut) { - if (settings.mir_cuts != 0) { - cut_pool_.add_cut(cut_type_t::MIXED_INTEGER_ROUNDING, cut, cut_rhs); - } + if (settings.mir_cuts != 0) { cut_pool_.add_cut(cut_type_t::MIXED_INTEGER_ROUNDING, cut); } break; } else { // Perform aggregation to try and find a cut @@ -1630,24 +1534,26 @@ void cut_generation_t::generate_mir_cuts( i_t num_continuous = 0; f_t max_off_bound = 0.0; i_t max_off_bound_var = -1; - for (i_t p = 0; p < inequality.i.size(); p++) { - const i_t j = inequality.i[p]; + for (i_t p = 0; p < inequality.size(); p++) { + const i_t j = inequality.index(p); + const f_t aj = inequality.coeff(p); + if (aj == 0.0) { continue; } if (var_types[j] == variable_type_t::CONTINUOUS) { num_continuous++; - const f_t off_lower = lp.lower[j] > -inf ? xstar[j] - lp.lower[j] : std::abs(xstar[j]); - const f_t off_upper = lp.upper[j] < inf ? lp.upper[j] - xstar[j] : std::abs(xstar[j]); - const f_t off_bound = std::max(off_lower, off_upper); - const i_t col_start = lp.A.col_start[j]; - const i_t col_end = lp.A.col_start[j + 1]; - const i_t col_len = col_end - col_start; + const f_t lb_star_j = complemented_mir.get_lb_star(j); + const f_t ub_star_j = complemented_mir.get_ub_star(j); + const f_t off_lower = lb_star_j > -inf ? xstar[j] - lb_star_j : std::abs(xstar[j]); + const f_t off_upper = ub_star_j < inf ? ub_star_j - xstar[j] : std::abs(xstar[j]); + const f_t off_bound = std::min(off_lower, off_upper); + const i_t col_len = lp.A.col_length(j); if (off_bound > max_off_bound && col_len > 1) { max_off_bound = off_bound; max_off_bound_var = j; } } } - work_estimate += 10 * inequality.i.size(); + work_estimate += 10 * inequality.size(); if (num_continuous == 0 || max_off_bound < 1e-6) { break; } @@ -1655,8 +1561,8 @@ void cut_generation_t::generate_mir_cuts( if (max_off_bound_var >= 0) { const i_t col_start = lp.A.col_start[max_off_bound_var]; const i_t col_end = lp.A.col_start[max_off_bound_var + 1]; - const i_t col_len = col_end - col_start; - const i_t max_potential_rows = 10; + const i_t col_len = lp.A.col_length(max_off_bound_var); + const i_t max_potential_rows = col_len; if (col_len > 1) { std::vector potential_rows; potential_rows.reserve(col_len); @@ -1666,35 +1572,44 @@ void cut_generation_t::generate_mir_cuts( const i_t i = lp.A.i[q]; const f_t val = lp.A.x[q]; // Can't use rows that have already been aggregated - if (std::abs(val) > threshold && aggregated_mark[i] == 0) { - potential_rows.push_back(i); - } + if (std::abs(val) > threshold && !aggregated_mark[i]) { potential_rows.push_back(i); } if (potential_rows.size() >= max_potential_rows) { break; } } work_estimate += 5 * (col_end - col_start); - if (!potential_rows.empty()) { - std::sort(potential_rows.begin(), potential_rows.end(), [&](i_t a, i_t b) { - return score[a] > score[b]; - }); - work_estimate += 10 * std::log2(10); - - const i_t pivot_row = potential_rows[0]; - - sparse_vector_t pivot_row_inequality(Arow, pivot_row); - f_t pivot_row_rhs = lp.rhs[pivot_row]; - work_estimate += pivot_row_inequality.i.size(); - mir.combine_rows(lp, - Arow, - max_off_bound_var, - pivot_row_inequality, - pivot_row_rhs, - inequality, - inequality_rhs); + bool did_aggregate = false; + while (!potential_rows.empty()) { + const i_t pivot_row = + *std::max_element(potential_rows.begin(), potential_rows.end(), [&](i_t a, i_t b) { + return scores[a] < scores[b]; + }); + work_estimate += potential_rows.size(); + + inequality_t pivot_row_inequality(Arow, pivot_row, lp.rhs[pivot_row]); + work_estimate += pivot_row_inequality.size(); + // Save inequality before combine_rows mutates it, so we can restore on rejection + inequality_t saved_inequality = inequality; + f_t multiplier = complemented_mir.combine_rows( + lp, Arow, max_off_bound_var, pivot_row_inequality, inequality); + if (max_abs_multiplier / std::abs(multiplier) > 10000 || + std::abs(multiplier) / min_abs_multiplier > 10000) { + inequality = saved_inequality; + // Erase the pivot row from the potential rows + potential_rows.erase( + std::remove(potential_rows.begin(), potential_rows.end(), pivot_row), + potential_rows.end()); + continue; + } + max_abs_multiplier = std::max(max_abs_multiplier, std::abs(multiplier)); + min_abs_multiplier = std::min(min_abs_multiplier, std::abs(multiplier)); aggregated_rows.push_back(pivot_row); aggregated_mark[pivot_row] = 1; - work_estimate += inequality.i.size() + pivot_row_inequality.i.size(); - } else { + work_estimate += inequality.size() + pivot_row_inequality.size(); + did_aggregate = true; + break; + } + + if (!did_aggregate) { // No potential rows to aggregate break; } @@ -1707,32 +1622,26 @@ void cut_generation_t::generate_mir_cuts( if (add_cut) { // We were successful in generating a cut. - // Set the score of the aggregated rows to zero + // Set the score of the aggregated rows to a lower value for (i_t row : aggregated_rows) { - score[row] = 0.0; + scores[row] = 0.99 * scores[row]; + score_queue.push(std::make_pair(scores[row], row)); } + work_estimate += aggregated_rows.size() * std::log2(score_queue.size()); } // Clear the aggregated mark + work_estimate += 2 * aggregated_rows.size(); for (i_t row : aggregated_rows) { aggregated_mark[row] = 0; } - work_estimate += 2 * aggregated_rows.size(); // Clear the aggregated rows aggregated_rows.clear(); // Set the score of the current row to zero - score[i] = 0.0; - - // Re-sort the rows by score - // It's possible this could be made more efficient by storing the rows in a data structure - // that allows us to: - // 1. Get the row with the best score - // 2. Get the row with a nonzero in column j that has the best score - // 3. Remove the rows that have been aggregated - // 4. Remove the current row - best_score_last_permutation(score, sorted_indices); - work_estimate += score.size() * std::log2(score.size()); + scores[i] = 0.0; + score_queue.push(std::make_pair(scores[i], i)); + work_estimate += std::log2(std::max(1, static_cast(score_queue.size()))); } } @@ -1749,100 +1658,118 @@ void cut_generation_t::generate_gomory_cuts( const std::vector& nonbasic_list) { tableau_equality_t tableau(lp, basis_update, nonbasic_list); - mixed_integer_rounding_cut_t mir(lp, settings, new_slacks, xstar); + mixed_integer_gomory_cut_t gomory_cut; + complemented_mixed_integer_rounding_cut_t complemented_mir(lp, settings, new_slacks); + simplex_solver_settings_t variable_settings = settings; + variable_settings.sub_mip = 1; + variable_bounds_t variable_bounds(lp, variable_settings, var_types, Arow, new_slacks); strong_cg_cut_t cg(lp, var_types, xstar); + std::vector transformed_xstar; + complemented_mir.bound_substitution(lp, variable_bounds, var_types, xstar, transformed_xstar); for (i_t i = 0; i < lp.num_rows; i++) { - sparse_vector_t inequality(lp.num_cols, 0); - f_t inequality_rhs; + inequality_t inequality(lp.num_cols); const i_t j = basic_list[i]; if (var_types[j] != variable_type_t::INTEGER) { continue; } const f_t x_j = xstar[j]; - if (std::abs(x_j - std::round(x_j)) < settings.integer_tol) { continue; } - i_t tableau_status = tableau.generate_base_equality(lp, - settings, - Arow, - var_types, - basis_update, - xstar, - basic_list, - nonbasic_list, - i, - inequality, - inequality_rhs); + if (fractional_part(x_j) < 0.05 || fractional_part(x_j) > 0.95) { continue; } + + i_t tableau_status = tableau.generate_base_equality( + lp, settings, Arow, var_types, basis_update, xstar, basic_list, nonbasic_list, i, inequality); if (tableau_status == 0) { // Generate a CG cut const bool generate_cg_cut = settings.strong_chvatal_gomory_cuts != 0; if (generate_cg_cut) { // Try to generate a CG cut - sparse_vector_t cg_inequality = inequality; - f_t cg_inequality_rhs = inequality_rhs; - if (fractional_part(inequality_rhs) < 0.5) { + inequality_t cg_inequality = inequality; + if (fractional_part(inequality.rhs) < 0.5) { // Multiply by -1 to force the fractional part to be greater than 0.5 - cg_inequality_rhs *= -1; cg_inequality.negate(); } - sparse_vector_t cg_cut(lp.num_cols, 0); - f_t cg_cut_rhs; - i_t cg_status = cg.generate_strong_cg_cut( - lp, settings, var_types, cg_inequality, cg_inequality_rhs, xstar, cg_cut, cg_cut_rhs); - if (cg_status == 0) { cut_pool_.add_cut(cut_type_t::CHVATAL_GOMORY, cg_cut, cg_cut_rhs); } + inequality_t cg_cut(lp.num_cols); + i_t cg_status = + cg.generate_strong_cg_cut(lp, settings, var_types, cg_inequality, xstar, cg_cut); + if (cg_status == 0) { cut_pool_.add_cut(cut_type_t::CHVATAL_GOMORY, cg_cut); } } if (settings.mixed_integer_gomory_cuts == 0) { continue; } - // Given the base inequality, generate a MIR cut - sparse_vector_t cut_A(lp.num_cols, 0); - f_t cut_A_rhs; - i_t mir_status = mir.generate_cut( - inequality, inequality_rhs, lp.upper, lp.lower, var_types, cut_A, cut_A_rhs); - bool A_valid = false; - f_t cut_A_distance = 0.0; - if (mir_status == 0) { - if (cut_A.i.size() == 0) { continue; } - mir.substitute_slacks(lp, Arow, cut_A, cut_A_rhs); - if (cut_A.i.size() == 0) { + // Transform the inequality + inequality_t transformed_inequality = inequality; + complemented_mir.transform_inequality(variable_bounds, var_types, transformed_inequality); + + // Generate a MIR cut from the transformed inequality + inequality_t cut_A_float(lp.num_cols); + bool cut_ok = complemented_mir.generate_cut_nonnegative_maintain_indicies( + transformed_inequality, var_types, cut_A_float); + + // Transform the cut back to the original variables + complemented_mir.untransform_inequality(variable_bounds, var_types, cut_A_float); + complemented_mir.remove_small_coefficients(lp.lower, lp.upper, cut_A_float); + + inequality_t cut_A(lp.num_cols); + if (cut_ok) { cut_ok = gomory_cut.rational_coefficients(var_types, cut_A_float, cut_A); } + + // See if the inequality is violated by the original relaxation solution + f_t cut_A_violation = complemented_mir.compute_violation(cut_A, xstar); + bool A_valid = false; + f_t cut_A_distance = 0.0; + if (cut_ok && cut_A_violation > 1e-6) { + if (cut_A.size() == 0) { continue; } + complemented_mir.substitute_slacks(lp, Arow, cut_A); + complemented_mir.remove_small_coefficients(lp.lower, lp.upper, cut_A); + if (cut_A.size() == 0) { A_valid = false; } else { // Check that the cut is violated - f_t dot = cut_A.dot(xstar); - f_t cut_norm = cut_A.norm2_squared(); - if (dot >= cut_A_rhs) { continue; } - cut_A_distance = (cut_A_rhs - dot) / std::sqrt(cut_norm); + f_t dot = cut_A.vector.dot(xstar); + f_t cut_norm = cut_A.vector.norm2_squared(); + if (dot >= cut_A.rhs) { continue; } + cut_A_distance = (cut_A.rhs - dot) / std::sqrt(cut_norm); A_valid = true; } } // Negate the base inequality inequality.negate(); - inequality_rhs *= -1; - - sparse_vector_t cut_B(lp.num_cols, 0); - f_t cut_B_rhs; - - mir_status = mir.generate_cut( - inequality, inequality_rhs, lp.upper, lp.lower, var_types, cut_B, cut_B_rhs); - bool B_valid = false; - f_t cut_B_distance = 0.0; - if (mir_status == 0) { - if (cut_B.i.size() == 0) { continue; } - mir.substitute_slacks(lp, Arow, cut_B, cut_B_rhs); - if (cut_B.i.size() == 0) { + + inequality_t cut_B_float(lp.num_cols); + + transformed_inequality = inequality; + complemented_mir.transform_inequality(variable_bounds, var_types, transformed_inequality); + + cut_ok = complemented_mir.generate_cut_nonnegative_maintain_indicies( + transformed_inequality, var_types, cut_B_float); + // Transform the cut back to the original variables + complemented_mir.untransform_inequality(variable_bounds, var_types, cut_B_float); + complemented_mir.remove_small_coefficients(lp.lower, lp.upper, cut_B_float); + + inequality_t cut_B(lp.num_cols); + if (cut_ok) { cut_ok = gomory_cut.rational_coefficients(var_types, cut_B_float, cut_B); } + + bool B_valid = false; + f_t cut_B_distance = 0.0; + f_t cut_B_violation = complemented_mir.compute_violation(cut_B, xstar); + if (cut_ok && cut_B_violation > 1e-6) { + if (cut_B.size() == 0) { continue; } + complemented_mir.substitute_slacks(lp, Arow, cut_B); + complemented_mir.remove_small_coefficients(lp.lower, lp.upper, cut_B); + if (cut_B.size() == 0) { B_valid = false; } else { // Check that the cut is violated - f_t dot = cut_B.dot(xstar); - f_t cut_norm = cut_B.norm2_squared(); - if (dot >= cut_B_rhs) { continue; } - cut_B_distance = (cut_B_rhs - dot) / std::sqrt(cut_norm); + f_t dot = cut_B.vector.dot(xstar); + f_t cut_norm = cut_B.vector.norm2_squared(); + if (dot >= cut_B.rhs) { continue; } + cut_B_distance = (cut_B.rhs - dot) / std::sqrt(cut_norm); B_valid = true; } } if ((cut_A_distance > cut_B_distance) && A_valid) { - cut_pool_.add_cut(cut_type_t::MIXED_INTEGER_GOMORY, cut_A, cut_A_rhs); + cut_pool_.add_cut(cut_type_t::MIXED_INTEGER_GOMORY, cut_A); } else if (B_valid) { - cut_pool_.add_cut(cut_type_t::MIXED_INTEGER_GOMORY, cut_B, cut_B_rhs); + cut_pool_.add_cut(cut_type_t::MIXED_INTEGER_GOMORY, cut_B); } } } @@ -1859,8 +1786,7 @@ i_t tableau_equality_t::generate_base_equality( const std::vector& basic_list, const std::vector& nonbasic_list, i_t i, - sparse_vector_t& inequality, - f_t& inequality_rhs) + inequality_t& inequality) { // Let's look for Gomory cuts const i_t j = basic_list[i]; @@ -1998,504 +1924,1155 @@ i_t tableau_equality_t::generate_base_equality( settings_.log.printf("b_bar[%d] = %e\n", i, b_bar[i]); #endif - inequality = a_bar; - inequality_rhs = b_bar_[i]; + inequality.vector = a_bar; + inequality.rhs = b_bar_[i]; return 0; } template -mixed_integer_rounding_cut_t::mixed_integer_rounding_cut_t( - const lp_problem_t& lp, - const simplex_solver_settings_t& settings, - const std::vector& new_slacks, - const std::vector& xstar) - : num_vars_(lp.num_cols), - settings_(settings), - x_workspace_(num_vars_, 0.0), - x_mark_(num_vars_, 0), - has_lower_(num_vars_, 0), - has_upper_(num_vars_, 0), - is_slack_(num_vars_, 0), - slack_rows_(num_vars_, 0), - bound_info_(num_vars_, 0) +bool mixed_integer_gomory_cut_t::rational_approximation(f_t x, + int64_t max_denominator, + int64_t& numerator, + int64_t& denominator) { + int64_t a, p0 = 0, q0 = 1, p1 = 1, q1 = 0; + f_t val = x; + bool negative = false; + + if (x < 0) { + negative = true; + val = -val; + } + + while (1) { + a = (int64_t)std::floor(val); + if (a < 0 || a > INT64_MAX) { return false; } // Protect against overflow + int64_t p2 = a * p1 + p0; + int64_t q2 = a * q1 + q0; + if (q2 > max_denominator) { break; } + p0 = p1; + q0 = q1; + p1 = p2; + q1 = q2; + + f_t rem = val - a; + if (rem < 1e-14) { break; } + val = 1.0 / rem; + } + + numerator = negative ? -p1 : p1; + denominator = q1; + + f_t approx = static_cast(numerator) / static_cast(denominator); + f_t err = std::abs(approx - x); + return err <= 1e-14; +} + +template +bool mixed_integer_gomory_cut_t::rational_coefficients( + const std::vector& var_types, + const inequality_t& input_inequality, + inequality_t& rational_inequality) +{ + rational_inequality = input_inequality; + + std::vector numerators; + std::vector denominators; + std::vector indices; + for (i_t k = 0; k < input_inequality.size(); k++) { + const i_t j = rational_inequality.index(k); + const f_t x = rational_inequality.coeff(k); + if (var_types[j] == variable_type_t::INTEGER) { + int64_t numerator, denominator; + if (!rational_approximation(x, static_cast(1000), numerator, denominator)) { + return false; + } + numerators.push_back(numerator); + denominators.push_back(denominator); + indices.push_back(k); + rational_inequality.vector.x[k] = static_cast(numerator) / static_cast(denominator); + } + } + + int64_t gcd_numerators = gcd(numerators); + int64_t lcm_denominators = lcm(denominators); + + f_t scalar = static_cast(lcm_denominators) / static_cast(gcd_numerators); + if (scalar < 0) { return false; } + if (std::abs(scalar) > 1000) { return false; } + + rational_inequality.scale(scalar); + + return true; +} + +template +int64_t mixed_integer_gomory_cut_t::gcd(const std::vector& integers) +{ + if (integers.empty()) { return 0; } + + int64_t result = integers[0]; + for (size_t i = 1; i < integers.size(); ++i) { + result = std::gcd(result, integers[i]); + } + return result; +} + +template +int64_t mixed_integer_gomory_cut_t::lcm(const std::vector& integers) +{ + if (integers.empty()) { return 0; } + int64_t result = + std::reduce(std::next(integers.begin()), integers.end(), integers[0], [](int64_t a, int64_t b) { + return std::lcm(a, b); + }); + return result; +} + +template +variable_bounds_t::variable_bounds_t(const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + const std::vector& var_types, + const csr_matrix_t& Arow, + const std::vector& new_slacks) + : upper_offsets(lp.num_cols + 1, 0), + lower_offsets(lp.num_cols + 1, 0), + upper_activities_(lp.num_rows, 0.0), + lower_activities_(lp.num_rows, 0.0), + num_pos_inf_(lp.num_rows, 0), + num_neg_inf_(lp.num_rows, 0) +{ + if (settings.sub_mip) { + return; // Don't compute the variable upper/lower bounds inside sub-MIP + } + f_t start_time = tic(); + + // Construct the slack map + slack_map_.resize(lp.num_rows, -1); + std::vector slack_coeff(lp.num_rows, 0.0); for (i_t j : new_slacks) { - is_slack_[j] = 1; const i_t col_start = lp.A.col_start[j]; - const i_t i = lp.A.i[col_start]; - slack_rows_[j] = i; - assert(std::abs(lp.A.x[col_start]) == 1.0); + const i_t col_end = lp.A.col_start[j + 1]; + const i_t col_len = col_end - col_start; + assert(col_len == 1); + const i_t i = lp.A.i[col_start]; + slack_map_[i] = j; + slack_coeff[i] = lp.A.x[col_start]; } - needs_complement_ = false; - for (i_t j = 0; j < num_vars_; j++) { - if (lp.lower[j] < 0) { - settings_.log.debug("Variable %d has negative lower bound %e\n", j, lp.lower[j]); + // The constraints are in the form: + // sum_j a_j x_j + sigma * slack = beta + + std::vector num_integer_in_row(lp.num_rows, 0); + // Compute the upper activities of the constraints + for (i_t i = 0; i < lp.num_rows; i++) { + const i_t row_start = Arow.row_start[i]; + const i_t row_end = Arow.row_start[i + 1]; + const i_t slack_index = slack_map_[i]; + f_t activity = 0.0; + for (i_t p = row_start; p < row_end; p++) { + const i_t j = Arow.j[p]; + if (j == slack_index) { continue; } + const f_t aj = Arow.x[p]; + const f_t uj = lp.upper[j]; + const f_t lj = lp.lower[j]; + + if (aj > 0.0) { + if (uj < inf) { + activity += aj * uj; + } else { + num_pos_inf_[i]++; + } + } else { // a_j < 0.0 + if (lj > -inf) { + activity += aj * lj; + } else { + num_pos_inf_[i]++; + } + } + + if (var_types[j] == variable_type_t::INTEGER) { num_integer_in_row[i]++; } } - const f_t uj = lp.upper[j]; - const f_t lj = lp.lower[j]; - const f_t xstar_j = xstar[j]; - if (uj < inf) { - if (uj - xstar_j <= xstar_j - lj) { - has_upper_[j] = 1; - bound_info_[j] = 1; - needs_complement_ = true; - } else if (lj != 0.0) { - has_lower_[j] = 1; - bound_info_[j] = -1; - needs_complement_ = true; + upper_activities_[i] = activity; + } + + // Compute the lower activities of the constraints + for (i_t i = 0; i < lp.num_rows; i++) { + const i_t row_start = Arow.row_start[i]; + const i_t row_end = Arow.row_start[i + 1]; + const i_t slack_index = slack_map_[i]; + f_t activity = 0.0; + for (i_t p = row_start; p < row_end; p++) { + const i_t j = Arow.j[p]; + if (j == slack_index) { continue; } + const f_t aj = Arow.x[p]; + const f_t uj = lp.upper[j]; + const f_t lj = lp.lower[j]; + if (aj > 0.0) { + if (lj > -inf) { + activity += aj * lj; + } else { + num_neg_inf_[i]++; + } + } else { // a_j < 0.0 + if (uj < inf) { + activity += aj * uj; + } else { + num_neg_inf_[i]++; + } } - continue; } + lower_activities_[i] = activity; + } - if (lj > -inf && lj != 0.0) { - has_lower_[j] = 1; - bound_info_[j] = -1; - needs_complement_ = true; + // Now go through all continuous variables and use the activiites to get upper variable bounds + i_t upper_edges = 0; + for (i_t j = 0; j < lp.num_cols; j++) { + upper_offsets[j] = upper_edges; + if (var_types[j] != variable_type_t::CONTINUOUS) { continue; } + const i_t col_start = lp.A.col_start[j]; + const i_t col_end = lp.A.col_start[j + 1]; + for (i_t p = col_start; p < col_end; p++) { + const i_t i = lp.A.i[p]; + if (num_integer_in_row[i] < 1) { continue; } + if (num_neg_inf_[i] > 2 && num_pos_inf_[i] > 2) { continue; } + const i_t row_start = Arow.row_start[i]; + const i_t row_end = Arow.row_start[i + 1]; + const i_t row_len = row_end - row_start; + if (row_len < 2) { continue; } + const f_t a_ij = lp.A.x[p]; + const f_t slack_lower = lp.lower[slack_map_[i]]; + const f_t slack_upper = lp.upper[slack_map_[i]]; + const f_t slack_coeff_i = slack_coeff[i]; + const f_t sigma_slack_lower = slack_coeff_i == 1.0 ? slack_lower : -slack_upper; + const f_t sigma_slack_upper = slack_coeff_i == 1.0 ? slack_upper : -slack_lower; + + if (sigma_slack_lower > -inf) { + const f_t beta = lp.rhs[i] - sigma_slack_lower; + // sum_k a_ik x_k <= beta + + // If we have too many variables in the row that would cause the activity to be infinite, + // we cannot derive an variable bound + if (a_ij > 0.0 && num_neg_inf_[i] <= 2) { + const f_t lower_activity_j = lower_activity(lp.lower[j], lp.upper[j], a_ij); + + // This is inefficient if num_neg_inf_[i] > 0 + // If num_neg_inf_[i] == 1 and var_types[s] != INTEGER, we can't derive a bound + // If num_neg_inf_[i] == 2 and var_types[s ^ j] != INTEGER, we can't derive a bound + // If num_neg_inf_[i] == 2 and var_types[s ^ j] == INTEGER, and lower_activity_j != -inf, + // we can't derive a bound + for (i_t q = row_start; q < row_end; q++) { + const i_t l = Arow.j[q]; + if (var_types[l] == variable_type_t::CONTINUOUS) { continue; } + // sum_{k != l, k != j} a_ik x_k + a_ij x_j + a_il x_l <= beta + // a_ij x_j <= -a_il x_l + beta - sum_{k != l, k != j} a_ik x_k + const f_t a_il = Arow.x[q]; + const f_t lower_activity_l = lower_activity(lp.lower[l], lp.upper[l], a_il); + const f_t sum = adjusted_lower_activity( + lower_activities_[i], num_neg_inf_[i], lower_activity_j, lower_activity_l); + if (sum > -inf) { + // We have a valid variable upper bound + // x_j <= -a_il/a_ij * x_l + beta/a_ij - 1/a_ij * sum_{k != l, k != j} a_ik * + // bound(x_k) + upper_variables.push_back(l); + upper_weights.push_back(-a_il / a_ij); + upper_biases.push_back(beta / a_ij - (1.0 / a_ij) * sum); + upper_edges++; + } + } + } + } + + if (sigma_slack_upper < inf) { + const f_t beta = lp.rhs[i] - sigma_slack_upper; + // sum_k a_ik x_k >= beta + + // If we have too many variables in the row that would cause the activity to be infinite, + // we cannot derive an variable bound + if (a_ij < 0.0 && num_pos_inf_[i] <= 2) { + const f_t upper_activity_j = upper_activity(lp.lower[j], lp.upper[j], a_ij); + + for (i_t q = row_start; q < row_end; q++) { + const i_t l = Arow.j[q]; + if (var_types[l] == variable_type_t::CONTINUOUS) { continue; } + // sum_{k != l, k != j} a_ik x_k + a_ij x_j + a_il x_l >= beta + // a_ij x_j >= -a_il x_l + beta - sum_{k != l, k != j} a_ik x_k + const f_t a_il = Arow.x[q]; + const f_t upper_activity_l = upper_activity(lp.lower[l], lp.upper[l], a_il); + const f_t sum = adjusted_upper_activity( + upper_activities_[i], num_pos_inf_[i], upper_activity_j, upper_activity_l); + if (sum < inf) { + // We have a valid variable upper bound + // x_j <= -a_il/a_ij * x_l + beta/a_ij - 1/a_ij * sum_{k != l, k != j} a_ik * + // bound(x_k) + upper_variables.push_back(l); + upper_weights.push_back(-a_il / a_ij); + upper_biases.push_back(beta / a_ij - (1.0 / a_ij) * sum); + upper_edges++; + } + } + } + } } } + upper_offsets[lp.num_cols] = upper_edges; + settings.log.printf("%d variable upper bounds in %.2f seconds\n", upper_edges, toc(start_time)); + + // Now go through all continuous variables and use the activiites to get lower variable bounds + i_t lower_edges = 0; + for (i_t j = 0; j < lp.num_cols; j++) { + lower_offsets[j] = lower_edges; + if (var_types[j] != variable_type_t::CONTINUOUS) { continue; } + const i_t col_start = lp.A.col_start[j]; + const i_t col_end = lp.A.col_start[j + 1]; + for (i_t p = col_start; p < col_end; p++) { + const i_t i = lp.A.i[p]; + if (num_integer_in_row[i] < 1) { continue; } + const i_t row_start = Arow.row_start[i]; + const i_t row_end = Arow.row_start[i + 1]; + const i_t row_len = row_end - row_start; + if (row_len < 2) { continue; } + const f_t a_ij = lp.A.x[p]; + const f_t slack_lower = lp.lower[slack_map_[i]]; + const f_t slack_upper = lp.upper[slack_map_[i]]; + const f_t slack_coeff_i = slack_coeff[i]; + const f_t sigma_slack_lower = slack_coeff_i == 1.0 ? slack_lower : -slack_upper; + const f_t sigma_slack_upper = slack_coeff_i == 1.0 ? slack_upper : -slack_lower; + + if (sigma_slack_lower > -inf) { + const f_t beta = lp.rhs[i] - sigma_slack_lower; + // sum_k a_ik x_k <= beta + + // If we have too many variables in the row that would cause the activity to be infinite, + // we cannot derive a variable bound + if (a_ij < 0.0 && num_neg_inf_[i] <= 2) { + const f_t lower_activity_j = lower_activity(lp.lower[j], lp.upper[j], a_ij); + + for (i_t q = row_start; q < row_end; q++) { + const i_t l = Arow.j[q]; + if (var_types[l] == variable_type_t::CONTINUOUS) { continue; } + // sum_{k != l, k != j} a_ik x_k + a_ij x_j + a_il x_l <= beta + // a_ij x_j <= -a_il x_l + beta - sum_{k != l, k != j} a_ik x_k + // x_j >= -a_il/a_ij * x_l + beta/a_ij - 1/a_ij * sum_{k != l, k != j} a_ik * bound(x_k) + const f_t a_il = Arow.x[q]; + const f_t lower_activity_l = lower_activity(lp.lower[l], lp.upper[l], a_il); + const f_t sum = adjusted_lower_activity( + lower_activities_[i], num_neg_inf_[i], lower_activity_j, lower_activity_l); + if (sum > -inf) { + // We have a valid variable lower bound + // x_j >= -a_il/a_ij * x_l + beta/a_ij - 1/a_ij * sum_{k != l, k != j} a_ik * + // bound(x_k) + lower_variables.push_back(l); + lower_weights.push_back(-a_il / a_ij); + lower_biases.push_back(beta / a_ij - (1.0 / a_ij) * sum); + lower_edges++; + } + } + } + } + + if (sigma_slack_upper < inf) { + const f_t beta = lp.rhs[i] - sigma_slack_upper; + // sum_k a_ik x_k >= beta + + // If we have too many variables in the row that would cause the activity to be infinite, + // we cannot derive a variable bound + if (a_ij > 0.0 && num_pos_inf_[i] <= 2) { + const f_t upper_activity_j = upper_activity(lp.lower[j], lp.upper[j], a_ij); + + for (i_t q = row_start; q < row_end; q++) { + const i_t l = Arow.j[q]; + if (var_types[l] == variable_type_t::CONTINUOUS) { continue; } + // sum_{k != l, k != j} a_ik x_k + a_ij x_j + a_il x_l >= beta + // a_ij x_j >= -a_il x_l + beta - sum_{k != l, k != j} a_ik x_k + const f_t a_il = Arow.x[q]; + const f_t upper_activity_l = upper_activity(lp.lower[l], lp.upper[l], a_il); + const f_t sum = adjusted_upper_activity( + upper_activities_[i], num_pos_inf_[i], upper_activity_j, upper_activity_l); + if (sum < inf) { + // We have a valid variable lower bound + // x_j >= -a_il/a_ij * x_l + beta/a_ij - 1/a_ij * sum_{k != l, k != j} a_ik * + // bound(x_k) + lower_variables.push_back(l); + lower_weights.push_back(-a_il / a_ij); + lower_biases.push_back(beta / a_ij - (1.0 / a_ij) * sum); + lower_edges++; + } + } + } + } + } + } + lower_offsets[lp.num_cols] = lower_edges; + settings.log.printf("%d variable lower bounds in %.2f seconds\n", lower_edges, toc(start_time)); } template -void mixed_integer_rounding_cut_t::to_nonnegative(const lp_problem_t& lp, - sparse_vector_t& inequality, - f_t& rhs) +complemented_mixed_integer_rounding_cut_t::complemented_mixed_integer_rounding_cut_t( + const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + const std::vector& new_slacks) + : is_slack_(lp.num_cols, 0), + slack_rows_(lp.num_cols, -1), + slack_cols_(lp.num_rows, -1), + lb_variable_(lp.num_cols, -1), + lb_star_(lp.num_cols, 0.0), + ub_variable_(lp.num_cols, -1), + ub_star_(lp.num_cols, 0.0), + transformed_upper_(lp.num_cols, inf), + bound_changed_(lp.num_cols, 0), + scratch_pad_(lp.num_cols) { - const i_t nz = inequality.i.size(); - for (i_t k = 0; k < nz; k++) { - const i_t j = inequality.i[k]; - const f_t aj = inequality.x[k]; - if (bound_info_[j] == -1) { - // v_j = x_j - l_j, v_j >= 0 - // x_j = v_j + l_j - // sum_{k != j} a_k x_j + a_j x_j <= beta - // sum_{k != j} a_k x_j + a_j (v_j + l_j) <= beta - // sum_{k != j} a_k x_j + a_j v_j <= beta - a_j l_j - const f_t lj = lp.lower[j]; - rhs -= aj * lj; - } else if (bound_info_[j] == 1) { - // w_j = u_j - x_j, w_j >= 0 - // x_j = u_j - w_j - // sum_{k != j} a_k x_k + a_j x_j <= beta - // sum_{k != j} a_k x_k + a_j (u_j - w_j) <= beta - // sum_{k != j} a_k x_k - a_j w_j <= beta - a_j u_j - const f_t uj = lp.upper[j]; - inequality.x[k] *= -1.0; - rhs -= aj * uj; - } + for (i_t j : new_slacks) { + is_slack_[j] = 1; + const i_t col_start = lp.A.col_start[j]; + const i_t i = lp.A.i[col_start]; + slack_rows_[j] = i; + slack_cols_[i] = j; + assert(std::abs(lp.A.x[col_start]) == 1.0); } } template -void mixed_integer_rounding_cut_t::relaxation_to_nonnegative( +void complemented_mixed_integer_rounding_cut_t::compute_initial_scores_for_rows( const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + const csr_matrix_t& Arow, const std::vector& xstar, - std::vector& xstar_nonnegative) + const std::vector& ystar, + std::vector& scores) { - xstar_nonnegative = xstar; - const i_t n = lp.num_cols; - for (i_t j = 0; j < n; ++j) { - if (bound_info_[j] == -1) { - // v_j = x_j - l_j - const f_t lj = lp.lower[j]; - xstar_nonnegative[j] -= lj; - } else if (bound_info_[j] == 1) { - // w_j = u_j - x_j - const f_t uj = lp.upper[j]; - xstar_nonnegative[j] = uj - xstar_nonnegative[j]; + const bool verbose = false; + const i_t n = lp.num_cols; + const f_t obj_norm = vector_norm2(lp.objective); + const f_t obj_denom = std::max(1.0, obj_norm); + + // Compute initial scores for all rows + scores.resize(lp.num_rows, 0.0); + for (i_t i = 0; i < lp.num_rows; i++) { + const i_t row_start = Arow.row_start[i]; + const i_t row_end = Arow.row_start[i + 1]; + + const i_t row_nz = row_end - row_start; + f_t row_norm = 0.0; + for (i_t p = row_start; p < row_end; p++) { + const f_t a_j = Arow.x[p]; + row_norm += a_j * a_j; + } + row_norm = std::sqrt(row_norm); + + const f_t density = static_cast(row_nz) / static_cast(n); + const f_t dual = std::abs(ystar[i]); + + const i_t slack = slack_cols_[i]; + assert(slack >= 0); + const f_t slack_value = std::max(xstar[slack], 0.0); + const f_t slack_denom = std::max(0.1, std::sqrt(row_norm)); + + const f_t nz_weight = 0.0001; + const f_t dual_weight = 1.0; + const f_t slack_weight = 0.001; + + scores[i] = nz_weight * (1.0 - density) + dual_weight * std::max(dual / obj_denom, 0.0001) + + slack_weight * (1.0 - slack_value / slack_denom); + + if (verbose) { + settings.log.printf("Scores[%d] = %e density %.2f dual %e slack %e\n", + i, + scores[i], + density, + dual, + slack_value); } } } template -void mixed_integer_rounding_cut_t::to_original(const lp_problem_t& lp, - sparse_vector_t& inequality, - f_t& rhs) +bool complemented_mixed_integer_rounding_cut_t::cut_generation_heuristic( + const inequality_t& transformed_inequality, + const std::vector& var_types, + const std::vector& transformed_xstar, + inequality_t& transformed_cut, + f_t& work_estimate) { - const i_t nz = inequality.i.size(); - for (i_t k = 0; k < nz; k++) { - const i_t j = inequality.i[k]; - const f_t dj = inequality.x[k]; - if (bound_info_[j] == -1) { - // v_j = x_j - l_j, v_j >= 0 - // sum_{k != j} d_k x_k + d_j v_j >= beta - // sum_{k != j} d_k x_k + d_j (x_j - l_j) >= beta - // sum_{k != j} d_k x_k + d_j x_j >= beta + d_j l_j - const f_t lj = lp.lower[j]; - rhs += dj * lj; - } else if (bound_info_[j] == 1) { - // w_j = u_j - x_j, w_j >= 0 - // sum_{k != j} d_k x_k + d_j w_j >= beta - // sum_{k != j} d_k x_k + d_j (u_j - x_j) >= beta - // sum_{k != j} d_k x_k - d_j x_j >= beta - d_j u_j - const f_t uj = lp.upper[j]; - inequality.x[k] *= -1.0; - rhs -= dj * uj; + std::vector deltas_to_try; + deltas_to_try.reserve(transformed_inequality.size()); + deltas_to_try.push_back(1.0); + work_estimate += transformed_inequality.size(); + i_t num_integers = 0; + f_t max_coeff = 0.0; + for (i_t k = 0; k < transformed_inequality.size(); k++) { + const i_t j = transformed_inequality.index(k); + const f_t abs_aj = std::abs(transformed_inequality.coeff(k)); + if (var_types[j] == variable_type_t::INTEGER) { + num_integers++; + max_coeff = std::max(max_coeff, abs_aj); + const f_t x_j = transformed_xstar[j]; + const f_t new_upper_j = new_upper(j); + const f_t dist_upper = new_upper_j - x_j; + const f_t dist_lower = x_j; + const bool between_bounds = x_j > 1e-6 && (new_upper_j == inf || dist_upper > 0.0); + if (between_bounds) { deltas_to_try.push_back(abs_aj); } + } + } + if (max_coeff > 1e-6 && max_coeff != 1.0) { + deltas_to_try.push_back(max_coeff); + deltas_to_try.push_back(max_coeff + 1.0); + } + + std::vector complemented_indices; + complemented_indices.reserve(num_integers); + std::vector distance_from_midpoint; + distance_from_midpoint.reserve(num_integers); + std::vector integer_indices; + integer_indices.reserve(num_integers); + for (i_t k = 0; k < transformed_inequality.size(); k++) { + const i_t j = transformed_inequality.index(k); + if (var_types[j] == variable_type_t::INTEGER && new_upper(j) < inf) { + const f_t x_j = transformed_xstar[j]; + const f_t new_upper_j = new_upper(j); + if (x_j > 1e-6 && new_upper_j < inf) { + const f_t midpoint_j = new_upper_j / 2.0; + distance_from_midpoint.push_back(x_j - midpoint_j); + integer_indices.push_back(k); + } } } + + std::vector perm(integer_indices.size()); + best_score_first_permutation(distance_from_midpoint, perm); + work_estimate += + integer_indices.size() > 0 ? integer_indices.size() * std::log2(integer_indices.size()) : 0; + + bool cut_found = false; + + inequality_t complemented_inequality = transformed_inequality; + work_estimate += 4 * transformed_inequality.size(); + + f_t delta = 0.0; + f_t best_violation = 0.0; + + // First try without any complementation + for (const f_t tmp_delta : deltas_to_try) { + bool cut_ok = scale_uncomplement_and_generate_cut(var_types, + transformed_xstar, + complemented_indices, + complemented_inequality, + tmp_delta, + transformed_cut, + work_estimate); + if (!cut_ok) { continue; } + // Check if the cut is violated + best_violation = compute_violation(transformed_cut, transformed_xstar); + work_estimate += 4 * transformed_cut.size(); + if (best_violation > 1e-6) { + cut_found = true; + delta = tmp_delta; + break; + } + } + + if (!cut_found) { + // Complement an integer variable + for (const i_t idx : perm) { + const i_t l = integer_indices[idx]; + const i_t j = complemented_inequality.index(l); + // We have an integer variable x_j <= b_j + // We create a new variable xbar_j such that + // x_j + xbar_j = b_j + // x_j = b_j - xbar_j, xbar_j = b_j - x_j + // + // The inequality + // sum_{k != j} a_k x_k + a_j x_j >= beta + // becomes + // sum_{k != j} a_k x_k + a_j (b_j - xbar_j) >= beta + // sum_{k != j} a_k x_k - a_j xbar_j >= beta - a_j b_j + const f_t b_j = new_upper(j); + const f_t a_j = complemented_inequality.coeff(l); + + complemented_inequality.vector.x[l] = -a_j; + complemented_inequality.rhs -= a_j * b_j; + complemented_indices.push_back(l); + + for (const f_t tmp_delta : deltas_to_try) { + bool cut_ok = scale_uncomplement_and_generate_cut(var_types, + transformed_xstar, + complemented_indices, + complemented_inequality, + tmp_delta, + transformed_cut, + work_estimate); + if (!cut_ok) { continue; } + // Check if the cut is violated + best_violation = compute_violation(transformed_cut, transformed_xstar); + work_estimate += 4 * transformed_cut.size(); + if (best_violation > 1e-6) { + cut_found = true; + delta = tmp_delta; + break; + } + } + if (cut_found) { break; } + } + } + + if (!cut_found) { return false; } + + // We have found a cut. Now try to improve the violation by scaling the cut by 1/2, 1/4, 1/8, etc. + std::vector scaled_deltas_to_try = {delta / 2.0, delta / 4.0, delta / 8.0}; + for (const f_t tmp_delta : scaled_deltas_to_try) { + inequality_t tmp_cut_delta; + bool cut_ok = scale_uncomplement_and_generate_cut(var_types, + transformed_xstar, + complemented_indices, + complemented_inequality, + tmp_delta, + tmp_cut_delta, + work_estimate); + if (!cut_ok) { continue; } + + // Check if the cut is violated + f_t violation = compute_violation(tmp_cut_delta, transformed_xstar); + work_estimate += 4 * tmp_cut_delta.size(); + if (violation > best_violation) { + best_violation = violation; + transformed_cut = tmp_cut_delta; + delta = tmp_delta; + } + } + + std::vector best_complemented_indices = complemented_indices; + work_estimate += 2 * best_complemented_indices.size(); + + // Try to improve the violation by complementing integer variables + complemented_inequality = transformed_inequality; + work_estimate += 4 * transformed_inequality.size(); + complemented_indices.clear(); + for (const i_t idx : perm) { + const i_t l = integer_indices[idx]; + const i_t j = complemented_inequality.index(l); + // We have an integer variable x_j <= b_j + // We create a new variable xbar_j such that + // x_j + xbar_j = b_j + // x_j = b_j - xbar_j, xbar_j = b_j - x_j + // + // The inequality + // sum_{k != j} a_k x_k + a_j x_j >= beta + // becomes + // sum_{k != j} a_k x_k + a_j (b_j - xbar_j) >= beta + // sum_{k != j} a_k x_k - a_j xbar_j >= beta - a_j b_j + const f_t b_j = new_upper(j); + const f_t a_j = complemented_inequality.coeff(l); + + complemented_inequality.vector.x[l] = -a_j; + complemented_inequality.rhs -= a_j * b_j; + complemented_indices.push_back(l); + + inequality_t tmp_cut_delta; + + bool cut_ok = scale_uncomplement_and_generate_cut(var_types, + transformed_xstar, + complemented_indices, + complemented_inequality, + delta, + tmp_cut_delta, + work_estimate); + if (!cut_ok) { continue; } + // Check if the cut is violated + f_t violation = compute_violation(tmp_cut_delta, transformed_xstar); + work_estimate += 4 * tmp_cut_delta.size(); + if (violation > best_violation) { + best_violation = violation; + best_complemented_indices = complemented_indices; + transformed_cut = tmp_cut_delta; + } + } + + return best_violation > 1e-6; +} + +template +bool complemented_mixed_integer_rounding_cut_t::scale_uncomplement_and_generate_cut( + const std::vector& var_types, + const std::vector& transformed_xstar, + const std::vector& complemented_indices, + const inequality_t& complemented_inequality, + f_t delta, + inequality_t& cut_delta, + f_t& work_estimate) +{ + inequality_t scaled_inequality = complemented_inequality; + if (delta != 1.0) { scaled_inequality.scale(1.0 / delta); } + bool cut_ok = generate_cut_nonnegative_maintain_indicies(scaled_inequality, var_types, cut_delta); + if (!cut_ok) { return false; } + work_estimate += 4 * scaled_inequality.size(); + + // Now we need to transform the complemented variables back + for (i_t h = 0; h < complemented_indices.size(); h++) { + const i_t l = complemented_indices[h]; + const i_t j = complemented_inequality.index(l); + // Our cut is of the form + // sum_{k != j} d_k x_k + d_j xbar_j >= alpha + // we have that xbar_j = b_j - x_j + // So + // sum_{k != j} d_k x_k + d_j (b_j - x_j) >= alpha + // Or + // sum_{k != j} d_k x_k - d_j x_j >= alpha - d_j b_j + + const f_t b_j = new_upper(j); + const f_t d_j = cut_delta.coeff(l); + cut_delta.vector.x[l] = -d_j; + cut_delta.rhs -= d_j * b_j; + } + work_estimate += 5 * complemented_indices.size(); + return true; } template -void mixed_integer_rounding_cut_t::remove_small_coefficients( +void complemented_mixed_integer_rounding_cut_t::remove_small_coefficients( const std::vector& lower_bounds, const std::vector& upper_bounds, - sparse_vector_t& cut, - f_t& cut_rhs) + inequality_t& cut) { - const i_t nz = cut.i.size(); + const i_t nz = cut.size(); i_t removed = 0; - for (i_t k = 0; k < cut.i.size(); k++) { - const i_t j = cut.i[k]; + for (i_t k = 0; k < cut.size(); k++) { + const i_t j = cut.index(k); // Check for small coefficients - const f_t aj = cut.x[k]; + const f_t aj = cut.coeff(k); if (std::abs(aj) < 1e-6) { if (aj >= 0.0 && upper_bounds[j] < inf) { // Move this to the right-hand side - cut_rhs -= aj * upper_bounds[j]; - cut.x[k] = 0.0; + cut.rhs -= aj * upper_bounds[j]; + cut.vector.x[k] = 0.0; removed++; } else if (aj <= 0.0 && lower_bounds[j] > -inf) { - cut_rhs += aj * lower_bounds[j]; - cut.x[k] = 0.0; + cut.rhs -= aj * lower_bounds[j]; + cut.vector.x[k] = 0.0; removed++; continue; } else { + // We need to keep the coefficient } } } if (removed > 0) { - sparse_vector_t new_cut(cut.n, 0); + inequality_t new_cut(cut.vector.n); cut.squeeze(new_cut); cut = new_cut; } } template -i_t mixed_integer_rounding_cut_t::generate_cut_nonnegative( - const sparse_vector_t& a, - f_t beta, +void complemented_mixed_integer_rounding_cut_t::bound_substitution( + const lp_problem_t& lp, + const variable_bounds_t& variable_bounds, const std::vector& var_types, - sparse_vector_t& cut, - f_t& cut_rhs) + const std::vector& xstar, + std::vector& transformed_xstar) { - auto f = [](f_t q_1, f_t q_2) -> f_t { - f_t q_1_hat = q_1 - std::floor(q_1); - f_t q_2_hat = q_2 - std::floor(q_2); - return std::min(q_1_hat, q_2_hat) + q_2_hat * std::floor(q_1); - }; - - auto h = [](f_t q) -> f_t { return std::max(q, 0.0); }; + transformed_xstar.resize(lp.num_cols); + // Perform bound substitution for continuous variables + for (i_t j = 0; j < lp.num_cols; j++) { + if (var_types[j] != variable_type_t::CONTINUOUS) { continue; } + // Step 1: Decide whether to use variable or simple bounds + const f_t uj = lp.upper[j]; + const f_t lj = lp.lower[j]; + const f_t xstar_j = xstar[j]; - std::vector cut_indices; - cut_indices.reserve(a.i.size()); - f_t R = (beta - std::floor(beta)) * std::ceil(beta); - - for (i_t k = 0; k < a.i.size(); k++) { - const i_t jj = a.i[k]; - f_t aj = a.x[k]; - if (var_types[jj] == variable_type_t::INTEGER) { - x_workspace_[jj] += f(aj, beta); - if (!x_mark_[jj] && x_workspace_[jj] != 0.0) { - x_mark_[jj] = 1; - cut_indices.push_back(jj); - } - } else { - x_workspace_[jj] += h(aj); - if (!x_mark_[jj] && x_workspace_[jj] != 0.0) { - x_mark_[jj] = 1; - cut_indices.push_back(jj); + // Set lb and lb_star to the simple lower bound + lb_variable_[j] = -1; + lb_star_[j] = lj; + + // Set ub and ub_star to the simple upper bound + ub_variable_[j] = -1; + ub_star_[j] = uj; + + // Check the variable lower bound and update lb and lb_star + // if these yield a tighter bound + const i_t lower_variable_start = variable_bounds.lower_offsets[j]; + const i_t lower_variable_end = variable_bounds.lower_offsets[j + 1]; + for (i_t p = lower_variable_start; p < lower_variable_end; p++) { + const i_t i = variable_bounds.lower_variables[p]; + const f_t gamma = variable_bounds.lower_weights[p]; + const f_t alpha = variable_bounds.lower_biases[p]; + // x_j >= gamma * x_i + alpha + + const f_t xstar_i = xstar[i]; + const f_t val = gamma * xstar_i + alpha; + if (val > lb_star_[j]) { + lb_variable_[j] = p; + lb_star_[j] = val; } } - } - - cut.i.reserve(cut_indices.size()); - cut.x.reserve(cut_indices.size()); - cut.i.clear(); - cut.x.clear(); - for (i_t k = 0; k < cut_indices.size(); k++) { - const i_t j = cut_indices[k]; - cut.i.push_back(j); - cut.x.push_back(x_workspace_[j]); - } - // Clear the workspace - for (i_t jj : cut_indices) { - x_workspace_[jj] = 0.0; - x_mark_[jj] = 0; - } - -#ifdef CHECK_WORKSPACE - for (i_t j = 0; j < x_workspace_.size(); j++) { - if (x_workspace_[j] != 0.0) { - printf("After generate_cut: Dirty x_workspace_[%d] = %e\n", j, x_workspace_[j]); - assert(x_workspace_[j] == 0.0); + // Check the variable upper bound and update ub and ub_star + // if these yield a tighter bound + const i_t upper_variable_start = variable_bounds.upper_offsets[j]; + const i_t upper_variable_end = variable_bounds.upper_offsets[j + 1]; + for (i_t p = upper_variable_start; p < upper_variable_end; p++) { + const i_t i = variable_bounds.upper_variables[p]; + const f_t gamma = variable_bounds.upper_weights[p]; + const f_t alpha = variable_bounds.upper_biases[p]; + // x_j <= gamma * x_i + alpha + + const f_t xstar_i = xstar[i]; + const f_t val = gamma * xstar_i + alpha; + if (val < ub_star_[j]) { + ub_variable_[j] = p; + ub_star_[j] = val; + } } - if (x_mark_[j] != 0) { - printf("After generate_cut: Dirty x_mark_[%d] = %d\n", j, x_mark_[j]); - assert(x_mark_[j] == 0); + + // Step 2: Decide to use the lower or upper bound + const bool has_finite_lower_bound = lb_star_[j] > -inf; + const bool has_finite_upper_bound = ub_star_[j] < inf; + if (!has_finite_lower_bound && !has_finite_upper_bound) { + transformed_xstar[j] = xstar_j; + transformed_upper_[j] = inf; + bound_changed_[j] = 0; + continue; } - } -#endif + if (has_finite_lower_bound && + (!has_finite_upper_bound || (xstar_j - lb_star_[j] <= ub_star_[j] - xstar_j))) { + // Use the lower bound + // lb_star_j <= x_j <= ub_star_j + // v_j = x_j - lb_star_j, + // 0 <= v_j <= ub_star_j - lb_star_j + transformed_upper_[j] = ub_star_[j] - lb_star_[j]; + transformed_xstar[j] = xstar_j - lb_star_[j]; + bound_changed_[j] = (lb_star_[j] == 0.0) ? 0 : -1; + } else if (has_finite_upper_bound) { + // Use the upper bound + // lb_star_j <= x_j <= ub_star_j + // x_j + w_j = ub_star_j, + // w_j = ub_star_j - x_j, + // x_j = ub_star_j - w_j + // 0 <= w_j <= ub_star_j - lb_star_j + transformed_upper_[j] = ub_star_[j] - lb_star_[j]; + transformed_xstar[j] = ub_star_[j] - xstar_j; + bound_changed_[j] = 1; + } + } + + // Perform bound substitution for the integer variables + for (i_t j = 0; j < lp.num_cols; j++) { + if (var_types[j] != variable_type_t::INTEGER) { continue; } + const f_t uj = lp.upper[j]; + const f_t lj = lp.lower[j]; + const f_t xstar_j = xstar[j]; - // The new cut is: g'*x >= R - // But we want to have it in the form h'*x <= b - cut.sort(); + lb_star_[j] = lj; + ub_star_[j] = uj; + transformed_xstar[j] = xstar_j; + transformed_upper_[j] = uj; + bound_changed_[j] = 0; - cut_rhs = R; + if (uj < inf) { + if (uj - xstar_j <= xstar_j - lj) { + // Use the upper bound + // lj <= x_j <= uj + // x_j + w_j = uj, + // w_j = uj - x_j, + // x_j = uj - w_j + // 0 <= w_j <= uj - lj + transformed_upper_[j] = uj - lj; + transformed_xstar[j] = uj - xstar_j; + bound_changed_[j] = 1; + } else if (lj != 0.0) { + // Use the lower bound + // lj <= x_j <= uj + // v_j = x_j - lj, + // 0 <= v_j <= uj - lj + transformed_upper_[j] = uj - lj; + transformed_xstar[j] = xstar_j - lj; + bound_changed_[j] = -1; + } + continue; + } -#ifdef CHECK_REPEATED_INDICES - // Check for repeated indicies - std::vector check(num_vars_, 0); - for (i_t p = 0; p < cut.i.size(); p++) { - if (check[cut.i[p]] != 0) { - printf("repeated index in generated cut\n"); - assert(check[cut.i[p]] == 0); + if (lj > -inf && lj != 0.0) { + // Use the lower bound + // lj <= x_j <= uj + // v_j = x_j - lj, + // 0 <= v_j <= uj - lj + transformed_upper_[j] = uj - lj; + transformed_xstar[j] = xstar_j - lj; + bound_changed_[j] = -1; } - check[cut.i[p]] = 1; } -#endif - - if (cut.i.size() == 0) { return -1; } - - return 0; } template -i_t mixed_integer_rounding_cut_t::generate_cut( - const sparse_vector_t& a, - f_t beta, - const std::vector& upper_bounds, - const std::vector& lower_bounds, - const std::vector& var_types, - sparse_vector_t& cut, - f_t& cut_rhs) +void complemented_mixed_integer_rounding_cut_t::transform_inequality( + const variable_bounds_t& variable_bounds, + const std::vector& var_type, + inequality_t& inequality) { -#ifdef CHECK_WORKSPACE - for (i_t j = 0; j < x_workspace_.size(); j++) { - if (x_workspace_[j] != 0.0) { - printf("Before generate_cut: Dirty x_workspace_[%d] = %e\n", j, x_workspace_[j]); - printf("num_vars_ %d\n", num_vars_); - printf("x_workspace_.size() %ld\n", x_workspace_.size()); - assert(x_workspace_[j] == 0.0); - } - if (x_mark_[j] != 0) { - printf("Before generate_cut: Dirty x_mark_[%d] = %d\n", j, x_mark_[j]); - assert(x_mark_[j] == 0); + const i_t nz = inequality.size(); + for (i_t k = 0; k < nz; k++) { + const i_t j = inequality.index(k); + const f_t aj = inequality.coeff(k); + if (var_type[j] != variable_type_t::CONTINUOUS) { + scratch_pad_.add_to_pad(j, aj); + continue; } - } -#endif - - auto f = [](f_t q_1, f_t q_2) -> f_t { - f_t q_1_hat = q_1 - std::floor(q_1); - f_t q_2_hat = q_2 - std::floor(q_2); - return std::min(q_1_hat, q_2_hat) + q_2_hat * std::floor(q_1); - }; - - auto h = [](f_t q) -> f_t { return std::max(q, 0.0); }; - - std::vector cut_indices; - cut_indices.reserve(a.i.size()); - f_t R; - if (!needs_complement_) { - R = (beta - std::floor(beta)) * std::ceil(beta); - - for (i_t k = 0; k < a.i.size(); k++) { - const i_t jj = a.i[k]; - f_t aj = a.x[k]; - if (var_types[jj] == variable_type_t::INTEGER) { - x_workspace_[jj] += f(aj, beta); - if (!x_mark_[jj] && x_workspace_[jj] != 0.0) { - x_mark_[jj] = 1; - cut_indices.push_back(jj); - } + if (bound_changed_[j] == -1) { + if (lb_variable_[j] == -1) { + // v_j = x_j - l_j, v_j >= 0 + // x_j = v_j + l_j + // sum_{k != j} a_k x_k + a_j x_j >= beta + // sum_{k != j} a_k x_k + a_j (v_j + l_j) >= beta + // sum_{k != j} a_k x_k + a_j v_j >= beta - a_j l_j + const f_t lj = lb_star_[j]; + inequality.rhs -= aj * lj; + scratch_pad_.add_to_pad(j, aj); } else { - x_workspace_[jj] += h(aj); - if (!x_mark_[jj] && x_workspace_[jj] != 0.0) { - x_mark_[jj] = 1; - cut_indices.push_back(jj); - } - } - } - } else { - // Compute r - f_t r = beta; - for (i_t k = 0; k < a.i.size(); k++) { - const i_t jj = a.i[k]; - if (has_upper_[jj]) { - const f_t uj = upper_bounds[jj]; - r -= uj * a.x[k]; - continue; - } - if (has_lower_[jj]) { - const f_t lj = lower_bounds[jj]; - r -= lj * a.x[k]; - } - } - - // Compute R - R = std::ceil(r) * (r - std::floor(r)); - for (i_t k = 0; k < a.i.size(); k++) { - const i_t jj = a.i[k]; - const f_t aj = a.x[k]; - if (has_upper_[jj]) { - const f_t uj = upper_bounds[jj]; - if (var_types[jj] == variable_type_t::INTEGER) { - R -= f(-aj, r) * uj; - } else { - R -= h(-aj) * uj; - } - } else if (has_lower_[jj]) { - const f_t lj = lower_bounds[jj]; - if (var_types[jj] == variable_type_t::INTEGER) { - R += f(aj, r) * lj; - } else { - R += h(aj) * lj; - } + // v_j = x_j - lb*_j, v_j >= 0 + // x_j = v_j + lb*_j + // lb*_j = gamma * x_i + alpha + // x_j = v_j + gamma * x_i + alpha + // sum_{k != j} a_k x_k + a_j x_j >= beta + // sum_{k != j} a_k x_k + a_j (v_j + gamma * x_i + alpha) >= beta + // sum_{k != j} a_k x_k + a_j v_j + a_j * gamma * x_i >= beta - a_j alpha + const i_t p = lb_variable_[j]; + const f_t alpha = variable_bounds.lower_biases[p]; + const f_t gamma = variable_bounds.lower_weights[p]; + const i_t i = variable_bounds.lower_variables[p]; + inequality.rhs -= aj * alpha; + scratch_pad_.add_to_pad(j, aj); + scratch_pad_.add_to_pad(i, aj * gamma); } - } - - // Compute the cut coefficients - for (i_t k = 0; k < a.i.size(); k++) { - const i_t jj = a.i[k]; - const f_t aj = a.x[k]; - if (has_upper_[jj]) { - if (var_types[jj] == variable_type_t::INTEGER) { - // Upper intersect I - x_workspace_[jj] -= f(-aj, r); - if (!x_mark_[jj] && x_workspace_[jj] != 0.0) { - x_mark_[jj] = 1; - cut_indices.push_back(jj); - } - } else { - // Upper intersect C - f_t h_j = h(-aj); - if (h_j != 0.0) { - x_workspace_[jj] -= h_j; - if (!x_mark_[jj]) { - x_mark_[jj] = 1; - cut_indices.push_back(jj); - } - } - } - } else if (var_types[jj] == variable_type_t::INTEGER) { - // I \ Upper - x_workspace_[jj] += f(aj, r); - if (!x_mark_[jj] && x_workspace_[jj] != 0.0) { - x_mark_[jj] = 1; - cut_indices.push_back(jj); - } + } else if (bound_changed_[j] == 1) { + if (ub_variable_[j] == -1) { + // w_j = u_j - x_j, w_j >= 0 + // x_j = u_j - w_j + // sum_{k != j} a_k x_k + a_j x_j >= beta + // sum_{k != j} a_k x_k + a_j (u_j - w_j) >= beta + // sum_{k != j} a_k x_k - a_j w_j >= beta - a_j u_j + const f_t uj = ub_star_[j]; + inequality.rhs -= aj * uj; + scratch_pad_.add_to_pad(j, -aj); } else { - // C \ Upper - f_t h_j = h(aj); - if (h_j != 0.0) { - x_workspace_[jj] += h_j; - if (!x_mark_[jj]) { - x_mark_[jj] = 1; - cut_indices.push_back(jj); - } - } + // w_j = ub*_j - x_j, w_j >= 0 + // x_j = ub*_j - w_j + // ub*_j = gamma * x_i + alpha + // x_j = gamma * x_i + alpha - w_j + // sum_{k != j} a_k x_k + a_j x_j >= beta + // sum_{k != j} a_k x_k + a_j (ub*_j - w_j) >= beta + // sum_{k != j} a_k x_k + a_j (gamma * x_i + alpha) - a_j w_j >= beta + // sum_{k != j} a_k x_k + a_j gamma * x_i - a_j w_j >= beta - a_j alpha + const i_t p = ub_variable_[j]; + const f_t alpha = variable_bounds.upper_biases[p]; + const f_t gamma = variable_bounds.upper_weights[p]; + const i_t i = variable_bounds.upper_variables[p]; + inequality.rhs -= aj * alpha; + scratch_pad_.add_to_pad(j, -aj); + scratch_pad_.add_to_pad(i, aj * gamma); } + } else if (bound_changed_[j] == 0) { + scratch_pad_.add_to_pad(j, aj); } } + scratch_pad_.get_pad(inequality.vector.i, inequality.vector.x); + // At this point we have converted all the continuous variables to be nonnegative + // Note that since continuous variables had VUB or VLB, they modified + // the integer variables. - cut.i.reserve(cut_indices.size()); - cut.x.reserve(cut_indices.size()); - cut.i.clear(); - cut.x.clear(); - for (i_t k = 0; k < cut_indices.size(); k++) { - const i_t jj = cut_indices[k]; + // We clear the scratch pad. As it is no longer needed. + scratch_pad_.clear_pad(); - // Check for small coefficients - const f_t aj = x_workspace_[jj]; - if (std::abs(aj) < 1e-6) { - if (aj >= 0.0 && upper_bounds[jj] < inf) { - // Move this to the right-hand side - R -= aj * upper_bounds[jj]; - continue; - } else if (aj <= 0.0 && lower_bounds[jj] > -inf) { - R += aj * lower_bounds[jj]; - continue; - } else { - } + // We now convert all the integer variables to be nonnegative + const i_t nz_after = inequality.size(); + for (i_t k = 0; k < nz_after; k++) { + const i_t j = inequality.index(k); + if (var_type[j] != variable_type_t::INTEGER) { continue; } + const f_t aj = inequality.coeff(k); + if (bound_changed_[j] == -1) { + // v_j = x_j - l_j, v_j >= 0 + // x_j = v_j + l_j + // sum_{k != j} a_k x_k + a_j x_j >= beta + // sum_{k != j} a_k x_k + a_j (v_j + l_j) >= beta + // sum_{k != j} a_k x_k + a_j v_j >= beta - a_j l_j + const f_t lj = lb_star_[j]; + inequality.rhs -= aj * lj; + } else if (bound_changed_[j] == 1) { + // w_j = u_j - x_j, w_j >= 0 + // x_j = u_j - w_j + // sum_{k != j} a_k x_j + a_j x_j >= beta + // sum_{k != j} a_k x_j + a_j (u_j - w_j) >= beta + // sum_{k != j} a_k x_j - a_j w_j >= beta - a_j u_j + const f_t uj = ub_star_[j]; + inequality.rhs -= aj * uj; + inequality.vector.x[k] *= -1.0; } - cut.i.push_back(jj); - cut.x.push_back(x_workspace_[jj]); } +} - // Clear the workspace - for (i_t jj : cut_indices) { - x_workspace_[jj] = 0.0; - x_mark_[jj] = 0; +template +void complemented_mixed_integer_rounding_cut_t::untransform_inequality( + const variable_bounds_t& variable_bounds, + const std::vector& var_type, + inequality_t& inequality) +{ + // First convert all the integers variables back to their original form: l_j <= x_j <= u_j + const i_t nz = inequality.size(); + for (i_t k = 0; k < nz; k++) { + const i_t j = inequality.index(k); + if (var_type[j] != variable_type_t::INTEGER) { continue; } + const f_t dj = inequality.coeff(k); + if (bound_changed_[j] == -1) { + // v_j = x_j - l_j, v_j >= 0 + // sum_{k != j} d_k x_k + d_j v_j >= beta + // sum_{k != j} d_k x_k + d_j (x_j - l_j) >= beta + // sum_{k != j} d_k x_k + d_j x_j >= beta + d_j l_j + const f_t lj = lb_star_[j]; + inequality.rhs += dj * lj; + } else if (bound_changed_[j] == 1) { + // w_j = u_j - x_j, w_j >= 0 + // sum_{k != j} d_k x_k + d_j w_j >= beta + // sum_{k != j} d_k x_k + d_j (u_j - x_j) >= beta + // sum_{k != j} d_k x_k - d_j x_j >= beta - d_j u_j + const f_t uj = ub_star_[j]; + inequality.rhs -= dj * uj; + inequality.vector.x[k] *= -1.0; + } } - -#ifdef CHECK_WORKSPACE - for (i_t j = 0; j < x_workspace_.size(); j++) { - if (x_workspace_[j] != 0.0) { - printf("After generate_cut: Dirty x_workspace_[%d] = %e\n", j, x_workspace_[j]); - assert(x_workspace_[j] == 0.0); + // Then undo the VUB/VLB substitions and bring continuous variables back to their original form + for (i_t k = 0; k < nz; k++) { + const i_t j = inequality.index(k); + const f_t dj = inequality.coeff(k); + if (var_type[j] != variable_type_t::CONTINUOUS) { + scratch_pad_.add_to_pad(j, dj); + continue; } - if (x_mark_[j] != 0) { - printf("After generate_cut: Dirty x_mark_[%d] = %d\n", j, x_mark_[j]); - assert(x_mark_[j] == 0); + if (bound_changed_[j] == -1) { + if (lb_variable_[j] == -1) { + // v_j = x_j - l_j, v_j >= 0 + // sum_{k != j} d_k x_k + d_j v_j >= beta + // sum_{k != j} d_k x_k + d_j (x_j - l_j) >= beta + // sum_{k != j} d_k x_k + d_j x_j >= beta + d_j l_j + const f_t lj = lb_star_[j]; + inequality.rhs += dj * lj; + scratch_pad_.add_to_pad(j, dj); + } else { + // v_j = x_j - lb*_j, v_j >= 0 + // lb*_j = gamma * x_i + alpha + // v_j = x_j - gamma * x_i - alpha + // sum_{k != j} d_k x_k + d_j v_j >= beta + // sum_{k != j} d_k x_k + d_j (x_j - gamma * x_i - alpha) >= beta + // sum_{k != j} d_k x_k + d_j x_j - d_j * gamma * x_i >= beta + d_j alpha + const i_t p = lb_variable_[j]; + const f_t alpha = variable_bounds.lower_biases[p]; + const f_t gamma = variable_bounds.lower_weights[p]; + const i_t i = variable_bounds.lower_variables[p]; + inequality.rhs += dj * alpha; + scratch_pad_.add_to_pad(j, dj); + scratch_pad_.add_to_pad(i, -dj * gamma); + } + } else if (bound_changed_[j] == 1) { + if (ub_variable_[j] == -1) { + // w_j = u_j - x_j, w_j >= 0 + // sum_{k != j} d_k x_k + d_j w_j >= beta + // sum_{k != j} d_k x_k + d_j (u_j - x_j) >= beta + // sum_{k != j} d_k x_k - d_j x_j >= beta - d_j u_j + const f_t uj = ub_star_[j]; + inequality.rhs -= dj * uj; + scratch_pad_.add_to_pad(j, -dj); + } else { + // w_j = ub*_j - x_j, w_j >= 0 + // ub*_j = gamma * x_i + alpha + // w_j = gamma * x_i + alpha - x_j + // sum_{k != j} d_k x_k + d_j w_j >= beta + // sum_{k != j} d_k x_k + d_j (gamma * x_i + alpha - x_j) >= beta + // sum_{k != j} d_k x_k + d_j gamma * x_i - d_j x_j >= beta - d_j alpha + const i_t p = ub_variable_[j]; + const f_t alpha = variable_bounds.upper_biases[p]; + const f_t gamma = variable_bounds.upper_weights[p]; + const i_t i = variable_bounds.upper_variables[p]; + inequality.rhs -= dj * alpha; + scratch_pad_.add_to_pad(j, -dj); + scratch_pad_.add_to_pad(i, dj * gamma); + } + } else { + scratch_pad_.add_to_pad(j, dj); } } -#endif - // The new cut is: g'*x >= R - // But we want to have it in the form h'*x <= b - cut.sort(); + scratch_pad_.get_pad(inequality.vector.i, inequality.vector.x); + scratch_pad_.clear_pad(); +} + +template +bool complemented_mixed_integer_rounding_cut_t:: + generate_cut_nonnegative_maintain_indicies(const inequality_t& inequality, + const std::vector& var_types, + inequality_t& cut) +{ + auto f = [](f_t q_1, f_t q_2) -> f_t { + f_t q_1_hat = q_1 - std::floor(q_1); + f_t q_2_hat = q_2 - std::floor(q_2); + return std::min(q_1_hat, q_2_hat) + q_2_hat * std::floor(q_1); + }; + + auto h = [](f_t q) -> f_t { return std::max(q, 0.0); }; - cut_rhs = R; + cut.vector = inequality.vector; + const f_t beta = inequality.rhs; + const f_t f_beta = fractional_part(beta); + cut.rhs = f_beta * std::ceil(beta); + if (f_beta < 0.05 || f_beta > 0.95) { return false; } -#ifdef CHECK_REPEATED_INDICES - // Check for repeated indicies - std::vector check(num_vars_, 0); - for (i_t p = 0; p < cut.i.size(); p++) { - if (check[cut.i[p]] != 0) { - printf("repeated index in generated cut\n"); - assert(check[cut.i[p]] == 0); + for (i_t k = 0; k < inequality.size(); k++) { + const i_t j = inequality.index(k); + f_t aj = inequality.coeff(k); + if (var_types[j] == variable_type_t::INTEGER) { + cut.vector.x[k] = f(aj, beta); + } else { + cut.vector.x[k] = h(aj); + } + if (cut.vector.x[k] != cut.vector.x[k]) { + printf("cut.x[%d] %e != cut.x[%d] %e. aj %e beta %e var type %d\n", + k, + cut.vector.x[k], + k, + cut.vector.x[k], + aj, + beta, + static_cast(var_types[j])); + exit(1); } - check[cut.i[p]] = 1; } -#endif - if (cut.i.size() == 0) { return -1; } + return true; +} - return 0; +template +f_t complemented_mixed_integer_rounding_cut_t::compute_violation( + const inequality_t& cut, const std::vector& xstar) +{ + f_t dot = cut.vector.dot(xstar); + f_t cut_violation = cut.rhs - dot; + return cut_violation; } template -void mixed_integer_rounding_cut_t::substitute_slacks(const lp_problem_t& lp, - csr_matrix_t& Arow, - sparse_vector_t& cut, - f_t& cut_rhs) +void complemented_mixed_integer_rounding_cut_t::substitute_slacks( + const lp_problem_t& lp, csr_matrix_t& Arow, inequality_t& cut) { // Remove slacks from the cut // So that the cut is only over the original variables bool found_slack = false; i_t cut_nz = 0; std::vector cut_indices; - cut_indices.reserve(cut.i.size()); + cut_indices.reserve(cut.size()); -#ifdef CHECK_WORKSPACE - for (i_t j = 0; j < x_workspace_.size(); j++) { - if (x_workspace_[j] != 0.0) { - printf("Begin Dirty x_workspace_[%d] = %e\n", j, x_workspace_[j]); - assert(x_workspace_[j] == 0.0); - } - if (x_mark_[j] != 0) { - printf("Begin Dirty x_mark_[%d] = %d\n", j, x_mark_[j]); - assert(x_mark_[j] == 0); - } - } -#endif - - for (i_t k = 0; k < cut.i.size(); k++) { - const i_t j = cut.i[k]; - const f_t cj = cut.x[k]; + for (i_t k = 0; k < cut.size(); k++) { + const i_t j = cut.index(k); + const f_t cj = cut.coeff(k); if (is_slack_[j]) { found_slack = true; const i_t slack_start = lp.A.col_start[j]; @@ -2531,198 +3108,97 @@ void mixed_integer_rounding_cut_t::substitute_slacks(const lp_problem_ // sum_{k != j} C(k) * x_k + sum_{h != j} -C(j)/alpha * A(i, h) * x_h >= cut_rhs - C(j)/alpha // * rhs_i const i_t i = slack_rows_[j]; - cut_rhs -= cj * lp.rhs[i] / alpha; + cut.rhs -= cj * lp.rhs[i] / alpha; const i_t row_start = Arow.row_start[i]; const i_t row_end = Arow.row_start[i + 1]; for (i_t q = row_start; q < row_end; q++) { const i_t h = Arow.j[q]; if (h != j) { const f_t aih = Arow.x[q]; - x_workspace_[h] -= cj * aih / alpha; - if (!x_mark_[h]) { - x_mark_[h] = 1; - cut_indices.push_back(h); - cut_nz++; - } + scratch_pad_.add_to_pad(h, -cj * aih / alpha); } else { const f_t aij = Arow.x[q]; if (std::abs(aij) != 1.0) { - settings_.log.printf( - "Slack row %d has non-unit coefficient %e for variable %d\n", i, aij, j); + printf("Slack row %d has non-unit coefficient %e for variable %d\n", i, aij, j); assert(std::abs(aij) == 1.0); } } } } else { - x_workspace_[j] += cj; - if (!x_mark_[j]) { - x_mark_[j] = 1; - cut_indices.push_back(j); - cut_nz++; - } + scratch_pad_.add_to_pad(j, cj); } } if (found_slack) { - cut.i.reserve(cut_nz); - cut.x.reserve(cut_nz); - cut.i.clear(); - cut.x.clear(); - - for (i_t k = 0; k < cut_nz; k++) { - const i_t j = cut_indices[k]; - - // Check for small coefficients - const f_t aj = x_workspace_[j]; - if (std::abs(aj) < 1e-6) { - if (aj >= 0.0 && lp.upper[j] < inf) { - // Move this to the right-hand side - cut_rhs -= aj * lp.upper[j]; - continue; - } else if (aj <= 0.0 && lp.lower[j] > -inf) { - cut_rhs += aj * lp.lower[j]; - continue; - } else { - } - } - - cut.i.push_back(j); - cut.x.push_back(x_workspace_[j]); - } + scratch_pad_.get_pad(cut.vector.i, cut.vector.x); // Sort the cut cut.sort(); } // Clear the workspace - for (i_t jj : cut_indices) { - x_workspace_[jj] = 0.0; - x_mark_[jj] = 0; - } - -#ifdef CHECK_WORKSPACE - for (i_t j = 0; j < x_workspace_.size(); j++) { - if (x_workspace_[j] != 0.0) { - printf("End Dirty x_workspace_[%d] = %e\n", j, x_workspace_[j]); - assert(x_workspace_[j] == 0.0); - } - if (x_mark_[j] != 0) { - printf("End Dirty x_mark_[%d] = %d\n", j, x_mark_[j]); - assert(x_mark_[j] == 0); - } - } -#endif -} - -template -f_t mixed_integer_rounding_cut_t::compute_violation(const sparse_vector_t& cut, - f_t cut_rhs, - const std::vector& xstar) -{ - f_t dot = cut.dot(xstar); - f_t cut_violation = cut_rhs - dot; - return cut_violation; + scratch_pad_.clear_pad(); } template -void mixed_integer_rounding_cut_t::combine_rows( +f_t complemented_mixed_integer_rounding_cut_t::combine_rows( const lp_problem_t& lp, csr_matrix_t& Arow, i_t xj, - const sparse_vector_t& pivot_row, - f_t pivot_row_rhs, - sparse_vector_t& inequality, - f_t& inequality_rhs) + const inequality_t& pivot_row, + inequality_t& inequality) { -#ifdef CHECK_WORKSPACE - for (i_t k = 0; k < x_workspace_.size(); k++) { - if (x_workspace_[k] != 0.0) { - printf("Dirty x_workspace_[%d] = %e\n", k, x_workspace_[k]); - assert(x_workspace_[k] == 0.0); - } - if (x_mark_[k] != 0) { - printf("Dirty x_mark_[%d] = %d\n", k, x_mark_[k]); - assert(x_mark_[k] == 0); - } - } -#endif - - indices_.clear(); - indices_.reserve(pivot_row.i.size() + inequality.i.size()); - // Find the coefficient associated with variable xj in the pivot row f_t a_l_j = 0.0; - for (i_t k = 0; k < pivot_row.i.size(); k++) { - const i_t j = pivot_row.i[k]; + for (i_t k = 0; k < pivot_row.size(); k++) { + const i_t j = pivot_row.index(k); if (j == xj) { - a_l_j = pivot_row.x[k]; + a_l_j = pivot_row.coeff(k); break; } } - if (a_l_j == 0) { return; } + if (a_l_j == 0) { + printf("Pivot row has no coefficient for variable %d\n", xj); + return 0.0; + } f_t a_i_j = 0.0; - i_t nz = 0; // Store the inequality in the workspace // and save the coefficient associated with variable xj - for (i_t k = 0; k < inequality.i.size(); k++) { - const i_t j = inequality.i[k]; + for (i_t k = 0; k < inequality.size(); k++) { + const i_t j = inequality.index(k); if (j != xj) { - x_workspace_[j] = inequality.x[k]; - x_mark_[j] = 1; - indices_.push_back(j); - nz++; + scratch_pad_.add_to_pad(j, inequality.coeff(k)); } else { - a_i_j = inequality.x[k]; + a_i_j = inequality.coeff(k); } } + if (a_i_j == 0.0) { + printf("Inequality has zero coefficient for variable %d\n", xj); + scratch_pad_.clear_pad(); + return 0.0; + } f_t pivot_value = a_i_j / a_l_j; // Adjust the rhs of the inequality - inequality_rhs -= pivot_value * pivot_row_rhs; + inequality.rhs -= pivot_value * pivot_row.rhs; // Adjust the coefficients of the inequality // based on the nonzeros in the pivot row - for (i_t k = 0; k < pivot_row.i.size(); k++) { - const i_t j = pivot_row.i[k]; - if (j != xj) { - x_workspace_[j] -= pivot_value * pivot_row.x[k]; - if (!x_mark_[j]) { - x_mark_[j] = 1; - indices_.push_back(j); - nz++; - } - } + for (i_t k = 0; k < pivot_row.size(); k++) { + const i_t j = pivot_row.index(k); + if (j != xj) { scratch_pad_.add_to_pad(j, -pivot_value * pivot_row.coeff(k)); } } // Store the new inequality - inequality.i.resize(nz); - inequality.x.resize(nz); - for (i_t k = 0; k < nz; k++) { - inequality.i[k] = indices_[k]; - inequality.x[k] = x_workspace_[indices_[k]]; - } - -#ifdef CHECK_REPEATED_INDICES - // Check for repeated indices - std::vector check(num_vars_, 0); - for (i_t k = 0; k < inequality.i.size(); k++) { - if (check[inequality.i[k]] == 1) { - printf("repeated index\n"); - assert(check[inequality.i[k]] == 0); - } - check[inequality.i[k]] = 1; - } -#endif + scratch_pad_.get_pad(inequality.vector.i, inequality.vector.x); // Clear the workspace - for (i_t j : indices_) { - x_workspace_[j] = 0.0; - x_mark_[j] = 0; - } - indices_.clear(); + scratch_pad_.clear_pad(); + + return -pivot_value; } template @@ -2758,15 +3234,14 @@ i_t strong_cg_cut_t::remove_continuous_variables_integers_nonnegative( const lp_problem_t& lp, const simplex_solver_settings_t& settings, const std::vector& var_types, - sparse_vector_t& inequality, - f_t& inequality_rhs) + inequality_t& inequality) { const bool verbose = false; // Count the number of continuous variables in the inequality i_t num_continuous = 0; - const i_t nz = inequality.i.size(); + const i_t nz = inequality.size(); for (i_t k = 0; k < nz; k++) { - const i_t j = inequality.i[k]; + const i_t j = inequality.index(k); if (var_types[j] == variable_type_t::CONTINUOUS) { num_continuous++; } } @@ -2774,10 +3249,10 @@ i_t strong_cg_cut_t::remove_continuous_variables_integers_nonnegative( // We assume the inequality is of the form sum_j a_j x_j <= rhs for (i_t k = 0; k < nz; k++) { - const i_t j = inequality.i[k]; + const i_t j = inequality.index(k); const f_t l_j = lp.lower[j]; const f_t u_j = lp.upper[j]; - const f_t a_j = inequality.x[k]; + const f_t a_j = inequality.coeff(k); if (var_types[j] == variable_type_t::CONTINUOUS) { if (a_j == 0.0) { continue; } @@ -2787,13 +3262,13 @@ i_t strong_cg_cut_t::remove_continuous_variables_integers_nonnegative( // sum_{k != j} a_k x_k + a_j x_j <= rhs // sum_{k != j} a_k x_k + a_j (v_j + l_j) <= rhs // sum_{k != j} a_k x_k + a_j v_j <= rhs - a_j l_j - inequality_rhs -= a_j * l_j; + inequality.rhs -= a_j * l_j; transformed_variables_[j] = -1; // We now have a_j * v_j with a_j, v_j >= 0 // So we have sum_{k != j} a_k x_k <= sum_{k != j} a_k x_k + a_j v_j <= rhs - a_j l_j // So we can now drop the continuous variable v_j - inequality.x[k] = 0.0; + inequality.vector.x[k] = 0.0; } else if (a_j < 0.0 && u_j < inf) { // w_j = u_j - x_j >= 0 @@ -2801,13 +3276,13 @@ i_t strong_cg_cut_t::remove_continuous_variables_integers_nonnegative( // sum_{k != j} a_k x_k + a_j x_j <= rhs // sum_{k != j} a_k x_k + a_j (u_j - w_j) <= rhs // sum_{k != j} a_k x_k - a_j w_j <= rhs - a_j u_j - inequality_rhs -= a_j * u_j; + inequality.rhs -= a_j * u_j; transformed_variables_[j] = 1; // We now have a_j * w_j with a_j, w_j >= 0 // So we have sum_{k != j} a_k x_k <= sum_{k != j} a_k x_k + a_j w_j <= rhs - a_j u_j // So we can now drop the continuous variable w_j - inequality.x[k] = 0.0; + inequality.vector.x[k] = 0.0; } else { // We can't keep the coefficient of the continuous variable positive // This means we can't eliminate the continuous variable @@ -2823,7 +3298,7 @@ i_t strong_cg_cut_t::remove_continuous_variables_integers_nonnegative( // sum_{k != j} a_k x_k + a_j x_j <= rhs // sum_{k != j} a_k x_k + a_j (v_j + l_j) <= rhs // sum_{k != j} a_k x_k + a_j v_j <= rhs - a_j l_j - inequality_rhs -= a_j * l_j; + inequality.rhs -= a_j * l_j; } else if (transformed_variables_[j] == 1) { // We are closer to the finite upper bound // w_j = u_j - x_j >= 0 @@ -2831,44 +3306,43 @@ i_t strong_cg_cut_t::remove_continuous_variables_integers_nonnegative( // sum_{k != j} a_k x_k + a_j x_j <= rhs // sum_{k != j} a_k x_k + a_j (u_j - w_j) <= rhs // sum_{k != j} a_k x_k - a_j w_j <= rhs - a_j u_j - inequality_rhs -= a_j * u_j; - inequality.x[k] *= -1.0; + inequality.rhs -= a_j * u_j; + inequality.vector.x[k] *= -1.0; } } } // Squeeze out the zero coefficents - sparse_vector_t new_inequality(inequality.n, 0); - inequality.squeeze(new_inequality); - inequality = new_inequality; + sparse_vector_t new_inequality_vector(inequality.vector.n, 0); + inequality.vector.squeeze(new_inequality_vector); + inequality.vector = new_inequality_vector; return 0; } template void strong_cg_cut_t::to_original_integer_variables(const lp_problem_t& lp, - sparse_vector_t& cut, - f_t& cut_rhs) + inequality_t& cut) { // We expect a cut of the form sum_j a_j y_j <= rhs // where y_j >= 0 is a transformed variable // We need to convert it back into a cut on the original variables - for (i_t k = 0; k < cut.i.size(); k++) { - const i_t j = cut.i[k]; - const f_t a_j = cut.x[k]; + for (i_t k = 0; k < cut.size(); k++) { + const i_t j = cut.index(k); + const f_t a_j = cut.coeff(k); if (transformed_variables_[j] == -1) { // sum_{k != j} a_k x_k + a_j v_j <= rhs // v_j = x_j - l_j >= 0, // sum_{k != j} a_k x_k + a_j (x_j - l_j) <= rhs // sum_{k != j} a_k x_k + a_j x_j <= rhs + a_j l_j - cut_rhs += a_j * lp.lower[j]; + cut.rhs += a_j * lp.lower[j]; } else if (transformed_variables_[j] == 1) { // sum_{k != j} a_k x_k + a_j w_j <= rhs // w_j = u_j - x_j >= 0 // sum_{k != j} a_k x_k + a_j (u_j - x_j) <= rhs // sum_{k != j} a_k x_k - a_j x_j <= rhs - a_j u_j - cut_rhs -= a_j * lp.upper[j]; - cut.x[k] *= -1.0; + cut.rhs -= a_j * lp.upper[j]; + cut.vector.x[k] *= -1.0; } } } @@ -2877,44 +3351,37 @@ template i_t strong_cg_cut_t::generate_strong_cg_cut_integer_only( const simplex_solver_settings_t& settings, const std::vector& var_types, - const sparse_vector_t& inequality, - f_t inequality_rhs, - sparse_vector_t& cut, - f_t& cut_rhs) + const inequality_t& inequality, + inequality_t& cut) { // We expect an inequality of the form sum_j a_j x_j <= rhs // where all the variables x_j are integer and nonnegative // We then apply the CG cut: // sum_j floor(a_j) x_j <= floor(rhs) - cut.i.reserve(inequality.i.size()); - cut.x.reserve(inequality.i.size()); - cut.i.clear(); - cut.x.clear(); + cut.reserve(inequality.size()); + cut.clear(); - f_t a_0 = inequality_rhs; + f_t a_0 = inequality.rhs; f_t f_a_0 = fractional_part(a_0); if (f_a_0 == 0.0) { // f(a_0) == 0.0 so we do a weak CG cut - cut.i.reserve(inequality.i.size()); - cut.x.reserve(inequality.i.size()); - cut.i.clear(); - cut.x.clear(); - for (i_t k = 0; k < inequality.i.size(); k++) { - const i_t j = inequality.i[k]; - const f_t a_j = inequality.x[k]; + cut.reserve(inequality.size()); + cut.clear(); + for (i_t k = 0; k < inequality.size(); k++) { + const i_t j = inequality.index(k); + const f_t a_j = inequality.coeff(k); if (var_types[j] == variable_type_t::INTEGER) { - cut.i.push_back(j); - cut.x.push_back(std::floor(a_j)); + cut.push_back(j, std::floor(a_j)); } else { return -1; } } - cut_rhs = std::floor(inequality_rhs); + cut.rhs = std::floor(inequality.rhs); } else { return generate_strong_cg_cut_helper( - inequality.i, inequality.x, inequality_rhs, var_types, cut, cut_rhs); + inequality.vector.i, inequality.vector.x, inequality.rhs, var_types, cut); } return 0; } @@ -2925,8 +3392,7 @@ i_t strong_cg_cut_t::generate_strong_cg_cut_helper( const std::vector& coefficients, f_t rhs, const std::vector& var_types, - sparse_vector_t& cut, - f_t& cut_rhs) + inequality_t& cut) { const bool verbose = false; const i_t nz = indicies.size(); @@ -2947,10 +3413,8 @@ i_t strong_cg_cut_t::generate_strong_cg_cut_helper( f_t upper = 1.0 / static_cast(k); if (verbose) { printf("f_a_0 %e lower %e upper %e alpha %e\n", f_a_0, lower, upper, alpha); } if (f_a_0 >= lower && f_a_0 < upper) { - cut.i.reserve(nz); - cut.x.reserve(nz); - cut.i.clear(); - cut.x.clear(); + cut.reserve(nz); + cut.clear(); for (i_t q = 0; q < nz; q++) { const i_t j = indicies[q]; const f_t a_j = coefficients[q]; @@ -2958,8 +3422,7 @@ i_t strong_cg_cut_t::generate_strong_cg_cut_helper( const f_t f_a_j = fractional_part(a_j); const f_t tol = 1e-4; if (f_a_j <= f_a_0 + tol) { - cut.i.push_back(j); - cut.x.push_back((k + 1.0) * std::floor(a_j)); + cut.push_back(j, (k + 1.0) * std::floor(a_j)); if (verbose) { printf("j %d a_j %e f_a_j %e k %d\n", j, a_j, f_a_j, k); } } else { // Find p such that p <= k * f(a_j) < p + 1 @@ -2968,11 +3431,9 @@ i_t strong_cg_cut_t::generate_strong_cg_cut_helper( const f_t rhs_j = f_a_0 + static_cast(p) / static_cast(k) * alpha; const i_t coeff = (k + 1) * static_cast(std::floor(a_j)) + p; if (f_a_j > rhs_j + tol) { - cut.i.push_back(j); - cut.x.push_back(static_cast(coeff + 1)); + cut.push_back(j, static_cast(coeff + 1)); } else { - cut.i.push_back(j); - cut.x.push_back(static_cast(coeff)); + cut.push_back(j, static_cast(coeff)); } } } else { @@ -2983,11 +3444,11 @@ i_t strong_cg_cut_t::generate_strong_cg_cut_helper( if (verbose) { printf("Error: k %d lower %e f(a_0) %e upper %e\n", k, lower, f_a_0, upper); } return -1; } - cut_rhs = (k + 1.0) * std::floor(rhs); + cut.rhs = (k + 1.0) * std::floor(rhs); if (verbose) { - printf("Generated strong CG cut: k %d f_a_0 %e cut_rhs %e\n", k, f_a_0, cut_rhs); - for (i_t q = 0; q < cut.i.size(); q++) { - if (cut.x[q] != 0.0) { printf("%.16e x%d ", cut.x[q], cut.i[q]); } + printf("Generated strong CG cut: k %d f_a_0 %e cut_rhs %e\n", k, f_a_0, cut.rhs); + for (i_t q = 0; q < cut.size(); q++) { + if (cut.vector.x[q] != 0.0) { printf("%.16e x%d ", cut.vector.x[q], cut.vector.i[q]); } } printf("\n"); printf("Original inequality rhs %e nz %ld\n", rhs, coefficients.size()); @@ -3004,11 +3465,9 @@ i_t strong_cg_cut_t::generate_strong_cg_cut( const lp_problem_t& lp, const simplex_solver_settings_t& settings, const std::vector& var_types, - const sparse_vector_t& inequality, - const f_t inequality_rhs, + const inequality_t& inequality, const std::vector& xstar, - sparse_vector_t& cut, - f_t& cut_rhs) + inequality_t& cut) { #ifdef PRINT_INEQUALITY_INFO for (i_t k = 0; k < inequality.i.size(); k++) { @@ -3023,36 +3482,33 @@ i_t strong_cg_cut_t::generate_strong_cg_cut( // and transform integer variables to be nonnegative // Copy the inequality since remove continuous variables will modify it - sparse_vector_t cg_inequality = inequality; - f_t cg_inequality_rhs = inequality_rhs; - i_t status = remove_continuous_variables_integers_nonnegative( - lp, settings, var_types, cg_inequality, cg_inequality_rhs); + inequality_t cg_inequality = inequality; + i_t status = + remove_continuous_variables_integers_nonnegative(lp, settings, var_types, cg_inequality); if (status != 0) { // Try negating the equality and see if that helps cg_inequality = inequality; cg_inequality.negate(); - cg_inequality_rhs = -inequality_rhs; - status = remove_continuous_variables_integers_nonnegative( - lp, settings, var_types, cg_inequality, cg_inequality_rhs); + status = + remove_continuous_variables_integers_nonnegative(lp, settings, var_types, cg_inequality); } if (status == 0) { // We have an inequality with no continuous variables // Generate a CG cut - status = generate_strong_cg_cut_integer_only( - settings, var_types, cg_inequality, cg_inequality_rhs, cut, cut_rhs); + status = generate_strong_cg_cut_integer_only(settings, var_types, cg_inequality, cut); if (status != 0) { return -1; } // Convert the CG cut back to the original variables - to_original_integer_variables(lp, cut, cut_rhs); + to_original_integer_variables(lp, cut); // Check for violation - f_t dot = cut.dot(xstar); + f_t dot = cut.vector.dot(xstar); // If the cut is violated we will have: sum_j a_j xstar_j > rhs - f_t violation = dot - cut_rhs; + f_t violation = dot - cut.rhs; const f_t min_violation_threshold = 1e-6; if (violation > min_violation_threshold) { // Note that no slacks are currently present. Since slacks are currently treated as @@ -3061,7 +3517,6 @@ i_t strong_cg_cut_t::generate_strong_cg_cut( // The CG cut is in the form: sum_j a_j x_j <= rhs // The cut pool wants the cut in the form: sum_j a_j x_j >= rhs cut.negate(); - cut_rhs *= -1.0; return 0; } } @@ -3549,7 +4004,8 @@ template class cut_pool_t; template class cut_generation_t; template class knapsack_generation_t; template class tableau_equality_t; -template class mixed_integer_rounding_cut_t; +template class complemented_mixed_integer_rounding_cut_t; +template class variable_bounds_t; template int add_cuts(const simplex_solver_settings_t& settings, const csr_matrix_t& cuts, diff --git a/cpp/src/cuts/cuts.hpp b/cpp/src/cuts/cuts.hpp index fffa4b10fe..91806d81aa 100644 --- a/cpp/src/cuts/cuts.hpp +++ b/cpp/src/cuts/cuts.hpp @@ -62,6 +62,59 @@ cut_gap_closure_t compute_cut_gap_closure(f_t objective_reference, return {initial_gap, final_gap, gap_closed, gap_closed_ratio}; } +template +struct inequality_t { + inequality_t() : vector(), rhs(0.0) {} + inequality_t(i_t num_cols) : vector(num_cols, 0), rhs(0.0) {} + inequality_t(csr_matrix_t& A, i_t row, f_t rhs_value) : vector(A, row), rhs(rhs_value) + { + } + sparse_vector_t vector; + f_t rhs; + + void push_back(i_t j, f_t x) + { + vector.i.push_back(j); + vector.x.push_back(x); + } + void clear() + { + vector.i.clear(); + vector.x.clear(); + } + void reserve(size_t n) + { + vector.i.reserve(n); + vector.x.reserve(n); + } + size_t size() const { return vector.i.size(); } + i_t index(i_t k) const { return vector.i[k]; } + f_t coeff(i_t k) const { return vector.x[k]; } + void negate() + { + vector.negate(); + rhs *= -1.0; + } + void sort() { vector.sort(); } + void squeeze(inequality_t& out) const + { + vector.squeeze(out.vector); + out.rhs = rhs; + } + void scale(f_t factor) + { + vector.scale(factor); + rhs *= factor; + } + void print() const + { + for (i_t k = 0; k < size(); k++) { + printf("%g x%d ", coeff(k), index(k)); + } + printf("\nrhs %g\n", rhs); + } +}; + template struct cut_info_t { bool has_cuts() const @@ -103,10 +156,8 @@ void print_cut_types(const std::string& prefix, cut_info.record_cut_types(cut_types); settings.log.printf("%s: ", prefix.c_str()); for (i_t i = 0; i < MAX_CUT_TYPE; i++) { - settings.log.printf("%s cuts: %d ", cut_info.cut_type_names[i], cut_info.num_cuts[i]); - if (i < MAX_CUT_TYPE - 1) { settings.log.printf(", "); } + settings.log.printf("%s cuts: %d\n", cut_info.cut_type_names[i], cut_info.num_cuts[i]); } - settings.log.printf("\n"); } template @@ -189,7 +240,7 @@ class cut_pool_t { // Add a cut in the form: cut'*x >= rhs. // We expect that the cut is violated by the current relaxation xstar // cut'*xstart < rhs - void add_cut(cut_type_t cut_type, const sparse_vector_t& cut, f_t rhs); + void add_cut(cut_type_t cut_type, const inequality_t& cut); void score_cuts(std::vector& x_relax); @@ -225,6 +276,7 @@ class cut_pool_t { std::vector cut_orthogonality_; std::vector cut_scores_; std::vector best_cuts_; + const f_t min_cut_distance_{1e-4}; }; template @@ -243,8 +295,7 @@ class knapsack_generation_t { const std::vector& var_types, const std::vector& xstar, i_t knapsack_row, - sparse_vector_t& cut, - f_t& cut_rhs); + inequality_t& cut); i_t num_knapsack_constraints() const { return knapsack_constraints_.size(); } const std::vector& get_knapsack_constraints() const { return knapsack_constraints_; } @@ -267,10 +318,13 @@ class knapsack_generation_t { const simplex_solver_settings_t& settings_; }; -// Forward declaration +// Forward declarations template class mixed_integer_rounding_cut_t; +template +class variable_bounds_t; + template class cut_generation_t { public: @@ -301,9 +355,11 @@ class cut_generation_t { const std::vector& var_types, basis_update_mpf_t& basis_update, const std::vector& xstar, - const std::vector& reduced_costs, + const std::vector& ystar, + const std::vector& zstar, const std::vector& basic_list, const std::vector& nonbasic_list, + variable_bounds_t& variable_bounds, f_t start_time); private: @@ -324,7 +380,9 @@ class cut_generation_t { csr_matrix_t& Arow, const std::vector& new_slacks, const std::vector& var_types, - const std::vector& xstar); + const std::vector& xstar, + const std::vector& ystar, + variable_bounds_t& variable_bounds); // Generate all knapsack cuts void generate_knapsack_cuts(const lp_problem_t& lp, @@ -350,6 +408,77 @@ class cut_generation_t { std::atomic* signal_extend_{nullptr}; }; +template +class scratch_pad_t { + public: + scratch_pad_t(i_t num_vars) : workspace_(num_vars, 0.0), mark_(num_vars, 0) + { + indices_.reserve(num_vars); + } + + // O(1) to add a value to the pad + void add_to_pad(i_t j, f_t value) + { + workspace_[j] += value; + if (!mark_[j]) { + mark_[j] = 1; + indices_.push_back(j); + } + } + + // O(nz) to clear the pad + void clear_pad() + { + for (i_t j : indices_) { + workspace_[j] = 0.0; + mark_[j] = 0; + } + indices_.clear(); + } + + // O(nz) to get the pad + void get_pad(std::vector& indices, std::vector& values) + { + indices.reserve(indices_.size()); + values.reserve(indices_.size()); + indices.clear(); + values.clear(); + const i_t nz = indices_.size(); + for (i_t k = 0; k < nz; k++) { + const i_t j = indices_[k]; + const f_t val = workspace_[j]; + if (val != 0.0) { + indices.push_back(j); + values.push_back(val); + } + } + } + + private: + std::vector workspace_; + std::vector mark_; + std::vector indices_; +}; + +template +class mixed_integer_gomory_cut_t { + public: + mixed_integer_gomory_cut_t() {} + + bool rational_coefficients(const std::vector& var_types, + const inequality_t& input_inequality, + inequality_t& rational_inequality); + + private: + bool rational_approximation(f_t x, + int64_t max_denominator, + int64_t& numerator, + int64_t& denominator); + + int64_t gcd(const std::vector& integers); + int64_t lcm(const std::vector& integers); +}; + template class tableau_equality_t { public: @@ -378,8 +507,7 @@ class tableau_equality_t { const std::vector& basic_list, const std::vector& nonbasic_list, i_t i, - sparse_vector_t& inequality, - f_t& inequality_rhs); + inequality_t& inequality); private: std::vector b_bar_; @@ -390,93 +518,227 @@ class tableau_equality_t { }; template -class mixed_integer_rounding_cut_t { +class variable_bounds_t { public: - mixed_integer_rounding_cut_t(const lp_problem_t& lp, - const simplex_solver_settings_t& settings, - const std::vector& new_slacks, - const std::vector& xstar); + variable_bounds_t(const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + const std::vector& var_types, + const csr_matrix_t& Arow, + const std::vector& new_slacks); + + std::vector upper_offsets; + std::vector upper_variables; + std::vector upper_weights; + std::vector upper_biases; + + std::vector lower_offsets; + std::vector lower_variables; + std::vector lower_weights; + std::vector lower_biases; + + void resize(i_t new_num_cols) + { + const i_t current_upper_nz = upper_offsets.back(); + upper_offsets.resize(new_num_cols + 1, current_upper_nz); + const i_t current_lower_nz = lower_offsets.back(); + lower_offsets.resize(new_num_cols + 1, current_lower_nz); + } + + private: + f_t lower_activity(f_t lower_bound, f_t upper_bound, f_t coefficient) + { + return (coefficient > 0.0 ? lower_bound : upper_bound) * coefficient; + } + + f_t upper_activity(f_t lower_bound, f_t upper_bound, f_t coefficient) + { + return (coefficient > 0.0 ? upper_bound : lower_bound) * coefficient; + } + + // Returns the lower activity adjusted for the number of lower inf variables + // adjusted_lower_activity = { activity - lower_activity_i - lower_activity_j, if num_lower_inf = + // 0 + // { activity - lower_activity_i , if num_lower_inf = + // 1, lower_activity_j = -inf { activity - lower_activity_j , if + // num_lower_inf = 1, lower_activity_i != -inf { activity , if + // num_lower_inf = 2, lower_activity_i = lower_activity_j = -inf { -inf + // , if num_lower_inf > 2 + f_t adjusted_lower_activity(f_t activity, + i_t num_lower_inf, + f_t lower_activity_i, + f_t lower_activity_j) + { + if (num_lower_inf == 0) { + return activity - lower_activity_i - lower_activity_j; + } else if (num_lower_inf == 1 && lower_activity_j == -inf) { + return activity - lower_activity_i; + } else if (num_lower_inf == 1 && lower_activity_i == -inf) { + return activity - lower_activity_j; + } else if (num_lower_inf == 2 && lower_activity_i == -inf && lower_activity_j == -inf) { + return activity; + } else { + return -inf; + } + } + + // Returns the upper activity adjusted for the number of upper inf variables + // adjusted_upper_activity = { activity - upper_activity_i - upper_activity_j, if num_upper_inf = + // 0 + // { activity - upper_activity_i , if num_upper_inf = + // 1, upper_activity_j = inf { activity - upper_activity_j , if + // num_upper_inf = 1, upper_activity_i != inf { activity , if + // num_upper_inf = 2, upper_activity_i = upper_activity_j = inf { inf , + // if num_upper_inf > 2 + f_t adjusted_upper_activity(f_t activity, + i_t num_upper_inf, + f_t upper_activity_i, + f_t upper_activity_j) + { + if (num_upper_inf == 0) { + return activity - upper_activity_i - upper_activity_j; + } else if (num_upper_inf == 1 && upper_activity_j == inf) { + return activity - upper_activity_i; + } else if (num_upper_inf == 1 && upper_activity_i == inf) { + return activity - upper_activity_j; + } else if (num_upper_inf == 2 && upper_activity_i == inf && upper_activity_j == inf) { + return activity; + } else { + return inf; + } + } - // Convert an inequality of the form: sum_j a_j x_j >= beta + std::vector upper_activities_; + std::vector num_pos_inf_; + std::vector lower_activities_; + std::vector num_neg_inf_; + + std::vector slack_map_; +}; + +template +class complemented_mixed_integer_rounding_cut_t { + public: + complemented_mixed_integer_rounding_cut_t(const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + const std::vector& new_slacks); + + void compute_initial_scores_for_rows(const lp_problem_t& lp, + const simplex_solver_settings_t& settings, + const csr_matrix_t& Arow, + const std::vector& xstar, + const std::vector& ystar, + std::vector& score); + + // Perform bound substitution for the continuous variables using simple bounds + // and variable bounds. And bound substitution for the integer variables + // using simple bounds. + void bound_substitution(const lp_problem_t& lp, + const variable_bounds_t& variable_bounds, + const std::vector& var_types, + const std::vector& xstar, + std::vector& transformed_xstar); + + // Converts an inequality of the form: sum_j a_j x_j >= beta // with l_j <= x_j <= u_j into the form: // sum_{j not in L union U} d_j x_j + sum_{j in L} d_j v_j // + sum_{j in U} d_j w_j >= delta, // where v_j = x_j - l_j for j in L - // and w_j = u_j - x_j for j in Us - void to_nonnegative(const lp_problem_t& lp, - sparse_vector_t& inequality, - f_t& rhs); - - void relaxation_to_nonnegative(const lp_problem_t& lp, - const std::vector& xstar, - std::vector& xstar_nonnegative); + // and w_j = u_j - x_j for j in U + void transform_inequality(const variable_bounds_t& variable_bounds, + const std::vector& var_type, + inequality_t& inequality); - // Convert an inequality of the form: + // Converts an inequality of the form: // sum_{j not in L union U} d_j x_j + sum_{j in L} d_j v_j - // + sum_{j in U} d_j w_j >= delta + // + sum_{j in U} d_j w_j >= delta, // where v_j = x_j - l_j for j in L // and w_j = u_j - x_j for j in U - // back to an inequality on the original variables - // sum_j a_j x_j >= beta - void to_original(const lp_problem_t& lp, - sparse_vector_t& inequality, - f_t& rhs); + // back to the form: sum_j a_j x_j >= beta + // with l_j <= x_j <= u_j + void untransform_inequality(const variable_bounds_t& variable_bounds, + const std::vector& var_type, + inequality_t& inequality); + + bool cut_generation_heuristic(const inequality_t& transformed_inequality, + const std::vector& var_types, + const std::vector& transformed_xstar, + inequality_t& transformed_cut, + f_t& work_estimate); + + bool scale_uncomplement_and_generate_cut(const std::vector& var_types, + const std::vector& transformed_xstar, + const std::vector& complemented_indices, + const inequality_t& complemented_inequality, + f_t delta, + inequality_t& cut_delta, + f_t& work_estimate); + + // This routine takes an inequality and generates the MIR cut + bool generate_cut_nonnegative_maintain_indicies(const inequality_t& inequality, + const std::vector& var_types, + inequality_t& cut); + + f_t compute_violation(const inequality_t& cut, const std::vector& xstar); + + f_t new_upper(i_t j) const { return transformed_upper_[j]; } // Given a cut of the form sum_j d_j x_j >= beta // with l_j <= x_j <= u_j, try to remove coefficients d_j // with | d_j | < epsilon void remove_small_coefficients(const std::vector& lower_bounds, const std::vector& upper_bounds, - sparse_vector_t& cut, - f_t& cut_rhs); - - // Given an inequality sum_j a_j x_j >= beta, x_j >= 0, x_j in Z, j in I - // generate an MIR cut of the form sum_j d_j x_j >= delta - i_t generate_cut_nonnegative(const sparse_vector_t& a, - f_t beta, - const std::vector& var_types, - sparse_vector_t& cut, - f_t& cut_rhs); - - f_t compute_violation(const sparse_vector_t& cut, - f_t cut_rhs, - const std::vector& xstar); - - i_t generate_cut(const sparse_vector_t& a, - f_t beta, - const std::vector& upper_bounds, - const std::vector& lower_bounds, - const std::vector& var_types, - sparse_vector_t& cut, - f_t& cut_rhs); + inequality_t& cut); void substitute_slacks(const lp_problem_t& lp, csr_matrix_t& Arow, - sparse_vector_t& cut, - f_t& cut_rhs); + inequality_t& cut); // Combine the pivot row with the inequality to eliminate the variable j // The new inequality is returned in inequality and inequality_rhs - void combine_rows(const lp_problem_t& lp, - csr_matrix_t& Arow, - i_t j, - const sparse_vector_t& pivot_row, - f_t pivot_row_rhs, - sparse_vector_t& inequality, - f_t& inequality_rhs); + // The multiplier for the pivot row is returned + f_t combine_rows(const lp_problem_t& lp, + csr_matrix_t& Arow, + i_t j, + const inequality_t& pivot_row, + inequality_t& inequality); + + const f_t get_lb_star(i_t j) const { return lb_star_[j]; } + const f_t get_ub_star(i_t j) const { return ub_star_[j]; } + + const i_t slack_rows(i_t j) const { return slack_rows_[j]; } + const i_t slack_cols(i_t i) const { return slack_cols_[i]; } + + bool scale_and_generate_mir_cut(const std::vector& var_types, + const std::vector& transformed_xstar, + const inequality_t& inequality, + f_t divisor, + std::vector>& cuts, + std::vector& violations, + std::vector& deltas); + + bool check_violation_and_add_cut(const inequality_t& inequality, + const std::vector& xstar, + f_t divisor, + std::vector>& cuts, + std::vector& violations, + std::vector& deltas); private: - i_t num_vars_; - const simplex_solver_settings_t& settings_; - std::vector x_workspace_; - std::vector x_mark_; - std::vector has_lower_; - std::vector has_upper_; std::vector is_slack_; - std::vector slack_rows_; - std::vector indices_; - std::vector bound_info_; - bool needs_complement_; + std::vector + slack_rows_; // slack_rows_[j] = i, if variable j is slack for row i, -1 is sentinal value + std::vector + slack_cols_; // slack_cols_[i] = j, if variable j is slack for row i -1 is sentinal value + + std::vector lb_variable_; + std::vector lb_star_; + std::vector ub_variable_; + std::vector ub_star_; + + std::vector bound_changed_; + std::vector transformed_upper_; + + scratch_pad_t scratch_pad_; }; template @@ -489,37 +751,29 @@ class strong_cg_cut_t { i_t generate_strong_cg_cut(const lp_problem_t& lp, const simplex_solver_settings_t& settings, const std::vector& var_types, - const sparse_vector_t& inequality, - const f_t inequality_rhs, + const inequality_t& inequality, const std::vector& xstar, - sparse_vector_t& cut, - f_t& cut_rhs); + inequality_t& cut); i_t remove_continuous_variables_integers_nonnegative( const lp_problem_t& lp, const simplex_solver_settings_t& settings, const std::vector& var_types, - sparse_vector_t& inequality, - f_t& inequality_rhs); + inequality_t& inequality); - void to_original_integer_variables(const lp_problem_t& lp, - sparse_vector_t& cut, - f_t& cut_rhs); + void to_original_integer_variables(const lp_problem_t& lp, inequality_t& cut); i_t generate_strong_cg_cut_integer_only(const simplex_solver_settings_t& settings, const std::vector& var_types, - const sparse_vector_t& inequality, - f_t inequality_rhs, - sparse_vector_t& cut, - f_t& cut_rhs); + const inequality_t& inequality, + inequality_t& cut); private: i_t generate_strong_cg_cut_helper(const std::vector& indicies, const std::vector& coefficients, f_t rhs, const std::vector& var_types, - sparse_vector_t& cut, - f_t& cut_rhs); + inequality_t& cut); std::vector transformed_variables_; }; diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 426d9a7535..8259bb8e2f 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -412,19 +412,23 @@ void compute_delta_z(const csc_matrix_t& A_transpose, delta_z[leaving_index] = direction; #ifdef CHECK_CHANGE_IN_REDUCED_COST - delta_y_sparse.to_dense(delta_y); + const i_t m = A_transpose.n; + const i_t n = A_transpose.m; + std::vector delta_y_dense(m); + delta_y.to_dense(delta_y_dense); std::vector delta_z_check(n); std::vector delta_z_mark_check(n, 0); std::vector delta_z_indices_check; phase2::compute_reduced_cost_update(lp, basic_list, nonbasic_list, - delta_y, + delta_y_dense, leaving_index, direction, delta_z_mark_check, delta_z_indices_check, - delta_z_check); + delta_z_check, + work_estimate); f_t error_check = 0.0; for (i_t k = 0; k < n; ++k) { const f_t diff = std::abs(delta_z[k] - delta_z_check[k]); @@ -1726,6 +1730,7 @@ i_t compute_delta_x(const lp_problem_t& lp, const std::vector& basic_list, const std::vector& delta_x_flip, const sparse_vector_t& rhs_sparse, + const std::vector& delta_z, const std::vector& x, sparse_vector_t& utilde_sparse, sparse_vector_t& scaled_delta_xB_sparse, @@ -1782,6 +1787,23 @@ i_t compute_delta_x(const lp_problem_t& lp, scaled_delta_xB_sparse.negate(); work_estimate += 2 * scaled_delta_xB_sparse.i.size() + scaled_delta_xB.size(); scale = -scaled_delta_xB[basic_leaving_index]; + } else if (delta_z[entering_index] != 0.0) { + scale = -delta_z[entering_index]; + // The sparse solve did not produce a coefficient for basic_leaving_index. + // Add it so update_primal_variables / update_primal_infeasibilities process + // the leaving variable (they iterate over scaled_delta_xB_sparse.i). + bool found_leaving = false; + for (i_t k = 0; k < static_cast(scaled_delta_xB_sparse.i.size()); ++k) { + if (scaled_delta_xB_sparse.i[k] == basic_leaving_index) { + scaled_delta_xB_sparse.x[k] = scale; + found_leaving = true; + break; + } + } + if (!found_leaving) { + scaled_delta_xB_sparse.i.push_back(basic_leaving_index); + scaled_delta_xB_sparse.x.push_back(scale); + } } else { return -1; } @@ -2029,8 +2051,8 @@ void check_primal_infeasibilities(const lp_problem_t& lp, const simplex_solver_settings_t& settings, const std::vector& basic_list, const std::vector& x, - const ins_vector& squared_infeasibilities, - const ins_vector& infeasibility_indices) + const std::vector& squared_infeasibilities, + const std::vector& infeasibility_indices) { const i_t m = basic_list.size(); for (i_t k = 0; k < m; ++k) { @@ -2054,14 +2076,30 @@ void check_primal_infeasibilities(const lp_problem_t& lp, } } if (!found) { settings.log.printf("Infeasibility index not found %d\n", j); } + } else { + bool found = false; + i_t h; + for (h = 0; h < infeasibility_indices.size(); ++h) { + if (infeasibility_indices[h] == j) { + found = true; + break; + } + } + if (found) { + settings.log.printf("Incorrect infeasible index %d/%d infeas %e sq %e\n", + j, + h, + infeas, + squared_infeasibilities[j]); + } } } } template void check_basic_infeasibilities(const std::vector& basic_list, - const ins_vector& basic_mark, - const ins_vector& infeasibility_indices, + const std::vector& basic_mark, + const std::vector& infeasibility_indices, i_t info) { for (i_t k = 0; k < infeasibility_indices.size(); ++k) { @@ -2104,8 +2142,8 @@ template void check_basis_mark(const simplex_solver_settings_t& settings, const std::vector& basic_list, const std::vector& nonbasic_list, - const ins_vector& basic_mark, - const ins_vector& nonbasic_mark) + const std::vector& basic_mark, + const std::vector& nonbasic_mark) { const i_t m = basic_list.size(); const i_t n = basic_mark.size(); @@ -2925,7 +2963,9 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, #ifdef COMPUTE_DUAL_RESIDUAL std::vector dual_residual; std::vector zeros(n, 0.0); - phase2::compute_dual_residual(lp.A, zeros, delta_y, delta_z, dual_residual); + std::vector delta_y_dense(m); + delta_y_sparse.to_dense(delta_y_dense); + phase2::compute_dual_residual(lp.A, zeros, delta_y_dense, delta_z, dual_residual); // || A'*delta_y + delta_z ||_inf f_t dual_residual_norm = vector_norm_inf(dual_residual); settings.log.printf( @@ -3182,6 +3222,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, timers.vector_time += timers.stop_timer(); #ifdef COMPUTE_DUAL_RESIDUAL + std::vector dual_res1; phase2::compute_dual_residual(lp.A, objective, y, z, dual_res1); f_t dual_res_norm = vector_norm_inf(dual_res1); if (dual_res_norm > settings.dual_tol) { @@ -3241,6 +3282,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, basic_list, delta_x_flip, rhs_sparse, + delta_z, x, utilde_sparse, scaled_delta_xB_sparse, @@ -3406,7 +3448,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, if (should_refactor) { PHASE2_NVTX_RANGE("DualSimplex::refactorization"); num_refactors++; - bool should_recompute_x = false; + bool should_recompute_x = true; // Need for numerically difficult problems like cbs-cta i_t refactor_status = ft.refactor_basis( lp.A, settings, lp.lower, lp.upper, start_time, basic_list, nonbasic_list, vstatus); if (refactor_status == CONCURRENT_HALT_RETURN) { return dual::status_t::CONCURRENT_LIMIT; } diff --git a/cpp/src/dual_simplex/presolve.hpp b/cpp/src/dual_simplex/presolve.hpp index 17e6176e3b..a068ed04ab 100644 --- a/cpp/src/dual_simplex/presolve.hpp +++ b/cpp/src/dual_simplex/presolve.hpp @@ -13,6 +13,13 @@ #include #include +#include +#include +#include +#include +#include +#include + namespace cuopt::linear_programming::dual_simplex { template @@ -42,6 +49,113 @@ struct lp_problem_t { f_t obj_constant; f_t obj_scale; // 1.0 for min, -1.0 for max bool objective_is_integral{false}; + + void write_problem(const std::string& path) const + { + FILE* fid = fopen(path.c_str(), "w"); + if (fid) { + fwrite(&num_rows, sizeof(i_t), 1, fid); + fwrite(&num_cols, sizeof(i_t), 1, fid); + fwrite(&obj_constant, sizeof(f_t), 1, fid); + fwrite(&obj_scale, sizeof(f_t), 1, fid); + i_t is_integral = objective_is_integral ? 1 : 0; + fwrite(&is_integral, sizeof(i_t), 1, fid); + fwrite(objective.data(), sizeof(f_t), num_cols, fid); + fwrite(rhs.data(), sizeof(f_t), num_rows, fid); + fwrite(lower.data(), sizeof(f_t), num_cols, fid); + fwrite(upper.data(), sizeof(f_t), num_cols, fid); + fwrite(A.col_start.data(), sizeof(i_t), A.col_start.size(), fid); + fwrite(A.i.data(), sizeof(i_t), A.i.size(), fid); + fwrite(A.x.data(), sizeof(f_t), A.x.size(), fid); + fclose(fid); + } + } + + void read_problem(const std::string& path) + { + FILE* fid = fopen(path.c_str(), "r"); + if (fid) { + fread(&num_rows, sizeof(i_t), 1, fid); + fread(&num_cols, sizeof(i_t), 1, fid); + fread(&obj_constant, sizeof(f_t), 1, fid); + fread(&obj_scale, sizeof(f_t), 1, fid); + i_t is_integral; + fread(&is_integral, sizeof(i_t), 1, fid); + objective_is_integral = is_integral == 1; + objective.resize(num_cols); + fread(objective.data(), sizeof(f_t), num_cols, fid); + rhs.resize(num_rows); + fread(rhs.data(), sizeof(f_t), num_rows, fid); + lower.resize(num_cols); + fread(lower.data(), sizeof(f_t), num_cols, fid); + upper.resize(num_cols); + fread(upper.data(), sizeof(f_t), num_cols, fid); + A.n = num_cols; + A.m = num_rows; + A.col_start.resize(num_cols + 1); + fread(A.col_start.data(), sizeof(i_t), num_cols + 1, fid); + A.i.resize(A.col_start[num_cols]); + fread(A.i.data(), sizeof(i_t), A.i.size(), fid); + A.x.resize(A.i.size()); + fread(A.x.data(), sizeof(f_t), A.x.size(), fid); + fclose(fid); + } + } + + void write_mps(const std::string& path) const + { + std::ofstream mps_file(path); + if (!mps_file.is_open()) { + printf("Failed to open file %s\n", path.c_str()); + return; + } + mps_file << std::setprecision(std::numeric_limits::max_digits10); + mps_file << "NAME " << "cuopt_lp_problem_t" << "\n"; + mps_file << "ROWS\n"; + mps_file << " N OBJ\n"; + for (i_t i = 0; i < num_rows; i++) { + mps_file << " E R" << i << "\n"; + } + mps_file << "COLUMNS\n"; + for (i_t j = 0; j < num_cols; j++) { + const i_t col_start = A.col_start[j]; + const i_t col_end = A.col_start[j + 1]; + mps_file << " " << "C" << j << " OBJ " << objective[j] << "\n"; + for (i_t k = col_start; k < col_end; k++) { + const i_t i = A.i[k]; + const f_t x = A.x[k]; + std::string col_name = "C" + std::to_string(j); + std::string row_name = "R" + std::to_string(i); + mps_file << " " << col_name << " " << row_name << " " << x << "\n"; + } + } + mps_file << "RHS\n"; + for (i_t i = 0; i < num_rows; i++) { + mps_file << " RHS1 R" << i << " " << rhs[i] << "\n"; + } + + mps_file << "BOUNDS\n"; + for (i_t j = 0; j < num_cols; j++) { + const f_t lb = lower[j]; + const f_t ub = upper[j]; + std::string col_name = "C" + std::to_string(j); + if (lb == -std::numeric_limits::infinity() && + ub == std::numeric_limits::infinity()) { + mps_file << " FR BOUND1 " << col_name << "\n"; + } else { + if (lb == -std::numeric_limits::infinity()) { + mps_file << " MI BOUND1 " << col_name << "\n"; + } else { + mps_file << " LO BOUND1 " << col_name << " " << lb << "\n"; + } + if (ub != std::numeric_limits::infinity()) { + mps_file << " UP BOUND1 " << col_name << " " << ub << "\n"; + } + } + } + mps_file << "ENDATA\n"; + mps_file.close(); + } }; template diff --git a/cpp/src/dual_simplex/sparse_matrix.hpp b/cpp/src/dual_simplex/sparse_matrix.hpp index b6d3ee9aae..09b580cad1 100644 --- a/cpp/src/dual_simplex/sparse_matrix.hpp +++ b/cpp/src/dual_simplex/sparse_matrix.hpp @@ -48,7 +48,12 @@ class csc_matrix_t { // Adjust to i and x vectors for a new number of nonzeros void reallocate(i_t new_nz); + // Get the number of nonzeros in the matrix i_t nnz() const { return col_start[n]; } + + // Get the number of nonzeros in column j + i_t col_length(i_t j) const { return col_start[j + 1] - col_start[j]; } + // Convert the CSC matrix to a CSR matrix i_t to_compressed_row( cuopt::linear_programming::dual_simplex::csr_matrix_t& Arow) const; @@ -145,6 +150,9 @@ class csr_matrix_t { { } + // Get the number of nonzeros in row i + i_t row_length(i_t i) const { return row_start[i + 1] - row_start[i]; } + // Convert the CSR matrix to CSC i_t to_compressed_col(csc_matrix_t& Acol) const; diff --git a/cpp/src/dual_simplex/sparse_vector.cpp b/cpp/src/dual_simplex/sparse_vector.cpp index 0d34d4c390..1f3798e7dd 100644 --- a/cpp/src/dual_simplex/sparse_vector.cpp +++ b/cpp/src/dual_simplex/sparse_vector.cpp @@ -240,6 +240,15 @@ void sparse_vector_t::negate() } } +template +void sparse_vector_t::scale(f_t factor) +{ + const i_t nz = x.size(); + for (i_t k = 0; k < nz; ++k) { + x[k] *= factor; + } +} + template f_t sparse_vector_t::find_coefficient(i_t index) const { diff --git a/cpp/src/dual_simplex/sparse_vector.hpp b/cpp/src/dual_simplex/sparse_vector.hpp index 6ea642ee07..d9ca540d18 100644 --- a/cpp/src/dual_simplex/sparse_vector.hpp +++ b/cpp/src/dual_simplex/sparse_vector.hpp @@ -48,7 +48,11 @@ class sparse_vector_t { void sort(); // compute the squared 2-norm of the sparse vector f_t norm2_squared() const; + // negate the coefficients in the sparse vector void negate(); + // scale the coefficients in the sparse vector by a factor + void scale(f_t factor); + // find the coefficient of a given index f_t find_coefficient(i_t index) const; void clear() diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index f7d0408aba..a60d508fac 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -74,7 +74,7 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_MIP_RELATIVE_GAP, &mip_settings.tolerances.relative_mip_gap, f_t(0.0), f_t(1e-1), f_t(1e-4)}, {CUOPT_PRIMAL_INFEASIBLE_TOLERANCE, &pdlp_settings.tolerances.primal_infeasible_tolerance, f_t(0.0), f_t(1e-1), std::max(f_t(1e-10), std::numeric_limits::epsilon())}, {CUOPT_DUAL_INFEASIBLE_TOLERANCE, &pdlp_settings.tolerances.dual_infeasible_tolerance, f_t(0.0), f_t(1e-1), std::max(f_t(1e-10), std::numeric_limits::epsilon())}, - {CUOPT_MIP_CUT_CHANGE_THRESHOLD, &mip_settings.cut_change_threshold, f_t(0.0), std::numeric_limits::infinity(), f_t(1e-3)}, + {CUOPT_MIP_CUT_CHANGE_THRESHOLD, &mip_settings.cut_change_threshold, f_t(-1.0), std::numeric_limits::infinity(), f_t(-1.0)}, {CUOPT_MIP_CUT_MIN_ORTHOGONALITY, &mip_settings.cut_min_orthogonality, f_t(0.0), f_t(1.0), f_t(0.5)} }; diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index da6c7c0b81..f25c093afb 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -229,6 +229,10 @@ solution_t mip_solver_t::run_solver() branch_and_bound_settings.cut_min_orthogonality = context.settings.cut_min_orthogonality; branch_and_bound_settings.mip_batch_pdlp_strong_branching = context.settings.mip_batch_pdlp_strong_branching; + branch_and_bound_settings.reduced_cost_strengthening = + context.settings.reduced_cost_strengthening == -1 + ? 2 + : context.settings.reduced_cost_strengthening; if (context.settings.num_cpu_threads < 0) { branch_and_bound_settings.num_threads = std::max(1, omp_get_max_threads() - 1); diff --git a/cpp/tests/mip/cuts_test.cu b/cpp/tests/mip/cuts_test.cu index 67baab5f12..7968d5513f 100644 --- a/cpp/tests/mip/cuts_test.cu +++ b/cpp/tests/mip/cuts_test.cu @@ -897,7 +897,7 @@ TEST(cuts, test_cuts_1) // Expected objective value from documentation example is approximately -28 EXPECT_NEAR(-28, obj_val, 1e-3); - EXPECT_EQ(solution.get_num_nodes(), 0); + EXPECT_LE(solution.get_num_nodes(), 2); } // Problem data for the mixed integer linear programming problem From e077cb4648de46a5d556bccad6fda29a383c2639 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 12 Mar 2026 04:14:11 -0700 Subject: [PATCH 176/225] setting for running gpu heuristics are pre-exploration --- .../mip/solver_settings.hpp | 2 + cpp/src/branch_and_bound/branch_and_bound.cpp | 39 +++++++++++++++---- cpp/src/branch_and_bound/branch_and_bound.hpp | 11 ++++++ .../diversity/diversity_manager.cu | 9 +++++ cpp/src/mip_heuristics/solver.cu | 20 ++++++++++ 5 files changed, 73 insertions(+), 8 deletions(-) diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 26caf2ad5f..10b5048083 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -107,6 +107,8 @@ class mip_solver_settings_t { // Scales deterministic B&B work units (LP iterations) exposed to the shared deterministic // timeline. f_t bnb_work_unit_scale = 1.0; + // When true, GPU heuristics wait for B&B to finish root solve before starting. + bool gpu_heur_wait_for_exploration = true; std::string log_file; std::string sol_file; diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 3515b4119a..db48d95371 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -600,7 +600,7 @@ void branch_and_bound_t::queue_external_solution_deterministic( const double bnb_work_total = work_unit_context_.current_work(); const uint32_t host_hash = detail::compute_hash(solution); settings_.log.printf( - "Queueing deterministic external incumbent: obj=%g wut=%.3f (bnb_wut=%.3f) origin=%s " + "Queueing deterministic external incumbent: obj=%g heur_wut=%.3f bnb_wut=%.3f origin=%s " "hash=0x%x\n", user_objective, work_unit_ts, @@ -640,14 +640,14 @@ void branch_and_bound_t::queue_external_solution_deterministic( mutex_original_lp_.unlock(); mutex_heuristic_queue_.lock(); - heuristic_solution_queue_.push_back({solution, user_objective, work_unit_ts, origin}); + heuristic_solution_queue_.push_back({solution, user_objective, bnb_work_total, origin}); const size_t heuristic_queue_size = heuristic_solution_queue_.size(); mutex_heuristic_queue_.unlock(); CUOPT_DETERMINISM_LOG( settings_.log, - "Deterministic external queued_for_retirement: wut=%.6f user_obj=%.16e host_hash=0x%x " + "Deterministic external queued_for_retirement: bnb_wut=%.6f user_obj=%.16e host_hash=0x%x " "heur_q=%zu\n", - work_unit_ts, + bnb_work_total, user_objective, host_hash, heuristic_queue_size); @@ -2281,6 +2281,13 @@ template mip_status_t branch_and_bound_t::solve(mip_solution_t& solution) { raft::common::nvtx::range scope("BB::solve"); + auto exploration_signal_guard = cuopt::scope_guard([this]() { + if (!exploration_started_.load()) { + std::lock_guard lock(exploration_started_mutex_); + exploration_started_ = true; + exploration_started_cv_.notify_all(); + } + }); auto heuristic_preemption_guard = cuopt::scope_guard([this]() { if (settings_.heuristic_preemption_callback != nullptr) { settings_.heuristic_preemption_callback(); @@ -2300,7 +2307,9 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut if (settings_.deterministic) { work_unit_context_.deterministic = true; cuopt_assert(settings_.bnb_work_unit_scale > 0.0, "B&B work-unit scale must be positive"); - work_unit_context_.work_unit_scale = settings_.bnb_work_unit_scale; + // Scale=0 during pre-exploration: root LP/cuts/SB don't advance the deterministic timeline. + // Restored to bnb_work_unit_scale when exploration begins. + work_unit_context_.work_unit_scale = 0.0; // Detach the scheduler during the serial root/cuts/SB phase. // record_work_sync_on_horizon still accumulates global_work_units_elapsed, @@ -3042,9 +3051,9 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut calculate_variable_locks(original_lp_, var_up_locks_, var_down_locks_); } if (settings_.deterministic) { - pre_exploration_work_ = work_unit_context_.current_work(); - work_unit_context_.scheduler = saved_scheduler; - settings_.log.printf("Pre-exploration work: %.2f work units\n", pre_exploration_work_); + pre_exploration_work_ = work_unit_context_.current_work(); + work_unit_context_.scheduler = saved_scheduler; + work_unit_context_.work_unit_scale = settings_.bnb_work_unit_scale; settings_.log.printf( " | Explored | Unexplored | Objective | Bound | IntInf | Depth | Iter/Node " "| Gap | Work | Time |\n"); @@ -3054,6 +3063,12 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut "| Gap | Time |\n"); } + { + std::lock_guard lock(exploration_started_mutex_); + exploration_started_ = true; + } + exploration_started_cv_.notify_all(); + if (settings_.deterministic) { run_deterministic_coordinator(Arow_); } else if (settings_.num_threads > 1) { @@ -3484,6 +3499,14 @@ void branch_and_bound_t::deterministic_sync_callback() total_producer_wait_time_ += wait_time; max_producer_wait_time_ = std::max(max_producer_wait_time_, wait_time); ++producer_wait_count_; + if (wait_time > 0.01) { + settings_.log.printf( + "Producer sync wait: %.3fs at horizon %.2f (cumulative: %.3fs, count: %d)\n", + wait_time, + horizon_end, + total_producer_wait_time_, + producer_wait_count_); + } work_unit_context_.set_current_work(horizon_end, false); diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index 5bf4fc062a..269f64a946 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -33,7 +33,9 @@ #include +#include #include +#include #include namespace cuopt::linear_programming::dual_simplex { @@ -176,12 +178,21 @@ class branch_and_bound_t { // Get producer sync for external heuristics (e.g., CPUFJ) to register producer_sync_t& get_producer_sync() { return producer_sync_; } + void wait_for_exploration_start() + { + std::unique_lock lock(exploration_started_mutex_); + exploration_started_cv_.wait(lock, [this] { return exploration_started_.load(); }); + } + private: const user_problem_t& original_problem_; const simplex_solver_settings_t settings_; work_limit_context_t work_unit_context_{"B&B"}; double pre_exploration_work_{0.0}; + std::atomic exploration_started_{false}; + std::mutex exploration_started_mutex_; + std::condition_variable exploration_started_cv_; // Initial guess. std::vector guess_; diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 8ef718e98a..5e538121f2 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -439,6 +439,15 @@ solution_t diversity_manager_t::run_solver() }); if (is_deterministic_mode(context.settings.determinism_mode) && context.branch_and_bound_ptr != nullptr) { + if (context.settings.gpu_heur_wait_for_exploration) { + CUOPT_LOG_INFO("GPU heuristics waiting for B&B tree exploration to start..."); + auto wait_start = std::chrono::high_resolution_clock::now(); + context.branch_and_bound_ptr->wait_for_exploration_start(); + double wait_elapsed = + std::chrono::duration(std::chrono::high_resolution_clock::now() - wait_start) + .count(); + CUOPT_LOG_INFO("GPU heuristics resumed after %.2fs (B&B exploration started)", wait_elapsed); + } auto& producer_sync = context.branch_and_bound_ptr->get_producer_sync(); context.gpu_heur_loop.attach_producer_sync(&producer_sync); producer_sync.register_producer(context.gpu_heur_loop.producer_progress_ptr()); diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index e11aaffdb7..435fc31410 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -461,9 +461,29 @@ solution_t mip_solver_t::run_solver() branch_and_bound_solution.objective, (int)std::isfinite(branch_and_bound_solution.objective), (int)branch_and_bound_solution.has_incumbent); + // In deterministic mode, only solutions formally retired by B&B are valid output. + // Discard the GPU heuristic incumbent that B&B never processed. + sol = solution_t(*context.problem_ptr); } context.stats.num_nodes = branch_and_bound_solution.nodes_explored; context.stats.num_simplex_iterations = branch_and_bound_solution.simplex_iterations; + + if (is_deterministic_mode(context.settings.determinism_mode)) { + double bnb_work = branch_and_bound->get_work_unit_context().current_work(); + double gpu_work = context.gpu_heur_loop.current_work(); + double bnb_scale = solver_settings_.bnb_work_unit_scale; + double gpu_scale = solver_settings_.gpu_heur_work_unit_scale; + CUOPT_LOG_INFO( + "Work unit summary: B&B=%.2f (scale=%.3f, raw=%.2f) GPU_heur=%.2f (scale=%.3f, raw=%.2f) " + "ratio=%.2fx", + bnb_work, + bnb_scale, + bnb_scale > 0 ? bnb_work / bnb_scale : 0.0, + gpu_work, + gpu_scale, + gpu_scale > 0 ? gpu_work / gpu_scale : 0.0, + gpu_work > 0 ? bnb_work / gpu_work : 0.0); + } } sol.compute_feasibility(); rmm::device_scalar is_feasible(sol.handle_ptr->get_stream()); From 061955455c7bc36c1ca982ca3941fa5f6972c1a3 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 12 Mar 2026 06:33:09 -0700 Subject: [PATCH 177/225] refactor and cleanup --- .../cuopt/linear_programming/constants.h | 14 +- .../mip/solver_settings.hpp | 16 +- .../utilities/internals.hpp | 25 +- cpp/src/barrier/barrier.cu | 2 +- cpp/src/branch_and_bound/pseudo_costs.cpp | 262 ++-- cpp/src/branch_and_bound/pseudo_costs.hpp | 13 +- cpp/src/dual_simplex/bb_event.hpp | 228 ---- cpp/src/dual_simplex/bb_worker_state.hpp | 644 ---------- cpp/src/dual_simplex/bounds_strengthening.cpp | 6 +- cpp/src/dual_simplex/bounds_strengthening.hpp | 14 +- cpp/src/dual_simplex/bsp_debug.hpp | 1139 ----------------- .../dual_simplex/dual_simplex_features.hpp | 219 ---- cpp/src/dual_simplex/phase2.cpp | 2 +- cpp/src/dual_simplex/primal.cpp | 3 - cpp/src/dual_simplex/sparse_matrix.cpp | 59 +- cpp/src/dual_simplex/sparse_matrix.hpp | 4 +- cpp/src/math_optimization/solver_settings.cu | 2 +- .../diversity/diversity_manager.cu | 19 +- cpp/src/mip_heuristics/diversity/lns/rins.cu | 2 +- .../mip_heuristics/diversity/population.cu | 114 +- .../recombiners/bound_prop_recombiner.cuh | 55 +- .../diversity/recombiners/fp_recombiner.cuh | 15 +- .../recombiners/line_segment_recombiner.cuh | 2 +- .../diversity/recombiners/recombiner.cuh | 13 +- .../feasibility_jump/feasibility_jump.cu | 8 +- .../feasibility_pump/feasibility_pump.cu | 81 +- .../local_search/local_search.cu | 6 +- .../local_search/rounding/bounds_repair.cu | 13 +- .../local_search/rounding/constraint_prop.cu | 2 +- cpp/src/mip_heuristics/mip_constants.hpp | 17 +- .../presolve/bounds_presolve.cu | 2 +- .../mip_heuristics/presolve/probing_cache.cu | 2 +- .../mip_heuristics/relaxed_lp/relaxed_lp.cu | 123 +- cpp/src/mip_heuristics/solve.cu | 3 +- cpp/src/mip_heuristics/solver.cu | 20 +- cpp/src/mip_heuristics/solver_context.cuh | 6 +- 36 files changed, 370 insertions(+), 2785 deletions(-) delete mode 100644 cpp/src/dual_simplex/bb_event.hpp delete mode 100644 cpp/src/dual_simplex/bb_worker_state.hpp delete mode 100644 cpp/src/dual_simplex/bsp_debug.hpp delete mode 100644 cpp/src/dual_simplex/dual_simplex_features.hpp diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index 7f06cbc236..813f60bea0 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -75,10 +75,16 @@ #define CUOPT_USER_PROBLEM_FILE "user_problem_file" #define CUOPT_RANDOM_SEED "random_seed" -/* @brief MIP determinism mode constants */ -#define CUOPT_MODE_OPPORTUNISTIC 0 -#define CUOPT_MODE_DETERMINISTIC 1 -#define CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS 2 +/* @brief MIP determinism mode flags (bitset) */ +#define CUOPT_DETERMINISM_NONE 0x0 +#define CUOPT_DETERMINISM_BB 0x1 +#define CUOPT_DETERMINISM_GPU_HEURISTICS 0x2 +#define CUOPT_DETERMINISM_FULL (CUOPT_DETERMINISM_BB | CUOPT_DETERMINISM_GPU_HEURISTICS) + +/* Backward compatibility aliases */ +#define CUOPT_MODE_OPPORTUNISTIC CUOPT_DETERMINISM_NONE +#define CUOPT_MODE_DETERMINISTIC CUOPT_DETERMINISM_FULL +#define CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS CUOPT_DETERMINISM_GPU_HEURISTICS /* @brief LP/MIP termination status constants */ #define CUOPT_TERIMINATION_STATUS_NO_TERMINATION 0 diff --git a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp index 10b5048083..fb63f31456 100644 --- a/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp +++ b/cpp/include/cuopt/linear_programming/mip/solver_settings.hpp @@ -119,17 +119,15 @@ class mip_solver_settings_t { bool mip_scaling = false; presolver_t presolver{presolver_t::Default}; /** - * @brief Determinism mode for MIP solver. + * @brief Determinism mode for MIP solver (bitset). * - * Controls the determinism behavior of the MIP solver: - * - CUOPT_MODE_OPPORTUNISTIC (0): Default mode, allows non-deterministic - * parallelism for better performance - * - CUOPT_MODE_DETERMINISTIC (1): Ensures deterministic results across runs - * at potential cost of performance. Runs deterministic B&B path without GPU heuristics. - * - CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS (2): Enables deterministic work-limit mode while - * still running GPU heuristics. + * Bitwise OR of CUOPT_DETERMINISM_* flags: + * - CUOPT_DETERMINISM_NONE (0x0): Opportunistic, non-deterministic. + * - CUOPT_DETERMINISM_BB (0x1): Deterministic B&B tree exploration. + * - CUOPT_DETERMINISM_GPU_HEURISTICS (0x2): Deterministic GPU heuristic pipeline. + * - CUOPT_DETERMINISM_FULL (0x3): Both B&B and GPU heuristics deterministic. */ - int determinism_mode = CUOPT_MODE_OPPORTUNISTIC; + int determinism_mode = CUOPT_DETERMINISM_NONE; /** * @brief Random seed for the MIP solver. * diff --git a/cpp/include/cuopt/linear_programming/utilities/internals.hpp b/cpp/include/cuopt/linear_programming/utilities/internals.hpp index 20de029d34..382553f6d3 100644 --- a/cpp/include/cuopt/linear_programming/utilities/internals.hpp +++ b/cpp/include/cuopt/linear_programming/utilities/internals.hpp @@ -23,18 +23,19 @@ class Callback { }; enum class mip_solution_origin_t : uint32_t { - UNKNOWN = 0, - BRANCH_AND_BOUND_NODE = 1, - BRANCH_AND_BOUND = BRANCH_AND_BOUND_NODE, - FEASIBILITY_JUMP = 2, - LOCAL_SEARCH = 3, - QUICK_FEASIBLE = 4, - USER_INITIAL = 5, - LP_ROUNDING = 6, - RECOMBINATION = 7, - SUB_MIP = 8, - CPU_FEASIBILITY_JUMP = 9, - BRANCH_AND_BOUND_DIVING = 10, + UNKNOWN = 0, + BRANCH_AND_BOUND_NODE, + BRANCH_AND_BOUND = BRANCH_AND_BOUND_NODE, + FEASIBILITY_JUMP, + LOCAL_SEARCH, + QUICK_FEASIBLE, + USER_INITIAL, + LP_ROUNDING, + RECOMBINATION, + SUB_MIP, + CPU_FEASIBILITY_JUMP, + BRANCH_AND_BOUND_DIVING, + RINS, }; constexpr const char* mip_solution_origin_to_string(mip_solution_origin_t origin) diff --git a/cpp/src/barrier/barrier.cu b/cpp/src/barrier/barrier.cu index 6ad48e2ba7..075323744d 100644 --- a/cpp/src/barrier/barrier.cu +++ b/cpp/src/barrier/barrier.cu @@ -1390,7 +1390,7 @@ class iteration_data_t { // v = alpha * A * w + beta * v = alpha * A * Dinv * A^T * y + beta * v matrix_vector_multiply(A, alpha, w, beta, v); if (debug) { - printf("||A|| = %.16e\n", vector_norm2>(A.x)); + printf("||A|| = %.16e\n", vector_norm2(A.x)); printf("||w|| = %.16e\n", vector_norm2(w)); printf("||v|| = %.16e\n", vector_norm2(v)); } diff --git a/cpp/src/branch_and_bound/pseudo_costs.cpp b/cpp/src/branch_and_bound/pseudo_costs.cpp index 4b0aef022d..ffa499a61a 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.cpp +++ b/cpp/src/branch_and_bound/pseudo_costs.cpp @@ -218,25 +218,62 @@ f_t trial_branching(const lp_problem_t& original_lp, } } -// Overload of trial_branching that takes a plain int64_t& counter (for deterministic/serial use). template -f_t trial_branching_plain(const lp_problem_t& original_lp, - const simplex_solver_settings_t& settings, - const std::vector& var_types, - const std::vector& vstatus, - const std::vector& edge_norms, - const basis_update_mpf_t& basis_factors, - const std::vector& basic_list, - const std::vector& nonbasic_list, - i_t branch_var, - f_t branch_var_lower, - f_t branch_var_upper, - f_t upper_bound, - i_t bnb_lp_iter_per_node, - f_t start_time, - i_t upper_max_lp_iter, - i_t lower_max_lp_iter, - int64_t& total_lp_iter) +f_t trial_branching_generic(const lp_problem_t& original_lp, + const simplex_solver_settings_t& settings, + const std::vector& var_types, + const std::vector& vstatus, + const std::vector& edge_norms, + const basis_update_mpf_t& basis_factors, + const std::vector& basic_list, + const std::vector& nonbasic_list, + i_t branch_var, + f_t branch_var_lower, + f_t branch_var_upper, + f_t upper_bound, + i_t bnb_lp_iter_per_node, + f_t start_time, + i_t upper_max_lp_iter, + i_t lower_max_lp_iter, + omp_atomic_t& total_lp_iter) +{ + return trial_branching(original_lp, + settings, + var_types, + vstatus, + edge_norms, + basis_factors, + basic_list, + nonbasic_list, + branch_var, + branch_var_lower, + branch_var_upper, + upper_bound, + bnb_lp_iter_per_node, + start_time, + upper_max_lp_iter, + lower_max_lp_iter, + total_lp_iter); +} + +template +f_t trial_branching_generic(const lp_problem_t& original_lp, + const simplex_solver_settings_t& settings, + const std::vector& var_types, + const std::vector& vstatus, + const std::vector& edge_norms, + const basis_update_mpf_t& basis_factors, + const std::vector& basic_list, + const std::vector& nonbasic_list, + i_t branch_var, + f_t branch_var_lower, + f_t branch_var_upper, + f_t upper_bound, + i_t bnb_lp_iter_per_node, + f_t start_time, + i_t upper_max_lp_iter, + i_t lower_max_lp_iter, + int64_t& total_lp_iter) { omp_atomic_t atomic_iter{0}; f_t result = trial_branching(original_lp, @@ -262,7 +299,7 @@ f_t trial_branching_plain(const lp_problem_t& original_lp, } // namespace -template +template i_t reliable_variable_selection_core(mip_node_t* node_ptr, const std::vector& fractional, const std::vector& solution, @@ -273,12 +310,12 @@ i_t reliable_variable_selection_core(mip_node_t* node_ptr, const basis_update_mpf_t& basis_factors, const std::vector& basic_list, const std::vector& nonbasic_list, - f_t* sum_down, - f_t* sum_up, - i_t* num_down, - i_t* num_up, + SumT* sum_down, + SumT* sum_up, + CountT* num_down, + CountT* num_up, i_t n_vars, - int64_t& strong_branching_lp_iter, + SBIterT& strong_branching_lp_iter, f_t upper_bound, int64_t bnb_lp_iters, int64_t bnb_nodes_explored, @@ -370,23 +407,23 @@ i_t reliable_variable_selection_core(mip_node_t* node_ptr, if (var_mutex_down) { var_mutex_down[j].lock(); } if (num_down[j] < reliable_threshold) { - f_t obj = trial_branching_plain(leaf_problem, - settings, - var_types, - node_ptr->vstatus, - edge_norms, - basis_factors, - basic_list, - nonbasic_list, - j, - leaf_problem.lower[j], - std::floor(solution[j]), - upper_bound, - bnb_lp_iter_per_node, - start_time, - rb_settings.upper_max_lp_iter, - rb_settings.lower_max_lp_iter, - strong_branching_lp_iter); + f_t obj = trial_branching_generic(leaf_problem, + settings, + var_types, + node_ptr->vstatus, + edge_norms, + basis_factors, + basic_list, + nonbasic_list, + j, + leaf_problem.lower[j], + std::floor(solution[j]), + upper_bound, + bnb_lp_iter_per_node, + start_time, + rb_settings.upper_max_lp_iter, + rb_settings.lower_max_lp_iter, + strong_branching_lp_iter); if (!std::isnan(obj)) { f_t change_in_obj = std::max(obj - node_ptr->lower_bound, eps); f_t change_in_x = solution[j] - std::floor(solution[j]); @@ -400,23 +437,23 @@ i_t reliable_variable_selection_core(mip_node_t* node_ptr, if (var_mutex_up) { var_mutex_up[j].lock(); } if (num_up[j] < reliable_threshold) { - f_t obj = trial_branching_plain(leaf_problem, - settings, - var_types, - node_ptr->vstatus, - edge_norms, - basis_factors, - basic_list, - nonbasic_list, - j, - std::ceil(solution[j]), - leaf_problem.upper[j], - upper_bound, - bnb_lp_iter_per_node, - start_time, - rb_settings.upper_max_lp_iter, - rb_settings.lower_max_lp_iter, - strong_branching_lp_iter); + f_t obj = trial_branching_generic(leaf_problem, + settings, + var_types, + node_ptr->vstatus, + edge_norms, + basis_factors, + basic_list, + nonbasic_list, + j, + std::ceil(solution[j]), + leaf_problem.upper[j], + upper_bound, + bnb_lp_iter_per_node, + start_time, + rb_settings.upper_max_lp_iter, + rb_settings.lower_max_lp_iter, + strong_branching_lp_iter); if (!std::isnan(obj)) { f_t change_in_obj = std::max(obj - node_ptr->lower_bound, eps); f_t change_in_x = std::ceil(solution[j]) - solution[j]; @@ -648,9 +685,10 @@ void strong_branching(const user_problem_t& original_problem, { i_t n = std::min(4 * settings.num_threads, fractional.size()); + // Here we are creating more tasks than the number of threads + // such that they can be scheduled dynamically to the threads. cuopt::work_limit_context_t* thread_ctx = use_work_accounting ? &thread_work_contexts[omp_get_thread_num()] : nullptr; - #pragma omp for schedule(dynamic, 1) for (i_t k = 0; k < n; k++) { i_t start = std::floor(k * fractional.size() / n); @@ -795,52 +833,31 @@ i_t pseudo_costs_t::reliable_variable_selection( int max_num_tasks, logger_t& log) { - const i_t n = (i_t)pseudo_cost_sum_down.size(); - std::vector sd(n), su(n); - std::vector nd(n), nu(n); - for (i_t j = 0; j < n; ++j) { - sd[j] = pseudo_cost_sum_down[j]; - su[j] = pseudo_cost_sum_up[j]; - nd[j] = pseudo_cost_num_down[j]; - nu[j] = pseudo_cost_num_up[j]; - } - int64_t sb_iter = strong_branching_lp_iter.load(); - - i_t result = reliable_variable_selection_core(node_ptr, - fractional, - solution, - settings, - var_types, - worker->leaf_problem, - worker->leaf_edge_norms, - worker->basis_factors, - worker->basic_list, - worker->nonbasic_list, - sd.data(), - su.data(), - nd.data(), - nu.data(), - n, - sb_iter, - upper_bound, - bnb_stats.total_lp_iters, - bnb_stats.nodes_explored, - bnb_stats.start_time, - reliability_branching_settings, - std::max(max_num_tasks, 1), - pseudo_cost_mutex_down.data(), - pseudo_cost_mutex_up.data(), - &worker->rng); - - for (i_t j = 0; j < n; ++j) { - pseudo_cost_sum_down[j] = sd[j]; - pseudo_cost_sum_up[j] = su[j]; - pseudo_cost_num_down[j] = nd[j]; - pseudo_cost_num_up[j] = nu[j]; - } - strong_branching_lp_iter = sb_iter; - - return result; + return reliable_variable_selection_core(node_ptr, + fractional, + solution, + settings, + var_types, + worker->leaf_problem, + worker->leaf_edge_norms, + worker->basis_factors, + worker->basic_list, + worker->nonbasic_list, + pseudo_cost_sum_down.data(), + pseudo_cost_sum_up.data(), + pseudo_cost_num_down.data(), + pseudo_cost_num_up.data(), + (i_t)pseudo_cost_sum_down.size(), + strong_branching_lp_iter, + upper_bound, + bnb_stats.total_lp_iters, + bnb_stats.nodes_explored, + bnb_stats.start_time, + reliability_branching_settings, + std::max(max_num_tasks, 1), + pseudo_cost_mutex_down.data(), + pseudo_cost_mutex_up.data(), + &worker->rng); } template @@ -902,7 +919,40 @@ void pseudo_costs_t::update_pseudo_costs_from_strong_branching( template class pseudo_costs_t; -template int reliable_variable_selection_core( +// Opportunistic: omp_atomic_t arrays, omp_atomic_t sb_iter +template int reliable_variable_selection_core, + omp_atomic_t, + omp_atomic_t>( + mip_node_t*, + const std::vector&, + const std::vector&, + const simplex_solver_settings_t&, + const std::vector&, + const lp_problem_t&, + const std::vector&, + const basis_update_mpf_t&, + const std::vector&, + const std::vector&, + omp_atomic_t*, + omp_atomic_t*, + omp_atomic_t*, + omp_atomic_t*, + int, + omp_atomic_t&, + double, + int64_t, + int64_t, + double, + const reliability_branching_settings_t&, + int, + omp_mutex_t*, + omp_mutex_t*, + pcgenerator_t*); + +// Deterministic: plain arrays, plain int64_t sb_iter +template int reliable_variable_selection_core( mip_node_t*, const std::vector&, const std::vector&, diff --git a/cpp/src/branch_and_bound/pseudo_costs.hpp b/cpp/src/branch_and_bound/pseudo_costs.hpp index e8fe6b976f..11c377febd 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.hpp +++ b/cpp/src/branch_and_bound/pseudo_costs.hpp @@ -523,7 +523,8 @@ class pseudo_costs_t { // Core reliability branching loop usable by both opportunistic and deterministic paths. // When num_tasks == 1, runs serially with no locking (deterministic). // When num_tasks > 1 with mutexes/rng, uses OMP taskloop (opportunistic). -template +// SumT/CountT can be f_t/i_t (deterministic snapshot) or omp_atomic_t/omp_atomic_t. +template i_t reliable_variable_selection_core(mip_node_t* node_ptr, const std::vector& fractional, const std::vector& solution, @@ -534,12 +535,12 @@ i_t reliable_variable_selection_core(mip_node_t* node_ptr, const basis_update_mpf_t& basis_factors, const std::vector& basic_list, const std::vector& nonbasic_list, - f_t* sum_down, - f_t* sum_up, - i_t* num_down, - i_t* num_up, + SumT* sum_down, + SumT* sum_up, + CountT* num_down, + CountT* num_up, i_t n_vars, - int64_t& strong_branching_lp_iter, + SBIterT& strong_branching_lp_iter, f_t upper_bound, int64_t bnb_lp_iters, int64_t bnb_nodes_explored, diff --git a/cpp/src/dual_simplex/bb_event.hpp b/cpp/src/dual_simplex/bb_event.hpp deleted file mode 100644 index 280863f6a2..0000000000 --- a/cpp/src/dual_simplex/bb_event.hpp +++ /dev/null @@ -1,228 +0,0 @@ -/* clang-format off */ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -/* clang-format on */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace cuopt::linear_programming::dual_simplex { - -// Event types generated by B&B workers during BSP execution -enum class bb_event_type_t : int8_t { - NODE_BRANCHED = 0, // Node was solved and branched into two children - NODE_FATHOMED = 1, // Node was fathomed (cutoff or infeasible) - NODE_INTEGER = 2, // Node has an integer feasible solution - NODE_PAUSED = 3, // Node processing paused at horizon boundary - NODE_INFEASIBLE = 4, // Node LP relaxation is infeasible - NODE_NUMERICAL = 5, // Numerical issue encountered - HEURISTIC_SOLUTION = 6 // Solution received from heuristics -}; - -// Payload for NODE_BRANCHED events -template -struct branched_payload_t { - i_t down_child_id; - i_t up_child_id; - f_t node_lower_bound; - i_t branch_var; - f_t branch_value; -}; - -// Payload for NODE_INTEGER events (integer feasible solution found) -template -struct integer_solution_payload_t { - f_t objective_value; - // Note: the full solution is stored separately, not in the event -}; - -// Payload for NODE_FATHOMED events -template -struct fathomed_payload_t { - f_t lower_bound; // Bound at which node was fathomed -}; - -// Payload for NODE_PAUSED events -template -struct paused_payload_t { - f_t accumulated_vt; // How much virtual time spent on this node so far -}; - -// Payload for HEURISTIC_SOLUTION events -template -struct heuristic_solution_payload_t { - f_t objective_value; - size_t solution_index; // Index into a solution storage vector -}; - -// A single event generated during BSP execution -template -struct bb_event_t { - bb_event_type_t type; - double vt_timestamp; // Virtual time when this event occurred - int worker_id; // Which worker generated this event - i_t node_id; // Node that generated this event - int event_sequence; // Sequence number within worker for tie-breaking - - // Payload union - only one is valid based on type - union { - branched_payload_t branched; - integer_solution_payload_t integer_solution; - fathomed_payload_t fathomed; - paused_payload_t paused; - heuristic_solution_payload_t heuristic; - } payload; - - // Default constructor - bb_event_t() - : type(bb_event_type_t::NODE_FATHOMED), - vt_timestamp(0.0), - worker_id(0), - node_id(0), - event_sequence(0) - { - payload.fathomed = {0.0}; - } - - // Comparison for deterministic sorting: (vt_timestamp, worker_id, event_sequence) - bool operator<(const bb_event_t& other) const - { - return std::tie(vt_timestamp, worker_id, event_sequence) < - std::tie(other.vt_timestamp, other.worker_id, other.event_sequence); - } - - // Factory methods for creating events - - static bb_event_t make_branched(double vt, - int worker, - i_t node, - int seq, - i_t down_id, - i_t up_id, - f_t lower_bound, - i_t branch_var, - f_t branch_val) - { - bb_event_t e; - e.type = bb_event_type_t::NODE_BRANCHED; - e.vt_timestamp = vt; - e.worker_id = worker; - e.node_id = node; - e.event_sequence = seq; - e.payload.branched = {down_id, up_id, lower_bound, branch_var, branch_val}; - return e; - } - - static bb_event_t make_integer_solution(double vt, int worker, i_t node, int seq, f_t objective) - { - bb_event_t e; - e.type = bb_event_type_t::NODE_INTEGER; - e.vt_timestamp = vt; - e.worker_id = worker; - e.node_id = node; - e.event_sequence = seq; - e.payload.integer_solution = {objective}; - return e; - } - - static bb_event_t make_fathomed(double vt, int worker, i_t node, int seq, f_t lower_bound) - { - bb_event_t e; - e.type = bb_event_type_t::NODE_FATHOMED; - e.vt_timestamp = vt; - e.worker_id = worker; - e.node_id = node; - e.event_sequence = seq; - e.payload.fathomed = {lower_bound}; - return e; - } - - static bb_event_t make_infeasible(double vt, int worker, i_t node, int seq) - { - bb_event_t e; - e.type = bb_event_type_t::NODE_INFEASIBLE; - e.vt_timestamp = vt; - e.worker_id = worker; - e.node_id = node; - e.event_sequence = seq; - e.payload.fathomed = {std::numeric_limits::infinity()}; - return e; - } - - static bb_event_t make_paused(double vt, int worker, i_t node, int seq, f_t accumulated) - { - bb_event_t e; - e.type = bb_event_type_t::NODE_PAUSED; - e.vt_timestamp = vt; - e.worker_id = worker; - e.node_id = node; - e.event_sequence = seq; - e.payload.paused = {accumulated}; - return e; - } - - static bb_event_t make_numerical(double vt, int worker, i_t node, int seq) - { - bb_event_t e; - e.type = bb_event_type_t::NODE_NUMERICAL; - e.vt_timestamp = vt; - e.worker_id = worker; - e.node_id = node; - e.event_sequence = seq; - e.payload.fathomed = {std::numeric_limits::infinity()}; - return e; - } - - static bb_event_t make_heuristic_solution(double vt, - int worker, - int seq, - f_t objective, - size_t sol_idx) - { - bb_event_t e; - e.type = bb_event_type_t::HEURISTIC_SOLUTION; - e.vt_timestamp = vt; - e.worker_id = worker; - e.node_id = -1; // Not associated with a B&B node - e.event_sequence = seq; - e.payload.heuristic = {objective, sol_idx}; - return e; - } -}; - -// Batch of events from a single horizon period -template -struct bb_event_batch_t { - std::vector> events; - double horizon_start; - double horizon_end; - - void clear() - { - events.clear(); - horizon_start = 0.0; - horizon_end = 0.0; - } - - void add(bb_event_t event) { events.push_back(std::move(event)); } - - // Sort events for deterministic replay - void sort_for_replay() { std::sort(events.begin(), events.end()); } - - size_t size() const { return events.size(); } - bool empty() const { return events.empty(); } -}; - -} // namespace cuopt::linear_programming::dual_simplex - - - - - diff --git a/cpp/src/dual_simplex/bb_worker_state.hpp b/cpp/src/dual_simplex/bb_worker_state.hpp deleted file mode 100644 index 54cb5f71e5..0000000000 --- a/cpp/src/dual_simplex/bb_worker_state.hpp +++ /dev/null @@ -1,644 +0,0 @@ -/* clang-format off */ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -/* clang-format on */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace cuopt::linear_programming::dual_simplex { - -// Queued pseudo-cost update for BSP determinism -// Updates are collected during horizon, then applied in deterministic order at sync -template -struct pseudo_cost_update_t { - i_t variable; - rounding_direction_t direction; - f_t delta; // change_in_obj / frac - double vt; // virtual time when update occurred (for deterministic ordering) - int worker_id; // for tie-breaking in sort -}; - -// Per-worker state for BSP (Bulk Synchronous Parallel) branch-and-bound -template -struct bb_worker_state_t { - int worker_id{0}; - - // ========================================================================== - // Plunging data structures (matching explore_subtree strategy) - // ========================================================================== - - // Plunge stack: depth-first path through the tree - // - Front = next node to process (LIFO) - // - Max size = 2 (current node's sibling only) - // - When branching with sibling on stack, sibling is moved to backlog - std::deque*> plunge_stack; - - // Backlog: nodes "plugged" when branching - candidates for load balancing - // When branching with a sibling on the plunge stack, that sibling moves here. - // At horizon sync, backlog nodes participate in redistribution. - std::vector*> backlog; - - // Current node being processed (may be paused at horizon boundary) - mip_node_t* current_node{nullptr}; - - // Last node that was solved (for basis warm-start detection) - // If next node's parent == last_solved_node, we can reuse basis - mip_node_t* last_solved_node{nullptr}; - - // Worker's virtual time clock (cumulative work units) - double clock{0.0}; - - // Current horizon boundaries (for BSP sync) - double horizon_start{0.0}; - double horizon_end{0.0}; - - // Creation sequence counter - cumulative across horizons for unique identity - // Each node created by this worker gets (worker_id, next_creation_seq++) - int32_t next_creation_seq{0}; - - // Events generated during this horizon - bb_event_batch_t events; - - // Event sequence counter for deterministic tie-breaking - int event_sequence{0}; - - // LP problem copy for this worker (bounds modified per node) - std::unique_ptr> leaf_problem; - - // Basis factorization state - std::unique_ptr> basis_factors; - - // Bounds strengthening (node presolver) - std::unique_ptr> node_presolver; - - // Working vectors for basis - std::vector basic_list; - std::vector nonbasic_list; - - // Work unit context for this worker - work_limit_context_t work_context; - - // Whether basis needs recomputation for next node - bool recompute_bounds_and_basis{true}; - - // Per-horizon statistics (reset each horizon) - i_t nodes_processed_this_horizon{0}; - double work_units_this_horizon{0.0}; - - // Cumulative statistics (across all horizons) - i_t total_nodes_processed{0}; - i_t total_nodes_pruned{0}; - i_t total_nodes_branched{0}; - i_t total_nodes_infeasible{0}; - i_t total_integer_solutions{0}; - i_t total_nodes_assigned{0}; // via load balancing - double total_work_units{0.0}; - - // Timing statistics (in seconds) - double total_runtime{0.0}; // Total time spent doing actual work - double total_barrier_wait{0.0}; // Total time spent waiting at horizon sync barriers - double total_nowork_time{0.0}; // Total time spent with no nodes to work on - double horizon_finish_time{ - 0.0}; // Timestamp when worker finished current horizon (for barrier wait calc) - - // Worker-local upper bound for BSP determinism (prevents cross-worker pruning races) - f_t local_upper_bound{std::numeric_limits::infinity()}; - - // Queued integer solutions found during this horizon (merged at sync) - struct queued_integer_solution_t { - f_t objective; - std::vector solution; - i_t depth; - }; - std::vector integer_solutions; - - // Queued pseudo-cost updates (applied to global pseudo-costs at sync) - std::vector> pseudo_cost_updates; - - // Pseudo-cost snapshot for deterministic variable selection - // Initialized from global pseudo-costs at horizon start, then updated locally - // during the horizon as the worker processes nodes. This allows within-horizon - // learning while maintaining determinism (each worker's updates are sequential). - // At sync, all workers' updates are merged into global pseudo-costs, and new - // snapshots are taken at the next horizon start. - std::vector pc_sum_up_snapshot; - std::vector pc_sum_down_snapshot; - std::vector pc_num_up_snapshot; - std::vector pc_num_down_snapshot; - - // Constructor - explicit bb_worker_state_t(int id) - : worker_id(id), work_context("BB_Worker_" + std::to_string(id)) - { - } - - // Initialize worker with problem data - void initialize(const lp_problem_t& original_lp, - const csr_matrix_t& Arow, - const std::vector& var_types, - i_t refactor_frequency, - bool deterministic) - { - // Create copy of LP problem for this worker - leaf_problem = std::make_unique>(original_lp); - - // Initialize basis factors - const i_t m = leaf_problem->num_rows; - basis_factors = std::make_unique>(m, refactor_frequency); - - // Initialize bounds strengthening - std::vector row_sense; - node_presolver = - std::make_unique>(*leaf_problem, Arow, row_sense, var_types); - - // Initialize working vectors - basic_list.resize(m); - nonbasic_list.clear(); - - // Configure work context - work_context.deterministic = deterministic; - } - - // Reset for new horizon - void reset_for_horizon(double horizon_start, double horizon_end, f_t global_upper_bound) - { - // Reset clock to horizon_start for consistent VT timestamps across workers - clock = horizon_start; - events.clear(); - events.horizon_start = horizon_start; - events.horizon_end = horizon_end; - event_sequence = 0; - nodes_processed_this_horizon = 0; - work_units_this_horizon = 0.0; - // Also sync work_context to match clock for consistent tracking - work_context.set_current_work(horizon_start, false); - // Note: next_creation_seq is NOT reset - it's cumulative for unique identity - - // Initialize worker-local upper bound from global (for BSP determinism) - local_upper_bound = global_upper_bound; - - // Clear queued updates from previous horizon - integer_solutions.clear(); - pseudo_cost_updates.clear(); - } - - // Queue a pseudo-cost update for global sync AND apply it to local snapshot immediately - // This allows within-horizon learning while maintaining determinism: - // - Local snapshot updates are sequential within each worker (deterministic) - // - Global updates are merged at sync in sorted (VT, worker_id) order (deterministic) - void queue_pseudo_cost_update(i_t variable, rounding_direction_t direction, f_t delta) - { - // Queue for global sync at horizon end - pseudo_cost_updates.push_back({variable, direction, delta, clock, worker_id}); - - // Also apply to local snapshot immediately for better variable selection - if (direction == rounding_direction_t::DOWN) { - pc_sum_down_snapshot[variable] += delta; - pc_num_down_snapshot[variable]++; - } else { - pc_sum_up_snapshot[variable] += delta; - pc_num_up_snapshot[variable]++; - } - } - - // Variable selection using snapshot (for BSP determinism) - // Returns the best variable to branch on based on pseudo-cost scores - i_t variable_selection_from_snapshot(const std::vector& fractional, - const std::vector& solution) const - { - const i_t num_fractional = fractional.size(); - if (num_fractional == 0) return -1; - - // Compute averages from snapshot - i_t num_initialized_down = 0; - i_t num_initialized_up = 0; - f_t pseudo_cost_down_avg = 0; - f_t pseudo_cost_up_avg = 0; - - const i_t n = pc_sum_down_snapshot.size(); - for (i_t j = 0; j < n; ++j) { - if (pc_num_down_snapshot[j] > 0) { - ++num_initialized_down; - if (std::isfinite(pc_sum_down_snapshot[j])) { - pseudo_cost_down_avg += pc_sum_down_snapshot[j] / pc_num_down_snapshot[j]; - } - } - if (pc_num_up_snapshot[j] > 0) { - ++num_initialized_up; - if (std::isfinite(pc_sum_up_snapshot[j])) { - pseudo_cost_up_avg += pc_sum_up_snapshot[j] / pc_num_up_snapshot[j]; - } - } - } - if (num_initialized_down > 0) { - pseudo_cost_down_avg /= num_initialized_down; - } else { - pseudo_cost_down_avg = 1.0; - } - if (num_initialized_up > 0) { - pseudo_cost_up_avg /= num_initialized_up; - } else { - pseudo_cost_up_avg = 1.0; - } - - // Compute scores - std::vector score(num_fractional); - for (i_t k = 0; k < num_fractional; ++k) { - const i_t j = fractional[k]; - f_t pc_down = (pc_num_down_snapshot[j] != 0) - ? pc_sum_down_snapshot[j] / pc_num_down_snapshot[j] - : pseudo_cost_down_avg; - f_t pc_up = (pc_num_up_snapshot[j] != 0) ? pc_sum_up_snapshot[j] / pc_num_up_snapshot[j] - : pseudo_cost_up_avg; - constexpr f_t eps = 1e-6; - const f_t f_down = solution[j] - std::floor(solution[j]); - const f_t f_up = std::ceil(solution[j]) - solution[j]; - score[k] = std::max(f_down * pc_down, eps) * std::max(f_up * pc_up, eps); - } - - // Select variable with maximum score - i_t branch_var = fractional[0]; - f_t max_score = score[0]; - for (i_t k = 1; k < num_fractional; ++k) { - if (score[k] > max_score) { - max_score = score[k]; - branch_var = fractional[k]; - } - } - - return branch_var; - } - - // ========================================================================== - // Node enqueueing methods - // ========================================================================== - - // Add a node to the plunge stack, assigning BSP identity if not already set - // Used for initial node assignment and when starting a new plunge from backlog - void enqueue_node(mip_node_t* node) - { - // Assign BSP identity if not already set - if (!node->has_bsp_identity()) { - node->origin_worker_id = worker_id; - node->creation_seq = next_creation_seq++; - } - plunge_stack.push_front(node); - } - - // Add a node that already has BSP identity (from load balancing or initial distribution) - // Goes to plunge stack front for immediate processing - void enqueue_node_with_identity(mip_node_t* node) - { - assert(node->has_bsp_identity() && - "Node must have BSP identity for enqueue_node_with_identity"); - plunge_stack.push_front(node); - } - - // Add children after branching with proper plunging behavior: - // 1. If plunge stack has a sibling, move it to backlog (plugging) - // 2. Push both children to plunge stack with preferred child on top - // Returns the child that was placed on top (to be explored first) - mip_node_t* enqueue_children_for_plunge(mip_node_t* down_child, - mip_node_t* up_child, - rounding_direction_t preferred_direction) - { - // PLUGGING: If plunge stack has a sibling from previous branch, move it to backlog - if (!plunge_stack.empty()) { - mip_node_t* sibling = plunge_stack.back(); - plunge_stack.pop_back(); - backlog.push_back(sibling); - } - - // Assign BSP identity to children - if (!down_child->has_bsp_identity()) { - down_child->origin_worker_id = worker_id; - down_child->creation_seq = next_creation_seq++; - } - if (!up_child->has_bsp_identity()) { - up_child->origin_worker_id = worker_id; - up_child->creation_seq = next_creation_seq++; - } - - // Push children - preferred child on top (front) for immediate exploration - mip_node_t* first_child; - if (preferred_direction == rounding_direction_t::UP) { - plunge_stack.push_front(down_child); // Second to explore - plunge_stack.push_front(up_child); // First to explore (on top) - first_child = up_child; - } else { - plunge_stack.push_front(up_child); // Second to explore - plunge_stack.push_front(down_child); // First to explore (on top) - first_child = down_child; - } - - return first_child; - } - - // ========================================================================== - // Node dequeueing methods - // ========================================================================== - - // Get next node to process using plunging strategy: - // 1. Resume paused node if any - // 2. Pop from plunge stack (depth-first continuation) - // 3. Fall back to backlog (best-first from plugged nodes) - mip_node_t* dequeue_node() - { - // 1. Resume paused node if any - if (current_node != nullptr) { - mip_node_t* node = current_node; - current_node = nullptr; - return node; - } - - // 2. Prefer plunge stack (depth-first continuation) - if (!plunge_stack.empty()) { - mip_node_t* node = plunge_stack.front(); - plunge_stack.pop_front(); - return node; - } - - // 3. Fall back to backlog - select best node (lowest lower_bound) - if (!backlog.empty()) { - auto best_it = - std::min_element(backlog.begin(), - backlog.end(), - [](const mip_node_t* a, const mip_node_t* b) { - // Best-first: prefer lower bound - if (a->lower_bound != b->lower_bound) { - return a->lower_bound < b->lower_bound; - } - // Deterministic tie-breaking by BSP identity - if (a->origin_worker_id != b->origin_worker_id) { - return a->origin_worker_id < b->origin_worker_id; - } - return a->creation_seq < b->creation_seq; - }); - mip_node_t* node = *best_it; - backlog.erase(best_it); - return node; - } - - return nullptr; - } - - // ========================================================================== - // Queue state queries - // ========================================================================== - - // Check if worker has work available - bool has_work() const - { - return current_node != nullptr || !plunge_stack.empty() || !backlog.empty(); - } - - // Get number of nodes in worker's queues (including paused node) - size_t queue_size() const - { - return plunge_stack.size() + backlog.size() + (current_node != nullptr ? 1 : 0); - } - - // Get number of nodes in plunge stack only - size_t plunge_stack_size() const { return plunge_stack.size(); } - - // Get number of nodes in backlog only - size_t backlog_size() const { return backlog.size(); } - - // ========================================================================== - // Load balancing support - // ========================================================================== - - // Extract all nodes from worker (for load balancing) - // Returns nodes in arbitrary order - caller should sort if deterministic order needed - std::vector*> extract_all_nodes() - { - std::vector*> nodes; - nodes.reserve(queue_size()); - - // Include paused node if any - if (current_node != nullptr) { - nodes.push_back(current_node); - current_node = nullptr; - } - - // Extract from plunge stack - for (auto* node : plunge_stack) { - nodes.push_back(node); - } - plunge_stack.clear(); - - // Extract from backlog - for (auto* node : backlog) { - nodes.push_back(node); - } - backlog.clear(); - - return nodes; - } - - // Extract only backlog nodes (for redistribution at horizon sync) - // Plunge stack nodes stay with worker for locality - std::vector*> extract_backlog_nodes() - { - std::vector*> nodes = std::move(backlog); - backlog.clear(); - return nodes; - } - - // Clear all queues without returning nodes (use with caution) - void clear_queue() - { - current_node = nullptr; - plunge_stack.clear(); - backlog.clear(); - } - - // Record an event - void record_event(bb_event_t event) - { - event.event_sequence = event_sequence++; - events.add(std::move(event)); - } - - // Pause current node processing at horizon boundary - void pause_current_node(mip_node_t* node, double accumulated_vt) - { - node->accumulated_vt = accumulated_vt; - node->bsp_state = bsp_node_state_t::PAUSED; - current_node = node; - - record_event( - bb_event_t::make_paused(clock, worker_id, node->node_id, 0, accumulated_vt)); - } - - // Record node branching event - void record_branched( - mip_node_t* node, i_t down_child_id, i_t up_child_id, i_t branch_var, f_t branch_val) - { - record_event(bb_event_t::make_branched(clock, - worker_id, - node->node_id, - 0, - down_child_id, - up_child_id, - node->lower_bound, - branch_var, - branch_val)); - } - - // Record integer solution found - void record_integer_solution(mip_node_t* node, f_t objective) - { - record_event( - bb_event_t::make_integer_solution(clock, worker_id, node->node_id, 0, objective)); - } - - // Record node fathomed - void record_fathomed(mip_node_t* node, f_t lower_bound) - { - record_event( - bb_event_t::make_fathomed(clock, worker_id, node->node_id, 0, lower_bound)); - } - - // Record node infeasible - void record_infeasible(mip_node_t* node) - { - record_event(bb_event_t::make_infeasible(clock, worker_id, node->node_id, 0)); - } - - // Record numerical error - void record_numerical(mip_node_t* node) - { - record_event(bb_event_t::make_numerical(clock, worker_id, node->node_id, 0)); - } - - // Update clock with work units - void advance_clock(double work_units) - { - clock += work_units; - work_units_this_horizon += work_units; - total_work_units += work_units; - work_context.record_work(work_units); - } - - // Track node processed (called when a node LP solve completes) - void track_node_processed() - { - ++nodes_processed_this_horizon; - ++total_nodes_processed; - } - - // Track node branched - void track_node_branched() { ++total_nodes_branched; } - - // Track node pruned (fathomed due to bound) - void track_node_pruned() { ++total_nodes_pruned; } - - // Track node infeasible - void track_node_infeasible() { ++total_nodes_infeasible; } - - // Track integer solution found - void track_integer_solution() { ++total_integer_solutions; } - - // Track node assigned via load balancing - void track_node_assigned() { ++total_nodes_assigned; } -}; - -// Container for all worker states in BSP B&B -template -class bb_worker_pool_t { - public: - bb_worker_pool_t() = default; - - // Initialize pool with specified number of workers - void initialize(int num_workers, - const lp_problem_t& original_lp, - const csr_matrix_t& Arow, - const std::vector& var_types, - i_t refactor_frequency, - bool deterministic) - { - workers_.clear(); - workers_.reserve(num_workers); - for (int i = 0; i < num_workers; ++i) { - workers_.emplace_back(i); - workers_.back().initialize(original_lp, Arow, var_types, refactor_frequency, deterministic); - } - } - - // Get worker by ID - bb_worker_state_t& operator[](int worker_id) { return workers_[worker_id]; } - - const bb_worker_state_t& operator[](int worker_id) const { return workers_[worker_id]; } - - // Get number of workers - int size() const { return static_cast(workers_.size()); } - - // Reset all workers for new horizon - void reset_for_horizon(double horizon_start, double horizon_end, f_t global_upper_bound) - { - for (auto& worker : workers_) { - worker.reset_for_horizon(horizon_start, horizon_end, global_upper_bound); - } - } - - // Collect all events from all workers into a single sorted batch - bb_event_batch_t collect_and_sort_events() - { - bb_event_batch_t all_events; - for (auto& worker : workers_) { - for (auto& event : worker.events.events) { - all_events.add(std::move(event)); - } - worker.events.clear(); - } - all_events.sort_for_replay(); - return all_events; - } - - // Check if any worker has work - bool any_has_work() const - { - for (const auto& worker : workers_) { - if (worker.has_work()) return true; - } - return false; - } - - // Get total queue size across all workers - size_t total_queue_size() const - { - size_t total = 0; - for (const auto& worker : workers_) { - total += worker.queue_size(); - } - return total; - } - - // Iterator support - auto begin() { return workers_.begin(); } - auto end() { return workers_.end(); } - auto begin() const { return workers_.begin(); } - auto end() const { return workers_.end(); } - - private: - std::vector> workers_; -}; - -} // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/bounds_strengthening.cpp b/cpp/src/dual_simplex/bounds_strengthening.cpp index 3f40110740..6506ce4873 100644 --- a/cpp/src/dual_simplex/bounds_strengthening.cpp +++ b/cpp/src/dual_simplex/bounds_strengthening.cpp @@ -34,9 +34,9 @@ static inline bool check_infeasibility(f_t min_a, f_t max_a, f_t cnst_lb, f_t cn #define DEBUG_BOUND_STRENGTHENING 0 -template -void print_bounds_stats(const LowerVec& lower, - const UpperVec& upper, +template +void print_bounds_stats(const std::vector& lower, + const std::vector& upper, const simplex_solver_settings_t& settings, const std::string msg) { diff --git a/cpp/src/dual_simplex/bounds_strengthening.hpp b/cpp/src/dual_simplex/bounds_strengthening.hpp index c9257b367e..55fdb36866 100644 --- a/cpp/src/dual_simplex/bounds_strengthening.hpp +++ b/cpp/src/dual_simplex/bounds_strengthening.hpp @@ -35,12 +35,12 @@ class bounds_strengthening_t { const csr_matrix_t& Arow; const std::vector& var_types; - // Instrumented vectors for memory tracking - cuopt::ins_vector lower; - cuopt::ins_vector upper; - cuopt::ins_vector delta_min_activity; - cuopt::ins_vector delta_max_activity; - cuopt::ins_vector constraint_lb; - cuopt::ins_vector constraint_ub; + std::vector lower; + std::vector upper; + + std::vector delta_min_activity; + std::vector delta_max_activity; + std::vector constraint_lb; + std::vector constraint_ub; }; } // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/bsp_debug.hpp b/cpp/src/dual_simplex/bsp_debug.hpp deleted file mode 100644 index edf543bb07..0000000000 --- a/cpp/src/dual_simplex/bsp_debug.hpp +++ /dev/null @@ -1,1139 +0,0 @@ -/* clang-format off */ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -/* clang-format on */ - -#pragma once - -// Enable BSP debug macros. The actual logging is controlled at runtime via -// environment variables (CUOPT_BSP_DEBUG_*). This define enables the macro -// infrastructure; without it, all BSP_DEBUG_* macros become complete no-ops. -#define BSP_DEBUG_ENABLED - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cuopt::linear_programming::dual_simplex { - -// ============================================================================ -// BSP Debug Event Types -// ============================================================================ - -enum class bsp_log_event_t { - HORIZON_START, // New horizon begins - HORIZON_END, // Horizon completed - HORIZON_HASH, // Determinism fingerprint for horizon - NODE_ASSIGNED, // Node assigned to worker - NODE_SOLVE_START, // Worker starts solving node - NODE_SOLVE_END, // Worker finishes node (with result type) - NODE_PAUSED, // Node paused at horizon - NODE_RESUMED, // Paused node resumed - NODE_BRANCHED, // Node branched into children - NODE_PRUNED, // Node pruned (cutoff) - NODE_INTEGER, // Integer solution found - NODE_INFEASIBLE, // Node infeasible - NODE_FATHOMED, // Node fathomed - FINAL_ID_ASSIGNED, // Final ID assigned during sync - HEURISTIC_RECEIVED, // Heuristic solution received - INCUMBENT_UPDATE, // New best solution found - SYNC_PHASE_START, // Sync phase begins - SYNC_PHASE_END, // Sync phase ends - WORKER_IDLE, // Worker has no work - LP_INPUT, // LP solver input hashes (for determinism debugging) - LP_OUTPUT, // LP solver output hashes (for determinism debugging) -}; - -inline const char* bsp_log_event_name(bsp_log_event_t event) -{ - switch (event) { - case bsp_log_event_t::HORIZON_START: return "HORIZON_START"; - case bsp_log_event_t::HORIZON_END: return "HORIZON_END"; - case bsp_log_event_t::HORIZON_HASH: return "HORIZON_HASH"; - case bsp_log_event_t::NODE_ASSIGNED: return "NODE_ASSIGNED"; - case bsp_log_event_t::NODE_SOLVE_START: return "NODE_SOLVE_START"; - case bsp_log_event_t::NODE_SOLVE_END: return "NODE_SOLVE_END"; - case bsp_log_event_t::NODE_PAUSED: return "NODE_PAUSED"; - case bsp_log_event_t::NODE_RESUMED: return "NODE_RESUMED"; - case bsp_log_event_t::NODE_BRANCHED: return "NODE_BRANCHED"; - case bsp_log_event_t::NODE_PRUNED: return "NODE_PRUNED"; - case bsp_log_event_t::NODE_INTEGER: return "NODE_INTEGER"; - case bsp_log_event_t::NODE_INFEASIBLE: return "NODE_INFEASIBLE"; - case bsp_log_event_t::NODE_FATHOMED: return "NODE_FATHOMED"; - case bsp_log_event_t::FINAL_ID_ASSIGNED: return "FINAL_ID_ASSIGNED"; - case bsp_log_event_t::HEURISTIC_RECEIVED: return "HEURISTIC_RECEIVED"; - case bsp_log_event_t::INCUMBENT_UPDATE: return "INCUMBENT_UPDATE"; - case bsp_log_event_t::SYNC_PHASE_START: return "SYNC_PHASE_START"; - case bsp_log_event_t::SYNC_PHASE_END: return "SYNC_PHASE_END"; - case bsp_log_event_t::WORKER_IDLE: return "WORKER_IDLE"; - case bsp_log_event_t::LP_INPUT: return "LP_INPUT"; - case bsp_log_event_t::LP_OUTPUT: return "LP_OUTPUT"; - default: return "UNKNOWN"; - } -} - -// ============================================================================ -// BSP Debug Settings -// ============================================================================ - -/** - * @brief BSP debug settings controlled via environment variables. - * - * Environment variables: - * CUOPT_BSP_DEBUG_LOG=1 Enable structured event log - * CUOPT_BSP_DEBUG_TIMELINE=1 Emit ASCII timeline visualization - * CUOPT_BSP_DEBUG_TREE=1 Emit DOT tree state files - * CUOPT_BSP_DEBUG_JSON=1 Emit JSON state dumps - * CUOPT_BSP_DEBUG_TRACE=1 Emit determinism trace file - * CUOPT_BSP_DEBUG_ALL=1 Enable all debug output - * CUOPT_BSP_DEBUG_DIR=path Output directory (default: ./bsp_debug/) - * CUOPT_BSP_DEBUG_LEVEL=N Log level: 0=off, 1=major events, 2=all events - * CUOPT_BSP_DEBUG_FLUSH_EVERY_HORIZON=0 Disable flushing debug output every horizon (default: 1) - */ -struct bsp_debug_settings_t { - bool enable_event_log{false}; - bool enable_timeline{false}; - bool enable_tree_dot{false}; - bool enable_state_json{false}; - bool enable_determinism_trace{false}; - bool flush_every_horizon{true}; // Flush debug output at end of each horizon - std::string output_dir{"./bsp_debug/"}; - int log_level{1}; // 0=off, 1=major events, 2=all events - - bool any_enabled() const - { - return enable_event_log || enable_timeline || enable_tree_dot || enable_state_json || - enable_determinism_trace; - } - - void enable_all() - { - enable_event_log = true; - enable_timeline = true; - enable_tree_dot = true; - enable_state_json = true; - enable_determinism_trace = true; - } - - void disable_all() - { - enable_event_log = false; - enable_timeline = false; - enable_tree_dot = false; - enable_state_json = false; - enable_determinism_trace = false; - } - - /** - * @brief Initialize settings from environment variables. - * @return A bsp_debug_settings_t populated from environment variables. - */ - static bsp_debug_settings_t from_environment() - { - bsp_debug_settings_t settings; - - auto get_env_bool = [](const char* name) -> bool { - const char* val = std::getenv(name); - if (val == nullptr) return false; - return std::string(val) == "1" || std::string(val) == "true" || std::string(val) == "TRUE"; - }; - - auto get_env_int = [](const char* name, int default_val) -> int { - const char* val = std::getenv(name); - if (val == nullptr) return default_val; - try { - return std::stoi(val); - } catch (...) { - return default_val; - } - }; - - auto get_env_string = [](const char* name, const std::string& default_val) -> std::string { - const char* val = std::getenv(name); - if (val == nullptr) return default_val; - return std::string(val); - }; - - // Check for CUOPT_BSP_DEBUG_ALL first - if (get_env_bool("CUOPT_BSP_DEBUG_ALL")) { - settings.enable_all(); - } else { - settings.enable_event_log = get_env_bool("CUOPT_BSP_DEBUG_LOG"); - settings.enable_timeline = get_env_bool("CUOPT_BSP_DEBUG_TIMELINE"); - settings.enable_tree_dot = get_env_bool("CUOPT_BSP_DEBUG_TREE"); - settings.enable_state_json = get_env_bool("CUOPT_BSP_DEBUG_JSON"); - settings.enable_determinism_trace = get_env_bool("CUOPT_BSP_DEBUG_TRACE"); - } - - settings.output_dir = get_env_string("CUOPT_BSP_DEBUG_DIR", "./bsp_debug/"); - settings.log_level = get_env_int("CUOPT_BSP_DEBUG_LEVEL", 1); - - // Flush every horizon is ON by default; set to 0 to disable - const char* flush_env = std::getenv("CUOPT_BSP_DEBUG_FLUSH_EVERY_HORIZON"); - if (flush_env != nullptr) { - settings.flush_every_horizon = - (std::string(flush_env) == "1" || std::string(flush_env) == "true"); - } - - return settings; - } -}; - -// ============================================================================ -// Timeline Event (for ASCII visualization) -// ============================================================================ - -struct timeline_event_t { - double vt_start; - double vt_end; - int worker_id; - int node_id; - int final_id; - std::string result; // "BRANCH", "INTEGER", "FATHOMED", "PAUSED", etc. -}; - -// ============================================================================ -// BSP Debug Logger Class -// ============================================================================ - -template -class bsp_debug_logger_t { - public: - bsp_debug_logger_t() : start_time_(std::chrono::steady_clock::now()) {} - - void set_settings(const bsp_debug_settings_t& settings) - { - settings_ = settings; - if (settings_.any_enabled()) { - // Create output directory if it doesn't exist - try { - std::filesystem::create_directories(settings_.output_dir); - } catch (const std::filesystem::filesystem_error& e) { - // Silently disable debug output if we can't create the directory - settings_.enable_event_log = false; - settings_.enable_timeline = false; - settings_.enable_tree_dot = false; - settings_.enable_state_json = false; - settings_.enable_determinism_trace = false; - } - - // Clear timeline file at start (it uses append mode for each horizon) - if (settings_.enable_timeline) { - std::string filename = settings_.output_dir + "bsp_timeline.txt"; - std::ofstream file(filename, std::ios::trunc); - if (file.is_open()) { - file << "BSP Timeline Visualization\n"; - file << "==========================\n"; - file.close(); - } - } - } - } - - void set_num_workers(int num_workers) { num_workers_ = num_workers; } - - void set_horizon_step(double step) { horizon_step_ = step; } - - // ======================================================================== - // Event Logging - // ======================================================================== - - void log_horizon_start(int horizon_num, double vt_start, double vt_end) - { - current_horizon_ = horizon_num; - horizon_vt_start_ = vt_start; - horizon_vt_end_ = vt_end; - - if (settings_.enable_event_log) { - log_event(vt_start, - -1, - bsp_log_event_t::HORIZON_START, - -1, - -1, - "horizon=[" + std::to_string(vt_start) + "," + std::to_string(vt_end) + ")"); - } - - if (settings_.enable_determinism_trace) { - trace_ss_ << "H" << horizon_num << ":START:vt=[" << vt_start << "," << vt_end << ")\n"; - } - - // Clear timeline events for new horizon - timeline_events_.clear(); - } - - void log_horizon_end(int horizon_num, double vt) - { - if (settings_.enable_event_log) { log_event(vt, -1, bsp_log_event_t::HORIZON_END, -1, -1, ""); } - - if (settings_.enable_timeline) { emit_timeline_for_horizon(horizon_num); } - } - - // Log determinism fingerprint hash for the horizon - // This hash captures all state that should be identical across deterministic runs - void log_horizon_hash(int horizon_num, double vt, uint32_t hash) - { - std::lock_guard lock(mutex_); - - if (settings_.enable_event_log) { - std::stringstream ss; - ss << "hash=0x" << std::hex << std::setfill('0') << std::setw(8) << hash; - log_event_unlocked(vt, -1, bsp_log_event_t::HORIZON_HASH, -1, -1, ss.str()); - } - - if (settings_.enable_determinism_trace) { - trace_ss_ << "H" << horizon_num << ":HASH:0x" << std::hex << std::setfill('0') << std::setw(8) - << hash << std::dec << "\n"; - } - } - - void log_node_assigned(double vt, int worker_id, i_t node_id, i_t final_id, f_t lower_bound) - { - if (settings_.enable_event_log) { - log_event(vt, - worker_id, - bsp_log_event_t::NODE_ASSIGNED, - node_id, - final_id, - "lb=" + std::to_string(lower_bound)); - } - - if (settings_.enable_determinism_trace) { - assign_trace_ss_ << "W" << worker_id << "=" << final_id << ","; - } - } - - void flush_assign_trace() - { - if (settings_.enable_determinism_trace && assign_trace_ss_.str().length() > 0) { - std::string s = assign_trace_ss_.str(); - if (!s.empty() && s.back() == ',') s.pop_back(); - trace_ss_ << "H" << current_horizon_ << ":ASSIGN:" << s << "\n"; - assign_trace_ss_.str(""); - assign_trace_ss_.clear(); - } - } - - void log_solve_start( - double vt, int worker_id, i_t node_id, i_t final_id, double work_limit, bool is_resumed) - { - std::lock_guard lock(mutex_); - - if (settings_.enable_event_log && settings_.log_level >= 2) { - log_event_unlocked(vt, - worker_id, - bsp_log_event_t::NODE_SOLVE_START, - node_id, - final_id, - "work_limit=" + std::to_string(work_limit)); - } - - // Start timeline event - current_solve_start_[worker_id] = vt; - current_solve_node_[worker_id] = node_id; - current_solve_fid_[worker_id] = final_id; - } - - void log_solve_end( - double vt, int worker_id, i_t node_id, i_t final_id, const std::string& result, f_t lower_bound) - { - std::lock_guard lock(mutex_); - - if (settings_.enable_event_log) { - log_event_unlocked(vt, - worker_id, - bsp_log_event_t::NODE_SOLVE_END, - node_id, - final_id, - "result=" + result + ",lb=" + std::to_string(lower_bound)); - } - - // Complete timeline event - if (settings_.enable_timeline) { - timeline_event_t te; - te.vt_start = current_solve_start_[worker_id]; - te.vt_end = vt; - te.worker_id = worker_id; - te.node_id = node_id; - te.final_id = final_id; - te.result = result; - timeline_events_.push_back(te); - } - } - - void log_branched(double vt, int worker_id, i_t parent_id, i_t parent_fid, i_t down_id, i_t up_id) - { - std::lock_guard lock(mutex_); - - if (settings_.enable_event_log) { - log_event_unlocked(vt, - worker_id, - bsp_log_event_t::NODE_BRANCHED, - parent_id, - parent_fid, - "children=" + std::to_string(down_id) + "," + std::to_string(up_id)); - } - - if (settings_.enable_determinism_trace) { - // Log parent final_id (deterministic) and children node_ids (non-deterministic until sync) - events_trace_ss_ << "BRANCH(" << vt << ",W" << worker_id << ",F" << parent_fid << "->" - << down_id << "," << up_id << "),"; - } - } - - void log_paused(double vt, int worker_id, i_t node_id, i_t final_id, f_t accumulated_vt) - { - std::lock_guard lock(mutex_); - - if (settings_.enable_event_log) { - log_event_unlocked(vt, - worker_id, - bsp_log_event_t::NODE_PAUSED, - node_id, - final_id, - "acc_vt=" + std::to_string(accumulated_vt)); - } - - if (settings_.enable_determinism_trace) { - events_trace_ss_ << "PAUSE(" << vt << ",W" << worker_id << "," << node_id << "),"; - } - } - - void log_integer(double vt, int worker_id, i_t node_id, f_t objective) - { - std::lock_guard lock(mutex_); - - if (settings_.enable_event_log) { - log_event_unlocked(vt, - worker_id, - bsp_log_event_t::NODE_INTEGER, - node_id, - -1, - "obj=" + std::to_string(objective)); - } - - if (settings_.enable_determinism_trace) { - events_trace_ss_ << "INT(" << vt << ",W" << worker_id << "," << objective << "),"; - } - } - - void log_fathomed(double vt, int worker_id, i_t node_id, f_t lower_bound) - { - std::lock_guard lock(mutex_); - - if (settings_.enable_event_log && settings_.log_level >= 2) { - log_event_unlocked(vt, - worker_id, - bsp_log_event_t::NODE_FATHOMED, - node_id, - -1, - "lb=" + std::to_string(lower_bound)); - } - } - - void log_infeasible(double vt, int worker_id, i_t node_id) - { - std::lock_guard lock(mutex_); - - if (settings_.enable_event_log && settings_.log_level >= 2) { - log_event_unlocked(vt, worker_id, bsp_log_event_t::NODE_INFEASIBLE, node_id, -1, ""); - } - } - - void log_pruned(double vt, i_t node_id, i_t final_id, f_t lower_bound, f_t upper_bound) - { - std::lock_guard lock(mutex_); - - if (settings_.enable_event_log && settings_.log_level >= 2) { - log_event_unlocked( - vt, - -1, - bsp_log_event_t::NODE_PRUNED, - node_id, - final_id, - "lb=" + std::to_string(lower_bound) + ",ub=" + std::to_string(upper_bound)); - } - } - - void log_sync_phase_start(double vt, size_t num_events) - { - if (settings_.enable_event_log) { - log_event(vt, - -1, - bsp_log_event_t::SYNC_PHASE_START, - -1, - -1, - "num_events=" + std::to_string(num_events)); - } - } - - void log_sync_phase_end(double vt) - { - if (settings_.enable_event_log) { - log_event(vt, -1, bsp_log_event_t::SYNC_PHASE_END, -1, -1, ""); - } - - // Flush event trace - if (settings_.enable_determinism_trace) { - std::string s = events_trace_ss_.str(); - if (!s.empty() && s.back() == ',') s.pop_back(); - trace_ss_ << "H" << current_horizon_ << ":EVENTS:" << s << "\n"; - events_trace_ss_.str(""); - events_trace_ss_.clear(); - } - } - - void log_final_id_assigned(i_t provisional_id, i_t final_id) - { - if (settings_.enable_determinism_trace) { - final_ids_trace_ss_ << provisional_id << "->" << final_id << ","; - } - } - - void flush_final_ids_trace() - { - if (settings_.enable_determinism_trace && final_ids_trace_ss_.str().length() > 0) { - std::string s = final_ids_trace_ss_.str(); - if (!s.empty() && s.back() == ',') s.pop_back(); - trace_ss_ << "H" << current_horizon_ << ":FINAL_IDS:" << s << "\n"; - final_ids_trace_ss_.str(""); - final_ids_trace_ss_.clear(); - } - } - - void log_heuristic_received(double vt, f_t objective) - { - if (settings_.enable_event_log) { - log_event( - vt, -1, bsp_log_event_t::HEURISTIC_RECEIVED, -1, -1, "obj=" + std::to_string(objective)); - } - - if (settings_.enable_determinism_trace) { - trace_ss_ << "H" << current_horizon_ << ":HEURISTIC:" << objective << "@" << vt << "\n"; - } - } - - void log_incumbent_update(double vt, f_t objective, const std::string& source) - { - if (settings_.enable_event_log) { - log_event(vt, - -1, - bsp_log_event_t::INCUMBENT_UPDATE, - -1, - -1, - "obj=" + std::to_string(objective) + ",source=" + source); - } - - if (settings_.enable_determinism_trace) { - trace_ss_ << "H" << current_horizon_ << ":INCUMBENT:" << objective << "@" << vt << "(" - << source << ")\n"; - } - } - - void log_heap_order(const std::vector& final_ids) - { - if (settings_.enable_determinism_trace) { - trace_ss_ << "H" << current_horizon_ << ":HEAP_ORDER:"; - for (size_t i = 0; i < final_ids.size(); ++i) { - trace_ss_ << final_ids[i]; - if (i < final_ids.size() - 1) trace_ss_ << ","; - } - trace_ss_ << "\n"; - } - } - - // ======================================================================== - // LP Determinism Logging (for debugging non-determinism in LP solver) - // ======================================================================== - - void log_lp_input(int worker_id, - i_t node_id, - uint64_t path_hash, - i_t depth, - uint64_t vstatus_hash, - uint64_t bounds_hash) - { - std::lock_guard lock(mutex_); - - // Log to main events file (always when BSP debug is enabled) - if (settings_.enable_event_log) { - char details[320]; - std::snprintf(details, - sizeof(details), - "path=0x%016llx,d=%d,vstatus=0x%016llx,bounds=0x%016llx", - static_cast(path_hash), - depth, - static_cast(vstatus_hash), - static_cast(bounds_hash)); - log_event_unlocked(-1.0, worker_id, bsp_log_event_t::LP_INPUT, node_id, -1, details); - } - - // Also log to determinism trace if enabled - if (settings_.enable_determinism_trace) { - char buf[320]; - std::snprintf(buf, - sizeof(buf), - "LP_IN(W%d,N%d,path=0x%016llx,d=%d,vs=0x%016llx,bnd=0x%016llx)", - worker_id, - node_id, - static_cast(path_hash), - depth, - static_cast(vstatus_hash), - static_cast(bounds_hash)); - events_trace_ss_ << buf << ","; - } - } - - void log_lp_output(int worker_id, - i_t node_id, - uint64_t path_hash, - int status, - i_t iters, - uint64_t obj_bits, - uint64_t sol_hash) - { - std::lock_guard lock(mutex_); - - // Log to main events file (always when BSP debug is enabled) - if (settings_.enable_event_log) { - char details[320]; - std::snprintf(details, - sizeof(details), - "path=0x%016llx,status=%d,iters=%d,obj=0x%016llx,sol=0x%016llx", - static_cast(path_hash), - status, - iters, - static_cast(obj_bits), - static_cast(sol_hash)); - log_event_unlocked(-1.0, worker_id, bsp_log_event_t::LP_OUTPUT, node_id, -1, details); - } - - // Also log to determinism trace if enabled - if (settings_.enable_determinism_trace) { - char buf[320]; - std::snprintf(buf, - sizeof(buf), - "LP_OUT(W%d,N%d,path=0x%016llx,st=%d,it=%d,obj=0x%016llx,sol=0x%016llx)", - worker_id, - node_id, - static_cast(path_hash), - status, - iters, - static_cast(obj_bits), - static_cast(sol_hash)); - events_trace_ss_ << buf << ","; - } - } - - void log_branch_decision(i_t branch_var, uint64_t score_hash, size_t num_ties) - { - if (settings_.enable_determinism_trace && settings_.log_level >= 2) { - std::lock_guard lock(mutex_); - char buf[128]; - std::snprintf(buf, - sizeof(buf), - "BVAR(v=%d,sc=0x%016llx,ties=%zu)", - branch_var, - static_cast(score_hash), - num_ties); - events_trace_ss_ << buf << ","; - } - } - - // ======================================================================== - // Tree State (DOT format) - // ======================================================================== - - void emit_tree_state(int horizon_num, const mip_node_t& root, f_t upper_bound) - { - if (!settings_.enable_tree_dot) return; - - std::string filename = - settings_.output_dir + "bsp_tree_h" + std::to_string(horizon_num) + ".dot"; - std::ofstream file(filename); - if (!file.is_open()) return; - - file << "digraph BSPTree_Horizon" << horizon_num << " {\n"; - file << " rankdir=TB;\n"; - file << " node [shape=box];\n\n"; - - // Traverse tree and emit nodes - emit_tree_node_recursive(file, &root, upper_bound); - - file << "}\n"; - file.close(); - } - - // ======================================================================== - // JSON State Dump - // ======================================================================== - - template - void emit_state_json(int horizon_num, - double vt_start, - double vt_end, - i_t next_final_id, - f_t upper_bound, - f_t lower_bound, - i_t nodes_explored, - i_t nodes_unexplored, - const WorkerPool& workers, - const std::vector*>& heap_nodes, - const bb_event_batch_t& events) - { - if (!settings_.enable_state_json) return; - - std::string filename = - settings_.output_dir + "bsp_state_h" + std::to_string(horizon_num) + ".json"; - std::ofstream file(filename); - if (!file.is_open()) return; - - file << "{\n"; - file << " \"horizon\": " << horizon_num << ",\n"; - file << " \"vt_range\": [" << vt_start << ", " << vt_end << "],\n"; - file << " \"bsp_next_final_id\": " << next_final_id << ",\n"; - file << " \"upper_bound\": " << (upper_bound >= 1e30 ? "\"inf\"" : std::to_string(upper_bound)) - << ",\n"; - file << " \"lower_bound\": " << lower_bound << ",\n"; - file << " \"nodes_explored\": " << nodes_explored << ",\n"; - file << " \"nodes_unexplored\": " << nodes_unexplored << ",\n"; - - // Workers - file << " \"workers\": [\n"; - for (int i = 0; i < workers.size(); ++i) { - const auto& w = workers[i]; - file << " {\n"; - file << " \"id\": " << i << ",\n"; - file << " \"clock\": " << w.clock << ",\n"; - if (w.current_node != nullptr) { - file << " \"current_node\": {\"id\": " << w.current_node->node_id - << ", \"origin_worker\": " << w.current_node->origin_worker_id - << ", \"creation_seq\": " << w.current_node->creation_seq - << ", \"acc_vt\": " << w.current_node->accumulated_vt << "},\n"; - } else { - file << " \"current_node\": null,\n"; - } - file << " \"plunge_stack_size\": " << w.plunge_stack.size() << ",\n"; - file << " \"backlog_size\": " << w.backlog.size() << "\n"; - file << " }" << (i < workers.size() - 1 ? "," : "") << "\n"; - } - file << " ],\n"; - - // Heap - file << " \"heap\": [\n"; - for (size_t i = 0; i < heap_nodes.size(); ++i) { - const auto* n = heap_nodes[i]; - file << " {\"id\": " << n->node_id << ", \"origin_worker\": " << n->origin_worker_id - << ", \"creation_seq\": " << n->creation_seq << ", \"lb\": " << n->lower_bound << "}" - << (i < heap_nodes.size() - 1 ? "," : "") << "\n"; - } - file << " ],\n"; - - // Events this horizon - file << " \"events_count\": " << events.events.size() << "\n"; - - file << "}\n"; - file.close(); - } - - // ======================================================================== - // Finalization - // ======================================================================== - - void finalize() - { - if (settings_.enable_event_log) { flush_event_log(); } - - if (settings_.enable_determinism_trace) { flush_trace(); } - } - - private: - bsp_debug_settings_t settings_; - std::chrono::steady_clock::time_point start_time_; - int num_workers_{0}; - double horizon_step_{5.0}; - int current_horizon_{0}; - double horizon_vt_start_{0.0}; - double horizon_vt_end_{0.0}; - - std::mutex mutex_; - std::stringstream log_ss_; - std::stringstream trace_ss_; - std::stringstream assign_trace_ss_; - std::stringstream events_trace_ss_; - std::stringstream final_ids_trace_ss_; - - // Timeline tracking - std::vector timeline_events_; - std::map current_solve_start_; - std::map current_solve_node_; - std::map current_solve_fid_; - - double get_real_time() const - { - auto now = std::chrono::steady_clock::now(); - return std::chrono::duration(now - start_time_).count(); - } - - void log_event(double vt, - int worker_id, - bsp_log_event_t event, - i_t node_id, - i_t final_id, - const std::string& details) - { - std::lock_guard lock(mutex_); - log_event_unlocked(vt, worker_id, event, node_id, final_id, details); - } - - void log_event_unlocked(double vt, - int worker_id, - bsp_log_event_t event, - i_t node_id, - i_t final_id, - const std::string& details) - { - log_ss_ << std::fixed << std::setprecision(3) << vt << "\t" << get_real_time() << "\t" - << current_horizon_ << "\t" << (worker_id < 0 ? "COORD" : std::to_string(worker_id)) - << "\t" << bsp_log_event_name(event) << "\t" - << (node_id < 0 ? "-" : std::to_string(node_id)) << "\t" - << (final_id < 0 ? "-" : std::to_string(final_id)) << "\t" << details << "\n"; - } - - void flush_event_log() - { - std::string filename = settings_.output_dir + "bsp_events.log"; - std::ofstream file(filename); - if (file.is_open()) { - file << "VT\tREAL_TIME\tHORIZON\tWORKER\tEVENT\tNODE_ID\tFINAL_ID\tDETAILS\n"; - file << log_ss_.str(); - file.close(); - } - } - - void flush_trace() - { - std::string filename = settings_.output_dir + "bsp_trace.txt"; - std::ofstream file(filename); - if (file.is_open()) { - file << "# BSP Determinism Trace\n"; - file << "# Compare this file across runs - must be identical for determinism\n\n"; - file << trace_ss_.str(); - file.close(); - } - } - - void emit_timeline_for_horizon(int horizon_num) - { - std::string filename = settings_.output_dir + "bsp_timeline.txt"; - std::ofstream file(filename, std::ios::app); - if (!file.is_open()) return; - - const int width = 80; - const double vt_min = horizon_vt_start_; - const double vt_max = horizon_vt_end_; - const double vt_range = vt_max - vt_min; - - // Avoid division by zero - if (vt_range <= 0.0) { - file << "\nHorizon " << horizon_num << ": Empty or invalid VT range\n"; - return; - } - - file << "\n"; - file << std::string(width, '=') << "\n"; - file << "Horizon " << horizon_num << ": VT [" << vt_min << ", " << vt_max << ")\n"; - file << std::string(width, '-') << "\n"; - - // VT scale - file << " VT: "; - for (int i = 0; i <= 5; ++i) { - double vt = vt_min + (vt_range * i / 5.0); - file << std::setw(10) << std::fixed << std::setprecision(1) << vt; - } - file << "\n"; - - // Worker rows - for (int w = 0; w < num_workers_; ++w) { - file << " W" << w << ": "; - - std::string row(60, '-'); - - for (const auto& te : timeline_events_) { - if (te.worker_id != w) continue; - - int start_col = static_cast((te.vt_start - vt_min) / vt_range * 60); - int end_col = static_cast((te.vt_end - vt_min) / vt_range * 60); - start_col = std::max(0, std::min(59, start_col)); - end_col = std::max(0, std::min(59, end_col)); - - char fill_char = '#'; - if (te.result == "PAUSED") - fill_char = '%'; - else if (te.result == "INTEGER") - fill_char = '*'; - else if (te.result == "FATHOMED" || te.result == "INFEASIBLE") - fill_char = 'x'; - - for (int c = start_col; c <= end_col; ++c) { - row[c] = fill_char; - } - } - - file << row << "\n"; - - // Labels row - file << " "; - std::string labels(60, ' '); - for (const auto& te : timeline_events_) { - if (te.worker_id != w) continue; - int mid_col = static_cast(((te.vt_start + te.vt_end) / 2 - vt_min) / vt_range * 60); - mid_col = std::max(0, std::min(55, mid_col)); - - std::string label = "N" + std::to_string(te.final_id); - for (size_t i = 0; i < label.size() && mid_col + i < 60; ++i) { - labels[mid_col + i] = label[i]; - } - } - file << labels << "\n"; - } - - file << std::string(width, '-') << "\n"; - file << "Legend: ### solving %%% paused *** integer xxx fathomed/infeasible\n"; - file << std::string(width, '=') << "\n"; - } - - void emit_tree_node_recursive(std::ofstream& file, - const mip_node_t* node, - f_t upper_bound) - { - if (node == nullptr) return; - - // Determine fill color based on status - std::string color = "white"; - std::string status_str; - switch (node->status) { - case node_status_t::PENDING: - color = "lightgray"; - status_str = "PENDING"; - break; - case node_status_t::INTEGER_FEASIBLE: - color = "lightgreen"; - status_str = "INTEGER"; - break; - case node_status_t::INFEASIBLE: - color = "lightcoral"; - status_str = "INFEASIBLE"; - break; - case node_status_t::FATHOMED: - color = "lightsalmon"; - status_str = "FATHOMED"; - break; - case node_status_t::HAS_CHILDREN: - color = "lightblue"; - status_str = "BRANCHED"; - break; - case node_status_t::NUMERICAL: - color = "orange"; - status_str = "NUMERICAL"; - break; - } - - if (node->bsp_state == bsp_node_state_t::PAUSED) { - color = "yellow"; - status_str = "PAUSED"; - } - - file << " N" << node->node_id << " [label=\"N" << node->node_id; - if (node->has_bsp_identity()) - file << " (w" << node->origin_worker_id << ":" << node->creation_seq << ")"; - file << "\\nlb=" << std::fixed << std::setprecision(1) << node->lower_bound; - file << "\\n" << status_str; - if (node->bsp_state == bsp_node_state_t::PAUSED) { - file << "\\nacc_vt=" << node->accumulated_vt; - } - file << "\" style=filled fillcolor=" << color << "];\n"; - - // Emit edges to children - if (node->get_down_child() != nullptr) { - file << " N" << node->node_id << " -> N" << node->get_down_child()->node_id << " [label=\"x" - << node->branch_var << "<=" << std::floor(node->fractional_val) << "\"];\n"; - emit_tree_node_recursive(file, node->get_down_child(), upper_bound); - } - - if (node->get_up_child() != nullptr) { - file << " N" << node->node_id << " -> N" << node->get_up_child()->node_id << " [label=\"x" - << node->branch_var << ">=" << std::ceil(node->fractional_val) << "\"];\n"; - emit_tree_node_recursive(file, node->get_up_child(), upper_bound); - } - } -}; - -// ============================================================================ -// Convenience Macros -// ============================================================================ -// -// These macros provide a clean interface for BSP debug logging. -// - When BSP_DEBUG_ENABLED is defined: macros are active (controlled by settings at runtime) -// - When BSP_DEBUG_ENABLED is not defined: macros are no-ops (zero overhead) -// -// The 'settings' parameter is a bsp_debug_settings_t& that controls which features are enabled. - -#ifdef BSP_DEBUG_ENABLED - -#define BSP_DEBUG_LOG_HORIZON_START(settings, logger, h, vs, ve) \ - do { \ - if ((settings).any_enabled()) (logger).log_horizon_start(h, vs, ve); \ - } while (0) -#define BSP_DEBUG_LOG_HORIZON_END(settings, logger, h, vt) \ - do { \ - if ((settings).any_enabled()) (logger).log_horizon_end(h, vt); \ - } while (0) -#define BSP_DEBUG_LOG_HORIZON_HASH(settings, logger, h, vt, hash) \ - do { \ - if ((settings).any_enabled()) (logger).log_horizon_hash(h, vt, hash); \ - } while (0) -#define BSP_DEBUG_LOG_NODE_ASSIGNED(settings, logger, vt, w, nid, fid, lb) \ - do { \ - if ((settings).any_enabled()) (logger).log_node_assigned(vt, w, nid, fid, lb); \ - } while (0) -#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(settings, logger) \ - do { \ - if ((settings).any_enabled() && (settings).flush_every_horizon) (logger).flush_assign_trace(); \ - } while (0) -#define BSP_DEBUG_LOG_SOLVE_START(settings, logger, vt, w, nid, fid, wl, resumed) \ - do { \ - if ((settings).any_enabled()) (logger).log_solve_start(vt, w, nid, fid, wl, resumed); \ - } while (0) -#define BSP_DEBUG_LOG_SOLVE_END(settings, logger, vt, w, nid, fid, result, lb) \ - do { \ - if ((settings).any_enabled()) (logger).log_solve_end(vt, w, nid, fid, result, lb); \ - } while (0) -#define BSP_DEBUG_LOG_BRANCHED(settings, logger, vt, w, pid, pfid, did, uid) \ - do { \ - if ((settings).any_enabled()) (logger).log_branched(vt, w, pid, pfid, did, uid); \ - } while (0) -#define BSP_DEBUG_LOG_PAUSED(settings, logger, vt, w, nid, fid, acc) \ - do { \ - if ((settings).any_enabled()) (logger).log_paused(vt, w, nid, fid, acc); \ - } while (0) -#define BSP_DEBUG_LOG_INTEGER(settings, logger, vt, w, nid, obj) \ - do { \ - if ((settings).any_enabled()) (logger).log_integer(vt, w, nid, obj); \ - } while (0) -#define BSP_DEBUG_LOG_FATHOMED(settings, logger, vt, w, nid, lb) \ - do { \ - if ((settings).any_enabled()) (logger).log_fathomed(vt, w, nid, lb); \ - } while (0) -#define BSP_DEBUG_LOG_INFEASIBLE(settings, logger, vt, w, nid) \ - do { \ - if ((settings).any_enabled()) (logger).log_infeasible(vt, w, nid); \ - } while (0) -#define BSP_DEBUG_LOG_PRUNED(settings, logger, vt, nid, fid, lb, ub) \ - do { \ - if ((settings).any_enabled()) (logger).log_pruned(vt, nid, fid, lb, ub); \ - } while (0) -#define BSP_DEBUG_LOG_SYNC_PHASE_START(settings, logger, vt, ne) \ - do { \ - if ((settings).any_enabled()) (logger).log_sync_phase_start(vt, ne); \ - } while (0) -#define BSP_DEBUG_LOG_SYNC_PHASE_END(settings, logger, vt) \ - do { \ - if ((settings).any_enabled()) (logger).log_sync_phase_end(vt); \ - } while (0) -#define BSP_DEBUG_LOG_FINAL_ID_ASSIGNED(settings, logger, pid, fid) \ - do { \ - if ((settings).any_enabled()) (logger).log_final_id_assigned(pid, fid); \ - } while (0) -#define BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(settings, logger) \ - do { \ - if ((settings).any_enabled() && (settings).flush_every_horizon) \ - (logger).flush_final_ids_trace(); \ - } while (0) -#define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(settings, logger, vt, obj) \ - do { \ - if ((settings).any_enabled()) (logger).log_heuristic_received(vt, obj); \ - } while (0) -#define BSP_DEBUG_LOG_INCUMBENT_UPDATE(settings, logger, vt, obj, src) \ - do { \ - if ((settings).any_enabled()) (logger).log_incumbent_update(vt, obj, src); \ - } while (0) -#define BSP_DEBUG_LOG_HEAP_ORDER(settings, logger, fids) \ - do { \ - if ((settings).any_enabled()) (logger).log_heap_order(fids); \ - } while (0) -#define BSP_DEBUG_LOG_LP_INPUT(settings, logger, w, nid, ph, d, vsh, bh) \ - do { \ - if ((settings).any_enabled()) (logger).log_lp_input(w, nid, ph, d, vsh, bh); \ - } while (0) -#define BSP_DEBUG_LOG_LP_OUTPUT(settings, logger, w, nid, ph, st, it, ob, sh) \ - do { \ - if ((settings).any_enabled()) (logger).log_lp_output(w, nid, ph, st, it, ob, sh); \ - } while (0) -#define BSP_DEBUG_LOG_BRANCH_DECISION(settings, logger, bv, sh, nt) \ - do { \ - if ((settings).any_enabled()) (logger).log_branch_decision(bv, sh, nt); \ - } while (0) -#define BSP_DEBUG_EMIT_TREE_STATE(settings, logger, h, root, ub) \ - do { \ - if ((settings).any_enabled() && (settings).flush_every_horizon) \ - (logger).emit_tree_state(h, root, ub); \ - } while (0) -#define BSP_DEBUG_EMIT_STATE_JSON( \ - settings, logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ - do { \ - if ((settings).any_enabled() && (settings).flush_every_horizon) \ - (logger).emit_state_json(h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events); \ - } while (0) -#define BSP_DEBUG_FINALIZE(settings, logger) \ - do { \ - if ((settings).any_enabled()) (logger).finalize(); \ - } while (0) - -#else - -#define BSP_DEBUG_LOG_HORIZON_START(settings, logger, h, vs, ve) ((void)0) -#define BSP_DEBUG_LOG_HORIZON_END(settings, logger, h, vt) ((void)0) -#define BSP_DEBUG_LOG_HORIZON_HASH(settings, logger, h, vt, hash) ((void)0) -#define BSP_DEBUG_LOG_NODE_ASSIGNED(settings, logger, vt, w, nid, fid, lb) ((void)0) -#define BSP_DEBUG_FLUSH_ASSIGN_TRACE(settings, logger) ((void)0) -#define BSP_DEBUG_LOG_SOLVE_START(settings, logger, vt, w, nid, fid, wl, resumed) ((void)0) -#define BSP_DEBUG_LOG_SOLVE_END(settings, logger, vt, w, nid, fid, result, lb) ((void)0) -#define BSP_DEBUG_LOG_BRANCHED(settings, logger, vt, w, pid, pfid, did, uid) ((void)0) -#define BSP_DEBUG_LOG_PAUSED(settings, logger, vt, w, nid, fid, acc) ((void)0) -#define BSP_DEBUG_LOG_INTEGER(settings, logger, vt, w, nid, obj) ((void)0) -#define BSP_DEBUG_LOG_FATHOMED(settings, logger, vt, w, nid, lb) ((void)0) -#define BSP_DEBUG_LOG_INFEASIBLE(settings, logger, vt, w, nid) ((void)0) -#define BSP_DEBUG_LOG_PRUNED(settings, logger, vt, nid, fid, lb, ub) ((void)0) -#define BSP_DEBUG_LOG_SYNC_PHASE_START(settings, logger, vt, ne) ((void)0) -#define BSP_DEBUG_LOG_SYNC_PHASE_END(settings, logger, vt) ((void)0) -#define BSP_DEBUG_LOG_FINAL_ID_ASSIGNED(settings, logger, pid, fid) ((void)0) -#define BSP_DEBUG_FLUSH_FINAL_IDS_TRACE(settings, logger) ((void)0) -#define BSP_DEBUG_LOG_HEURISTIC_RECEIVED(settings, logger, vt, obj) ((void)0) -#define BSP_DEBUG_LOG_INCUMBENT_UPDATE(settings, logger, vt, obj, src) ((void)0) -#define BSP_DEBUG_LOG_HEAP_ORDER(settings, logger, fids) ((void)0) -#define BSP_DEBUG_LOG_LP_INPUT(settings, logger, w, nid, ph, d, vsh, bh) ((void)0) -#define BSP_DEBUG_LOG_LP_OUTPUT(settings, logger, w, nid, ph, st, it, ob, sh) ((void)0) -#define BSP_DEBUG_LOG_BRANCH_DECISION(settings, logger, bv, sh, nt) ((void)0) -#define BSP_DEBUG_EMIT_TREE_STATE(settings, logger, h, root, ub) ((void)0) -#define BSP_DEBUG_EMIT_STATE_JSON( \ - settings, logger, h, vs, ve, nfid, ub, lb, ne, nu, workers, heap, events) \ - ((void)0) -#define BSP_DEBUG_FINALIZE(settings, logger) ((void)0) - -#endif - -} // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/dual_simplex_features.hpp b/cpp/src/dual_simplex/dual_simplex_features.hpp deleted file mode 100644 index 04164c55e6..0000000000 --- a/cpp/src/dual_simplex/dual_simplex_features.hpp +++ /dev/null @@ -1,219 +0,0 @@ -/* clang-format off */ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -/* clang-format on */ - -#pragma once - -#include -#include -#include - -#include - -#include - -namespace cuopt::linear_programming::dual_simplex { - -/** - * @brief Feature collection structure for dual simplex runtime prediction. - * - * This structure collects features that can be used to train regression models - * for predicting the runtime of dual_phase2_with_advanced_basis. - */ -template -struct dual_simplex_features_t { - // Model/Problem Features (static) - i_t num_rows{0}; // m - number of constraints - i_t num_cols{0}; // n - number of variables - i_t num_nonzeros{0}; // nnz - total nonzeros in constraint matrix - f_t matrix_density{0.0}; // nnz / (m * n) - f_t avg_nnz_per_col{0.0}; // nnz / n - f_t avg_nnz_per_row{0.0}; // nnz / m - i_t num_bounded_vars{0}; // variables with finite lower AND upper bounds - i_t num_free_vars{0}; // variables with infinite bounds on both sides - i_t num_fixed_vars{0}; // variables where lower == upper - - // Iteration-based features (dynamic) - i_t iteration{0}; // current iteration count - i_t start_iteration{0}; // iteration at start of this call - i_t num_refactors{0}; // number of basis refactorizations - i_t num_basis_updates{0}; // basis updates since last refactor - i_t sparse_delta_z_count{0}; // iterations using sparse delta_z - i_t dense_delta_z_count{0}; // iterations using dense delta_z - i_t total_bound_flips{0}; // cumulative bound flips - - // Sparsity during solve - i_t num_infeasibilities{0}; // size of infeasibility_indices - f_t delta_y_nz_percentage{0.0}; // sparsity of BTran result - - // Phase-specific features - i_t phase{0}; // 1 or 2 - bool slack_basis{false}; // whether starting from slack basis - bool initialize_basis{false}; // whether basis factorization performed initially - - // Settings that impact runtime - i_t refactor_frequency{0}; // from settings - - // Memory access statistics (aggregated from instrumentation) - size_t byte_loads{0}; // total bytes loaded - size_t byte_stores{0}; // total bytes stored - - // Runtime for the interval (in seconds) - f_t interval_runtime{0.0}; - - /** - * @brief Initialize static features from problem data. - */ - void init_from_problem(const lp_problem_t& lp, - const simplex_solver_settings_t& settings, - i_t phase_, - bool slack_basis_, - bool initialize_basis_) - { - raft::common::nvtx::range scope("DualSimplex::init_from_problem"); - - num_rows = lp.num_rows; - num_cols = lp.num_cols; - num_nonzeros = lp.A.col_start[lp.num_cols]; - - const f_t total_elements = static_cast(num_rows) * static_cast(num_cols); - matrix_density = (total_elements > 0) ? num_nonzeros / total_elements : 0.0; - avg_nnz_per_col = (num_cols > 0) ? static_cast(num_nonzeros) / num_cols : 0.0; - avg_nnz_per_row = (num_rows > 0) ? static_cast(num_nonzeros) / num_rows : 0.0; - - // Count bound types - num_bounded_vars = 0; - num_free_vars = 0; - num_fixed_vars = 0; - constexpr f_t inf_val = std::numeric_limits::infinity(); - for (i_t j = 0; j < num_cols; ++j) { - const bool has_lower = lp.lower[j] > -inf_val; - const bool has_upper = lp.upper[j] < inf_val; - if (has_lower && has_upper) { - if (lp.lower[j] == lp.upper[j]) { - num_fixed_vars++; - } else { - num_bounded_vars++; - } - } else if (!has_lower && !has_upper) { - num_free_vars++; - } - } - - phase = phase_; - slack_basis = slack_basis_; - initialize_basis = initialize_basis_; - refactor_frequency = settings.refactor_frequency; - } - - /** - * @brief Print all features on a single line in key=value format. - * - * Format: DS_FEATURES: iter=N m=M n=N nnz=K ... - */ - void log_features(const simplex_solver_settings_t& settings) const - { - printf( - "DS_FEATURES: iter=%d m=%d n=%d nnz=%d density=%.6e avg_nnz_col=%.2f avg_nnz_row=%.2f " - "bounded=%d free=%d fixed=%d phase=%d refact_freq=%d num_refacts=%d num_updates=%d " - "sparse_dz=%d dense_dz=%d bound_flips=%d num_infeas=%d dy_nz_pct=%.2f " - "byte_loads=%zu byte_stores=%zu runtime=%.6f\n", - iteration, - num_rows, - num_cols, - num_nonzeros, - matrix_density, - avg_nnz_per_col, - avg_nnz_per_row, - num_bounded_vars, - num_free_vars, - num_fixed_vars, - phase, - refactor_frequency, - num_refactors, - num_basis_updates, - sparse_delta_z_count, - dense_delta_z_count, - total_bound_flips, - num_infeasibilities, - delta_y_nz_percentage, - byte_loads, - byte_stores, - interval_runtime); - } - - /** - * @brief Reset per-interval counters (called after each logging interval). - */ - void reset_interval_counters() - { - byte_loads = 0; - byte_stores = 0; - interval_runtime = 0.0; - } -}; - -// Feature logging interval (every N iterations) -constexpr int FEATURE_LOG_INTERVAL = 100; - -// Node bounds strengthening features (for B&B) -template -struct bounds_strengthening_features_t { - i_t m{0}; // number of constraints - i_t n{0}; // number of variables - i_t nnz{0}; // number of nonzeros in constraint matrix - i_t num_iterations{0}; // propagation iterations until fixpoint - i_t num_bounds_changed{0}; // total bounds tightened - size_t byte_loads{0}; - size_t byte_stores{0}; - f_t runtime{0.0}; - - // Interval aggregates (for when bounds strengthening is called multiple times) - i_t call_count{0}; - i_t total_iterations{0}; - i_t total_bounds_changed{0}; - size_t total_byte_loads{0}; - size_t total_byte_stores{0}; - f_t total_runtime{0.0}; - - void accumulate() - { - call_count++; - total_iterations += num_iterations; - total_bounds_changed += num_bounds_changed; - total_byte_loads += byte_loads; - total_byte_stores += byte_stores; - total_runtime += runtime; - } - - void log_single(i_t m_val, i_t n_val, i_t nnz_val) const - { - // printf( - // "BOUNDS_STRENGTH_FEATURES: m=%d n=%d nnz=%d " - // "iterations=%d bounds_changed=%d " - // "byte_loads=%zu byte_stores=%zu runtime=%.6f\n", - // m_val, - // n_val, - // nnz_val, - // num_iterations, - // num_bounds_changed, - // byte_loads, - // byte_stores, - // runtime); - } - - void reset() - { - call_count = 0; - total_iterations = 0; - total_bounds_changed = 0; - total_byte_loads = 0; - total_byte_stores = 0; - total_runtime = 0.0; - } -}; - -} // namespace cuopt::linear_programming::dual_simplex diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 55c044d6e7..9146693691 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -3497,7 +3497,7 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase, f_t now = toc(start_time); // Feature logging for regression training (every FEATURE_LOG_INTERVAL iterations) - if ((iter % FEATURE_LOG_INTERVAL) == 0) { + if ((iter % FEATURE_LOG_INTERVAL) == 0 && work_unit_context) { [[maybe_unused]] i_t iters_elapsed = iter - last_feature_log_iter; phase2_work_estimate += ft.work_estimate(); diff --git a/cpp/src/dual_simplex/primal.cpp b/cpp/src/dual_simplex/primal.cpp index 3f2a939aa9..d4c6743dc6 100644 --- a/cpp/src/dual_simplex/primal.cpp +++ b/cpp/src/dual_simplex/primal.cpp @@ -14,8 +14,6 @@ #include #include -#include - namespace cuopt::linear_programming::dual_simplex { namespace { @@ -256,7 +254,6 @@ primal::status_t primal_phase2(i_t phase, lp_solution_t& sol, i_t& iter) { - raft::common::nvtx::range scope("PrimalSimplex::phase2"); const i_t m = lp.num_rows; const i_t n = lp.num_cols; assert(m <= n); diff --git a/cpp/src/dual_simplex/sparse_matrix.cpp b/cpp/src/dual_simplex/sparse_matrix.cpp index 2a8e1a4dbf..8ccccd57cf 100644 --- a/cpp/src/dual_simplex/sparse_matrix.cpp +++ b/cpp/src/dual_simplex/sparse_matrix.cpp @@ -8,12 +8,9 @@ // #include #include #include -#include #include -using cuopt::ins_vector; - // #include // #include @@ -37,8 +34,8 @@ void csc_matrix_t::reallocate(i_t new_nz) this->nz_max = new_nz; } -template -void cumulative_sum(std::vector& inout, OutputVector& output) +template +void cumulative_sum(std::vector& inout, std::vector& output) { i_t n = inout.size(); assert(output.size() == n + 1); @@ -701,41 +698,6 @@ void scatter_dense(const csc_matrix_t& A, } } -// Instrumented vector overload: x <- x + alpha * A(:, j) -template -void scatter_dense(const csc_matrix_t& A, i_t j, f_t alpha, ins_vector& x) -{ - const i_t col_start = A.col_start[j]; - const i_t col_end = A.col_start[j + 1]; - for (i_t p = col_start; p < col_end; ++p) { - const i_t i = A.i[p]; - const f_t ax = A.x[p]; - x[i] += alpha * ax; - } -} - -// Instrumented vector overload: x <- x + alpha * A(:, j) with mark/indices tracking -template -void scatter_dense(const csc_matrix_t& A, - i_t j, - f_t alpha, - ins_vector& x, - ins_vector& mark, - ins_vector& indices) -{ - const i_t col_start = A.col_start[j]; - const i_t col_end = A.col_start[j + 1]; - for (i_t p = col_start; p < col_end; ++p) { - const i_t i = A.i[p]; - const f_t ax = A.x[p]; - x[i] += alpha * ax; - if (!mark[i]) { - mark[i] = 1; - indices.push_back(i); - } - } -} - // Compute C = A*B where C is m x n, A is m x k, and B = k x n // Do this by computing C(:, j) = A*B(:, j) = sum (i=1 to k) A(:, k)*B(i, j) template @@ -981,10 +943,7 @@ template class csc_matrix_t; template class csr_matrix_t; -template void cumulative_sum>(std::vector& inout, - std::vector& output); -template void cumulative_sum>(std::vector& inout, - ins_vector& output); +template void cumulative_sum(std::vector& inout, std::vector& output); template int coo_to_csc(const std::vector& Ai, const std::vector& Aj, @@ -1012,18 +971,6 @@ template void scatter_dense(const csc_matrix_t& A, std::vector& mark, std::vector& indices); -template void scatter_dense(const csc_matrix_t& A, - int j, - double alpha, - ins_vector& x); - -template void scatter_dense(const csc_matrix_t& A, - int j, - double alpha, - ins_vector& x, - ins_vector& mark, - ins_vector& indices); - template int multiply(const csc_matrix_t& A, const csc_matrix_t& B, csc_matrix_t& C); diff --git a/cpp/src/dual_simplex/sparse_matrix.hpp b/cpp/src/dual_simplex/sparse_matrix.hpp index ea006b7b27..b6d3ee9aae 100644 --- a/cpp/src/dual_simplex/sparse_matrix.hpp +++ b/cpp/src/dual_simplex/sparse_matrix.hpp @@ -185,8 +185,8 @@ class csr_matrix_t { static_assert(std::is_signed_v); }; -template -void cumulative_sum(std::vector& inout, OutputVector& output); +template +void cumulative_sum(std::vector& inout, std::vector& output); template i_t coo_to_csc(const std::vector& Ai, diff --git a/cpp/src/math_optimization/solver_settings.cu b/cpp/src/math_optimization/solver_settings.cu index d49dc152ad..5e86856496 100644 --- a/cpp/src/math_optimization/solver_settings.cu +++ b/cpp/src/math_optimization/solver_settings.cu @@ -101,7 +101,7 @@ solver_settings_t::solver_settings_t() : pdlp_settings(), mip_settings {CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING, &mip_settings.mip_batch_pdlp_strong_branching, 0, 1, 0}, {CUOPT_PRESOLVE, reinterpret_cast(&pdlp_settings.presolver), CUOPT_PRESOLVE_DEFAULT, CUOPT_PRESOLVE_PSLP, CUOPT_PRESOLVE_DEFAULT}, {CUOPT_PRESOLVE, reinterpret_cast(&mip_settings.presolver), CUOPT_PRESOLVE_DEFAULT, CUOPT_PRESOLVE_PSLP, CUOPT_PRESOLVE_DEFAULT}, - {CUOPT_MIP_DETERMINISM_MODE, &mip_settings.determinism_mode, CUOPT_MODE_OPPORTUNISTIC, CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS, CUOPT_MODE_OPPORTUNISTIC}, + {CUOPT_MIP_DETERMINISM_MODE, &mip_settings.determinism_mode, CUOPT_DETERMINISM_NONE, CUOPT_DETERMINISM_FULL, CUOPT_DETERMINISM_NONE}, {CUOPT_RANDOM_SEED, &mip_settings.seed, -1, std::numeric_limits::max(), -1}, {CUOPT_MIP_RELIABILITY_BRANCHING, &mip_settings.reliability_branching, -1, std::numeric_limits::max(), -1} }; diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 5e538121f2..6aa361036f 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -127,7 +127,7 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::run_presolve(f_t time_limit, if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; } const bool run_clique_table = !presolve_timer.check_time_limit() && !context.settings.heuristics_only && !problem_ptr->empty && - !is_deterministic_mode(context.settings.determinism_mode); + !(context.settings.determinism_mode & CUOPT_DETERMINISM_BB); if (run_clique_table) { f_t time_limit_for_clique_table = std::min(3., presolve_timer.remaining_time() / 5); timer_t clique_timer(time_limit_for_clique_table); @@ -393,7 +393,7 @@ solution_t diversity_manager_t::run_solver() CUOPT_LOG_DEBUG( "Determinism mode: %s", - is_deterministic_mode(context.settings.determinism_mode) ? "deterministic" : "opportunistic"); + (context.settings.determinism_mode & CUOPT_DETERMINISM_BB) ? "deterministic" : "opportunistic"); // to automatically compute the solving time on scope exit auto timer_raii_guard = @@ -414,7 +414,7 @@ solution_t diversity_manager_t::run_solver() const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); - if (is_deterministic_mode(context.settings.determinism_mode) && + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && context.branch_and_bound_ptr != nullptr) { auto& producer_sync = context.branch_and_bound_ptr->get_producer_sync(); producer_sync.registration_complete(); @@ -437,7 +437,7 @@ solution_t diversity_manager_t::run_solver() producer_sync.deregister_producer(context.gpu_heur_loop.producer_progress_ptr()); context.gpu_heur_loop.detach_producer_sync(); }); - if (is_deterministic_mode(context.settings.determinism_mode) && + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && context.branch_and_bound_ptr != nullptr) { if (context.settings.gpu_heur_wait_for_exploration) { CUOPT_LOG_INFO("GPU heuristics waiting for B&B tree exploration to start..."); @@ -488,7 +488,7 @@ solution_t diversity_manager_t::run_solver() // Run CPUFJ early to find quick initial solutions ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop - if (!diversity_config.dry_run && context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + if (!diversity_config.dry_run && !(context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { ls.start_cpufj_scratch_threads(population); } @@ -665,8 +665,7 @@ solution_t diversity_manager_t::run_solver() lp_rounded_sol.get_user_objective()); population.add_solution(std::move(lp_rounded_sol), internals::mip_solution_origin_t::LP_ROUNDING); - if (!diversity_config.dry_run && - context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + if (!diversity_config.dry_run && !(context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { ls.start_cpufj_lptopt_scratch_threads(population); } } @@ -703,7 +702,7 @@ solution_t diversity_manager_t::run_solver() log_return_solution("fj_only_run", sol); return sol; } - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { rins.enable(); } + if (!(context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { rins.enable(); } generate_solution(timer.remaining_time(), false); if (diversity_config.initial_solution_only) { @@ -877,7 +876,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& sol1.get_feasible(), sol2.get_quality(population.weights), sol2.get_feasible()); - bool deterministic = is_deterministic_mode(context.settings.determinism_mode); + bool deterministic = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB); double best_objective_of_parents = std::min(sol1.get_objective(), sol2.get_objective()); bool at_least_one_parent_feasible = sol1.get_feasible() || sol2.get_feasible(); // randomly choose among 3 recombiners diff --git a/cpp/src/mip_heuristics/diversity/lns/rins.cu b/cpp/src/mip_heuristics/diversity/lns/rins.cu index 73b527a77b..1a9260ce9a 100644 --- a/cpp/src/mip_heuristics/diversity/lns/rins.cu +++ b/cpp/src/mip_heuristics/diversity/lns/rins.cu @@ -344,7 +344,7 @@ void rins_t::run_rins() "Assignment size mismatch"); dm.population.add_external_solution(best_sol.get_host_assignment(), best_sol.get_objective(), - internals::mip_solution_origin_t::LOCAL_SEARCH); + internals::mip_solution_origin_t::RINS); } } diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index 53d6e8aa1d..01a14680b1 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -238,7 +238,7 @@ void population_t::add_external_solution(const std::vector& solut template void population_t::add_external_solutions_to_population() { - if (is_deterministic_mode(context.settings.determinism_mode)) { return; } + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { return; } // don't do early exit checks here. mutex needs to be acquired to prevent race conditions auto new_sol_vector = get_external_solutions(); for (auto& drained_sol : new_sol_vector) { @@ -328,7 +328,7 @@ void population_t::run_solution_callbacks( auto user_callbacks = context.settings.get_mip_callbacks(); if (better_solution_found) { const bool deterministic_callback_owner_is_bb = - is_deterministic_mode(context.settings.determinism_mode) && + (context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && context.branch_and_bound_ptr != nullptr; if (deterministic_callback_owner_is_bb) { cuopt_assert(sol.get_feasible(), @@ -464,18 +464,9 @@ std::pair population_t::add_solution( // and we need those operations to complete before reading device data // for hash computation, quality calculation, and similarity comparisons. sol.handle_ptr->sync_stream(); - assert_solution_matches_population_problem( - "population.add_solution(candidate)", "candidate", problem_ptr, sol); population_hash_map.insert(sol); - double sol_cost = sol.get_quality(weights); - bool best_updated = false; - const bool had_best_feasible_before = solutions[0].first; - const uint32_t best_hash_before = had_best_feasible_before ? solutions[0].second.get_hash() : 0U; - const f_t best_user_objective_before = had_best_feasible_before - ? solutions[0].second.get_user_objective() - : std::numeric_limits::infinity(); - const f_t best_quality_before = - had_best_feasible_before ? indices[0].second : std::numeric_limits::infinity(); + double sol_cost = sol.get_quality(weights); + bool best_updated = false; const uint32_t candidate_hash = sol.get_hash(); CUOPT_LOG_DEBUG("Adding solution with quality %f and objective %f n_integers %d, hash %x!", sol_cost, @@ -493,40 +484,11 @@ std::pair population_t::add_solution( solutions[0].second = std::move(temp_sol); indices[0].second = sol_cost; best_updated = true; - CUOPT_DETERMINISM_LOG_INFO( - "Deterministic population best update: candidate_hash=0x%x candidate_feasible=%d " - "candidate_obj=%.16e candidate_quality=%.16e best_hash_before=0x%x " - "best_obj_before=%.16e best_quality_before=%.16e best_hash_after=0x%x " - "best_obj_after=%.16e best_quality_after=%.16e", - candidate_hash, - (int)sol.get_feasible(), - sol.get_user_objective(), - sol_cost, - best_hash_before, - best_user_objective_before, - best_quality_before, - solutions[0].second.get_hash(), - solutions[0].second.get_user_objective(), - indices[0].second); } // Fast reject if (indices.size() == max_solutions && indices.back().second <= sol_cost + OBJECTIVE_EPSILON) { CUOPT_LOG_TRACE("Rejecting solution objective is not better!"); - CUOPT_DETERMINISM_LOG_INFO( - "Deterministic population add decision: candidate_hash=0x%x candidate_feasible=%d " - "candidate_obj=%.16e candidate_quality=%.16e action=reject_fast best_updated=%d " - "best_hash_before=0x%x best_obj_before=%.16e best_quality_before=%.16e " - "worst_quality=%.16e", - candidate_hash, - (int)sol.get_feasible(), - sol.get_user_objective(), - sol_cost, - (int)best_updated, - best_hash_before, - best_user_objective_before, - best_quality_before, - indices.back().second); return std::make_pair(-1, best_updated); } @@ -557,24 +519,6 @@ std::pair population_t::add_solution( int inserted_pos = insert_index(std::pair((size_t)hint, sol_cost)); cuopt_assert(test_invariant(), "Population invariant doesn't hold"); test_invariant(); - CUOPT_DETERMINISM_LOG_INFO( - "Deterministic population add decision: candidate_hash=0x%x candidate_feasible=%d " - "candidate_obj=%.16e candidate_quality=%.16e action=insert_new inserted_pos=%d " - "best_updated=%d best_hash_before=0x%x best_obj_before=%.16e best_quality_before=%.16e " - "best_hash_after=0x%x best_obj_after=%.16e best_quality_after=%.16e", - candidate_hash, - (int)solutions[hint].second.get_feasible(), - solutions[hint].second.get_user_objective(), - sol_cost, - inserted_pos, - (int)best_updated, - best_hash_before, - best_user_objective_before, - best_quality_before, - solutions[0].first ? solutions[0].second.get_hash() : 0U, - solutions[0].first ? solutions[0].second.get_user_objective() - : std::numeric_limits::infinity(), - solutions[0].first ? indices[0].second : std::numeric_limits::infinity()); return std::make_pair(inserted_pos, best_updated); } else if (sol_cost + OBJECTIVE_EPSILON < indices[index].second) { @@ -589,50 +533,11 @@ std::pair population_t::add_solution( int inserted_pos = insert_index(std::pair((size_t)free, sol_cost)); cuopt_assert(test_invariant(), "Population invariant doesn't hold"); test_invariant(); - CUOPT_DETERMINISM_LOG_INFO( - "Deterministic population add decision: candidate_hash=0x%x candidate_feasible=%d " - "candidate_obj=%.16e candidate_quality=%.16e action=replace_similar similar_index=%zu " - "inserted_pos=%d best_updated=%d best_hash_before=0x%x best_obj_before=%.16e " - "best_quality_before=%.16e best_hash_after=0x%x best_obj_after=%.16e " - "best_quality_after=%.16e", - candidate_hash, - (int)solutions[free].second.get_feasible(), - solutions[free].second.get_user_objective(), - sol_cost, - index, - inserted_pos, - (int)best_updated, - best_hash_before, - best_user_objective_before, - best_quality_before, - solutions[0].first ? solutions[0].second.get_hash() : 0U, - solutions[0].first ? solutions[0].second.get_user_objective() - : std::numeric_limits::infinity(), - solutions[0].first ? indices[0].second : std::numeric_limits::infinity()); return std::make_pair(inserted_pos, best_updated); } CUOPT_LOG_TRACE("Adding solution failed!"); cuopt_assert(test_invariant(), "Population invariant doesn't hold"); test_invariant(); - CUOPT_DETERMINISM_LOG_INFO( - "Deterministic population add decision: candidate_hash=0x%x candidate_feasible=%d " - "candidate_obj=%.16e candidate_quality=%.16e action=reject_similar_or_worse " - "similar_index=%zu best_updated=%d best_hash_before=0x%x best_obj_before=%.16e " - "best_quality_before=%.16e best_hash_after=0x%x best_obj_after=%.16e " - "best_quality_after=%.16e", - candidate_hash, - (int)sol.get_feasible(), - sol.get_user_objective(), - sol_cost, - index, - (int)best_updated, - best_hash_before, - best_user_objective_before, - best_quality_before, - solutions[0].first ? solutions[0].second.get_hash() : 0U, - solutions[0].first ? solutions[0].second.get_user_objective() - : std::numeric_limits::infinity(), - solutions[0].first ? indices[0].second : std::numeric_limits::infinity()); return std::make_pair(-1, best_updated); } @@ -760,8 +665,6 @@ template bool population_t::check_sols_similar(solution_t& sol1, solution_t& sol2) const { - assert_solutions_compatible_for_similarity( - "population.check_sols_similar", problem_ptr, sol1, "lhs", sol2, "rhs"); return sol1.calculate_similarity_radius(sol2) > var_threshold; } @@ -771,15 +674,6 @@ size_t population_t::best_similar_index(solution_t& sol) raft::common::nvtx::range fun_scope("best_similar_index"); if (indices.size() == 1) return max_solutions; for (size_t i = 1; i < indices.size(); i++) { - const size_t resident_idx = indices[i].first; - cuopt_assert(resident_idx < solutions.size(), "Population resident index out of bounds"); - cuopt_assert(solutions[resident_idx].first, "Population resident must be occupied"); - assert_solution_matches_population_problem( - "population.best_similar_index(candidate)", "candidate", problem_ptr, sol); - assert_solution_matches_population_problem("population.best_similar_index(resident)", - "resident", - problem_ptr, - solutions[resident_idx].second); if (check_sols_similar(sol, solutions[indices[i].first].second)) { return i; } } diff --git a/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh index 3660927d7b..80f8383fab 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/bound_prop_recombiner.cuh @@ -164,24 +164,24 @@ class bound_prop_recombiner_t : public recombiner_t { "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); // DETERMINISM DEBUG: Log everything that could affect divergence - CUOPT_LOG_TRACE("BP_DET: sol_a_hash=0x%x sol_b_hash=0x%x offspring_hash=0x%x, seed %x", - a.get_hash(), - b.get_hash(), - offspring.get_hash(), - seed); - CUOPT_LOG_TRACE("BP_DET: n_different_vars=%d n_vars_from_other=%d n_vars_from_guiding=%d", - n_different_vars, - n_vars_from_other, - n_vars_from_guiding); - CUOPT_LOG_TRACE( + CUOPT_DETERMINISM_LOG("BP_DET: sol_a_hash=0x%x sol_b_hash=0x%x offspring_hash=0x%x, seed %x", + a.get_hash(), + b.get_hash(), + offspring.get_hash(), + seed); + CUOPT_DETERMINISM_LOG("BP_DET: n_different_vars=%d n_vars_from_other=%d n_vars_from_guiding=%d", + n_different_vars, + n_vars_from_other, + n_vars_from_guiding); + CUOPT_DETERMINISM_LOG( "BP_DET: remaining_indices_hash=0x%x (first %d elements)", detail::compute_hash(make_span(this->remaining_indices), a.handle_ptr->get_stream()), std::min((i_t)10, n_vars_from_other)); - CUOPT_LOG_TRACE("BP_DET: guiding_feasible=%d other_feasible=%d expensive_to_fix=%d", - guiding_solution.get_feasible(), - other_solution.get_feasible(), - a.problem_ptr->expensive_to_fix_vars); - CUOPT_LOG_TRACE( + CUOPT_DETERMINISM_LOG("BP_DET: guiding_feasible=%d other_feasible=%d expensive_to_fix=%d", + guiding_solution.get_feasible(), + other_solution.get_feasible(), + a.problem_ptr->expensive_to_fix_vars); + CUOPT_DETERMINISM_LOG( "BP_DET: fixed_from_guiding=%d fixed_from_other=%d", fixed_from_guiding, fixed_from_other); // if either all integers are from A(meaning all are common) or all integers are from B(meaning @@ -200,13 +200,13 @@ class bound_prop_recombiner_t : public recombiner_t { a.handle_ptr->get_stream()); probing_config_t probing_config(a.problem_ptr->n_variables, a.handle_ptr); if (guiding_solution.get_feasible() && !a.problem_ptr->expensive_to_fix_vars) { - CUOPT_LOG_DEBUG("BP_DET: Taking FEASIBLE path (with variable fixing)"); + CUOPT_DETERMINISM_LOG("BP_DET: Taking FEASIBLE path (with variable fixing)"); this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); - CUOPT_LOG_DEBUG("BP_DET: vars_to_fix_size=%lu", vars_to_fix.size()); + CUOPT_DETERMINISM_LOG("BP_DET: vars_to_fix_size=%lu", vars_to_fix.size()); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); - CUOPT_LOG_DEBUG("BP_DET: fixed_problem_fingerprint=0x%x variable_map_size=%lu", - fixed_problem.get_fingerprint(), - variable_map.size()); + CUOPT_DETERMINISM_LOG("BP_DET: fixed_problem_fingerprint=0x%x variable_map_size=%lu", + fixed_problem.get_fingerprint(), + variable_map.size()); work_limit_timer_t timer(this->context.gpu_heur_loop, bp_recombiner_config_t::bounds_prop_time_limit, *this->context.termination); @@ -252,20 +252,21 @@ class bound_prop_recombiner_t : public recombiner_t { } a.handle_ptr->sync_stream(); } else { - CUOPT_LOG_TRACE("BP_DET: Taking INFEASIBLE path (no variable fixing)"); + CUOPT_DETERMINISM_LOG("BP_DET: Taking INFEASIBLE path (no variable fixing)"); work_limit_timer_t timer(this->context.gpu_heur_loop, bp_recombiner_config_t::bounds_prop_time_limit, *this->context.termination); get_probing_values_for_infeasible( guiding_solution, other_solution, offspring, probing_values, n_vars_from_other); probing_config.probing_values = host_copy(probing_values, offspring.handle_ptr->get_stream()); - CUOPT_LOG_TRACE("BP_DET: probing_values_hash=0x%x", - detail::compute_hash(make_span(probing_values), a.handle_ptr->get_stream())); + CUOPT_DETERMINISM_LOG( + "BP_DET: probing_values_hash=0x%x", + detail::compute_hash(make_span(probing_values), a.handle_ptr->get_stream())); constraint_prop.apply_round(offspring, lp_run_time_after_feasible, timer, probing_config); } - CUOPT_LOG_TRACE("BP_DET: After apply_round: offspring_hash=0x%x feasible=%d", - offspring.get_hash(), - offspring.get_feasible()); + CUOPT_DETERMINISM_LOG("BP_DET: After apply_round: offspring_hash=0x%x feasible=%d", + offspring.get_hash(), + offspring.get_feasible()); constraint_prop.max_n_failed_repair_iterations = 1; cuopt_func_call(offspring.test_number_all_integer()); bool better_cost_than_parents = @@ -285,7 +286,7 @@ class bound_prop_recombiner_t : public recombiner_t { bp_recombiner_config_t::decrease_max_n_of_vars_from_other(); } } - CUOPT_LOG_TRACE( + CUOPT_DETERMINISM_LOG( "BP_DET: Final offspring_hash=0x%x same_as_parents=%d better_cost=%d better_feas=%d", offspring.get_hash(), same_as_parents, diff --git a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh index 1e8bb1f1c6..abb1811e44 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh @@ -50,7 +50,7 @@ class fp_recombiner_t : public recombiner_t { CUOPT_LOG_DEBUG("FP rec: Number of different variables %d MAX_VARS %d", n_different_vars, fp_recombiner_config_t::max_n_of_vars_from_other); - CUOPT_LOG_TRACE("FP rec: offspring hash 0x%x", offspring.get_hash()); + CUOPT_DETERMINISM_LOG("FP rec: offspring hash 0x%x", offspring.get_hash()); i_t n_vars_from_other = n_different_vars; if (n_vars_from_other > (i_t)fp_recombiner_config_t::max_n_of_vars_from_other) { n_vars_from_other = fp_recombiner_config_t::max_n_of_vars_from_other; @@ -69,27 +69,26 @@ class fp_recombiner_t : public recombiner_t { double work = static_cast(n_vars_from_other); CUOPT_LOG_DEBUG( "n_vars_from_guiding %d n_vars_from_other %d", n_vars_from_guiding, n_vars_from_other); - CUOPT_LOG_TRACE( + CUOPT_DETERMINISM_LOG( "FP rec: offspring hash 0x%x, vars to fix 0x%x", offspring.get_hash(), detail::compute_hash(make_span(vars_to_fix), offspring.handle_ptr->get_stream())); this->compute_vars_to_fix(offspring, vars_to_fix, n_vars_from_other, n_vars_from_guiding); - CUOPT_LOG_TRACE( + CUOPT_DETERMINISM_LOG( "FP rec post computevarstofix: offspring hash 0x%x, vars to fix 0x%x", offspring.get_hash(), detail::compute_hash(make_span(vars_to_fix), offspring.handle_ptr->get_stream())); auto [fixed_problem, fixed_assignment, variable_map] = offspring.fix_variables(vars_to_fix); - CUOPT_LOG_TRACE( + CUOPT_DETERMINISM_LOG( "FP rec: fixed_problem hash 0x%x assigned hash 0x%x", fixed_problem.get_fingerprint(), detail::compute_hash(make_span(fixed_assignment), offspring.handle_ptr->get_stream())); fixed_problem.check_problem_representation(true); if (!guiding_solution.get_feasible() && !other_solution.get_feasible()) { - CUOPT_LOG_TRACE("FP rec: running LP with infeasibility detection"); + CUOPT_DETERMINISM_LOG("FP rec: running LP with infeasibility detection"); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; - if (this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC || - this->context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS) { + if (this->context.settings.determinism_mode & CUOPT_DETERMINISM_BB) { lp_settings.time_limit = std::numeric_limits::max(); // TODO should be global time limit lp_settings.work_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; @@ -120,7 +119,7 @@ class fp_recombiner_t : public recombiner_t { offspring.handle_ptr->sync_stream(); offspring.assignment = std::move(fixed_assignment); cuopt_func_call(offspring.test_variable_bounds(false)); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "FP rec pre-descent: offspring_hash=0x%x fixed_assignment_hash=0x%x " "problem_fingerprint=0x%x fixed_n_integer_vars=%d", offspring.get_hash(), diff --git a/cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh index 50c63405d8..ab0ba3d21c 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/line_segment_recombiner.cuh @@ -71,7 +71,7 @@ class line_segment_recombiner_t : public recombiner_t { const weight_t& weights) { raft::common::nvtx::range fun_scope("line_segment_recombiner"); - CUOPT_LOG_TRACE("LS rec: a %d b %d", a.get_hash(), b.get_hash()); + CUOPT_DETERMINISM_LOG("LS rec: a %d b %d", a.get_hash(), b.get_hash()); auto& guiding_solution = a.get_feasible() ? a : b; auto& other_solution = a.get_feasible() ? b : a; // copy the solution from A diff --git a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh index a4a9be59e2..8cc226dfb4 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh @@ -110,7 +110,7 @@ class recombiner_t { this->remaining_indices.data(), this->remaining_indices.data() + remaining_variables); - CUOPT_LOG_TRACE( + CUOPT_DETERMINISM_LOG( "remaining indices hash 0x%x, size %d", detail::compute_hash(make_span(this->remaining_indices), a.handle_ptr->get_stream()), remaining_variables); @@ -195,12 +195,12 @@ class recombiner_t { i_t n_vars_from_guiding) { vars_to_fix.resize(n_vars_from_guiding, offspring.handle_ptr->get_stream()); - CUOPT_LOG_TRACE( + CUOPT_DETERMINISM_LOG( "remaining indices hash 0x%x", detail::compute_hash(make_span(this->remaining_indices), offspring.handle_ptr->get_stream())); - CUOPT_LOG_TRACE("integer_indices hash 0x%x", - detail::compute_hash(make_span(offspring.problem_ptr->integer_indices), - offspring.handle_ptr->get_stream())); + CUOPT_DETERMINISM_LOG("integer_indices hash 0x%x", + detail::compute_hash(make_span(offspring.problem_ptr->integer_indices), + offspring.handle_ptr->get_stream())); // set difference needs two sorted arrays thrust::sort(offspring.handle_ptr->get_thrust_policy(), this->remaining_indices.data(), @@ -232,8 +232,7 @@ class recombiner_t { const bool disable_submip_for_continuous_limit = n_continuous_vars > (i_t)sub_mip_recombiner_config_t::max_continuous_vars; const bool disable_submip_for_determinism = - context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC || - context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; + (context.settings.determinism_mode & CUOPT_DETERMINISM_BB) != 0; for (auto recombiner : recombiner_types) { enabled_recombiners.insert(recombiner); } diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu index cb53f7879e..d41ebdfbb4 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu @@ -869,7 +869,7 @@ void fj_t::refresh_lhs_and_violation(const rmm::cuda_stream_view& stre thrust::plus()); data.violation_score.set_value_async(violation, stream); data.weighted_violation_score.set_value_async(weighted_violation, stream); - if (is_deterministic_mode(context.settings.determinism_mode)) { + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { data.violated_constraints.sort(stream); } #if FJ_SINGLE_STEP @@ -1064,7 +1064,7 @@ i_t fj_t::host_loop(solution_t& solution, i_t climber_idx) auto& data = *climbers[climber_idx]; auto v = data.view(); // == climber_views[climber_idx] const double work_units_at_start = context.gpu_heur_loop.current_work(); - const bool publish_progress = is_deterministic_mode(context.settings.determinism_mode) && + const bool publish_progress = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && context.branch_and_bound_ptr != nullptr && std::isfinite(settings.work_limit) && settings.work_limit > 0.0 && settings.iteration_limit > 0 && @@ -1293,7 +1293,7 @@ template i_t fj_t::solve(solution_t& solution) { raft::common::nvtx::range scope("fj_solve"); - bool deterministic = is_deterministic_mode(context.settings.determinism_mode); + bool deterministic = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB); if (deterministic) { settings.time_limit = std::max((f_t)0.0, settings.time_limit); settings.work_limit = settings.time_limit; @@ -1429,7 +1429,7 @@ i_t fj_t::solve(solution_t& solution) // Compute the work unit corresponding to the number of iterations elapsed // by incrementally guessing work units until the model predicts >= actual iterations // TODO: awfully ugly, change - if (is_deterministic_mode(context.settings.determinism_mode) && iterations > 0) { + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && iterations > 0) { double guessed_work = 0.0; const double work_increment = 0.1; const double max_work = settings.work_limit * 2.0; // Safety limit diff --git a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu index ba0e2d2e56..5689ea3226 100644 --- a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu @@ -150,7 +150,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tn_variables, "Size mismatch"); cuopt_assert(h_last_projection.size() == solution.problem_ptr->n_variables, "Size mismatch"); cuopt_assert(h_variable_bounds.size() == solution.problem_ptr->n_variables, "Size mismatch"); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_INFO( "FP proj inputs: assign_hash=0x%x last_proj_hash=0x%x integer_idx_hash=0x%x n_vars=%d n_int=%d", detail::compute_hash(h_assignment), detail::compute_hash(h_last_projection), @@ -205,7 +205,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t::linear_project_onto_polytope(solution_t 0) { temp_p.insert_variables(h_variables); } if (h_constraints.n_constraints() > 0) { temp_p.insert_constraints(h_constraints); } @@ -234,11 +235,12 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tget_stream()), - temp_p.n_variables, - temp_p.n_constraints); + CUOPT_DETERMINISM_LOG_INFO( + "FP proj pre-LP: temp_fingerprint=0x%x assignment_hash=0x%x n_vars=%d n_cstr=%d", + temp_p.get_fingerprint(), + detail::compute_hash(solution.assignment, solution.handle_ptr->get_stream()), + temp_p.n_variables, + temp_p.n_constraints); // copy new objective coefficients raft::copy(temp_p.objective_coefficients.data(), obj_coefficients.data(), @@ -535,21 +537,20 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s { raft::common::nvtx::range fun_scope("run_single_fp_descent"); i_t fp_iter = 0; - CUOPT_LOG_DEBUG("FP descent start: hash=0x%x feas=%d obj=%.12f timer_det=%d rem=%.6f", - solution.get_hash(), - (int)solution.get_feasible(), - solution.get_user_objective(), - (int)timer.deterministic, - timer.remaining_time()); + CUOPT_DETERMINISM_LOG_INFO("FP descent start: hash=0x%x feas=%d obj=%.12f timer_det=%d rem=%.6f", + solution.get_hash(), + (int)solution.get_feasible(), + solution.get_user_objective(), + (int)timer.deterministic, + timer.remaining_time()); // start by doing nearest rounding solution.round_nearest(); - CUOPT_LOG_DEBUG("FP descent after initial round: hash=0x%x feas=%d obj=%.12f", - solution.get_hash(), - (int)solution.get_feasible(), - solution.get_user_objective()); + CUOPT_DETERMINISM_LOG_INFO("FP descent after initial round: hash=0x%x feas=%d obj=%.12f", + solution.get_hash(), + (int)solution.get_feasible(), + solution.get_user_objective()); cuopt_assert(last_projection.size() == solution.assignment.size(), "Size mismatch"); // First projection in a descent has no previous projection history: initialize explicitly - // to avoid dependence on stale/uninitialized buffer contents. raft::copy(last_projection.data(), solution.assignment.data(), solution.assignment.size(), @@ -559,12 +560,12 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s solution.assignment.size(), solution.handle_ptr->get_stream()); while (true) { - CUOPT_LOG_DEBUG("FP iter %d pre-projection: hash=0x%x feas=%d obj=%.12f rem=%.6f", - fp_iter, - solution.get_hash(), - (int)solution.get_feasible(), - solution.get_user_objective(), - timer.remaining_time()); + CUOPT_DETERMINISM_LOG_INFO("FP iter %d pre-projection: hash=0x%x feas=%d obj=%.12f rem=%.6f", + fp_iter, + solution.get_hash(), + (int)solution.get_feasible(), + solution.get_user_objective(), + timer.remaining_time()); bool preempt = (context.diversity_manager_ptr != nullptr && context.diversity_manager_ptr->check_b_b_preemption()); if (preempt || timer.check_time_limit()) { @@ -582,7 +583,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s CUOPT_LOG_DEBUG("after fp projection n_integers %d total n_integes %d", n_integers, solution.problem_ptr->n_integer_vars); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_INFO( "FP iter %d post-projection: hash=0x%x feasible_after_lp=%d obj=%.12f rem=%.6f lp_stage=%.6f", fp_iter, solution.get_hash(), @@ -590,12 +591,12 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s solution.get_user_objective(), remaining_after_projection, proj_begin - remaining_after_projection); - CUOPT_LOG_DEBUG("FP iter %d pre-round: hash=0x%x feas=%d obj=%.12f rem=%.6f", - fp_iter, - solution.get_hash(), - (int)is_feasible, - solution.get_user_objective(), - remaining_after_projection); + CUOPT_DETERMINISM_LOG_INFO("FP iter %d pre-round: hash=0x%x feas=%d obj=%.12f rem=%.6f", + fp_iter, + solution.get_hash(), + (int)is_feasible, + solution.get_user_objective(), + remaining_after_projection); bool is_cycle = true; // temp comment for presolve run if (config.check_distance_cycle) { @@ -652,7 +653,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s is_feasible = solution.get_feasible(); n_integers = solution.compute_number_of_integers(); if (is_feasible && n_integers == solution.problem_ptr->n_integer_vars) { - CUOPT_LOG_DEBUG("Feasible solution verified with LP!"); + CUOPT_LOG_TRACE("Feasible solution verified with LP!"); return true; } } @@ -663,7 +664,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s cuopt_func_call(solution.test_variable_bounds(true)); const f_t remaining_after_round = timer.remaining_time(); proj_and_round_time = proj_begin - remaining_after_round; - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_INFO( "FP iter %d post-round: hash=0x%x feasible_after_round=%d obj=%.12f rem=%.6f " "round_stage=%.6f proj_round_total=%.6f", fp_iter, @@ -676,14 +677,14 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (!is_feasible) { const f_t time_ratio = 0.2; const f_t fj_budget = time_ratio * proj_and_round_time; - CUOPT_LOG_DEBUG("FP iter %d pre-fj-fallback: hash=0x%x rem=%.6f fj_budget=%.6f", - fp_iter, - solution.get_hash(), - remaining_after_round, - fj_budget); + CUOPT_DETERMINISM_LOG_INFO("FP iter %d pre-fj-fallback: hash=0x%x rem=%.6f fj_budget=%.6f", + fp_iter, + solution.get_hash(), + remaining_after_round, + fj_budget); is_feasible = test_fj_feasible(solution, fj_budget); const f_t remaining_after_fj = timer.remaining_time(); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_INFO( "FP iter %d post-fj-fallback: hash=0x%x feasible_after_fj=%d obj=%.12f rem=%.6f " "fj_stage=%.6f", fp_iter, diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index 68a7257fd7..478fbed49a 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -68,7 +68,7 @@ local_search_t::local_search_t(mip_solver_context_t& context template void local_search_t::start_cpufj_scratch_threads(population_t& population) { - cuopt_assert(context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC, + cuopt_assert(!(context.settings.determinism_mode & CUOPT_DETERMINISM_BB), "Scratch CPUFJ must remain opportunistic-only"); pop_ptr = &population; @@ -119,7 +119,7 @@ template void local_search_t::start_cpufj_lptopt_scratch_threads( population_t& population) { - cuopt_assert(context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC, + cuopt_assert(!(context.settings.determinism_mode & CUOPT_DETERMINISM_BB), "LP-opt CPUFJ scratch must remain opportunistic-only"); pop_ptr = &population; @@ -223,7 +223,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, const std::string& source) { if (time_limit == 0.) return solution.get_feasible(); - const bool deterministic = is_deterministic_mode(context.settings.determinism_mode); + const bool deterministic = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB); work_limit_timer_t timer(context.gpu_heur_loop, time_limit, *context.termination); const auto old_n_cstr_weights = in_fj.cstr_weights.size(); diff --git a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu index d6686bc671..7ee552d252 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu @@ -500,7 +500,7 @@ bool bounds_repair_t::repair_problem(problem_t& problem, if (timer.deterministic) { const double setup_work = estimate_bounds_repair_setup_work(problem); record_estimated_work(timer, &total_estimated_work, setup_work); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_INFO( "Repair entry: pb_hash=0x%x bounds_hash=0x%x violated_hash=0x%x n_violated=%d " "best_violation=%.6f timer_rem=%.6f total_work=%.6f setup_work=%.6f", problem.get_fingerprint(), @@ -686,11 +686,12 @@ bool bounds_repair_t::repair_problem(problem_t& problem, // copy best bounds into problem best_bounds.update_to(problem, handle_ptr); if (timer.deterministic) { - CUOPT_LOG_DEBUG("Repair work estimate: loops=%d total=%.6f best_violation=%.6f feasible=%d", - repair_iterations, - total_estimated_work, - best_violation, - (int)feasible); + CUOPT_DETERMINISM_LOG( + "Repair work estimate: loops=%d total=%.6f best_violation=%.6f feasible=%d", + repair_iterations, + total_estimated_work, + best_violation, + (int)feasible); } CUOPT_LOG_DEBUG("Repair: returning with feas: %d vio %f", feasible, best_violation); return feasible; diff --git a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu index bad90fe0bd..48fd858a0f 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu @@ -772,7 +772,7 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob i_t n_of_repairs_needed_for_feasible = 0; // TODO: do this better i_t iter_limit = std::numeric_limits::max(); - if (is_deterministic_mode(this->context.settings.determinism_mode)) { iter_limit = 100; } + if ((this->context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { iter_limit = 100; } do { n_of_repairs_needed_for_feasible++; if (timer.check_time_limit() || iter_limit-- <= 0) { diff --git a/cpp/src/mip_heuristics/mip_constants.hpp b/cpp/src/mip_heuristics/mip_constants.hpp index 3d37a7f967..66f5ebd273 100644 --- a/cpp/src/mip_heuristics/mip_constants.hpp +++ b/cpp/src/mip_heuristics/mip_constants.hpp @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -11,18 +11,3 @@ #define MIP_INSTANTIATE_FLOAT CUOPT_INSTANTIATE_FLOAT #define MIP_INSTANTIATE_DOUBLE CUOPT_INSTANTIATE_DOUBLE - -namespace cuopt::linear_programming::detail { - -inline constexpr bool is_deterministic_mode(int determinism_mode) -{ - return determinism_mode == CUOPT_MODE_DETERMINISTIC || - determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; -} - -inline constexpr bool is_gpu_heuristics_deterministic_mode(int determinism_mode) -{ - return determinism_mode == CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; -} - -} // namespace cuopt::linear_programming::detail diff --git a/cpp/src/mip_heuristics/presolve/bounds_presolve.cu b/cpp/src/mip_heuristics/presolve/bounds_presolve.cu index 04a9287b13..1e853436b5 100644 --- a/cpp/src/mip_heuristics/presolve/bounds_presolve.cu +++ b/cpp/src/mip_heuristics/presolve/bounds_presolve.cu @@ -172,7 +172,7 @@ termination_criterion_t bound_presolve_t::bound_update_loop(problem_t< termination_criterion_t criteria = termination_criterion_t::ITERATION_LIMIT; // CHANGE once we have a work predictor - if (is_deterministic_mode(context.settings.determinism_mode)) { + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { timer = timer_t(std::numeric_limits::infinity()); settings.iteration_limit = std::min(settings.iteration_limit, 50); } diff --git a/cpp/src/mip_heuristics/presolve/probing_cache.cu b/cpp/src/mip_heuristics/presolve/probing_cache.cu index b50bd2a34b..e6913f2393 100644 --- a/cpp/src/mip_heuristics/presolve/probing_cache.cu +++ b/cpp/src/mip_heuristics/presolve/probing_cache.cu @@ -858,7 +858,7 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, bound_presolve.settings.time_limit = timer.remaining_time(); // TODO: proper work unit accounting in deterministic mode for the probing cache - if (is_deterministic_mode(bound_presolve.context.settings.determinism_mode)) { + if ((bound_presolve.context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { bound_presolve.settings.iteration_limit = 1; priority_indices.resize(std::min(priority_indices.size(), 2048)); } diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index 3dc35ddd02..13ec23bde3 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -144,45 +144,47 @@ optimization_problem_solution_t get_relaxed_lp_solution( lp_solver.set_initial_primal_solution(assignment); lp_solver.set_initial_dual_solution(lp_state.prev_dual); } - // CUOPT_LOG_DEBUG( - // "running LP with n_vars %d n_cstr %d", op_problem.n_variables, op_problem.n_constraints); + CUOPT_LOG_TRACE( + "running LP with n_vars %d n_cstr %d", op_problem.n_variables, op_problem.n_constraints); // before LP flush the logs as it takes quite some time cuopt::default_logger().flush(); // temporarily add timer auto start_time = timer_t(pdlp_settings.time_limit); lp_solver.set_inside_mip(true); - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG_INFO( "prev solution sizes primal=%lu dual=%lu", assignment.size(), lp_state.prev_dual.size()); auto solver_response = lp_solver.run_solver(start_time); - CUOPT_LOG_DEBUG("post LP primal size %lu", solver_response.get_primal_solution().size()); + CUOPT_DETERMINISM_LOG_INFO("post LP primal size %lu", + solver_response.get_primal_solution().size()); const int actual_iters = solver_response.get_additional_termination_information().number_of_steps_taken; - CUOPT_LOG_DEBUG("LP call %lu result: status=%d iters=%d primal_hash=0x%x", - lp_call_id, - (int)solver_response.get_termination_status(), - actual_iters, - solver_response.get_primal_solution().size() != 0 - ? detail::compute_hash(solver_response.get_primal_solution(), - op_problem.handle_ptr->get_stream()) - : 0u); + CUOPT_DETERMINISM_LOG_INFO("LP call %lu result: status=%d iters=%d primal_hash=0x%x", + lp_call_id, + (int)solver_response.get_termination_status(), + actual_iters, + solver_response.get_primal_solution().size() != 0 + ? detail::compute_hash(solver_response.get_primal_solution(), + op_problem.handle_ptr->get_stream()) + : 0u); if (determinism_mode && settings.work_context != nullptr) { double work_to_record = settings.work_limit; if (estim_iters > 0) { work_to_record = settings.work_limit * std::clamp((double)actual_iters / (double)estim_iters, 0.0, 1.0); } - CUOPT_LOG_DEBUG("LP call %lu recording %.6fwu (actual_iters=%d estim_iters=%d requested=%.6f)", - lp_call_id, - work_to_record, - actual_iters, - estim_iters, - settings.work_limit); + CUOPT_DETERMINISM_LOG_INFO( + "LP call %lu recording %.6fwu (actual_iters=%d estim_iters=%d requested=%.6f)", + lp_call_id, + work_to_record, + actual_iters, + estim_iters, + settings.work_limit); settings.work_context->record_work_sync_on_horizon(work_to_record); } if (solver_response.get_primal_solution().size() != 0 && solver_response.get_dual_solution().size() != 0 && settings.save_state) { - // CUOPT_LOG_DEBUG("saving initial primal solution of size %d", lp_state.prev_primal.size()); + CUOPT_LOG_TRACE("saving initial primal solution of size %d", lp_state.prev_primal.size()); lp_state.set_state(solver_response.get_primal_solution(), solver_response.get_dual_solution()); } if (solver_response.get_primal_solution().size() != 0) { @@ -192,84 +194,19 @@ optimization_problem_solution_t get_relaxed_lp_solution( solver_response.get_primal_solution().size(), op_problem.handle_ptr->get_stream()); } - CUOPT_LOG_DEBUG("LP call %lu assignment_after_copy hash=0x%x", - lp_call_id, - detail::compute_hash(assignment, op_problem.handle_ptr->get_stream())); + CUOPT_DETERMINISM_LOG_INFO("LP call %lu assignment_after_copy hash=0x%x", + lp_call_id, + detail::compute_hash(assignment, op_problem.handle_ptr->get_stream())); if (solver_response.get_termination_status() == pdlp_termination_status_t::Optimal) { - // CUOPT_LOG_DEBUG("feasible solution found with LP objective %f", - // solver_response.get_objective_value()); + CUOPT_LOG_TRACE("feasible solution found with LP objective %f", + solver_response.get_objective_value()); } else { - CUOPT_LOG_DEBUG("LP returned with reason %d, %d iterations", - solver_response.get_termination_status(), - solver_response.get_additional_termination_information().number_of_steps_taken); + CUOPT_DETERMINISM_LOG_INFO( + "LP returned with reason %d, %d iterations", + solver_response.get_termination_status(), + solver_response.get_additional_termination_information().number_of_steps_taken); } - // auto function_end_time = std::chrono::high_resolution_clock::now(); - // auto elapsed_ms = - // std::chrono::duration_cast(function_end_time - - // function_start_time) - // .count(); - // CUOPT_LOG_DEBUG("get_relaxed_lp_solution took %lld ms for %d iterations", - // elapsed_ms, - // solver_response.get_additional_termination_information().number_of_steps_taken); - - // // === PDLP PREDICTOR RESULTS - START === - // auto term_info = solver_response.get_additional_termination_information(); - // const i_t n_vars = op_problem.n_variables; - // const i_t n_cstrs = op_problem.n_constraints; - // const int64_t nnz = op_problem.nnz; - // const int64_t total_spmv = lp_solver.get_total_spmv_ops(); - // const int64_t total_nnz = total_spmv * nnz; - // const double nnz_per_sec = - // (elapsed_ms > 0) ? static_cast(total_nnz) / (elapsed_ms / 1000.0) : 0.0; - // const double nnz_per_iter = (term_info.number_of_steps_taken > 0) - // ? static_cast(total_nnz) / - // term_info.number_of_steps_taken : 0.0; - - // // Compute sparsity metrics - // const double sparsity = (n_cstrs > 0 && n_vars > 0) - // ? static_cast(nnz) / - // (static_cast(n_cstrs) * n_vars) : 0.0; - // double nnz_stddev = 0.0; - // [[maybe_unused]] double unbalancedness = 0.0; - // if (op_problem.offsets.size() == static_cast(n_cstrs + 1) && n_cstrs > 0) { - // std::vector h_offsets(n_cstrs + 1); - // raft::copy(h_offsets.data(), - // op_problem.offsets.data(), - // n_cstrs + 1, - // op_problem.handle_ptr->get_stream()); - // op_problem.handle_ptr->sync_stream(); - - // const double mean_nnz = static_cast(nnz) / n_cstrs; - // double variance_sum = 0.0; - // for (i_t row = 0; row < n_cstrs; ++row) { - // const double row_nnz = static_cast(h_offsets[row + 1] - h_offsets[row]); - // const double diff = row_nnz - mean_nnz; - // variance_sum += diff * diff; - // } - // const double variance = variance_sum / n_cstrs; - // nnz_stddev = std::sqrt(variance); - // unbalancedness = (mean_nnz > 0) ? nnz_stddev / mean_nnz : 0.0; - // } - - // CUOPT_LOG_INFO( - // "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f - // " "iters=%d time_ms=%lld term=%d spmv_ops=%ld total_nnz=%.2e nnz/s=%.2e nnz/iter=%.2e", - // n_vars, - // n_cstrs, - // nnz, - // sparsity, - // nnz_stddev, - // unbalancedness, - // term_info.number_of_steps_taken, - // elapsed_ms, - // static_cast(solver_response.get_termination_status()), - // total_spmv, - // static_cast(total_nnz), - // nnz_per_sec, - // nnz_per_iter); - // === PDLP PREDICTOR RESULTS - END === - return solver_response; } diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index e2004ded46..21c57cd605 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -194,7 +194,6 @@ mip_solution_t run_mip(detail::problem_t& problem, int hidesol = std::getenv("CUOPT_MIP_HIDE_SOLUTION") ? atoi(std::getenv("CUOPT_MIP_HIDE_SOLUTION")) : 0; - hidesol = 0; if (!hidesol) { detail::print_solution(scaled_problem.handle_ptr, sol.get_solution()); } return sol; } @@ -262,7 +261,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, auto timer = cuopt::termination_checker_t(time_limit, cuopt::termination_checker_t::root_tag_t{}); - const bool deterministic_run = detail::is_deterministic_mode(settings.determinism_mode); + const bool deterministic_run = (settings.determinism_mode & CUOPT_DETERMINISM_BB); double presolve_time = 0.0; std::unique_ptr> presolver; diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 435fc31410..11b26b7552 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -146,7 +146,7 @@ solution_t mip_solver_t::run_solver() context.problem_ptr->post_process_solution(sol); return sol; } - const bool deterministic_run = is_deterministic_mode(context.settings.determinism_mode); + const bool deterministic_run = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB); const f_t gpu_heur_work_limit = deterministic_run ? context.settings.work_limit : timer_.get_time_limit(); if (deterministic_run) @@ -259,9 +259,9 @@ solution_t mip_solver_t::run_solver() branch_and_bound_settings.max_cut_passes = context.settings.max_cut_passes; branch_and_bound_settings.mir_cuts = context.settings.mir_cuts; branch_and_bound_settings.deterministic = - is_deterministic_mode(context.settings.determinism_mode); + (context.settings.determinism_mode & CUOPT_DETERMINISM_BB); - if (is_deterministic_mode(context.settings.determinism_mode)) { + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { branch_and_bound_settings.work_limit = context.settings.work_limit; } else { branch_and_bound_settings.work_limit = std::numeric_limits::infinity(); @@ -302,7 +302,7 @@ solution_t mip_solver_t::run_solver() // heuristic_preemption_callback is needed in both modes to properly stop the heuristic thread branch_and_bound_settings.heuristic_preemption_callback = std::bind( &branch_and_bound_solution_helper_t::preempt_heuristic_solver, &solution_helper); - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + if (!(context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { branch_and_bound_settings.set_simplex_solution_callback = std::bind(&branch_and_bound_solution_helper_t::set_simplex_solution, &solution_helper, @@ -326,14 +326,14 @@ solution_t mip_solver_t::run_solver() [stats_ptr](f_t user_bound) { stats_ptr->set_solution_bound(user_bound); }); // Set the primal heuristics -> branch and bound callback - if (context.settings.determinism_mode == CUOPT_MODE_OPPORTUNISTIC) { + if (!(context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { branch_and_bound->set_concurrent_lp_root_solve(true); context.problem_ptr->branch_and_bound_callback = std::bind(&dual_simplex::branch_and_bound_t::set_new_solution, branch_and_bound.get(), std::placeholders::_1); - } else if (is_deterministic_mode(context.settings.determinism_mode)) { + } else if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { branch_and_bound->set_concurrent_lp_root_solve(false); // TODO once deterministic GPU heuristics are integrated // context.problem_ptr->branch_and_bound_callback = @@ -344,7 +344,7 @@ solution_t mip_solver_t::run_solver() context.work_unit_scheduler_.register_context(branch_and_bound->get_work_unit_context()); - if (is_deterministic_mode(context.settings.determinism_mode)) { + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { context.problem_ptr->set_root_relaxation_solution_callback = nullptr; } else { context.problem_ptr->set_root_relaxation_solution_callback = @@ -389,7 +389,7 @@ solution_t mip_solver_t::run_solver() context.problem_ptr->get_user_obj_from_solver_obj(branch_and_bound_solution.lower_bound)); } if (bb_status == dual_simplex::mip_status_t::INFEASIBLE) { sol.set_problem_fully_reduced(); } - if (is_deterministic_mode(context.settings.determinism_mode) && + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && std::isfinite(branch_and_bound_solution.objective)) { CUOPT_DETERMINISM_LOG_INFO( "Deterministic solver B&B overwrite: bb_status=%d bb_obj=%.16e bb_lower=%.16e " @@ -455,7 +455,7 @@ solution_t mip_solver_t::run_solver() } } sol = std::move(bb_sol); - } else if (is_deterministic_mode(context.settings.determinism_mode)) { + } else if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { CUOPT_DETERMINISM_LOG_INFO( "Deterministic B&B overwrite skipped: bb_obj=%.16e bb_obj_finite=%d bb_has_incumbent=%d", branch_and_bound_solution.objective, @@ -468,7 +468,7 @@ solution_t mip_solver_t::run_solver() context.stats.num_nodes = branch_and_bound_solution.nodes_explored; context.stats.num_simplex_iterations = branch_and_bound_solution.simplex_iterations; - if (is_deterministic_mode(context.settings.determinism_mode)) { + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { double bnb_work = branch_and_bound->get_work_unit_context().current_work(); double gpu_work = context.gpu_heur_loop.current_work(); double bnb_scale = solver_settings_.bnb_work_unit_scale; diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index 5fea7be96d..90312f7a4a 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -62,7 +62,7 @@ solution_callback_payload_t make_solution_callback_payload_from_soluti double work_timestamp) { cuopt_assert(problem_ptr != nullptr, "Callback payload problem pointer must not be null"); - if (work_timestamp < 0.0 && is_deterministic_mode(settings.determinism_mode)) { + if (work_timestamp < 0.0 && (settings.determinism_mode & CUOPT_DETERMINISM_BB)) { work_timestamp = gpu_heur_loop.current_work(); } solution_callback_payload_t payload{}; @@ -94,7 +94,7 @@ solution_callback_payload_t make_solution_callback_payload_from_host_s double work_timestamp) { cuopt_assert(problem_ptr != nullptr, "Callback payload problem pointer must not be null"); - if (work_timestamp < 0.0 && is_deterministic_mode(settings.determinism_mode)) { + if (work_timestamp < 0.0 && (settings.determinism_mode & CUOPT_DETERMINISM_BB)) { work_timestamp = gpu_heur_loop.current_work(); } solution_callback_payload_t payload{}; @@ -192,7 +192,7 @@ struct mip_solver_context_t { cuopt_assert(problem_ptr != nullptr, "problem_ptr is nullptr"); stats.set_solution_bound(problem_ptr->maximize ? std::numeric_limits::infinity() : -std::numeric_limits::infinity()); - gpu_heur_loop.deterministic = is_deterministic_mode(settings.determinism_mode); + gpu_heur_loop.deterministic = (settings.determinism_mode & CUOPT_DETERMINISM_BB); cuopt_assert(settings.cpufj_work_unit_scale > 0.0, "CPUFJ work-unit scale must be positive"); cuopt_assert(settings.gpu_heur_work_unit_scale > 0.0, "GPU heuristic work-unit scale must be positive"); From d531ad17545f7b4deb5669f136477cd0be497531 Mon Sep 17 00:00:00 2001 From: Gil Forsyth Date: Thu, 12 Mar 2026 10:16:55 -0400 Subject: [PATCH 178/225] feat: add support for Python 3.14 (#933) Contributes to https://github.com/rapidsai/build-planning/issues/205 This PR adds support for Python 3.14. ## Notes for Reviewers This is part of ongoing work to add Python 3.14 support across RAPIDS. It temporarily introduces a build/test matrix including Python 3.14, from https://github.com/rapidsai/shared-workflows/pull/508. A follow-up PR will revert back to pointing at the `main` branch of `shared-workflows` once all RAPIDS repos have added Python 3.14 support. ### This will fail until all dependencies have been updated to Python 3.14 CI here is expected to fail until all of this project's upstream dependencies support Python 3.14. This can be merged whenever all CI jobs are passing. Authors: - Gil Forsyth (https://github.com/gforsyth) Approvers: - James Lamb (https://github.com/jameslamb) - Ramakrishnap (https://github.com/rgsl888prabhu) URL: https://github.com/NVIDIA/cuopt/pull/933 --- .github/workflows/build.yaml | 28 ++++++++--------- .github/workflows/pr.yaml | 30 +++++++++---------- CONTRIBUTING.md | 2 +- README.md | 8 ++--- .../all_cuda-129_arch-aarch64.yaml | 8 ++--- .../all_cuda-129_arch-x86_64.yaml | 8 ++--- .../all_cuda-131_arch-aarch64.yaml | 8 ++--- .../all_cuda-131_arch-x86_64.yaml | 8 ++--- conda/recipes/cuopt-server/recipe.yaml | 4 +-- conda/recipes/cuopt-sh-client/recipe.yaml | 6 ++-- conda/recipes/cuopt/recipe.yaml | 6 ++-- conda/recipes/mps-parser/recipe.yaml | 4 +-- dependencies.yaml | 14 +++++---- docs/cuopt/source/system-requirements.rst | 4 +-- .../cuopt/linear_programming/pyproject.toml | 1 + python/cuopt/pyproject.toml | 5 ++-- python/cuopt_self_hosted/pyproject.toml | 3 +- .../cuopt_server/cuopt_service.py | 20 +++++++------ .../cuopt_server/utils/process_handler.py | 9 +++--- python/cuopt_server/pyproject.toml | 5 ++-- python/libcuopt/pyproject.toml | 1 + .../assets/mps_solver/model.py | 2 +- 22 files changed, 98 insertions(+), 86 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 051d91d1f5..593d48bd74 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -45,7 +45,7 @@ concurrency: jobs: cpp-build: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -55,7 +55,7 @@ jobs: python-build: needs: [cpp-build] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -65,7 +65,7 @@ jobs: upload-conda: needs: [cpp-build, python-build] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/conda-upload-packages.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -73,7 +73,7 @@ jobs: sha: ${{ inputs.sha }} wheel-build-cuopt-mps-parser: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -88,7 +88,7 @@ jobs: wheel-publish-cuopt-mps-parser: needs: wheel-build-cuopt-mps-parser secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -99,7 +99,7 @@ jobs: wheel-build-libcuopt: needs: wheel-build-cuopt-mps-parser secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -112,7 +112,7 @@ jobs: wheel-publish-libcuopt: needs: wheel-build-libcuopt secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -123,7 +123,7 @@ jobs: wheel-build-cuopt: needs: [wheel-build-cuopt-mps-parser, wheel-build-libcuopt] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -135,7 +135,7 @@ jobs: wheel-publish-cuopt: needs: wheel-build-cuopt secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -145,7 +145,7 @@ jobs: package-type: python wheel-build-cuopt-server: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -160,7 +160,7 @@ jobs: wheel-publish-cuopt-server: needs: wheel-build-cuopt-server secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -171,7 +171,7 @@ jobs: docs-build: needs: [python-build] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} node_type: "gpu-l4-latest-1" @@ -185,7 +185,7 @@ jobs: script: "ci/build_docs.sh" wheel-build-cuopt-sh-client: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} @@ -201,7 +201,7 @@ jobs: wheel-publish-cuopt-sh-client: needs: wheel-build-cuopt-sh-client secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@python-3.14 with: build_type: ${{ inputs.build_type || 'branch' }} branch: ${{ inputs.branch }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 52dfa3b60e..95741c1fb5 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -34,7 +34,7 @@ jobs: - wheel-build-cuopt-sh-client - test-self-hosted-server secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/pr-builder.yaml@python-3.14 if: always() with: needs: ${{ toJSON(needs) }} @@ -111,7 +111,7 @@ jobs: changed-files: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/changed-files.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/changed-files.yaml@python-3.14 with: files_yaml: | build_docs: @@ -279,20 +279,20 @@ jobs: - '!gemini-extension.json' checks: secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@python-3.14 with: enable_check_generated_files: false conda-cpp-build: needs: [checks, compute-matrix-filters] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-build.yaml@python-3.14 with: build_type: pull-request script: ci/build_cpp.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_lean_filter }} conda-cpp-tests: needs: [conda-cpp-build, changed-files, compute-matrix-filters] - uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@python-3.14 if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp with: build_type: pull-request @@ -308,14 +308,14 @@ jobs: conda-python-build: needs: [conda-cpp-build, compute-matrix-filters] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/conda-python-build.yaml@python-3.14 with: build_type: pull-request script: ci/build_python.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_test_filter }} conda-python-tests: needs: [conda-python-build, changed-files, compute-matrix-filters] - uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@python-3.14 if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_conda with: run_codecov: false @@ -332,7 +332,7 @@ jobs: docs-build: needs: [conda-python-build, changed-files] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/custom-job.yaml@python-3.14 if: fromJSON(needs.changed-files.outputs.changed_file_groups).build_docs with: build_type: pull-request @@ -345,7 +345,7 @@ jobs: wheel-build-cuopt-mps-parser: needs: compute-matrix-filters secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: build_type: pull-request script: ci/build_wheel_cuopt_mps_parser.sh @@ -357,7 +357,7 @@ jobs: wheel-build-libcuopt: needs: [wheel-build-cuopt-mps-parser, compute-matrix-filters] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: # build for every combination of arch and CUDA version, but only for the latest Python matrix_filter: ${{ needs.compute-matrix-filters.outputs.libcuopt_filter }} @@ -368,7 +368,7 @@ jobs: wheel-build-cuopt: needs: [wheel-build-cuopt-mps-parser, wheel-build-libcuopt, compute-matrix-filters] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: build_type: pull-request script: ci/build_wheel_cuopt.sh @@ -377,7 +377,7 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.wheel_lean_filter }} wheel-tests-cuopt: needs: [wheel-build-cuopt, wheel-build-cuopt-mps-parser, wheel-build-cuopt-sh-client, changed-files, compute-matrix-filters] - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@python-3.14 if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_wheels with: build_type: pull-request @@ -393,7 +393,7 @@ jobs: wheel-build-cuopt-server: needs: [checks, compute-matrix-filters] secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: build_type: pull-request script: ci/build_wheel_cuopt_server.sh @@ -405,7 +405,7 @@ jobs: wheel-build-cuopt-sh-client: needs: compute-matrix-filters secrets: inherit - uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@python-3.14 with: build_type: pull-request script: ci/build_wheel_cuopt_sh_client.sh @@ -417,7 +417,7 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.cuopt_sh_client_filter }} wheel-tests-cuopt-server: needs: [wheel-build-cuopt, wheel-build-cuopt-server, changed-files, compute-matrix-filters] - uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@main + uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@python-3.14 if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_wheels with: build_type: pull-request diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f3b75eb14..2835786ae4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,7 +101,7 @@ CUDA/GPU Runtime: Python: -* Python >=3.11.x, <= 3.13.x +* Python >=3.11.x, <= 3.14.x OS: diff --git a/README.md b/README.md index 5e817ea6ef..379a48c350 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ This repo is also hosted as a [COIN-OR](http://github.com/coin-or/cuopt/) projec ### Python requirements -* Python >=3.11, <=3.13 +* Python >=3.11, <=3.14 ### OS requirements @@ -130,13 +130,13 @@ Users can pull the cuOpt container from the NVIDIA container registry. ```bash # For CUDA 12.x -docker pull nvidia/cuopt:latest-cuda12.9-py3.13 +docker pull nvidia/cuopt:latest-cuda12.9-py3.14 # For CUDA 13.x -docker pull nvidia/cuopt:latest-cuda13.0-py3.13 +docker pull nvidia/cuopt:latest-cuda13.0-py3.14 ``` -Note: The ``latest`` tag is the latest stable release of cuOpt. If you want to use a specific version, you can use the ``-cuda12.9-py3.13`` or ``-cuda13.0-py3.13`` tag. For example, to use cuOpt 25.10.0, you can use the ``25.10.0-cuda12.9-py3.13`` or ``25.10.0-cuda13.0-py3.13`` tag. Please refer to `cuOpt dockerhub page `_ for the list of available tags. +Note: The ``latest`` tag is the latest stable release of cuOpt. If you want to use a specific version, you can use the ``-cuda12.9-py3.14`` or ``-cuda13.0-py3.14`` tag. For example, to use cuOpt 25.10.0, you can use the ``25.10.0-cuda12.9-py3.13`` or ``25.10.0-cuda13.0-py3.13`` tag. Please refer to `cuOpt dockerhub page `_ for the list of available tags. More information about the cuOpt container can be found [here](https://docs.nvidia.com/cuopt/user-guide/latest/cuopt-server/quick-start.html#container-from-docker-hub). diff --git a/conda/environments/all_cuda-129_arch-aarch64.yaml b/conda/environments/all_cuda-129_arch-aarch64.yaml index 3cee401c5c..ecef112dd5 100644 --- a/conda/environments/all_cuda-129_arch-aarch64.yaml +++ b/conda/environments/all_cuda-129_arch-aarch64.yaml @@ -36,13 +36,13 @@ dependencies: - librmm==26.4.*,>=0.0.0a0 - make - msgpack-numpy==0.4.8 -- msgpack-python==1.1.0 +- msgpack-python==1.1.2 - myst-nb - myst-parser - ninja - notebook -- numba-cuda>=0.22.1,<0.23.0 -- numba>=0.60.0 +- numba-cuda>=0.22.1 +- numba>=0.60.0,<0.65.0 - numpy>=1.23.5,<3.0 - numpydoc - pandas>=2.0 @@ -54,7 +54,7 @@ dependencies: - pyrsistent - pytest-cov - pytest<9.0 -- python>=3.11,<3.14 +- python>=3.11,<3.15 - pyyaml>=6.0.0 - rapids-build-backend>=0.4.0,<0.5.0 - rapids-logger==0.2.*,>=0.0.0a0 diff --git a/conda/environments/all_cuda-129_arch-x86_64.yaml b/conda/environments/all_cuda-129_arch-x86_64.yaml index 5632c8c9c7..35c825280c 100644 --- a/conda/environments/all_cuda-129_arch-x86_64.yaml +++ b/conda/environments/all_cuda-129_arch-x86_64.yaml @@ -36,13 +36,13 @@ dependencies: - librmm==26.4.*,>=0.0.0a0 - make - msgpack-numpy==0.4.8 -- msgpack-python==1.1.0 +- msgpack-python==1.1.2 - myst-nb - myst-parser - ninja - notebook -- numba-cuda>=0.22.1,<0.23.0 -- numba>=0.60.0 +- numba-cuda>=0.22.1 +- numba>=0.60.0,<0.65.0 - numpy>=1.23.5,<3.0 - numpydoc - pandas>=2.0 @@ -54,7 +54,7 @@ dependencies: - pyrsistent - pytest-cov - pytest<9.0 -- python>=3.11,<3.14 +- python>=3.11,<3.15 - pyyaml>=6.0.0 - rapids-build-backend>=0.4.0,<0.5.0 - rapids-logger==0.2.*,>=0.0.0a0 diff --git a/conda/environments/all_cuda-131_arch-aarch64.yaml b/conda/environments/all_cuda-131_arch-aarch64.yaml index add21cbb2f..2b717d4e98 100644 --- a/conda/environments/all_cuda-131_arch-aarch64.yaml +++ b/conda/environments/all_cuda-131_arch-aarch64.yaml @@ -36,13 +36,13 @@ dependencies: - librmm==26.4.*,>=0.0.0a0 - make - msgpack-numpy==0.4.8 -- msgpack-python==1.1.0 +- msgpack-python==1.1.2 - myst-nb - myst-parser - ninja - notebook -- numba-cuda>=0.22.1,<0.23.0 -- numba>=0.60.0 +- numba-cuda>=0.22.1 +- numba>=0.60.0,<0.65.0 - numpy>=1.23.5,<3.0 - numpydoc - pandas>=2.0 @@ -54,7 +54,7 @@ dependencies: - pyrsistent - pytest-cov - pytest<9.0 -- python>=3.11,<3.14 +- python>=3.11,<3.15 - pyyaml>=6.0.0 - rapids-build-backend>=0.4.0,<0.5.0 - rapids-logger==0.2.*,>=0.0.0a0 diff --git a/conda/environments/all_cuda-131_arch-x86_64.yaml b/conda/environments/all_cuda-131_arch-x86_64.yaml index 0fa31c7961..f605a83f3b 100644 --- a/conda/environments/all_cuda-131_arch-x86_64.yaml +++ b/conda/environments/all_cuda-131_arch-x86_64.yaml @@ -36,13 +36,13 @@ dependencies: - librmm==26.4.*,>=0.0.0a0 - make - msgpack-numpy==0.4.8 -- msgpack-python==1.1.0 +- msgpack-python==1.1.2 - myst-nb - myst-parser - ninja - notebook -- numba-cuda>=0.22.1,<0.23.0 -- numba>=0.60.0 +- numba-cuda>=0.22.1 +- numba>=0.60.0,<0.65.0 - numpy>=1.23.5,<3.0 - numpydoc - pandas>=2.0 @@ -54,7 +54,7 @@ dependencies: - pyrsistent - pytest-cov - pytest<9.0 -- python>=3.11,<3.14 +- python>=3.11,<3.15 - pyyaml>=6.0.0 - rapids-build-backend>=0.4.0,<0.5.0 - rapids-logger==0.2.*,>=0.0.0a0 diff --git a/conda/recipes/cuopt-server/recipe.yaml b/conda/recipes/cuopt-server/recipe.yaml index 8c2875fc49..58227487f8 100644 --- a/conda/recipes/cuopt-server/recipe.yaml +++ b/conda/recipes/cuopt-server/recipe.yaml @@ -28,14 +28,14 @@ build: requirements: host: - pip - - python =${{ py_version }} + - python-gil =${{ py_version }} - rapids-build-backend >=0.4.0,<0.5.0 - setuptools>=77.0.0 run: - cuopt =${{ version }} - fastapi >=0.104.1 - jsonref =1.1.0 - - msgpack-python =1.1.0 + - msgpack-python =1.1.2 - msgpack-numpy =0.4.8 - numpy >=1.23,<3.0 - pandas>=2 diff --git a/conda/recipes/cuopt-sh-client/recipe.yaml b/conda/recipes/cuopt-sh-client/recipe.yaml index c0a1d0dc66..d471f1917f 100644 --- a/conda/recipes/cuopt-sh-client/recipe.yaml +++ b/conda/recipes/cuopt-sh-client/recipe.yaml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 schema_version: 1 @@ -26,10 +26,10 @@ build: requirements: host: - pip - - python =${{ py_version }} + - python-gil =${{ py_version }} - setuptools>=77.0.0 run: - - msgpack-python =1.1.0 + - msgpack-python =1.1.2 - python - requests diff --git a/conda/recipes/cuopt/recipe.yaml b/conda/recipes/cuopt/recipe.yaml index 0b4c8abc4f..87f0ee1057 100644 --- a/conda/recipes/cuopt/recipe.yaml +++ b/conda/recipes/cuopt/recipe.yaml @@ -75,7 +75,7 @@ requirements: - libcuopt =${{ version }} - pip - pylibraft =${{ minor_version }} - - python =${{ py_version }} + - python-gil =${{ py_version }} - rapids-build-backend >=0.4.0,<0.5.0 - rmm =${{ minor_version }} - scikit-build-core>=0.11.0 @@ -89,8 +89,8 @@ requirements: - cupy >=13.6.0 - h5py - libcuopt =${{ version }} - - numba >=0.60.0 - - numba-cuda>=0.22.1,<0.23.0 + - numba>=0.60.0,<0.65.0 + - numba-cuda>=0.22.1 - numpy >=1.23,<3.0 - pandas >=2.0 - pylibraft =${{ minor_version }} diff --git a/conda/recipes/mps-parser/recipe.yaml b/conda/recipes/mps-parser/recipe.yaml index 859bae6fae..7e423715b4 100644 --- a/conda/recipes/mps-parser/recipe.yaml +++ b/conda/recipes/mps-parser/recipe.yaml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION. # SPDX-License-Identifier: Apache-2.0 schema_version: 1 @@ -41,7 +41,7 @@ requirements: - cython >=3.0.0 - libmps-parser =${{ version }} - pip - - python =${{ py_version }} + - python-gil =${{ py_version }} - rapids-build-backend >=0.4.0,<0.5.0 - scikit-build-core >=0.11.0 run: diff --git a/dependencies.yaml b/dependencies.yaml index 011dfbcee6..014889c7d5 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -346,8 +346,8 @@ dependencies: common: - output_types: [conda, requirements, pyproject] packages: - - numba-cuda>=0.22.1,<0.23.0 - - numba>=0.60.0 + - numba-cuda>=0.22.1 + - numba>=0.60.0,<0.65.0 - &pandas pandas>=2.0 - &pyyaml pyyaml>=6.0.0 - scipy>=1.14.1 @@ -373,10 +373,10 @@ dependencies: - &requests requests - output_types: [requirements, pyproject] packages: - - &msgpack msgpack==1.1.0 + - &msgpack msgpack==1.1.2 - output_types: conda packages: - - &msgpack_python msgpack-python==1.1.0 + - &msgpack_python msgpack-python==1.1.2 run_cuopt_server: common: @@ -784,5 +784,9 @@ dependencies: packages: - python=3.13 - matrix: + py: "3.14" packages: - - python>=3.11,<3.14 + - python=3.14 + - matrix: + packages: + - python>=3.11,<3.15 diff --git a/docs/cuopt/source/system-requirements.rst b/docs/cuopt/source/system-requirements.rst index 92ac2d70fc..bbc37f26e9 100644 --- a/docs/cuopt/source/system-requirements.rst +++ b/docs/cuopt/source/system-requirements.rst @@ -26,7 +26,7 @@ Dependencies are installed automatically when using the pip and Conda installati - 12.0+ * Python: - - >= 3.11.* and <= 3.13.* + - >= 3.11.* and <= 3.14.* * NVIDIA drivers: - 525.60.13+ (Linux) @@ -98,4 +98,4 @@ Thin-client for Self-Hosted - x86-64 - ARM64 -* Python >= 3.11.x <= 3.13.x +* Python >= 3.11.x <= 3.14.x diff --git a/python/cuopt/cuopt/linear_programming/pyproject.toml b/python/cuopt/cuopt/linear_programming/pyproject.toml index e59f8d3a0f..934b12f547 100644 --- a/python/cuopt/cuopt/linear_programming/pyproject.toml +++ b/python/cuopt/cuopt/linear_programming/pyproject.toml @@ -27,6 +27,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] [project.urls] diff --git a/python/cuopt/pyproject.toml b/python/cuopt/pyproject.toml index 44734577c9..e86b5bdd73 100644 --- a/python/cuopt/pyproject.toml +++ b/python/cuopt/pyproject.toml @@ -24,8 +24,8 @@ dependencies = [ "cuopt-mps-parser==26.4.*,>=0.0.0a0", "cupy-cuda13x>=13.6.0", "libcuopt==26.4.*,>=0.0.0a0", - "numba-cuda>=0.22.1,<0.23.0", - "numba>=0.60.0", + "numba-cuda>=0.22.1", + "numba>=0.60.0,<0.65.0", "numpy>=1.23.5,<3.0", "pandas>=2.0", "pylibraft==26.4.*,>=0.0.0a0", @@ -40,6 +40,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] [project.optional-dependencies] diff --git a/python/cuopt_self_hosted/pyproject.toml b/python/cuopt_self_hosted/pyproject.toml index ce1fb9ae15..7645c99ed0 100644 --- a/python/cuopt_self_hosted/pyproject.toml +++ b/python/cuopt_self_hosted/pyproject.toml @@ -22,7 +22,7 @@ requires-python = ">=3.11" dependencies = [ "cuopt-mps-parser==26.4.*,>=0.0.0a0", "msgpack-numpy==0.4.8", - "msgpack==1.1.0", + "msgpack==1.1.2", "requests", ] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`. classifiers = [ @@ -30,6 +30,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Operating System :: OS Independent", ] diff --git a/python/cuopt_server/cuopt_server/cuopt_service.py b/python/cuopt_server/cuopt_server/cuopt_service.py index 15106bd293..8da26a39ad 100644 --- a/python/cuopt_server/cuopt_server/cuopt_service.py +++ b/python/cuopt_server/cuopt_server/cuopt_service.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 import argparse @@ -6,7 +6,7 @@ import os import signal import sys -from multiprocessing import Event, Process, Queue +from multiprocessing import get_context import psutil @@ -66,21 +66,23 @@ def watcher(app_exit, results_queue, job_queue, abort_queue, abort_list): except Exception: pass + ctx = get_context("fork") + # Flag for this process that says we have already run the # exit handler - terminated = Event() + terminated = ctx.Event() # Flag for all processes that the app is shutting down - app_exit = Event() + app_exit = ctx.Event() # Flag set by results thread when all jobs have been # marked done, to give a chance for anyone actively # waiting to get a graceful response - jobs_marked_done = Event() + jobs_marked_done = ctx.Event() - job_queue = Queue() - abort_queue = Queue() - results_queue = Queue() + job_queue = ctx.Queue() + abort_queue = ctx.Queue() + results_queue = ctx.Queue() w = None @@ -401,7 +403,7 @@ def record_factory(*args, **kwargs): from cuopt_server.webserver import run_server - w = Process( + w = ctx.Process( target=run_server, args=( app_exit, diff --git a/python/cuopt_server/cuopt_server/utils/process_handler.py b/python/cuopt_server/cuopt_server/utils/process_handler.py index 2a7d9e7969..d3d54d7009 100644 --- a/python/cuopt_server/cuopt_server/utils/process_handler.py +++ b/python/cuopt_server/cuopt_server/utils/process_handler.py @@ -1,10 +1,10 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 import logging import queue import time -from multiprocessing import Event, Process +from multiprocessing import get_context import psutil @@ -70,11 +70,12 @@ def terminate(job_queue, results_queue, abort_queue, signame): def create_process(app_exit, job_queue, results_queue, abort_list, gpu_id): global s_procs - complete = Event() + ctx = get_context("fork") + complete = ctx.Event() from cuopt_server.utils import solver - s = Process( + s = ctx.Process( target=solver.process_async_solve, args=( app_exit, diff --git a/python/cuopt_server/pyproject.toml b/python/cuopt_server/pyproject.toml index 0a39531b89..d24cfcbd77 100644 --- a/python/cuopt_server/pyproject.toml +++ b/python/cuopt_server/pyproject.toml @@ -26,7 +26,7 @@ dependencies = [ "fastapi", "jsonref==1.1.0", "msgpack-numpy==0.4.8", - "msgpack==1.1.0", + "msgpack==1.1.2", "numpy>=1.23.5,<3.0", "pandas>=2.0", "psutil>=6.0.0", @@ -38,13 +38,14 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] [project.optional-dependencies] test = [ "jsonref==1.1.0", "msgpack-numpy==0.4.8", - "msgpack==1.1.0", + "msgpack==1.1.2", "pexpect", "pytest-cov", "pytest<9.0", diff --git a/python/libcuopt/pyproject.toml b/python/libcuopt/pyproject.toml index fabd3da0df..2507971a0f 100644 --- a/python/libcuopt/pyproject.toml +++ b/python/libcuopt/pyproject.toml @@ -27,6 +27,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] dependencies = [ "cuopt-mps-parser==26.4.*,>=0.0.0a0", diff --git a/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py b/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py index 42a7490398..fb8918c11c 100644 --- a/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py +++ b/skills/cuopt-lp-milp-api-python/assets/mps_solver/model.py @@ -193,7 +193,7 @@ def compare_gaps( "--mip-gap", type=float, default=0.01, help="MIP gap tolerance" ) parser.add_argument( - "--compare", action="store_true", help="Compare 1% vs 0.1% gap" + "--compare", action="store_true", help="Compare 1%% vs 0.1%% gap" ) parser.add_argument( "--known-optimal", From 4907783b84c4323bfc8ad73efc701d6279320cbd Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 12 Mar 2026 09:02:37 -0700 Subject: [PATCH 179/225] fix reliability branching regression --- cpp/src/branch_and_bound/pseudo_costs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/branch_and_bound/pseudo_costs.cpp b/cpp/src/branch_and_bound/pseudo_costs.cpp index ffa499a61a..91b5d3deb0 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.cpp +++ b/cpp/src/branch_and_bound/pseudo_costs.cpp @@ -399,7 +399,7 @@ i_t reliable_variable_selection_core(mip_node_t* node_ptr, const int task_priority = rb_settings.task_priority; #pragma omp taskloop if (num_tasks > 1) priority(task_priority) num_tasks(num_tasks) \ - shared(score_mutex) + shared(score_mutex, strong_branching_lp_iter) for (i_t i = 0; i < num_candidates; ++i) { const i_t j = unreliable_list[i]; From 8cd14cc9d86d8a81d6eb017cc8270f6b99aa5575 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 12 Mar 2026 11:24:08 -0700 Subject: [PATCH 180/225] run gpuheur immediately --- benchmarks/linear_programming/cuopt/run_mip.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 10479a68d1..a190f65237 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -284,6 +284,7 @@ int run_single_file(std::string file_path, settings.bnb_work_unit_scale = 1; settings.gpu_heur_work_unit_scale = 0.4; settings.mip_scaling = false; + settings.gpu_heur_wait_for_exploration = false; cuopt::linear_programming::benchmark_info_t benchmark_info; settings.benchmark_info_ptr = &benchmark_info; auto start_run_solver = std::chrono::high_resolution_clock::now(); From 6e1be5b769daf004cd68c4e755cc81b2d17780d6 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 12 Mar 2026 12:27:39 -0700 Subject: [PATCH 181/225] fix crash in cut loop --- cpp/src/branch_and_bound/branch_and_bound.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index c8f8c995da..8c73c68861 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -2574,6 +2574,12 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut } #endif + if (toc(exploration_stats_.start_time) > settings_.time_limit) { + solver_status_ = mip_status_t::TIME_LIMIT; + set_final_solution(solution, root_objective_); + return solver_status_; + } + // Generate cuts and add them to the cut pool f_t cut_start_time = tic(); bool problem_feasible = cut_generation.generate_cuts(original_lp_, From bc937817fa4d538b120324750849d374edee8c05 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Thu, 12 Mar 2026 12:50:12 -0700 Subject: [PATCH 182/225] fix llm sloppiness --- cpp/src/branch_and_bound/branch_and_bound.cpp | 11 ++++++++--- cpp/src/dual_simplex/simplex_solver_settings.hpp | 1 + cpp/src/mip_heuristics/solver.cu | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 8c73c68861..4cdb1e7534 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -2329,9 +2329,14 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut if (settings_.deterministic) { work_unit_context_.deterministic = true; cuopt_assert(settings_.bnb_work_unit_scale > 0.0, "B&B work-unit scale must be positive"); - // Scale=0 during pre-exploration: root LP/cuts/SB don't advance the deterministic timeline. - // Restored to bnb_work_unit_scale when exploration begins. - work_unit_context_.work_unit_scale = 0.0; + if (settings_.gpu_heur_wait_for_exploration) { + // Scale=0 during pre-exploration: root LP/cuts/SB don't advance the deterministic timeline. + // GPU heuristics start after exploration, so both timelines begin at 0 together. + work_unit_context_.work_unit_scale = 0.0; + } else { + // GPU heuristics race with B&B pre-exploration, so B&B work must advance normally. + work_unit_context_.work_unit_scale = settings_.bnb_work_unit_scale; + } // Detach the scheduler during the serial root/cuts/SB phase. // record_work_sync_on_horizon still accumulates global_work_units_elapsed, diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 52d6e2fc9f..506c7623af 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -192,6 +192,7 @@ struct simplex_solver_settings_t { i_t mip_batch_pdlp_strong_branching{0}; // 0 if not using batch PDLP for strong branching, 1 if // using batch PDLP for strong branching f_t bnb_work_unit_scale{1.0}; + bool gpu_heur_wait_for_exploration{true}; diving_heuristics_settings_t diving_settings; // Settings for the diving heuristics diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 9f4ef03bb9..8b007e53bb 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -283,6 +283,8 @@ solution_t mip_solver_t::run_solver() ? 2 : context.settings.reduced_cost_strengthening; branch_and_bound_settings.bnb_work_unit_scale = solver_settings_.bnb_work_unit_scale; + branch_and_bound_settings.gpu_heur_wait_for_exploration = + solver_settings_.gpu_heur_wait_for_exploration; if (context.settings.num_cpu_threads < 0) { branch_and_bound_settings.num_threads = std::max(1, omp_get_max_threads() - 1); From e6b7457081f18c1ddff2f5dcd7526eb7875890ce Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 13 Mar 2026 04:23:47 -0700 Subject: [PATCH 183/225] fix heuristic solutions dropped when exploration doesnt get to start --- cpp/src/branch_and_bound/branch_and_bound.cpp | 116 ++++++++++-------- cpp/src/branch_and_bound/branch_and_bound.hpp | 4 + 2 files changed, 67 insertions(+), 53 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 4cdb1e7534..df23527d82 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -828,6 +828,40 @@ void branch_and_bound_t::set_solution_at_root(mip_solution_t } } +template +bool branch_and_bound_t::retire_queued_solution( + const queued_external_solution_t& queued_solution, f_t& out_obj, std::vector& out_crushed) +{ + f_t primal_err; + f_t bound_err; + i_t num_fractional; + + mutex_original_lp_.lock(); + crush_primal_solution( + original_problem_, original_lp_, queued_solution.solution, new_slacks_, out_crushed); + out_obj = compute_objective(original_lp_, out_crushed); + bool is_feasible = check_guess( + original_lp_, settings_, var_types_, out_crushed, primal_err, bound_err, num_fractional); + mutex_original_lp_.unlock(); + + if (is_feasible) { return true; } + + std::vector repaired_solution; + f_t repaired_obj; + bool repaired = repair_solution(edge_norms_, out_crushed, repaired_obj, repaired_solution); + if (repaired) { + out_crushed = std::move(repaired_solution); + out_obj = repaired_obj; + return true; + } + + CUOPT_DETERMINISM_LOG(settings_.log, + "Deterministic repair FAILED: wut=%.3f origin=%s\n", + queued_solution.work_timestamp, + cuopt::internals::mip_solution_origin_to_string(queued_solution.origin)); + return false; +} + template void branch_and_bound_t::set_final_solution(mip_solution_t& solution, f_t lower_bound) @@ -901,6 +935,34 @@ void branch_and_bound_t::set_final_solution(mip_solution_t& } } + // Drain any pending heuristic solutions that B&B never got to retire during exploration + // (e.g., root solve consumed the entire time limit before tree exploration started). + if (settings_.deterministic && !incumbent_.has_incumbent) { + const double current_work = work_unit_context_.current_work(); + mutex_heuristic_queue_.lock(); + std::vector pending; + pending.swap(heuristic_solution_queue_); + mutex_heuristic_queue_.unlock(); + + for (const auto& queued_solution : pending) { + if (queued_solution.work_timestamp > current_work) { continue; } + std::vector crushed_solution; + f_t obj; + bool is_feasible = retire_queued_solution(queued_solution, obj, crushed_solution); + + if (is_feasible && obj < upper_bound_) { + upper_bound_ = obj; + incumbent_.set_incumbent_solution(obj, crushed_solution); + settings_.log.printf( + "Late-retired heuristic incumbent: obj=%.6e wut=%.3f origin=%s\n", + compute_user_objective(original_lp_, obj), + queued_solution.work_timestamp, + cuopt::internals::mip_solution_origin_to_string(queued_solution.origin)); + emit_solution_callback_from_crushed(crushed_solution, obj, queued_solution.origin, -1.0); + } + } + } + if (upper_bound_ != inf) { assert(incumbent_.has_incumbent); uncrush_primal_solution(original_problem_, original_lp_, incumbent_.x, solution.x); @@ -4016,23 +4078,7 @@ void branch_and_bound_t::deterministic_sort_replay_events( for (const auto& queued_solution : due_solutions) { std::vector crushed_solution; f_t obj; - f_t primal_err; - f_t bound_err; - i_t num_fractional; - bool is_feasible = false; - mutex_original_lp_.lock(); - crush_primal_solution( - original_problem_, original_lp_, queued_solution.solution, new_slacks_, crushed_solution); - obj = compute_objective(original_lp_, crushed_solution); - is_feasible = check_guess(original_lp_, - settings_, - var_types_, - crushed_solution, - primal_err, - bound_err, - num_fractional); - mutex_original_lp_.unlock(); - + bool is_feasible = retire_queued_solution(queued_solution, obj, crushed_solution); if (is_feasible) { replay_solutions.push_back({{obj, std::move(crushed_solution), @@ -4042,42 +4088,6 @@ void branch_and_bound_t::deterministic_sort_replay_events( queued_solution.work_timestamp, queued_solution.origin}, search_strategy_t::BEST_FIRST}); - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic retirement accepted: wut=%.6f obj=%.16e origin=%s primal_err=%.6e " - "bound_err=%.6e fractional=%d\n", - queued_solution.work_timestamp, - obj, - cuopt::internals::mip_solution_origin_to_string(queued_solution.origin), - primal_err, - bound_err, - num_fractional); - continue; - } - - std::vector repaired_solution; - f_t repaired_obj; - bool success = - repair_solution(edge_norms_, crushed_solution, repaired_obj, repaired_solution); - if (success) { - settings_.log.printf( - "Deterministic repair success: wut=%.3f obj=%.16e origin=%s\n", - queued_solution.work_timestamp, - repaired_obj, - cuopt::internals::mip_solution_origin_to_string(queued_solution.origin)); - replay_solutions.push_back({{repaired_obj, - std::move(repaired_solution), - 0, - -1, - 0, - queued_solution.work_timestamp, - queued_solution.origin}, - search_strategy_t::BEST_FIRST}); - } else { - settings_.log.printf( - "Deterministic repair FAILED: wut=%.3f origin=%s\n", - queued_solution.work_timestamp, - cuopt::internals::mip_solution_origin_to_string(queued_solution.origin)); } } } diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index af008c5e10..b6f3f76511 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -465,6 +465,10 @@ class branch_and_bound_t { cuopt::internals::mip_solution_origin_t::UNKNOWN}; }; + bool retire_queued_solution(const queued_external_solution_t& queued_solution, + f_t& out_obj, + std::vector& out_crushed); + // Deterministic pending external solution queue. // External solutions stay raw until their retirement horizon, where they are // crushed, checked, and repaired immediately if needed. From 0e9427f6dba69d42e1d833e6dd9e38f6b7d735cc Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 13 Mar 2026 04:26:49 -0700 Subject: [PATCH 184/225] csv even if no incumbent found --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index a190f65237..9b95c42ccb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -325,7 +325,7 @@ int run_single_file(std::string file_path, << benchmark_info.last_improvement_after_recombination << "," << mip_gap << "," << is_optimal << "\n"; write_to_output_file(out_dir, base_filename, device, n_gpus, batch_id, ss.str()); - if (!out_dir.empty() && incumbent_tracker.size() > 0) { + if (!out_dir.empty()) { std::string mps_stem = base_filename.substr(0, base_filename.find(".mps")); std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); From 295fd7ed149f355a23e8bfba935f6cd56b75e836 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 13 Mar 2026 08:38:40 -0700 Subject: [PATCH 185/225] fix root PDLP bug --- cpp/src/branch_and_bound/branch_and_bound.cpp | 13 ++-- .../diversity/diversity_manager.cu | 33 +++++---- .../diversity/recombiners/recombiner.cuh | 2 +- .../mip_heuristics/feasibility_jump/fj_cpu.cu | 2 +- .../feasibility_pump/feasibility_pump.cu | 70 +++++++++---------- .../local_search/local_search.cu | 2 +- .../local_search/rounding/bounds_repair.cu | 2 +- .../mip_heuristics/relaxed_lp/relaxed_lp.cu | 61 +++++++++------- cpp/src/mip_heuristics/solve.cu | 2 +- cpp/src/mip_heuristics/solver.cu | 12 ++-- cpp/src/utilities/determinism_log.hpp | 12 ++-- cpp/src/utilities/work_limit_context.hpp | 2 +- 12 files changed, 111 insertions(+), 102 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index df23527d82..6ed41d791e 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -46,11 +46,11 @@ #include // uncomment to enable detailed detemrinism logs -// #undef CUOPT_DETERMINISM_LOG -// #define CUOPT_DETERMINISM_LOG(logger, ...) \ -// do { \ -// logger.printf(__VA_ARGS__); \ -// } while (0) +#undef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(logger, ...) \ + do { \ + logger.printf(__VA_ARGS__); \ + } while (0) namespace cuopt::linear_programming::dual_simplex { @@ -2442,7 +2442,8 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_relax_soln_.resize(original_lp_.num_rows, original_lp_.num_cols); - if (settings_.clique_cuts != 0 && clique_table_ == nullptr) { + // TODO: ensure clique tables work well w/ determinism + if (settings_.clique_cuts != 0 && clique_table_ == nullptr && !settings_.deterministic) { signal_extend_cliques_.store(false, std::memory_order_release); typename ::cuopt::linear_programming::mip_solver_settings_t::tolerances_t tolerances_for_clique{}; diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index c3600af256..f4ac43b467 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -21,7 +21,12 @@ #include #include -#include +// uncomment to enable detailed detemrinism logs +// #undef CUOPT_DETERMINISM_LOG +// #define CUOPT_DETERMINISM_LOG(...) \ +// do { \ +// CUOPT_LOG_INFO(__VA_ARGS__); \ +// } while (0) constexpr bool fj_only_run = false; @@ -90,7 +95,7 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::run_fj_alone(solution_t& solution) template void diversity_manager_t::run_fp_alone() { - CUOPT_DETERMINISM_LOG_INFO("Deterministic FP alone enter"); + CUOPT_DETERMINISM_LOG("Deterministic FP alone enter"); solution_t sol(population.best_feasible()); sol.handle_ptr->sync_stream(); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic FP alone input: hash=0x%x feasible=%d obj=%.16e excess=%.16e", sol.get_hash(), (int)sol.get_feasible(), @@ -363,7 +368,7 @@ void diversity_manager_t::run_fp_alone() sol.get_total_excess()); ls.run_fp(sol, timer, &population, diversity_config.n_fp_iterations); sol.handle_ptr->sync_stream(); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic FP alone output: hash=0x%x feasible=%d obj=%.16e excess=%.16e", sol.get_hash(), (int)sol.get_feasible(), @@ -374,7 +379,7 @@ void diversity_manager_t::run_fp_alone() } auto& best_sol = population.best_feasible(); best_sol.handle_ptr->sync_stream(); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic FP alone population best after: hash=0x%x feasible=%d obj=%.16e excess=%.16e", best_sol.get_hash(), (int)best_sol.get_feasible(), @@ -404,7 +409,7 @@ solution_t diversity_manager_t::run_solver() cuopt::scope_guard([&]() { stats.total_solve_time = timer.elapsed_time(); }); auto log_return_solution = [&](const char* reason, solution_t& sol) { sol.handle_ptr->sync_stream(); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic run_solver return: reason=%s hash=0x%x feasible=%d " "obj=%.16e excess=%.16e", reason, @@ -529,7 +534,7 @@ solution_t diversity_manager_t::run_solver() lp_settings.concurrent_halt = &global_concurrent_halt; lp_settings.work_context = &context.gpu_heur_loop; cuopt_assert(lp_settings.work_context != nullptr, "Missing deterministic work context"); - CUOPT_DETERMINISM_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "DM root LP config: dry_run=%d deterministic=%d work_limit=%.6f time_limit=%.6f", (int)diversity_config.dry_run, (int)timer.deterministic, @@ -549,16 +554,10 @@ solution_t diversity_manager_t::run_solver() pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; pdlp_settings.num_gpus = context.settings.num_gpus; pdlp_settings.presolver = presolver_t::None; - CUOPT_DETERMINISM_LOG_DEBUG( - "DM root LP config: dry_run=%d deterministic=%d lp_time_limit=%.6f iter_limit=%d", - (int)diversity_config.dry_run, - (int)timer.deterministic, - pdlp_settings.time_limit, - pdlp_settings.iteration_limit); timer_t lp_timer(lp_time_limit); return solve_lp_with_method(*problem_ptr, pdlp_settings, lp_timer); }(); - CUOPT_DETERMINISM_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "DM root LP result: status=%d iters=%d user_obj=%.12f primal_hash=0x%x", (int)lp_result.get_termination_status(), lp_result.get_additional_termination_information().number_of_steps_taken, @@ -649,7 +648,7 @@ solution_t diversity_manager_t::run_solver() // in case the pdlp returned var boudns that are out of bounds clamp_within_var_bounds(lp_optimal_solution, problem_ptr, problem_ptr->handle_ptr); - CUOPT_DETERMINISM_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "DM root LP post-clamp: lp_optimal_solution hash=0x%x", detail::compute_hash(lp_optimal_solution, problem_ptr->handle_ptr->get_stream())); } @@ -1008,7 +1007,7 @@ std::pair, bool> diversity_manager_t::recombine( } } } - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic recombiner selection: requested=%s selected_index=%d chosen=%s " "enabled_size=%zu last_choice_before=%d", recombiner_t::recombiner_name(recombiner_type), diff --git a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh index 8cc226dfb4..af5c0966ab 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh @@ -261,7 +261,7 @@ class recombiner_t { const char* enabled_3 = recombiner_t::enabled_recombiners.size() > 3 ? recombiner_name(recombiner_t::enabled_recombiners[3]) : "NONE"; - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic recombiner init: expensive_to_fix=%d n_continuous=%d " "max_continuous=%zu disable_fp_submip_expensive=%d " "disable_submip_continuous=%d disable_submip_deterministic=%d size=%zu " diff --git a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu index 6c4fb4e039..1aead97bc0 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/fj_cpu.cu @@ -1336,7 +1336,7 @@ std::unique_ptr> fj_t::create_cpu_climber( : read_positive_work_unit_scale("CUOPT_CPUFJ_WORK_UNIT_SCALE"); fj_cpu->work_unit_bias *= cpu_work_unit_scale; if (cpu_work_unit_scale != 1.0) { - CUOPT_DETERMINISM_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "CPUFJ using work-unit scale %f (bias=%f)", cpu_work_unit_scale, fj_cpu->work_unit_bias); } fj_cpu->settings = settings; diff --git a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu index 5689ea3226..3d3af61b2f 100644 --- a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu @@ -150,7 +150,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tn_variables, "Size mismatch"); cuopt_assert(h_last_projection.size() == solution.problem_ptr->n_variables, "Size mismatch"); cuopt_assert(h_variable_bounds.size() == solution.problem_ptr->n_variables, "Size mismatch"); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "FP proj inputs: assign_hash=0x%x last_proj_hash=0x%x integer_idx_hash=0x%x n_vars=%d n_int=%d", detail::compute_hash(h_assignment), detail::compute_hash(h_last_projection), @@ -205,7 +205,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_t::linear_project_onto_polytope(solution_t 0) { temp_p.insert_variables(h_variables); } if (h_constraints.n_constraints() > 0) { temp_p.insert_constraints(h_constraints); } @@ -235,7 +235,7 @@ bool feasibility_pump_t::linear_project_onto_polytope(solution_tget_stream()), @@ -537,18 +537,18 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s { raft::common::nvtx::range fun_scope("run_single_fp_descent"); i_t fp_iter = 0; - CUOPT_DETERMINISM_LOG_INFO("FP descent start: hash=0x%x feas=%d obj=%.12f timer_det=%d rem=%.6f", - solution.get_hash(), - (int)solution.get_feasible(), - solution.get_user_objective(), - (int)timer.deterministic, - timer.remaining_time()); + CUOPT_DETERMINISM_LOG("FP descent start: hash=0x%x feas=%d obj=%.12f timer_det=%d rem=%.6f", + solution.get_hash(), + (int)solution.get_feasible(), + solution.get_user_objective(), + (int)timer.deterministic, + timer.remaining_time()); // start by doing nearest rounding solution.round_nearest(); - CUOPT_DETERMINISM_LOG_INFO("FP descent after initial round: hash=0x%x feas=%d obj=%.12f", - solution.get_hash(), - (int)solution.get_feasible(), - solution.get_user_objective()); + CUOPT_DETERMINISM_LOG("FP descent after initial round: hash=0x%x feas=%d obj=%.12f", + solution.get_hash(), + (int)solution.get_feasible(), + solution.get_user_objective()); cuopt_assert(last_projection.size() == solution.assignment.size(), "Size mismatch"); // First projection in a descent has no previous projection history: initialize explicitly raft::copy(last_projection.data(), @@ -560,12 +560,12 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s solution.assignment.size(), solution.handle_ptr->get_stream()); while (true) { - CUOPT_DETERMINISM_LOG_INFO("FP iter %d pre-projection: hash=0x%x feas=%d obj=%.12f rem=%.6f", - fp_iter, - solution.get_hash(), - (int)solution.get_feasible(), - solution.get_user_objective(), - timer.remaining_time()); + CUOPT_DETERMINISM_LOG("FP iter %d pre-projection: hash=0x%x feas=%d obj=%.12f rem=%.6f", + fp_iter, + solution.get_hash(), + (int)solution.get_feasible(), + solution.get_user_objective(), + timer.remaining_time()); bool preempt = (context.diversity_manager_ptr != nullptr && context.diversity_manager_ptr->check_b_b_preemption()); if (preempt || timer.check_time_limit()) { @@ -583,7 +583,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s CUOPT_LOG_DEBUG("after fp projection n_integers %d total n_integes %d", n_integers, solution.problem_ptr->n_integer_vars); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "FP iter %d post-projection: hash=0x%x feasible_after_lp=%d obj=%.12f rem=%.6f lp_stage=%.6f", fp_iter, solution.get_hash(), @@ -591,12 +591,12 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s solution.get_user_objective(), remaining_after_projection, proj_begin - remaining_after_projection); - CUOPT_DETERMINISM_LOG_INFO("FP iter %d pre-round: hash=0x%x feas=%d obj=%.12f rem=%.6f", - fp_iter, - solution.get_hash(), - (int)is_feasible, - solution.get_user_objective(), - remaining_after_projection); + CUOPT_DETERMINISM_LOG("FP iter %d pre-round: hash=0x%x feas=%d obj=%.12f rem=%.6f", + fp_iter, + solution.get_hash(), + (int)is_feasible, + solution.get_user_objective(), + remaining_after_projection); bool is_cycle = true; // temp comment for presolve run if (config.check_distance_cycle) { @@ -664,7 +664,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s cuopt_func_call(solution.test_variable_bounds(true)); const f_t remaining_after_round = timer.remaining_time(); proj_and_round_time = proj_begin - remaining_after_round; - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "FP iter %d post-round: hash=0x%x feasible_after_round=%d obj=%.12f rem=%.6f " "round_stage=%.6f proj_round_total=%.6f", fp_iter, @@ -677,14 +677,14 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s if (!is_feasible) { const f_t time_ratio = 0.2; const f_t fj_budget = time_ratio * proj_and_round_time; - CUOPT_DETERMINISM_LOG_INFO("FP iter %d pre-fj-fallback: hash=0x%x rem=%.6f fj_budget=%.6f", - fp_iter, - solution.get_hash(), - remaining_after_round, - fj_budget); + CUOPT_DETERMINISM_LOG("FP iter %d pre-fj-fallback: hash=0x%x rem=%.6f fj_budget=%.6f", + fp_iter, + solution.get_hash(), + remaining_after_round, + fj_budget); is_feasible = test_fj_feasible(solution, fj_budget); const f_t remaining_after_fj = timer.remaining_time(); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "FP iter %d post-fj-fallback: hash=0x%x feasible_after_fj=%d obj=%.12f rem=%.6f " "fj_stage=%.6f", fp_iter, diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index 478fbed49a..54e1bcf134 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -57,7 +57,7 @@ local_search_t::local_search_t(mip_solver_context_t& context cpu_fj.fj_ptr = &fj; } scratch_cpu_fj_on_lp_opt.fj_ptr = &fj; - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic solve start local_search state: seed_state=%lld " "local_search_best_obj=%.16e pop_ptr_set=%d", (long long)cuopt::seed_generator::peek_seed(), diff --git a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu index 7ee552d252..51e607eb87 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu @@ -500,7 +500,7 @@ bool bounds_repair_t::repair_problem(problem_t& problem, if (timer.deterministic) { const double setup_work = estimate_bounds_repair_setup_work(problem); record_estimated_work(timer, &total_estimated_work, setup_work); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Repair entry: pb_hash=0x%x bounds_hash=0x%x violated_hash=0x%x n_violated=%d " "best_violation=%.6f timer_rem=%.6f total_work=%.6f setup_work=%.6f", problem.get_fingerprint(), diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index 13ec23bde3..62ae616698 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -23,6 +23,12 @@ #include +// #undef CUOPT_DETERMINISM_LOG +// #define CUOPT_DETERMINISM_LOG(...) \ +// do { \ +// CUOPT_LOG_INFO(__VA_ARGS__); \ +// } while (0) + namespace cuopt::linear_programming::detail { template @@ -90,18 +96,22 @@ optimization_problem_solution_t get_relaxed_lp_solution( if (determinism_mode) { // try to estimate the iteration count based on the requested work limit estim_iters = 100; - do { - // TODO: use an actual predictor model here - double estim_ms = 313 + 200 * op_problem.n_variables - 400 * op_problem.n_constraints + - 600 * op_problem.coefficients.size() + 7100 * estim_iters; - estim_ms = std::max(0.0, estim_ms); - if (estim_ms > work_limit * 1000) { break; } - estim_iters += 100; - } while (true); - CUOPT_LOG_DEBUG("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); + if (!std::isinf(work_limit)) { + do { + // TODO: use an actual predictor model here + double estim_ms = 313 + 200 * op_problem.n_variables - 400 * op_problem.n_constraints + + 600 * op_problem.coefficients.size() + 7100 * estim_iters; + estim_ms = std::max(0.0, estim_ms); + if (estim_ms > work_limit * 1000) { break; } + estim_iters += 100; + } while (true); + } else { + estim_iters = std::numeric_limits::max(); + } + CUOPT_LOG_INFO("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); pdlp_settings.iteration_limit = estim_iters; pdlp_settings.time_limit = std::numeric_limits::infinity(); - pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable3; + pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; pdlp_settings.presolver = presolver_t::None; } CUOPT_LOG_DEBUG( @@ -151,28 +161,27 @@ optimization_problem_solution_t get_relaxed_lp_solution( // temporarily add timer auto start_time = timer_t(pdlp_settings.time_limit); lp_solver.set_inside_mip(true); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "prev solution sizes primal=%lu dual=%lu", assignment.size(), lp_state.prev_dual.size()); auto solver_response = lp_solver.run_solver(start_time); - CUOPT_DETERMINISM_LOG_INFO("post LP primal size %lu", - solver_response.get_primal_solution().size()); + CUOPT_DETERMINISM_LOG("post LP primal size %lu", solver_response.get_primal_solution().size()); const int actual_iters = solver_response.get_additional_termination_information().number_of_steps_taken; - CUOPT_DETERMINISM_LOG_INFO("LP call %lu result: status=%d iters=%d primal_hash=0x%x", - lp_call_id, - (int)solver_response.get_termination_status(), - actual_iters, - solver_response.get_primal_solution().size() != 0 - ? detail::compute_hash(solver_response.get_primal_solution(), - op_problem.handle_ptr->get_stream()) - : 0u); + CUOPT_DETERMINISM_LOG("LP call %lu result: status=%d iters=%d primal_hash=0x%x", + lp_call_id, + (int)solver_response.get_termination_status(), + actual_iters, + solver_response.get_primal_solution().size() != 0 + ? detail::compute_hash(solver_response.get_primal_solution(), + op_problem.handle_ptr->get_stream()) + : 0u); if (determinism_mode && settings.work_context != nullptr) { double work_to_record = settings.work_limit; if (estim_iters > 0) { work_to_record = settings.work_limit * std::clamp((double)actual_iters / (double)estim_iters, 0.0, 1.0); } - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "LP call %lu recording %.6fwu (actual_iters=%d estim_iters=%d requested=%.6f)", lp_call_id, work_to_record, @@ -194,14 +203,14 @@ optimization_problem_solution_t get_relaxed_lp_solution( solver_response.get_primal_solution().size(), op_problem.handle_ptr->get_stream()); } - CUOPT_DETERMINISM_LOG_INFO("LP call %lu assignment_after_copy hash=0x%x", - lp_call_id, - detail::compute_hash(assignment, op_problem.handle_ptr->get_stream())); + CUOPT_DETERMINISM_LOG("LP call %lu assignment_after_copy hash=0x%x", + lp_call_id, + detail::compute_hash(assignment, op_problem.handle_ptr->get_stream())); if (solver_response.get_termination_status() == pdlp_termination_status_t::Optimal) { CUOPT_LOG_TRACE("feasible solution found with LP objective %f", solver_response.get_objective_value()); } else { - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "LP returned with reason %d, %d iterations", solver_response.get_termination_status(), solver_response.get_additional_termination_information().number_of_steps_taken); diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 21c57cd605..2b853499ff 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -228,7 +228,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, // Initialize seed generator if a specific seed is requested if (settings.seed >= 0) { cuopt::seed_generator::set_seed(settings.seed); } - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic solve start settings: seed=%lld seed_state=%lld det_mode=%d " "work_limit=%.6f max_cut_passes=%d num_cpu_threads=%d", (long long)settings.seed, diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 8b007e53bb..c2037758d5 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -19,8 +19,8 @@ #include #include -#undef CUOPT_DETERMINISM_LOG_INFO -#define CUOPT_DETERMINISM_LOG_INFO(...) CUOPT_LOG_INFO(__VA_ARGS__) +#undef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(...) CUOPT_LOG_INFO(__VA_ARGS__) #include #include @@ -401,7 +401,7 @@ solution_t mip_solver_t::run_solver() if (bb_status == dual_simplex::mip_status_t::INFEASIBLE) { sol.set_problem_fully_reduced(); } if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && std::isfinite(branch_and_bound_solution.objective)) { - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic solver B&B overwrite: bb_status=%d bb_obj=%.16e bb_lower=%.16e " "bb_x_size=%zu bb_has_incumbent=%d " "bb_hash=0x%x dm_hash=0x%x nodes=%d simplex_iterations=%d", @@ -420,7 +420,7 @@ solution_t mip_solver_t::run_solver() f_t max_cstr_vio = bb_sol.compute_max_constraint_violation(); f_t max_int_vio = bb_sol.compute_max_int_violation(); f_t max_bnd_vio = bb_sol.compute_max_variable_violation(); - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic B&B overwrite feasibility: feasible=%d obj=%.16e user_obj=%.16e " "max_cstr_vio=%.6e max_int_vio=%.6e max_bnd_vio=%.6e " "n_integers=%d n_int_vars=%d n_feas_cstr=%d n_cstr=%d hash=0x%x", @@ -451,7 +451,7 @@ solution_t mip_solver_t::run_solver() f_t lo_vio = std::max(0.0, (double)(h_clb[row] - ax)); f_t hi_vio = std::max(0.0, (double)(ax - h_cub[row])); if (lo_vio > 1e-6 || hi_vio > 1e-6) { - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( " Constraint %d violated: Ax=%.16e lb=%.16e ub=%.16e lo_vio=%.6e hi_vio=%.6e " "nnz=%d", row, @@ -466,7 +466,7 @@ solution_t mip_solver_t::run_solver() } sol = std::move(bb_sol); } else if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { - CUOPT_DETERMINISM_LOG_INFO( + CUOPT_DETERMINISM_LOG( "Deterministic B&B overwrite skipped: bb_obj=%.16e bb_obj_finite=%d bb_has_incumbent=%d", branch_and_bound_solution.objective, (int)std::isfinite(branch_and_bound_solution.objective), diff --git a/cpp/src/utilities/determinism_log.hpp b/cpp/src/utilities/determinism_log.hpp index d2e5acae48..5cad81c249 100644 --- a/cpp/src/utilities/determinism_log.hpp +++ b/cpp/src/utilities/determinism_log.hpp @@ -22,14 +22,14 @@ } while (0) #endif -#ifndef CUOPT_DETERMINISM_LOG_INFO -#define CUOPT_DETERMINISM_LOG_INFO(...) \ - do { \ +#ifndef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(...) \ + do { \ } while (0) #endif -#ifndef CUOPT_DETERMINISM_LOG_DEBUG -#define CUOPT_DETERMINISM_LOG_DEBUG(...) \ - do { \ +#ifndef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(...) \ + do { \ } while (0) #endif diff --git a/cpp/src/utilities/work_limit_context.hpp b/cpp/src/utilities/work_limit_context.hpp index 513f7dd176..0c8435c77a 100644 --- a/cpp/src/utilities/work_limit_context.hpp +++ b/cpp/src/utilities/work_limit_context.hpp @@ -137,7 +137,7 @@ struct work_limit_context_t { producer_sync = producer_sync_; producer_work_units_elapsed->store(current_producer_work(), std::memory_order_release); if (work_unit_scale != 1.0) { - CUOPT_DETERMINISM_LOG_DEBUG("[%s] Using work-unit scale %f", name.c_str(), work_unit_scale); + CUOPT_DETERMINISM_LOG("[%s] Using work-unit scale %f", name.c_str(), work_unit_scale); } } From 92ccbe9be6072eb443185367d7f08d65b2786947 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 13 Mar 2026 09:00:40 -0700 Subject: [PATCH 186/225] fix build --- cpp/src/mip_heuristics/diversity/diversity_manager.cu | 6 +++--- cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index f4ac43b467..631df91e37 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -23,9 +23,9 @@ // uncomment to enable detailed detemrinism logs // #undef CUOPT_DETERMINISM_LOG -// #define CUOPT_DETERMINISM_LOG(...) \ -// do { \ -// CUOPT_LOG_INFO(__VA_ARGS__); \ +// #define CUOPT_DETERMINISM_LOG(...) +// do { +// CUOPT_LOG_INFO(__VA_ARGS__); // } while (0) constexpr bool fj_only_run = false; diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index 62ae616698..713128fe38 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -24,9 +24,9 @@ #include // #undef CUOPT_DETERMINISM_LOG -// #define CUOPT_DETERMINISM_LOG(...) \ -// do { \ -// CUOPT_LOG_INFO(__VA_ARGS__); \ +// #define CUOPT_DETERMINISM_LOG(...) +// do { +// CUOPT_LOG_INFO(__VA_ARGS__); // } while (0) namespace cuopt::linear_programming::detail { From 59e0ad0e605d87c070bd0a5bd650c72d5c3d49f4 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 13 Mar 2026 10:05:54 -0700 Subject: [PATCH 187/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 9b95c42ccb..8d18dfceac 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From 0a47fe0942394cdb4f717a7d3e21f4a6ea44f724 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 13 Mar 2026 10:11:01 -0700 Subject: [PATCH 188/225] bump2 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 8d18dfceac..9b95c42ccb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From eaf45e63acdff38d6bacf9216884265aa04abeb7 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 13 Mar 2026 14:27:18 -0700 Subject: [PATCH 189/225] fix reliability branching nondeterminism --- cpp/src/branch_and_bound/branch_and_bound.cpp | 17 +++++++++-------- .../branch_and_bound/deterministic_workers.hpp | 3 +++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 6ed41d791e..4337ca9604 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -46,11 +46,11 @@ #include // uncomment to enable detailed detemrinism logs -#undef CUOPT_DETERMINISM_LOG -#define CUOPT_DETERMINISM_LOG(logger, ...) \ - do { \ - logger.printf(__VA_ARGS__); \ - } while (0) +// #undef CUOPT_DETERMINISM_LOG +// #define CUOPT_DETERMINISM_LOG(logger, ...) \ +// do { \ +// logger.printf(__VA_ARGS__); \ +// } while (0) namespace cuopt::linear_programming::dual_simplex { @@ -1329,7 +1329,7 @@ struct deterministic_bfs_policy_t { i_t var; if (this->bnb.settings_.reliability_branching != 0 && - this->bnb.exploration_stats_.nodes_explored > 0) { + this->worker.nodes_explored_snapshot > 0) { var = reliable_variable_selection_core(node, fractional, x, @@ -1347,8 +1347,8 @@ struct deterministic_bfs_policy_t this->worker.pc_snapshot.n_vars(), this->worker.pc_snapshot.strong_branching_lp_iter_, this->worker.local_upper_bound, - this->bnb.exploration_stats_.total_lp_iters, - this->bnb.exploration_stats_.nodes_explored, + (int64_t)this->worker.total_lp_iters_snapshot, + (int64_t)this->worker.nodes_explored_snapshot, this->bnb.exploration_stats_.start_time, this->bnb.pc_.reliability_branching_settings, 1, @@ -4027,6 +4027,7 @@ void branch_and_bound_t::deterministic_broadcast_snapshots( deterministic_snapshot_t snap; snap.upper_bound = upper_bound_.load(); snap.total_lp_iters = exploration_stats_.total_lp_iters.load(); + snap.nodes_explored = exploration_stats_.nodes_explored.load(); snap.incumbent = incumbent_snapshot; snap.pc_snapshot = pc_.create_snapshot(); diff --git a/cpp/src/branch_and_bound/deterministic_workers.hpp b/cpp/src/branch_and_bound/deterministic_workers.hpp index 2e94c13a3a..f6f9a1bd51 100644 --- a/cpp/src/branch_and_bound/deterministic_workers.hpp +++ b/cpp/src/branch_and_bound/deterministic_workers.hpp @@ -62,6 +62,7 @@ struct deterministic_snapshot_t { pseudo_cost_snapshot_t pc_snapshot; std::vector incumbent; i_t total_lp_iters; + i_t nodes_explored; }; template @@ -78,6 +79,7 @@ class deterministic_worker_base_t : public branch_and_bound_worker_t { // Diving-specific snapshots (ignored by BFS workers) std::vector incumbent_snapshot; i_t total_lp_iters_snapshot{0}; + i_t nodes_explored_snapshot{0}; std::vector> integer_solutions; int next_solution_seq{0}; @@ -104,6 +106,7 @@ class deterministic_worker_base_t : public branch_and_bound_worker_t { pc_snapshot = snap.pc_snapshot; incumbent_snapshot = snap.incumbent; total_lp_iters_snapshot = snap.total_lp_iters; + nodes_explored_snapshot = snap.nodes_explored; } bool has_work() const { return static_cast(this)->has_work_impl(); } From 02a5bf849fe57ca3686949d236e2af622b789fe6 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 13 Mar 2026 14:28:03 -0700 Subject: [PATCH 190/225] bump 1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 9b95c42ccb..8d18dfceac 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From fff6a8e4d79d3e8b41a5df55d718fcc3c3ba3cfd Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Fri, 13 Mar 2026 14:28:59 -0700 Subject: [PATCH 191/225] bump 2 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 8d18dfceac..9b95c42ccb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From d915f6a9c1594e73bb70b22a73a987cb4bd85358 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 14 Mar 2026 03:15:04 -0700 Subject: [PATCH 192/225] fix tests and cpufj local search --- .../cuopt/linear_programming/constants.h | 1 + .../diversity/diversity_manager.cu | 11 +++++--- .../local_search/local_search.cu | 8 +++--- cpp/tests/mip/determinism_test.cu | 8 +++--- cpp/tests/mip/local_search_test.cu | 28 +++++++++++-------- 5 files changed, 33 insertions(+), 23 deletions(-) diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index 1c48b6c285..fd941fe098 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -86,6 +86,7 @@ /* Backward compatibility aliases */ #define CUOPT_MODE_OPPORTUNISTIC CUOPT_DETERMINISM_NONE #define CUOPT_MODE_DETERMINISTIC CUOPT_DETERMINISM_FULL +#define CUOPT_MODE_DETERMINISTIC_BB CUOPT_DETERMINISM_BB #define CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS CUOPT_DETERMINISM_GPU_HEURISTICS /* @brief LP/MIP termination status constants */ diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 631df91e37..c8fb10781a 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -400,9 +400,10 @@ solution_t diversity_manager_t::run_solver() { raft::common::nvtx::range fun_scope("run_solver"); - CUOPT_LOG_DEBUG( - "Determinism mode: %s", - (context.settings.determinism_mode & CUOPT_DETERMINISM_BB) ? "deterministic" : "opportunistic"); + CUOPT_LOG_DEBUG("Determinism mode: %s", + (context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS) + ? "deterministic" + : "opportunistic"); // to automatically compute the solving time on scope exit auto timer_raii_guard = @@ -419,9 +420,11 @@ solution_t diversity_manager_t::run_solver() sol.get_total_excess()); }; + bool bb_only = (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC_BB); // Debug: Allow disabling GPU heuristics to test B&B tree determinism in isolation const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); - if (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1") { + if (bb_only || + (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1")) { CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && context.branch_and_bound_ptr != nullptr) { diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index 54e1bcf134..aa0994a6f8 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -275,10 +275,10 @@ bool local_search_t::do_fj_solve(solution_t& solution, in_fj.settings.time_limit = timer.remaining_time(); in_fj.solve(solution); - // Stop CPU solver (in opportunistic mode this halts immediately; - // in deterministic mode CPUFJ self-halts via work_budget) - for (auto& cpu_fj : ls_cpu_fj) { - cpu_fj.stop_cpu_solver(); + if (!deterministic) { + for (auto& cpu_fj : ls_cpu_fj) { + cpu_fj.stop_cpu_solver(); + } } auto gpu_fj_end = std::chrono::high_resolution_clock::now(); diff --git a/cpp/tests/mip/determinism_test.cu b/cpp/tests/mip/determinism_test.cu index c013b7a0d9..b2185189ef 100644 --- a/cpp/tests/mip/determinism_test.cu +++ b/cpp/tests/mip/determinism_test.cu @@ -203,7 +203,7 @@ TEST_F(DeterministicBBTest, reproducible_objective) mip_solver_settings_t settings; settings.time_limit = 60.0; - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_BB; settings.num_cpu_threads = 8; settings.work_limit = 4; @@ -235,7 +235,7 @@ TEST_F(DeterministicBBTest, reproducible_infeasibility) mip_solver_settings_t settings; settings.time_limit = 60.0; - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_BB; settings.num_cpu_threads = 8; settings.work_limit = 100; // High enough to fully explore @@ -267,7 +267,7 @@ TEST_F(DeterministicBBTest, reproducible_high_contention) mip_solver_settings_t settings; settings.time_limit = 60.0; - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_BB; settings.num_cpu_threads = 128; // High thread count to stress contention settings.work_limit = 1; @@ -302,7 +302,7 @@ TEST_F(DeterministicBBTest, reproducible_solution_vector) mip_solver_settings_t settings; settings.time_limit = 60.0; - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_BB; settings.num_cpu_threads = 8; settings.work_limit = 2; diff --git a/cpp/tests/mip/local_search_test.cu b/cpp/tests/mip/local_search_test.cu index 18a99a4233..b5ef19d58a 100644 --- a/cpp/tests/mip/local_search_test.cu +++ b/cpp/tests/mip/local_search_test.cu @@ -90,7 +90,11 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) init_handler(op_problem.get_handle_ptr()); // run the problem constructor of MIP, so that we do bounds standardization - detail::problem_t problem(op_problem); + auto settings = mip_solver_settings_t{}; + settings.time_limit = 120.; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; + + detail::problem_t problem(op_problem, settings.get_tolerances(), true); problem.preprocess_problem(); setup_device_symbols(op_problem.get_handle_ptr()->get_stream()); @@ -106,11 +110,8 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) nullptr, hyper_params, true); - - auto settings = mip_solver_settings_t{}; - settings.time_limit = 30.; - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; - auto timer = cuopt::termination_checker_t(30.0, cuopt::termination_checker_t::root_tag_t{}); + auto timer = + cuopt::termination_checker_t(settings.time_limit, cuopt::termination_checker_t::root_tag_t{}); detail::mip_solver_t solver(problem, settings, scaling, timer); problem.tolerances = settings.get_tolerances(); @@ -148,7 +149,8 @@ static uint32_t run_fp(std::string test_instance, local_search_mode_t mode) printf("running mode: %d\n", mode); work_limit_context_t work_limit_context("LocalSearch"); - local_search.fp.timer = work_limit_timer_t(work_limit_context, 6000, timer); + work_limit_context.deterministic = true; + local_search.fp.timer = work_limit_timer_t(work_limit_context, 10, timer); detail::ls_config_t ls_config{}; @@ -208,7 +210,10 @@ class LocalSearchTestParams : public testing::TestWithParam Date: Sat, 14 Mar 2026 03:15:26 -0700 Subject: [PATCH 193/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 9b95c42ccb..8d18dfceac 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From 3a7b43fa089c9badacdf7651aeb98c7884992a40 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 14 Mar 2026 03:15:42 -0700 Subject: [PATCH 194/225] bump2 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 8d18dfceac..9b95c42ccb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From 6494275d7a4fbccc9b62cf14d5afee585d2e1015 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 14 Mar 2026 08:03:55 -0700 Subject: [PATCH 195/225] bugfixes and renaming --- cpp/src/branch_and_bound/branch_and_bound.cpp | 2 +- .../diversity/diversity_manager.cu | 21 ++++++++++++------- .../diversity/recombiners/fp_recombiner.cuh | 2 +- .../diversity/recombiners/recombiner.cuh | 2 +- .../feasibility_jump/feasibility_jump.cu | 7 ++++--- .../local_search/local_search.cu | 6 +++--- .../local_search/rounding/constraint_prop.cu | 4 +++- .../presolve/bounds_presolve.cu | 2 +- .../mip_heuristics/presolve/probing_cache.cu | 2 +- cpp/src/mip_heuristics/solve.cu | 2 +- cpp/src/mip_heuristics/solver.cu | 3 ++- cpp/src/mip_heuristics/solver_context.cuh | 2 +- cpp/tests/mip/determinism_test.cu | 6 +++--- 13 files changed, 35 insertions(+), 26 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 4337ca9604..d1ffbcec9b 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -655,7 +655,7 @@ void branch_and_bound_t::queue_external_solution_deterministic( mutex_original_lp_.unlock(); mutex_heuristic_queue_.lock(); - heuristic_solution_queue_.push_back({solution, user_objective, bnb_work_total, origin}); + heuristic_solution_queue_.push_back({solution, user_objective, work_unit_ts, origin}); const size_t heuristic_queue_size = heuristic_solution_queue_.size(); mutex_heuristic_queue_.unlock(); CUOPT_DETERMINISM_LOG( diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index c8fb10781a..1646779603 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -94,6 +94,8 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::last_lm_config = 0; + mab_ls_config_t::last_ls_mab_option = 0; CUOPT_DETERMINISM_LOG( "Deterministic solve start diversity state: seed_state=%lld fp_max=%zu " @@ -136,7 +138,8 @@ diversity_manager_t::diversity_manager_t(mip_solver_context_t::run_presolve(f_t time_limit, const bool remap_cache_ids = true; if (!global_timer.check_time_limit()) { trivial_presolve(*problem_ptr, remap_cache_ids); } if (!problem_ptr->empty && !check_bounds_sanity(*problem_ptr)) { return false; } - const bool run_clique_table = !presolve_timer.check_time_limit() && - !context.settings.heuristics_only && !problem_ptr->empty && - !(context.settings.determinism_mode & CUOPT_DETERMINISM_BB); + const bool run_clique_table = + !presolve_timer.check_time_limit() && !context.settings.heuristics_only && + !problem_ptr->empty && !(context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS); // if (run_clique_table) { // f_t time_limit_for_clique_table = std::min(3., presolve_timer.remaining_time() / 5); // timer_t clique_timer(time_limit_for_clique_table); @@ -500,7 +503,8 @@ solution_t diversity_manager_t::run_solver() // Run CPUFJ early to find quick initial solutions ls_cpufj_raii_guard_t ls_cpufj_raii_guard(ls); // RAII to stop cpufj threads on solve stop - if (!diversity_config.dry_run && !(context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { + if (!diversity_config.dry_run && + !(context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS)) { ls.start_cpufj_scratch_threads(population); } @@ -671,7 +675,8 @@ solution_t diversity_manager_t::run_solver() lp_rounded_sol.get_user_objective()); population.add_solution(std::move(lp_rounded_sol), internals::mip_solution_origin_t::LP_ROUNDING); - if (!diversity_config.dry_run && !(context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { + if (!diversity_config.dry_run && + !(context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS)) { ls.start_cpufj_lptopt_scratch_threads(population); } } @@ -708,7 +713,7 @@ solution_t diversity_manager_t::run_solver() log_return_solution("fj_only_run", sol); return sol; } - if (!(context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { rins.enable(); } + if (!(context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS)) { rins.enable(); } generate_solution(timer.remaining_time(), false); if (diversity_config.initial_solution_only) { @@ -882,7 +887,7 @@ diversity_manager_t::recombine_and_local_search(solution_t& sol1.get_feasible(), sol2.get_quality(population.weights), sol2.get_feasible()); - bool deterministic = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB); + bool deterministic = (context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS); double best_objective_of_parents = std::min(sol1.get_objective(), sol2.get_objective()); bool at_least_one_parent_feasible = sol1.get_feasible() || sol2.get_feasible(); // randomly choose among 3 recombiners diff --git a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh index abb1811e44..786a3e8798 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/fp_recombiner.cuh @@ -88,7 +88,7 @@ class fp_recombiner_t : public recombiner_t { CUOPT_DETERMINISM_LOG("FP rec: running LP with infeasibility detection"); relaxed_lp_settings_t lp_settings; lp_settings.time_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; - if (this->context.settings.determinism_mode & CUOPT_DETERMINISM_BB) { + if (this->context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS) { lp_settings.time_limit = std::numeric_limits::max(); // TODO should be global time limit lp_settings.work_limit = fp_recombiner_config_t::infeasibility_detection_time_limit; diff --git a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh index af5c0966ab..11a9af1081 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh @@ -232,7 +232,7 @@ class recombiner_t { const bool disable_submip_for_continuous_limit = n_continuous_vars > (i_t)sub_mip_recombiner_config_t::max_continuous_vars; const bool disable_submip_for_determinism = - (context.settings.determinism_mode & CUOPT_DETERMINISM_BB) != 0; + (context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS) != 0; for (auto recombiner : recombiner_types) { enabled_recombiners.insert(recombiner); } diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu index d41ebdfbb4..33ff207260 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump.cu @@ -869,7 +869,7 @@ void fj_t::refresh_lhs_and_violation(const rmm::cuda_stream_view& stre thrust::plus()); data.violation_score.set_value_async(violation, stream); data.weighted_violation_score.set_value_async(weighted_violation, stream); - if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS)) { data.violated_constraints.sort(stream); } #if FJ_SINGLE_STEP @@ -1293,7 +1293,7 @@ template i_t fj_t::solve(solution_t& solution) { raft::common::nvtx::range scope("fj_solve"); - bool deterministic = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB); + bool deterministic = (context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS); if (deterministic) { settings.time_limit = std::max((f_t)0.0, settings.time_limit); settings.work_limit = settings.time_limit; @@ -1429,7 +1429,8 @@ i_t fj_t::solve(solution_t& solution) // Compute the work unit corresponding to the number of iterations elapsed // by incrementally guessing work units until the model predicts >= actual iterations // TODO: awfully ugly, change - if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && iterations > 0) { + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS) && + iterations > 0) { double guessed_work = 0.0; const double work_increment = 0.1; const double max_work = settings.work_limit * 2.0; // Safety limit diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index aa0994a6f8..252736f3cc 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -68,7 +68,7 @@ local_search_t::local_search_t(mip_solver_context_t& context template void local_search_t::start_cpufj_scratch_threads(population_t& population) { - cuopt_assert(!(context.settings.determinism_mode & CUOPT_DETERMINISM_BB), + cuopt_assert(!(context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS), "Scratch CPUFJ must remain opportunistic-only"); pop_ptr = &population; @@ -119,7 +119,7 @@ template void local_search_t::start_cpufj_lptopt_scratch_threads( population_t& population) { - cuopt_assert(!(context.settings.determinism_mode & CUOPT_DETERMINISM_BB), + cuopt_assert(!(context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS), "LP-opt CPUFJ scratch must remain opportunistic-only"); pop_ptr = &population; @@ -223,7 +223,7 @@ bool local_search_t::do_fj_solve(solution_t& solution, const std::string& source) { if (time_limit == 0.) return solution.get_feasible(); - const bool deterministic = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB); + const bool deterministic = (context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS); work_limit_timer_t timer(context.gpu_heur_loop, time_limit, *context.termination); const auto old_n_cstr_weights = in_fj.cstr_weights.size(); diff --git a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu index 48fd858a0f..4263df9c5b 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu @@ -772,7 +772,9 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob i_t n_of_repairs_needed_for_feasible = 0; // TODO: do this better i_t iter_limit = std::numeric_limits::max(); - if ((this->context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { iter_limit = 100; } + if ((this->context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS)) { + iter_limit = 100; + } do { n_of_repairs_needed_for_feasible++; if (timer.check_time_limit() || iter_limit-- <= 0) { diff --git a/cpp/src/mip_heuristics/presolve/bounds_presolve.cu b/cpp/src/mip_heuristics/presolve/bounds_presolve.cu index 1e853436b5..de6ae8c51a 100644 --- a/cpp/src/mip_heuristics/presolve/bounds_presolve.cu +++ b/cpp/src/mip_heuristics/presolve/bounds_presolve.cu @@ -172,7 +172,7 @@ termination_criterion_t bound_presolve_t::bound_update_loop(problem_t< termination_criterion_t criteria = termination_criterion_t::ITERATION_LIMIT; // CHANGE once we have a work predictor - if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS)) { timer = timer_t(std::numeric_limits::infinity()); settings.iteration_limit = std::min(settings.iteration_limit, 50); } diff --git a/cpp/src/mip_heuristics/presolve/probing_cache.cu b/cpp/src/mip_heuristics/presolve/probing_cache.cu index e6913f2393..de2a63d918 100644 --- a/cpp/src/mip_heuristics/presolve/probing_cache.cu +++ b/cpp/src/mip_heuristics/presolve/probing_cache.cu @@ -858,7 +858,7 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, bound_presolve.settings.time_limit = timer.remaining_time(); // TODO: proper work unit accounting in deterministic mode for the probing cache - if ((bound_presolve.context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { + if ((bound_presolve.context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS)) { bound_presolve.settings.iteration_limit = 1; priority_indices.resize(std::min(priority_indices.size(), 2048)); } diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 2b853499ff..736ef9466d 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -261,7 +261,7 @@ mip_solution_t solve_mip(optimization_problem_t& op_problem, auto timer = cuopt::termination_checker_t(time_limit, cuopt::termination_checker_t::root_tag_t{}); - const bool deterministic_run = (settings.determinism_mode & CUOPT_DETERMINISM_BB); + const bool deterministic_run = (settings.determinism_mode != CUOPT_DETERMINISM_NONE); double presolve_time = 0.0; std::unique_ptr> presolver; diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index c2037758d5..754e955df8 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -146,7 +146,8 @@ solution_t mip_solver_t::run_solver() context.problem_ptr->post_process_solution(sol); return sol; } - const bool deterministic_run = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB); + const bool deterministic_run = + (context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS); const f_t gpu_heur_work_limit = deterministic_run ? context.settings.work_limit : timer_.get_time_limit(); if (deterministic_run) diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index 90312f7a4a..46c558e49b 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -192,7 +192,7 @@ struct mip_solver_context_t { cuopt_assert(problem_ptr != nullptr, "problem_ptr is nullptr"); stats.set_solution_bound(problem_ptr->maximize ? std::numeric_limits::infinity() : -std::numeric_limits::infinity()); - gpu_heur_loop.deterministic = (settings.determinism_mode & CUOPT_DETERMINISM_BB); + gpu_heur_loop.deterministic = (settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS); cuopt_assert(settings.cpufj_work_unit_scale > 0.0, "CPUFJ work-unit scale must be positive"); cuopt_assert(settings.gpu_heur_work_unit_scale > 0.0, "GPU heuristic work-unit scale must be positive"); diff --git a/cpp/tests/mip/determinism_test.cu b/cpp/tests/mip/determinism_test.cu index b2185189ef..0623cbf8f2 100644 --- a/cpp/tests/mip/determinism_test.cu +++ b/cpp/tests/mip/determinism_test.cu @@ -483,7 +483,7 @@ TEST_P(DeterministicGpuHeuristicsInstanceTest, reproducible_with_gpu_heuristics) mip_solver_settings_t settings; settings.time_limit = 60.0; - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; settings.num_cpu_threads = 8; settings.work_limit = 30; @@ -519,7 +519,7 @@ TEST_F(DeterministicBBTest, reproducible_with_gpu_heuristics_50v10_no_cuts) mip_solver_settings_t settings; settings.time_limit = 60.0; - settings.determinism_mode = CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS; + settings.determinism_mode = CUOPT_MODE_DETERMINISTIC; settings.num_cpu_threads = 8; settings.work_limit = 30; // settings.max_cut_passes = 0; @@ -572,7 +572,7 @@ class DeterministicBBInstanceTest TEST_P(DeterministicBBInstanceTest, deterministic_across_runs) { - scoped_env_var_t gpu_fj_work_scale("CUOPT_GPU_HEUR_WORK_UNIT_SCALE", "0.1"); + // scoped_env_var_t gpu_fj_work_scale("CUOPT_GPU_HEUR_WORK_UNIT_SCALE", "0.1"); auto [instance_path, num_threads, time_limit, work_limit] = GetParam(); auto path = make_path_absolute(instance_path); auto problem = mps_parser::parse_mps(path, false); From 588c77fb3270ad604699429fb5882d027040c814 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 14 Mar 2026 08:04:25 -0700 Subject: [PATCH 196/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 9b95c42ccb..8d18dfceac 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From 7613978ff846db09159e821814f91dd215354875 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 14 Mar 2026 08:04:43 -0700 Subject: [PATCH 197/225] bump2 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 8d18dfceac..9b95c42ccb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From d423c6ff22d9e0ad3edf35a494bd6592c10fba07 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 14 Mar 2026 11:00:36 -0700 Subject: [PATCH 198/225] bugfix --- cpp/src/branch_and_bound/branch_and_bound.cpp | 20 ++++++++++++------- cpp/src/branch_and_bound/branch_and_bound.hpp | 4 ++-- .../mip_heuristics/diversity/population.cu | 3 ++- cpp/src/mip_heuristics/solver.cu | 6 +++--- cpp/src/mip_heuristics/solver_context.cuh | 8 ++------ 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index d1ffbcec9b..948a63fc8e 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -493,6 +493,7 @@ void branch_and_bound_t::emit_solution_callback( cuopt::internals::mip_solution_origin_t origin, double work_timestamp) { + cuopt_assert(work_timestamp >= 0.0, "work_timestamp must not be negative"); const size_t callback_count = (settings_.solution_callback != nullptr ? 1UL : 0UL) + (settings_.solution_callback_ext != nullptr ? 1UL : 0UL); settings_.log.debug("Publishing incumbent: obj=%g wut=%.6f origin=%s callbacks=%zu\n", @@ -655,7 +656,7 @@ void branch_and_bound_t::queue_external_solution_deterministic( mutex_original_lp_.unlock(); mutex_heuristic_queue_.lock(); - heuristic_solution_queue_.push_back({solution, user_objective, work_unit_ts, origin}); + heuristic_solution_queue_.push_back({solution, user_objective, bnb_work_total, origin}); const size_t heuristic_queue_size = heuristic_solution_queue_.size(); mutex_heuristic_queue_.unlock(); CUOPT_DETERMINISM_LOG( @@ -781,8 +782,10 @@ void branch_and_bound_t::repair_heuristic_solutions() detail::compute_hash(repaired_solution)); report_heuristic(repaired_obj, queued_solution.work_timestamp); - emit_solution_callback_from_crushed( - repaired_solution, repaired_obj, queued_solution.origin, -1.0); + emit_solution_callback_from_crushed(repaired_solution, + repaired_obj, + queued_solution.origin, + queued_solution.work_timestamp); } mutex_upper_.unlock(); @@ -821,8 +824,10 @@ void branch_and_bound_t::set_solution_at_root(mip_solution_t compute_user_objective(original_lp_, root_objective_), toc(exploration_stats_.start_time)); - emit_solution_callback( - solution.x, solution.objective, cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE); + emit_solution_callback(solution.x, + solution.objective, + cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE, + work_unit_context_.current_work()); if (settings_.heuristic_preemption_callback != nullptr) { settings_.heuristic_preemption_callback(); } @@ -958,7 +963,8 @@ void branch_and_bound_t::set_final_solution(mip_solution_t& compute_user_objective(original_lp_, obj), queued_solution.work_timestamp, cuopt::internals::mip_solution_origin_to_string(queued_solution.origin)); - emit_solution_callback_from_crushed(crushed_solution, obj, queued_solution.origin, -1.0); + emit_solution_callback_from_crushed( + crushed_solution, obj, queued_solution.origin, queued_solution.work_timestamp); } } } @@ -1020,7 +1026,7 @@ void branch_and_bound_t::add_feasible_solution(f_t leaf_objective, incumbent_.x, upper_bound_, cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE, - -1.0); + work_unit_context_.current_work()); } mutex_upper_.unlock(); } diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index b6f3f76511..eefd6c8187 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -302,11 +302,11 @@ class branch_and_bound_t { void emit_solution_callback(std::vector& original_x, f_t objective, cuopt::internals::mip_solution_origin_t origin, - double work_timestamp = -1.0); + double work_timestamp); void emit_solution_callback_from_crushed(const std::vector& crushed_solution, f_t objective, cuopt::internals::mip_solution_origin_t origin, - double work_timestamp = -1.0); + double work_timestamp); // Set the solution when found at the root node void set_solution_at_root(mip_solution_t& solution, diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index 01a14680b1..adc667d1e8 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -346,6 +346,7 @@ void population_t::run_solution_callbacks( if (problem_ptr->branch_and_bound_callback != nullptr) { problem_ptr->branch_and_bound_callback(sol.get_host_assignment()); } + const double work_timestamp = context.gpu_heur_loop.current_work(); const auto payload = make_solution_callback_payload_from_solution(problem_ptr, context.settings, @@ -353,7 +354,7 @@ void population_t::run_solution_callbacks( context.gpu_heur_loop, sol, callback_origin, - -1.0); + work_timestamp); const bool published = context.solution_publication.publish_new_best_feasible(payload, timer.elapsed_time()); cuopt_assert(published, "New best feasible solution should publish to GET callbacks"); diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 754e955df8..1b01db8e1a 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -141,7 +141,7 @@ solution_t mip_solver_t::run_solver() context.gpu_heur_loop, sol, internals::mip_solution_origin_t::UNKNOWN, - -1.0); + 0.0); context.solution_publication.invoke_get_solution_callbacks(payload); context.problem_ptr->post_process_solution(sol); return sol; @@ -179,7 +179,7 @@ solution_t mip_solver_t::run_solver() context.gpu_heur_loop, sol, internals::mip_solution_origin_t::UNKNOWN, - -1.0); + 0.0); context.solution_publication.invoke_get_solution_callbacks(payload); context.problem_ptr->post_process_solution(sol); return sol; @@ -220,7 +220,7 @@ solution_t mip_solver_t::run_solver() context.gpu_heur_loop, sol, internals::mip_solution_origin_t::UNKNOWN, - -1.0); + 0.0); context.solution_publication.invoke_get_solution_callbacks(payload); } context.problem_ptr->post_process_solution(sol); diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index 46c558e49b..aa14c188b8 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -62,9 +62,7 @@ solution_callback_payload_t make_solution_callback_payload_from_soluti double work_timestamp) { cuopt_assert(problem_ptr != nullptr, "Callback payload problem pointer must not be null"); - if (work_timestamp < 0.0 && (settings.determinism_mode & CUOPT_DETERMINISM_BB)) { - work_timestamp = gpu_heur_loop.current_work(); - } + cuopt_assert(work_timestamp >= 0.0, "work_timestamp must not be negative"); solution_callback_payload_t payload{}; payload.user_objective = sol.get_user_objective(); payload.solver_objective = sol.get_objective(); @@ -94,9 +92,7 @@ solution_callback_payload_t make_solution_callback_payload_from_host_s double work_timestamp) { cuopt_assert(problem_ptr != nullptr, "Callback payload problem pointer must not be null"); - if (work_timestamp < 0.0 && (settings.determinism_mode & CUOPT_DETERMINISM_BB)) { - work_timestamp = gpu_heur_loop.current_work(); - } + cuopt_assert(work_timestamp >= 0.0, "work_timestamp must not be negative"); solution_callback_payload_t payload{}; payload.assignment = assignment; payload.user_objective = problem_ptr->get_user_obj_from_solver_obj(solver_objective); From 638d35ff5cbe73cd31a6d8e015bc7dc46bce4fbe Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Sat, 14 Mar 2026 11:01:02 -0700 Subject: [PATCH 199/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 9b95c42ccb..8d18dfceac 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From e8bdc7cca1a37be6dd727624c3bf27343e75b347 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 02:39:31 -0700 Subject: [PATCH 200/225] fix wrong work unit timestamp beign used --- cpp/src/branch_and_bound/branch_and_bound.cpp | 6 +++--- cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 948a63fc8e..426cac3683 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -656,14 +656,14 @@ void branch_and_bound_t::queue_external_solution_deterministic( mutex_original_lp_.unlock(); mutex_heuristic_queue_.lock(); - heuristic_solution_queue_.push_back({solution, user_objective, bnb_work_total, origin}); + heuristic_solution_queue_.push_back({solution, user_objective, work_unit_ts, origin}); const size_t heuristic_queue_size = heuristic_solution_queue_.size(); mutex_heuristic_queue_.unlock(); CUOPT_DETERMINISM_LOG( settings_.log, - "Deterministic external queued_for_retirement: bnb_wut=%.6f user_obj=%.16e host_hash=0x%x " + "Deterministic external queued_for_retirement: wut=%.6f user_obj=%.16e host_hash=0x%x " "heur_q=%zu\n", - bnb_work_total, + work_unit_ts, user_objective, host_hash, heuristic_queue_size); diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index 713128fe38..99ad595386 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -108,7 +108,7 @@ optimization_problem_solution_t get_relaxed_lp_solution( } else { estim_iters = std::numeric_limits::max(); } - CUOPT_LOG_INFO("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); + CUOPT_LOG_DEBUG("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); pdlp_settings.iteration_limit = estim_iters; pdlp_settings.time_limit = std::numeric_limits::infinity(); pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; From 41ac7bbee1c6f12d89b6085af8c165530aacf820 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 02:41:16 -0700 Subject: [PATCH 201/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 8d18dfceac..9b95c42ccb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -330,7 +330,7 @@ int run_single_file(std::string file_path, std::string csv_path = out_dir + "/" + mps_stem + "_incumbents.csv"; incumbent_tracker.write_csv(csv_path); CUOPT_LOG_INFO( - "1Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); + "Incumbent trace (%zu entries) written to %s", incumbent_tracker.size(), csv_path.c_str()); } return sol_found; } From 1867f30c192be72c61aac6d36e12acf2845eea90 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 05:14:43 -0700 Subject: [PATCH 202/225] missing missing clock rebase --- cpp/src/branch_and_bound/branch_and_bound.cpp | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 426cac3683..9934ac0725 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -979,7 +979,7 @@ void branch_and_bound_t::set_final_solution(mip_solution_t& solution.simplex_iterations = exploration_stats_.total_lp_iters; CUOPT_DETERMINISM_LOG( settings_.log, - "Deterministic B&B final package: status=%d incumbent_obj=%.16e lower_bound=%.16e " + "1Deterministic B&B final package: status=%d incumbent_obj=%.16e lower_bound=%.16e " "incumbent_hash=0x%x final_hash=0x%x nodes=%d simplex_iterations=%d\n", (int)solver_status_.load(), solution.objective, @@ -3440,10 +3440,12 @@ void branch_and_bound_t::run_deterministic_coordinator(const csr_matri scoped_context_registrations_t context_registrations(*deterministic_scheduler_); for (auto& worker : *deterministic_workers_) { + worker.clock = pre_exploration_work_; context_registrations.add(worker.work_context); } if (deterministic_diving_workers_) { for (auto& worker : *deterministic_diving_workers_) { + worker.clock = pre_exploration_work_; context_registrations.add(worker.work_context); } } @@ -3663,6 +3665,21 @@ void branch_and_bound_t::deterministic_sync_callback() work_unit_context_.set_current_work(horizon_end, false); + { + std::string worker_clocks_str; + for (const auto& w : *deterministic_workers_) { + worker_clocks_str += std::to_string(w.worker_id) + ":" + std::to_string(w.clock) + "/" + + std::to_string(w.integer_solutions.size()) + " "; + } + settings_.log.printf( + "Deterministic sync #%d: horizon=%.6f pre_expl=%.6f heur_q=%zu workers=[%s]\n", + deterministic_horizon_number_, + deterministic_current_horizon_, + pre_exploration_work_, + heuristic_solution_queue_.size(), + worker_clocks_str.c_str()); + } + bb_event_batch_t all_events = deterministic_workers_->collect_and_sort_events(); if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { CUOPT_DETERMINISM_LOG( @@ -4079,6 +4096,22 @@ void branch_and_bound_t::deterministic_sort_replay_events( return a.solution < b.solution; }); + if (!due_solutions.empty() || !heuristic_solution_queue_.empty()) { + settings_.log.printf( + "Deterministic sync retire: horizon=%.6f due=%zu future=%zu pre_expl=%.6f\n", + deterministic_current_horizon_, + due_solutions.size(), + heuristic_solution_queue_.size(), + pre_exploration_work_); + for (size_t i = 0; i < due_solutions.size(); ++i) { + settings_.log.printf( + " due[%zu]: wut=%.6f obj=%g origin=%s\n", + i, + due_solutions[i].work_timestamp, + due_solutions[i].user_objective, + cuopt::internals::mip_solution_origin_to_string(due_solutions[i].origin)); + } + } if (!due_solutions.empty()) { CUOPT_DETERMINISM_LOG(settings_.log, "Deterministic sync: retiring %ld external solutions\n", @@ -4231,6 +4264,15 @@ void branch_and_bound_t::deterministic_sort_replay_events( detail::compute_hash(sol.solution)); if (improved) { + settings_.log.printf( + "Deterministic replay PUBLISH: horizon=%.6f wut=%.6f obj=%g origin=%s worker=%d " + "upper_after=%.16e\n", + deterministic_current_horizon_, + sol.work_timestamp, + compute_user_objective(original_lp_, sol.objective), + cuopt::internals::mip_solution_origin_to_string(sol.origin), + sol.worker_id, + current_upper); if (sol.origin == cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE || sol.origin == cuopt::internals::mip_solution_origin_t::BRANCH_AND_BOUND_DIVING) { report(feasible_solution_symbol(replay.strategy), @@ -4607,7 +4649,7 @@ void branch_and_bound_t::deterministic_dive( } worker.lp_iters_this_dive += node_iter; - worker.clock = worker.work_context.global_work_units_elapsed; + worker.clock = pre_exploration_work_ + worker.work_context.global_work_units_elapsed; if (lp_status == dual::status_t::TIME_LIMIT || lp_status == dual::status_t::WORK_LIMIT || lp_status == dual::status_t::ITERATION_LIMIT) { From a90233f1a20cb395880eeefbedfa9de17b6eb2d8 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 05:14:57 -0700 Subject: [PATCH 203/225] bump1 --- cpp/src/branch_and_bound/branch_and_bound.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 9934ac0725..e55ad3271e 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -979,7 +979,7 @@ void branch_and_bound_t::set_final_solution(mip_solution_t& solution.simplex_iterations = exploration_stats_.total_lp_iters; CUOPT_DETERMINISM_LOG( settings_.log, - "1Deterministic B&B final package: status=%d incumbent_obj=%.16e lower_bound=%.16e " + "Deterministic B&B final package: status=%d incumbent_obj=%.16e lower_bound=%.16e " "incumbent_hash=0x%x final_hash=0x%x nodes=%d simplex_iterations=%d\n", (int)solver_status_.load(), solution.objective, From 9598b378349309140c3e4c7cc0f35a2be7ccc672 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 06:54:53 -0700 Subject: [PATCH 204/225] task-level work unit accounting for strong branching instead of thread-level --- cpp/src/branch_and_bound/branch_and_bound.cpp | 34 +++++++++++++------ cpp/src/branch_and_bound/pseudo_costs.cpp | 33 +++++++++--------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index e55ad3271e..e15c0966d3 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -46,11 +46,11 @@ #include // uncomment to enable detailed detemrinism logs -// #undef CUOPT_DETERMINISM_LOG -// #define CUOPT_DETERMINISM_LOG(logger, ...) \ -// do { \ -// logger.printf(__VA_ARGS__); \ -// } while (0) +#undef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(logger, ...) \ + do { \ + logger.printf(__VA_ARGS__); \ + } while (0) namespace cuopt::linear_programming::dual_simplex { @@ -2514,6 +2514,10 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut solving_root_relaxation_ = false; exploration_stats_.total_lp_iters = root_relax_soln_.iterations; exploration_stats_.total_lp_solve_time = toc(exploration_stats_.start_time); + CUOPT_DETERMINISM_LOG(settings_.log, + "Post-root-LP work: %.16e iters=%d\n", + work_unit_context_.current_work(), + root_relax_soln_.iterations); auto finish_clique_thread = [this]() { if (clique_table_future_.valid()) { @@ -3204,7 +3208,13 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut calculate_variable_locks(original_lp_, var_up_locks_, var_down_locks_); } if (settings_.deterministic) { - pre_exploration_work_ = work_unit_context_.current_work(); + pre_exploration_work_ = work_unit_context_.current_work(); + CUOPT_DETERMINISM_LOG( + settings_.log, + "Pre-exploration work breakdown: total=%.16e scale=%.6f deterministic=%d\n", + pre_exploration_work_, + work_unit_context_.work_unit_scale, + (int)work_unit_context_.deterministic); work_unit_context_.scheduler = saved_scheduler; work_unit_context_.work_unit_scale = settings_.bnb_work_unit_scale; settings_.log.printf( @@ -3671,7 +3681,8 @@ void branch_and_bound_t::deterministic_sync_callback() worker_clocks_str += std::to_string(w.worker_id) + ":" + std::to_string(w.clock) + "/" + std::to_string(w.integer_solutions.size()) + " "; } - settings_.log.printf( + CUOPT_DETERMINISM_LOG( + settings_.log, "Deterministic sync #%d: horizon=%.6f pre_expl=%.6f heur_q=%zu workers=[%s]\n", deterministic_horizon_number_, deterministic_current_horizon_, @@ -4097,14 +4108,16 @@ void branch_and_bound_t::deterministic_sort_replay_events( }); if (!due_solutions.empty() || !heuristic_solution_queue_.empty()) { - settings_.log.printf( + CUOPT_DETERMINISM_LOG( + settings_.log, "Deterministic sync retire: horizon=%.6f due=%zu future=%zu pre_expl=%.6f\n", deterministic_current_horizon_, due_solutions.size(), heuristic_solution_queue_.size(), pre_exploration_work_); for (size_t i = 0; i < due_solutions.size(); ++i) { - settings_.log.printf( + CUOPT_DETERMINISM_LOG( + settings_.log, " due[%zu]: wut=%.6f obj=%g origin=%s\n", i, due_solutions[i].work_timestamp, @@ -4264,7 +4277,8 @@ void branch_and_bound_t::deterministic_sort_replay_events( detail::compute_hash(sol.solution)); if (improved) { - settings_.log.printf( + CUOPT_DETERMINISM_LOG( + settings_.log, "Deterministic replay PUBLISH: horizon=%.6f wut=%.6f obj=%g origin=%s worker=%d " "upper_after=%.16e\n", deterministic_current_horizon_, diff --git a/cpp/src/branch_and_bound/pseudo_costs.cpp b/cpp/src/branch_and_bound/pseudo_costs.cpp index 91b5d3deb0..e3c3ab8250 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.cpp +++ b/cpp/src/branch_and_bound/pseudo_costs.cpp @@ -672,27 +672,25 @@ void strong_branching(const user_problem_t& original_problem, f_t strong_branching_start_time = tic(); const bool use_work_accounting = work_unit_context && work_unit_context->deterministic; - std::vector thread_work_contexts; + // more tasks than threads in order to allow for dynamic load balancing through OpenMP. + // work context accounting needs to be one on a task basis to avoid + // nondeterminism, because openmp is free to schedule threads as it likes. + const i_t n_tasks = std::min(4 * settings.num_threads, fractional.size()); + std::vector task_work_contexts; if (use_work_accounting) { - thread_work_contexts.reserve(settings.num_threads); - for (i_t t = 0; t < settings.num_threads; ++t) { - thread_work_contexts.emplace_back("sb_thread_" + std::to_string(t)); - thread_work_contexts.back().deterministic = true; + task_work_contexts.reserve(n_tasks); + for (i_t k = 0; k < n_tasks; ++k) { + task_work_contexts.emplace_back("sb_task_" + std::to_string(k)); + task_work_contexts.back().deterministic = true; } } #pragma omp parallel num_threads(settings.num_threads) { - i_t n = std::min(4 * settings.num_threads, fractional.size()); - - // Here we are creating more tasks than the number of threads - // such that they can be scheduled dynamically to the threads. - cuopt::work_limit_context_t* thread_ctx = - use_work_accounting ? &thread_work_contexts[omp_get_thread_num()] : nullptr; #pragma omp for schedule(dynamic, 1) - for (i_t k = 0; k < n; k++) { - i_t start = std::floor(k * fractional.size() / n); - i_t end = std::floor((k + 1) * fractional.size() / n); + for (i_t k = 0; k < n_tasks; k++) { + i_t start = std::floor(k * fractional.size() / n_tasks); + i_t end = std::floor((k + 1) * fractional.size() / n_tasks); constexpr bool verbose = false; if (verbose) { @@ -704,6 +702,9 @@ void strong_branching(const user_problem_t& original_problem, end - start); } + cuopt::work_limit_context_t* task_ctx = + use_work_accounting ? &task_work_contexts[k] : nullptr; + strong_branch_helper(start, end, start_time, @@ -716,13 +717,13 @@ void strong_branching(const user_problem_t& original_problem, root_vstatus, edge_norms, pc, - thread_ctx); + task_ctx); } } if (use_work_accounting) { double max_work = 0.0; - for (const auto& ctx : thread_work_contexts) { + for (auto& ctx : task_work_contexts) { max_work = std::max(max_work, ctx.current_work()); } work_unit_context->record_work_sync_on_horizon(max_work); From 83b78ef2b034e7736dd2e7e0c921830fa047ef4a Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 06:55:20 -0700 Subject: [PATCH 205/225] bump1 --- cpp/src/branch_and_bound/pseudo_costs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/branch_and_bound/pseudo_costs.cpp b/cpp/src/branch_and_bound/pseudo_costs.cpp index e3c3ab8250..189f5804ab 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.cpp +++ b/cpp/src/branch_and_bound/pseudo_costs.cpp @@ -672,7 +672,7 @@ void strong_branching(const user_problem_t& original_problem, f_t strong_branching_start_time = tic(); const bool use_work_accounting = work_unit_context && work_unit_context->deterministic; - // more tasks than threads in order to allow for dynamic load balancing through OpenMP. + // More tasks than threads in order to allow for dynamic load balancing through OpenMP. // work context accounting needs to be one on a task basis to avoid // nondeterminism, because openmp is free to schedule threads as it likes. const i_t n_tasks = std::min(4 * settings.num_threads, fractional.size()); From 0227513d5f75587e671178994cd38f4f83e09ac0 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 09:59:37 -0700 Subject: [PATCH 206/225] full determinsm logs --- .../mip_heuristics/diversity/diversity_manager.cu | 15 +++++++-------- .../feasibility_pump/feasibility_pump.cu | 7 +++++++ .../mip_heuristics/local_search/local_search.cu | 7 +++++++ cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu | 10 +++++----- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 1646779603..42d534e7d1 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -5,7 +5,13 @@ */ /* clang-format on */ -#include "cuda_profiler_api.h" +// uncomment to enable detailed detemrinism logs +#undef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(...) \ + do { \ + CUOPT_LOG_INFO(__VA_ARGS__); \ + } while (0) + #include "diversity_manager.cuh" #include @@ -21,13 +27,6 @@ #include #include -// uncomment to enable detailed detemrinism logs -// #undef CUOPT_DETERMINISM_LOG -// #define CUOPT_DETERMINISM_LOG(...) -// do { -// CUOPT_LOG_INFO(__VA_ARGS__); -// } while (0) - constexpr bool fj_only_run = false; namespace cuopt::linear_programming::detail { diff --git a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu index 3d3af61b2f..b560971914 100644 --- a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu @@ -5,6 +5,13 @@ */ /* clang-format on */ +// uncomment to enable detailed detemrinism logs +#undef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(...) \ + do { \ + CUOPT_LOG_INFO(__VA_ARGS__); \ + } while (0) + #include "feasibility_pump.cuh" #include diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index 252736f3cc..371aa7f58c 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -5,6 +5,13 @@ */ /* clang-format on */ +// uncomment to enable detailed detemrinism logs +#undef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(...) \ + do { \ + CUOPT_LOG_INFO(__VA_ARGS__); \ + } while (0) + #include "lagrangian.cuh" #include "local_search.cuh" diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index 99ad595386..e93d939e39 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -23,11 +23,11 @@ #include -// #undef CUOPT_DETERMINISM_LOG -// #define CUOPT_DETERMINISM_LOG(...) -// do { -// CUOPT_LOG_INFO(__VA_ARGS__); -// } while (0) +#undef CUOPT_DETERMINISM_LOG +#define CUOPT_DETERMINISM_LOG(...) \ + do { \ + CUOPT_LOG_INFO(__VA_ARGS__); \ + } while (0) namespace cuopt::linear_programming::detail { From 18a2d4d775804ce47edf31dc01fb070846dd798f Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 10:04:08 -0700 Subject: [PATCH 207/225] bump1 --- cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index e93d939e39..fd101e0681 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -23,6 +23,7 @@ #include +// uncomment to enable detailed detemrinism logs #undef CUOPT_DETERMINISM_LOG #define CUOPT_DETERMINISM_LOG(...) \ do { \ From 3e214a8601399bf8da258f1b12caa1f715dc8019 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 10:06:14 -0700 Subject: [PATCH 208/225] thread local seed generator --- cpp/src/utilities/seed_generator.cu | 4 ++-- cpp/src/utilities/seed_generator.cuh | 30 +++++++++++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/cpp/src/utilities/seed_generator.cu b/cpp/src/utilities/seed_generator.cu index 1da6662bc1..9e7e48bebf 100644 --- a/cpp/src/utilities/seed_generator.cu +++ b/cpp/src/utilities/seed_generator.cu @@ -1,10 +1,10 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ #include -int64_t cuopt::seed_generator::seed_ = 0; +int64_t cuopt::seed_generator::base_seed_ = 0; diff --git a/cpp/src/utilities/seed_generator.cuh b/cpp/src/utilities/seed_generator.cuh index 8206fbcbfe..aaf08830f8 100644 --- a/cpp/src/utilities/seed_generator.cuh +++ b/cpp/src/utilities/seed_generator.cuh @@ -11,18 +11,34 @@ namespace cuopt { -// TODO: should be thread local? class seed_generator { - static int64_t seed_; + static int64_t base_seed_; + + struct thread_state_t { + int64_t counter{0}; + int64_t last_base{0}; + bool initialized{false}; + }; + + static thread_state_t& local_state() + { + thread_local thread_state_t state; + if (!state.initialized || state.last_base != base_seed_) { + state.counter = base_seed_; + state.last_base = base_seed_; + state.initialized = true; + } + return state; + } public: template static void set_seed(seed_t seed) { #ifdef BENCHMARK - seed_ = std::random_device{}(); + base_seed_ = std::random_device{}(); #else - seed_ = static_cast(seed); + base_seed_ = static_cast(seed); #endif } template @@ -37,13 +53,13 @@ class seed_generator { int line = __builtin_LINE()) { printf("&&&&&&& SEED CALLED BY %s:%d: %s() ***\n", file, line, caller); - return seed_++; + return local_state().counter++; } #else - static int64_t get_seed() { return seed_++; } + static int64_t get_seed() { return local_state().counter++; } #endif - static int64_t peek_seed() { return seed_; } + static int64_t peek_seed() { return local_state().counter; } public: seed_generator(seed_generator const&) = delete; From e8b1291eafe03c3409fa9167a98e3bb2fca1c12b Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 10:52:23 -0700 Subject: [PATCH 209/225] no presolve --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 9b95c42ccb..00b41ae32c 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -277,7 +277,7 @@ int run_single_file(std::string file_path, settings.work_limit); settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; - settings.presolver = cuopt::linear_programming::presolver_t::Default; + settings.presolver = cuopt::linear_programming::presolver_t::None; settings.reliability_branching = reliability_branching; settings.clique_cuts = -1; settings.seed = 42; From cc1e5c36308d68e5faa9de05fff17dfa528913c5 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 10:52:40 -0700 Subject: [PATCH 210/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 00b41ae32c..cf0db72b73 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -277,6 +277,7 @@ int run_single_file(std::string file_path, settings.work_limit); settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; + // TODO: restore settings.presolver = cuopt::linear_programming::presolver_t::None; settings.reliability_branching = reliability_branching; settings.clique_cuts = -1; From 859975fd3cc7352e7f16946a34c486e18145eaa5 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 11:30:04 -0700 Subject: [PATCH 211/225] gate probing behind deterministm flag --- benchmarks/linear_programming/cuopt/run_mip.cpp | 3 +-- cpp/src/mip_heuristics/presolve/third_party_presolve.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index cf0db72b73..9b95c42ccb 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -277,8 +277,7 @@ int run_single_file(std::string file_path, settings.work_limit); settings.tolerances.relative_tolerance = 1e-12; settings.tolerances.absolute_tolerance = 1e-6; - // TODO: restore - settings.presolver = cuopt::linear_programming::presolver_t::None; + settings.presolver = cuopt::linear_programming::presolver_t::Default; settings.reliability_branching = reliability_branching; settings.clique_cuts = -1; settings.seed = 42; diff --git a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp index f9ce4cf1b3..61a7adf895 100644 --- a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp +++ b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp @@ -492,7 +492,8 @@ void check_postsolve_status(const papilo::PostsolveStatus& status) template void set_presolve_methods(papilo::Presolve& presolver, problem_category_t category, - bool dual_postsolve) + bool dual_postsolve, + bool deterministic) { using uptr = std::unique_ptr>; @@ -519,7 +520,7 @@ void set_presolve_methods(papilo::Presolve& presolver, // exhaustive presolvers presolver.addPresolveMethod(uptr(new papilo::ImplIntDetection())); presolver.addPresolveMethod(uptr(new papilo::DominatedCols())); - presolver.addPresolveMethod(uptr(new papilo::Probing())); + if (!deterministic) { presolver.addPresolveMethod(uptr(new papilo::Probing())); } if (!dual_postsolve) { presolver.addPresolveMethod(uptr(new papilo::DualInfer())); @@ -634,7 +635,7 @@ std::optional> third_party_presolve_t papilo_presolver; - set_presolve_methods(papilo_presolver, category, dual_postsolve); + set_presolve_methods(papilo_presolver, category, dual_postsolve, deterministic_); set_presolve_options(papilo_presolver, category, absolute_tolerance, From a63af82a1575757d2887f9508cfbcc8793f4f443 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Mon, 16 Mar 2026 11:30:23 -0700 Subject: [PATCH 212/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index 9b95c42ccb..d72ea9f064 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -268,7 +268,7 @@ int run_single_file(std::string file_path, settings.determinism_mode = CUOPT_MODE_OPPORTUNISTIC; } CUOPT_LOG_INFO( - "run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " + "1run_mip settings: heuristics_only=%d deterministic=%d determinism_mode=%d " "time_limit=%.6f work_limit=%.6f", (int)heuristics_only, (int)deterministic, From c5116e18157404b88a5b3b0c15970db91720afe2 Mon Sep 17 00:00:00 2001 From: Trevor McKay Date: Mon, 16 Mar 2026 15:02:57 -0400 Subject: [PATCH 213/225] turn off cpu only tests to unblock CI during investigation (#959) something has changed in the CI environment to cause cpu-only tests related to remote execution to fail, disabling these tests while we investigate root cause for the change Authors: - Trevor McKay (https://github.com/tmckayus) Approvers: - Ramakrishnap (https://github.com/rgsl888prabhu) - Rajesh Gandham (https://github.com/rg20) URL: https://github.com/NVIDIA/cuopt/pull/959 --- cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp | 4 ++-- .../cuopt/tests/linear_programming/test_cpu_only_execution.py | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp b/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp index d39a970763..995b2194c4 100644 --- a/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp +++ b/cpp/tests/linear_programming/c_api_tests/c_api_tests.cpp @@ -382,7 +382,7 @@ class CPUOnlyTestEnvironment { // TODO: Add numerical assertions once gRPC remote solver replaces the stub implementation. // Currently validates that the CPU-only C API path completes without errors. -TEST(c_api_cpu_only, lp_solve) +TEST(c_api_cpu_only, DISABLED_lp_solve) { CPUOnlyTestEnvironment env; const std::string& rapidsDatasetRootDir = cuopt::test::get_rapids_dataset_root_dir(); @@ -391,7 +391,7 @@ TEST(c_api_cpu_only, lp_solve) } // TODO: Add numerical assertions once gRPC remote solver replaces the stub implementation. -TEST(c_api_cpu_only, mip_solve) +TEST(c_api_cpu_only, DISABLED_mip_solve) { CPUOnlyTestEnvironment env; const std::string& rapidsDatasetRootDir = cuopt::test::get_rapids_dataset_root_dir(); diff --git a/python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py b/python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py index 792942aae9..e8dd179e05 100644 --- a/python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py +++ b/python/cuopt/cuopt/tests/linear_programming/test_cpu_only_execution.py @@ -168,6 +168,8 @@ def _impl_warmstart_cpu_only(): class TestCPUOnlyExecution: """Tests that run with CUDA_VISIBLE_DEVICES='' to simulate CPU-only hosts.""" + pytestmark = pytest.mark.skip(reason="CPU-only tests temporarily disabled") + @pytest.fixture def env(self): return _cpu_only_env() @@ -201,6 +203,8 @@ def test_warmstart_cpu_only(self, env): class TestCuoptCliCPUOnly: """Test that cuopt_cli runs without CUDA in remote-execution mode.""" + pytestmark = pytest.mark.skip(reason="CPU-only tests temporarily disabled") + @pytest.fixture def env(self): return _cpu_only_env() From 894b2002c71723b13bdde653309eeb915a692cca Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 02:39:03 -0700 Subject: [PATCH 214/225] extra logging --- .../mip_heuristics/relaxed_lp/relaxed_lp.cu | 16 +++++++++ cpp/src/pdlp/pdlp.cu | 34 +++++++++++++++++++ cpp/src/pdlp/pdlp.cuh | 2 ++ 3 files changed, 52 insertions(+) diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index fd101e0681..8f0bfb3bea 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -164,6 +164,21 @@ optimization_problem_solution_t get_relaxed_lp_solution( lp_solver.set_inside_mip(true); CUOPT_DETERMINISM_LOG( "prev solution sizes primal=%lu dual=%lu", assignment.size(), lp_state.prev_dual.size()); + if (determinism_mode) { + auto scaling_hash = lp_solver.get_scaling_hash(); + auto init_primal_hash = + detail::compute_hash(make_span(assignment), op_problem.handle_ptr->get_stream()); + auto init_dual_hash = + settings.has_initial_primal + ? detail::compute_hash(make_span(lp_state.prev_dual), op_problem.handle_ptr->get_stream()) + : 0u; + CUOPT_DETERMINISM_LOG( + "LP call %lu pre-solve state: scaling_hash=0x%x init_primal_hash=0x%x init_dual_hash=0x%x", + lp_call_id, + scaling_hash, + init_primal_hash, + init_dual_hash); + } auto solver_response = lp_solver.run_solver(start_time); CUOPT_DETERMINISM_LOG("post LP primal size %lu", solver_response.get_primal_solution().size()); const int actual_iters = @@ -176,6 +191,7 @@ optimization_problem_solution_t get_relaxed_lp_solution( ? detail::compute_hash(solver_response.get_primal_solution(), op_problem.handle_ptr->get_stream()) : 0u); + if (determinism_mode && settings.work_context != nullptr) { double work_to_record = settings.work_limit; if (estim_iters > 0) { diff --git a/cpp/src/pdlp/pdlp.cu b/cpp/src/pdlp/pdlp.cu index 82e79098a7..b931f5071e 100644 --- a/cpp/src/pdlp/pdlp.cu +++ b/cpp/src/pdlp/pdlp.cu @@ -16,6 +16,10 @@ #include #include +#include +#include + +#include #include "cuopt/linear_programming/pdlp/solver_solution.hpp" #include @@ -2565,6 +2569,18 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co ++internal_solver_iterations_; if (settings_.hyper_params.never_restart_to_average) restart_strategy_.increment_iteration_since_last_restart(); + + if (inside_mip_ && + (internal_solver_iterations_ % 10 == 0 || internal_solver_iterations_ <= 3)) { + auto primal_hash = detail::compute_hash( + make_span(pdhg_solver_.get_potential_next_primal_solution()), stream_view_); + auto dual_hash = detail::compute_hash( + make_span(pdhg_solver_.get_potential_next_dual_solution()), stream_view_); + CUOPT_DETERMINISM_LOG("PDLP iter %d: primal_hash=0x%x dual_hash=0x%x", + internal_solver_iterations_, + primal_hash, + dual_hash); + } } return optimization_problem_solution_t{pdlp_termination_status_t::NumericalError, stream_view_}; @@ -2971,6 +2987,24 @@ pdlp_solver_t::get_current_termination_strategy() return current_termination_strategy_; } +template +uint32_t pdlp_solver_t::get_scaling_hash() +{ + auto stream = handle_ptr_->get_stream(); + auto v = initial_scaling_strategy_.view(); + uint32_t h = 0; + h ^= detail::compute_hash(v.cummulative_constraint_matrix_scaling, stream); + h ^= detail::compute_hash(v.cummulative_variable_scaling, stream) * 2654435761u; + auto bound_val = initial_scaling_strategy_.get_h_bound_rescaling(); + auto obj_val = initial_scaling_strategy_.get_h_objective_rescaling(); + uint32_t bh, oh; + std::memcpy(&bh, &bound_val, std::min(sizeof(bh), sizeof(bound_val))); + std::memcpy(&oh, &obj_val, std::min(sizeof(oh), sizeof(obj_val))); + h ^= bh * 2246822519u; + h ^= oh * 3266489917u; + return h; +} + #if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class pdlp_solver_t; diff --git a/cpp/src/pdlp/pdlp.cuh b/cpp/src/pdlp/pdlp.cuh index de0cf69c91..9ebae676f0 100644 --- a/cpp/src/pdlp/pdlp.cuh +++ b/cpp/src/pdlp/pdlp.cuh @@ -97,6 +97,8 @@ class pdlp_solver_t { void set_inside_mip(bool inside_mip); + uint32_t get_scaling_hash(); + void compute_initial_step_size(); void compute_initial_primal_weight(); From 0a20329b6ffbf6ec2f76e2f055b6f04698ab3230 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 02:39:28 -0700 Subject: [PATCH 215/225] bump1 --- cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index 8f0bfb3bea..8199bc766f 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -192,6 +192,8 @@ optimization_problem_solution_t get_relaxed_lp_solution( op_problem.handle_ptr->get_stream()) : 0u); + // tmp + if (determinism_mode && settings.work_context != nullptr) { double work_to_record = settings.work_limit; if (estim_iters > 0) { From f7ebf56a2099f3261add99b19e942d6859b41eea Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 05:03:39 -0700 Subject: [PATCH 216/225] fix unintiialized reads on problem copy --- cpp/src/mip_heuristics/problem/problem.cu | 6 +++++- cpp/src/mip_heuristics/problem/problem_fixing.cuh | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip_heuristics/problem/problem.cu b/cpp/src/mip_heuristics/problem/problem.cu index 66344f27d7..87df8a55f0 100644 --- a/cpp/src/mip_heuristics/problem/problem.cu +++ b/cpp/src/mip_heuristics/problem/problem.cu @@ -64,6 +64,10 @@ void problem_t::op_problem_cstr_body(const optimization_problem_tget_thrust_policy(), + integer_fixed_variable_map.begin(), + integer_fixed_variable_map.end(), + -1); const bool is_mip = original_problem_ptr->get_problem_category() != problem_category_t::LP; if (is_mip) { @@ -136,7 +140,7 @@ problem_t::problem_t( nonbinary_indices(0, problem_.get_handle_ptr()->get_stream()), is_binary_variable(0, problem_.get_handle_ptr()->get_stream()), related_variables(0, problem_.get_handle_ptr()->get_stream()), - related_variables_offsets(n_variables, problem_.get_handle_ptr()->get_stream()), + related_variables_offsets(0, problem_.get_handle_ptr()->get_stream()), var_names(problem_.get_variable_names()), row_names(problem_.get_row_names()), objective_name(problem_.get_objective_name()), diff --git a/cpp/src/mip_heuristics/problem/problem_fixing.cuh b/cpp/src/mip_heuristics/problem/problem_fixing.cuh index 820b74e329..c462838d96 100644 --- a/cpp/src/mip_heuristics/problem/problem_fixing.cuh +++ b/cpp/src/mip_heuristics/problem/problem_fixing.cuh @@ -1,12 +1,13 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ #pragma once +#include #include namespace cuopt { @@ -18,6 +19,10 @@ struct problem_fixing_helpers_t { : reduction_in_rhs(n_constraints, handle_ptr->get_stream()), variable_fix_mask(n_variables, handle_ptr->get_stream()) { + thrust::fill( + handle_ptr->get_thrust_policy(), reduction_in_rhs.begin(), reduction_in_rhs.end(), f_t(0)); + thrust::fill( + handle_ptr->get_thrust_policy(), variable_fix_mask.begin(), variable_fix_mask.end(), i_t(0)); } problem_fixing_helpers_t(const problem_fixing_helpers_t& other, const raft::handle_t* handle_ptr) From 479b6627c0bf744b9f2a6ca47d077ada8e3f5143 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 08:57:38 -0700 Subject: [PATCH 217/225] uninitialized access fixes --- ci/compute-sanitizer-suppressions.xml | 249 ++++++++++++++++++ .../diversity/diversity_manager.cu | 5 + .../mip_heuristics/relaxed_lp/relaxed_lp.cu | 5 +- cpp/src/mip_heuristics/solution/solution.cu | 29 +- cpp/src/pdlp/pdlp.cu | 4 + .../infeasibility_information.cu | 32 +-- cpp/src/utilities/copy_helpers.hpp | 12 + cpp/src/utilities/cuda_helpers.cuh | 56 ++++ 8 files changed, 369 insertions(+), 23 deletions(-) create mode 100644 ci/compute-sanitizer-suppressions.xml diff --git a/ci/compute-sanitizer-suppressions.xml b/ci/compute-sanitizer-suppressions.xml new file mode 100644 index 0000000000..624b3aa0bd --- /dev/null +++ b/ci/compute-sanitizer-suppressions.xml @@ -0,0 +1,249 @@ + + + + Initcheck + + Uninitialized __global__ memory read of size 4 bytes + 4 + + + .* + + + + .*libcuda.so.* + + + cusparseCsr2cscEx2 + .*libcusparse.so.* + + + + + Initcheck + + Uninitialized __global__ memory read of size 4 bytes + 4 + + + ThreadLoad + + + + .*libcuda.so.* + + + libcudart.* + + + cudaLaunchKernel + + + .*cub::.*::Device(Segmented)?(Reduce|Scan)(SingleTile)?Kernel.* + + + + + Initcheck + + Uninitialized __global__ memory read of size 2 bytes + 2 + + + ThreadLoad + + + + .*libcuda.so.* + + + libcudart.* + + + cudaLaunchKernel + + + .*cub::.*::Device(Segmented)?(Reduce|Scan)(SingleTile)?Kernel.* + + + + + Initcheck + + Uninitialized __global__ memory read of size 8 bytes + 8 + + + DeviceSegmentedReduceKernel + + + + Initcheck + + Uninitialized __global__ memory read of size 4 bytes + 4 + + + ThreadLoad + + + + .*libcuda.so.* + + + libcudart.* + + + libcudart.* + + + .*libcuopt.* + + + .*Device(Reduce|Scan)Kernel.* + + + + + + + InitcheckApiError + Error + + Host API uninitialized memory access + 16 + + + + cuMemcpyDtoHAsync.* + .*libcuda.so.* + + + + + + InitcheckApiError + Error + + Host API uninitialized memory access + + + + cuMemcpyAsync + .*libcuda.so.* + + + .*libcudart.so.* + + + .*libcudart.so.* + + + .*libcudart.so.* + + + .*librmm.so.* + + + rmm::device_buffer::device_buffer + .*librmm.so.* + + + + + + Initcheck + + Uninitialized __global__ memory read + + + transform_kernel + + + + cuLaunchKernel_ptsz + .*libcuda.so.* + + + .*libcudart.so.* + + + cudaLaunchKernel_ptsz + + + + + InitcheckApiError + Error + + Host API uninitialized memory access + + + + cuMemcpyAsync + .*libcuda.so.* + + + .*libcudart.so.* + + + .*libcudart.so.* + + + .*libcudart.so.* + + + .*librmm.so.* + + + .*librmm.so.* + + + rmm::device_uvector.*::device_uvector + .*libcuopt.so.* + + + + + + InitcheckApiError + Error + + Host API uninitialized memory access + + + + cuMemcpyDtoDAsync.* + .*libcuda.so.* + + + + + InitcheckApiError + Error + + Host API uninitialized memory access + + + + cuMemcpyAsync + .*libcuda.so.* + + + .*libcudart.so.* + + + .*libcudart.so.* + + + cudaMemcpyAsync + + + rmm::device_buffer::resize + .*librmm.so.* + + + + diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 42d534e7d1..0e2d073248 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -231,6 +231,7 @@ bool diversity_manager_t::run_presolve(f_t time_limit, if (termination_criterion_t::NO_UPDATE != term_crit) { ls.constraint_prop.bounds_update.set_updated_bounds(*problem_ptr); } + // tmp bool run_probing_cache = !fj_only_run; if (run_probing_cache) { // Run probing cache before trivial presolve to discover variable implications @@ -284,6 +285,10 @@ bool diversity_manager_t::run_presolve(f_t time_limit, } stats.presolve_time = presolve_timer.elapsed_time(); lp_optimal_solution.resize(problem_ptr->n_variables, problem_ptr->handle_ptr->get_stream()); + thrust::fill(problem_ptr->handle_ptr->get_thrust_policy(), + lp_optimal_solution.begin(), + lp_optimal_solution.end(), + f_t(0)); lp_dual_optimal_solution.resize(problem_ptr->n_constraints, problem_ptr->handle_ptr->get_stream()); problem_ptr->handle_ptr->sync_stream(); diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index 8199bc766f..0885a8ef96 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -148,8 +148,9 @@ optimization_problem_solution_t get_relaxed_lp_solution( lp_state.prev_dual.data(), lp_state.prev_dual.data() + op_problem.n_constraints, [prev_size, dual = make_span(lp_state.prev_dual)] __device__(i_t i) { + if (i >= prev_size) { return 0.0; } f_t x = dual[i]; - if (!isfinite(x) || i >= prev_size) { return 0.0; } + if (!isfinite(x)) { return 0.0; } return x; }); lp_solver.set_initial_primal_solution(assignment); @@ -192,8 +193,6 @@ optimization_problem_solution_t get_relaxed_lp_solution( op_problem.handle_ptr->get_stream()) : 0u); - // tmp - if (determinism_mode && settings.work_context != nullptr) { double work_to_record = settings.work_limit; if (estim_iters > 0) { diff --git a/cpp/src/mip_heuristics/solution/solution.cu b/cpp/src/mip_heuristics/solution/solution.cu index b0fbbb846a..396f3c76d0 100644 --- a/cpp/src/mip_heuristics/solution/solution.cu +++ b/cpp/src/mip_heuristics/solution/solution.cu @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -91,10 +92,20 @@ void solution_t::copy_from(const solution_t& other_sol) h_user_obj = other_sol.h_user_obj; h_infeasibility_cost = other_sol.h_infeasibility_cost; expand_device_copy(assignment, other_sol.assignment, handle_ptr->get_stream()); + + // slack, excess, and constraint value may be uninitialized (and computed later). Mark them as + // such + cuopt::mark_span_as_initialized(make_span(other_sol.lower_excess), handle_ptr->get_stream()); + cuopt::mark_span_as_initialized(make_span(other_sol.upper_excess), handle_ptr->get_stream()); + cuopt::mark_span_as_initialized(make_span(other_sol.lower_slack), handle_ptr->get_stream()); + cuopt::mark_span_as_initialized(make_span(other_sol.upper_slack), handle_ptr->get_stream()); + cuopt::mark_span_as_initialized(make_span(other_sol.constraint_value), handle_ptr->get_stream()); + cuopt::mark_span_as_initialized(make_span(other_sol.obj_val), handle_ptr->get_stream()); + cuopt::mark_span_as_initialized(make_span(other_sol.n_feasible_constraints), + handle_ptr->get_stream()); + expand_device_copy(lower_excess, other_sol.lower_excess, handle_ptr->get_stream()); expand_device_copy(upper_excess, other_sol.upper_excess, handle_ptr->get_stream()); - expand_device_copy(lower_slack, other_sol.lower_slack, handle_ptr->get_stream()); - expand_device_copy(upper_slack, other_sol.upper_slack, handle_ptr->get_stream()); expand_device_copy(constraint_value, other_sol.constraint_value, handle_ptr->get_stream()); raft::copy(obj_val.data(), other_sol.obj_val.data(), 1, handle_ptr->get_stream()); raft::copy(n_feasible_constraints.data(), @@ -113,6 +124,8 @@ void solution_t::copy_from(const solution_t& other_sol) template void solution_t::resize_to_problem() { + i_t old_n_vars = lp_state.prev_primal.size(); + i_t old_n_cstrs = lp_state.prev_dual.size(); assignment.resize(problem_ptr->n_variables, handle_ptr->get_stream()); lower_excess.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); upper_excess.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); @@ -121,6 +134,18 @@ void solution_t::resize_to_problem() constraint_value.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); lp_state.prev_primal.resize(problem_ptr->n_variables, handle_ptr->get_stream()); lp_state.prev_dual.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); + if (problem_ptr->n_variables > old_n_vars) { + thrust::fill(handle_ptr->get_thrust_policy(), + lp_state.prev_primal.data() + old_n_vars, + lp_state.prev_primal.data() + problem_ptr->n_variables, + f_t(0)); + } + if (problem_ptr->n_constraints > old_n_cstrs) { + thrust::fill(handle_ptr->get_thrust_policy(), + lp_state.prev_dual.data() + old_n_cstrs, + lp_state.prev_dual.data() + problem_ptr->n_constraints, + f_t(0)); + } } template diff --git a/cpp/src/pdlp/pdlp.cu b/cpp/src/pdlp/pdlp.cu index b931f5071e..650c58b83f 100644 --- a/cpp/src/pdlp/pdlp.cu +++ b/cpp/src/pdlp/pdlp.cu @@ -2261,6 +2261,10 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co cuopt_expects(!batch_mode_, cuopt::error_type_t::ValidationError, "Restart to average not supported in batch mode"); + raft::copy(unscaled_primal_avg_solution_.data(), + pdhg_solver_.get_primal_solution().data(), + primal_size_h_, + stream_view_); cub::DeviceTransform::Transform( cuda::std::make_tuple(unscaled_primal_avg_solution_.data(), op_problem_scaled_.variable_bounds.data()), diff --git a/cpp/src/pdlp/termination_strategy/infeasibility_information.cu b/cpp/src/pdlp/termination_strategy/infeasibility_information.cu index dbb35b732d..235a399bd2 100644 --- a/cpp/src/pdlp/termination_strategy/infeasibility_information.cu +++ b/cpp/src/pdlp/termination_strategy/infeasibility_information.cu @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -89,6 +90,19 @@ infeasibility_information_t::infeasibility_information_t( climber_strategies_(climber_strategies), hyper_params_(hyper_params) { + cuopt::mark_memory_as_initialized(max_primal_ray_infeasibility_.data(), + max_primal_ray_infeasibility_.size() * sizeof(f_t), + stream_view_); + cuopt::mark_memory_as_initialized(primal_ray_linear_objective_.data(), + primal_ray_linear_objective_.size() * sizeof(f_t), + stream_view_); + cuopt::mark_memory_as_initialized(max_dual_ray_infeasibility_.data(), + max_dual_ray_infeasibility_.size() * sizeof(f_t), + stream_view_); + cuopt::mark_memory_as_initialized(dual_ray_linear_objective_.data(), + dual_ray_linear_objective_.size() * sizeof(f_t), + stream_view_); + if (infeasibility_detection) { RAFT_CUDA_TRY(cudaMemsetAsync(homogenous_primal_residual_.data(), 0.0, @@ -131,24 +145,6 @@ infeasibility_information_t::infeasibility_information_t( size_of_buffer_ = std::max({temp_storage_bytes_1, temp_storage_bytes_2}); this->rmm_tmp_buffer_ = rmm::device_buffer{size_of_buffer_, stream_view_}; - - RAFT_CUDA_TRY(cudaMemsetAsync(dual_ray_linear_objective_.data(), - 0, - sizeof(f_t) * dual_ray_linear_objective_.size(), - stream_view_)); - RAFT_CUDA_TRY(cudaMemsetAsync(max_dual_ray_infeasibility_.data(), - 0, - sizeof(f_t) * max_dual_ray_infeasibility_.size(), - stream_view_)); - - RAFT_CUDA_TRY(cudaMemsetAsync(primal_ray_linear_objective_.data(), - 0, - sizeof(f_t) * primal_ray_linear_objective_.size(), - stream_view_)); - RAFT_CUDA_TRY(cudaMemsetAsync(max_primal_ray_infeasibility_.data(), - 0, - sizeof(f_t) * max_primal_ray_infeasibility_.size(), - stream_view_)); } } diff --git a/cpp/src/utilities/copy_helpers.hpp b/cpp/src/utilities/copy_helpers.hpp index 36a4659059..3558d31ea5 100644 --- a/cpp/src/utilities/copy_helpers.hpp +++ b/cpp/src/utilities/copy_helpers.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -335,6 +336,17 @@ raft::device_span make_span(rmm::device_uvector const& container) return raft::device_span(container.data(), container.size()); } +template +raft::device_span make_span(rmm::device_scalar& scalar) +{ + return raft::device_span(scalar.data(), 1); +} + +template +raft::device_span make_span(rmm::device_scalar const& scalar) +{ + return raft::device_span(scalar.data(), 1); +} // resizes the device vector if it the std vector is larger template inline void expand_device_copy(rmm::device_uvector& device_vec, diff --git a/cpp/src/utilities/cuda_helpers.cuh b/cpp/src/utilities/cuda_helpers.cuh index 946099648d..679a1916b7 100644 --- a/cpp/src/utilities/cuda_helpers.cuh +++ b/cpp/src/utilities/cuda_helpers.cuh @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,17 @@ #include #include +#if CUDART_VERSION >= 12080 +// TODO: investigate why this is necessary? dependency conflict? file NVBUG if necessary +#include +#ifndef NVTX_NULLPTR +#define NVTX_NULLPTR nullptr +#endif +#ifndef NVTX_REINTERPRET_CAST +#define NVTX_REINTERPRET_CAST(type, value) (reinterpret_cast(value)) +#endif +#include +#endif namespace cuopt { #if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ < 700) @@ -237,4 +249,48 @@ inline size_t get_device_memory_size() } } +// NOTE: this marks a range of virtual memory as initialized. This is not tied to any object's +// lifetime As such, when using a pool for allocations, false negatives could occurs e.g. a range +// previously marked as initialized is now occupied by a new uninitialized object Unlikely to cause +// issues in practice - but worth noting (RAII? I'm not even sure the API allows to un-mark a range +// as initialized) +static inline void mark_memory_as_initialized(const void* ptr, size_t size, cudaStream_t stream = 0) +{ +#if CUDART_VERSION >= 12080 + + if (size == 0 || ptr == nullptr) return; + +#if defined(CUDA_API_PER_THREAD_DEFAULT_STREAM) + constexpr auto PerThreadDefaultStream = true; +#else + constexpr auto PerThreadDefaultStream = false; +#endif + + nvtxMemVirtualRangeDesc_t nvtxRangeDesc = {}; + nvtxRangeDesc.size = size; + nvtxRangeDesc.ptr = ptr; + + nvtxMemMarkInitializedBatch_t nvtxRegionsDesc = {}; + nvtxRegionsDesc.extCompatID = NVTX_EXT_COMPATID_MEM; + nvtxRegionsDesc.structSize = sizeof(nvtxRegionsDesc); + nvtxRegionsDesc.regionType = NVTX_MEM_TYPE_VIRTUAL_ADDRESS; + nvtxRegionsDesc.regionDescCount = 1; + nvtxRegionsDesc.regionDescElementSize = sizeof(nvtxRangeDesc); + nvtxRegionsDesc.regionDescElements = &nvtxRangeDesc; + + nvtxMemCudaMarkInitialized( + raft::common::nvtx::detail::domain_store::value(), + stream, + PerThreadDefaultStream, + &nvtxRegionsDesc); +#endif +} + +template +static inline void mark_span_as_initialized(const raft::device_span span, + rmm::cuda_stream_view stream) +{ + mark_memory_as_initialized(span.data(), span.size() * sizeof(T), stream.value()); +} + } // namespace cuopt From 27fb24bc3fce3b251d3e4cba24c5bcaa3d6a0a12 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 08:58:09 -0700 Subject: [PATCH 218/225] bump 1 --- cpp/src/mip_heuristics/diversity/diversity_manager.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 0e2d073248..0831f9ab38 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -231,7 +231,7 @@ bool diversity_manager_t::run_presolve(f_t time_limit, if (termination_criterion_t::NO_UPDATE != term_crit) { ls.constraint_prop.bounds_update.set_updated_bounds(*problem_ptr); } - // tmp + bool run_probing_cache = !fj_only_run; if (run_probing_cache) { // Run probing cache before trivial presolve to discover variable implications From 9a4b1d0a4ce8a66648b051935e4c9ee8580b99f8 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 09:22:14 -0700 Subject: [PATCH 219/225] fix build --- cpp/src/utilities/cuda_helpers.cuh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/utilities/cuda_helpers.cuh b/cpp/src/utilities/cuda_helpers.cuh index 679a1916b7..7c591624d2 100644 --- a/cpp/src/utilities/cuda_helpers.cuh +++ b/cpp/src/utilities/cuda_helpers.cuh @@ -12,8 +12,8 @@ #include #include #include -#include #include +#include #include #include #include From e87c98f44b5d15489263611624a26dcfe4b623e7 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 09:22:30 -0700 Subject: [PATCH 220/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index d72ea9f064..e851fb45a0 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -296,7 +296,7 @@ int run_single_file(std::string file_path, benchmark_info.objective_of_initial_population, benchmark_info.last_improvement_of_best_feasible, benchmark_info.last_improvement_after_recombination); - // solution.write_to_sol_file(base_filename + ".sol", handle_.get_stream()); + // 1solution.write_to_sol_file(base_filename + ".sol", handle_.get_stream()); std::chrono::milliseconds duration; auto end = std::chrono::high_resolution_clock::now(); duration = std::chrono::duration_cast(end - start_run_solver); From 73dffbe3f15026565201d4682e7e94621929d0c9 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 11:14:32 -0700 Subject: [PATCH 221/225] refactoring --- cpp/src/branch_and_bound/branch_and_bound.cpp | 25 ++++------- cpp/src/branch_and_bound/branch_and_bound.hpp | 1 + .../dual_simplex/simplex_solver_settings.hpp | 6 +-- cpp/src/mip_heuristics/diversity/lns/rins.cu | 9 ++-- .../mip_heuristics/diversity/population.cu | 13 +++--- .../diversity/recombiners/sub_mip.cuh | 9 ++-- cpp/src/mip_heuristics/solver.cu | 45 +++++++------------ cpp/src/mip_heuristics/solver_context.cuh | 21 --------- cpp/src/utilities/work_unit_predictor.hpp | 3 +- 9 files changed, 47 insertions(+), 85 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index e15c0966d3..02fb907fdb 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -494,20 +494,15 @@ void branch_and_bound_t::emit_solution_callback( double work_timestamp) { cuopt_assert(work_timestamp >= 0.0, "work_timestamp must not be negative"); - const size_t callback_count = (settings_.solution_callback != nullptr ? 1UL : 0UL) + - (settings_.solution_callback_ext != nullptr ? 1UL : 0UL); - settings_.log.debug("Publishing incumbent: obj=%g wut=%.6f origin=%s callbacks=%zu\n", - compute_user_objective(original_lp_, objective), - work_timestamp, - cuopt::internals::mip_solution_origin_to_string(origin), - callback_count); - if (settings_.solution_callback_ext != nullptr) { + if (settings_.new_incumbent_callback != nullptr) { + settings_.log.debug("Publishing incumbent: obj=%g wut=%.6f origin=%s\n", + compute_user_objective(original_lp_, objective), + work_timestamp, + cuopt::internals::mip_solution_origin_to_string(origin)); cuopt::internals::mip_solution_callback_info_t callback_info{}; callback_info.origin = origin; callback_info.work_timestamp = work_timestamp; - settings_.solution_callback_ext(original_x, objective, callback_info, work_timestamp); - } else if (settings_.solution_callback != nullptr) { - settings_.solution_callback(original_x, objective); + settings_.new_incumbent_callback(original_x, objective, callback_info, work_timestamp); } } @@ -518,9 +513,7 @@ void branch_and_bound_t::emit_solution_callback_from_crushed( cuopt::internals::mip_solution_origin_t origin, double work_timestamp) { - if (settings_.solution_callback == nullptr && settings_.solution_callback_ext == nullptr) { - return; - } + if (settings_.new_incumbent_callback == nullptr) { return; } std::vector original_x; uncrush_primal_solution(original_problem_, original_lp_, crushed_solution, original_x); emit_solution_callback(original_x, objective, origin, work_timestamp); @@ -941,8 +934,8 @@ void branch_and_bound_t::set_final_solution(mip_solution_t& } // Drain any pending heuristic solutions that B&B never got to retire during exploration - // (e.g., root solve consumed the entire time limit before tree exploration started). - if (settings_.deterministic && !incumbent_.has_incumbent) { + // (e.g., root solve consumed the entire budget, or exploration ended between sync horizons). + if (settings_.deterministic) { const double current_work = work_unit_context_.current_work(); mutex_heuristic_queue_.lock(); std::vector pending; diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index eefd6c8187..64aca268db 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -162,6 +162,7 @@ class branch_and_bound_t { std::vector& repaired_solution) const; f_t get_lower_bound(); + f_t get_upper_bound() const { return upper_bound_; } bool enable_concurrent_lp_root_solve() const { return enable_concurrent_lp_root_solve_; } std::atomic* get_root_concurrent_halt() { return &root_concurrent_halt_; } void set_root_concurrent_halt(int value) { root_concurrent_halt_ = value; } diff --git a/cpp/src/dual_simplex/simplex_solver_settings.hpp b/cpp/src/dual_simplex/simplex_solver_settings.hpp index 506c7623af..57514a7488 100644 --- a/cpp/src/dual_simplex/simplex_solver_settings.hpp +++ b/cpp/src/dual_simplex/simplex_solver_settings.hpp @@ -110,8 +110,7 @@ struct simplex_solver_settings_t { reliability_branching(-1), inside_mip(0), sub_mip(0), - solution_callback(nullptr), - solution_callback_ext(nullptr), + new_incumbent_callback(nullptr), heuristic_preemption_callback(nullptr), dual_simplex_objective_callback(nullptr), concurrent_halt(nullptr) @@ -205,10 +204,9 @@ struct simplex_solver_settings_t { i_t inside_mip; // 0 if outside MIP, 1 if inside MIP at root node, 2 if inside MIP at leaf node i_t sub_mip; // 0 if in regular MIP solve, 1 if in sub-MIP solve - std::function&, f_t)> solution_callback; std::function&, f_t, const cuopt::internals::mip_solution_callback_info_t&, double)> - solution_callback_ext; + new_incumbent_callback; std::function&, f_t)> node_processed_callback; std::function heuristic_preemption_callback; std::function&, std::vector&, f_t)> set_simplex_solution_callback; diff --git a/cpp/src/mip_heuristics/diversity/lns/rins.cu b/cpp/src/mip_heuristics/diversity/lns/rins.cu index afc89181ac..6c94c159f2 100644 --- a/cpp/src/mip_heuristics/diversity/lns/rins.cu +++ b/cpp/src/mip_heuristics/diversity/lns/rins.cu @@ -270,10 +270,11 @@ void rins_t::run_rins() branch_and_bound_settings.sub_mip = 1; branch_and_bound_settings.log.log = false; branch_and_bound_settings.log.log_prefix = "[RINS] "; - branch_and_bound_settings.solution_callback = [&rins_solution_queue](std::vector& solution, - f_t objective) { - rins_solution_queue.push_back(solution); - }; + branch_and_bound_settings.new_incumbent_callback = + [&rins_solution_queue](std::vector& solution, + f_t objective, + const cuopt::internals::mip_solution_callback_info_t&, + double) { rins_solution_queue.push_back(solution); }; dual_simplex::branch_and_bound_t branch_and_bound( branch_and_bound_problem, branch_and_bound_settings, dual_simplex::tic()); branch_and_bound.set_initial_guess(cuopt::host_copy(fixed_assignment, rins_handle.get_stream())); diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index adc667d1e8..2211caf28b 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -316,8 +316,13 @@ population_t::get_external_solutions() template bool population_t::is_better_than_best_feasible(solution_t& sol) { - bool obj_better = sol.get_objective() < best_feasible_objective; - return obj_better && sol.get_feasible(); + if (!sol.get_feasible()) { return false; } + f_t threshold = best_feasible_objective; + if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && + context.branch_and_bound_ptr != nullptr) { + threshold = context.branch_and_bound_ptr->get_upper_bound(); + } + return sol.get_objective() < threshold; } template @@ -333,15 +338,11 @@ void population_t::run_solution_callbacks( if (deterministic_callback_owner_is_bb) { cuopt_assert(sol.get_feasible(), "Deterministic heuristic posting requires a feasible solution"); - cuopt_assert(sol.get_objective() < best_feasible_objective, - "Deterministic heuristic posting must strictly improve best feasible objective"); const double work_timestamp = context.gpu_heur_loop.current_producer_work(); cuopt_assert(std::isfinite(work_timestamp), "Deterministic heuristic work timestamp must be finite"); context.branch_and_bound_ptr->queue_external_solution_deterministic( sol.get_host_assignment(), sol.get_user_objective(), work_timestamp, callback_origin); - // In deterministic mode, B&B replay is the single owner of GET_SOLUTION callback ordering. - best_feasible_objective = sol.get_objective(); } else { if (problem_ptr->branch_and_bound_callback != nullptr) { problem_ptr->branch_and_bound_callback(sol.get_host_assignment()); diff --git a/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh b/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh index e7f55aaae1..1b4e3f562b 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/sub_mip.cuh @@ -112,10 +112,11 @@ class sub_mip_recombiner_t : public recombiner_t { branch_and_bound_settings.max_cut_passes = 0; branch_and_bound_settings.clique_cuts = 0; branch_and_bound_settings.sub_mip = 1; - branch_and_bound_settings.solution_callback = [this](std::vector& solution, - f_t objective) { - this->solution_callback(solution, objective); - }; + branch_and_bound_settings.new_incumbent_callback = + [this](std::vector& solution, + f_t objective, + const cuopt::internals::mip_solution_callback_info_t&, + double) { this->solution_callback(solution, objective); }; // disable B&B logs, so that it is not interfering with the main B&B thread branch_and_bound_settings.log.log = false; diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index 1b01db8e1a..f03002f820 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -65,19 +65,10 @@ struct branch_and_bound_solution_helper_t { dual_simplex::simplex_solver_settings_t& settings) : dm(dm), settings_(settings) {}; - void solution_callback(std::vector& solution, f_t objective) - { - if (!settings_.deterministic) { - dm->population.add_external_solution( - solution, objective, internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE); - dm->rins.new_best_incumbent_callback(solution); - } - } - - void solution_callback_ext(std::vector& solution, - f_t objective, - const internals::mip_solution_callback_info_t& callback_info, - double work_timestamp) + void new_incumbent_callback(std::vector& solution, + f_t objective, + const internals::mip_solution_callback_info_t& callback_info, + double work_timestamp) { if (!settings_.deterministic) { dm->population.add_external_solution( @@ -91,14 +82,16 @@ struct branch_and_bound_solution_helper_t { cuopt_assert(solution.size() == (size_t)dm->context.problem_ptr->n_variables, "Deterministic B&B callback solution size mismatch"); cuopt_assert(std::isfinite(objective), "Deterministic B&B callback objective must be finite"); + solution_t temp_sol(*dm->context.problem_ptr); + temp_sol.copy_new_assignment(solution); const auto payload = - make_solution_callback_payload_from_host_solution(dm->context.problem_ptr, - dm->context.settings, - dm->context.gpu_heur_loop, - solution, - objective, - callback_info.origin, - work_timestamp); + make_solution_callback_payload_from_solution(dm->context.problem_ptr, + dm->context.settings, + dm->context.scaling, + dm->context.gpu_heur_loop, + temp_sol, + callback_info.origin, + work_timestamp); dm->context.solution_publication.publish_new_best_feasible(payload, dm->timer.elapsed_time()); } @@ -294,14 +287,8 @@ solution_t mip_solver_t::run_solver() } CUOPT_LOG_INFO("Using %d CPU threads for B&B", branch_and_bound_settings.num_threads); - // Set the branch and bound -> primal heuristics callback - branch_and_bound_settings.solution_callback = - std::bind(&branch_and_bound_solution_helper_t::solution_callback, - &solution_helper, - std::placeholders::_1, - std::placeholders::_2); - branch_and_bound_settings.solution_callback_ext = - std::bind(&branch_and_bound_solution_helper_t::solution_callback_ext, + branch_and_bound_settings.new_incumbent_callback = + std::bind(&branch_and_bound_solution_helper_t::new_incumbent_callback, &solution_helper, std::placeholders::_1, std::placeholders::_2, @@ -465,7 +452,7 @@ solution_t mip_solver_t::run_solver() } } } - sol = std::move(bb_sol); + if (bb_feasible) { sol = std::move(bb_sol); } } else if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { CUOPT_DETERMINISM_LOG( "Deterministic B&B overwrite skipped: bb_obj=%.16e bb_obj_finite=%d bb_has_incumbent=%d", diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index aa14c188b8..78b9b8a1f3 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -81,27 +81,6 @@ solution_callback_payload_t make_solution_callback_payload_from_soluti return payload; } -template -solution_callback_payload_t make_solution_callback_payload_from_host_solution( - problem_t* problem_ptr, - const mip_solver_settings_t& settings, - work_limit_context_t& gpu_heur_loop, - const std::vector& assignment, - f_t solver_objective, - internals::mip_solution_origin_t callback_origin, - double work_timestamp) -{ - cuopt_assert(problem_ptr != nullptr, "Callback payload problem pointer must not be null"); - cuopt_assert(work_timestamp >= 0.0, "work_timestamp must not be negative"); - solution_callback_payload_t payload{}; - payload.assignment = assignment; - payload.user_objective = problem_ptr->get_user_obj_from_solver_obj(solver_objective); - payload.solver_objective = solver_objective; - payload.callback_info.origin = callback_origin; - payload.callback_info.work_timestamp = work_timestamp; - return payload; -} - template class solution_publication_t { public: diff --git a/cpp/src/utilities/work_unit_predictor.hpp b/cpp/src/utilities/work_unit_predictor.hpp index c46db15fb5..9d445c437e 100644 --- a/cpp/src/utilities/work_unit_predictor.hpp +++ b/cpp/src/utilities/work_unit_predictor.hpp @@ -35,7 +35,8 @@ struct cpu_work_unit_scaler_t { { constexpr double baseline_max_clock = 3800.0; double max_clock = get_cpu_max_clock_mhz(); - scaling_factor_ = baseline_max_clock / max_clock; + if (max_clock <= 0.0) { max_clock = baseline_max_clock; } + scaling_factor_ = baseline_max_clock / max_clock; } double scale_work_units(double work_units) const { return work_units * scaling_factor_; } From 472f2b6c37c40f9eaaaa1893daffc7c3cc67d910 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 11:15:31 -0700 Subject: [PATCH 222/225] bump2 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index e851fb45a0..a8e9f7e9c5 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -605,7 +605,7 @@ int main(int argc, char* argv[]) sleep(1); } int remaining = paths.size() - tests_ran; - // wait for all processes to finish + // Wait for all processes to finish for (int i = 0; i < remaining; ++i) { return_gpu_to_the_queue(pid_gpu_map, pid_file_map, gpu_queue); } From 8d4e34b0e55ab531c332edba1a6647e20ccbf3ed Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Tue, 17 Mar 2026 11:26:40 -0700 Subject: [PATCH 223/225] mssing compute_feasibilty --- cpp/src/mip_heuristics/solver.cu | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index f03002f820..ba2b2cd8dc 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -84,6 +84,7 @@ struct branch_and_bound_solution_helper_t { cuopt_assert(std::isfinite(objective), "Deterministic B&B callback objective must be finite"); solution_t temp_sol(*dm->context.problem_ptr); temp_sol.copy_new_assignment(solution); + temp_sol.compute_feasibility(); const auto payload = make_solution_callback_payload_from_solution(dm->context.problem_ptr, dm->context.settings, From 1ef5fd9b71341a7b29e8e78bbb210dcbac309955 Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 18 Mar 2026 05:18:39 -0700 Subject: [PATCH 224/225] refactoring ahead of PR --- .../cuopt/linear_programming/constants.h | 21 +- .../cuopt/linear_programming/cuopt_c.h | 17 +- .../utilities/internals.hpp | 32 +- cpp/src/CMakeLists.txt | 6 - cpp/src/branch_and_bound/branch_and_bound.cpp | 227 +- cpp/src/branch_and_bound/branch_and_bound.hpp | 33 - cpp/src/branch_and_bound/pseudo_costs.cpp | 3 +- cpp/src/cuts/cuts.cpp | 151 - cpp/src/dual_simplex/bounds_strengthening.hpp | 1 - .../diversity/diversity_manager.cu | 11 +- .../diversity/multi_armed_bandit.cuh | 2 +- .../mip_heuristics/diversity/population.cu | 158 +- .../mip_heuristics/diversity/population.cuh | 2 - .../diversity/recombiners/recombiner.cuh | 29 +- .../feasibility_jump_impl_common.cuh | 16 +- .../feasibility_pump/feasibility_pump.cu | 3 +- .../local_search/local_search.cu | 36 +- .../local_search/local_search.cuh | 6 - .../local_search/rounding/bounds_repair.cu | 33 +- .../local_search/rounding/constraint_prop.cu | 168 +- .../mip_heuristics/presolve/probing_cache.cu | 26 +- .../presolve/third_party_presolve.cpp | 1 + .../mip_heuristics/relaxed_lp/relaxed_lp.cu | 53 +- cpp/src/mip_heuristics/solution/solution.cu | 16 - cpp/src/mip_heuristics/solution/solution.cuh | 4 - cpp/src/mip_heuristics/solve.cu | 36 +- cpp/src/mip_heuristics/solver.cu | 160 +- cpp/src/mip_heuristics/solver.cuh | 1 + cpp/src/mip_heuristics/solver_context.cuh | 174 +- cpp/src/pdlp/cuopt_c.cpp | 2 +- cpp/src/pdlp/pdlp.cu | 34 - cpp/src/pdlp/pdlp.cuh | 2 - cpp/src/pdlp/pdlp_features.hpp | 243 - .../restart_strategy/pdlp_restart_strategy.cu | 3 - .../infeasibility_information.cu | 32 +- cpp/src/utilities/embed_models.sh | 89 - .../utilities/models/cpufj_predictor/header.h | 35 - .../utilities/models/cpufj_predictor/main.cpp | 5412 ----- .../models/cpufj_predictor/quantize.cpp | 293 - .../models/dualsimplex_predictor/header.h | 35 - .../models/dualsimplex_predictor/main.cpp | 19265 ---------------- .../models/dualsimplex_predictor/quantize.cpp | 1891 -- .../utilities/models/pdlp_predictor/header.h | 35 - .../utilities/models/pdlp_predictor/main.cpp | 16893 -------------- .../models/pdlp_predictor/quantize.cpp | 1006 - cpp/src/utilities/seed_generator.cu | 1 + cpp/src/utilities/seed_generator.cuh | 17 +- cpp/src/utilities/timer.hpp | 16 +- cpp/src/utilities/work_unit_predictor.cpp | 7 - cpp/tsan_suppressions.txt | 6 - scripts/README_PREDICTOR_WORKFLOW.md | 214 - scripts/README_REGRESSION.md | 354 - scripts/determinism_logs_parse.py | 925 - scripts/train_regressor.py | 2221 -- 54 files changed, 374 insertions(+), 50083 deletions(-) delete mode 100644 cpp/src/pdlp/pdlp_features.hpp delete mode 100755 cpp/src/utilities/embed_models.sh delete mode 100644 cpp/src/utilities/models/cpufj_predictor/header.h delete mode 100644 cpp/src/utilities/models/cpufj_predictor/main.cpp delete mode 100644 cpp/src/utilities/models/cpufj_predictor/quantize.cpp delete mode 100644 cpp/src/utilities/models/dualsimplex_predictor/header.h delete mode 100644 cpp/src/utilities/models/dualsimplex_predictor/main.cpp delete mode 100644 cpp/src/utilities/models/dualsimplex_predictor/quantize.cpp delete mode 100644 cpp/src/utilities/models/pdlp_predictor/header.h delete mode 100644 cpp/src/utilities/models/pdlp_predictor/main.cpp delete mode 100644 cpp/src/utilities/models/pdlp_predictor/quantize.cpp delete mode 100644 cpp/tsan_suppressions.txt delete mode 100644 scripts/README_PREDICTOR_WORKFLOW.md delete mode 100644 scripts/README_REGRESSION.md delete mode 100755 scripts/determinism_logs_parse.py delete mode 100755 scripts/train_regressor.py diff --git a/cpp/include/cuopt/linear_programming/constants.h b/cpp/include/cuopt/linear_programming/constants.h index fd941fe098..a1584dd44b 100644 --- a/cpp/include/cuopt/linear_programming/constants.h +++ b/cpp/include/cuopt/linear_programming/constants.h @@ -78,8 +78,10 @@ #define CUOPT_PDLP_PRECISION "pdlp_precision" /* @brief MIP determinism mode flags (bitset) */ -#define CUOPT_DETERMINISM_NONE 0x0 -#define CUOPT_DETERMINISM_BB 0x1 +#define CUOPT_DETERMINISM_NONE 0x0 +#define CUOPT_DETERMINISM_BB \ + 0x1 // matches the previous value of '1' which was for B&B-only determinism in the previous + // rleease #define CUOPT_DETERMINISM_GPU_HEURISTICS 0x2 #define CUOPT_DETERMINISM_FULL (CUOPT_DETERMINISM_BB | CUOPT_DETERMINISM_GPU_HEURISTICS) @@ -89,6 +91,21 @@ #define CUOPT_MODE_DETERMINISTIC_BB CUOPT_DETERMINISM_BB #define CUOPT_MODE_DETERMINISTIC_GPU_HEURISTICS CUOPT_DETERMINISM_GPU_HEURISTICS +/* @brief MIP solution origin constants */ +#define CUOPT_MIP_SOLUTION_ORIGIN_UNKNOWN 0 +#define CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND 1 +#define CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND_DIVING 2 +#define CUOPT_MIP_SOLUTION_ORIGIN_FEASIBILITY_JUMP 3 +#define CUOPT_MIP_SOLUTION_ORIGIN_CPU_FEASIBILITY_JUMP 4 +#define CUOPT_MIP_SOLUTION_ORIGIN_LOCAL_SEARCH 5 +#define CUOPT_MIP_SOLUTION_ORIGIN_QUICK_FEASIBLE 6 +#define CUOPT_MIP_SOLUTION_ORIGIN_LP_ROUNDING 7 +#define CUOPT_MIP_SOLUTION_ORIGIN_RECOMBINATION 8 +#define CUOPT_MIP_SOLUTION_ORIGIN_SUB_MIP 9 +#define CUOPT_MIP_SOLUTION_ORIGIN_USER_INITIAL 10 +#define CUOPT_MIP_SOLUTION_ORIGIN_USER_INJECTED 11 +#define CUOPT_MIP_SOLUTION_ORIGIN_RINS 12 + /* @brief LP/MIP termination status constants */ #define CUOPT_TERIMINATION_STATUS_NO_TERMINATION 0 #define CUOPT_TERIMINATION_STATUS_OPTIMAL 1 diff --git a/cpp/include/cuopt/linear_programming/cuopt_c.h b/cpp/include/cuopt/linear_programming/cuopt_c.h index b0239e19f3..d49cd703af 100644 --- a/cpp/include/cuopt/linear_programming/cuopt_c.h +++ b/cpp/include/cuopt/linear_programming/cuopt_c.h @@ -71,24 +71,9 @@ typedef int32_t cuopt_int_t; typedef int64_t cuopt_int_t; #endif -typedef enum { - CUOPT_MIP_SOLUTION_ORIGIN_UNKNOWN = 0, - CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND_NODE = 1, - CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND = CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND_NODE, - CUOPT_MIP_SOLUTION_ORIGIN_FEASIBILITY_JUMP = 2, - CUOPT_MIP_SOLUTION_ORIGIN_LOCAL_SEARCH = 3, - CUOPT_MIP_SOLUTION_ORIGIN_QUICK_FEASIBLE = 4, - CUOPT_MIP_SOLUTION_ORIGIN_USER_INITIAL = 5, - CUOPT_MIP_SOLUTION_ORIGIN_LP_ROUNDING = 6, - CUOPT_MIP_SOLUTION_ORIGIN_RECOMBINATION = 7, - CUOPT_MIP_SOLUTION_ORIGIN_SUB_MIP = 8, - CUOPT_MIP_SOLUTION_ORIGIN_CPU_FEASIBILITY_JUMP = 9, - CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND_DIVING = 10, -} cuOptMIPSolutionOrigin; - typedef struct { uint64_t struct_size; - cuOptMIPSolutionOrigin origin; + uint32_t origin; double work_timestamp; } cuOptMIPSolutionCallbackInfo; diff --git a/cpp/include/cuopt/linear_programming/utilities/internals.hpp b/cpp/include/cuopt/linear_programming/utilities/internals.hpp index 382553f6d3..3af713fd2f 100644 --- a/cpp/include/cuopt/linear_programming/utilities/internals.hpp +++ b/cpp/include/cuopt/linear_programming/utilities/internals.hpp @@ -14,6 +14,8 @@ #include #include +#include + namespace cuopt { namespace internals { @@ -23,19 +25,19 @@ class Callback { }; enum class mip_solution_origin_t : uint32_t { - UNKNOWN = 0, - BRANCH_AND_BOUND_NODE, - BRANCH_AND_BOUND = BRANCH_AND_BOUND_NODE, - FEASIBILITY_JUMP, - LOCAL_SEARCH, - QUICK_FEASIBLE, - USER_INITIAL, - LP_ROUNDING, - RECOMBINATION, - SUB_MIP, - CPU_FEASIBILITY_JUMP, - BRANCH_AND_BOUND_DIVING, - RINS, + UNKNOWN = CUOPT_MIP_SOLUTION_ORIGIN_UNKNOWN, + BRANCH_AND_BOUND_NODE = CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND, + BRANCH_AND_BOUND_DIVING = CUOPT_MIP_SOLUTION_ORIGIN_BRANCH_AND_BOUND_DIVING, + FEASIBILITY_JUMP = CUOPT_MIP_SOLUTION_ORIGIN_FEASIBILITY_JUMP, + CPU_FEASIBILITY_JUMP = CUOPT_MIP_SOLUTION_ORIGIN_CPU_FEASIBILITY_JUMP, + LOCAL_SEARCH = CUOPT_MIP_SOLUTION_ORIGIN_LOCAL_SEARCH, + QUICK_FEASIBLE = CUOPT_MIP_SOLUTION_ORIGIN_QUICK_FEASIBLE, + LP_ROUNDING = CUOPT_MIP_SOLUTION_ORIGIN_LP_ROUNDING, + RECOMBINATION = CUOPT_MIP_SOLUTION_ORIGIN_RECOMBINATION, + SUB_MIP = CUOPT_MIP_SOLUTION_ORIGIN_SUB_MIP, + USER_INITIAL = CUOPT_MIP_SOLUTION_ORIGIN_USER_INITIAL, + USER_INJECTED = CUOPT_MIP_SOLUTION_ORIGIN_USER_INJECTED, + RINS = CUOPT_MIP_SOLUTION_ORIGIN_RINS, }; constexpr const char* mip_solution_origin_to_string(mip_solution_origin_t origin) @@ -52,6 +54,8 @@ constexpr const char* mip_solution_origin_to_string(mip_solution_origin_t origin case mip_solution_origin_t::RECOMBINATION: return "recombination"; case mip_solution_origin_t::SUB_MIP: return "sub_mip"; case mip_solution_origin_t::CPU_FEASIBILITY_JUMP: return "cpu_feasibility_jump"; + case mip_solution_origin_t::USER_INJECTED: return "user_injected"; + case mip_solution_origin_t::RINS: return "rins"; default: return "unknown"; } } @@ -62,6 +66,8 @@ struct mip_solution_callback_info_t { double work_timestamp{-1.0}; }; +// get_solution_ext was added to support passing additional information to the get_solution callback +// without inducing a breaking ABI change enum class base_solution_callback_type { GET_SOLUTION, GET_SOLUTION_EXT, SET_SOLUTION }; class base_solution_callback_t : public Callback { diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 82828477a5..411db36289 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -7,14 +7,8 @@ set(UTIL_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/utilities/seed_generator.cu ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/version_info.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/timestamp_utils.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/cpufj_predictor/quantize.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/dualsimplex_predictor/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/dualsimplex_predictor/quantize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/fj_predictor/quantize.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/pdlp_predictor/main.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/utilities/models/pdlp_predictor/quantize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_predictor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utilities/work_unit_scheduler.cpp) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 02fb907fdb..5c2d62289d 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -702,24 +702,23 @@ bool branch_and_bound_t::repair_solution(const std::vector& edge_ f_t primal_error; f_t bound_error; i_t num_fractional; - feasible = check_guess(original_lp_, + feasible = check_guess(original_lp_, settings_, var_types_, lp_solution.x, primal_error, bound_error, num_fractional); - repaired_obj = compute_objective(original_lp_, repaired_solution); - if (!feasible) { + repaired_obj = compute_objective(original_lp_, repaired_solution); + constexpr bool verbose = false; + if (verbose) { settings_.log.printf( - "Repair LP optimal but check_guess failed: primal_err=%e bound_err=%e fractional=%d " - "obj=%e iters=%d time=%.3fs\n", + "After repair: feasible %d primal error %e bound error %e fractional %d. Objective %e\n", + feasible, primal_error, bound_error, num_fractional, - repaired_obj, - iter, - toc(lp_start_time)); + repaired_obj); } } else { settings_.log.printf( @@ -792,17 +791,8 @@ void branch_and_bound_t::set_solution_at_root(mip_solution_t const cut_info_t& cut_info) { mutex_upper_.lock(); - const f_t previous_upper = upper_bound_; incumbent_.set_incumbent_solution(root_objective_, root_relax_soln_.x); upper_bound_ = root_objective_; - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic B&B incumbent update: source=root_solution prev_upper=%.16e new_upper=%.16e " - "obj=%.16e hash=0x%x\n", - previous_upper, - upper_bound_.load(), - root_objective_, - detail::compute_hash(root_relax_soln_.x)); mutex_upper_.unlock(); print_cut_info(settings_, cut_info); @@ -2721,26 +2711,6 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut f_t add_cuts_start_time = tic(); mutex_original_lp_.lock(); - assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic root LP before add_cuts: pass=%d fractional=%d num_cuts=%d rows=%d " - "cols=%d nnz=%zu slacks=%zu slack_hash=0x%x rhs_hash=0x%x lower_hash=0x%x " - "upper_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x Aval_hash=0x%x\n", - cut_pass, - num_fractional, - num_cuts, - original_lp_.num_rows, - original_lp_.num_cols, - original_lp_.A.x.size(), - new_slacks_.size(), - detail::compute_hash(new_slacks_), - detail::compute_hash(original_lp_.rhs), - detail::compute_hash(original_lp_.lower), - detail::compute_hash(original_lp_.upper), - detail::compute_hash(original_lp_.A.col_start), - detail::compute_hash(original_lp_.A.i), - detail::compute_hash(original_lp_.A.x)); i_t add_cuts_status = add_cuts(settings_, cuts_to_add, cut_rhs, @@ -2754,27 +2724,6 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut edge_norms_); var_types_.resize(original_lp_.num_cols, variable_type_t::CONTINUOUS); variable_bounds.resize(original_lp_.num_cols); - assert(new_slacks_.size() == static_cast(original_lp_.num_rows)); - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic root LP after add_cuts: pass=%d fractional=%d num_cuts=%d status=%d " - "rows=%d cols=%d nnz=%zu slacks=%zu slack_hash=0x%x rhs_hash=0x%x lower_hash=0x%x " - "upper_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x Aval_hash=0x%x\n", - cut_pass, - num_fractional, - num_cuts, - add_cuts_status, - original_lp_.num_rows, - original_lp_.num_cols, - original_lp_.A.x.size(), - new_slacks_.size(), - detail::compute_hash(new_slacks_), - detail::compute_hash(original_lp_.rhs), - detail::compute_hash(original_lp_.lower), - detail::compute_hash(original_lp_.upper), - detail::compute_hash(original_lp_.A.col_start), - detail::compute_hash(original_lp_.A.i), - detail::compute_hash(original_lp_.A.x)); mutex_original_lp_.unlock(); f_t add_cuts_time = toc(add_cuts_start_time); if (add_cuts_time > 1.0) { @@ -3397,7 +3346,6 @@ void branch_and_bound_t::run_deterministic_coordinator(const csr_matri deterministic_horizon_step_ = 0.50; - // In deterministic mode, honor the user-visible B&B thread budget exactly. const i_t num_workers = settings_.num_threads; std::vector search_strategies = get_search_strategies(settings_.diving_settings); @@ -3577,43 +3525,10 @@ void branch_and_bound_t::run_deterministic_bfs_loop( if (node == nullptr) { continue; } worker.current_node = node; - if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9 && - worker.total_nodes_processed < 16) { - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic BFS dequeue: horizon=%.6f worker=%d node_id=%d packed=0x%llx " - "path_hash=0x%x depth=%d lower=%.16e local_upper=%.16e clock=%.6f queue_size=%zu\n", - deterministic_current_horizon_, - worker.worker_id, - node->creation_seq, - (unsigned long long)node->get_id_packed(), - node->compute_path_hash(), - node->depth, - node->lower_bound, - worker.local_upper_bound, - worker.clock, - worker.queue_size()); - } f_t upper_bound = worker.local_upper_bound; f_t rel_gap = user_relative_gap(original_lp_, upper_bound, node->lower_bound); if (node->lower_bound > upper_bound) { - if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic BFS prune vs local upper: horizon=%.6f worker=%d node_id=%d " - "packed=0x%llx " - "path_hash=0x%x depth=%d node_lower=%.16e local_upper=%.16e clock=%.6f\n", - deterministic_current_horizon_, - worker.worker_id, - node->creation_seq, - (unsigned long long)node->get_id_packed(), - node->compute_path_hash(), - node->depth, - node->lower_bound, - upper_bound, - worker.clock); - } worker.current_node = nullptr; worker.record_fathomed(node, node->lower_bound); search_tree.update(node, node_status_t::FATHOMED); @@ -3685,56 +3600,6 @@ void branch_and_bound_t::deterministic_sync_callback() } bb_event_batch_t all_events = deterministic_workers_->collect_and_sort_events(); - if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9) { - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic sync start: horizon_idx=%d horizon_end=%.6f global_upper=%.16e " - "nodes_explored=%d nodes_unexplored=%lu event_count=%zu heuristic_queue=%zu\n", - deterministic_horizon_number_, - horizon_end, - upper_bound_.load(), - exploration_stats_.nodes_explored.load(), - exploration_stats_.nodes_unexplored.load(), - all_events.events.size(), - heuristic_solution_queue_.size()); - for (size_t i = 0; i < all_events.events.size(); ++i) { - const auto& event = all_events.events[i]; - if (event.type != bb_event_type_t::NODE_INTEGER) { continue; } - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic sync integer event[%zu]: wut=%.6f worker=%d node_id=%d event_seq=%d " - "obj=%.16e\n", - i, - event.work_timestamp, - event.worker_id, - event.node_id, - event.event_sequence, - event.payload.integer_solution.objective_value); - } - for (const auto& worker : *deterministic_workers_) { - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic BFS horizon summary: horizon=%.6f worker=%d clock=%.16e " - "nodes_processed_h=%d total_nodes=%d integer_queue=%zu next_creation_seq=%d " - "next_solution_seq=%d queue_size=%zu current_node=0x%llx last_solved=0x%llx " - "local_upper=%.16e\n", - deterministic_current_horizon_, - worker.worker_id, - worker.clock, - worker.nodes_processed_this_horizon, - worker.total_nodes_processed, - worker.integer_solutions.size(), - worker.next_creation_seq, - worker.next_solution_seq, - worker.queue_size(), - worker.current_node != nullptr ? (unsigned long long)worker.current_node->get_id_packed() - : 0ULL, - worker.last_solved_node != nullptr - ? (unsigned long long)worker.last_solved_node->get_id_packed() - : 0ULL, - worker.local_upper_bound); - } - } std::vector::deterministic_replay_solution_t> replay_solutions; @@ -3919,6 +3784,7 @@ node_status_t branch_and_bound_t::solve_node_deterministic( lp_settings, worker.bounds_changed, worker.leaf_problem.lower, worker.leaf_problem.upper); if (settings_.deterministic) { + // TEMP APPROXIMATION; worker.work_context.record_work_sync_on_horizon(worker.node_presolver.last_nnz_processed / 1e8); } #endif @@ -3986,27 +3852,6 @@ node_status_t branch_and_bound_t::solve_node_deterministic( deterministic_bfs_policy_t policy{*this, worker}; auto [status, round_dir] = update_tree_impl(node_ptr, search_tree, &worker, lp_status, policy); - if (deterministic_current_horizon_ <= deterministic_horizon_step_ + 1e-9 && - (status == node_status_t::HAS_CHILDREN || status == node_status_t::INTEGER_FEASIBLE)) { - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic BFS solve progress: horizon=%.6f worker=%d node_packed=0x%llx " - "node_path_hash=0x%x depth=%d lp_status=%d status=%d node_iter=%d work=%.16e " - "clock_before=%.16e clock_after=%.16e local_upper=%.16e node_lower=%.16e\n", - deterministic_current_horizon_, - worker.worker_id, - (unsigned long long)node_ptr->get_id_packed(), - node_ptr->compute_path_hash(), - node_ptr->depth, - (int)lp_status, - (int)status, - node_iter, - work_performed, - clock_before, - worker.clock, - worker.local_upper_bound, - node_ptr->lower_bound); - } return status; } @@ -4217,11 +4062,6 @@ void branch_and_bound_t::deterministic_sort_replay_events( if (process_event) { const auto& event = events.events[event_idx++]; - /* - settings_.log.printf("Deterministic replay event: type=%d wut=%.6f worker=%d node=%d - seq=%d\n", (int)event.type, event.work_timestamp, event.worker_id, event.node_id, - event.event_sequence); - */ switch (event.type) { case bb_event_type_t::NODE_INTEGER: case bb_event_type_t::NODE_BRANCHED: @@ -4350,33 +4190,46 @@ void branch_and_bound_t::deterministic_balance_worker_loads() const size_t num_workers = deterministic_workers_->size(); if (num_workers <= 1) return; - std::vector*> all_nodes; + constexpr bool force_rebalance_every_sync = false; + + std::vector backlog_counts(num_workers); + size_t total_backlog = 0; + size_t max_backlog = 0; + size_t min_backlog = std::numeric_limits::max(); + + for (size_t w = 0; w < num_workers; ++w) { + auto& worker = (*deterministic_workers_)[w]; + backlog_counts[w] = worker.backlog.size(); + total_backlog += backlog_counts[w]; + max_backlog = std::max(max_backlog, backlog_counts[w]); + min_backlog = std::min(min_backlog, backlog_counts[w]); + } + if (total_backlog == 0) return; + + bool needs_balance; + if (force_rebalance_every_sync) { + needs_balance = (total_backlog > 1); + } else { + needs_balance = + (min_backlog == 0 && max_backlog >= 2) || (min_backlog > 0 && max_backlog > 4 * min_backlog); + } + + if (!needs_balance) return; + + std::vector*> all_backlog_nodes; for (auto& worker : *deterministic_workers_) { - for (auto* node : worker.plunge_stack) { - all_nodes.push_back(node); - } - worker.plunge_stack.clear(); for (auto* node : worker.backlog.data()) { - all_nodes.push_back(node); + all_backlog_nodes.push_back(node); } worker.backlog.clear(); } - if (all_nodes.empty()) return; - - std::sort(all_nodes.begin(), - all_nodes.end(), - [](const mip_node_t* a, const mip_node_t* b) { - if (a->lower_bound != b->lower_bound) { return a->lower_bound < b->lower_bound; } - if (a->origin_worker_id != b->origin_worker_id) { - return a->origin_worker_id < b->origin_worker_id; - } - return a->creation_seq < b->creation_seq; - }); + if (all_backlog_nodes.empty()) return; - for (int i = (int)all_nodes.size() - 1; i >= 0; --i) { + // Round-robin distribute into backlogs; priority queue handles ordering internally + for (size_t i = 0; i < all_backlog_nodes.size(); ++i) { size_t worker_idx = i % num_workers; - (*deterministic_workers_)[worker_idx].enqueue_node(all_nodes[i]); + (*deterministic_workers_)[worker_idx].backlog.push(all_backlog_nodes[i]); } } diff --git a/cpp/src/branch_and_bound/branch_and_bound.hpp b/cpp/src/branch_and_bound/branch_and_bound.hpp index 64aca268db..ad8a0a88bd 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.hpp +++ b/cpp/src/branch_and_bound/branch_and_bound.hpp @@ -93,22 +93,7 @@ class branch_and_bound_t { f_t user_objective, i_t iterations) { - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic root callback enter: guard=%d crossover_flag=%d current_root_obj=%.16e " - "callback_solver_obj=%.16e callback_user_obj=%.16e primal_size=%zu dual_size=%zu " - "reduced_cost_size=%zu iterations=%d\n", - (int)is_root_solution_set, - (int)root_crossover_solution_set_.load(std::memory_order_acquire), - root_objective_, - objective, - user_objective, - primal.size(), - dual.size(), - reduced_costs.size(), - iterations); if (!is_root_solution_set) { - const f_t previous_root_objective = root_objective_; root_crossover_soln_.x = primal; root_crossover_soln_.y = dual; root_crossover_soln_.z = reduced_costs; @@ -117,24 +102,6 @@ class branch_and_bound_t { root_crossover_soln_.user_objective = user_objective; root_crossover_soln_.iterations = iterations; root_crossover_solution_set_.store(true, std::memory_order_release); - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic root callback accept: root_obj_before=%.16e root_obj_after=%.16e " - "callback_solver_obj=%.16e callback_user_obj=%.16e iterations=%d\n", - previous_root_objective, - root_objective_, - objective, - user_objective, - iterations); - } else { - CUOPT_DETERMINISM_LOG( - settings_.log, - "Deterministic root callback ignore: current_root_obj=%.16e callback_solver_obj=%.16e " - "callback_user_obj=%.16e iterations=%d\n", - root_objective_, - objective, - user_objective, - iterations); } } diff --git a/cpp/src/branch_and_bound/pseudo_costs.cpp b/cpp/src/branch_and_bound/pseudo_costs.cpp index 189f5804ab..7be411ac7b 100644 --- a/cpp/src/branch_and_bound/pseudo_costs.cpp +++ b/cpp/src/branch_and_bound/pseudo_costs.cpp @@ -678,7 +678,6 @@ void strong_branching(const user_problem_t& original_problem, const i_t n_tasks = std::min(4 * settings.num_threads, fractional.size()); std::vector task_work_contexts; if (use_work_accounting) { - task_work_contexts.reserve(n_tasks); for (i_t k = 0; k < n_tasks; ++k) { task_work_contexts.emplace_back("sb_task_" + std::to_string(k)); task_work_contexts.back().deterministic = true; @@ -721,6 +720,8 @@ void strong_branching(const user_problem_t& original_problem, } } + // record pre-exploration work by taking the max work performed by any task + // important to aggregate by task and not thread, as openmp uses dynamic scheduling here if (use_work_accounting) { double max_work = 0.0; for (auto& ctx : task_work_contexts) { diff --git a/cpp/src/cuts/cuts.cpp b/cpp/src/cuts/cuts.cpp index 2753efe687..c74a12b36b 100644 --- a/cpp/src/cuts/cuts.cpp +++ b/cpp/src/cuts/cuts.cpp @@ -18,8 +18,6 @@ #include #include -#include -#include #include #include @@ -46,86 +44,6 @@ enum class clique_cut_build_status_t : int8_t { NO_CUT = 0, CUT_ADDED = 1, INFEA } while (0) #endif -namespace { - -inline uint32_t hash_combine(uint32_t hash, uint32_t value) -{ - hash ^= value; - hash *= 16777619u; - return hash; -} - -template -uint32_t compute_sparse_cut_hash(const sparse_vector_t& cut, f_t rhs, cut_type_t cut_type) -{ - uint32_t hash = 2166136261u; - hash = hash_combine(hash, cuopt::linear_programming::detail::compute_hash(cut.i)); - hash = hash_combine(hash, cuopt::linear_programming::detail::compute_hash(cut.x)); - hash = hash_combine(hash, cuopt::linear_programming::detail::compute_hash(rhs)); - hash = hash_combine( - hash, cuopt::linear_programming::detail::compute_hash(static_cast(cut_type))); - return hash; -} - -template -uint32_t compute_stored_cut_hash(const csr_matrix_t& cut_storage, - const std::vector& rhs_storage, - const std::vector& cut_types, - i_t row) -{ - sparse_vector_t cut(cut_storage, row); - return compute_sparse_cut_hash(cut, rhs_storage[row], cut_types[row]); -} - -template -uint32_t compute_index_order_hash(const std::vector& ordered_indices) -{ - uint32_t hash = 2166136261u; - for (auto idx : ordered_indices) { - hash = hash_combine(hash, cuopt::linear_programming::detail::compute_hash(idx)); - } - return hash; -} - -uint32_t compute_ordered_hash_list_hash(const std::vector& ordered_hashes) -{ - uint32_t hash = 2166136261u; - for (auto cut_hash : ordered_hashes) { - hash = hash_combine(hash, cut_hash); - } - return hash; -} - -template -void log_lp_state_summary(const simplex_solver_settings_t& settings, - const char* label, - const lp_problem_t& lp, - const std::vector& new_slacks, - i_t cuts_delta) -{ - assert(new_slacks.size() == static_cast(lp.num_rows)); - - CUOPT_DETERMINISM_LOG( - settings.log, - "%s: cuts_delta=%d rows=%d cols=%d nnz=%zu slacks=%zu slack_hash=0x%x rhs_hash=0x%x " - "lower_hash=0x%x upper_hash=0x%x Acol_hash=0x%x Arow_hash=0x%x Aval_hash=0x%x\n", - label, - cuts_delta, - lp.num_rows, - lp.num_cols, - lp.A.x.size(), - new_slacks.size(), - cuopt::linear_programming::detail::compute_hash(new_slacks), - cuopt::linear_programming::detail::compute_hash(lp.rhs), - cuopt::linear_programming::detail::compute_hash(lp.lower), - cuopt::linear_programming::detail::compute_hash(lp.upper), - cuopt::linear_programming::detail::compute_hash(lp.A.col_start), - cuopt::linear_programming::detail::compute_hash(lp.A.i), - cuopt::linear_programming::detail::compute_hash(lp.A.x)); -} - -} // namespace - template clique_cut_build_status_t build_clique_cut(const std::vector& clique_vertices, i_t num_vars, @@ -678,40 +596,6 @@ void cut_pool_t::score_cuts(std::vector& x_relax) std::vector sorted_indices; best_score_last_permutation(cut_distances_, sorted_indices); - if (!sorted_indices.empty()) { - std::vector candidate_order; - candidate_order.reserve(sorted_indices.size()); - std::vector candidate_cut_hashes; - candidate_cut_hashes.reserve(sorted_indices.size()); - for (auto it = sorted_indices.rbegin(); it != sorted_indices.rend(); ++it) { - const i_t cut_idx = *it; - candidate_order.push_back(cut_idx); - candidate_cut_hashes.push_back( - compute_stored_cut_hash(cut_storage_, rhs_storage_, cut_type_, cut_idx)); - } - - const uint32_t candidate_order_hash = compute_index_order_hash(candidate_order); - const uint32_t candidate_cut_hash = compute_ordered_hash_list_hash(candidate_cut_hashes); - CUOPT_DETERMINISM_LOG(settings_.log, - "Deterministic cut candidate ranking: cuts=%zu order_hash=0x%x " - "cut_hash=0x%x top=", - candidate_order.size(), - candidate_order_hash, - candidate_cut_hash); - const size_t max_top_cuts = std::min((size_t)8, candidate_order.size()); - for (size_t k = 0; k < max_top_cuts; ++k) { - const i_t cut_idx = candidate_order[k]; - CUOPT_DETERMINISM_LOG(settings_.log, - "%s[%d type=%d dist=%.16e hash=0x%x]", - k == 0 ? "" : " ", - cut_idx, - static_cast(cut_type_[cut_idx]), - cut_distances_[cut_idx], - candidate_cut_hashes[k]); - } - CUOPT_DETERMINISM_LOG(settings_.log, "\n"); - } - const i_t max_cuts = 2000; const f_t min_orthogonality = settings_.cut_min_orthogonality; best_cuts_.reserve(std::min(max_cuts, cut_storage_.m)); @@ -742,32 +626,6 @@ void cut_pool_t::score_cuts(std::vector& x_relax) scored_cuts_++; } } - - if (!best_cuts_.empty()) { - std::vector selected_cut_hashes; - selected_cut_hashes.reserve(best_cuts_.size()); - for (auto cut_idx : best_cuts_) { - selected_cut_hashes.push_back( - compute_stored_cut_hash(cut_storage_, rhs_storage_, cut_type_, cut_idx)); - } - const uint32_t selected_cut_hash = compute_ordered_hash_list_hash(selected_cut_hashes); - CUOPT_DETERMINISM_LOG(settings_.log, - "Deterministic cut selection summary: selected=%zu order_hash=0x%x top=", - best_cuts_.size(), - selected_cut_hash); - const size_t max_selected_cuts = std::min((size_t)8, best_cuts_.size()); - for (size_t k = 0; k < max_selected_cuts; ++k) { - const i_t cut_idx = best_cuts_[k]; - CUOPT_DETERMINISM_LOG(settings_.log, - "%s[%d type=%d dist=%.16e hash=0x%x]", - k == 0 ? "" : " ", - cut_idx, - static_cast(cut_type_[cut_idx]), - cut_distances_[cut_idx], - selected_cut_hashes[k]); - } - CUOPT_DETERMINISM_LOG(settings_.log, "\n"); - } } template @@ -3683,8 +3541,6 @@ i_t add_cuts(const simplex_solver_settings_t& settings, // by the current solution x* (i.e. C*x* > d), this function // adds the cuts into the LP and solves again. - log_lp_state_summary(settings, "Deterministic add_cuts state before", lp, new_slacks, cuts.m); - #ifdef CHECK_BASIS { csc_matrix_t Btest(lp.num_rows, lp.num_rows, 1); @@ -3861,8 +3717,6 @@ i_t add_cuts(const simplex_solver_settings_t& settings, solution.y.resize(lp.num_rows, 0.0); solution.z.resize(lp.num_cols, 0.0); - log_lp_state_summary(settings, "Deterministic add_cuts state after", lp, new_slacks, cuts.m); - return 0; } @@ -3883,8 +3737,6 @@ i_t remove_cuts(lp_problem_t& lp, std::vector& nonbasic_list, basis_update_mpf_t& basis_update) { - log_lp_state_summary(settings, "Deterministic remove_cuts state before", lp, new_slacks, 0); - std::vector cuts_to_remove; cuts_to_remove.reserve(lp.num_rows - original_rows); std::vector slacks_to_remove; @@ -4019,9 +3871,6 @@ i_t remove_cuts(lp_problem_t& lp, if (refactor_status == TIME_LIMIT_RETURN) { return TIME_LIMIT_RETURN; } } - log_lp_state_summary( - settings, "Deterministic remove_cuts state after", lp, new_slacks, (i_t)cuts_to_remove.size()); - return 0; } diff --git a/cpp/src/dual_simplex/bounds_strengthening.hpp b/cpp/src/dual_simplex/bounds_strengthening.hpp index 55fdb36866..009f7b2433 100644 --- a/cpp/src/dual_simplex/bounds_strengthening.hpp +++ b/cpp/src/dual_simplex/bounds_strengthening.hpp @@ -8,7 +8,6 @@ #pragma once #include -#include namespace cuopt::linear_programming::dual_simplex { diff --git a/cpp/src/mip_heuristics/diversity/diversity_manager.cu b/cpp/src/mip_heuristics/diversity/diversity_manager.cu index 0831f9ab38..2ee17090c0 100644 --- a/cpp/src/mip_heuristics/diversity/diversity_manager.cu +++ b/cpp/src/mip_heuristics/diversity/diversity_manager.cu @@ -427,12 +427,15 @@ solution_t diversity_manager_t::run_solver() sol.get_total_excess()); }; - bool bb_only = (context.settings.determinism_mode == CUOPT_MODE_DETERMINISTIC_BB); - // Debug: Allow disabling GPU heuristics to test B&B tree determinism in isolation + const bool deterministic_bb_without_deterministic_heuristics = + (context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && + !(context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS); const char* disable_heuristics_env = std::getenv("CUOPT_DISABLE_GPU_HEURISTICS"); - if (bb_only || + if (deterministic_bb_without_deterministic_heuristics || (disable_heuristics_env != nullptr && std::string(disable_heuristics_env) == "1")) { - CUOPT_LOG_INFO("GPU heuristics disabled via CUOPT_DISABLE_GPU_HEURISTICS=1"); + CUOPT_LOG_INFO("GPU heuristics disabled (det_bb_only=%d env=%s)", + (int)deterministic_bb_without_deterministic_heuristics, + disable_heuristics_env ? disable_heuristics_env : "unset"); if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && context.branch_and_bound_ptr != nullptr) { auto& producer_sync = context.branch_and_bound_ptr->get_producer_sync(); diff --git a/cpp/src/mip_heuristics/diversity/multi_armed_bandit.cuh b/cpp/src/mip_heuristics/diversity/multi_armed_bandit.cuh index c98429b3a1..b9219b8dcb 100644 --- a/cpp/src/mip_heuristics/diversity/multi_armed_bandit.cuh +++ b/cpp/src/mip_heuristics/diversity/multi_armed_bandit.cuh @@ -55,7 +55,7 @@ struct recombiner_work_normalized_reward_t { double operator()(double factor) const { // normal recombiners take 2000 ms - if (deterministic) { + if (!deterministic) { double time_in_miliseconds = work; return factor * (std::max(0.1, 4.0 - (time_in_miliseconds / 2000))); } else { diff --git a/cpp/src/mip_heuristics/diversity/population.cu b/cpp/src/mip_heuristics/diversity/population.cu index 2211caf28b..3b19949d05 100644 --- a/cpp/src/mip_heuristics/diversity/population.cu +++ b/cpp/src/mip_heuristics/diversity/population.cu @@ -29,60 +29,6 @@ constexpr double min_infeasibility_weight = 1.; constexpr double infeasibility_balance_ratio = 1.1; constexpr double halving_skip_ratio = 0.75; -template -void assert_solution_matches_population_problem(const char* location, - const char* role, - const problem_t* population_problem, - const solution_t& sol) -{ - cuopt_assert(population_problem != nullptr, "Population problem must not be null"); - cuopt_assert(sol.problem_ptr != nullptr, "Solution problem must not be null"); - const size_t assignment_size = sol.assignment.size(); - const size_t solution_vars = (size_t)sol.problem_ptr->n_variables; - const size_t population_vars = (size_t)population_problem->n_variables; - if (assignment_size != solution_vars || assignment_size != population_vars || - sol.problem_ptr != population_problem) { - CUOPT_LOG_ERROR( - "%s: %s shape mismatch assignment=%zu solution_vars=%zu population_vars=%zu sol_hash=0x%x " - "solution_problem=0x%x population_problem=0x%x", - location, - role, - assignment_size, - solution_vars, - population_vars, - sol.get_hash(), - sol.problem_ptr->get_fingerprint(), - population_problem->get_fingerprint()); - } - cuopt_assert(assignment_size == solution_vars, "Solution assignment must match its problem size"); - cuopt_assert(assignment_size == population_vars, - "Solution assignment must match the population problem size"); - cuopt_assert(sol.problem_ptr == population_problem, - "Solution problem must match the population problem"); -} - -template -void assert_solutions_compatible_for_similarity(const char* location, - const problem_t* population_problem, - const solution_t& lhs, - const char* lhs_role, - const solution_t& rhs, - const char* rhs_role) -{ - assert_solution_matches_population_problem(location, lhs_role, population_problem, lhs); - assert_solution_matches_population_problem(location, rhs_role, population_problem, rhs); - if (lhs.assignment.size() != rhs.assignment.size()) { - CUOPT_LOG_ERROR("%s: pair mismatch lhs_size=%zu rhs_size=%zu lhs_hash=0x%x rhs_hash=0x%x", - location, - lhs.assignment.size(), - rhs.assignment.size(), - lhs.get_hash(), - rhs.get_hash()); - } - cuopt_assert(lhs.assignment.size() == rhs.assignment.size(), - "Similarity check requires equal assignment sizes"); -} - template population_t::population_t(std::string const& name_, mip_solver_context_t& context_, @@ -104,7 +50,6 @@ population_t::population_t(std::string const& name_, timer(0.0, cuopt::termination_checker_t::root_tag_t{}) { best_feasible_objective = std::numeric_limits::max(); - context.solution_publication.reset_published_best(); } template @@ -329,98 +274,39 @@ template void population_t::run_solution_callbacks( solution_t& sol, internals::mip_solution_origin_t callback_origin) { - bool better_solution_found = is_better_than_best_feasible(sol); - auto user_callbacks = context.settings.get_mip_callbacks(); - if (better_solution_found) { - const bool deterministic_callback_owner_is_bb = - (context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && - context.branch_and_bound_ptr != nullptr; - if (deterministic_callback_owner_is_bb) { - cuopt_assert(sol.get_feasible(), - "Deterministic heuristic posting requires a feasible solution"); + if (is_better_than_best_feasible(sol)) { + const bool deterministic_bb = (context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && + context.branch_and_bound_ptr != nullptr; + + if (deterministic_bb) { const double work_timestamp = context.gpu_heur_loop.current_producer_work(); cuopt_assert(std::isfinite(work_timestamp), "Deterministic heuristic work timestamp must be finite"); context.branch_and_bound_ptr->queue_external_solution_deterministic( sol.get_host_assignment(), sol.get_user_objective(), work_timestamp, callback_origin); } else { - if (problem_ptr->branch_and_bound_callback != nullptr) { - problem_ptr->branch_and_bound_callback(sol.get_host_assignment()); - } const double work_timestamp = context.gpu_heur_loop.current_work(); - const auto payload = - make_solution_callback_payload_from_solution(problem_ptr, - context.settings, - context.scaling, - context.gpu_heur_loop, - sol, - callback_origin, - work_timestamp); - const bool published = - context.solution_publication.publish_new_best_feasible(payload, timer.elapsed_time()); - cuopt_assert(published, "New best feasible solution should publish to GET callbacks"); - best_feasible_objective = sol.get_objective(); - } - } + const auto payload = context.solution_publication.build_payload( + context.problem_ptr, context.scaling, sol, callback_origin, work_timestamp); + context.solution_publication.publish_new_best_feasible(payload, timer.elapsed_time()); - for (auto callback : user_callbacks) { - if (callback->get_type() == internals::base_solution_callback_type::SET_SOLUTION) { - auto set_sol_callback = static_cast(callback); - f_t user_bound = context.stats.get_solution_bound(); - auto callback_num_variables = problem_ptr->original_problem_ptr->get_n_variables(); - rmm::device_uvector incumbent_assignment(callback_num_variables, - sol.handle_ptr->get_stream()); - solution_t outside_sol(sol); - rmm::device_scalar d_outside_sol_objective(sol.handle_ptr->get_stream()); - auto inf = std::numeric_limits::infinity(); - d_outside_sol_objective.set_value_async(inf, sol.handle_ptr->get_stream()); - sol.handle_ptr->sync_stream(); - std::vector h_incumbent_assignment(incumbent_assignment.size()); - std::vector h_outside_sol_objective(1, inf); - std::vector h_user_bound(1, user_bound); - set_sol_callback->set_solution(h_incumbent_assignment.data(), - h_outside_sol_objective.data(), - h_user_bound.data(), - set_sol_callback->get_user_data()); - f_t outside_sol_objective = h_outside_sol_objective[0]; - // The callback might be called without setting any valid solution or objective which triggers - // asserts - if (outside_sol_objective == inf) { return; } - d_outside_sol_objective.set_value_async(outside_sol_objective, sol.handle_ptr->get_stream()); - raft::copy(incumbent_assignment.data(), - h_incumbent_assignment.data(), - incumbent_assignment.size(), - sol.handle_ptr->get_stream()); - - if (context.settings.mip_scaling) { context.scaling.scale_solutions(incumbent_assignment); } - bool is_valid = problem_ptr->pre_process_assignment(incumbent_assignment); - if (!is_valid) { return; } - cuopt_assert(outside_sol.assignment.size() == incumbent_assignment.size(), - "Incumbent assignment size mismatch"); - raft::copy(outside_sol.assignment.data(), - incumbent_assignment.data(), - incumbent_assignment.size(), - sol.handle_ptr->get_stream()); - outside_sol.compute_feasibility(); - - CUOPT_LOG_DEBUG("Injected solution feasibility = %d objective = %g excess = %g", - outside_sol.get_feasible(), - outside_sol.get_user_objective(), - outside_sol.get_total_excess()); - if (std::abs(outside_sol.get_user_objective() - outside_sol_objective) > 1e-6) { - cuopt_func_call( - CUOPT_LOG_DEBUG("External solution objective mismatch: outside_sol.get_user_objective() " - "= %g, outside_sol_objective = %g", - outside_sol.get_user_objective(), - outside_sol_objective)); + if (context.branch_and_bound_ptr != nullptr && + context.problem_ptr->branch_and_bound_callback != nullptr) { + context.problem_ptr->branch_and_bound_callback(sol.get_host_assignment()); } - cuopt_assert(std::abs(outside_sol.get_user_objective() - outside_sol_objective) <= 1e-6, - "External solution objective mismatch"); - auto h_outside_sol = outside_sol.get_host_assignment(); - add_external_solution( - h_outside_sol, outside_sol.get_objective(), internals::mip_solution_origin_t::USER_INITIAL); } + + best_feasible_objective = sol.get_objective(); } + + context.solution_injection.invoke_set_solution_callbacks( + problem_ptr, + context.scaling, + sol, + [this]( + const std::vector& assignment, f_t objective, internals::mip_solution_origin_t origin) { + add_external_solution(assignment, objective, origin); + }); } template diff --git a/cpp/src/mip_heuristics/diversity/population.cuh b/cpp/src/mip_heuristics/diversity/population.cuh index b92b7f1a97..a19f4ff2df 100644 --- a/cpp/src/mip_heuristics/diversity/population.cuh +++ b/cpp/src/mip_heuristics/diversity/population.cuh @@ -82,7 +82,6 @@ class population_t { indices[0].second = std::numeric_limits::max(); indices.erase(indices.begin() + 1, indices.end()); best_feasible_objective = std::numeric_limits::max(); - context.solution_publication.reset_published_best(); } void clear_except_best_feasible() @@ -93,7 +92,6 @@ class population_t { solutions[indices[0].first].first = true; indices.erase(indices.begin() + 1, indices.end()); best_feasible_objective = solutions[indices[0].first].second.get_objective(); - context.solution_publication.reset_published_best(best_feasible_objective); } // ------------------- diff --git a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh index 11a9af1081..e43a1d1efd 100644 --- a/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh +++ b/cpp/src/mip_heuristics/diversity/recombiners/recombiner.cuh @@ -245,27 +245,23 @@ class recombiner_t { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } // submip not supported in deterministic mode yet - if (disable_submip_for_determinism) { enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); } + if (disable_submip_for_determinism) { + // temp, added for debugging + enabled_recombiners.erase(recombiner_enum_t::SUB_MIP); + } recombiner_t::enabled_recombiners = std::vector(enabled_recombiners.begin(), enabled_recombiners.end()); cuopt_assert(!recombiner_t::enabled_recombiners.empty(), "No recombiners enabled after init"); - const char* enabled_0 = recombiner_t::enabled_recombiners.size() > 0 - ? recombiner_name(recombiner_t::enabled_recombiners[0]) - : "NONE"; - const char* enabled_1 = recombiner_t::enabled_recombiners.size() > 1 - ? recombiner_name(recombiner_t::enabled_recombiners[1]) - : "NONE"; - const char* enabled_2 = recombiner_t::enabled_recombiners.size() > 2 - ? recombiner_name(recombiner_t::enabled_recombiners[2]) - : "NONE"; - const char* enabled_3 = recombiner_t::enabled_recombiners.size() > 3 - ? recombiner_name(recombiner_t::enabled_recombiners[3]) - : "NONE"; + std::string order_str; + for (size_t i = 0; i < recombiner_t::enabled_recombiners.size(); ++i) { + if (i > 0) { order_str += ','; } + order_str += recombiner_name(recombiner_t::enabled_recombiners[i]); + } CUOPT_DETERMINISM_LOG( "Deterministic recombiner init: expensive_to_fix=%d n_continuous=%d " "max_continuous=%zu disable_fp_submip_expensive=%d " "disable_submip_continuous=%d disable_submip_deterministic=%d size=%zu " - "order=[%s,%s,%s,%s]", + "order=[%s]", (int)problem.expensive_to_fix_vars, (int)n_continuous_vars, sub_mip_recombiner_config_t::max_continuous_vars, @@ -273,10 +269,7 @@ class recombiner_t { (int)disable_submip_for_continuous_limit, (int)disable_submip_for_determinism, recombiner_t::enabled_recombiners.size(), - enabled_0, - enabled_1, - enabled_2, - enabled_3); + order_str.c_str()); } mip_solver_context_t& context; diff --git a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_impl_common.cuh b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_impl_common.cuh index bd2f7315f5..44d3fe55d0 100644 --- a/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_impl_common.cuh +++ b/cpp/src/mip_heuristics/feasibility_jump/feasibility_jump_impl_common.cuh @@ -126,16 +126,12 @@ HDI std::pair feas_score_constraint( // usually occur on a rarer basis (around 50 iteratiosn to 1 local minimum) // likely unreasonable and overkill however f_t cstr_weight = bound_idx == 0 ? cstr_left_weight : cstr_right_weight; - if (!isfinite(cstr_weight)) { - cstr_weight = - bound_idx == 0 ? fj.cstr_left_weights[cstr_idx] : fj.cstr_right_weights[cstr_idx]; - } - f_t sign = bound_idx == 0 ? -1 : 1; - f_t rhs = bounds[bound_idx] * sign; - f_t old_lhs = current_lhs * sign; - f_t new_lhs = (current_lhs + cstr_coeff * delta) * sign; - f_t old_slack = rhs - old_lhs; - f_t new_slack = rhs - new_lhs; + f_t sign = bound_idx == 0 ? -1 : 1; + f_t rhs = bounds[bound_idx] * sign; + f_t old_lhs = current_lhs * sign; + f_t new_lhs = (current_lhs + cstr_coeff * delta) * sign; + f_t old_slack = rhs - old_lhs; + f_t new_slack = rhs - new_lhs; cuopt_assert(isfinite(cstr_weight), "invalid weight"); cuopt_assert(cstr_weight >= 0, "invalid weight"); diff --git a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu index b560971914..1a62281450 100644 --- a/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu +++ b/cpp/src/mip_heuristics/local_search/feasibility_pump/feasibility_pump.cu @@ -573,8 +573,7 @@ bool feasibility_pump_t::run_single_fp_descent(solution_t& s (int)solution.get_feasible(), solution.get_user_objective(), timer.remaining_time()); - bool preempt = (context.diversity_manager_ptr != nullptr && - context.diversity_manager_ptr->check_b_b_preemption()); + bool preempt = (context.diversity_manager_ptr->check_b_b_preemption()); if (preempt || timer.check_time_limit()) { CUOPT_LOG_DEBUG("FP time limit reached!"); round(solution); diff --git a/cpp/src/mip_heuristics/local_search/local_search.cu b/cpp/src/mip_heuristics/local_search/local_search.cu index 371aa7f58c..75dc93002c 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cu +++ b/cpp/src/mip_heuristics/local_search/local_search.cu @@ -34,9 +34,6 @@ namespace cuopt::linear_programming::detail { -static double local_search_best_obj = std::numeric_limits::max(); -static population_t* pop_ptr = nullptr; - template local_search_t::local_search_t(mip_solver_context_t& context_, rmm::device_uvector& lp_optimal_solution_) @@ -64,12 +61,8 @@ local_search_t::local_search_t(mip_solver_context_t& context cpu_fj.fj_ptr = &fj; } scratch_cpu_fj_on_lp_opt.fj_ptr = &fj; - CUOPT_DETERMINISM_LOG( - "Deterministic solve start local_search state: seed_state=%lld " - "local_search_best_obj=%.16e pop_ptr_set=%d", - (long long)cuopt::seed_generator::peek_seed(), - local_search_best_obj, - (int)(pop_ptr != nullptr)); + CUOPT_DETERMINISM_LOG("Deterministic solve start local_search state: seed_state=%lld", + (long long)cuopt::seed_generator::peek_seed()); } template @@ -77,8 +70,6 @@ void local_search_t::start_cpufj_scratch_threads(population_t default_weights(context.problem_ptr->n_constraints, 1.); solution_t solution(*context.problem_ptr); @@ -100,19 +91,9 @@ void local_search_t::start_cpufj_scratch_threads(population_tlog_prefix = "******* scratch " + std::to_string(counter) + ": "; cpu_fj.fj_cpu->improvement_callback = - [&population, problem_ptr = context.problem_ptr]( - f_t obj, const std::vector& h_vec, double /*work_units*/) { + [&population](f_t obj, const std::vector& h_vec, double /*work_units*/) { population.add_external_solution( h_vec, obj, internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP); - (void)problem_ptr; - if (obj < local_search_best_obj) { - CUOPT_LOG_TRACE("******* New local search best obj %g, best overall %g", - problem_ptr->get_user_obj_from_solver_obj(obj), - problem_ptr->get_user_obj_from_solver_obj( - population.is_feasible() ? population.best_feasible().get_objective() - : std::numeric_limits::max())); - local_search_best_obj = obj; - } }; counter++; }; @@ -128,7 +109,6 @@ void local_search_t::start_cpufj_lptopt_scratch_threads( { cuopt_assert(!(context.settings.determinism_mode & CUOPT_DETERMINISM_GPU_HEURISTICS), "LP-opt CPUFJ scratch must remain opportunistic-only"); - pop_ptr = &population; std::vector default_weights(context.problem_ptr->n_constraints, 1.); @@ -140,17 +120,9 @@ void local_search_t::start_cpufj_lptopt_scratch_threads( solution_lp, default_weights, default_weights, 0., context.preempt_heuristic_solver_); scratch_cpu_fj_on_lp_opt.fj_cpu->log_prefix = "******* scratch on LP optimal: "; scratch_cpu_fj_on_lp_opt.fj_cpu->improvement_callback = - [this, &population](f_t obj, const std::vector& h_vec, double /*work_units*/) { + [&population](f_t obj, const std::vector& h_vec, double /*work_units*/) { population.add_external_solution( h_vec, obj, internals::mip_solution_origin_t::CPU_FEASIBILITY_JUMP); - if (obj < local_search_best_obj) { - CUOPT_LOG_DEBUG("******* New local search best obj %g, best overall %g", - context.problem_ptr->get_user_obj_from_solver_obj(obj), - context.problem_ptr->get_user_obj_from_solver_obj( - population.is_feasible() ? population.best_feasible().get_objective() - : std::numeric_limits::max())); - local_search_best_obj = obj; - } }; // default weights diff --git a/cpp/src/mip_heuristics/local_search/local_search.cuh b/cpp/src/mip_heuristics/local_search/local_search.cuh index a22a810712..fc1dd6135c 100644 --- a/cpp/src/mip_heuristics/local_search/local_search.cuh +++ b/cpp/src/mip_heuristics/local_search/local_search.cuh @@ -15,12 +15,6 @@ #include #include -#include -#include -#include -#include -#include - namespace cuopt::linear_programming::dual_simplex { template class branch_and_bound_t; diff --git a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu index 51e607eb87..23cd9b41e3 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/bounds_repair.cu @@ -601,11 +601,10 @@ bool bounds_repair_t::repair_problem(problem_t& problem, CUOPT_LOG_TRACE( "Repair: best_cstr_delta value %d best_damage %f", best_cstr_delta, best_damage); i_t best_move_idx; - i_t n_of_eligible_candidates = -1; - const double random_draw = best_cstr_delta > 0 ? rand_double(0, 1, gen) : -1.0; - const bool choose_random_move = (best_cstr_delta > 0 && random_draw < p) || is_cycle; + i_t n_of_eligible_candidates = -1; + // if the best damage is positive and we are within the prop (paper uses 0.75) - if (choose_random_move) { + if ((best_cstr_delta > 0 && rand_double(0, 1, gen) < p) || is_cycle) { // pick a random move from the candidate list best_move_idx = get_random_idx(n_candidates); } else { @@ -617,24 +616,6 @@ bool bounds_repair_t::repair_problem(problem_t& problem, CUOPT_LOG_TRACE("n_of_eligible_candidates %d", n_of_eligible_candidates); best_move_idx = get_random_idx(n_of_eligible_candidates); } - if (timer.deterministic) { - CUOPT_DETERMINISM_LOG( - "Repair iter choice: iter=%d curr_cstr=%d best_cstr_delta=%d best_damage=%.6f " - "choose_random=%d random_draw=%.6f eligible=%d best_move_idx=%d move_var=%d " - "move_shift=%.6f move_cdelta=%d move_damage=%.6f", - repair_iterations, - curr_cstr, - best_cstr_delta, - best_damage, - (int)choose_random_move, - random_draw, - n_of_eligible_candidates, - best_move_idx, - candidates.variable_index.element(best_move_idx, handle_ptr->get_stream()), - candidates.bound_shift.element(best_move_idx, handle_ptr->get_stream()), - candidates.cstr_delta.element(best_move_idx, handle_ptr->get_stream()), - candidates.damage.element(best_move_idx, handle_ptr->get_stream())); - } CUOPT_LOG_TRACE("Repair: selected best_move_idx %d var id %d shift %f cstr_delta %d damage %f", best_move_idx, candidates.variable_index.element(best_move_idx, handle_ptr->get_stream()), @@ -685,14 +666,6 @@ bool bounds_repair_t::repair_problem(problem_t& problem, bool feasible = h_n_violated_cstr == 0; // copy best bounds into problem best_bounds.update_to(problem, handle_ptr); - if (timer.deterministic) { - CUOPT_DETERMINISM_LOG( - "Repair work estimate: loops=%d total=%.6f best_violation=%.6f feasible=%d", - repair_iterations, - total_estimated_work, - best_violation, - (int)feasible); - } CUOPT_LOG_DEBUG("Repair: returning with feas: %d vio %f", feasible, best_violation); return feasible; } diff --git a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu index 4263df9c5b..ed371676cc 100644 --- a/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu +++ b/cpp/src/mip_heuristics/local_search/rounding/constraint_prop.cu @@ -761,7 +761,7 @@ bool constraint_prop_t::run_repair_procedure(problem_t& prob work_limit_timer_t& timer, const raft::handle_t* handle_ptr) { - CUOPT_LOG_DEBUG("Running repair procedure"); + CUOPT_LOG_TRACE("Running repair procedure"); // select the first probing value i_t select = 0; @@ -842,7 +842,6 @@ bool constraint_prop_t::is_problem_ii(problem_t& problem) { bounds_update.calculate_activity_on_problem_bounds(problem); bounds_update.calculate_infeasible_redundant_constraints(problem); - multi_probe.calculate_activity(problem, problem.handle_ptr); bool problem_ii = bounds_update.infeas_constraints_count > 0; return problem_ii; } @@ -942,12 +941,10 @@ bool constraint_prop_t::find_integer( bool timeout_happened = false; i_t n_failed_repair_iterations = 0; while (set_count < unset_integer_vars.size()) { - auto iter_start_time = std::chrono::high_resolution_clock::now(); CUOPT_LOG_TRACE("n_set_vars %d vars to set %lu", set_count, unset_integer_vars.size()); CUOPT_LOG_TRACE("unset_integer_vars size %lu", unset_integer_vars.size()); const size_t set_count_before = set_count; update_host_assignment(sol); - auto after_update_host_assignment = std::chrono::high_resolution_clock::now(); if (max_timer.check_time_limit()) { CUOPT_LOG_DEBUG("Second time limit is reached returning nearest rounding!"); collapse_crossing_bounds(*sol.problem_ptr, *orig_sol.problem_ptr, sol.handle_ptr); @@ -978,31 +975,15 @@ bool constraint_prop_t::find_integer( sort_by_implied_slack_consumption( sol, make_span(unset_integer_vars, set_count, unset_integer_vars.size()), problem_ii); } - auto after_sort = std::chrono::high_resolution_clock::now(); std::vector host_vars_to_set(n_vars_to_set); raft::copy(host_vars_to_set.data(), unset_integer_vars.data() + set_count, n_vars_to_set, sol.handle_ptr->get_stream()); - - CUOPT_LOG_TRACE("host_vars_to_set hash 0x%x", detail::compute_hash(host_vars_to_set)); - auto var_probe_vals = generate_bulk_rounding_vector(sol, orig_sol, host_vars_to_set, probing_config); - auto after_generate_probe_vals = std::chrono::high_resolution_clock::now(); - - CUOPT_LOG_TRACE("var_probe_vals hash 1 0x%x, hash 2 0x%x, hash 3 0x%x", - detail::compute_hash(std::get<0>(var_probe_vals)), - detail::compute_hash(std::get<1>(var_probe_vals)), - detail::compute_hash(std::get<2>(var_probe_vals))); probe( sol, orig_sol.problem_ptr, var_probe_vals, &set_count, unset_integer_vars, probing_config); - auto after_probe = std::chrono::high_resolution_clock::now(); - CUOPT_LOG_TRACE( - "post probe, set count %d, unset var hash 0x%x, size %lu", - (int)set_count, - detail::compute_hash(make_span(unset_integer_vars), sol.handle_ptr->get_stream()), - unset_integer_vars.size()); [[maybe_unused]] bool repair_attempted = false; bool bounds_repaired = false; i_t n_fixed_vars = 0; @@ -1012,56 +993,9 @@ bool constraint_prop_t::find_integer( work_limit_timer_t repair_timer( context.gpu_heur_loop, timer.remaining_time() / 5, *context.termination); save_bounds(sol); - auto remaining_unset = make_span(unset_integer_vars, set_count, unset_integer_vars.size()); - const auto orig_pb_fingerprint = orig_sol.problem_ptr->get_fingerprint(); - const auto curr_pb_fingerprint = sol.problem_ptr->get_fingerprint(); - CUOPT_LOG_DEBUG( - "CP repair entry: set_count=%lu unset_remaining=%lu unset_hash=0x%x assign_hash=0x%x " - "bounds_hash=0x%x orig_pb_hash=0x%x curr_pb_hash=0x%x ii_counts=%d/%d recovery=%d " - "rounding_ii=%d failed_repairs=%d interval=%d timer_rem=%.6f max_timer_rem=%.6f " - "repair_budget=%.6f", - set_count, - remaining_unset.size(), - detail::compute_hash(remaining_unset, sol.handle_ptr->get_stream()), - sol.get_hash(), - detail::compute_hash(make_span(sol.problem_ptr->variable_bounds), - sol.handle_ptr->get_stream()), - orig_pb_fingerprint, - curr_pb_fingerprint, - multi_probe.infeas_constraints_count_0, - multi_probe.infeas_constraints_count_1, - (int)recovery_mode, - (int)rounding_ii, - n_failed_repair_iterations, - bounds_prop_interval, - timer.remaining_time(), - max_timer.remaining_time(), - repair_timer.remaining_time()); // update bounds and run repair procedure - repair_attempted = true; - bounds_repaired = + bool bounds_repaired = run_repair_procedure(*sol.problem_ptr, *orig_sol.problem_ptr, repair_timer, sol.handle_ptr); - remaining_unset = make_span(unset_integer_vars, set_count, unset_integer_vars.size()); - CUOPT_LOG_DEBUG( - "CP repair exit: success=%d set_count=%lu unset_remaining=%lu unset_hash=0x%x " - "assign_hash=0x%x bounds_hash=0x%x curr_pb_hash=0x%x ii_counts=%d/%d " - "infeas_after_bounds_update=%d timer_rem=%.6f max_timer_rem=%.6f repair_elapsed=%.6f " - "repair_remaining=%.6f", - (int)bounds_repaired, - set_count, - remaining_unset.size(), - detail::compute_hash(remaining_unset, sol.handle_ptr->get_stream()), - sol.get_hash(), - detail::compute_hash(make_span(sol.problem_ptr->variable_bounds), - sol.handle_ptr->get_stream()), - curr_pb_fingerprint, - multi_probe.infeas_constraints_count_0, - multi_probe.infeas_constraints_count_1, - bounds_update.infeas_constraints_count, - timer.remaining_time(), - max_timer.remaining_time(), - repair_timer.elapsed_time(), - repair_timer.remaining_time()); if (!bounds_repaired) { restore_bounds(sol); n_failed_repair_iterations++; @@ -1089,43 +1023,6 @@ bool constraint_prop_t::find_integer( set_count += n_fixed_vars; } } - auto after_repair = std::chrono::high_resolution_clock::now(); - const double update_host_assignment_ms = - std::chrono::duration(after_update_host_assignment - iter_start_time) - .count(); - const double sort_ms = - std::chrono::duration(after_sort - after_update_host_assignment).count(); - const double generate_probe_vals_ms = - std::chrono::duration(after_generate_probe_vals - after_sort).count(); - const double probe_ms = - std::chrono::duration(after_probe - after_generate_probe_vals).count(); - const double repair_ms = - std::chrono::duration(after_repair - after_probe).count(); - const double iter_total_ms = - std::chrono::duration(after_repair - iter_start_time).count(); - CUOPT_DETERMINISM_LOG( - "CP iter: set_count=%lu->%lu unset=%d interval=%d n_vars_to_set=%d sort=%d recovery=%d " - "rounding_ii=%d probe_ii=%d timeout=%d repair_attempted=%d repair_success=%d " - "repair_fixed=%d t_ms(update=%.3f sort=%.3f gen=%.3f probe=%.3f repair=%.3f total=%.3f)", - set_count_before, - set_count, - n_curr_unset, - bounds_prop_interval, - n_vars_to_set, - (int)did_sort, - (int)recovery_mode, - (int)rounding_ii, - (int)problem_ii, - (int)timeout_happened, - (int)repair_attempted, - (int)bounds_repaired, - n_fixed_vars, - update_host_assignment_ms, - sort_ms, - generate_probe_vals_ms, - probe_ms, - repair_ms, - iter_total_ms); // we can keep normal bounds_update here because this is only activated after the repair if (recovery_mode && multi_probe.infeas_constraints_count_0 > 0 && multi_probe.infeas_constraints_count_1 > 0) { @@ -1170,7 +1067,6 @@ bool constraint_prop_t::find_integer( lp_settings.tolerance = orig_sol.problem_ptr->tolerances.absolute_tolerance; lp_settings.save_state = false; lp_settings.return_first_feasible = true; - CUOPT_LOG_TRACE("bounds repair LP, sol hash 0x%x", orig_sol.get_hash()); run_lp_with_vars_fixed(*orig_sol.problem_ptr, orig_sol, orig_sol.problem_ptr->integer_indices, @@ -1191,41 +1087,10 @@ bool constraint_prop_t::apply_round( { raft::common::nvtx::range fun_scope("constraint prop round"); - // === CONSTRAINT PROP PREDICTOR FEATURES - START === - auto cp_start_time = std::chrono::high_resolution_clock::now(); - - // CUOPT_LOG_INFO("CP_FEATURES: n_variables=%d n_constraints=%d n_integer_vars=%d", - // sol.problem_ptr->n_variables, - // sol.problem_ptr->n_constraints, - // sol.problem_ptr->n_integer_vars); - - // CUOPT_LOG_INFO("CP_FEATURES: nnz=%lu sparsity=%.6f", - // sol.problem_ptr->coefficients.size(), - // sol.problem_ptr->sparsity); - sol.compute_feasibility(); - i_t n_unset_integers = sol.problem_ptr->n_integer_vars - sol.compute_number_of_integers(); - - // CUOPT_LOG_INFO("CP_FEATURES: n_unset_vars=%d initial_excess=%.6f time_budget=%.6f", - // n_unset_integers, - // sol.get_total_excess(), - // max_time_for_bounds_prop); - - // CUOPT_LOG_INFO("CP_FEATURES: round_all_vars=%d lp_run_time_after_feasible=%.6f", - // round_all_vars, - // lp_run_time_after_feasible); - // === CONSTRAINT PROP PREDICTOR FEATURES - END === - max_timer = work_limit_timer_t{context.gpu_heur_loop, max_time_for_bounds_prop, *context.termination}; - if (check_brute_force_rounding(sol)) { - auto cp_end_time = std::chrono::high_resolution_clock::now(); - auto cp_elapsed_ms = - std::chrono::duration_cast(cp_end_time - cp_start_time).count(); - // CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=BRUTE_FORCE_SUCCESS iterations=0", - // cp_elapsed_ms); - return true; - } + if (check_brute_force_rounding(sol)) { return true; } recovery_mode = false; rounding_ii = false; n_iter_in_recovery = 0; @@ -1250,25 +1115,11 @@ bool constraint_prop_t::apply_round( repair_stats.total_time_spent_on_repair, repair_stats.total_time_spent_bounds_prop_after_repair, repair_stats.total_time_spent_on_bounds_prop); - // === CONSTRAINT PROP PREDICTOR RESULTS - START === - auto cp_end_time = std::chrono::high_resolution_clock::now(); - auto cp_elapsed_ms = - std::chrono::duration_cast(cp_end_time - cp_start_time).count(); - // === CONSTRAINT PROP PREDICTOR RESULTS - END === - if (!sol_found) { sol.compute_feasibility(); - // CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=FAILED iterations=%d", - // cp_elapsed_ms, - // 0); // TODO: track actual iterations return false; } - bool result = sol.compute_feasibility(); - // CUOPT_LOG_INFO("CP_RESULT: time_ms=%lld termination=%s iterations=%d", - // cp_elapsed_ms, - // result ? "SUCCESS" : "FAILED", - // 0); // TODO: track actual iterations - return result; + return sol.compute_feasibility(); } template @@ -1350,17 +1201,6 @@ bool constraint_prop_t::handle_fixed_vars( auto set_count = *set_count_ptr; const f_t int_tol = sol.problem_ptr->tolerances.integrality_tolerance; // which other variables were affected? - CUOPT_LOG_TRACE("handle_fixed_vars, unset vars hash 0x%x, sol.assignment hash 0x%x", - detail::compute_hash(make_span(unset_vars), sol.handle_ptr->get_stream()), - detail::compute_hash(make_span(sol.assignment), sol.handle_ptr->get_stream())); - CUOPT_LOG_TRACE( - "handle_fixed_vars, original_problem->variable_bounds hash 0x%x, " - "sol.problem_ptr->variable_bounds hash 0x%x", - detail::compute_hash(make_span(original_problem->variable_bounds), - sol.handle_ptr->get_stream()), - detail::compute_hash(make_span(sol.problem_ptr->variable_bounds), - sol.handle_ptr->get_stream())); - auto iter = thrust::stable_partition(sol.handle_ptr->get_thrust_policy(), unset_vars.begin() + set_count, unset_vars.end(), diff --git a/cpp/src/mip_heuristics/presolve/probing_cache.cu b/cpp/src/mip_heuristics/presolve/probing_cache.cu index de2a63d918..f152e440a8 100644 --- a/cpp/src/mip_heuristics/presolve/probing_cache.cu +++ b/cpp/src/mip_heuristics/presolve/probing_cache.cu @@ -905,19 +905,19 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, auto& multi_probe_presolve = multi_probe_presolve_pool[thread_idx]; - compute_cache_for_var(var_idx, - bound_presolve, - problem, - multi_probe_presolve, - h_var_bounds, - h_integer_indices, - n_of_implied_singletons, - n_of_cached_probings, - problem_is_infeasible, - modification_vector_pool[thread_idx], - substitution_vector_pool[thread_idx], - timer, - problem.handle_ptr->get_device()); + compute_cache_for_var(var_idx, + bound_presolve, + problem, + multi_probe_presolve, + h_var_bounds, + h_integer_indices, + n_of_implied_singletons, + n_of_cached_probings, + problem_is_infeasible, + modification_vector_pool[thread_idx], + substitution_vector_pool[thread_idx], + timer, + problem.handle_ptr->get_device()); } } #pragma omp single diff --git a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp index 61a7adf895..af11265551 100644 --- a/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp +++ b/cpp/src/mip_heuristics/presolve/third_party_presolve.cpp @@ -557,6 +557,7 @@ void set_presolve_parameters(papilo::Presolve& presolver, int ncols, bool deterministic = false) { + // It looks like a copy. But this copy has the pointers to relevant variables in papilo auto params = presolver.getParameters(); if (category == problem_category_t::MIP) { if (!deterministic) { diff --git a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu index 0885a8ef96..d1fd0a6392 100644 --- a/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu +++ b/cpp/src/mip_heuristics/relaxed_lp/relaxed_lp.cu @@ -51,30 +51,6 @@ optimization_problem_solution_t get_relaxed_lp_solution( raft::common::nvtx::range fun_scope("get_relaxed_lp_solution"); static std::atomic lp_call_counter{0}; const uint64_t lp_call_id = lp_call_counter.fetch_add(1, std::memory_order_relaxed); - // auto function_start_time = std::chrono::high_resolution_clock::now(); - - // // === PDLP PREDICTOR FEATURES - START === - // CUOPT_LOG_INFO("PDLP_FEATURES: n_variables=%d n_constraints=%d nnz=%lu", - // op_problem.n_variables, - // op_problem.n_constraints, - // op_problem.coefficients.size()); - - // CUOPT_LOG_INFO("PDLP_FEATURES: sparsity=%.6f nnz_stddev=%.6f unbalancedness=%.6f", - // op_problem.sparsity, - // op_problem.nnz_stddev, - // op_problem.unbalancedness); - - // CUOPT_LOG_INFO("PDLP_FEATURES: has_warm_start=%d time_limit=%.6f iteration_limit=%d", - // settings.has_initial_primal, - // settings.time_limit, - // settings.iteration_limit); - - // CUOPT_LOG_INFO("PDLP_FEATURES: tolerance=%.10f check_infeasibility=%d - // return_first_feasible=%d", - // settings.tolerance, - // settings.check_infeasibility, - // settings.return_first_feasible); - // // === PDLP PREDICTOR FEATURES - END === pdlp_solver_settings_t pdlp_settings{}; pdlp_settings.detect_infeasibility = settings.check_infeasibility; @@ -109,13 +85,14 @@ optimization_problem_solution_t get_relaxed_lp_solution( } else { estim_iters = std::numeric_limits::max(); } - CUOPT_LOG_DEBUG("estimated iterations %d for work limit %f", estim_iters, settings.work_limit); + CUOPT_DETERMINISM_LOG( + "estimated iterations %d for work limit %f", estim_iters, settings.work_limit); pdlp_settings.iteration_limit = estim_iters; pdlp_settings.time_limit = std::numeric_limits::infinity(); pdlp_settings.pdlp_solver_mode = pdlp_solver_mode_t::Stable2; pdlp_settings.presolver = presolver_t::None; } - CUOPT_LOG_DEBUG( + CUOPT_DETERMINISM_LOG( "LP call %lu config: det=%d work_limit=%.6f time_limit=%.6f iter_limit=%d method=%d mode=%d " "presolver=%d save_state=%d has_initial=%d assignment_hash=0x%x", lp_call_id, @@ -134,12 +111,12 @@ optimization_problem_solution_t get_relaxed_lp_solution( pdlp_solver_t lp_solver(op_problem, pdlp_settings); if (settings.has_initial_primal) { i_t prev_size = lp_state.prev_dual.size(); - // CUOPT_LOG_DEBUG( - // "setting initial primal solution of size %d dual size %d problem vars %d cstrs %d", - // assignment.size(), - // lp_state.prev_dual.size(), - // op_problem.n_variables, - // op_problem.n_constraints); + CUOPT_LOG_TRACE( + "setting initial primal solution of size %d dual size %d problem vars %d cstrs %d", + assignment.size(), + lp_state.prev_dual.size(), + op_problem.n_variables, + op_problem.n_constraints); lp_state.resize(op_problem, op_problem.handle_ptr->get_stream()); clamp_within_var_bounds(assignment, &op_problem, op_problem.handle_ptr); // The previous dual sometimes contain invalid values w.r.t current problem @@ -148,6 +125,7 @@ optimization_problem_solution_t get_relaxed_lp_solution( lp_state.prev_dual.data(), lp_state.prev_dual.data() + op_problem.n_constraints, [prev_size, dual = make_span(lp_state.prev_dual)] __device__(i_t i) { + // early exit to avoid a false positive in compute-sanitizer initcheck if (i >= prev_size) { return 0.0; } f_t x = dual[i]; if (!isfinite(x)) { return 0.0; } @@ -166,19 +144,16 @@ optimization_problem_solution_t get_relaxed_lp_solution( CUOPT_DETERMINISM_LOG( "prev solution sizes primal=%lu dual=%lu", assignment.size(), lp_state.prev_dual.size()); if (determinism_mode) { - auto scaling_hash = lp_solver.get_scaling_hash(); auto init_primal_hash = detail::compute_hash(make_span(assignment), op_problem.handle_ptr->get_stream()); auto init_dual_hash = settings.has_initial_primal ? detail::compute_hash(make_span(lp_state.prev_dual), op_problem.handle_ptr->get_stream()) : 0u; - CUOPT_DETERMINISM_LOG( - "LP call %lu pre-solve state: scaling_hash=0x%x init_primal_hash=0x%x init_dual_hash=0x%x", - lp_call_id, - scaling_hash, - init_primal_hash, - init_dual_hash); + CUOPT_DETERMINISM_LOG("LP call %lu pre-solve state: init_primal_hash=0x%x init_dual_hash=0x%x", + lp_call_id, + init_primal_hash, + init_dual_hash); } auto solver_response = lp_solver.run_solver(start_time); CUOPT_DETERMINISM_LOG("post LP primal size %lu", solver_response.get_primal_solution().size()); diff --git a/cpp/src/mip_heuristics/solution/solution.cu b/cpp/src/mip_heuristics/solution/solution.cu index 396f3c76d0..bd6a3861ea 100644 --- a/cpp/src/mip_heuristics/solution/solution.cu +++ b/cpp/src/mip_heuristics/solution/solution.cu @@ -47,8 +47,6 @@ solution_t::solution_t(problem_t& problem_) assignment(std::move(get_lower_bounds(problem_.variable_bounds, handle_ptr))), lower_excess(problem_.n_constraints, handle_ptr->get_stream()), upper_excess(problem_.n_constraints, handle_ptr->get_stream()), - lower_slack(problem_.n_constraints, handle_ptr->get_stream()), - upper_slack(problem_.n_constraints, handle_ptr->get_stream()), constraint_value(problem_.n_constraints, handle_ptr->get_stream()), obj_val(handle_ptr->get_stream()), n_feasible_constraints(handle_ptr->get_stream()), @@ -64,8 +62,6 @@ solution_t::solution_t(const solution_t& other) assignment(other.assignment, handle_ptr->get_stream()), lower_excess(other.lower_excess, handle_ptr->get_stream()), upper_excess(other.upper_excess, handle_ptr->get_stream()), - lower_slack(other.lower_slack, handle_ptr->get_stream()), - upper_slack(other.upper_slack, handle_ptr->get_stream()), constraint_value(other.constraint_value, handle_ptr->get_stream()), obj_val(other.obj_val, handle_ptr->get_stream()), n_feasible_constraints(other.n_feasible_constraints, handle_ptr->get_stream()), @@ -97,8 +93,6 @@ void solution_t::copy_from(const solution_t& other_sol) // such cuopt::mark_span_as_initialized(make_span(other_sol.lower_excess), handle_ptr->get_stream()); cuopt::mark_span_as_initialized(make_span(other_sol.upper_excess), handle_ptr->get_stream()); - cuopt::mark_span_as_initialized(make_span(other_sol.lower_slack), handle_ptr->get_stream()); - cuopt::mark_span_as_initialized(make_span(other_sol.upper_slack), handle_ptr->get_stream()); cuopt::mark_span_as_initialized(make_span(other_sol.constraint_value), handle_ptr->get_stream()); cuopt::mark_span_as_initialized(make_span(other_sol.obj_val), handle_ptr->get_stream()); cuopt::mark_span_as_initialized(make_span(other_sol.n_feasible_constraints), @@ -129,8 +123,6 @@ void solution_t::resize_to_problem() assignment.resize(problem_ptr->n_variables, handle_ptr->get_stream()); lower_excess.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); upper_excess.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); - lower_slack.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); - upper_slack.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); constraint_value.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); lp_state.prev_primal.resize(problem_ptr->n_variables, handle_ptr->get_stream()); lp_state.prev_dual.resize(problem_ptr->n_constraints, handle_ptr->get_stream()); @@ -156,10 +148,6 @@ void solution_t::resize_to_original_problem() handle_ptr->get_stream()); upper_excess.resize(problem_ptr->original_problem_ptr->get_n_constraints(), handle_ptr->get_stream()); - lower_slack.resize(problem_ptr->original_problem_ptr->get_n_constraints(), - handle_ptr->get_stream()); - upper_slack.resize(problem_ptr->original_problem_ptr->get_n_constraints(), - handle_ptr->get_stream()); constraint_value.resize(problem_ptr->original_problem_ptr->get_n_constraints(), handle_ptr->get_stream()); lp_state.prev_primal.resize(problem_ptr->original_problem_ptr->get_n_variables(), @@ -174,8 +162,6 @@ void solution_t::resize_copy(const solution_t& other_sol) assignment.resize(other_sol.assignment.size(), handle_ptr->get_stream()); lower_excess.resize(other_sol.lower_excess.size(), handle_ptr->get_stream()); upper_excess.resize(other_sol.upper_excess.size(), handle_ptr->get_stream()); - lower_slack.resize(other_sol.lower_slack.size(), handle_ptr->get_stream()); - upper_slack.resize(other_sol.upper_slack.size(), handle_ptr->get_stream()); constraint_value.resize(other_sol.constraint_value.size(), handle_ptr->get_stream()); lp_state.prev_primal.resize(other_sol.lp_state.prev_primal.size(), handle_ptr->get_stream()); lp_state.prev_dual.resize(other_sol.lp_state.prev_dual.size(), handle_ptr->get_stream()); @@ -190,8 +176,6 @@ typename solution_t::view_t solution_t::view() v.assignment = raft::device_span{assignment.data(), assignment.size()}; v.lower_excess = raft::device_span{lower_excess.data(), lower_excess.size()}; v.upper_excess = raft::device_span{upper_excess.data(), upper_excess.size()}; - v.lower_slack = raft::device_span{lower_slack.data(), lower_slack.size()}; - v.upper_slack = raft::device_span{upper_slack.data(), upper_slack.size()}; v.constraint_value = raft::device_span{constraint_value.data(), constraint_value.size()}; v.obj_val = obj_val.data(); v.n_feasible_constraints = n_feasible_constraints.data(); diff --git a/cpp/src/mip_heuristics/solution/solution.cuh b/cpp/src/mip_heuristics/solution/solution.cuh index d90f586425..ae271aaf08 100644 --- a/cpp/src/mip_heuristics/solution/solution.cuh +++ b/cpp/src/mip_heuristics/solution/solution.cuh @@ -113,8 +113,6 @@ class solution_t { raft::device_span assignment; raft::device_span lower_excess; raft::device_span upper_excess; - raft::device_span lower_slack; - raft::device_span upper_slack; raft::device_span constraint_value; f_t* obj_val; i_t* n_feasible_constraints; @@ -129,8 +127,6 @@ class solution_t { rmm::device_uvector assignment; rmm::device_uvector lower_excess; rmm::device_uvector upper_excess; - rmm::device_uvector lower_slack; - rmm::device_uvector upper_slack; rmm::device_uvector constraint_value; rmm::device_scalar obj_val; rmm::device_scalar n_feasible_constraints; diff --git a/cpp/src/mip_heuristics/solve.cu b/cpp/src/mip_heuristics/solve.cu index 736ef9466d..b783281d61 100644 --- a/cpp/src/mip_heuristics/solve.cu +++ b/cpp/src/mip_heuristics/solve.cu @@ -97,38 +97,18 @@ mip_solution_t run_mip(detail::problem_t& problem, solution.compute_objective(); // just to ensure h_user_obj is set auto stats = solver_stats_t{}; stats.set_solution_bound(solution.get_user_objective()); - // log the objective for scripts which need it CUOPT_LOG_INFO("Best feasible: %f", solution.get_user_objective()); - for (auto callback : settings.get_mip_callbacks()) { - auto temp_sol(solution); - std::vector user_objective_vec(1); - std::vector user_bound_vec(1); - user_objective_vec[0] = solution.get_user_objective(); - user_bound_vec[0] = stats.get_solution_bound(); + { + detail::solution_callback_payload_t payload{}; + payload.user_objective = solution.get_user_objective(); + payload.solver_objective = solution.get_objective(); + detail::solution_t temp_sol(solution); if (problem.has_papilo_presolve_data()) { problem.papilo_uncrush_assignment(temp_sol.assignment); } - std::vector user_assignment_vec(temp_sol.assignment.size()); - raft::copy(user_assignment_vec.data(), - temp_sol.assignment.data(), - temp_sol.assignment.size(), - temp_sol.handle_ptr->get_stream()); - solution.handle_ptr->sync_stream(); - if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION) { - auto get_sol_callback = static_cast(callback); - get_sol_callback->get_solution(user_assignment_vec.data(), - user_objective_vec.data(), - user_bound_vec.data(), - get_sol_callback->get_user_data()); - } else if (callback->get_type() == internals::base_solution_callback_type::GET_SOLUTION_EXT) { - internals::mip_solution_callback_info_t callback_info{}; - auto get_sol_callback_ext = static_cast(callback); - get_sol_callback_ext->get_solution(user_assignment_vec.data(), - user_objective_vec.data(), - user_bound_vec.data(), - &callback_info, - get_sol_callback_ext->get_user_data()); - } + payload.assignment = temp_sol.get_host_assignment(); + detail::solution_publication_t pub(settings, stats); + pub.publish_terminal_solution(payload); } return solution.get_solution(true, stats, false); } diff --git a/cpp/src/mip_heuristics/solver.cu b/cpp/src/mip_heuristics/solver.cu index ba2b2cd8dc..11b4c26e2e 100644 --- a/cpp/src/mip_heuristics/solver.cu +++ b/cpp/src/mip_heuristics/solver.cu @@ -60,40 +60,28 @@ mip_solver_t::mip_solver_t(const problem_t& op_problem, } template -struct branch_and_bound_solution_helper_t { - branch_and_bound_solution_helper_t(diversity_manager_t* dm, - dual_simplex::simplex_solver_settings_t& settings) - : dm(dm), settings_(settings) {}; +struct bb_observer_adapter_t { + bb_observer_adapter_t(mip_solver_context_t* context, diversity_manager_t* dm) + : context(context), dm(dm) {}; void new_incumbent_callback(std::vector& solution, f_t objective, - const internals::mip_solution_callback_info_t& callback_info, + const internals::mip_solution_callback_info_t& info, double work_timestamp) { - if (!settings_.deterministic) { - dm->population.add_external_solution( + if (context->settings.determinism_mode & CUOPT_DETERMINISM_BB) { + solution_t temp_sol(*context->problem_ptr); + temp_sol.copy_new_assignment(solution); + temp_sol.compute_feasibility(); + const auto payload = context->solution_publication.build_payload( + context->problem_ptr, context->scaling, temp_sol, info.origin, work_timestamp); + context->solution_publication.publish_new_best_feasible(payload, dm->timer.elapsed_time()); + } + if (context->diversity_manager_ptr != nullptr) { + context->diversity_manager_ptr->population.add_external_solution( solution, objective, internals::mip_solution_origin_t::BRANCH_AND_BOUND_NODE); - dm->rins.new_best_incumbent_callback(solution); - return; + context->diversity_manager_ptr->rins.new_best_incumbent_callback(solution); } - - cuopt_assert(dm != nullptr, "Diversity manager pointer must be valid"); - cuopt_assert(dm->context.problem_ptr != nullptr, "Problem pointer must be valid"); - cuopt_assert(solution.size() == (size_t)dm->context.problem_ptr->n_variables, - "Deterministic B&B callback solution size mismatch"); - cuopt_assert(std::isfinite(objective), "Deterministic B&B callback objective must be finite"); - solution_t temp_sol(*dm->context.problem_ptr); - temp_sol.copy_new_assignment(solution); - temp_sol.compute_feasibility(); - const auto payload = - make_solution_callback_payload_from_solution(dm->context.problem_ptr, - dm->context.settings, - dm->context.scaling, - dm->context.gpu_heur_loop, - temp_sol, - callback_info.origin, - work_timestamp); - dm->context.solution_publication.publish_new_best_feasible(payload, dm->timer.elapsed_time()); } void set_simplex_solution(std::vector& solution, @@ -109,8 +97,8 @@ struct branch_and_bound_solution_helper_t { } void preempt_heuristic_solver() { dm->population.preempt_heuristic_solver(); } + mip_solver_context_t* context; diversity_manager_t* dm; - dual_simplex::simplex_solver_settings_t& settings_; }; template @@ -128,15 +116,9 @@ solution_t mip_solver_t::run_solver() CUOPT_LOG_INFO("Problem fully reduced in presolve"); solution_t sol(*context.problem_ptr); sol.set_problem_fully_reduced(); - const auto payload = make_solution_callback_payload_from_solution( - context.problem_ptr, - context.settings, - context.scaling, - context.gpu_heur_loop, - sol, - internals::mip_solution_origin_t::UNKNOWN, - 0.0); - context.solution_publication.invoke_get_solution_callbacks(payload); + const auto payload = context.solution_publication.build_payload( + context.problem_ptr, context.scaling, sol, internals::mip_solution_origin_t::UNKNOWN, 0.0); + context.solution_publication.publish_terminal_solution(payload); context.problem_ptr->post_process_solution(sol); return sol; } @@ -166,15 +148,9 @@ solution_t mip_solver_t::run_solver() CUOPT_LOG_INFO("Problem full reduced in presolve"); solution_t sol(*context.problem_ptr); sol.set_problem_fully_reduced(); - const auto payload = make_solution_callback_payload_from_solution( - context.problem_ptr, - context.settings, - context.scaling, - context.gpu_heur_loop, - sol, - internals::mip_solution_origin_t::UNKNOWN, - 0.0); - context.solution_publication.invoke_get_solution_callbacks(payload); + const auto payload = context.solution_publication.build_payload( + context.problem_ptr, context.scaling, sol, internals::mip_solution_origin_t::UNKNOWN, 0.0); + context.solution_publication.publish_terminal_solution(payload); context.problem_ptr->post_process_solution(sol); return sol; } @@ -207,15 +183,9 @@ solution_t mip_solver_t::run_solver() sol.set_problem_fully_reduced(); } if (opt_sol.get_termination_status() == pdlp_termination_status_t::Optimal) { - const auto payload = make_solution_callback_payload_from_solution( - context.problem_ptr, - context.settings, - context.scaling, - context.gpu_heur_loop, - sol, - internals::mip_solution_origin_t::UNKNOWN, - 0.0); - context.solution_publication.invoke_get_solution_callbacks(payload); + const auto payload = context.solution_publication.build_payload( + context.problem_ptr, context.scaling, sol, internals::mip_solution_origin_t::UNKNOWN, 0.0); + context.solution_publication.publish_terminal_solution(payload); } context.problem_ptr->post_process_solution(sol); return sol; @@ -233,7 +203,7 @@ solution_t mip_solver_t::run_solver() branch_and_bound_problem.objective_is_integral = context.problem_ptr->is_objective_integral(); dual_simplex::simplex_solver_settings_t branch_and_bound_settings; std::unique_ptr> branch_and_bound; - branch_and_bound_solution_helper_t solution_helper(&dm, branch_and_bound_settings); + bb_observer_adapter_t solution_helper(&context, &dm); dual_simplex::mip_solution_t branch_and_bound_solution(1); bool run_bb = !context.settings.heuristics_only; @@ -289,25 +259,24 @@ solution_t mip_solver_t::run_solver() CUOPT_LOG_INFO("Using %d CPU threads for B&B", branch_and_bound_settings.num_threads); branch_and_bound_settings.new_incumbent_callback = - std::bind(&branch_and_bound_solution_helper_t::new_incumbent_callback, + std::bind(&bb_observer_adapter_t::new_incumbent_callback, &solution_helper, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - // heuristic_preemption_callback is needed in both modes to properly stop the heuristic thread - branch_and_bound_settings.heuristic_preemption_callback = std::bind( - &branch_and_bound_solution_helper_t::preempt_heuristic_solver, &solution_helper); + branch_and_bound_settings.heuristic_preemption_callback = + std::bind(&bb_observer_adapter_t::preempt_heuristic_solver, &solution_helper); if (!(context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { branch_and_bound_settings.set_simplex_solution_callback = - std::bind(&branch_and_bound_solution_helper_t::set_simplex_solution, + std::bind(&bb_observer_adapter_t::set_simplex_solution, &solution_helper, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); branch_and_bound_settings.node_processed_callback = - std::bind(&branch_and_bound_solution_helper_t::node_processed_callback, + std::bind(&bb_observer_adapter_t::node_processed_callback, &solution_helper, std::placeholders::_1, std::placeholders::_2); @@ -387,83 +356,18 @@ solution_t mip_solver_t::run_solver() context.stats.set_solution_bound( context.problem_ptr->get_user_obj_from_solver_obj(branch_and_bound_solution.lower_bound)); } - if (bb_status == dual_simplex::mip_status_t::INFEASIBLE) { sol.set_problem_fully_reduced(); } if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB) && std::isfinite(branch_and_bound_solution.objective)) { - CUOPT_DETERMINISM_LOG( - "Deterministic solver B&B overwrite: bb_status=%d bb_obj=%.16e bb_lower=%.16e " - "bb_x_size=%zu bb_has_incumbent=%d " - "bb_hash=0x%x dm_hash=0x%x nodes=%d simplex_iterations=%d", - (int)bb_status, - branch_and_bound_solution.objective, - branch_and_bound_solution.lower_bound, - branch_and_bound_solution.x.size(), - (int)branch_and_bound_solution.has_incumbent, - detail::compute_hash(branch_and_bound_solution.x), - sol.get_hash(), - branch_and_bound_solution.nodes_explored, - branch_and_bound_solution.simplex_iterations); solution_t bb_sol(*context.problem_ptr); bb_sol.copy_new_assignment(branch_and_bound_solution.x); bool bb_feasible = bb_sol.compute_feasibility(); - f_t max_cstr_vio = bb_sol.compute_max_constraint_violation(); - f_t max_int_vio = bb_sol.compute_max_int_violation(); - f_t max_bnd_vio = bb_sol.compute_max_variable_violation(); - CUOPT_DETERMINISM_LOG( - "Deterministic B&B overwrite feasibility: feasible=%d obj=%.16e user_obj=%.16e " - "max_cstr_vio=%.6e max_int_vio=%.6e max_bnd_vio=%.6e " - "n_integers=%d n_int_vars=%d n_feas_cstr=%d n_cstr=%d hash=0x%x", - (int)bb_feasible, - bb_sol.get_objective(), - bb_sol.get_user_objective(), - max_cstr_vio, - max_int_vio, - max_bnd_vio, - bb_sol.n_assigned_integers, - context.problem_ptr->n_integer_vars, - bb_sol.n_feasible_constraints.value(bb_sol.handle_ptr->get_stream()), - context.problem_ptr->n_constraints, - bb_sol.get_hash()); - if (!bb_feasible) { - auto stream = context.problem_ptr->handle_ptr->get_stream(); - auto h_clb = cuopt::host_copy(context.problem_ptr->constraint_lower_bounds, stream); - auto h_cub = cuopt::host_copy(context.problem_ptr->constraint_upper_bounds, stream); - auto h_coef = cuopt::host_copy(context.problem_ptr->coefficients, stream); - auto h_vars = cuopt::host_copy(context.problem_ptr->variables, stream); - auto h_offs = cuopt::host_copy(context.problem_ptr->offsets, stream); - const auto& x = branch_and_bound_solution.x; - for (i_t row = 0; row < context.problem_ptr->n_constraints; ++row) { - f_t ax = 0; - for (i_t p = h_offs[row]; p < h_offs[row + 1]; ++p) { - ax += h_coef[p] * x[h_vars[p]]; - } - f_t lo_vio = std::max(0.0, (double)(h_clb[row] - ax)); - f_t hi_vio = std::max(0.0, (double)(ax - h_cub[row])); - if (lo_vio > 1e-6 || hi_vio > 1e-6) { - CUOPT_DETERMINISM_LOG( - " Constraint %d violated: Ax=%.16e lb=%.16e ub=%.16e lo_vio=%.6e hi_vio=%.6e " - "nnz=%d", - row, - ax, - h_clb[row], - h_cub[row], - lo_vio, - hi_vio, - h_offs[row + 1] - h_offs[row]); - } - } - } if (bb_feasible) { sol = std::move(bb_sol); } } else if ((context.settings.determinism_mode & CUOPT_DETERMINISM_BB)) { - CUOPT_DETERMINISM_LOG( - "Deterministic B&B overwrite skipped: bb_obj=%.16e bb_obj_finite=%d bb_has_incumbent=%d", - branch_and_bound_solution.objective, - (int)std::isfinite(branch_and_bound_solution.objective), - (int)branch_and_bound_solution.has_incumbent); // In deterministic mode, only solutions formally retired by B&B are valid output. // Discard the GPU heuristic incumbent that B&B never processed. sol = solution_t(*context.problem_ptr); } + if (bb_status == dual_simplex::mip_status_t::INFEASIBLE) { sol.set_problem_fully_reduced(); } context.stats.num_nodes = branch_and_bound_solution.nodes_explored; context.stats.num_simplex_iterations = branch_and_bound_solution.simplex_iterations; diff --git a/cpp/src/mip_heuristics/solver.cuh b/cpp/src/mip_heuristics/solver.cuh index 58f2d4ff42..177fe6e14a 100644 --- a/cpp/src/mip_heuristics/solver.cuh +++ b/cpp/src/mip_heuristics/solver.cuh @@ -27,6 +27,7 @@ class mip_solver_t { solver_stats_t& get_solver_stats() { return context.stats; } mip_solver_context_t context; + // reference to the original problem const problem_t& op_problem_; const mip_solver_settings_t& solver_settings_; cuopt::termination_checker_t& timer_; diff --git a/cpp/src/mip_heuristics/solver_context.cuh b/cpp/src/mip_heuristics/solver_context.cuh index 78b9b8a1f3..1acf57c5f7 100644 --- a/cpp/src/mip_heuristics/solver_context.cuh +++ b/cpp/src/mip_heuristics/solver_context.cuh @@ -19,9 +19,7 @@ #include #include -#include #include -#include #include #include @@ -37,8 +35,6 @@ namespace cuopt::linear_programming::detail { struct mip_solver_work_unit_predictors_t { work_unit_predictor_t fj_predictor{}; - work_unit_predictor_t cpufj_predictor{}; - work_unit_predictor_t pdlp_predictor{}; }; template class diversity_manager_t; @@ -51,36 +47,6 @@ struct solution_callback_payload_t { internals::mip_solution_callback_info_t callback_info{}; }; -template -solution_callback_payload_t make_solution_callback_payload_from_solution( - problem_t* problem_ptr, - const mip_solver_settings_t& settings, - pdlp_initial_scaling_strategy_t& scaling, - work_limit_context_t& gpu_heur_loop, - solution_t& sol, - internals::mip_solution_origin_t callback_origin, - double work_timestamp) -{ - cuopt_assert(problem_ptr != nullptr, "Callback payload problem pointer must not be null"); - cuopt_assert(work_timestamp >= 0.0, "work_timestamp must not be negative"); - solution_callback_payload_t payload{}; - payload.user_objective = sol.get_user_objective(); - payload.solver_objective = sol.get_objective(); - payload.callback_info.origin = callback_origin; - payload.callback_info.work_timestamp = work_timestamp; - solution_t temp_sol(sol); - problem_ptr->post_process_assignment(temp_sol.assignment); - if (settings.mip_scaling) { - rmm::device_uvector dummy(0, temp_sol.handle_ptr->get_stream()); - scaling.unscale_solutions(temp_sol.assignment, dummy); - } - if (problem_ptr->has_papilo_presolve_data()) { - problem_ptr->papilo_uncrush_assignment(temp_sol.assignment); - } - payload.assignment = temp_sol.get_host_assignment(); - return payload; -} - template class solution_publication_t { public: @@ -95,6 +61,57 @@ class solution_publication_t { best_callback_feasible_objective_ = objective; } + solution_callback_payload_t build_payload( + problem_t* problem_ptr, + pdlp_initial_scaling_strategy_t& scaling, + solution_t& sol, + internals::mip_solution_origin_t origin, + double work_timestamp) + { + cuopt_assert(problem_ptr != nullptr, "Callback payload problem pointer must not be null"); + cuopt_assert(work_timestamp >= 0.0, "work_timestamp must not be negative"); + solution_callback_payload_t payload{}; + payload.user_objective = sol.get_user_objective(); + payload.solver_objective = sol.get_objective(); + payload.callback_info.origin = origin; + payload.callback_info.work_timestamp = work_timestamp; + solution_t temp_sol(sol); + problem_ptr->post_process_assignment(temp_sol.assignment); + if (settings.mip_scaling) { + rmm::device_uvector dummy(0, temp_sol.handle_ptr->get_stream()); + scaling.unscale_solutions(temp_sol.assignment, dummy); + } + if (problem_ptr->has_papilo_presolve_data()) { + problem_ptr->papilo_uncrush_assignment(temp_sol.assignment); + } + payload.assignment = temp_sol.get_host_assignment(); + return payload; + } + + bool publish_new_best_feasible(const solution_callback_payload_t& payload, + double elapsed_time = -1.0) + { + std::lock_guard lock(solution_callback_mutex_); + cuopt_assert(std::isfinite(payload.solver_objective), + "Feasible incumbent objective must be finite"); + if (!(payload.solver_objective < best_callback_feasible_objective_)) { return false; } + + if (settings.benchmark_info_ptr != nullptr && elapsed_time >= 0.0) { + settings.benchmark_info_ptr->last_improvement_of_best_feasible = elapsed_time; + } + invoke_get_solution_callbacks(payload); + best_callback_feasible_objective_ = payload.solver_objective; + return true; + } + + void publish_terminal_solution(const solution_callback_payload_t& payload) + { + std::lock_guard lock(solution_callback_mutex_); + invoke_get_solution_callbacks(payload); + best_callback_feasible_objective_ = payload.solver_objective; + } + + private: void invoke_get_solution_callbacks(const solution_callback_payload_t& payload) { auto user_callbacks = settings.get_mip_callbacks(); @@ -127,27 +144,86 @@ class solution_publication_t { } } - bool publish_new_best_feasible(const solution_callback_payload_t& payload, - double elapsed_time = -1.0) + const mip_solver_settings_t& settings; + solver_stats_t& stats; + std::mutex solution_callback_mutex_; + f_t best_callback_feasible_objective_{std::numeric_limits::max()}; +}; + +// Processes SET_SOLUTION user callbacks: invokes the callback, validates/scales/preprocesses +// the returned assignment, and returns it for the caller to reinject. +template +class solution_injection_t { + public: + solution_injection_t(const mip_solver_settings_t& settings_, + solver_stats_t& stats_) + : settings(settings_), stats(stats_) { - std::lock_guard lock(solution_callback_mutex_); - cuopt_assert(std::isfinite(payload.solver_objective), - "Feasible incumbent objective must be finite"); - if (!(payload.solver_objective < best_callback_feasible_objective_)) { return false; } + } - if (settings.benchmark_info_ptr != nullptr && elapsed_time >= 0.0) { - settings.benchmark_info_ptr->last_improvement_of_best_feasible = elapsed_time; + // Invokes SET_SOLUTION callbacks with current incumbent info. + // For each callback that returns a valid solution, calls `on_injected` with the processed + // host assignment and solver-space objective. + template + void invoke_set_solution_callbacks(problem_t* problem_ptr, + pdlp_initial_scaling_strategy_t& scaling, + solution_t& current_incumbent, + OnInjectedFn&& on_injected) + { + auto user_callbacks = settings.get_mip_callbacks(); + for (auto callback : user_callbacks) { + if (callback->get_type() != internals::base_solution_callback_type::SET_SOLUTION) { + continue; + } + auto set_sol_callback = static_cast(callback); + f_t user_bound = stats.get_solution_bound(); + auto callback_num_variables = problem_ptr->original_problem_ptr->get_n_variables(); + rmm::device_uvector incumbent_assignment(callback_num_variables, + current_incumbent.handle_ptr->get_stream()); + auto inf = std::numeric_limits::infinity(); + current_incumbent.handle_ptr->sync_stream(); + std::vector h_incumbent_assignment(incumbent_assignment.size()); + std::vector h_outside_sol_objective(1, inf); + std::vector h_user_bound(1, user_bound); + set_sol_callback->set_solution(h_incumbent_assignment.data(), + h_outside_sol_objective.data(), + h_user_bound.data(), + set_sol_callback->get_user_data()); + f_t outside_sol_objective = h_outside_sol_objective[0]; + if (outside_sol_objective == inf) { continue; } + + raft::copy(incumbent_assignment.data(), + h_incumbent_assignment.data(), + incumbent_assignment.size(), + current_incumbent.handle_ptr->get_stream()); + if (settings.mip_scaling) { scaling.scale_solutions(incumbent_assignment); } + bool is_valid = problem_ptr->pre_process_assignment(incumbent_assignment); + if (!is_valid) { continue; } + + solution_t outside_sol(current_incumbent); + cuopt_assert(outside_sol.assignment.size() == incumbent_assignment.size(), + "Incumbent assignment size mismatch"); + raft::copy(outside_sol.assignment.data(), + incumbent_assignment.data(), + incumbent_assignment.size(), + current_incumbent.handle_ptr->get_stream()); + outside_sol.compute_feasibility(); + + CUOPT_LOG_DEBUG("Injected solution feasibility = %d objective = %g excess = %g", + outside_sol.get_feasible(), + outside_sol.get_user_objective(), + outside_sol.get_total_excess()); + cuopt_assert(std::abs(outside_sol.get_user_objective() - outside_sol_objective) <= 1e-6, + "External solution objective mismatch"); + on_injected(outside_sol.get_host_assignment(), + outside_sol.get_objective(), + internals::mip_solution_origin_t::USER_INITIAL); } - invoke_get_solution_callbacks(payload); - best_callback_feasible_objective_ = payload.solver_objective; - return true; } private: const mip_solver_settings_t& settings; solver_stats_t& stats; - std::mutex solution_callback_mutex_; - f_t best_callback_feasible_objective_{std::numeric_limits::max()}; }; // Aggregate structure containing the global context of the solving process for convenience: @@ -162,7 +238,8 @@ struct mip_solver_context_t { problem_ptr(problem_ptr_), settings(settings_), scaling(scaling), - solution_publication(settings, stats) + solution_publication(settings, stats), + solution_injection(settings, stats) { cuopt_assert(problem_ptr != nullptr, "problem_ptr is nullptr"); stats.set_solution_bound(problem_ptr->maximize ? std::numeric_limits::infinity() @@ -191,6 +268,7 @@ struct mip_solver_context_t { // GPU heuristic loop) work_limit_context_t gpu_heur_loop{"GPUHeur"}; solution_publication_t solution_publication; + solution_injection_t solution_injection; // Root termination checker — set by mip_solver_t after construction. // All sub-timers should use this as parent for wall-clock safety. diff --git a/cpp/src/pdlp/cuopt_c.cpp b/cpp/src/pdlp/cuopt_c.cpp index 5b2a0a7ba5..0c9736646f 100644 --- a/cpp/src/pdlp/cuopt_c.cpp +++ b/cpp/src/pdlp/cuopt_c.cpp @@ -66,7 +66,7 @@ class c_get_solution_callback_ext_t : public cuopt::internals::get_solution_call cuOptMIPSolutionCallbackInfo c_callback_info{}; c_callback_info.struct_size = sizeof(cuOptMIPSolutionCallbackInfo); if (callback_info != nullptr) { - c_callback_info.origin = static_cast(callback_info->origin); + c_callback_info.origin = (uint32_t)callback_info->origin; c_callback_info.work_timestamp = callback_info->work_timestamp; } else { c_callback_info.origin = CUOPT_MIP_SOLUTION_ORIGIN_UNKNOWN; diff --git a/cpp/src/pdlp/pdlp.cu b/cpp/src/pdlp/pdlp.cu index 650c58b83f..a6f16ec972 100644 --- a/cpp/src/pdlp/pdlp.cu +++ b/cpp/src/pdlp/pdlp.cu @@ -16,10 +16,6 @@ #include #include -#include -#include - -#include #include "cuopt/linear_programming/pdlp/solver_solution.hpp" #include @@ -2573,18 +2569,6 @@ optimization_problem_solution_t pdlp_solver_t::run_solver(co ++internal_solver_iterations_; if (settings_.hyper_params.never_restart_to_average) restart_strategy_.increment_iteration_since_last_restart(); - - if (inside_mip_ && - (internal_solver_iterations_ % 10 == 0 || internal_solver_iterations_ <= 3)) { - auto primal_hash = detail::compute_hash( - make_span(pdhg_solver_.get_potential_next_primal_solution()), stream_view_); - auto dual_hash = detail::compute_hash( - make_span(pdhg_solver_.get_potential_next_dual_solution()), stream_view_); - CUOPT_DETERMINISM_LOG("PDLP iter %d: primal_hash=0x%x dual_hash=0x%x", - internal_solver_iterations_, - primal_hash, - dual_hash); - } } return optimization_problem_solution_t{pdlp_termination_status_t::NumericalError, stream_view_}; @@ -2991,24 +2975,6 @@ pdlp_solver_t::get_current_termination_strategy() return current_termination_strategy_; } -template -uint32_t pdlp_solver_t::get_scaling_hash() -{ - auto stream = handle_ptr_->get_stream(); - auto v = initial_scaling_strategy_.view(); - uint32_t h = 0; - h ^= detail::compute_hash(v.cummulative_constraint_matrix_scaling, stream); - h ^= detail::compute_hash(v.cummulative_variable_scaling, stream) * 2654435761u; - auto bound_val = initial_scaling_strategy_.get_h_bound_rescaling(); - auto obj_val = initial_scaling_strategy_.get_h_objective_rescaling(); - uint32_t bh, oh; - std::memcpy(&bh, &bound_val, std::min(sizeof(bh), sizeof(bound_val))); - std::memcpy(&oh, &obj_val, std::min(sizeof(oh), sizeof(obj_val))); - h ^= bh * 2246822519u; - h ^= oh * 3266489917u; - return h; -} - #if MIP_INSTANTIATE_FLOAT || PDLP_INSTANTIATE_FLOAT template class pdlp_solver_t; diff --git a/cpp/src/pdlp/pdlp.cuh b/cpp/src/pdlp/pdlp.cuh index 9ebae676f0..de0cf69c91 100644 --- a/cpp/src/pdlp/pdlp.cuh +++ b/cpp/src/pdlp/pdlp.cuh @@ -97,8 +97,6 @@ class pdlp_solver_t { void set_inside_mip(bool inside_mip); - uint32_t get_scaling_hash(); - void compute_initial_step_size(); void compute_initial_primal_weight(); diff --git a/cpp/src/pdlp/pdlp_features.hpp b/cpp/src/pdlp/pdlp_features.hpp deleted file mode 100644 index 6af6b01053..0000000000 --- a/cpp/src/pdlp/pdlp_features.hpp +++ /dev/null @@ -1,243 +0,0 @@ -/* clang-format off */ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ -/* clang-format on */ - -#pragma once - -#include -#include -#include - -namespace cuopt::linear_programming::detail { - -// Feature logging interval (every N iterations) -constexpr int PDLP_FEATURE_LOG_INTERVAL = 50; - -/** - * @brief Feature collection structure for PDLP Stable3 runtime prediction. - * - * This structure collects features that can be used to train regression models - * for predicting the runtime of PDLP iterations in Stable3 mode. - * - * Stable3 key characteristics: - * - No adaptive step size retries (deterministic SpMV count) - * - Uses reflected primal/dual (Halpern update) - * - Uses fixed point error computation on major iterations - * - Uses conditional major iterations (frequency increases with iteration count) - * - Never restarts to average (only to current solution) - */ -template -struct pdlp_features_t { - // ========================================================================= - // Problem Features (static, set once at initialization) - // ========================================================================= - i_t n_vars{0}; // Number of variables - i_t n_cstrs{0}; // Number of constraints - int64_t nnz{0}; // Number of nonzeros in constraint matrix - f_t sparsity{0.0}; // nnz / (n_vars * n_cstrs) - f_t nnz_stddev{0.0}; // Standard deviation of row nnz counts - f_t unbalancedness{0.0}; // nnz_stddev / mean_nnz_per_row - - // ========================================================================= - // Interval Counters (reset after each log) - // ========================================================================= - i_t interval_iters{0}; // Iterations in this logging interval - i_t interval_major{0}; // Major iterations in this interval - i_t interval_restarts{0}; // Restarts in this interval - - // ========================================================================= - // SpMV Metrics - // ========================================================================= - // In Stable3: regular iter = 2 SpMV, major iter = 3 SpMV (fixed point error) - int64_t interval_spmv_ops{0}; // SpMV operations in this interval - - // ========================================================================= - // Cumulative Counters (never reset) - // ========================================================================= - i_t total_iters{0}; // Total iterations since solver start - i_t total_restarts{0}; // Total restarts since solver start - i_t iters_since_restart{0}; // Iterations since last restart - - // ========================================================================= - // Convergence Metrics (snapshot at log time, from last major iteration) - // ========================================================================= - f_t primal_res{0.0}; // L2 primal residual - f_t dual_res{0.0}; // L2 dual residual - f_t gap{0.0}; // Duality gap - f_t kkt_score{0.0}; // KKT score (used for restart decisions) - - // ========================================================================= - // Step Parameters (can change on restarts) - // ========================================================================= - f_t step_size{0.0}; // Current step size - f_t primal_weight{0.0}; // Current primal weight - - // ========================================================================= - // Timing - // ========================================================================= - f_t interval_time_ms{0.0}; // Time elapsed in this interval (milliseconds) - - // ========================================================================= - // Warm Start Info - // ========================================================================= - bool has_warm_start{false}; // Whether warm start was provided - - /** - * @brief Initialize static problem features. - * - * Called once at solver initialization. Computes sparsity metrics from - * the problem structure. - */ - void init_from_problem(i_t n_variables, - i_t n_constraints, - int64_t num_nonzeros, - f_t computed_sparsity, - f_t computed_nnz_stddev, - f_t computed_unbalancedness, - bool warm_start) - { - n_vars = n_variables; - n_cstrs = n_constraints; - nnz = num_nonzeros; - sparsity = computed_sparsity; - nnz_stddev = computed_nnz_stddev; - unbalancedness = computed_unbalancedness; - has_warm_start = warm_start; - } - - /** - * @brief Record a regular iteration. - * - * In Stable3, each regular iteration does 2 SpMV operations: - * - compute_At_y(): A^T @ y - * - compute_A_x(): A @ x - */ - void record_regular_iteration() - { - ++interval_iters; - ++total_iters; - ++iters_since_restart; - interval_spmv_ops += 2; - } - - /** - * @brief Record a major iteration. - * - * Major iterations do an additional SpMV for fixed point error computation. - * They also involve termination checks and potential restarts. - */ - void record_major_iteration() - { - ++interval_major; - // Fixed point error computation adds 1 SpMV - interval_spmv_ops += 1; - } - - /** - * @brief Record a restart event. - */ - void record_restart() - { - ++interval_restarts; - ++total_restarts; - iters_since_restart = 0; - } - - /** - * @brief Update convergence metrics from termination strategy. - * - * Called during major iterations when convergence info is computed. - */ - void update_convergence(f_t l2_primal_residual, - f_t l2_dual_residual, - f_t duality_gap, - f_t computed_kkt_score) - { - primal_res = l2_primal_residual; - dual_res = l2_dual_residual; - gap = duality_gap; - kkt_score = computed_kkt_score; - } - - /** - * @brief Update step parameters. - * - * Called when step size or primal weight changes (typically after restarts). - */ - void update_step_params(f_t current_step_size, f_t current_primal_weight) - { - step_size = current_step_size; - primal_weight = current_primal_weight; - } - - /** - * @brief Log all features in key=value format. - * - * Format: PDLP_RESULT: n_vars=N n_cstrs=M nnz=K ... - * - * This format is parsed by determinism_logs_parse.py with --algorithm PDLP - */ - void log_features() const - { - // Compute derived metrics - const int64_t total_nnz_processed = interval_spmv_ops * nnz; - const double nnz_per_sec = (interval_time_ms > 0) ? static_cast(total_nnz_processed) / - (interval_time_ms / 1000.0) - : 0.0; - - // printf( - // "PDLP_RESULT: n_vars=%d n_cstrs=%d nnz=%ld sparsity=%.6e nnz_stddev=%.4f " - // "unbalancedness=%.4f interval_iters=%d interval_major=%d interval_restarts=%d " - // "spmv_ops=%ld total_iters=%d total_restarts=%d iters_since_restart=%d " - // "primal_res=%.6e dual_res=%.6e gap=%.6e kkt=%.6e " - // "step_size=%.6e primal_weight=%.6e time_ms=%.2f nnz_per_s=%.2e warm_start=%d", - // n_vars, - // n_cstrs, - // nnz, - // sparsity, - // nnz_stddev, - // unbalancedness, - // interval_iters, - // interval_major, - // interval_restarts, - // interval_spmv_ops, - // total_iters, - // total_restarts, - // iters_since_restart, - // primal_res, - // dual_res, - // gap, - // kkt_score, - // step_size, - // primal_weight, - // interval_time_ms, - // nnz_per_sec, - // static_cast(has_warm_start)); - } - - /** - * @brief Reset per-interval counters. - * - * Called after each log to start fresh for the next interval. - */ - void reset_interval_counters() - { - interval_iters = 0; - interval_major = 0; - interval_restarts = 0; - interval_spmv_ops = 0; - interval_time_ms = 0.0; - } - - /** - * @brief Check if it's time to log features. - * - * Returns true every PDLP_FEATURE_LOG_INTERVAL iterations. - */ - bool should_log() const { return (interval_iters >= PDLP_FEATURE_LOG_INTERVAL); } -}; - -} // namespace cuopt::linear_programming::detail diff --git a/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu b/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu index 31376f6045..149e99a431 100644 --- a/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu +++ b/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu @@ -1959,9 +1959,6 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( // Perform the reduction // Convert raw pointer to thrust::device_ptr to write directly device side through reduce - // TODO - // use a guaranteed-deterministic reduce instead of thrust::reduce which I'm not sure actually - // guarantees determinism (but in practice it does) thrust::device_ptr thrust_hrsp(high_radius_squared_.data()); *thrust_hrsp = thrust::reduce(handle_ptr_->get_thrust_policy(), transformed_begin, diff --git a/cpp/src/pdlp/termination_strategy/infeasibility_information.cu b/cpp/src/pdlp/termination_strategy/infeasibility_information.cu index 235a399bd2..dbb35b732d 100644 --- a/cpp/src/pdlp/termination_strategy/infeasibility_information.cu +++ b/cpp/src/pdlp/termination_strategy/infeasibility_information.cu @@ -14,7 +14,6 @@ #include #include -#include #include #include @@ -90,19 +89,6 @@ infeasibility_information_t::infeasibility_information_t( climber_strategies_(climber_strategies), hyper_params_(hyper_params) { - cuopt::mark_memory_as_initialized(max_primal_ray_infeasibility_.data(), - max_primal_ray_infeasibility_.size() * sizeof(f_t), - stream_view_); - cuopt::mark_memory_as_initialized(primal_ray_linear_objective_.data(), - primal_ray_linear_objective_.size() * sizeof(f_t), - stream_view_); - cuopt::mark_memory_as_initialized(max_dual_ray_infeasibility_.data(), - max_dual_ray_infeasibility_.size() * sizeof(f_t), - stream_view_); - cuopt::mark_memory_as_initialized(dual_ray_linear_objective_.data(), - dual_ray_linear_objective_.size() * sizeof(f_t), - stream_view_); - if (infeasibility_detection) { RAFT_CUDA_TRY(cudaMemsetAsync(homogenous_primal_residual_.data(), 0.0, @@ -145,6 +131,24 @@ infeasibility_information_t::infeasibility_information_t( size_of_buffer_ = std::max({temp_storage_bytes_1, temp_storage_bytes_2}); this->rmm_tmp_buffer_ = rmm::device_buffer{size_of_buffer_, stream_view_}; + + RAFT_CUDA_TRY(cudaMemsetAsync(dual_ray_linear_objective_.data(), + 0, + sizeof(f_t) * dual_ray_linear_objective_.size(), + stream_view_)); + RAFT_CUDA_TRY(cudaMemsetAsync(max_dual_ray_infeasibility_.data(), + 0, + sizeof(f_t) * max_dual_ray_infeasibility_.size(), + stream_view_)); + + RAFT_CUDA_TRY(cudaMemsetAsync(primal_ray_linear_objective_.data(), + 0, + sizeof(f_t) * primal_ray_linear_objective_.size(), + stream_view_)); + RAFT_CUDA_TRY(cudaMemsetAsync(max_primal_ray_infeasibility_.data(), + 0, + sizeof(f_t) * max_primal_ray_infeasibility_.size(), + stream_view_)); } } diff --git a/cpp/src/utilities/embed_models.sh b/cpp/src/utilities/embed_models.sh deleted file mode 100755 index f402c411ce..0000000000 --- a/cpp/src/utilities/embed_models.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/sh -# 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. - -set -e - -if [ $# -lt 2 ]; then - echo "Usage: $0 [model2.ubj.gz ...]" - exit 1 -fi - -OUTPUT_HEADER="$1" -shift - -# Initialize the header file -echo "// Auto-generated model arrays" > "$OUTPUT_HEADER" -echo "" >> "$OUTPUT_HEADER" - -# Collect model names for the mapping array -MODEL_NAMES="" - -# Process each model file -for MODEL_FILE in "$@"; do - # Extract base name without .ubj.gz extension - MODEL_BASENAME=$(basename "$MODEL_FILE" .ubj.gz) - - echo "Processing: $MODEL_BASENAME from $MODEL_FILE" - - # Create temporary file with proper name so xxd uses it - TEMP_DIR=$(mktemp -d) - TEMP_FILE="$TEMP_DIR/${MODEL_BASENAME}.ubj" - - # Decompress the model - gzip -cd "$MODEL_FILE" > "$TEMP_FILE" - - # Convert to C array with xxd and append to header - # xxd generates variable names like: _tmp_tmp_xyz_model_ubj - # We need to replace the entire variable name, not just add to it - # Add const to place arrays in .rodata section - xxd -i "$TEMP_FILE" | \ - sed "s/unsigned char [_a-zA-Z0-9]*/const unsigned char ${MODEL_BASENAME}_ubj/" | \ - sed "s/unsigned int [_a-zA-Z0-9]*/const unsigned int ${MODEL_BASENAME}_ubj_len/" >> "$OUTPUT_HEADER" - - # Clean up - rm -rf "$TEMP_DIR" - - # Add to list for mapping array - if [ -z "$MODEL_NAMES" ]; then - MODEL_NAMES="$MODEL_BASENAME" - else - MODEL_NAMES="$MODEL_NAMES $MODEL_BASENAME" - fi -done - -# Add model mapping structure -echo "" >> "$OUTPUT_HEADER" -echo "// Model mapping structure" >> "$OUTPUT_HEADER" -echo "struct xgboost_model_info {" >> "$OUTPUT_HEADER" -echo " const char* name;" >> "$OUTPUT_HEADER" -echo " const unsigned char* data;" >> "$OUTPUT_HEADER" -echo " unsigned int length;" >> "$OUTPUT_HEADER" -echo "};" >> "$OUTPUT_HEADER" -echo "" >> "$OUTPUT_HEADER" - -# Generate the mapping array -echo "// Array of all available models" >> "$OUTPUT_HEADER" -echo "static const struct xgboost_model_info xgboost_models[] = {" >> "$OUTPUT_HEADER" - -for MODEL_NAME in $MODEL_NAMES; do - echo " {\"$MODEL_NAME\", ${MODEL_NAME}_ubj, ${MODEL_NAME}_ubj_len}," >> "$OUTPUT_HEADER" -done - -echo "};" >> "$OUTPUT_HEADER" -echo "" >> "$OUTPUT_HEADER" -echo "static const unsigned int xgboost_models_count = sizeof(xgboost_models) / sizeof(xgboost_models[0]);" >> "$OUTPUT_HEADER" - -echo "Successfully embedded models into $OUTPUT_HEADER" diff --git a/cpp/src/utilities/models/cpufj_predictor/header.h b/cpp/src/utilities/models/cpufj_predictor/header.h deleted file mode 100644 index 800072da21..0000000000 --- a/cpp/src/utilities/models/cpufj_predictor/header.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -class cpufj_predictor { - public: - union Entry { - int missing; - double fvalue; - int qvalue; - }; - - static int32_t get_num_target(void); - static void get_num_class(int32_t* out); - static int32_t get_num_feature(void); - static const char* get_threshold_type(void); - static const char* get_leaf_output_type(void); - static void predict(union Entry* data, int pred_margin, double* result); - static void postprocess(double* result); - static int quantize(double val, unsigned fid); - - // Feature names - static constexpr int NUM_FEATURES = 4; - static const char* feature_names[NUM_FEATURES]; -}; // class cpufj_data diff --git a/cpp/src/utilities/models/cpufj_predictor/main.cpp b/cpp/src/utilities/models/cpufj_predictor/main.cpp deleted file mode 100644 index 3f4bf08374..0000000000 --- a/cpp/src/utilities/models/cpufj_predictor/main.cpp +++ /dev/null @@ -1,5412 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "header.h" - -#if defined(__clang__) || defined(__GNUC__) -#define LIKELY(x) __builtin_expect(!!(x), 1) -#define UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define LIKELY(x) (x) -#define UNLIKELY(x) (x) -#endif -#define N_TARGET 1 -#define MAX_N_CLASS 1 - -const unsigned char is_categorical[] = { - 0, - 0, - 0, - 0, -}; -static const int32_t num_class[] = { - 1, -}; - -int32_t cpufj_predictor::get_num_target(void) { return N_TARGET; } -void cpufj_predictor::get_num_class(int32_t* out) -{ - for (int i = 0; i < N_TARGET; ++i) { - out[i] = num_class[i]; - } -} -int32_t cpufj_predictor::get_num_feature(void) { return 4; } -const char* cpufj_predictor::get_threshold_type(void) { return "float64"; } -const char* cpufj_predictor::get_leaf_output_type(void) { return "float64"; } - -void cpufj_predictor::predict(union Entry* data, int pred_margin, double* result) -{ - // Quantize data - for (int i = 0; i < 4; ++i) { - if (data[i].missing != -1 && !is_categorical[i]) { - data[i].qvalue = quantize(data[i].fvalue, i); - } - } - - unsigned int tmp; - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += 156.82697390964927; - } else { - result[0] += 160.51072839782955; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 24))) { - result[0] += 167.07152558194775; - } else { - result[0] += 173.16044577743776; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (UNLIKELY(false || (data[0].qvalue <= 60))) { - result[0] += 197.67049116907555; - } else { - result[0] += 193.73228835720744; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 204.19401403625514; - } else { - result[0] += 233.21262399057034; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - result[0] += 271.7565681764123; - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 384.56067848347016; - } else { - result[0] += 490.5143183873164; - } - } - } - if (LIKELY(false || (data[2].qvalue <= 50))) { - if (LIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[3].qvalue <= 114))) { - if (LIKELY(false || (data[3].qvalue <= 74))) { - result[0] += -13.943317103763608; - } else { - result[0] += -10.411451244915156; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -1.6607479643348968; - } else { - result[0] += 1.873995960655583; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += 13.317445150475752; - } else { - result[0] += -3.2833804644580917; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 42))) { - result[0] += 28.787436474976477; - } else { - result[0] += 22.808869505615306; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 116))) { - result[0] += 3.5889216009186664; - } else { - result[0] += 5.910052863589392; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 210))) { - result[0] += 89.99770637090484; - } else { - result[0] += 127.79448643063864; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 191.2644805975275; - } else { - result[0] += 286.4874098858173; - } - } - } - if (LIKELY(false || (data[2].qvalue <= 50))) { - if (LIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[3].qvalue <= 114))) { - if (LIKELY(false || (data[3].qvalue <= 76))) { - result[0] += -12.539408056743746; - } else { - result[0] += -9.311441710639189; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -1.4947050956018029; - } else { - result[0] += 1.6866257811476963; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += 11.98745272164608; - } else { - result[0] += -2.9554856143381034; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 22.718528792836054; - } else { - result[0] += 52.594838808257; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 116))) { - result[0] += 3.2306536812875586; - } else { - result[0] += 5.321595177064682; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 79.5197036158342; - } else { - result[0] += 112.02363049281628; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 172.34821010044644; - } else { - result[0] += 257.92682935697115; - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -11.281112483305671; - } else { - result[0] += -8.601904513350851; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 24))) { - result[0] += -4.457196969778106; - } else { - result[0] += 0.6775720991155234; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (UNLIKELY(false || (data[0].qvalue <= 60))) { - result[0] += 18.28399618068051; - } else { - result[0] += 15.34024990698243; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 23.56902428310345; - } else { - result[0] += 47.371626254377695; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - result[0] += 72.50774203412541; - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 155.30278921274038; - } else { - result[0] += 232.21351482872598; - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[3].qvalue <= 114))) { - if (LIKELY(false || (data[3].qvalue <= 78))) { - result[0] += -10.157360048782069; - } else { - result[0] += -7.469901314898597; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -1.2836068490987675; - } else { - result[0] += 1.4928852675361874; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 148))) { - if (LIKELY(false || (data[0].qvalue <= 68))) { - result[0] += 11.862787216911165; - } else { - result[0] += -3.825179380397125; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - result[0] += 16.467439176084763; - } else { - result[0] += 22.870842463159736; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 116))) { - result[0] += -4.329979207208883; - } else { - result[0] += -2.4277052596041226; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 206))) { - result[0] += 62.899179793202485; - } else { - result[0] += 89.86758030525095; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 144.9081294228374; - } else { - result[0] += 209.06360369591349; - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[3].qvalue <= 118))) { - if (LIKELY(false || (data[3].qvalue <= 80))) { - result[0] += -9.111244309609868; - } else { - result[0] += -6.450300041406144; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 146))) { - result[0] += -0.908464880592677; - } else { - result[0] += 1.3456266411166913; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 144))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 9.598829002806253; - } else { - result[0] += -3.458980194428218; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - result[0] += 14.685884008167873; - } else { - result[0] += 19.70403341262041; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 116))) { - result[0] += -3.8977343521833427; - } else { - result[0] += -2.185981180004069; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 202))) { - result[0] += 53.636361895007624; - } else { - result[0] += 73.84635747136831; - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 54))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 109.30218868014822; - } else { - result[0] += 130.5223189467278; - } - } else { - result[0] += 188.22157308443514; - } - } - } - if (LIKELY(false || (data[2].qvalue <= 50))) { - if (LIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[3].qvalue <= 114))) { - if (LIKELY(false || (data[3].qvalue <= 74))) { - result[0] += -8.242985704258855; - } else { - result[0] += -6.103964758757681; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 122))) { - result[0] += -3.0003459256849077; - } else { - result[0] += 0.5938833671659278; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 148))) { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += 9.054639032941743; - } else { - result[0] += -3.3592089510831986; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 18.040143668653474; - } else { - result[0] += 13.175784904763427; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 236))) { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 118))) { - result[0] += -3.214705374718561; - } else { - result[0] += 0.31848526393665993; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 202))) { - result[0] += 48.27447091272866; - } else { - result[0] += 66.18473238316001; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 97.70898775652987; - } else { - result[0] += 117.56467429340749; - } - } else { - result[0] += 169.45732965745194; - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -7.402034186828849; - } else { - result[0] += -5.63837490580214; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 24))) { - result[0] += -3.2673346602099738; - } else { - result[0] += 0.4720904756386771; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (UNLIKELY(false || (data[0].qvalue <= 60))) { - result[0] += 11.693457400016031; - } else { - result[0] += 10.03852332044065; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 15.437715046858271; - } else { - result[0] += 36.647436411890496; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - result[0] += 47.53118689085973; - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 106.35664032988495; - } else { - result[0] += 152.56374281850964; - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -6.661840917407535; - } else { - result[0] += -5.074574846660258; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 24))) { - result[0] += -2.94088446825355; - } else { - result[0] += 0.4248859491848843; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (UNLIKELY(false || (data[0].qvalue <= 60))) { - result[0] += 10.52521173349777; - } else { - result[0] += 9.034789845952849; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 13.894155617598422; - } else { - result[0] += 33.00796459750472; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 42.823961164678224; - } else { - result[0] += 36.98781896591187; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 95.8378507576408; - } else { - result[0] += 137.35430146859977; - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 12))) { - result[0] += -5.922535414763472; - } else { - result[0] += -3.9535302977307456; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 24))) { - result[0] += -2.647051125523947; - } else { - result[0] += 0.38240139128726525; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (UNLIKELY(false || (data[0].qvalue <= 60))) { - result[0] += 9.473680911813272; - } else { - result[0] += 8.131417758482929; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 42))) { - result[0] += 12.474218356244899; - } else { - result[0] += 28.32144924784816; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 38.542156344073405; - } else { - result[0] += 33.34683068752289; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 86.359383424193; - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 130.56753145370016; - } else { - result[0] += 115.99602850603911; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -5.403420533141926; - } else { - result[0] += -4.075259880702465; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 24))) { - result[0] += -2.3825756020703164; - } else { - result[0] += 0.34416491637505503; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (UNLIKELY(false || (data[0].qvalue <= 62))) { - result[0] += 6.690473585247339; - } else { - result[0] += 7.675205616748645; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 11.251804458709806; - } else { - result[0] += 26.917319955760036; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 34.68847201632427; - } else { - result[0] += 30.064251933097843; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 77.81834311684409; - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 117.59088283047355; - } else { - result[0] += 104.46758514965971; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - if (LIKELY(false || (data[3].qvalue <= 96))) { - if (UNLIKELY(false || (data[3].qvalue <= 22))) { - result[0] += -5.645610033293023; - } else { - result[0] += -4.712904234092419; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 68))) { - result[0] += -1.7467118855770272; - } else { - result[0] += -6.7806729780163355; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -3.3298901489085715; - } else { - result[0] += 0.9879303535654625; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 156))) { - result[0] += 4.765656733422152; - } else { - result[0] += 10.436091866890687; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 118))) { - result[0] += -19.231066348531115; - } else { - result[0] += -15.589280898150276; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 218))) { - if (UNLIKELY(false || (data[3].qvalue <= 200))) { - result[0] += 25.747728264474212; - } else { - result[0] += 40.04473103921778; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 64.38265010597043; - } else { - result[0] += 95.58272802311622; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - if (LIKELY(false || (data[3].qvalue <= 98))) { - if (LIKELY(false || (data[3].qvalue <= 74))) { - result[0] += -4.409030071223973; - } else { - result[0] += -3.5647114194984613; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 68))) { - result[0] += -1.3542190966799321; - } else { - result[0] += -6.103673214709666; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -2.999404705305745; - } else { - result[0] += 0.8891527883879043; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 156))) { - result[0] += 4.289215943564471; - } else { - result[0] += 9.392568858155158; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 116))) { - result[0] += -17.598092511782436; - } else { - result[0] += -16.123547517184555; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 216))) { - if (UNLIKELY(false || (data[3].qvalue <= 200))) { - result[0] += 23.17394211472633; - } else { - result[0] += 35.171000449187375; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 55.995760587885144; - } else { - result[0] += 86.04514530858954; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 22))) { - if (LIKELY(false || (data[1].qvalue <= 12))) { - result[0] += -3.8888486100132043; - } else { - result[0] += -2.5693198969248536; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 46))) { - result[0] += 0.301134908182464; - } else { - result[0] += 4.7321266664777495; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (UNLIKELY(false || (data[0].qvalue <= 60))) { - result[0] += 5.997381523357489; - } else { - result[0] += 5.0375213284435185; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 8.559482811493893; - } else { - result[0] += 22.27496138368804; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 25.27353965981968; - } else { - result[0] += 21.13871028184891; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 55.52418384593922; - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 87.85257342192293; - } else { - result[0] += 76.03356082097153; - } - } - } - } - if (LIKELY(false || (data[2].qvalue <= 50))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - if (LIKELY(false || (data[3].qvalue <= 90))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - result[0] += -3.5064268267703387; - } else { - result[0] += -6.065722823494794; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 68))) { - result[0] += -1.4039415524212295; - } else { - result[0] += -5.997225846159178; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -2.9652186181689757; - } else { - result[0] += 0.7283126984643262; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 154))) { - result[0] += 2.789907904600108; - } else { - result[0] += 7.718533347299168; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - result[0] += -18.00435797338746; - } else { - if (LIKELY(false || (data[3].qvalue <= 214))) { - if (UNLIKELY(false || (data[3].qvalue <= 198))) { - result[0] += 17.330223423965826; - } else { - result[0] += 27.46055877083792; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 46.12967429173014; - } else { - result[0] += 70.70123466855004; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - if (LIKELY(false || (data[3].qvalue <= 102))) { - if (UNLIKELY(false || (data[3].qvalue <= 22))) { - result[0] += -3.888827166320002; - } else { - result[0] += -3.04723191570044; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 34))) { - result[0] += -0.7424769082893876; - } else { - result[0] += -5.398447803084307; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -2.670943097291571; - } else { - result[0] += 0.6554928174570391; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 158))) { - result[0] += 2.9919951855699423; - } else { - result[0] += 7.128361704676317; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 118))) { - result[0] += -16.31666198848907; - } else { - result[0] += -13.216809526331284; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 220))) { - if (LIKELY(false || (data[3].qvalue <= 206))) { - result[0] += 18.698365978047963; - } else { - result[0] += 30.323819222880072; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 54))) { - result[0] += 48.19287170494928; - } else { - result[0] += 66.97776871619591; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[0].qvalue <= 22))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -2.8871386766483713; - } else { - result[0] += -2.194022438199927; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 24))) { - result[0] += -1.2643512510325772; - } else { - result[0] += 0.2946143237550731; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 42))) { - if (UNLIKELY(false || (data[0].qvalue <= 28))) { - result[0] += 6.18334904875928; - } else { - result[0] += 3.4917401374748973; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 42))) { - result[0] += 6.637851347005048; - } else { - result[0] += 17.628961355963423; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 18.427701194716725; - } else { - result[0] += 15.068101975917816; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 39.624109077872816; - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 65.43777984806364; - } else { - result[0] += 54.793414778446135; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - if (LIKELY(false || (data[2].qvalue <= 52))) { - if (LIKELY(false || (data[3].qvalue <= 86))) { - result[0] += -2.5929923519240776; - } else { - result[0] += -1.2308422313969718; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 116))) { - result[0] += -16.777735206670346; - } else { - result[0] += -15.496213790630472; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - if (LIKELY(false || (data[3].qvalue <= 166))) { - result[0] += 3.153837529703604; - } else { - result[0] += 10.653343513120348; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 1.2707685237496547; - } else { - result[0] += -7.577582551412722; - } - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 50))) { - if (LIKELY(false || (data[0].qvalue <= 70))) { - if (UNLIKELY(false || (data[3].qvalue <= 216))) { - result[0] += 5.086264511434565; - } else { - result[0] += 4.004129196876291; - } - } else { - result[0] += 18.011869379087937; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 214))) { - if (UNLIKELY(false || (data[3].qvalue <= 200))) { - result[0] += 12.716813581606937; - } else { - result[0] += 21.19923662562386; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - result[0] += 33.0928592549443; - } else { - result[0] += 48.6196284504888; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (LIKELY(false || (data[3].qvalue <= 104))) { - result[0] += -2.2842871895780763; - } else { - result[0] += -0.6940580648135516; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 118))) { - result[0] += -14.884714915484075; - } else { - result[0] += -12.21852856355555; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (LIKELY(false || (data[0].qvalue <= 68))) { - result[0] += 1.1320691013866366; - } else { - result[0] += -6.824281661647207; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 168))) { - result[0] += 3.133632898542928; - } else { - result[0] += 10.26787174244473; - } - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 56))) { - if (UNLIKELY(false || (data[3].qvalue <= 216))) { - if (UNLIKELY(false || (data[0].qvalue <= 66))) { - result[0] += 0.3519373175810123; - } else { - result[0] += 4.883879225540344; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 234))) { - result[0] += 3.4475335994592777; - } else { - result[0] += 4.289605817529637; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 214))) { - if (UNLIKELY(false || (data[3].qvalue <= 198))) { - result[0] += 10.944053551737898; - } else { - result[0] += 18.520104025075135; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 31.9020659327635; - } else { - result[0] += 47.722870097304835; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (LIKELY(false || (data[3].qvalue <= 84))) { - result[0] += -2.1126641262849875; - } else { - result[0] += -1.0034167734194253; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 114))) { - result[0] += -14.666533134028597; - } else { - result[0] += -13.091642320227521; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -2.67357385169376; - } else { - result[0] += 0.317539563282027; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 172))) { - result[0] += 2.9924052201762423; - } else { - result[0] += 10.018166909712392; - } - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 56))) { - if (UNLIKELY(false || (data[3].qvalue <= 216))) { - if (UNLIKELY(false || (data[2].qvalue <= 46))) { - result[0] += 0.3173503774211839; - } else { - result[0] += 4.396111790242462; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 234))) { - result[0] += 3.1028551632609607; - } else { - result[0] += 3.8610566164274576; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 212))) { - if (UNLIKELY(false || (data[3].qvalue <= 196))) { - result[0] += 9.079411213991389; - } else { - result[0] += 15.631981353661537; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 230))) { - result[0] += 24.953231107660457; - } else { - result[0] += 38.732870695055425; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (LIKELY(false || (data[3].qvalue <= 106))) { - result[0] += -1.8507802734637893; - } else { - result[0] += -0.4780342275095875; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 116))) { - result[0] += -12.281350727645211; - } else { - result[0] += -11.20561954498291; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -2.4082419443490855; - } else { - result[0] += 0.2857906276722831; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 164))) { - result[0] += 1.8193510979340806; - } else { - result[0] += 7.12452591373636; - } - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 56))) { - if (UNLIKELY(false || (data[3].qvalue <= 216))) { - if (UNLIKELY(false || (data[2].qvalue <= 46))) { - result[0] += 0.28616249149215633; - } else { - result[0] += 3.957059242184614; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 236))) { - result[0] += 2.8268370028874497; - } else { - result[0] += 3.686134112733879; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 212))) { - if (LIKELY(false || (data[3].qvalue <= 200))) { - result[0] += 9.321451071849161; - } else { - result[0] += 15.02085895338778; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 24.772053316683984; - } else { - result[0] += 39.09600893416962; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 150))) { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (LIKELY(false || (data[3].qvalue <= 124))) { - result[0] += -1.6348218331553022; - } else { - result[0] += 0.16446761997202702; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 114))) { - result[0] += -11.997166693345555; - } else { - result[0] += -10.593410837279578; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 176))) { - result[0] += -0.26077036500572887; - } else { - result[0] += 0.26721227295908545; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 174))) { - result[0] += 3.50927093938965; - } else { - result[0] += 9.399961201456877; - } - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 56))) { - if (UNLIKELY(false || (data[3].qvalue <= 218))) { - if (UNLIKELY(false || (data[2].qvalue <= 46))) { - result[0] += 0.25803962657163887; - } else { - result[0] += 3.413014759107607; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 230))) { - result[0] += 2.36220596981744; - } else { - result[0] += 2.9185929060991107; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 218))) { - if (LIKELY(false || (data[3].qvalue <= 204))) { - result[0] += 9.258923590762315; - } else { - result[0] += 15.922847318580807; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 25.53935408466994; - } else { - result[0] += 35.194870544491394; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - if (LIKELY(false || (data[2].qvalue <= 52))) { - if (LIKELY(false || (data[3].qvalue <= 82))) { - result[0] += -1.5559766022197978; - } else { - result[0] += -0.6821089851736815; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 118))) { - result[0] += -9.793275858100605; - } else { - result[0] += -7.646164855957032; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -2.185564344146035; - } else { - result[0] += 0.23136153315509006; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 164))) { - result[0] += 1.4281790213369323; - } else { - result[0] += 5.862740580154604; - } - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 52))) { - if (LIKELY(false || (data[0].qvalue <= 70))) { - if (UNLIKELY(false || (data[3].qvalue <= 214))) { - result[0] += 3.1620136193354766; - } else { - result[0] += 2.374787311783622; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 12.394186796439712; - } else { - result[0] += 7.221490024859086; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 222))) { - if (LIKELY(false || (data[3].qvalue <= 202))) { - result[0] += 7.724518171783787; - } else { - result[0] += 14.107049853741623; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 25.09318574808453; - } else { - result[0] += 33.43533238246624; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 150))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 110))) { - result[0] += -1.348448669905975; - } else { - result[0] += 0.3221871974704163; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 126))) { - result[0] += -8.519520394077722; - } else { - result[0] += -4.128400709863002; - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 16))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - result[0] += 0.20971876754977306; - } else { - result[0] += 2.000764232879495; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 176))) { - result[0] += 3.2250018408327294; - } else { - result[0] += 9.091428443343196; - } - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 56))) { - if (UNLIKELY(false || (data[2].qvalue <= 46))) { - result[0] += -0.07772423557166397; - } else { - if (UNLIKELY(false || (data[3].qvalue <= 206))) { - result[0] += 7.465886690298717; - } else { - result[0] += 2.199134462061703; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 212))) { - if (UNLIKELY(false || (data[3].qvalue <= 196))) { - result[0] += 5.510852046313868; - } else { - result[0] += 10.349949332911033; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - result[0] += 17.106146295672758; - } else { - result[0] += 26.416688466239478; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 152))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 120))) { - result[0] += -1.1991422347150693; - } else { - result[0] += 0.46237283922421873; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 126))) { - result[0] += -7.6684254163131875; - } else { - result[0] += -3.7949608540857165; - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 16))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += 0.18039368614868972; - } else { - result[0] += 1.85527472375075; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 172))) { - result[0] += 2.72143013117147; - } else { - result[0] += 6.582942954251957; - } - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 52))) { - if (LIKELY(false || (data[0].qvalue <= 70))) { - if (UNLIKELY(false || (data[3].qvalue <= 214))) { - result[0] += 2.622990881969633; - } else { - result[0] += 1.9174728788131057; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 10.617562740828639; - } else { - result[0] += 5.75643968641758; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 214))) { - if (UNLIKELY(false || (data[3].qvalue <= 196))) { - result[0] += 4.573403401881785; - } else { - result[0] += 9.61596988143479; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 17.169701079810455; - } else { - result[0] += 25.958129802402993; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 152))) { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (LIKELY(false || (data[3].qvalue <= 92))) { - result[0] += -1.130718809808625; - } else { - result[0] += -0.2762627382286883; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 114))) { - result[0] += -8.235156694808095; - } else { - result[0] += -6.951272825766222; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 32))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += 0.16235716809342288; - } else { - result[0] += 1.707373695884848; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 176))) { - result[0] += 2.8296562924600828; - } else { - result[0] += 7.488739806649071; - } - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 56))) { - if (UNLIKELY(false || (data[0].qvalue <= 66))) { - result[0] += -0.32786249211636087; - } else { - if (UNLIKELY(false || (data[3].qvalue <= 206))) { - result[0] += 6.490628203531107; - } else { - result[0] += 1.7818277591800613; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 212))) { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - result[0] += 3.807200463755219; - } else { - result[0] += 8.111758332078471; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 226))) { - result[0] += 13.144779116022248; - } else { - result[0] += 20.863007074513657; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 70))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 22))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -1.0274672289090832; - } else { - result[0] += -0.6823801552731604; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - result[0] += 1.0283586431978333; - } else { - result[0] += -0.23713978185064843; - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[0].qvalue <= 62))) { - result[0] += 2.1905285297360657; - } else { - result[0] += 6.504341335474002; - } - } else { - result[0] += 0.9435717207184171; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (UNLIKELY(false || (data[1].qvalue <= 74))) { - if (UNLIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 3.5741883312100953; - } else { - result[0] += 4.626694408655167; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 74))) { - result[0] += 6.38776066924514; - } else { - result[0] += 7.9433524366525505; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 27.524095430198624; - } else { - result[0] += 17.93763900587164; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 154))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 108))) { - result[0] += -0.8928303502649112; - } else { - result[0] += 0.1926267660463724; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 122))) { - result[0] += -7.0074780337366285; - } else { - result[0] += -3.8229517707148526; - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 16))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - result[0] += 0.17887769899557107; - } else { - result[0] += 1.2261906364605684; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 178))) { - result[0] += 2.7207480610255765; - } else { - result[0] += 8.3997445237607; - } - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 56))) { - if (LIKELY(false || (data[3].qvalue <= 236))) { - if (UNLIKELY(false || (data[3].qvalue <= 218))) { - result[0] += 1.928126817594764; - } else { - result[0] += 1.3411342581129906; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - result[0] += 1.9525385201789491; - } else { - result[0] += 2.4821401437493256; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 212))) { - if (UNLIKELY(false || (data[3].qvalue <= 198))) { - result[0] += 3.713535595855519; - } else { - result[0] += 7.180656615177431; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 226))) { - result[0] += 11.193661980597666; - } else { - result[0] += 17.555807665127727; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[3].qvalue <= 154))) { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -0.7003375835348792; - } else { - result[0] += -3.7302757340894694; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 116))) { - result[0] += -6.599387818444294; - } else { - result[0] += -5.72489429399885; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 32))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += 0.15262275808585868; - } else { - result[0] += 1.1954652973697173; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 178))) { - result[0] += 2.4487311442319593; - } else { - result[0] += 7.490679951544426; - } - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 56))) { - if (UNLIKELY(false || (data[3].qvalue <= 214))) { - if (UNLIKELY(false || (data[0].qvalue <= 66))) { - result[0] += -0.4618247323919986; - } else { - result[0] += 2.1528753353821717; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 234))) { - result[0] += 1.2097848819662675; - } else { - result[0] += 1.752838457626999; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 212))) { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - result[0] += 2.420871486643817; - } else { - result[0] += 6.029272785166614; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 230))) { - result[0] += 10.227185956634905; - } else { - result[0] += 15.974606997518203; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 124))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += -2.865659975119095; - } else { - result[0] += -0.6896181889893488; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 116))) { - result[0] += -5.944063382797502; - } else { - result[0] += -5.094536373408761; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 52))) { - if (LIKELY(false || (data[3].qvalue <= 160))) { - result[0] += 0.6397310298301389; - } else { - result[0] += 2.9220460179454832; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 138))) { - result[0] += -2.666624663971065; - } else { - result[0] += 0.4270094967652458; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[3].qvalue <= 236))) { - if (UNLIKELY(false || (data[3].qvalue <= 218))) { - result[0] += 1.521282712441143; - } else { - result[0] += 1.0810978861849587; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - result[0] += 1.582856585015188; - } else { - result[0] += 2.0611044482921446; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 216))) { - if (LIKELY(false || (data[3].qvalue <= 206))) { - result[0] += 4.439645479292941; - } else { - result[0] += 7.705468000634004; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 11.427783097865925; - } else { - result[0] += 17.19202195652448; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 124))) { - if (LIKELY(false || (data[2].qvalue <= 28))) { - if (UNLIKELY(false || (data[3].qvalue <= 24))) { - result[0] += -1.2367334931972405; - } else { - result[0] += -0.568679959736126; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 112))) { - result[0] += -8.32192050869182; - } else { - result[0] += -5.006960492312537; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 42))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 0.4550818668115695; - } else { - result[0] += -3.0743532402463507; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 160))) { - result[0] += 0.13097623312980142; - } else { - result[0] += 2.786328266103951; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - if (LIKELY(false || (data[1].qvalue <= 72))) { - result[0] += 1.0925176197221544; - } else { - result[0] += 7.582882419296458; - } - } else { - result[0] += -3.9018135684874005; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 224))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 4.15326719153954; - } else { - result[0] += 7.678964746257113; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 78))) { - result[0] += 19.747446627865543; - } else { - result[0] += 12.500771902929218; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 124))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (UNLIKELY(false || (data[3].qvalue <= 22))) { - result[0] += -1.167979990569673; - } else { - result[0] += -0.5133742356132379; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 114))) { - result[0] += -5.603649494204901; - } else { - result[0] += -4.409295198837661; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 52))) { - if (LIKELY(false || (data[3].qvalue <= 160))) { - result[0] += 0.5480854924955038; - } else { - result[0] += 2.363109929409574; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 138))) { - result[0] += -2.302846027254356; - } else { - result[0] += 0.34306161983695516; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 0.9832828581009277; - } else { - result[0] += 6.834192669120016; - } - } else { - result[0] += -3.521149036128347; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 224))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 3.738054988080166; - } else { - result[0] += 6.911557381148448; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 78))) { - result[0] += 17.78481683344929; - } else { - result[0] += 11.252482971264655; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 56))) { - if (LIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.5726678321736987; - } else { - if (LIKELY(false || (data[2].qvalue <= 46))) { - result[0] += -0.14924717663033055; - } else { - result[0] += 6.235019242422922; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[0].qvalue <= 60))) { - result[0] += 1.162497192248416; - } else { - result[0] += 2.0120424899788536; - } - } else { - result[0] += 0.47169244387428955; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (UNLIKELY(false || (data[2].qvalue <= 42))) { - if (UNLIKELY(false || (data[2].qvalue <= 34))) { - result[0] += -0.357844108120637; - } else { - result[0] += 1.7605654400603017; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 70))) { - result[0] += 6.52940587259161; - } else { - result[0] += 3.3080480937743113; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 16.01724653501452; - } else { - result[0] += 8.753001223254058; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 156))) { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -0.4137669463081151; - } else { - result[0] += -2.6409307308959122; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 114))) { - result[0] += -5.376507786444898; - } else { - result[0] += -4.389560249429967; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 32))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += 0.025008362302337356; - } else { - result[0] += 1.019241657971192; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 72))) { - result[0] += 2.1411528979907484; - } else { - result[0] += -0.47484663873831007; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 0.8378079743141229; - } else { - result[0] += 5.832806958458091; - } - } else { - result[0] += -3.500358439422236; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 222))) { - if (LIKELY(false || (data[3].qvalue <= 206))) { - result[0] += 2.851221265160206; - } else { - result[0] += 5.566558597933049; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 78))) { - result[0] += 14.425347984875641; - } else { - result[0] += 9.578878534245574; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 124))) { - if (LIKELY(false || (data[2].qvalue <= 28))) { - if (UNLIKELY(false || (data[3].qvalue <= 22))) { - result[0] += -0.9195523527649098; - } else { - result[0] += -0.3719639557405976; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 74))) { - result[0] += -7.786786672274272; - } else { - result[0] += -3.9362138288079898; - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += 0.6022720589428014; - } else { - result[0] += 7.092300986713834; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 190))) { - result[0] += -2.1043147125075716; - } else { - result[0] += 0.12674302444068922; - } - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 50))) { - if (LIKELY(false || (data[3].qvalue <= 232))) { - if (UNLIKELY(false || (data[3].qvalue <= 218))) { - result[0] += 1.031090335758177; - } else { - result[0] += 0.5392873356886984; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - result[0] += 0.9291700147586669; - } else { - result[0] += 1.5196304502959777; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 216))) { - if (LIKELY(false || (data[3].qvalue <= 206))) { - result[0] += 2.5829880193644055; - } else { - result[0] += 4.763908216949989; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 6.935099835666378; - } else { - result[0] += 10.036216690922197; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 164))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 72))) { - if (LIKELY(false || (data[1].qvalue <= 6))) { - result[0] += -0.43590572608869044; - } else { - result[0] += -2.4316455018950878; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 42))) { - result[0] += -0.04380141068835597; - } else { - result[0] += 1.7110383905553694; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 144))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += -3.530168794490836; - } else { - result[0] += -1.0214699319685518; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 148))) { - result[0] += -6.775844377790179; - } else { - result[0] += -10.790508870091934; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 42))) { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[3].qvalue <= 188))) { - result[0] += -0.02275360025237618; - } else { - result[0] += -0.5838206132835355; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 1.7417272007672004; - } else { - result[0] += 0.660660463610099; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 208))) { - if (LIKELY(false || (data[3].qvalue <= 200))) { - result[0] += 1.9156509211867474; - } else { - result[0] += 3.135028625615758; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - result[0] += 4.960511180617512; - } else { - result[0] += 8.359091821368834; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 194))) { - if (LIKELY(false || (data[3].qvalue <= 160))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 70))) { - result[0] += -0.42133291745401114; - } else { - result[0] += -0.010851476443648929; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -2.4223382968632965; - } else { - result[0] += -6.899222971006882; - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 16))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += -0.03413012978096565; - } else { - result[0] += 0.5317297186286644; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 2.211563570567631; - } else { - result[0] += -0.16486923946003673; - } - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 50))) { - if (LIKELY(false || (data[3].qvalue <= 234))) { - if (UNLIKELY(false || (data[3].qvalue <= 218))) { - result[0] += 0.8620758848535985; - } else { - result[0] += 0.47621186848670294; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - result[0] += 0.8507697571985345; - } else { - result[0] += 1.3028689973708651; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 216))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 2.41099243454025; - } else { - result[0] += 4.090173047276771; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 5.632316419302413; - } else { - result[0] += 8.200667309946828; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 166))) { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (LIKELY(false || (data[3].qvalue <= 68))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - result[0] += -0.38327232438457565; - } else { - result[0] += -2.1415671585396967; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 68))) { - result[0] += -0.048803059306061464; - } else { - result[0] += -1.7470427286358465; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 118))) { - if (UNLIKELY(false || (data[3].qvalue <= 114))) { - result[0] += -3.8643859740923037; - } else { - result[0] += -3.030297649090965; - } - } else { - result[0] += -1.569212761065539; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 42))) { - if (UNLIKELY(false || (data[0].qvalue <= 54))) { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - result[0] += 1.1504605549094933; - } else { - result[0] += -0.02918860846727582; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - result[0] += 2.077977738321157; - } else { - result[0] += 0.5359649431721014; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 212))) { - if (LIKELY(false || (data[3].qvalue <= 202))) { - result[0] += 1.652849064266966; - } else { - result[0] += 2.954255485765737; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 76))) { - result[0] += -0.5669657233750427; - } else { - result[0] += 5.676198660468206; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 168))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[2].qvalue <= 38))) { - if (UNLIKELY(false || (data[3].qvalue <= 40))) { - result[0] += -0.5244874333393534; - } else { - result[0] += -0.18256259456848412; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 74))) { - result[0] += -5.51355459890058; - } else { - result[0] += 1.411684886661277; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 144))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += -2.6606175581636338; - } else { - result[0] += -0.47356034191920265; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 152))) { - result[0] += -5.942664098495093; - } else { - result[0] += -10.448295749482655; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 38))) { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[3].qvalue <= 188))) { - result[0] += -0.011800565116617453; - } else { - result[0] += -0.5202224220078566; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 1.7775453851705354; - } else { - result[0] += 0.49081054656235107; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 222))) { - if (LIKELY(false || (data[3].qvalue <= 204))) { - result[0] += 1.614115694236453; - } else { - result[0] += 3.0704803932483227; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 76))) { - result[0] += -0.5108923716568848; - } else { - result[0] += 6.589366885775977; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 42))) { - if (LIKELY(false || (data[0].qvalue <= 30))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.2892059588012491; - } else { - if (LIKELY(false || (data[0].qvalue <= 14))) { - result[0] += -0.08035600794549216; - } else { - result[0] += -0.4720559073325542; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[2].qvalue <= 46))) { - result[0] += 0.8967780238095124; - } else { - result[0] += 6.595205778394427; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - result[0] += -0.3520288227121683; - } else { - result[0] += 0.38974464605775183; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (UNLIKELY(false || (data[2].qvalue <= 38))) { - if (LIKELY(false || (data[1].qvalue <= 64))) { - result[0] += 0.7589099157010143; - } else { - result[0] += -1.366375525846731; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 66))) { - result[0] += 4.22967240474964; - } else { - result[0] += 1.4943104257462279; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 9.129350705249179; - } else { - result[0] += 3.068747078527702; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 168))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[2].qvalue <= 42))) { - if (LIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -0.18545114512070737; - } else { - result[0] += -1.7692822476814012; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 74))) { - result[0] += -4.801651736039382; - } else { - result[0] += 1.3646239105274842; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 142))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += -2.5169209574656417; - } else { - result[0] += -0.18660289084267964; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 148))) { - result[0] += -4.541809815605482; - } else { - result[0] += -8.652151740789414; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 1.3663672464257006; - } else { - result[0] += 3.04170718867983; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 0.2559705229975156; - } else { - result[0] += 2.9168273909132068; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 4.97017162342233; - } else { - result[0] += 11.503874847499691; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 0.4665647305038452; - } else { - result[0] += 5.509817603230935; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 162))) { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - result[0] += -1.3283728127725134; - } else { - result[0] += -6.130559274355571; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 62))) { - result[0] += -0.2896825481817099; - } else { - result[0] += -0.07162487032383137; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 118))) { - if (UNLIKELY(false || (data[3].qvalue <= 114))) { - result[0] += -3.1167449558905838; - } else { - result[0] += -2.3599501347059975; - } - } else { - result[0] += -1.059417066398789; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 42))) { - if (UNLIKELY(false || (data[0].qvalue <= 54))) { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - result[0] += 0.834224641552816; - } else { - result[0] += -0.015370587395285587; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - result[0] += 0.9254950158058017; - } else { - result[0] += 0.36421699256211726; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 212))) { - if (LIKELY(false || (data[3].qvalue <= 200))) { - result[0] += 1.0215455914435474; - } else { - result[0] += 1.9619948363613522; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 76))) { - result[0] += -0.6542972311475775; - } else { - result[0] += 3.9645617071617623; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 42))) { - if (LIKELY(false || (data[0].qvalue <= 22))) { - if (LIKELY(false || (data[1].qvalue <= 14))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.22003805724566253; - } else { - result[0] += -0.05306048893366094; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 16))) { - result[0] += -1.3451441339924273; - } else { - result[0] += -1.5488482760393083; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 28))) { - if (UNLIKELY(false || (data[2].qvalue <= 10))) { - result[0] += 0.21580833438485533; - } else { - result[0] += 1.177993229883637; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 56))) { - result[0] += -0.2994307611075257; - } else { - result[0] += 0.305894106833898; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (UNLIKELY(false || (data[2].qvalue <= 34))) { - if (LIKELY(false || (data[2].qvalue <= 32))) { - result[0] += 0.6300423600302785; - } else { - result[0] += -0.4677024667015657; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 60))) { - result[0] += 2.92097058914907; - } else { - result[0] += 0.9749046067206072; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 7.280391073314689; - } else { - result[0] += 1.8221294908582069; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 170))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[2].qvalue <= 42))) { - if (UNLIKELY(false || (data[3].qvalue <= 20))) { - result[0] += -0.5336229335357556; - } else { - result[0] += -0.12326033481423136; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 120))) { - result[0] += -3.7782573749400954; - } else { - result[0] += 1.2031945074524026; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 142))) { - if (LIKELY(false || (data[3].qvalue <= 136))) { - result[0] += -2.0138202324018293; - } else { - result[0] += 0.4287253040269907; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 150))) { - result[0] += -4.26031586774496; - } else { - result[0] += -8.189678469057437; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[3].qvalue <= 188))) { - result[0] += 0.03067279449262314; - } else { - result[0] += -0.4287195475348111; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 1.8796244512104765; - } else { - result[0] += 0.3056819085450995; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 226))) { - if (LIKELY(false || (data[3].qvalue <= 202))) { - result[0] += 0.9636725612848687; - } else { - result[0] += 1.932067083622997; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 76))) { - result[0] += -0.6860055204538198; - } else { - result[0] += 4.570361189431599; - } - } - } - } - if (LIKELY(false || (data[1].qvalue <= 42))) { - if (LIKELY(false || (data[0].qvalue <= 22))) { - if (LIKELY(false || (data[1].qvalue <= 14))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.1814554734769651; - } else { - result[0] += -0.03493688255810839; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 16))) { - result[0] += -1.1580987454621139; - } else { - result[0] += -1.3413217625073497; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[2].qvalue <= 46))) { - result[0] += 0.6065784994119716; - } else { - result[0] += 6.64156315122332; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 56))) { - result[0] += -0.2710747772797691; - } else { - result[0] += 0.24590059195978556; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (UNLIKELY(false || (data[2].qvalue <= 38))) { - if (LIKELY(false || (data[1].qvalue <= 64))) { - result[0] += 0.52839377206309; - } else { - result[0] += -1.3097734584676666; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 66))) { - result[0] += 3.0327157128432702; - } else { - result[0] += 0.8301613199220086; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 6.102585921419179; - } else { - result[0] += 1.1868023468270625; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 170))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[2].qvalue <= 42))) { - if (LIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -0.1099508760937471; - } else { - result[0] += -1.4799074079672025; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 74))) { - result[0] += -3.9533722562056326; - } else { - result[0] += 1.0527497525643543; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 142))) { - if (LIKELY(false || (data[3].qvalue <= 136))) { - result[0] += -1.874856650108779; - } else { - result[0] += 0.36151445149430145; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 152))) { - result[0] += -4.013347158834968; - } else { - result[0] += -7.940386810302735; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - if (LIKELY(false || (data[3].qvalue <= 206))) { - result[0] += 0.8194996574746792; - } else { - result[0] += 1.9073466437063578; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 0.1914437036803978; - } else { - result[0] += 1.9462096802861646; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 3.445255372400778; - } else { - result[0] += 9.341778161720354; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 0.3243846697838024; - } else { - result[0] += 3.292591407943689; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 160))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - result[0] += -0.9774807565883121; - } else { - result[0] += -5.423777811527253; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += -0.12073383773519127; - } else { - result[0] += 0.9911425235059078; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 142))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += -1.8435700376685866; - } else { - result[0] += -0.10737506746612407; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 148))) { - result[0] += -3.3288382873336477; - } else { - result[0] += -5.702344606187609; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - if (LIKELY(false || (data[3].qvalue <= 202))) { - result[0] += 0.6121670947641649; - } else { - result[0] += 1.5478825951517399; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 0.15159187149918565; - } else { - result[0] += 1.7525244521134302; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 3.1021024062818072; - } else { - result[0] += 8.417132546913868; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 0.29207001641087005; - } else { - result[0] += 2.9643452592336215; - } - } - } - } - if (LIKELY(false || (data[2].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.14003554272994065; - } else { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 46))) { - result[0] += -0.015514364763912126; - } else { - result[0] += 1.0510222973719732; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 30))) { - result[0] += -0.27117390740511166; - } else { - result[0] += 0.14354185812943476; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (LIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[0].qvalue <= 62))) { - result[0] += 0.5018898936445311; - } else { - result[0] += 2.5336810498326523; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 2.3642130662022995; - } else { - result[0] += 0.07374194009502717; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 4.874216369284443; - } else { - result[0] += 0.44699526391786304; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 170))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (UNLIKELY(false || (data[3].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - result[0] += -0.35060793036231724; - } else { - result[0] += -5.047665471633276; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 38))) { - result[0] += -0.07028608501406199; - } else { - result[0] += 0.7109949202510606; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 144))) { - if (LIKELY(false || (data[3].qvalue <= 122))) { - result[0] += -1.8132749471478289; - } else { - result[0] += -0.4798894299710876; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 152))) { - result[0] += -3.6392164039153325; - } else { - result[0] += -6.78748151870001; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - if (LIKELY(false || (data[0].qvalue <= 32))) { - result[0] += 0.6818792886595983; - } else { - result[0] += 2.0346700524088903; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 70))) { - result[0] += 0.1650370627648028; - } else { - result[0] += 1.4122047675458287; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 2.743139011175984; - } else { - result[0] += 7.534331532576863; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 0.25562844944051427; - } else { - result[0] += 2.4035809405106767; - } - } - } - } - if (LIKELY(false || (data[2].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.1149636651708558; - } else { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 46))) { - result[0] += -0.005786597801570078; - } else { - result[0] += 0.9536320919638706; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 30))) { - result[0] += -0.25774071878779176; - } else { - result[0] += 0.12494917135148927; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (LIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += 0.416251413237658; - } else { - result[0] += 4.65468264502448; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 1.9891757155656817; - } else { - result[0] += 0.06062405904969436; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 4.150901784204815; - } else { - result[0] += 0.1636860781066988; - } - } - } - if (LIKELY(false || (data[2].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.10346745600044038; - } else { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 46))) { - result[0] += -0.005207970482558494; - } else { - result[0] += 0.8583114141430566; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 30))) { - result[0] += -0.2319702347159344; - } else { - result[0] += 0.11246252282811312; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (LIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[0].qvalue <= 62))) { - result[0] += 0.36237019282195543; - } else { - result[0] += 2.185361301747475; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 1.7916299525787094; - } else { - result[0] += 0.05456246786985419; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 3.7383578847668653; - } else { - result[0] += 0.1474179036193099; - } - } - } - if (LIKELY(false || (data[2].qvalue <= 20))) { - if (LIKELY(false || (data[0].qvalue <= 22))) { - if (LIKELY(false || (data[1].qvalue <= 14))) { - if (LIKELY(false || (data[0].qvalue <= 16))) { - result[0] += -0.07824623744678852; - } else { - result[0] += 2.4147477472795025; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 20))) { - result[0] += -0.9602189832977626; - } else { - result[0] += -1.2336897440823642; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[2].qvalue <= 10))) { - result[0] += 0.17808383312156362; - } else { - result[0] += 0.7764366538076664; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 30))) { - result[0] += -0.20877644063379633; - } else { - result[0] += 0.10122370049456518; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (LIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += 0.3366817124844217; - } else { - result[0] += 3.9891647478052095; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 1.613702605033743; - } else { - result[0] += 0.04910696472777197; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 3.3668153467865807; - } else { - result[0] += 0.13276691204199761; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 170))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - if (LIKELY(false || (data[2].qvalue <= 28))) { - result[0] += -0.8003296552631252; - } else { - result[0] += -5.168554219404857; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 28))) { - result[0] += -0.04191612245860841; - } else { - result[0] += -0.31322033502373364; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 142))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += -1.6260716001993314; - } else { - result[0] += -0.07253028186242319; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 152))) { - result[0] += -3.059761641596405; - } else { - result[0] += -6.156701689220611; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (LIKELY(false || (data[1].qvalue <= 36))) { - if (UNLIKELY(false || (data[3].qvalue <= 176))) { - result[0] += 0.8382331706298907; - } else { - result[0] += 0.1470265483651418; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 68))) { - result[0] += 1.4136662728343927; - } else { - result[0] += 0.42639604831684264; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (UNLIKELY(false || (data[1].qvalue <= 76))) { - result[0] += 0.2137965369719358; - } else { - result[0] += 1.5807652149640596; - } - } else { - result[0] += 3.582011053264141; - } - } - } - if (LIKELY(false || (data[2].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.0809220032676647; - } else { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 46))) { - result[0] += 0.009295531913092564; - } else { - result[0] += 0.7015704698474159; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 56))) { - result[0] += -0.18839625520645842; - } else { - result[0] += 0.13815172995417008; - } - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (LIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += 0.2833961104028503; - } else { - result[0] += 3.9494598141232053; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 1.3130535747634955; - } else { - result[0] += 0.04139692407062909; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 2.875092684213369; - } else { - result[0] += -0.03741247869342383; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 198))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 170))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += -0.9280781442850904; - } else { - result[0] += -0.05519963885812408; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 18))) { - result[0] += 0.13575882302728218; - } else { - result[0] += 1.3530812716367793; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -1.0690103616905782; - } else { - result[0] += -2.214087039734459; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - result[0] += -0.6739287079004651; - } else { - result[0] += 0.08192197004299273; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - if (LIKELY(false || (data[1].qvalue <= 72))) { - result[0] += 0.16779935065337956; - } else { - result[0] += 2.7936280922346484; - } - } else { - result[0] += -6.01828142584824; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 216))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 0.6464965410008258; - } else { - result[0] += 1.2127821355938402; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 56))) { - result[0] += 2.304393523269968; - } else { - result[0] += -0.03369406329707866; - } - } - } - } - if (LIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 44))) { - if (LIKELY(false || (data[2].qvalue <= 6))) { - result[0] += -0.053129047160861614; - } else { - result[0] += -0.6797773812157768; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 50))) { - result[0] += 0.4863998644119598; - } else { - result[0] += 3.517865025900506; - } - } - } else { - result[0] += -0.18996639918014335; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (UNLIKELY(false || (data[0].qvalue <= 36))) { - if (LIKELY(false || (data[0].qvalue <= 34))) { - result[0] += 0.21126964469127507; - } else { - result[0] += 0.9292041399773837; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 0.38898047466443736; - } else { - result[0] += 0.0343316630215836; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 2.3603220532788822; - } else { - result[0] += -0.030345338012543195; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 198))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 170))) { - if (LIKELY(false || (data[2].qvalue <= 12))) { - result[0] += -0.03196203652937306; - } else { - result[0] += -0.32291491116843224; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 14))) { - result[0] += 0.1059633693894297; - } else { - result[0] += 1.1109217701835943; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -0.9750124564919093; - } else { - result[0] += -2.011326445104133; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - result[0] += -0.6277616333681295; - } else { - result[0] += 0.052653140828700706; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - if (LIKELY(false || (data[1].qvalue <= 72))) { - result[0] += 0.14758938512729353; - } else { - result[0] += 2.5144118215047015; - } - } else { - result[0] += -5.434481533329662; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 218))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 0.5607584407428231; - } else { - result[0] += 1.097345812808785; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 56))) { - result[0] += 2.188365555677249; - } else { - result[0] += -0.027329945962853226; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 196))) { - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[3].qvalue <= 174))) { - if (UNLIKELY(false || (data[3].qvalue <= 16))) { - result[0] += -0.339980565686603; - } else { - result[0] += -0.03185759232203992; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 42))) { - result[0] += 0.1301433477915698; - } else { - result[0] += 1.580000992364498; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 190))) { - result[0] += -1.685654765519544; - } else { - result[0] += -1.0927866830676793; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - result[0] += -0.5651196538004228; - } else { - result[0] += -0.10798605171852133; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - if (LIKELY(false || (data[0].qvalue <= 74))) { - result[0] += 0.13680401406260853; - } else { - result[0] += 2.8238424551720716; - } - } else { - result[0] += -4.904288046301866; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 226))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 0.45463644431627187; - } else { - result[0] += 1.0130458030200111; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 2.236542576950049; - } else { - result[0] += -0.024613159223933895; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 198))) { - if (LIKELY(false || (data[1].qvalue <= 72))) { - if (LIKELY(false || (data[3].qvalue <= 172))) { - if (LIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -0.032568909202472296; - } else { - result[0] += -0.4469851314428541; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 30))) { - result[0] += 0.11294361883379918; - } else { - result[0] += 1.0056842141087612; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 190))) { - result[0] += -1.517244761721867; - } else { - result[0] += -0.9837414394446418; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - result[0] += -0.508728386740654; - } else { - result[0] += 0.03608265112965852; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 0.11915447057376925; - } else { - result[0] += 2.0825805265428143; - } - } else { - result[0] += -4.425821080207825; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 226))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 0.4592645630622676; - } else { - result[0] += 0.9118048684410376; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 2.0132186895684288; - } else { - result[0] += -0.022167118571104448; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 198))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 160))) { - if (LIKELY(false || (data[2].qvalue <= 12))) { - result[0] += -0.020052189264742216; - } else { - result[0] += -0.4390079826397957; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 18))) { - result[0] += 0.06723042230135214; - } else { - result[0] += 0.6321455651905148; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 192))) { - if (LIKELY(false || (data[3].qvalue <= 142))) { - result[0] += -0.6849792752988858; - } else { - result[0] += -1.5843785915044357; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - result[0] += -0.45796421690851963; - } else { - result[0] += 0.026405314510040745; - } - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 50))) { - if (LIKELY(false || (data[3].qvalue <= 230))) { - if (UNLIKELY(false || (data[3].qvalue <= 218))) { - result[0] += 0.3385478130008445; - } else { - result[0] += -0.10764744508292204; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 236))) { - result[0] += 0.20488484351232758; - } else { - result[0] += 0.47811499378647015; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 212))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 0.4209993070324332; - } else { - result[0] += 0.7287778763739126; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 1.187064839378982; - } else { - result[0] += -0.01996407508027334; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 176))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[0].qvalue <= 60))) { - if (LIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -0.027774129306039258; - } else { - result[0] += -0.8385662967485399; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 120))) { - result[0] += -3.519087969462077; - } else { - result[0] += 0.7293787751512967; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 148))) { - if (LIKELY(false || (data[3].qvalue <= 118))) { - result[0] += -1.1172824090308644; - } else { - result[0] += -0.20902441584933884; - } - } else { - result[0] += -5.111647744634573; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - result[0] += 1.8609278771357982; - } else { - result[0] += 0.12480108283901199; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 196))) { - result[0] += -0.08227578925901015; - } else { - result[0] += 0.25625433402161485; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.9637861349021059; - } else { - result[0] += 5.291859932724311; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.9927995753498028; - } else { - result[0] += 0.14113094589752082; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 160))) { - if (LIKELY(false || (data[2].qvalue <= 12))) { - if (LIKELY(false || (data[3].qvalue <= 130))) { - if (UNLIKELY(false || (data[3].qvalue <= 46))) { - result[0] += -0.13887715472257542; - } else { - result[0] += 0.01749027468530986; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += 0.8747182175833713; - } else { - result[0] += 3.3873552828056868; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - result[0] += -2.0719160557169487; - } else { - result[0] += -0.9527323590531762; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 112))) { - result[0] += -4.278540461588714; - } else { - result[0] += -0.22532110219787005; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - if (LIKELY(false || (data[3].qvalue <= 204))) { - result[0] += 0.13994283376545083; - } else { - result[0] += 0.628766931454757; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 168))) { - result[0] += -0.2077112285433431; - } else { - result[0] += 0.0971080345193495; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.8677912951323141; - } else { - result[0] += 4.768073993215755; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.8939668307876266; - } else { - result[0] += 0.1270565048333717; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 160))) { - if (LIKELY(false || (data[2].qvalue <= 12))) { - if (LIKELY(false || (data[3].qvalue <= 130))) { - if (UNLIKELY(false || (data[3].qvalue <= 36))) { - result[0] += -0.166060933557046; - } else { - result[0] += 0.004294648853588787; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += 0.7873344634071392; - } else { - result[0] += 3.0512455477455793; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - if (UNLIKELY(false || (data[1].qvalue <= 16))) { - result[0] += 3.321445830033885; - } else { - result[0] += -1.8410162796558962; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 0.5648229020291158; - } else { - result[0] += -0.40508307941792243; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (LIKELY(false || (data[1].qvalue <= 38))) { - if (LIKELY(false || (data[3].qvalue <= 218))) { - result[0] += 0.116824997205243; - } else { - result[0] += 0.0017106091707403482; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 200))) { - result[0] += 0.09749333921964695; - } else { - result[0] += 0.512061380766423; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (UNLIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.8049728208270159; - } else { - result[0] += 0.11438560255422983; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.7813580416089511; - } else { - result[0] += 4.296131911715682; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 160))) { - if (LIKELY(false || (data[2].qvalue <= 12))) { - if (LIKELY(false || (data[3].qvalue <= 132))) { - if (UNLIKELY(false || (data[3].qvalue <= 50))) { - result[0] += -0.10165509214748221; - } else { - result[0] += 0.027573987237460126; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += 0.8965004316498251; - } else { - result[0] += 2.7484863860847417; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - result[0] += -1.6997008955624044; - } else { - result[0] += -0.678268726322754; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 118))) { - result[0] += -1.114816829956962; - } else { - result[0] += -0.07990083268080249; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (LIKELY(false || (data[3].qvalue <= 178))) { - result[0] += 0.08501821194709441; - } else { - result[0] += 0.7073547907959752; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 196))) { - result[0] += -0.09598099681424313; - } else { - result[0] += 0.1882562171309855; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.7035334691233133; - } else { - result[0] += 3.8709025872240264; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 76))) { - result[0] += -0.03477044912415934; - } else { - result[0] += 0.6388646830943916; - } - } - } - } - if (LIKELY(false || (data[2].qvalue <= 4))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.034231165787913284; - } else { - if (UNLIKELY(false || (data[2].qvalue <= 0))) { - result[0] += -0.008538500447320395; - } else { - result[0] += 0.030661489227495748; - } - } - } else { - result[0] += -0.6560679714832831; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[2].qvalue <= 46))) { - if (LIKELY(false || (data[0].qvalue <= 46))) { - result[0] += 0.05367898474501495; - } else { - result[0] += 0.44735028827783085; - } - } else { - result[0] += 6.229511664254325; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - result[0] += -0.14752365398422523; - } else { - if (UNLIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 0.38398941999281766; - } else { - result[0] += 0.04404838897933778; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 176))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (UNLIKELY(false || (data[3].qvalue <= 0))) { - if (LIKELY(false || (data[2].qvalue <= 28))) { - result[0] += -0.7549177495924217; - } else { - result[0] += -4.158142198324204; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 42))) { - result[0] += -0.02530038694314765; - } else { - result[0] += 0.5546607631795957; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 148))) { - if (LIKELY(false || (data[3].qvalue <= 136))) { - result[0] += -0.6813185032064286; - } else { - result[0] += 0.29119811621696806; - } - } else { - result[0] += -4.5671472818360614; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (LIKELY(false || (data[1].qvalue <= 40))) { - if (LIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 0.06089688550626267; - } else { - result[0] += -0.9885366824653841; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 180))) { - result[0] += 0.8258845435182254; - } else { - result[0] += 5.761587441940307; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (UNLIKELY(false || (data[3].qvalue <= 196))) { - result[0] += -0.07740265934103074; - } else { - result[0] += 0.16502792587020448; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 80))) { - result[0] += 0.3005847261842887; - } else { - result[0] += 1.4347149255319134; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 152))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - result[0] += -0.010806768205029583; - } else { - result[0] += -3.3904279910193553; - } - } else { - result[0] += 3.6360500988061872; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 56))) { - if (UNLIKELY(false || (data[1].qvalue <= 48))) { - result[0] += -1.3970841696833907; - } else { - result[0] += -0.5310959589164045; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 136))) { - result[0] += -0.43780724551513983; - } else { - result[0] += 0.38160829548283537; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 238))) { - if (LIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - result[0] += 0.1260601616574519; - } else { - result[0] += 2.3280856465403716; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 188))) { - result[0] += -6.748794538567707; - } else { - result[0] += 0.03388208818968886; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.4861732172767124; - } else { - result[0] += 3.34139486286105; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.6939914549665678; - } else { - result[0] += 0.012280274946395664; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 176))) { - if (LIKELY(false || (data[1].qvalue <= 54))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - result[0] += -0.464813733628163; - } else { - result[0] += -3.2180759784840705; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 48))) { - result[0] += -0.07920781023396078; - } else { - result[0] += 0.019886892967067187; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 146))) { - if (UNLIKELY(false || (data[1].qvalue <= 60))) { - result[0] += -1.3944523714625319; - } else { - result[0] += -0.6633490458105974; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 58))) { - result[0] += -0.4361867431137107; - } else { - result[0] += 0.09866640502908253; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (LIKELY(false || (data[1].qvalue <= 40))) { - if (LIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 0.04221310244085489; - } else { - result[0] += -0.8607172810069976; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 180))) { - result[0] += 0.7308448858222341; - } else { - result[0] += 5.17753285443306; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 198))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 0.12604388146867565; - } else { - result[0] += -0.5198739781442024; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - result[0] += 0.0001483644827354292; - } else { - result[0] += 0.4382489570445441; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 12))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += 0.2715686357969588; - } else { - result[0] += -0.17382652834003876; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 24))) { - result[0] += -0.5295756487690885; - } else { - result[0] += -1.247911300102006; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -4.132038760807204; - } else { - result[0] += -2.048248645525712; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 236))) { - if (LIKELY(false || (data[3].qvalue <= 152))) { - if (LIKELY(false || (data[2].qvalue <= 12))) { - result[0] += 0.009045345990511855; - } else { - result[0] += -0.36819514234182105; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 0.15071853324608264; - } else { - result[0] += 0.0023504781977204342; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.4615429381822286; - } else { - result[0] += 2.967287079375618; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.39428092203572945; - } else { - result[0] += -0.027793250841637182; - } - } - } - } - if (LIKELY(false || (data[2].qvalue <= 4))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (LIKELY(false || (data[1].qvalue <= 2))) { - if (UNLIKELY(false || (data[0].qvalue <= 0))) { - result[0] += 0.02095625550210984; - } else { - result[0] += -0.02470681499709372; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 0))) { - result[0] += -0.006936135990179306; - } else { - result[0] += 0.028313332943848205; - } - } - } else { - result[0] += -0.45814704971029124; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[2].qvalue <= 46))) { - if (LIKELY(false || (data[0].qvalue <= 46))) { - result[0] += 0.059813599712526144; - } else { - result[0] += 0.4052626695215156; - } - } else { - result[0] += 6.220781436647688; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - result[0] += -0.14287609636708531; - } else { - if (UNLIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 0.28909888374082054; - } else { - result[0] += 0.0223884762447394; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 178))) { - if (LIKELY(false || (data[1].qvalue <= 52))) { - if (UNLIKELY(false || (data[3].qvalue <= 10))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - result[0] += -0.20717493847839827; - } else { - result[0] += -2.852040849850889; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 30))) { - result[0] += -0.008160190303522096; - } else { - result[0] += 0.27599937314740675; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 146))) { - if (UNLIKELY(false || (data[1].qvalue <= 60))) { - result[0] += -1.1881260380224656; - } else { - result[0] += -0.5625438965153544; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 58))) { - result[0] += -0.403338839639409; - } else { - result[0] += 0.10268439573411683; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 184))) { - if (LIKELY(false || (data[1].qvalue <= 40))) { - if (LIKELY(false || (data[3].qvalue <= 182))) { - result[0] += -0.013368157221764328; - } else { - result[0] += 0.11043304821096991; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 180))) { - result[0] += 1.5817916361431956; - } else { - result[0] += 4.191132561870824; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 236))) { - if (UNLIKELY(false || (data[3].qvalue <= 196))) { - result[0] += -0.05418531103810077; - } else { - result[0] += 0.11582636097487908; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 80))) { - result[0] += 0.21679116682674598; - } else { - result[0] += 0.9937182303717; - } - } - } - } - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (LIKELY(false || (data[0].qvalue <= 74))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - result[0] += -0.020289773669981; - } else { - if (UNLIKELY(false || (data[1].qvalue <= 28))) { - result[0] += 0.07888539366069491; - } else { - result[0] += -0.0043942118674004035; - } - } - } else { - result[0] += -0.8819353631826549; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 1.166342015953883; - } else { - result[0] += -0.21953414862872633; - } - } - if (LIKELY(false || (data[3].qvalue <= 178))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - if (LIKELY(false || (data[3].qvalue <= 128))) { - result[0] += -0.014985389955920162; - } else { - result[0] += 0.5030502722795092; - } - } else { - result[0] += 3.209527899147808; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[3].qvalue <= 146))) { - result[0] += -1.359497708335802; - } else { - result[0] += -0.048409546375607654; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 0.2462483312344813; - } else { - result[0] += -0.15689708895777443; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (LIKELY(false || (data[1].qvalue <= 40))) { - if (UNLIKELY(false || (data[3].qvalue <= 182))) { - result[0] += -0.011608086477160455; - } else { - result[0] += 0.054071549124002416; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 180))) { - result[0] += 1.4247202960054501; - } else { - result[0] += 4.292557497723102; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 196))) { - if (LIKELY(false || (data[1].qvalue <= 72))) { - result[0] += 0.11744769877683264; - } else { - result[0] += -0.6228392828110428; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - result[0] += -0.01810292778067102; - } else { - result[0] += 0.33713691230011644; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 32))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += 0.271129972670566; - } else { - result[0] += -0.0968952898723599; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 24))) { - result[0] += -0.4419965803483365; - } else { - result[0] += -1.1427374036977256; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -2.964990935176611; - } else { - result[0] += -2.0024242342435397; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 236))) { - if (LIKELY(false || (data[3].qvalue <= 152))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - result[0] += 0.019913351953155762; - } else { - result[0] += -0.3451557406567103; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 68))) { - result[0] += 0.09431825657210167; - } else { - result[0] += -0.040080507458776934; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.2811388814670214; - } else { - result[0] += 2.54007776661795; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.33998703221387494; - } else { - result[0] += -0.11096001409545336; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 54))) { - if (LIKELY(false || (data[0].qvalue <= 46))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 18))) { - result[0] += -0.10381124910582794; - } else { - result[0] += -0.036053796621203345; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 18))) { - result[0] += -0.4795079805215589; - } else { - result[0] += -0.3343803406439497; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (UNLIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -1.3594113743305207; - } else { - result[0] += -0.8394250588417054; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -3.2252649987262227; - } else { - result[0] += -1.8098834428420432; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 234))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += 0.03700457930168985; - } else { - result[0] += 17.47663833459218; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 138))) { - result[0] += -0.5601782767579584; - } else { - result[0] += 0.017180070918654807; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.2387817133710211; - } else { - result[0] += -4.100601076265661; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 0.843944808030713; - } else { - result[0] += -0.2201947167753442; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 200))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 188))) { - if (LIKELY(false || (data[0].qvalue <= 66))) { - result[0] += -0.0005495605667856106; - } else { - result[0] += -3.58726428370322; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 66))) { - result[0] += -0.3697917053822814; - } else { - result[0] += 2.6442676259341997; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 194))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - result[0] += -0.1322526748337809; - } else { - result[0] += -0.9165228440058644; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 198))) { - result[0] += 0.015399111888784775; - } else { - result[0] += -0.03293383786820958; - } - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 50))) { - if (UNLIKELY(false || (data[3].qvalue <= 206))) { - result[0] += -1.74668638865153; - } else { - if (UNLIKELY(false || (data[3].qvalue <= 214))) { - result[0] += 0.41404376746989746; - } else { - result[0] += -0.04320938041815753; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 208))) { - if (LIKELY(false || (data[0].qvalue <= 72))) { - result[0] += 0.15385322801835455; - } else { - result[0] += 0.8175728843952048; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 0.4756074244340375; - } else { - result[0] += -0.1983103516972138; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 56))) { - if (LIKELY(false || (data[0].qvalue <= 24))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 26))) { - result[0] += -0.08484652763250765; - } else { - result[0] += -0.028152113688230847; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 6))) { - result[0] += -0.4321762504373764; - } else { - result[0] += -0.2651707672412985; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (LIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -1.0555429015159608; - } else { - result[0] += -0.7582275236447653; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -2.9167088172746745; - } else { - result[0] += -1.4978527782972042; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 234))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[2].qvalue <= 38))) { - result[0] += 0.02016281760562264; - } else { - result[0] += 0.5381136571935687; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - result[0] += -0.4649008843759111; - } else { - result[0] += 0.06919020340221031; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.15702109026459932; - } else { - result[0] += 2.1580531330984467; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.21686901261145552; - } else { - result[0] += -0.1001601769605728; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 42))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 8))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += 0.19641092388804365; - } else { - result[0] += -0.055276022085065206; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 8))) { - result[0] += -0.3392746616594763; - } else { - result[0] += -0.7843794295296335; - } - } - } else { - if (LIKELY(false || (data[2].qvalue <= 44))) { - result[0] += -2.238581113219261; - } else { - result[0] += -1.3538284465441337; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 200))) { - if (LIKELY(false || (data[1].qvalue <= 62))) { - if (LIKELY(false || (data[3].qvalue <= 188))) { - result[0] += 0.017158471900924056; - } else { - result[0] += 1.4687521383872566; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 192))) { - result[0] += -0.48506636615045484; - } else { - result[0] += -0.047229123064139726; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += -0.003343629263232336; - } else { - result[0] += -3.737171672379098; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.24658213105746754; - } else { - result[0] += 0.7366530590168956; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 60))) { - if (LIKELY(false || (data[0].qvalue <= 24))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 18))) { - result[0] += -0.07999229789885372; - } else { - result[0] += -0.024486695046509094; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 6))) { - result[0] += -0.3591096819916043; - } else { - result[0] += -0.19716339864139187; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (LIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -0.8995184892360121; - } else { - result[0] += -0.6091088518301646; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -2.423594094981318; - } else { - result[0] += -1.2236526361795572; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 236))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[2].qvalue <= 38))) { - result[0] += 0.017590391024004715; - } else { - result[0] += 0.48376995639076664; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 192))) { - result[0] += -0.4336376838327545; - } else { - result[0] += 0.04774652419020946; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.08141260086235247; - } else { - result[0] += 1.8715363113490904; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.2612265991556262; - } else { - result[0] += -0.11460675569674739; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 178))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - if (LIKELY(false || (data[3].qvalue <= 128))) { - result[0] += -0.010014087252989079; - } else { - result[0] += 0.44165882919228355; - } - } else { - result[0] += 2.767749158126721; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - result[0] += -1.265884517270139; - } else { - result[0] += -0.326555396705971; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 34))) { - result[0] += 0.17874279127753523; - } else { - result[0] += -0.10461815372106785; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (LIKELY(false || (data[1].qvalue <= 40))) { - if (UNLIKELY(false || (data[3].qvalue <= 182))) { - result[0] += -0.0291664181175232; - } else { - result[0] += 0.0321031669500698; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 180))) { - result[0] += 1.2655710841109187; - } else { - result[0] += 3.8452706585121152; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 200))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 0.07346629730705066; - } else { - result[0] += -0.2790375844518317; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - result[0] += -0.030938073075268954; - } else { - result[0] += 0.2584663489831829; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 8))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 8))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += 0.1897708646960375; - } else { - result[0] += -0.10591389661914655; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 8))) { - result[0] += -0.27429189760805434; - } else { - result[0] += -0.6438250486029867; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 22))) { - result[0] += -1.9822659349441527; - } else { - result[0] += -1.1856181401364942; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 176))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += 0.004593246195533139; - } else { - result[0] += 2.4929798120346622; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - result[0] += -0.9223952289248065; - } else { - result[0] += -0.044695212630605684; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (LIKELY(false || (data[1].qvalue <= 42))) { - result[0] += 0.0022744264617153534; - } else { - result[0] += 0.9887394270605384; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - result[0] += -0.05980165741450547; - } else { - result[0] += 0.06763695160928662; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 58))) { - if (LIKELY(false || (data[0].qvalue <= 46))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += 0.23353499906984243; - } else { - result[0] += -0.030567457307068176; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 6))) { - result[0] += -0.3026360332604611; - } else { - result[0] += -0.15352749305440105; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (UNLIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -0.9208640919129055; - } else { - result[0] += -0.4870238716204962; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -2.028915634984555; - } else { - result[0] += -0.9910290916149432; - } - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 230))) { - if (LIKELY(false || (data[3].qvalue <= 218))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 0.012556621103070601; - } else { - result[0] += 0.27622010195921237; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - result[0] += 0.4583737829879677; - } else { - result[0] += -0.1957218213752224; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.13572258094742976; - } else { - result[0] += 1.654014792332844; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 0.10699664077497309; - } else { - result[0] += -0.26287929402538607; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 34))) { - if (LIKELY(false || (data[1].qvalue <= 20))) { - if (LIKELY(false || (data[1].qvalue <= 0))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += 0.21031418917361988; - } else { - result[0] += -0.05299115840815619; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 8))) { - result[0] += -0.21890068136134136; - } else { - result[0] += -0.5242896185723835; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 22))) { - result[0] += -1.6681880847613018; - } else { - result[0] += -0.9499344626594992; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 230))) { - if (LIKELY(false || (data[3].qvalue <= 218))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += 0.003964550672321244; - } else { - result[0] += 0.248609910955958; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 80))) { - result[0] += -0.17615619642542008; - } else { - result[0] += 0.41270129405980494; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.1074364755511174; - } else { - result[0] += -3.3754073861750165; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 56))) { - result[0] += 0.5143961430823207; - } else { - result[0] += -0.23675266527233682; - } - } - } - } - if (LIKELY(false || (data[0].qvalue <= 10))) { - if (UNLIKELY(false || (data[2].qvalue <= 0))) { - if (LIKELY(false || (data[1].qvalue <= 4))) { - if (LIKELY(false || (data[0].qvalue <= 6))) { - result[0] += -0.02091550687275058; - } else { - result[0] += 0.05471821127697738; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 8))) { - result[0] += -0.06265624329964264; - } else { - result[0] += -0.003474992384641048; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 4))) { - if (LIKELY(false || (data[0].qvalue <= 2))) { - result[0] += -0.05813859439852901; - } else { - result[0] += 1.685043735887323; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 2))) { - result[0] += 0.005356865798300517; - } else { - result[0] += -0.009951643674742359; - } - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - if (LIKELY(false || (data[1].qvalue <= 26))) { - result[0] += 0.04421518555621426; - } else { - result[0] += 0.3861890671974087; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 46))) { - result[0] += 2.733613769617948; - } else { - result[0] += 5.786070743288313; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 26))) { - if (LIKELY(false || (data[1].qvalue <= 50))) { - result[0] += -0.482797800051755; - } else { - result[0] += 2.5325565222740174; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 36))) { - result[0] += 0.09547398872773903; - } else { - result[0] += -0.046573119312707335; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 4))) { - if (LIKELY(false || (data[2].qvalue <= 28))) { - if (LIKELY(false || (data[1].qvalue <= 10))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += 0.08210078603934584; - } else { - result[0] += -0.11656505933452627; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 8))) { - result[0] += -0.20987820191852383; - } else { - result[0] += -0.5384319635554019; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 44))) { - result[0] += -1.9617384305207626; - } else { - result[0] += -1.2668292170304518; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 178))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += 0.003420268764872335; - } else { - result[0] += 2.213071489783301; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - result[0] += -0.7776470923789272; - } else { - result[0] += -0.04327273641201862; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (LIKELY(false || (data[1].qvalue <= 40))) { - result[0] += 0.00861831306648558; - } else { - result[0] += 1.848056525397011; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 196))) { - result[0] += -0.04828241624484346; - } else { - result[0] += 0.05563476812699414; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 64))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 26))) { - result[0] += -0.05078687692889694; - } else { - result[0] += -0.014356568909767928; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 24))) { - result[0] += -0.17534628583710493; - } else { - result[0] += -0.5621430105402849; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -1.7740939281297767; - } else { - result[0] += -1.145018720260033; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - if (LIKELY(false || (data[3].qvalue <= 130))) { - result[0] += 0.018993981486318878; - } else { - result[0] += 0.5365927638406978; - } - } else { - result[0] += 15.05643708864848; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 152))) { - if (LIKELY(false || (data[0].qvalue <= 56))) { - result[0] += -0.4728617369458554; - } else { - result[0] += 0.20752761050703802; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 38))) { - result[0] += 0.09305968936956194; - } else { - result[0] += -0.009524064343686378; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 52))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += 0.18727818329014223; - } else { - result[0] += -0.025890677188174106; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 24))) { - result[0] += -0.15753058727404748; - } else { - result[0] += -0.5067434101778527; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -1.604397966136103; - } else { - result[0] += -1.0349207672706018; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 234))) { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += 0.020542942769893005; - } else { - result[0] += 13.61352872212728; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 152))) { - result[0] += -0.20312529336650453; - } else { - result[0] += 0.019356250676449485; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.1529675914478733; - } else { - result[0] += -3.046054223456034; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 78))) { - result[0] += 0.4578596813081651; - } else { - result[0] += -0.21317670897106453; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 100))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (LIKELY(false || (data[0].qvalue <= 12))) { - if (LIKELY(false || (data[3].qvalue <= 88))) { - result[0] += -0.009515774573242132; - } else { - result[0] += 0.10722986880689861; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 24))) { - result[0] += -0.08947096205225108; - } else { - result[0] += -0.4843778472947222; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -1.9798399291992188; - } else { - result[0] += -1.155246785481771; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 12))) { - if (LIKELY(false || (data[3].qvalue <= 132))) { - if (LIKELY(false || (data[3].qvalue <= 126))) { - result[0] += 0.058783065329708964; - } else { - result[0] += 0.20552873062508797; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 48))) { - result[0] += 0.5012681102076195; - } else { - result[0] += 1.1655076967659646; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 152))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += -0.4052823818071666; - } else { - result[0] += 0.2663182250508323; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 0.09131312159878124; - } else { - result[0] += -0.023826012683033533; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 14))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (LIKELY(false || (data[0].qvalue <= 22))) { - if (LIKELY(false || (data[1].qvalue <= 16))) { - result[0] += -0.05587497206644211; - } else { - result[0] += -0.18871587509128254; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -0.471164876208749; - } else { - result[0] += -0.2899516754150391; - } - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 64))) { - result[0] += -1.2615578046052351; - } else { - result[0] += -0.824327716093797; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 230))) { - if (LIKELY(false || (data[3].qvalue <= 218))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - result[0] += -0.0018346942873173101; - } else { - result[0] += 0.16034261671985583; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - result[0] += 0.3582006235217019; - } else { - result[0] += -0.15805343752114553; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.09120323986998596; - } else { - result[0] += -2.746553548719825; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 240))) { - result[0] += 0.020237036833509074; - } else { - result[0] += 0.5211988226836327; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 178))) { - if (LIKELY(false || (data[2].qvalue <= 12))) { - if (LIKELY(false || (data[3].qvalue <= 130))) { - if (LIKELY(false || (data[0].qvalue <= 48))) { - result[0] += -0.005991282302809805; - } else { - result[0] += 0.21808256969336925; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 48))) { - result[0] += 0.3648159058387132; - } else { - result[0] += 1.0501265993764848; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 14))) { - if (LIKELY(false || (data[3].qvalue <= 140))) { - result[0] += -0.7560950076917835; - } else { - result[0] += -0.011609365583746693; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 114))) { - result[0] += -0.9929890502662195; - } else { - result[0] += -0.018430796040420066; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (UNLIKELY(false || (data[0].qvalue <= 38))) { - if (LIKELY(false || (data[3].qvalue <= 182))) { - result[0] += 1.4941535253935205; - } else { - result[0] += 5.01069572504829; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 40))) { - result[0] += -1.6721194921221052; - } else { - result[0] += 0.06600910509196743; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - if (LIKELY(false || (data[2].qvalue <= 50))) { - result[0] += 0.06599082621417188; - } else { - result[0] += -0.5629778590534413; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 50))) { - result[0] += -0.031091763815894752; - } else { - result[0] += 0.12480377527902276; - } - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 38))) { - if (LIKELY(false || (data[2].qvalue <= 36))) { - if (LIKELY(false || (data[0].qvalue <= 14))) { - if (UNLIKELY(false || (data[3].qvalue <= 2))) { - result[0] += 0.1757544212263416; - } else { - result[0] += -0.03321362620945462; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 24))) { - result[0] += -0.1209793900016442; - } else { - result[0] += -0.3390527655087508; - } - } - } else { - result[0] += -0.9544804904460907; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 228))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - if (LIKELY(false || (data[3].qvalue <= 188))) { - result[0] += 0.00850193251229063; - } else { - result[0] += 1.253315189034531; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 192))) { - result[0] += -0.3207565361013706; - } else { - result[0] += 0.01040093706091699; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 78))) { - if (LIKELY(false || (data[0].qvalue <= 76))) { - result[0] += 0.07622775564010142; - } else { - result[0] += 0.6434668296100172; - } - } else { - result[0] += -0.25382570830590884; - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 66))) { - if (LIKELY(false || (data[2].qvalue <= 36))) { - if (UNLIKELY(false || (data[3].qvalue <= 6))) { - if (UNLIKELY(false || (data[3].qvalue <= 0))) { - result[0] += -0.15891414485012728; - } else { - result[0] += -0.06083332526536694; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 30))) { - result[0] += -0.025560713454775653; - } else { - result[0] += -0.009423279209306063; - } - } - } else { - result[0] += -0.8614186177253723; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[2].qvalue <= 12))) { - if (LIKELY(false || (data[3].qvalue <= 130))) { - result[0] += 0.014946627847899083; - } else { - result[0] += 0.3928370868714558; - } - } else { - result[0] += 12.309830587704978; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 146))) { - if (UNLIKELY(false || (data[2].qvalue <= 40))) { - result[0] += -0.7278951537054682; - } else { - result[0] += 0.10042924111104164; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 0.048389262179927645; - } else { - result[0] += -0.024802333325858735; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 228))) { - if (LIKELY(false || (data[3].qvalue <= 218))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 0.0026677754941615505; - } else { - result[0] += -0.11795346042813584; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 214))) { - result[0] += 0.25660651477274304; - } else { - result[0] += 0.12738714081176464; - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 80))) { - if (UNLIKELY(false || (data[3].qvalue <= 222))) { - result[0] += -0.36182422209697296; - } else { - result[0] += -0.09068035614479159; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 226))) { - result[0] += 0.29626333166012725; - } else { - result[0] += 0.501580802754658; - } - } - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - if (LIKELY(false || (data[1].qvalue <= 74))) { - result[0] += 0.04606551220989991; - } else { - result[0] += 1.6768793160307642; - } - } else { - result[0] += -2.4957903161863; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 56))) { - if (LIKELY(false || (data[3].qvalue <= 240))) { - result[0] += -0.001995007368938002; - } else { - result[0] += 0.8600447828586285; - } - } else { - result[0] += -0.22613400126344585; - } - } - } - if (LIKELY(false || (data[3].qvalue <= 100))) { - if (LIKELY(false || (data[2].qvalue <= 28))) { - if (LIKELY(false || (data[1].qvalue <= 10))) { - if (LIKELY(false || (data[3].qvalue <= 88))) { - result[0] += -0.007032593132406662; - } else { - result[0] += 0.10789191115072033; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 94))) { - result[0] += -0.14776386905895944; - } else { - result[0] += -0.04740764971575276; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 44))) { - result[0] += -1.5670684486389161; - } else { - result[0] += -0.7503883746818261; - } - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 10))) { - result[0] += 2.769067636305286; - } else { - if (UNLIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[3].qvalue <= 134))) { - result[0] += 0.07602934068056531; - } else { - result[0] += 2.8183202544858474; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 146))) { - result[0] += -0.16233532230561315; - } else { - result[0] += 0.01202817781399166; - } - } - } - } - if (LIKELY(false || (data[0].qvalue <= 54))) { - if (LIKELY(false || (data[0].qvalue <= 50))) { - if (LIKELY(false || (data[0].qvalue <= 48))) { - if (LIKELY(false || (data[0].qvalue <= 36))) { - result[0] += 0.0029886117550836865; - } else { - result[0] += -0.06408425115195845; - } - } else { - result[0] += 0.26602660123013633; - } - } else { - result[0] += -0.07929562686095666; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - if (LIKELY(false || (data[2].qvalue <= 46))) { - if (UNLIKELY(false || (data[2].qvalue <= 36))) { - result[0] += 3.7119815171616426; - } else { - result[0] += 0.547443312065942; - } - } else { - result[0] += 4.166473594393049; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 48))) { - if (UNLIKELY(false || (data[0].qvalue <= 60))) { - result[0] += 0.0017977573714891022; - } else { - result[0] += 0.23845299048938143; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 76))) { - result[0] += -0.037150860814943704; - } else { - result[0] += 0.14564257745376; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 178))) { - if (LIKELY(false || (data[0].qvalue <= 60))) { - if (LIKELY(false || (data[0].qvalue <= 50))) { - if (LIKELY(false || (data[0].qvalue <= 48))) { - result[0] += -0.007183108829239199; - } else { - result[0] += 0.2394491795303417; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 140))) { - result[0] += -0.6344022675495424; - } else { - result[0] += 0.007260844712462359; - } - } - } else { - if (LIKELY(false || (data[3].qvalue <= 142))) { - if (UNLIKELY(false || (data[3].qvalue <= 138))) { - result[0] += -0.08417562885148172; - } else { - result[0] += 0.5906773558105556; - } - } else { - if (LIKELY(false || (data[0].qvalue <= 68))) { - result[0] += 0.18764238988337378; - } else { - result[0] += -2.326712134777498; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (UNLIKELY(false || (data[0].qvalue <= 38))) { - if (LIKELY(false || (data[3].qvalue <= 182))) { - result[0] += 1.339431215953403; - } else { - result[0] += 4.521662556143368; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 52))) { - result[0] += 0.7292394052527168; - } else { - result[0] += -0.012391112078889212; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 194))) { - if (LIKELY(false || (data[2].qvalue <= 50))) { - result[0] += 0.05246317581619821; - } else { - result[0] += -0.4745475472223324; - } - } else { - if (LIKELY(false || (data[2].qvalue <= 50))) { - result[0] += -0.024886056556697605; - } else { - result[0] += 0.10675635333641575; - } - } - } - } - if (LIKELY(false || (data[3].qvalue <= 178))) { - if (LIKELY(false || (data[1].qvalue <= 46))) { - if (LIKELY(false || (data[1].qvalue <= 44))) { - if (LIKELY(false || (data[3].qvalue <= 170))) { - result[0] += -0.0035541639556744805; - } else { - result[0] += 0.23776616044647245; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 22))) { - result[0] += 1.6540061310768128; - } else { - result[0] += -0.007316129265448475; - } - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 24))) { - if (UNLIKELY(false || (data[3].qvalue <= 156))) { - result[0] += -0.9207533250588412; - } else { - result[0] += -0.19604021761441262; - } - } else { - if (UNLIKELY(false || (data[2].qvalue <= 26))) { - result[0] += 0.39069615612658914; - } else { - result[0] += -0.07078300532699774; - } - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 186))) { - if (LIKELY(false || (data[1].qvalue <= 42))) { - if (UNLIKELY(false || (data[3].qvalue <= 182))) { - result[0] += -0.0537103470292483; - } else { - result[0] += 0.01762700340556235; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 180))) { - result[0] += 0.6253090474974919; - } else { - result[0] += 2.807307553162299; - } - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 202))) { - if (LIKELY(false || (data[2].qvalue <= 48))) { - result[0] += 0.04721793457525833; - } else { - result[0] += -0.17271753814495214; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 76))) { - result[0] += -0.01901583571747938; - } else { - result[0] += 0.1447237594141037; - } - } - } - } - if (LIKELY(false || (data[0].qvalue <= 78))) { - if (LIKELY(false || (data[0].qvalue <= 76))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - if (LIKELY(false || (data[0].qvalue <= 50))) { - result[0] += 0.0021335393865604085; - } else { - result[0] += -0.07177648191161112; - } - } else { - if (UNLIKELY(false || (data[1].qvalue <= 30))) { - result[0] += 2.634354959784485; - } else { - result[0] += 0.004247555141275988; - } - } - } else { - result[0] += 0.45583923039077984; - } - } else { - result[0] += -0.24432320779772257; - } - if (UNLIKELY(false || (data[3].qvalue <= 6))) { - if (LIKELY(false || (data[0].qvalue <= 58))) { - if (LIKELY(false || (data[0].qvalue <= 46))) { - if (LIKELY(false || (data[1].qvalue <= 18))) { - result[0] += -0.053137056565348964; - } else { - result[0] += -0.1615091542830301; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 50))) { - result[0] += -0.48585747241973887; - } else { - result[0] += -0.2905982590516409; - } - } - } else { - result[0] += -1.0460150627295177; - } - } else { - if (LIKELY(false || (data[3].qvalue <= 228))) { - if (LIKELY(false || (data[3].qvalue <= 218))) { - if (LIKELY(false || (data[3].qvalue <= 208))) { - result[0] += -0.0007595050498410175; - } else { - result[0] += 0.17149519474492791; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 68))) { - result[0] += 0.2696969214216211; - } else { - result[0] += -0.148602349719807; - } - } - } else { - if (LIKELY(false || (data[0].qvalue <= 78))) { - if (LIKELY(false || (data[1].qvalue <= 76))) { - result[0] += 0.041070141212163; - } else { - result[0] += 0.27300094236384387; - } - } else { - result[0] += -0.220040733112148; - } - } - } - if (UNLIKELY(false || (data[3].qvalue <= 66))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - if (UNLIKELY(false || (data[3].qvalue <= 8))) { - if (LIKELY(false || (data[0].qvalue <= 24))) { - result[0] += -0.04786216375242139; - } else { - result[0] += -0.2854615034882365; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 44))) { - result[0] += -0.0157676019115889; - } else { - result[0] += -0.00516823734649734; - } - } - } else { - result[0] += -0.8536656070967852; - } - } else { - if (LIKELY(false || (data[1].qvalue <= 28))) { - if (LIKELY(false || (data[0].qvalue <= 54))) { - if (LIKELY(false || (data[3].qvalue <= 130))) { - result[0] += 0.01178608871677298; - } else { - result[0] += 0.31573188820888726; - } - } else { - result[0] += 10.428866252899171; - } - } else { - if (UNLIKELY(false || (data[3].qvalue <= 146))) { - if (LIKELY(false || (data[0].qvalue <= 60))) { - result[0] += -0.39214777400557815; - } else { - result[0] += 0.15464362462318024; - } - } else { - if (UNLIKELY(false || (data[0].qvalue <= 36))) { - result[0] += 0.05795968864116782; - } else { - result[0] += -0.018277034463468993; - } - } - } - } - - // Apply base_scores - result[0] += 0; - - // Apply postprocessor - if (!pred_margin) { postprocess(result); } -} - -void cpufj_predictor::postprocess(double* result) -{ - // Do nothing -} - -// Feature names array -const char* cpufj_predictor::feature_names[cpufj_predictor::NUM_FEATURES] = { - "n_vars", "n_cstrs", "total_nnz", "mem_total_mb"}; diff --git a/cpp/src/utilities/models/cpufj_predictor/quantize.cpp b/cpp/src/utilities/models/cpufj_predictor/quantize.cpp deleted file mode 100644 index 0b292d03fb..0000000000 --- a/cpp/src/utilities/models/cpufj_predictor/quantize.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "header.h" - -static const double threshold[] = { - 940.50000000000011, - 962.50000000000011, - 964.50000000000011, - 1001.5000000000001, - 1010.5000000000001, - 1088.5000000000002, - 1250.5000000000002, - 1435.5000000000002, - 3395.5000000000005, - 3603.0000000000005, - 4535.5000000000009, - 5197.5000000000009, - 5419.0000000000009, - 5949.5000000000009, - 5959.5000000000009, - 5962.5000000000009, - 5963.5000000000009, - 5964.5000000000009, - 5966.5000000000009, - 5969.5000000000009, - 5972.5000000000009, - 5984.0000000000009, - 6803.5000000000009, - 7344.0000000000009, - 8291.0000000000018, - 9184.0000000000018, - 9992.0000000000018, - 10730.500000000002, - 12831.000000000002, - 14626.500000000002, - 15810.500000000002, - 16371.000000000002, - 17411.000000000004, - 18723.500000000004, - 19548.000000000004, - 20752.500000000004, - 23386.000000000004, - 35418.500000000007, - 49402.000000000007, - 53915.500000000007, - 598.50000000000011, - 1717.5000000000002, - 1767.5000000000002, - 1847.5000000000002, - 2005.5000000000002, - 2290.0000000000005, - 2305.5000000000005, - 2649.0000000000005, - 5179.5000000000009, - 5250.5000000000009, - 5362.5000000000009, - 6412.5000000000009, - 7682.0000000000009, - 9243.0000000000018, - 10037.000000000002, - 12675.000000000002, - 18404.000000000004, - 19600.500000000004, - 21814.500000000004, - 23409.000000000004, - 23497.000000000004, - 23508.000000000004, - 23558.000000000004, - 23603.500000000004, - 23652.500000000004, - 23744.500000000004, - 23818.500000000004, - 23827.000000000004, - 23874.000000000004, - 23897.500000000004, - 23920.500000000004, - 23949.500000000004, - 23978.000000000004, - 24015.000000000004, - 24102.000000000004, - 24164.500000000004, - 26354.000000000004, - 40391.500000000007, - 58010.500000000007, - 64224.000000000007, - 64894.000000000007, - 5656.5000000000009, - 7438.5000000000009, - 7622.5000000000009, - 18609.000000000004, - 23424.500000000004, - 24989.500000000004, - 29342.000000000004, - 43154.000000000007, - 46402.000000000007, - 46815.000000000007, - 47052.000000000007, - 47178.000000000007, - 47358.000000000007, - 47388.000000000007, - 47485.000000000007, - 47659.000000000007, - 47752.500000000007, - 47839.500000000007, - 48007.000000000007, - 48085.500000000007, - 48300.500000000007, - 48410.000000000007, - 54272.500000000007, - 57404.000000000007, - 76382.000000000015, - 83368.000000000015, - 108268.00000000001, - 170279.00000000003, - 186892.00000000003, - 3.8570000000000007, - 9.8880000000000017, - 11.190500000000002, - 11.561500000000001, - 11.861500000000001, - 12.013500000000002, - 12.3085, - 12.4625, - 12.647500000000003, - 13.102500000000001, - 13.439500000000001, - 14.092500000000003, - 15.505500000000003, - 16.265500000000003, - 16.586500000000004, - 16.893500000000003, - 17.051500000000001, - 17.255500000000001, - 17.432500000000001, - 17.537500000000005, - 17.814500000000006, - 18.095500000000005, - 18.167500000000004, - 18.334500000000002, - 18.546500000000005, - 18.743500000000001, - 18.851500000000005, - 18.949500000000004, - 19.151500000000002, - 19.201500000000006, - 19.275500000000005, - 19.453500000000002, - 19.604500000000005, - 19.704500000000003, - 20.009500000000006, - 20.944500000000001, - 21.655500000000004, - 24.058500000000006, - 56.284500000000001, - 58.642500000000005, - 63.514000000000003, - 66.785000000000011, - 67.949500000000015, - 70.709000000000017, - 72.252000000000024, - 74.410500000000013, - 76.675500000000014, - 78.813500000000019, - 81.035000000000011, - 86.201000000000008, - 87.71850000000002, - 90.722000000000023, - 93.919500000000014, - 95.885000000000005, - 98.094500000000025, - 101.17400000000002, - 105.48300000000002, - 119.34800000000001, - 135.33800000000005, - 148.20100000000002, - 155.32500000000002, - 202.12250000000003, - 215.85450000000003, - 222.94300000000001, - 227.80600000000001, - 231.09150000000002, - 235.98200000000006, - 238.51450000000003, - 289.45400000000001, - 328.42950000000002, - 358.75900000000007, - 402.21050000000008, - 420.89000000000004, - 436.57650000000007, - 443.69200000000006, - 453.70450000000005, - 462.34700000000004, - 471.61900000000009, - 478.55100000000004, - 486.96150000000006, - 495.03150000000005, - 501.28050000000007, - 505.49750000000006, - 510.61650000000003, - 518.64250000000004, - 524.98750000000007, - 530.02200000000005, - 538.52950000000021, - 548.17750000000012, - 563.13350000000003, - 585.95900000000017, - 606.86650000000009, - 622.19850000000008, - 632.28650000000016, - 662.49250000000006, - 870.10250000000008, - 885.30550000000005, - 899.1785000000001, - 914.79400000000021, - 932.5870000000001, - 945.74600000000009, - 1009.9455000000002, - 1021.0850000000002, - 1048.7770000000003, - 1084.9000000000003, - 1115.9705000000001, - 1145.3940000000002, - 1175.9330000000002, - 1208.2720000000002, - 1235.9550000000002, - 1253.6805000000002, - 1266.5855000000004, - 1276.1255000000003, - 1282.5315000000003, - 1291.7940000000001, - 1295.7590000000002, - 1299.4545000000001, - 1307.3780000000004, - 1312.9645000000003, - 1322.5440000000001, - 1465.1910000000003, -}; - -static const int th_begin[] = { - 0, - 40, - 81, - 110, -}; - -static const int th_len[] = { - 40, - 41, - 29, - 121, -}; - -/* - * \brief Function to convert a feature value into bin index. - * \param val Feature value, in floating-point - * \param fid Feature identifier - * \return bin Index corresponding to given feature value - */ -int cpufj_predictor::quantize(double val, unsigned fid) -{ - const size_t offset = th_begin[fid]; - const double* array = &threshold[offset]; - int len = th_len[fid]; - int low = 0; - int high = len; - int mid; - double mval; - // It is possible th_begin[i] == [total_num_threshold]. This means that - // all features i, (i+1), ... are not used for any of the splits in the model. - // So in this case, just return something - if (offset == 231 || val < array[0]) { return -10; } - while (low + 1 < high) { - mid = (low + high) / 2; - mval = array[mid]; - if (val == mval) { - return mid * 2; - } else if (val < mval) { - high = mid; - } else { - low = mid; - } - } - if (array[low] == val) { - return low * 2; - } else if (high == len) { - return len * 2; - } else { - return low * 2 + 1; - } -} diff --git a/cpp/src/utilities/models/dualsimplex_predictor/header.h b/cpp/src/utilities/models/dualsimplex_predictor/header.h deleted file mode 100644 index 990607ebad..0000000000 --- a/cpp/src/utilities/models/dualsimplex_predictor/header.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -class dualsimplex_predictor { - public: - union Entry { - int missing; - float fvalue; - int qvalue; - }; - - static int32_t get_num_target(void); - static void get_num_class(int32_t* out); - static int32_t get_num_feature(void); - static const char* get_threshold_type(void); - static const char* get_leaf_output_type(void); - static void predict(union Entry* data, int pred_margin, double* result); - static void postprocess(double* result); - static int quantize(float val, unsigned fid); - - // Feature names - static constexpr int NUM_FEATURES = 18; - static const char* feature_names[NUM_FEATURES]; -}; // class dualsimplex_predictor diff --git a/cpp/src/utilities/models/dualsimplex_predictor/main.cpp b/cpp/src/utilities/models/dualsimplex_predictor/main.cpp deleted file mode 100644 index 211e2d0979..0000000000 --- a/cpp/src/utilities/models/dualsimplex_predictor/main.cpp +++ /dev/null @@ -1,19265 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "header.h" - -#if defined(__clang__) || defined(__GNUC__) -#define LIKELY(x) __builtin_expect(!!(x), 1) -#define UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define LIKELY(x) (x) -#define UNLIKELY(x) (x) -#endif -#define N_TARGET 1 -#define MAX_N_CLASS 1 - -const unsigned char is_categorical[] = { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -}; -static const int32_t num_class[] = { - 1, -}; - -int32_t dualsimplex_predictor::get_num_target(void) { return N_TARGET; } -void dualsimplex_predictor::get_num_class(int32_t* out) -{ - for (int i = 0; i < N_TARGET; ++i) { - out[i] = num_class[i]; - } -} -int32_t dualsimplex_predictor::get_num_feature(void) { return 18; } -const char* dualsimplex_predictor::get_threshold_type(void) { return "float32"; } -const char* dualsimplex_predictor::get_leaf_output_type(void) { return "float32"; } - -void dualsimplex_predictor::predict(union Entry* data, int pred_margin, double* result) -{ - // Quantize data - for (int i = 0; i < 18; ++i) { - if (data[i].missing != -1 && !is_categorical[i]) { - data[i].qvalue = quantize(data[i].fvalue, i); - } - } - - unsigned int tmp; - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 30))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += -0.23892987; - } else { - result[0] += -0.41049096; - } - } else { - result[0] += -0.32070833; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { - result[0] += -0.3301402; - } else { - result[0] += -0.27258733; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 158))) { - result[0] += -0.2263252; - } else { - result[0] += -0.03232357; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 244))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += -0.21953094; - } else { - result[0] += -0.2782724; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 252))) { - result[0] += -0.10610497; - } else { - result[0] += -0.18065642; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 238))) { - result[0] += -0.14425437; - } else { - result[0] += -0.027287258; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { - result[0] += -0.06345942; - } else { - result[0] += -0.026511494; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { - result[0] += -0.20900369; - } else { - result[0] += -0.03617132; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += -0.17355898; - } else { - result[0] += -0.094911605; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 174))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { - result[0] += -0.064165816; - } else { - result[0] += -0.12049206; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { - result[0] += -0.11783655; - } else { - result[0] += 0.031279184; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 14))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - result[0] += -0.16023064; - } else { - result[0] += -0.01693966; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 92))) { - result[0] += -0.03719565; - } else { - result[0] += 0.002473806; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 180))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 194))) { - result[0] += 0.015176967; - } else { - result[0] += -0.033581402; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { - result[0] += 0.060070585; - } else { - result[0] += 0.102768324; - } - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 210))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 152))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - result[0] += -0.018653875; - } else { - result[0] += 0.21873346; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 158))) { - result[0] += 0.020794097; - } else { - result[0] += 0.0017005502; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 54))) { - result[0] += -0.15759692; - } else { - result[0] += 0.037903294; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - result[0] += 0.073502585; - } else { - result[0] += 0.1481389; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 72))) { - result[0] += -0.20302705; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { - result[0] += -0.0740808; - } else { - result[0] += -0.0013614334; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { - result[0] += 0.007832853; - } else { - result[0] += 0.05149386; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 286))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - result[0] += 0.038612913; - } else { - result[0] += -0.011268199; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { - result[0] += 0.12736425; - } else { - result[0] += 0.057369012; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 270))) { - result[0] += 0.09891705; - } else { - result[0] += 0.1404978; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 200))) { - result[0] += -0.09467173; - } else { - result[0] += 0.058953542; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 98))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 12))) { - result[0] += 0.24495018; - } else { - result[0] += 0.4303154; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += 0.23759733; - } else { - result[0] += 0.31459442; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { - result[0] += 0.18160371; - } else { - result[0] += 0.119219065; - } - } else { - result[0] += 0.2541432; - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += -0.13811207; - } else { - result[0] += -0.39534476; - } - } else { - result[0] += -0.3041533; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 20))) { - result[0] += -0.29433212; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 78))) { - result[0] += -0.19736992; - } else { - result[0] += -0.25767314; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += -0.19146764; - } else { - result[0] += -0.15695396; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { - result[0] += -0.23099203; - } else { - result[0] += -0.117888466; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 16))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { - result[0] += -0.1496231; - } else { - result[0] += -0.10690468; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 160))) { - result[0] += -0.027002404; - } else { - result[0] += -0.08140517; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { - result[0] += -0.18216138; - } else { - result[0] += -0.023672506; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { - result[0] += -0.12947556; - } else { - result[0] += -0.04930939; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { - result[0] += -0.10542603; - } else { - result[0] += -0.05690325; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 70))) { - result[0] += -0.11127614; - } else { - result[0] += 0.029446835; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 24))) { - result[0] += -0.07772863; - } else { - result[0] += -0.02781359; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 86))) { - result[0] += -0.13588855; - } else { - result[0] += -0.0629831; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 180))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 298))) { - result[0] += 0.008945032; - } else { - result[0] += -0.030025298; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { - result[0] += 0.053789187; - } else { - result[0] += 0.09317514; - } - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 172))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 150))) { - result[0] += -0.01571287; - } else { - result[0] += 0.01397138; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 22))) { - result[0] += -0.004348435; - } else { - result[0] += 0.28950748; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 62))) { - result[0] += 0.073582016; - } else { - result[0] += 0.15677379; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { - result[0] += -0.031289082; - } else { - result[0] += 0.06539955; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { - result[0] += -0.18994586; - } else { - result[0] += -0.066469155; - } - } else { - result[0] += 0.026545838; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 82))) { - result[0] += 0.0066967257; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 122))) { - result[0] += 0.04904222; - } else { - result[0] += 0.012652091; - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 308))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 54))) { - result[0] += 0.036342237; - } else { - result[0] += -0.0249609; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { - result[0] += 0.10429803; - } else { - result[0] += 0.3026218; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 252))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.19158468; - } else { - result[0] += -0.07063005; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 264))) { - result[0] += -0.019653698; - } else { - result[0] += 0.029620511; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 328))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 222))) { - result[0] += 0.16111112; - } else { - result[0] += 0.25347957; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 128))) { - result[0] += 0.084318936; - } else { - result[0] += 0.14822438; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 92))) { - result[0] += 0.22045271; - } else { - result[0] += 0.26758388; - } - } else { - result[0] += 0.46477053; - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 134))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += -0.1654226; - } else { - result[0] += -0.36575767; - } - } else { - result[0] += -0.28903162; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { - result[0] += -0.24255578; - } else { - result[0] += -0.27643654; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { - result[0] += -0.29263017; - } else { - result[0] += -0.20599626; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { - result[0] += -0.18999758; - } else { - result[0] += -0.15109059; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { - result[0] += -0.11279365; - } else { - result[0] += -0.027640833; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { - result[0] += -0.12278713; - } else { - result[0] += -0.029810125; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 100))) { - result[0] += -0.21111389; - } else { - result[0] += -0.13208562; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 146))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 78))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { - result[0] += -0.11854438; - } else { - result[0] += -0.17666526; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 64))) { - result[0] += -0.012537091; - } else { - result[0] += -0.081729345; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 80))) { - result[0] += -0.10848161; - } else { - result[0] += -0.06910487; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - result[0] += -0.048394956; - } else { - result[0] += -0.10724862; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 18))) { - result[0] += -0.05678125; - } else { - result[0] += -0.15847282; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { - result[0] += -0.02826718; - } else { - result[0] += 0.016257456; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 170))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 194))) { - result[0] += 0.009919471; - } else { - result[0] += -0.02828365; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { - result[0] += 0.048264306; - } else { - result[0] += 0.084261395; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 220))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 204))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 154))) { - result[0] += -0.015983945; - } else { - result[0] += 0.0104940515; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - result[0] += 0.059158504; - } else { - result[0] += -0.013891051; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 166))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += -0.05883024; - } else { - result[0] += -0.15169065; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 50))) { - result[0] += 0.017066706; - } else { - result[0] += 0.046345506; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 302))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - result[0] += 0.114555776; - } else { - result[0] += 0.40284094; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - result[0] += 0.027867997; - } else { - result[0] += -0.05320992; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 160))) { - result[0] += 0.02757929; - } else { - result[0] += 0.0005367231; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { - result[0] += 0.0925671; - } else { - result[0] += -0.04869043; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 54))) { - result[0] += 0.033666294; - } else { - result[0] += -0.019405624; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 138))) { - result[0] += 0.085760355; - } else { - result[0] += 0.11623438; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 136))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 78))) { - result[0] += -0.078748904; - } else { - result[0] += -0.03139619; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { - result[0] += 0.020764524; - } else { - result[0] += 0.15907182; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 156))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += 0.19595024; - } else { - result[0] += 0.25730672; - } - } else { - result[0] += 0.41746393; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 90))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 20))) { - result[0] += 0.18315254; - } else { - result[0] += 0.3391243; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { - result[0] += 0.1157012; - } else { - result[0] += 0.20929395; - } - } - } - } - } - } - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 108))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 74))) { - result[0] += -0.14199895; - } else { - result[0] += -0.0956778; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 140))) { - result[0] += -0.010430599; - } else { - result[0] += 0.019095603; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { - result[0] += -0.3010808; - } else { - result[0] += -0.23643827; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { - result[0] += -0.20573525; - } else { - result[0] += -0.02070453; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 204))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 68))) { - result[0] += -0.120953314; - } else { - result[0] += -0.075053215; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { - result[0] += 0.023893813; - } else { - result[0] += -0.05592366; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += -0.16090266; - } else { - result[0] += -0.22481023; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 86))) { - result[0] += -0.14591117; - } else { - result[0] += -0.081503354; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 124))) { - result[0] += 0.014306935; - } else { - result[0] += -0.15783915; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 156))) { - result[0] += -0.04105756; - } else { - result[0] += -0.014158033; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 142))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 82))) { - result[0] += -0.120835006; - } else { - result[0] += -0.073639594; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 60))) { - result[0] += -0.19654752; - } else { - result[0] += -0.14132342; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 192))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 26))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 76))) { - result[0] += 0.01790429; - } else { - result[0] += -0.00943239; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 238))) { - result[0] += 0.04468555; - } else { - result[0] += 0.011115446; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 62))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 270))) { - result[0] += 0.07178088; - } else { - result[0] += 0.011843091; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { - result[0] += 0.0443505; - } else { - result[0] += 0.14387575; - } - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 254))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 148))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { - result[0] += -0.04890412; - } else { - result[0] += -0.0113124; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 190))) { - result[0] += 0.015577379; - } else { - result[0] += -0.0047443947; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 300))) { - result[0] += 0.03677338; - } else { - result[0] += 0.08133157; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 58))) { - result[0] += 0.11096779; - } else { - result[0] += 0.15190493; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 186))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 70))) { - result[0] += 0.0974144; - } else { - result[0] += 0.012945512; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 228))) { - result[0] += -0.016480766; - } else { - result[0] += 0.020737674; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 176))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { - result[0] += -0.105966404; - } else { - result[0] += -0.01857656; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { - result[0] += -0.033622157; - } else { - result[0] += 0.042780038; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { - result[0] += -0.0662734; - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 140))) { - result[0] += 0.07991419; - } else { - result[0] += 0.039648328; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 172))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { - result[0] += 0.10550159; - } else { - result[0] += 0.03400041; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { - result[0] += 0.11472396; - } else { - result[0] += 0.1589642; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 8))) { - result[0] += 0.14915675; - } else { - result[0] += 0.3244665; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += 0.17384307; - } else { - result[0] += 0.23245731; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { - result[0] += 0.13902494; - } else { - result[0] += 0.07743486; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - result[0] += 0.19507462; - } else { - result[0] += 0.12790385; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 14))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += -0.12810235; - } else { - result[0] += -0.30201578; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { - result[0] += -0.25788355; - } else { - result[0] += -0.21340947; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { - result[0] += -0.21370807; - } else { - result[0] += -0.167573; - } - } else { - result[0] += -0.024952482; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 100))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { - result[0] += -0.171652; - } else { - result[0] += -0.12910992; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 110))) { - result[0] += -0.07442858; - } else { - result[0] += -0.12269157; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 12))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { - result[0] += -0.11061738; - } else { - result[0] += -0.06790245; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 160))) { - result[0] += -0.017483674; - } else { - result[0] += -0.059728812; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 146))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 82))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { - result[0] += -0.13760419; - } else { - result[0] += -0.01986711; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { - result[0] += -0.0773085; - } else { - result[0] += 0.004780628; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 62))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 144))) { - result[0] += -0.08369659; - } else { - result[0] += -0.0037746632; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - result[0] += -0.033646163; - } else { - result[0] += -0.09390003; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 64))) { - result[0] += -0.10188579; - } else { - result[0] += -0.022887342; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { - result[0] += -0.025158664; - } else { - result[0] += 0.011464008; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 170))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 204))) { - result[0] += 0.009676366; - } else { - result[0] += -0.025687149; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { - result[0] += 0.03910928; - } else { - result[0] += 0.06689557; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 220))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 204))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 80))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { - result[0] += 0.039443213; - } else { - result[0] += 0.20666161; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 128))) { - result[0] += -0.01548522; - } else { - result[0] += 0.015606319; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { - result[0] += -0.0018438485; - } else { - result[0] += -0.03514717; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { - result[0] += -0.029947493; - } else { - result[0] += -0.056570943; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 302))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - result[0] += 0.15045454; - } else { - result[0] += 0.01461363; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - result[0] += 0.031331427; - } else { - result[0] += 0.2708533; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += -0.044682816; - } else { - result[0] += 0.019505082; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 278))) { - result[0] += 0.10829522; - } else { - result[0] += 0.07073908; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - result[0] += 0.027972206; - } else { - result[0] += 0.005189055; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { - result[0] += -0.066226505; - } else { - result[0] += 0.0877003; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 148))) { - result[0] += 0.055681467; - } else { - result[0] += -0.06665366; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 280))) { - result[0] += 0.0809778; - } else { - result[0] += 0.1468879; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { - result[0] += 0.15474413; - } else { - result[0] += 0.20727856; - } - } else { - result[0] += 0.35305986; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 158))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { - result[0] += 0.07976506; - } else { - result[0] += 0.1597683; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - result[0] += 0.13799594; - } else { - result[0] += 0.23070168; - } - } - } - } - } - } - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 104))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.24169908; - } else { - result[0] += -0.107800476; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { - result[0] += 0.0015814465; - } else { - result[0] += -0.14862476; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 82))) { - result[0] += -0.2477706; - } else { - result[0] += -0.1950383; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { - result[0] += -0.17119522; - } else { - result[0] += -0.045236852; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 178))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { - result[0] += 0.025135875; - } else { - result[0] += -0.08694006; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { - result[0] += 0.029477531; - } else { - result[0] += -0.04847209; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 116))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - result[0] += 0.011227789; - } else { - result[0] += -0.09084964; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 82))) { - result[0] += -0.13153963; - } else { - result[0] += -0.18369597; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 152))) { - result[0] += -0.061374586; - } else { - result[0] += -0.111760534; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 276))) { - result[0] += -0.03116385; - } else { - result[0] += -0.085914634; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 142))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - result[0] += 0.0005968995; - } else { - result[0] += -0.08622521; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 70))) { - result[0] += -0.17008883; - } else { - result[0] += -0.11829443; - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 34))) { - result[0] += 0.0151279615; - } else { - result[0] += 0.037952267; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 58))) { - result[0] += 0.059345204; - } else { - result[0] += 0.11877208; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 206))) { - result[0] += -0.12507677; - } else { - result[0] += -0.025324045; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 102))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 246))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - result[0] += 0.044818092; - } else { - result[0] += -0.01875449; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 148))) { - result[0] += -0.11071397; - } else { - result[0] += -0.059991468; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { - result[0] += 0.049274206; - } else { - result[0] += 0.012975462; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { - result[0] += -0.0189122; - } else { - result[0] += 0.060573917; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 148))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - result[0] += 0.02068209; - } else { - result[0] += -0.027593452; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 60))) { - result[0] += -0.008190083; - } else { - result[0] += 0.01192194; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 268))) { - result[0] += 0.02700766; - } else { - result[0] += 0.07031421; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 58))) { - result[0] += 0.09329746; - } else { - result[0] += 0.12738243; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 336))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { - result[0] += 0.04464999; - } else { - result[0] += 0.0775819; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 62))) { - result[0] += -0.05400176; - } else { - result[0] += 0.0332122; - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 60))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { - result[0] += 0.06202929; - } else { - result[0] += 0.0011356722; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - result[0] += 0.093274996; - } else { - result[0] += 0.12987015; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 124))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { - result[0] += 0.15837678; - } else { - result[0] += 0.3200806; - } - } else { - result[0] += 0.34475276; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { - result[0] += 0.09141225; - } else { - result[0] += 0.15360029; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += 0.14422126; - } else { - result[0] += 0.18972561; - } - } - } - } - } - } - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 106))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 102))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 38))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { - result[0] += -0.22463696; - } else { - result[0] += -0.17236648; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { - result[0] += -0.15470378; - } else { - result[0] += -0.014106827; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 80))) { - result[0] += -0.104941204; - } else { - result[0] += -0.06797316; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { - result[0] += 0.0034419482; - } else { - result[0] += -0.04432465; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 220))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 66))) { - result[0] += -0.09611558; - } else { - result[0] += -0.05750701; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { - result[0] += 0.02356928; - } else { - result[0] += -0.045556568; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { - result[0] += -0.12279941; - } else { - result[0] += -0.16899776; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 124))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { - result[0] += -0.017295085; - } else { - result[0] += 0.031519514; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 156))) { - result[0] += -0.16593443; - } else { - result[0] += -0.110011; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 112))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { - result[0] += -0.010664171; - } else { - result[0] += -0.032727893; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 142))) { - result[0] += -0.07378478; - } else { - result[0] += -0.13850205; - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 134))) { - result[0] += 0.007132952; - } else { - result[0] += 0.033364255; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 62))) { - result[0] += 0.053948194; - } else { - result[0] += 0.105476275; - } - } - } else { - result[0] += -0.07528159; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 254))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 154))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - result[0] += 0.017853899; - } else { - result[0] += -0.021608775; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 190))) { - result[0] += 0.012284887; - } else { - result[0] += -0.0040449556; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - result[0] += 0.027741803; - } else { - result[0] += 0.063282035; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 56))) { - result[0] += 0.08446597; - } else { - result[0] += 0.116814755; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 200))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 70))) { - result[0] += 0.0801467; - } else { - result[0] += 0.00028896204; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 206))) { - result[0] += -0.0006912606; - } else { - result[0] += 0.038264047; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 152))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { - result[0] += -0.09579014; - } else { - result[0] += -0.011496059; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 124))) { - result[0] += 0.028059239; - } else { - result[0] += -0.029746488; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 332))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 60))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { - result[0] += 0.04334253; - } else { - result[0] += 0.094181366; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { - result[0] += 0.010113914; - } else { - result[0] += 0.12312211; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 12))) { - result[0] += 0.008063223; - } else { - result[0] += 0.05981284; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 44))) { - result[0] += 0.055709254; - } else { - result[0] += 0.102501765; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 126))) { - result[0] += 0.19393599; - } else { - result[0] += 0.31415454; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += 0.12953043; - } else { - result[0] += 0.17184684; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 152))) { - result[0] += 0.105829634; - } else { - result[0] += 0.05405576; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 342))) { - result[0] += 0.11501894; - } else { - result[0] += 0.14885037; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 142))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { - result[0] += -0.22360039; - } else { - result[0] += -0.16961168; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 62))) { - result[0] += -0.17449044; - } else { - result[0] += -0.13112317; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { - result[0] += -0.19438948; - } else { - result[0] += -0.07163301; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 58))) { - result[0] += -0.12211982; - } else { - result[0] += -0.1598212; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 104))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { - result[0] += -0.1360238; - } else { - result[0] += -0.08778509; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 172))) { - result[0] += -0.058016848; - } else { - result[0] += -0.01712372; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += -0.10796758; - } else { - result[0] += -0.1462996; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { - result[0] += -0.026550526; - } else { - result[0] += -0.09057524; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { - result[0] += -0.1045504; - } else { - result[0] += -0.06835998; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - result[0] += -0.16272134; - } else { - result[0] += -0.10771642; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 236))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { - result[0] += -0.1004568; - } else { - result[0] += -0.02189359; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 12))) { - result[0] += -0.11555834; - } else { - result[0] += -0.064784154; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 110))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 78))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { - result[0] += -0.09062075; - } else { - result[0] += -0.01616345; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 144))) { - result[0] += -0.046594307; - } else { - result[0] += -0.00848129; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 220))) { - result[0] += 0.048658125; - } else { - result[0] += 0.015779605; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += -0.003939248; - } else { - result[0] += -0.031186854; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 222))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 254))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 154))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - result[0] += 0.0046192957; - } else { - result[0] += 0.02478583; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - result[0] += 0.24694644; - } else { - result[0] += -0.104915835; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - result[0] += -0.0028355815; - } else { - result[0] += -0.03066038; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { - result[0] += -0.038436286; - } else { - result[0] += 0.03750279; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 78))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 186))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 318))) { - result[0] += -0.012060843; - } else { - result[0] += -0.0011414163; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 314))) { - result[0] += -0.008850611; - } else { - result[0] += 0.015718905; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 262))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { - result[0] += 0.05882001; - } else { - result[0] += 0.036198247; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 184))) { - result[0] += -0.065134026; - } else { - result[0] += -0.01131267; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 122))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { - result[0] += 0.2215176; - } else { - result[0] += -0.10633468; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - result[0] += 0.1440095; - } else { - result[0] += 0.051038798; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 254))) { - result[0] += -0.014680609; - } else { - result[0] += 0.014171252; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 348))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 224))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 94))) { - result[0] += 0.056311846; - } else { - result[0] += 0.1058489; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 362))) { - result[0] += 0.037274715; - } else { - result[0] += 0.11873694; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 34))) { - result[0] += 0.11126106; - } else { - result[0] += 0.14717433; - } - } else { - result[0] += 0.26775053; - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 134))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - result[0] += -0.20885597; - } else { - result[0] += -0.16570954; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += -0.13637789; - } else { - result[0] += -0.1756009; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { - result[0] += -0.21495152; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { - result[0] += -0.15078692; - } else { - result[0] += -0.11473024; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - result[0] += -0.08377748; - } else { - result[0] += -0.020055672; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - result[0] += -0.12106979; - } else { - result[0] += -0.092178635; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 16))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { - result[0] += -0.07951072; - } else { - result[0] += -0.04535841; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 52))) { - result[0] += 0.015479415; - } else { - result[0] += -0.034244347; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 202))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { - result[0] += -0.10090798; - } else { - result[0] += -0.072621964; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - result[0] += -0.08769948; - } else { - result[0] += -0.020480268; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 52))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 158))) { - result[0] += -0.06127919; - } else { - result[0] += 0.0065026283; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { - result[0] += -0.06342202; - } else { - result[0] += -0.021188881; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 72))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 100))) { - result[0] += -0.04954914; - } else { - result[0] += 0.00018559814; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 44))) { - result[0] += 0.011565405; - } else { - result[0] += -0.011750548; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 146))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { - result[0] += 0.075431995; - } else { - result[0] += -0.005443739; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 62))) { - result[0] += 0.025297; - } else { - result[0] += 0.044371624; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 234))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 206))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 112))) { - result[0] += -0.012151074; - } else { - result[0] += 0.0095630875; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.19431071; - } else { - result[0] += 0.039510164; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - result[0] += -0.002731852; - } else { - result[0] += -0.02748406; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { - result[0] += -0.039663028; - } else { - result[0] += -0.002819218; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 294))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - result[0] += 0.012211016; - } else { - result[0] += 0.04506604; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { - result[0] += -8.271459e-05; - } else { - result[0] += -0.031217247; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 10))) { - result[0] += -0.027022822; - } else { - result[0] += 0.015010217; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - result[0] += 0.08849927; - } else { - result[0] += 0.04585655; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 330))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - result[0] += 0.015790751; - } else { - result[0] += 0.0709383; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 302))) { - result[0] += 0.014195338; - } else { - result[0] += 0.043147992; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 306))) { - result[0] += 0.027121753; - } else { - result[0] += 0.06687878; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { - result[0] += 0.06856555; - } else { - result[0] += 0.11084541; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 12))) { - result[0] += 0.023235757; - } else { - result[0] += 0.12572078; - } - } else { - result[0] += 0.24674617; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 362))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { - result[0] += 0.08457372; - } else { - result[0] += 0.04069808; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { - result[0] += 0.11791612; - } else { - result[0] += 0.07502523; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 142))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += -0.054601975; - } else { - result[0] += -0.17477903; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 48))) { - result[0] += -0.00046666022; - } else { - result[0] += -0.13438587; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 8))) { - result[0] += -0.07183619; - } else { - result[0] += -0.17046754; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { - result[0] += -0.10375444; - } else { - result[0] += -0.14129147; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - result[0] += -0.07520628; - } else { - result[0] += -0.01688747; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 14))) { - result[0] += -0.11755419; - } else { - result[0] += -0.08725922; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 28))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 2))) { - result[0] += -0.06106487; - } else { - result[0] += -0.09542205; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 202))) { - result[0] += 0.008344304; - } else { - result[0] += -0.03182082; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 64))) { - result[0] += -0.061504174; - } else { - result[0] += -0.13946581; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - result[0] += -0.13620088; - } else { - result[0] += -0.08506644; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 236))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 78))) { - result[0] += -0.10902418; - } else { - result[0] += -0.016639745; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 244))) { - result[0] += -0.10518378; - } else { - result[0] += -0.05821876; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { - result[0] += -0.06888045; - } else { - result[0] += -0.0376487; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 88))) { - result[0] += -0.012150378; - } else { - result[0] += 0.015397436; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 216))) { - result[0] += 0.039432753; - } else { - result[0] += 0.013889983; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += -0.0010314513; - } else { - result[0] += -0.02555494; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 232))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 206))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 200))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 80))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { - result[0] += 0.030333001; - } else { - result[0] += 0.17310323; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { - result[0] += 0.02483304; - } else { - result[0] += 0.0021163383; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { - result[0] += -0.017784828; - } else { - result[0] += -0.039406788; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 234))) { - result[0] += -0.0026142828; - } else { - result[0] += 0.03959627; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 284))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { - result[0] += 0.019107591; - } else { - result[0] += -0.011726064; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { - result[0] += 0.096682; - } else { - result[0] += 0.22430603; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - result[0] += 0.012120399; - } else { - result[0] += -0.03992904; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - result[0] += 0.06968047; - } else { - result[0] += 0.04038763; - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 304))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - result[0] += 0.11028786; - } else { - result[0] += 0.040963266; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - result[0] += 0.19369291; - } else { - result[0] += 0.0747213; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 78))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += -0.057252772; - } else { - result[0] += -0.14311497; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 232))) { - result[0] += -0.0102287065; - } else { - result[0] += 0.016080577; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 330))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { - result[0] += 0.07653522; - } else { - result[0] += 0.13459362; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 130))) { - result[0] += 0.03455645; - } else { - result[0] += 0.06964803; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += 0.09500605; - } else { - result[0] += 0.12430712; - } - } else { - result[0] += 0.22509204; - } - } - } - } - } - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 74))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 20))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 48))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += -0.16930752; - } else { - result[0] += -0.1269749; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += -0.06503186; - } else { - result[0] += -0.12498716; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { - result[0] += -0.1927099; - } else { - result[0] += -0.08201694; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 38))) { - result[0] += -0.068333164; - } else { - result[0] += -0.13142657; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 12))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { - result[0] += 0.042084176; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.14493789; - } else { - result[0] += -0.09196159; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 98))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { - result[0] += 0.03108189; - } else { - result[0] += -0.067129865; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - result[0] += 0.0054919203; - } else { - result[0] += -0.05392629; - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 144))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 98))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { - result[0] += -0.21116984; - } else { - result[0] += -0.0651527; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { - result[0] += 0.116855875; - } else { - result[0] += 0.050092816; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 148))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 20))) { - result[0] += -0.08852083; - } else { - result[0] += -0.04572432; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 270))) { - result[0] += -0.0053127306; - } else { - result[0] += 0.024463532; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 32))) { - result[0] += -0.22384882; - } else { - result[0] += -0.093135744; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 16))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 74))) { - result[0] += -0.031588506; - } else { - result[0] += -0.08342363; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 116))) { - result[0] += 0.012553929; - } else { - result[0] += 0.058796555; - } - } - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 146))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 300))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 46))) { - result[0] += 0.08138124; - } else { - result[0] += 0.13923828; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 202))) { - result[0] += 0.021075254; - } else { - result[0] += 0.064726695; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 98))) { - result[0] += 0.016024219; - } else { - result[0] += -0.014299775; - } - } else { - result[0] += -0.04494404; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 82))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - result[0] += -0.03340462; - } else { - result[0] += -0.002975365; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { - result[0] += -0.13237435; - } else { - result[0] += -0.098908305; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - result[0] += -0.019236477; - } else { - result[0] += -0.033017647; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { - result[0] += 0.0251749; - } else { - result[0] += 0.0012758941; - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - result[0] += 0.016069753; - } else { - result[0] += -0.010248724; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - result[0] += -0.028139247; - } else { - result[0] += -0.06801506; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 238))) { - result[0] += 0.031309973; - } else { - result[0] += 0.07697828; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 258))) { - result[0] += 0.03671503; - } else { - result[0] += 0.0007923256; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 282))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { - result[0] += 0.02349461; - } else { - result[0] += -0.00087367493; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { - result[0] += 0.03977532; - } else { - result[0] += 0.096183844; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { - result[0] += -0.08533253; - } else { - result[0] += -0.06052809; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 238))) { - result[0] += -0.055827565; - } else { - result[0] += -0.0045159357; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 152))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { - result[0] += -0.17435847; - } else { - result[0] += -0.09712316; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 88))) { - result[0] += -0.121227026; - } else { - result[0] += -0.09049445; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 90))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 148))) { - result[0] += -0.07094587; - } else { - result[0] += -0.13034694; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - result[0] += -0.0074528246; - } else { - result[0] += -0.1171114; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - result[0] += -0.03242543; - } else { - result[0] += -0.09674471; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 136))) { - result[0] += -0.060467966; - } else { - result[0] += -0.08785518; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 28))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 54))) { - result[0] += -0.047304705; - } else { - result[0] += -0.070193194; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 238))) { - result[0] += -0.027161488; - } else { - result[0] += 0.0049651144; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 124))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - result[0] += -0.05672009; - } else { - result[0] += -0.133577; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { - result[0] += -0.034619935; - } else { - result[0] += -0.074495845; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { - result[0] += -0.016871883; - } else { - result[0] += 0.0060241153; - } - } else { - result[0] += -0.07992915; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 256))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { - result[0] += -0.059571445; - } else { - result[0] += -0.026049837; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 164))) { - result[0] += 0.0035159283; - } else { - result[0] += -0.047432255; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { - result[0] += -0.009207371; - } else { - result[0] += 0.026254697; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 28))) { - result[0] += -0.083354376; - } else { - result[0] += -0.025358835; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 234))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { - result[0] += 0.004427155; - } else { - result[0] += 0.030195776; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - result[0] += 0.043274168; - } else { - result[0] += 0.0072428077; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 92))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 164))) { - result[0] += -0.052554864; - } else { - result[0] += 0.0033617232; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { - result[0] += -0.037242856; - } else { - result[0] += -0.008167746; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 112))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.094510235; - } else { - result[0] += 0.01284566; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 26))) { - result[0] += 0.07989215; - } else { - result[0] += 0.17329922; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 76))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += 0.058576096; - } else { - result[0] += -0.055972952; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - result[0] += 0.21819083; - } else { - result[0] += 0.09395635; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 130))) { - result[0] += 0.01516776; - } else { - result[0] += 0.033623938; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 106))) { - result[0] += 0.043214235; - } else { - result[0] += 0.119155124; - } - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 38))) { - result[0] += 0.09584941; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - result[0] += 0.080481224; - } else { - result[0] += 0.03998689; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 318))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 126))) { - result[0] += 0.026821358; - } else { - result[0] += -0.031004164; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 202))) { - result[0] += 0.06501947; - } else { - result[0] += 0.19631971; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { - result[0] += 0.07131386; - } else { - result[0] += 0.028573189; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 314))) { - result[0] += 0.083791435; - } else { - result[0] += 0.17529456; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 124))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 22))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { - result[0] += -0.01719068; - } else { - result[0] += -0.064043045; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { - result[0] += -0.1791604; - } else { - result[0] += -0.13283774; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { - result[0] += -0.056413215; - } else { - result[0] += -0.14871566; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { - result[0] += -0.09970745; - } else { - result[0] += -0.010530283; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 34))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { - result[0] += -0.019443454; - } else { - result[0] += -0.09627802; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { - result[0] += -0.072033755; - } else { - result[0] += -0.029162338; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { - result[0] += -0.04072467; - } else { - result[0] += -0.07095223; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { - result[0] += 9.782781e-05; - } else { - result[0] += -0.028554544; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += -0.10857904; - } else { - result[0] += -0.17578779; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 148))) { - result[0] += -0.05417463; - } else { - result[0] += -0.08070096; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 300))) { - result[0] += -0.06944825; - } else { - result[0] += -0.0195438; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 96))) { - result[0] += -0.032838166; - } else { - result[0] += -0.013822776; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 298))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 232))) { - result[0] += -0.034293767; - } else { - result[0] += -0.010428538; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 38))) { - result[0] += -0.008245598; - } else { - result[0] += 0.0061710696; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { - result[0] += -0.00058508094; - } else { - result[0] += -0.048794635; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { - result[0] += 0.014517671; - } else { - result[0] += 0.029143566; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 210))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 162))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 10))) { - result[0] += -0.051883638; - } else { - result[0] += -0.121891774; - } - } else { - result[0] += 0.006774173; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { - result[0] += -0.05183841; - } else { - result[0] += -0.023333719; - } - } else { - result[0] += 0.041755553; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 128))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += -0.021229211; - } else { - result[0] += 0.023228422; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 76))) { - result[0] += 0.0065774354; - } else { - result[0] += -0.0068485746; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 28))) { - result[0] += 0.049541216; - } else { - result[0] += 0.21175821; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 10))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 292))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.1843134; - } else { - result[0] += 0.011620779; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 280))) { - result[0] += -0.049533408; - } else { - result[0] += 0.019996347; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 272))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - result[0] += 0.07229465; - } else { - result[0] += 0.019160887; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - result[0] += 0.0056915763; - } else { - result[0] += 0.04898624; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 328))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 56))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 124))) { - result[0] += 0.010618052; - } else { - result[0] += 0.039828878; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - result[0] += 0.08413791; - } else { - result[0] += 0.05419789; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 128))) { - result[0] += 0.04146382; - } else { - result[0] += 0.08484615; - } - } else { - result[0] += 0.17491151; - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 146))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 30))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { - result[0] += -0.14357229; - } else { - result[0] += -0.06613964; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - result[0] += -0.07836775; - } else { - result[0] += 0.008682779; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 20))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { - result[0] += -0.085223936; - } else { - result[0] += -0.10550176; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 84))) { - result[0] += -0.04918786; - } else { - result[0] += -0.0712194; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { - result[0] += -0.0485091; - } else { - result[0] += -0.07085284; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 154))) { - result[0] += -0.04310399; - } else { - result[0] += -0; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 168))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { - result[0] += -0.03786544; - } else { - result[0] += -0.06086583; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 24))) { - result[0] += -0.026798641; - } else { - result[0] += -0.0029160806; - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 66))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { - result[0] += -0.041853126; - } else { - result[0] += -0.067471944; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.10217434; - } else { - result[0] += -0.031203955; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { - result[0] += -0.020556139; - } else { - result[0] += 0.0036511559; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { - result[0] += -0.0052104336; - } else { - result[0] += 0.022399493; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 266))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { - result[0] += -0.050368812; - } else { - result[0] += -0.015018652; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 98))) { - result[0] += 0.050732918; - } else { - result[0] += -0.017873917; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { - result[0] += 0.16741006; - } else { - result[0] += 0.08613744; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { - result[0] += 0.02335409; - } else { - result[0] += -0.0018855215; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 234))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 200))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 172))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - result[0] += 0.0073416415; - } else { - result[0] += 0.029692546; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 146))) { - result[0] += -0.0061891414; - } else { - result[0] += -0.021139516; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 378))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 224))) { - result[0] += -0.018264985; - } else { - result[0] += -0.0061430032; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - result[0] += 0.024317574; - } else { - result[0] += -0.023863515; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 294))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 90))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.06744468; - } else { - result[0] += 0.2128239; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - result[0] += 0.013560965; - } else { - result[0] += -0.010385789; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 156))) { - result[0] += 0.036912605; - } else { - result[0] += 0.008162293; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { - result[0] += 0.07527434; - } else { - result[0] += 0.013765368; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 362))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 74))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 132))) { - result[0] += -0.040987097; - } else { - result[0] += 0.009021326; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { - result[0] += 0.026009029; - } else { - result[0] += 0.08366104; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 34))) { - result[0] += 0.064916424; - } else { - result[0] += 0.09441544; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 4))) { - result[0] += 0.08905909; - } else { - result[0] += 0.020877834; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 318))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 126))) { - result[0] += 0.02257039; - } else { - result[0] += -0.025986714; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 202))) { - result[0] += 0.05338145; - } else { - result[0] += 0.15790914; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 220))) { - result[0] += 0.018759893; - } else { - result[0] += 0.056597423; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 318))) { - result[0] += 0.06810229; - } else { - result[0] += 0.16054772; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 156))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { - result[0] += -0.06675883; - } else { - result[0] += -0.04909338; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 80))) { - result[0] += -0.045537602; - } else { - result[0] += -0.021407653; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 102))) { - result[0] += -0.036747407; - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 38))) { - result[0] += -0.0055241673; - } else { - result[0] += 0.0064017037; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { - result[0] += -0.1232142; - } else { - result[0] += -0.08944999; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - result[0] += -0.103527024; - } else { - result[0] += -0.06685146; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 8))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 110))) { - result[0] += -0.087132424; - } else { - result[0] += -0.0155411465; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { - result[0] += 0.007360445; - } else { - result[0] += -0.042419024; - } - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 104))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 102))) { - result[0] += -0.02354617; - } else { - result[0] += 0.0067315414; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { - result[0] += -0.02450421; - } else { - result[0] += -0.058597732; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 32))) { - result[0] += 0.023825407; - } else { - result[0] += 0.18043439; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 8))) { - result[0] += -0.043022137; - } else { - result[0] += 0.029165251; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 222))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 208))) { - result[0] += -0.07280388; - } else { - result[0] += -0.046989717; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += 0.016371334; - } else { - result[0] += -0.0062531256; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { - result[0] += -0.00938953; - } else { - result[0] += 0.011327556; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 98))) { - result[0] += -0.057005074; - } else { - result[0] += -0.011778428; - } - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 268))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 264))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 46))) { - result[0] += 0.030093227; - } else { - result[0] += 0.11276569; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - result[0] += 0.0048071328; - } else { - result[0] += -0.030283172; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { - result[0] += 0.07801795; - } else { - result[0] += 0.010139103; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { - result[0] += 0.015687512; - } else { - result[0] += 0.057320017; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 128))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 162))) { - result[0] += 0.16135572; - } else { - result[0] += 0.10743537; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { - result[0] += 0.0299298; - } else { - result[0] += -0.025237197; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 62))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { - result[0] += 0.01356712; - } else { - result[0] += 0.027944017; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 284))) { - result[0] += 0.09368516; - } else { - result[0] += 0.00735568; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 212))) { - result[0] += 0.06613142; - } else { - result[0] += 0.030379802; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 296))) { - result[0] += 0.037492864; - } else { - result[0] += 0.08968493; - } - } - } else { - result[0] += 0.14285138; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 240))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 122))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 294))) { - result[0] += 0.10012319; - } else { - result[0] += 0.04607804; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { - result[0] += 0.00882749; - } else { - result[0] += 0.034421023; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 188))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { - result[0] += 0.020126143; - } else { - result[0] += 0.056154665; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 344))) { - result[0] += 0.06664233; - } else { - result[0] += 0.028045407; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 124))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { - result[0] += -0.08264944; - } else { - result[0] += -0.017300455; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 132))) { - result[0] += -0.09584019; - } else { - result[0] += -0.03871246; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 8))) { - result[0] += -0.027366564; - } else { - result[0] += 0.005001399; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { - result[0] += -0.015287772; - } else { - result[0] += -0.06076094; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - result[0] += -0.01928839; - } else { - result[0] += -0.05802682; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 14))) { - result[0] += -0.069114976; - } else { - result[0] += -0.050886452; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 168))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 58))) { - result[0] += -0.02828979; - } else { - result[0] += -0.049417857; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 54))) { - result[0] += 0.018446451; - } else { - result[0] += -0.017286932; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { - result[0] += -0; - } else { - result[0] += -0.1095063; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - result[0] += -0.112913184; - } else { - result[0] += -0.041024093; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 236))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { - result[0] += -0.036767628; - } else { - result[0] += -0.0056491303; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 50))) { - result[0] += -0.05497813; - } else { - result[0] += -0.02247507; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 124))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 94))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { - result[0] += -0.04725503; - } else { - result[0] += -0.022799945; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - result[0] += -0.03265686; - } else { - result[0] += 0.0007802085; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 54))) { - result[0] += 0.008465467; - } else { - result[0] += 0.022264598; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { - result[0] += -0.008557126; - } else { - result[0] += -0.050682377; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 230))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 56))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 84))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { - result[0] += -0.23723964; - } else { - result[0] += -0.0674392; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 96))) { - result[0] += 0.017011134; - } else { - result[0] += -0.023735067; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 156))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { - result[0] += -0.021249495; - } else { - result[0] += 0.0016016475; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { - result[0] += 0.004315733; - } else { - result[0] += 0.029064924; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 30))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 18))) { - result[0] += 0.016252542; - } else { - result[0] += 0.07114914; - } - } else { - result[0] += 0.18867145; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 4))) { - result[0] += -0.043993976; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 52))) { - result[0] += 0.04012975; - } else { - result[0] += 0.0784916; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 68))) { - result[0] += -0.1251441; - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 124))) { - result[0] += 0.1326; - } else { - result[0] += 0.07419976; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - result[0] += 0.070240654; - } else { - result[0] += 0.0142424395; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - result[0] += 0.008635036; - } else { - result[0] += 0.05565952; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 104))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 326))) { - result[0] += 0.0021902567; - } else { - result[0] += 0.12992251; - } - } else { - result[0] += 0.14213897; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 342))) { - result[0] += 0.018112415; - } else { - result[0] += 0.048771314; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { - result[0] += 0.040432267; - } else { - result[0] += 0.06690516; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 158))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 4))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { - result[0] += -0.084643945; - } else { - result[0] += -0.05658353; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { - result[0] += -0.14270312; - } else { - result[0] += -0.096725866; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 10))) { - result[0] += -0.072900295; - } else { - result[0] += -0.050103154; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { - result[0] += 0.0026472413; - } else { - result[0] += -0.034194175; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 210))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 172))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - result[0] += -0.08699354; - } else { - result[0] += -0.029962752; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 258))) { - result[0] += -0.012024823; - } else { - result[0] += -0.040718153; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 38))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 278))) { - result[0] += 1.4413594e-05; - } else { - result[0] += -0.01927815; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 52))) { - result[0] += -0.004854994; - } else { - result[0] += 0.0106780855; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 88))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 28))) { - result[0] += -0.07230939; - } else { - result[0] += -0.029629577; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { - result[0] += -0.018495014; - } else { - result[0] += 0.019453213; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 90))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - result[0] += -0.01461862; - } else { - result[0] += 0.10096019; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - result[0] += -0.029421015; - } else { - result[0] += 9.543482e-05; - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 90))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 60))) { - result[0] += -0.021362139; - } else { - result[0] += -0.063853346; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 62))) { - result[0] += 0.00073504547; - } else { - result[0] += 0.019957332; - } - } - } else { - result[0] += 0.09791734; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 294))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 60))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 18))) { - result[0] += -0.005831562; - } else { - result[0] += -0.06499735; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 132))) { - result[0] += -0.0071320483; - } else { - result[0] += -0.055945735; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 58))) { - result[0] += 0.0037681882; - } else { - result[0] += 0.05542149; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 82))) { - result[0] += 0.011588092; - } else { - result[0] += -0.0017960105; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { - result[0] += -0.16209017; - } else { - result[0] += 0.019222626; - } - } else { - result[0] += 0.13090767; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 270))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 14))) { - result[0] += -0.030162899; - } else { - result[0] += -0.008859159; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 184))) { - result[0] += 0.028086677; - } else { - result[0] += 0.0010727844; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - result[0] += 0.07769736; - } else { - result[0] += 0.114956; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 62))) { - result[0] += 0.026280258; - } else { - result[0] += 0.063517205; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 198))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 184))) { - result[0] += 0.028841827; - } else { - result[0] += 0.07754768; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 220))) { - result[0] += 0.023495244; - } else { - result[0] += 0.058550116; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 230))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { - result[0] += 0.006005039; - } else { - result[0] += 0.0330562; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 182))) { - result[0] += 0.02288873; - } else { - result[0] += 0.052012473; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 92))) { - result[0] += -0.023153892; - } else { - result[0] += 0.06317775; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 122))) { - result[0] += 0.09178891; - } else { - result[0] += 0.17105517; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 162))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { - result[0] += -0.051588424; - } else { - result[0] += -0.03475411; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { - result[0] += -0.0234789; - } else { - result[0] += 0.0038708851; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 214))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - result[0] += -0.008086841; - } else { - result[0] += -0.027431283; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 68))) { - result[0] += -0.001538304; - } else { - result[0] += 0.0073204334; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { - result[0] += -0.05180776; - } else { - result[0] += 0.005863587; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { - result[0] += -0.09820559; - } else { - result[0] += -0.042626407; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { - result[0] += -0.048510984; - } else { - result[0] += -0.071686305; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 58))) { - result[0] += -0.04765085; - } else { - result[0] += -0.010419096; - } - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 28))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { - result[0] += 0.06322976; - } else { - result[0] += -0.03347231; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 12))) { - result[0] += -0.17174184; - } else { - result[0] += -0.08866473; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { - result[0] += -0.019928426; - } else { - result[0] += -0.07354166; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { - result[0] += -0.020242494; - } else { - result[0] += -0.040445287; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - result[0] += -0.008752908; - } else { - result[0] += 0.09380058; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 176))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 198))) { - result[0] += 0.017689208; - } else { - result[0] += -0.0005958941; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 2))) { - result[0] += -0.0046095774; - } else { - result[0] += -0.038875457; - } - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 272))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { - result[0] += -0.046054542; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 132))) { - result[0] += -0.21343382; - } else { - result[0] += -0.087919936; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 50))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - result[0] += 0.012674006; - } else { - result[0] += 0.11185076; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { - result[0] += 0.041192535; - } else { - result[0] += 0.070588894; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { - result[0] += 0.0028114773; - } else { - result[0] += 0.019441808; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { - result[0] += 0.011045033; - } else { - result[0] += -0.022086669; - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 306))) { - result[0] += 0.071599804; - } else { - result[0] += 0.03955371; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 130))) { - result[0] += 0.010970717; - } else { - result[0] += 0.03211321; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 334))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 68))) { - result[0] += 0.059979778; - } else { - result[0] += 0.036047976; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { - result[0] += 0.10536631; - } else { - result[0] += 0.024781441; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 322))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 254))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 210))) { - result[0] += -0.052476525; - } else { - result[0] += -0.019687535; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - result[0] += 0.012497628; - } else { - result[0] += -0.011794323; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 362))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 120))) { - result[0] += 0.013189244; - } else { - result[0] += 0.03505987; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { - result[0] += 0.056928415; - } else { - result[0] += 0.022988325; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 126))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 22))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { - result[0] += -0.058271427; - } else { - result[0] += 0.0006807263; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { - result[0] += -0.10462822; - } else { - result[0] += -0.068169676; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 12))) { - result[0] += -0.0072206096; - } else { - result[0] += 0.0307555; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 50))) { - result[0] += -0.050685406; - } else { - result[0] += -0.023581887; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 296))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - result[0] += -0.005921537; - } else { - result[0] += -0.042507444; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += -0.052036572; - } else { - result[0] += -0.034811404; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - result[0] += 0.0008109753; - } else { - result[0] += -0.023712752; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { - result[0] += -0.081094466; - } else { - result[0] += -0.017712194; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { - result[0] += -0.09813706; - } else { - result[0] += -0.03229853; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 236))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 82))) { - result[0] += -0.028393775; - } else { - result[0] += -0.0027526347; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { - result[0] += -0.047838878; - } else { - result[0] += -0.026909484; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 18))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 12))) { - result[0] += -0.13414939; - } else { - result[0] += -0.05853174; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 8))) { - result[0] += -0.08385962; - } else { - result[0] += -0.029232157; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 112))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 94))) { - result[0] += -0.012187723; - } else { - result[0] += -0.00030637285; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - result[0] += -0.05476123; - } else { - result[0] += -0.002212812; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 234))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 62))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 56))) { - result[0] += 0.034629773; - } else { - result[0] += 0.090757936; - } - } else { - result[0] += -0.015683705; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 4))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - result[0] += 0.0014823362; - } else { - result[0] += -0.11727782; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 328))) { - result[0] += -0.011885715; - } else { - result[0] += 0.06458332; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - result[0] += 0.009296111; - } else { - result[0] += -0.0022434364; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 390))) { - result[0] += 0.09074904; - } else { - result[0] += 0.022570081; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - result[0] += -0.010527322; - } else { - result[0] += 0.0112762675; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { - result[0] += -0.026221529; - } else { - result[0] += -0.0167725; - } - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 128))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 156))) { - result[0] += 0.134217; - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 242))) { - result[0] += 0.055591363; - } else { - result[0] += 0.09201013; - } - } - } else { - result[0] += -0.07688349; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 10))) { - result[0] += 0.008139768; - } else { - result[0] += 0.027951911; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - result[0] += 0.03482144; - } else { - result[0] += 0.12041791; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 330))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 56))) { - result[0] += 0.019060235; - } else { - result[0] += 0.03520998; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { - result[0] += 0.05164655; - } else { - result[0] += 0.10352349; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 62))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 4))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { - result[0] += -0.05586126; - } else { - result[0] += -0.0062299026; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { - result[0] += -0.08666905; - } else { - result[0] += -0.057932485; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 106))) { - result[0] += -0.037010778; - } else { - result[0] += -0.060017806; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { - result[0] += 0.008048224; - } else { - result[0] += -0.025649264; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += -0.018599523; - } else { - result[0] += -0.063888915; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { - result[0] += -0.055958636; - } else { - result[0] += 0.0054995283; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 242))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { - result[0] += -0.015452884; - } else { - result[0] += -0.0031880846; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { - result[0] += 6.0049097e-06; - } else { - result[0] += 0.011630835; - } - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 80))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 104))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - result[0] += -0.012825543; - } else { - result[0] += 0.0061619915; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - result[0] += -0.035948735; - } else { - result[0] += -0.005659299; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 22))) { - result[0] += -0.0034629384; - } else { - result[0] += 0.11748111; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { - result[0] += -0.045964625; - } else { - result[0] += 0.027189994; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 220))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 288))) { - result[0] += -0.043134686; - } else { - result[0] += -0.008114795; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 136))) { - result[0] += 0.022731418; - } else { - result[0] += -0.0039848676; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { - result[0] += -0.0025353373; - } else { - result[0] += 0.017748738; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 68))) { - result[0] += -0.00209215; - } else { - result[0] += -0.026470816; - } - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 268))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += -0.0426252; - } else { - result[0] += -0.17894174; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 46))) { - result[0] += 0.034646492; - } else { - result[0] += 0.08830755; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - result[0] += 0.0036898192; - } else { - result[0] += 0.10474273; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 278))) { - result[0] += 0.0591173; - } else { - result[0] += 0.10110114; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { - result[0] += 0.017330313; - } else { - result[0] += -0.03201217; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 92))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { - result[0] += 0.020424707; - } else { - result[0] += 0.06761751; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 374))) { - result[0] += 0.0006410443; - } else { - result[0] += 0.025116405; - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 342))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 228))) { - result[0] += 0.002580609; - } else { - result[0] += 0.015501295; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 26))) { - result[0] += -0.021991013; - } else { - result[0] += 0.1534664; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 272))) { - result[0] += -0.004891763; - } else { - result[0] += 0.024440682; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 344))) { - result[0] += 0.04597979; - } else { - result[0] += 0.013181034; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - result[0] += 0.08511517; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 12))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { - result[0] += 0.017548429; - } else { - result[0] += -0.051454138; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 312))) { - result[0] += 0.0665479; - } else { - result[0] += 0.03926411; - } - } - } - } - } - } - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 52))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 4))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 120))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += -0.034856033; - } else { - result[0] += 0.035472337; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.082092844; - } else { - result[0] += -0.057657477; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 96))) { - result[0] += 0.042515777; - } else { - result[0] += -0.018864583; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 142))) { - result[0] += -0.057759553; - } else { - result[0] += -0.020723581; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 26))) { - result[0] += 0.054745015; - } else { - result[0] += 0.017524384; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 164))) { - result[0] += -0.03406791; - } else { - result[0] += -0.08434524; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 28))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { - result[0] += 0.017600825; - } else { - result[0] += -0.03500613; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { - result[0] += -0.046339255; - } else { - result[0] += -0.018127348; - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { - result[0] += -0.037658464; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { - result[0] += 0.0075216624; - } else { - result[0] += 0.0364659; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 134))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { - result[0] += 0.033616688; - } else { - result[0] += 0.083399825; - } - } else { - result[0] += 0.12963086; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.074191265; - } else { - result[0] += 0.007478449; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 42))) { - result[0] += 0.098748855; - } else { - result[0] += 0.030946104; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - result[0] += -0.026153265; - } else { - result[0] += -0.15095265; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 74))) { - result[0] += -0.018939627; - } else { - result[0] += -0.06403255; - } - } - } - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - result[0] += -0.021922093; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 134))) { - result[0] += -0.08688069; - } else { - result[0] += -0.033859696; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 86))) { - result[0] += -0.016842697; - } else { - result[0] += -0.0006648888; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 242))) { - result[0] += -0.023793818; - } else { - result[0] += -0.035974815; - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 302))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { - result[0] += 0.012690376; - } else { - result[0] += -0.0033918878; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { - result[0] += -0.005548475; - } else { - result[0] += -0.036335457; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 206))) { - result[0] += -0.0005064619; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 376))) { - result[0] += 0.03696196; - } else { - result[0] += 0.015889136; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 84))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 44))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 108))) { - result[0] += -0.019162795; - } else { - result[0] += -0.0054707523; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - result[0] += 0.0037607402; - } else { - result[0] += -0.009698882; - } - } - } else { - result[0] += -0.069641024; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 256))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { - result[0] += 0.045991335; - } else { - result[0] += 0.0303468; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - result[0] += 0.018556785; - } else { - result[0] += -0.028926486; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 222))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 126))) { - result[0] += 0.014895338; - } else { - result[0] += -0.0033647448; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - result[0] += 0.031980734; - } else { - result[0] += 0.0069378153; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 28))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 30))) { - result[0] += -0.091816425; - } else { - result[0] += -0.053907175; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += -0.035644125; - } else { - result[0] += -0.08623389; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - result[0] += -0.038305838; - } else { - result[0] += -0.022053199; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 34))) { - result[0] += -0.041282233; - } else { - result[0] += -0.011869354; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 136))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 118))) { - result[0] += -0.008556094; - } else { - result[0] += -0.023536053; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { - result[0] += -0.023365794; - } else { - result[0] += -0.0052509154; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { - result[0] += -0.008722498; - } else { - result[0] += -0.08987408; - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { - result[0] += 0.032462854; - } else { - result[0] += -0.013032225; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 44))) { - result[0] += 0.0020141487; - } else { - result[0] += 0.016696744; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 172))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { - result[0] += 0.0063471817; - } else { - result[0] += 0.01982216; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { - result[0] += 0.00396263; - } else { - result[0] += -0.017814338; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 310))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 22))) { - result[0] += -0.042612687; - } else { - result[0] += -0.0046601035; - } - } else { - result[0] += -0.007730845; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 66))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { - result[0] += -0.039969202; - } else { - result[0] += -0.116455294; - } - } else { - result[0] += -0.02463033; - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 208))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 60))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 144))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 42))) { - result[0] += 0.022728456; - } else { - result[0] += -0.023419065; - } - } else { - result[0] += 0.06361882; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 74))) { - result[0] += -0.025523862; - } else { - result[0] += -0.051117957; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += -0.006611984; - } else { - result[0] += -0.04705615; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += 0.010768503; - } else { - result[0] += -0.0008824992; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 298))) { - result[0] += -0.040758982; - } else { - result[0] += -0.008637625; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 130))) { - result[0] += 0.029029582; - } else { - result[0] += 0.08626362; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 302))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { - result[0] += 0.011424046; - } else { - result[0] += 0.04683126; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 72))) { - result[0] += -0.033311624; - } else { - result[0] += 0.076620966; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 264))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 106))) { - result[0] += -0.01699209; - } else { - result[0] += -0.032285534; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 104))) { - result[0] += 0.008964994; - } else { - result[0] += -0.01650845; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 60))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 316))) { - result[0] += 0.025268046; - } else { - result[0] += 0.055473793; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { - result[0] += 0.08008851; - } else { - result[0] += 0.01032548; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 326))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 138))) { - result[0] += 0.0054397047; - } else { - result[0] += 0.02308716; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { - result[0] += 0.08063211; - } else { - result[0] += 0.03532891; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 10))) { - result[0] += -0.04106638; - } else { - result[0] += -0.02793704; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { - result[0] += 0.022730371; - } else { - result[0] += -0.015745891; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 38))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += -0.056902584; - } else { - result[0] += -0.11115749; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 52))) { - result[0] += -0.0027035407; - } else { - result[0] += -0.0484084; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 64))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - result[0] += -0.06669981; - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 8))) { - result[0] += -0.058218993; - } else { - result[0] += -0.01607107; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { - result[0] += -0.004913365; - } else { - result[0] += 0.010270695; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 288))) { - result[0] += -0.013211744; - } else { - result[0] += -0.029026702; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 96))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { - result[0] += -0.017454801; - } else { - result[0] += 0.0241193; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.07878145; - } else { - result[0] += -0.021389706; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 150))) { - result[0] += 0.005198505; - } else { - result[0] += -0.025203273; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - result[0] += -0.0036238257; - } else { - result[0] += 0.030112168; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 206))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { - result[0] += -0.014281181; - } else { - result[0] += 0.044825673; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { - result[0] += 0.00095986744; - } else { - result[0] += 0.014824574; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 78))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 248))) { - result[0] += -0.004780053; - } else { - result[0] += 0.023393003; - } - } else { - result[0] += -0.05524193; - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 240))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 264))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 200))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - result[0] += 0.008043467; - } else { - result[0] += -0.035642993; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - result[0] += 0.025907112; - } else { - result[0] += 0.0037340687; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { - result[0] += 0.0053138766; - } else { - result[0] += -0.019315336; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - result[0] += 0.012754604; - } else { - result[0] += -0.017428217; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { - result[0] += -0.018819278; - } else { - result[0] += 0.005015341; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 322))) { - result[0] += -0.046993464; - } else { - result[0] += -0.00060153054; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 334))) { - result[0] += 0.032121107; - } else { - result[0] += 0.07720378; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 206))) { - result[0] += -0.01248775; - } else { - result[0] += 0.021081064; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 220))) { - result[0] += -0.002822095; - } else { - result[0] += 0.010395023; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 24))) { - result[0] += -0.00028166902; - } else { - result[0] += 0.05817884; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 368))) { - result[0] += 0.033362225; - } else { - result[0] += 0.059052836; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 76))) { - result[0] += -0.022754088; - } else { - result[0] += 0.032660067; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 164))) { - result[0] += 0.05608514; - } else { - result[0] += 0.028797118; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 310))) { - result[0] += 0.0028316458; - } else { - result[0] += 0.023484427; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { - result[0] += 0.029160103; - } else { - result[0] += 0.06451139; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 212))) { - result[0] += -0.008154633; - } else { - result[0] += 0.02224008; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 102))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 66))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 34))) { - result[0] += -0.025441715; - } else { - result[0] += -0; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.09877158; - } else { - result[0] += -0.05378988; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { - result[0] += -0.02562209; - } else { - result[0] += -0.042403318; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { - result[0] += 0.02021136; - } else { - result[0] += -0.01660019; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 28))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - result[0] += 0.01719434; - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { - result[0] += -0.018652868; - } else { - result[0] += -0.072789736; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += -0.0050430708; - } else { - result[0] += -0.020398756; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 88))) { - result[0] += 0.0035772503; - } else { - result[0] += 0.04239833; - } - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 222))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - result[0] += -0.008277955; - } else { - result[0] += -0.02545519; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { - result[0] += 0.035175905; - } else { - result[0] += -0.0068374695; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 274))) { - result[0] += 0.0034994117; - } else { - result[0] += -0.012190169; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { - result[0] += -0.0075547704; - } else { - result[0] += 0.01670718; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { - result[0] += -0.043555766; - } else { - result[0] += -0.09016201; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 176))) { - result[0] += -0.043037914; - } else { - result[0] += -2.9695482e-05; - } - } - } else { - result[0] += -0.024823932; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 230))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 6))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += 0.05400118; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { - result[0] += -0.07639449; - } else { - result[0] += -0.016434733; - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 148))) { - result[0] += -0.05022436; - } else { - result[0] += 0.00233022; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 126))) { - result[0] += -0.055654902; - } else { - result[0] += -0.13723731; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 104))) { - result[0] += -0.003547279; - } else { - result[0] += 0.0030764283; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { - result[0] += 0.016396696; - } else { - result[0] += 0.07968046; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - result[0] += 0.017225603; - } else { - result[0] += 0.037653413; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.077591464; - } else { - result[0] += 0.15476403; - } - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 238))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { - result[0] += -0.016144833; - } else { - result[0] += 0.032200087; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 282))) { - result[0] += 0.06538327; - } else { - result[0] += 0.10325643; - } - } - } else { - result[0] += -0.10228916; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 272))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - result[0] += -0.008800676; - } else { - result[0] += 0.009970379; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += -0.015078469; - } else { - result[0] += -0.03748675; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - result[0] += 0.034970764; - } else { - result[0] += -0.026505647; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { - result[0] += 0.037449267; - } else { - result[0] += 0.012034763; - } - } - } - } - } - } - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 160))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 64))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 32))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - result[0] += -0.042211335; - } else { - result[0] += -0.0153662665; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.08464201; - } else { - result[0] += -0.039633475; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 18))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 32))) { - result[0] += -0.0023176766; - } else { - result[0] += 0.011839011; - } - } else { - result[0] += -0.024568608; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 114))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { - result[0] += -0.011506539; - } else { - result[0] += -0.041527543; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 30))) { - result[0] += 0.006311471; - } else { - result[0] += 0.096107006; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { - result[0] += -0.07760205; - } else { - result[0] += -0.024386143; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { - result[0] += 0.0042932476; - } else { - result[0] += -0.024321787; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 278))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 108))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 78))) { - result[0] += -0.00662189; - } else { - result[0] += 0.01475962; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 130))) { - result[0] += 0.03366244; - } else { - result[0] += 0.01162212; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 114))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 28))) { - result[0] += 0.030401085; - } else { - result[0] += 0.1034078; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 76))) { - result[0] += 0.06511612; - } else { - result[0] += 0.004562137; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 258))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 132))) { - result[0] += -0.017277284; - } else { - result[0] += -0.00040395462; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 274))) { - result[0] += -0.028246973; - } else { - result[0] += -0.043952625; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 212))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - result[0] += -0.045117795; - } else { - result[0] += -0.00930117; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { - result[0] += 0.013196451; - } else { - result[0] += -0.019043565; - } - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 310))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 42))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 84))) { - result[0] += -0.026572356; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 164))) { - result[0] += 0.051293522; - } else { - result[0] += -0; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { - result[0] += 0.08133901; - } else { - result[0] += 0.05325532; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 114))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { - result[0] += -0.015609048; - } else { - result[0] += -0.038890243; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 102))) { - result[0] += -0.008053626; - } else { - result[0] += 0.0021804231; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { - result[0] += 0.013441979; - } else { - result[0] += 0.0015576767; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { - result[0] += -0.012684277; - } else { - result[0] += 0.0018506636; - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 66))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { - result[0] += 0.1326864; - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { - result[0] += 0.0802758; - } else { - result[0] += 0.04734662; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 162))) { - result[0] += -0.0053350735; - } else { - result[0] += -0.040489998; - } - } else { - result[0] += 0.057940014; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 228))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 318))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 38))) { - result[0] += -0.00303559; - } else { - result[0] += 0.027338777; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 98))) { - result[0] += 0.021263791; - } else { - result[0] += 0.0065736147; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 220))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { - result[0] += 0.036004547; - } else { - result[0] += 0.00041290582; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { - result[0] += 0.003415088; - } else { - result[0] += 0.03065363; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 112))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 182))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 30))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { - result[0] += -0.061424643; - } else { - result[0] += -0.004366158; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 12))) { - result[0] += -0.029432973; - } else { - result[0] += 0.024416348; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 44))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 38))) { - result[0] += -0.028231425; - } else { - result[0] += 0.013277724; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { - result[0] += -0.05933566; - } else { - result[0] += -0.033664998; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 64))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 276))) { - result[0] += 0.0039472985; - } else { - result[0] += -0.010128739; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 96))) { - result[0] += -0.032275613; - } else { - result[0] += -0.007163971; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 148))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { - result[0] += -0.009553356; - } else { - result[0] += -0.024487672; - } - } else { - result[0] += -0.04257847; - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 106))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - result[0] += -0.017238883; - } else { - result[0] += -0.005016202; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - result[0] += -0.031886365; - } else { - result[0] += -0.013730754; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 64))) { - result[0] += 0.008240308; - } else { - result[0] += 0.03919262; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 38))) { - result[0] += 0.005747178; - } else { - result[0] += -0.058204617; - } - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - result[0] += -0.055713423; - } else { - result[0] += -0.126311; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 160))) { - result[0] += -0.010013968; - } else { - result[0] += -0.03667828; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 62))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 90))) { - result[0] += -0.021773633; - } else { - result[0] += -0.060646553; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { - result[0] += -0.028524075; - } else { - result[0] += -0.0014835782; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 240))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 250))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 58))) { - result[0] += -0.009143605; - } else { - result[0] += 0.0004789403; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { - result[0] += 0.017569097; - } else { - result[0] += 0.06969844; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.053881604; - } else { - result[0] += 0.030100072; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 28))) { - result[0] += 0.07642787; - } else { - result[0] += 0.14859438; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 276))) { - result[0] += -0.0064661647; - } else { - result[0] += 0.0021556418; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { - result[0] += -0.042718846; - } else { - result[0] += 0.004742034; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { - result[0] += 0.0150559945; - } else { - result[0] += 0.02904155; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { - result[0] += 0.030410746; - } else { - result[0] += -0.00021071863; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 130))) { - result[0] += -0.0019750185; - } else { - result[0] += 0.010826028; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 92))) { - result[0] += -0.027783621; - } else { - result[0] += 0.040064003; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 34))) { - result[0] += 0.025106177; - } else { - result[0] += 0.04847647; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { - result[0] += -0.020649737; - } else { - result[0] += 0.047101017; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { - result[0] += 0.0425664; - } else { - result[0] += 0.010982556; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 320))) { - result[0] += 0.0027861602; - } else { - result[0] += 0.024253966; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - result[0] += 0.040306833; - } else { - result[0] += 0.019420182; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 250))) { - result[0] += -0.07389223; - } else { - result[0] += -0.018163418; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 76))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 32))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 134))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - result[0] += -0.033120375; - } else { - result[0] += -0.019771839; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - result[0] += 0.033069532; - } else { - result[0] += -0.024330398; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - result[0] += -0.00891195; - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 260))) { - result[0] += -0.06895655; - } else { - result[0] += -0.029440561; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 112))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { - result[0] += -0.008321228; - } else { - result[0] += -0.032401457; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 28))) { - result[0] += 0.01681263; - } else { - result[0] += -0.011843439; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { - result[0] += 0.009037083; - } else { - result[0] += 0.084029466; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { - result[0] += -0.039284047; - } else { - result[0] += 0.0024618404; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 30))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 138))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 284))) { - result[0] += -0.004148552; - } else { - result[0] += -0.028972754; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 182))) { - result[0] += -0.060149025; - } else { - result[0] += -0.023092858; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 246))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 124))) { - result[0] += -0.0048736627; - } else { - result[0] += 0.011900749; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 152))) { - result[0] += -0.027425656; - } else { - result[0] += -0.0049680914; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - result[0] += 0.018673597; - } else { - result[0] += -0.002092194; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 158))) { - result[0] += 0.025586123; - } else { - result[0] += 0.043332465; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 140))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 134))) { - result[0] += -0.009704775; - } else { - result[0] += 0.033585932; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { - result[0] += -0.04070072; - } else { - result[0] += -0.02585305; - } - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 132))) { - result[0] += 0.026704356; - } else { - result[0] += -0.0026301623; - } - } else { - result[0] += -0.083959244; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 116))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - result[0] += 0.00727385; - } else { - result[0] += -0.00793132; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 34))) { - result[0] += -0.0025618703; - } else { - result[0] += 0.03066427; - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 80))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 192))) { - result[0] += -0.0036948516; - } else { - result[0] += 0.014927791; - } - } else { - result[0] += 0.06770062; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 112))) { - result[0] += 0.021252338; - } else { - result[0] += 0.08691488; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { - result[0] += 0.030034242; - } else { - result[0] += 0.0126524735; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 84))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 294))) { - result[0] += 0.03448388; - } else { - result[0] += 0.012207389; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 82))) { - result[0] += -0.011361829; - } else { - result[0] += 0.010800744; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 374))) { - result[0] += -0.019659309; - } else { - result[0] += -0.010299957; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 222))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 128))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { - result[0] += -0.022616882; - } else { - result[0] += 0.0058049723; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 210))) { - result[0] += -0.05356568; - } else { - result[0] += -0.011999037; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - result[0] += 0.007046343; - } else { - result[0] += 0.04422299; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 200))) { - result[0] += -0.023396257; - } else { - result[0] += -0.015149494; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 110))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 38))) { - result[0] += -0.012656443; - } else { - result[0] += 0.04053043; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 30))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { - result[0] += -0.04405209; - } else { - result[0] += -0.07002534; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.0050658057; - } else { - result[0] += -0.035065096; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 44))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { - result[0] += -0.016180787; - } else { - result[0] += 0.035941508; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 62))) { - result[0] += 0.0028223798; - } else { - result[0] += -0.028867302; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { - result[0] += -0.0008212989; - } else { - result[0] += -0.019350568; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 100))) { - result[0] += -0.012334666; - } else { - result[0] += -0.02596775; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - result[0] += -0.03201983; - } else { - result[0] += -0.009261032; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.043002192; - } else { - result[0] += -0.004841908; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { - result[0] += -0.034105346; - } else { - result[0] += -0.001769832; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 18))) { - result[0] += -0.026078701; - } else { - result[0] += -0.0034125247; - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 68))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 204))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 72))) { - result[0] += -0.035768237; - } else { - result[0] += 0.007326287; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { - result[0] += -0.013266384; - } else { - result[0] += 0.0073778555; - } - } - } else { - result[0] += 0.04042886; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 262))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 314))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - result[0] += -0.031073779; - } else { - result[0] += 0.009902022; - } - } else { - result[0] += -0.10413245; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 304))) { - result[0] += -0.0015145864; - } else { - result[0] += 0.0044673397; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { - result[0] += 0.016366184; - } else { - result[0] += 0.06682373; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - result[0] += 0.1125614; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 158))) { - result[0] += 0.015199658; - } else { - result[0] += -0.047179397; - } - } else { - result[0] += 0.07871269; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 304))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 140))) { - result[0] += 0.037271794; - } else { - result[0] += 0.055074245; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 350))) { - result[0] += 0.01456887; - } else { - result[0] += 0.043336138; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 62))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - result[0] += -0.011651535; - } else { - result[0] += 0.02064044; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 326))) { - result[0] += 0.040328953; - } else { - result[0] += 0.016403003; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 12))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 262))) { - result[0] += -0.0107279355; - } else { - result[0] += 0.005842201; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { - result[0] += -0.038425323; - } else { - result[0] += 0.021354888; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { - result[0] += 0.045149498; - } else { - result[0] += -0; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 272))) { - result[0] += 0.0030525408; - } else { - result[0] += 0.016887192; - } - } - } - } - } - } - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 80))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 80))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { - result[0] += -0.022748465; - } else { - result[0] += -0.0015031536; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 58))) { - result[0] += 0.038551223; - } else { - result[0] += -0.02340021; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { - result[0] += -0.022547202; - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 64))) { - result[0] += 0.006482385; - } else { - result[0] += 0.043839242; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 84))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - result[0] += -0.032270882; - } else { - result[0] += 0.041581474; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 74))) { - result[0] += -0.02748827; - } else { - result[0] += -0.06621357; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { - result[0] += -0.15229563; - } else { - result[0] += -0.056743264; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += 0.0090522; - } else { - result[0] += -0.021619566; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { - result[0] += 0.011768992; - } else { - result[0] += 0.034092333; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 174))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 296))) { - result[0] += -0.0050243097; - } else { - result[0] += 0.0058637843; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 218))) { - result[0] += -0.022230322; - } else { - result[0] += -0; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 234))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 178))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += -0.0040832614; - } else { - result[0] += -0.026436746; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 188))) { - result[0] += 0.0104423; - } else { - result[0] += -0.009707513; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 146))) { - result[0] += 0.021574568; - } else { - result[0] += -0.01677997; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += -0.01483564; - } else { - result[0] += -0.044649884; - } - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 266))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 270))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 58))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - result[0] += 0.027206311; - } else { - result[0] += -0.0112104425; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - result[0] += 0.002884076; - } else { - result[0] += -0.008946375; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 242))) { - result[0] += -0.021231398; - } else { - result[0] += -0.007831926; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - result[0] += -0.010156151; - } else { - result[0] += 0.006795832; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += -0; - } else { - result[0] += 0.01808864; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 156))) { - result[0] += 0.0849195; - } else { - result[0] += 0.040363234; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 208))) { - result[0] += 0.018572433; - } else { - result[0] += -0.016821038; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 238))) { - result[0] += -0.013755165; - } else { - result[0] += 0.010804783; - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 124))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { - result[0] += 0.03828322; - } else { - result[0] += 0.0062655816; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { - result[0] += -0.039595883; - } else { - result[0] += -0.00876256; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 162))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { - result[0] += 0.028006807; - } else { - result[0] += 0.011207984; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 104))) { - result[0] += -0; - } else { - result[0] += 0.04828812; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 236))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 134))) { - result[0] += 0.019871203; - } else { - result[0] += -0.0020931112; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { - result[0] += -0.054860026; - } else { - result[0] += -0.016543666; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 126))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - result[0] += 0.017333688; - } else { - result[0] += -0.014761999; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { - result[0] += 0.0047273724; - } else { - result[0] += 0.018399872; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 8))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { - result[0] += -0.07626998; - } else { - result[0] += 0.020595873; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - result[0] += 0.0014158572; - } else { - result[0] += 0.039809644; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 26))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 28))) { - result[0] += -0.047608785; - } else { - result[0] += -0.08177885; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { - result[0] += -0.018627753; - } else { - result[0] += -0.03965402; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 152))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - result[0] += -0.016198985; - } else { - result[0] += -0.034763936; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { - result[0] += 0.0029702466; - } else { - result[0] += -0.010453141; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 80))) { - result[0] += 0.002711076; - } else { - result[0] += -0.040787876; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { - result[0] += -0.0030517352; - } else { - result[0] += -0.018486565; - } - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 204))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 296))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { - result[0] += -0.01466268; - } else { - result[0] += 0.0016207602; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - result[0] += 0.014294909; - } else { - result[0] += -0.00088145724; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 270))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 230))) { - result[0] += 0.017696586; - } else { - result[0] += 0.0033850037; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 96))) { - result[0] += 0.003758549; - } else { - result[0] += -0.008070237; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 108))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 38))) { - result[0] += -0.012763503; - } else { - result[0] += 0.015098961; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 260))) { - result[0] += -0.06455403; - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { - result[0] += -0.017203202; - } else { - result[0] += 0.0007284813; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 260))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 264))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - result[0] += -0.0004502135; - } else { - result[0] += -0.013600699; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 104))) { - result[0] += 0.0078685535; - } else { - result[0] += 0.052134164; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { - result[0] += -0.047534015; - } else { - result[0] += 0.021326948; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.056880232; - } else { - result[0] += 0.09747363; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 92))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 242))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 276))) { - result[0] += 0.023308432; - } else { - result[0] += 0.011319171; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += 0.051132478; - } else { - result[0] += 0.02103916; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 238))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 288))) { - result[0] += 0.006215774; - } else { - result[0] += -0.0061574043; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 330))) { - result[0] += 0.0041284435; - } else { - result[0] += 0.023122046; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 288))) { - result[0] += 0.034480914; - } else { - result[0] += 0.01336052; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.056260873; - } else { - result[0] += 0.042514727; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 172))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - result[0] += 0.02588587; - } else { - result[0] += -0.032997902; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 316))) { - result[0] += -0.0100605395; - } else { - result[0] += 0.020955171; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 12))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 320))) { - result[0] += 0.0012241629; - } else { - result[0] += 0.010477929; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { - result[0] += -0.038784113; - } else { - result[0] += 0.019064387; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { - result[0] += 0.014094616; - } else { - result[0] += -0.0027222696; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { - result[0] += 0.043263398; - } else { - result[0] += 0.016619174; - } - } - } - } - } - } - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 164))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 106))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 52))) { - result[0] += -0.07700386; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { - result[0] += -0.027042255; - } else { - result[0] += 0.0029873038; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { - result[0] += -0.063133374; - } else { - result[0] += -0.0046702675; - } - } else { - result[0] += -0.097486384; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 110))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 258))) { - result[0] += -0.011726489; - } else { - result[0] += -0.058912832; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { - result[0] += -0.0659786; - } else { - result[0] += -0.02953866; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 58))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 90))) { - result[0] += 0.0242778; - } else { - result[0] += -0.0023044557; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 118))) { - result[0] += 0.029503396; - } else { - result[0] += -0.015513777; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 278))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { - result[0] += -0.003784867; - } else { - result[0] += 0.012548617; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 26))) { - result[0] += 0.0061970223; - } else { - result[0] += 0.023837453; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 330))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { - result[0] += 0.050173163; - } else { - result[0] += -0; - } - } else { - result[0] += -0.007900632; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 86))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 288))) { - result[0] += -0.027921408; - } else { - result[0] += -0.0038356972; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 126))) { - result[0] += -0.0057223137; - } else { - result[0] += 0.010252192; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 188))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 232))) { - result[0] += -0.0093339635; - } else { - result[0] += -0.002617002; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - result[0] += -0.01492499; - } else { - result[0] += -0.025125777; - } - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 308))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 42))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { - result[0] += 0.03968758; - } else { - result[0] += -0.001241333; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { - result[0] += -0.01191957; - } else { - result[0] += 0.002430247; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 116))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 54))) { - result[0] += -0; - } else { - result[0] += 0.08623359; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { - result[0] += -0.0005277793; - } else { - result[0] += 0.01962237; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { - result[0] += -0.0047836783; - } else { - result[0] += -0.015227089; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { - result[0] += 0.008447981; - } else { - result[0] += 0.04540634; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 232))) { - result[0] += -0.022486782; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - result[0] += -0; - } else { - result[0] += -0.015179853; - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 16))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 4))) { - result[0] += -0.041306626; - } else { - result[0] += -0.008218481; - } - } else { - result[0] += 0.042096373; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 316))) { - result[0] += 0.022573106; - } else { - result[0] += 0.07205055; - } - } else { - result[0] += 0.028815215; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 318))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 38))) { - result[0] += -0.0054102; - } else { - result[0] += 0.019898819; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 98))) { - result[0] += 0.013574933; - } else { - result[0] += 0.0023076402; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { - result[0] += 0.023586577; - } else { - result[0] += -0.004840189; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { - result[0] += -0.0051436094; - } else { - result[0] += 0.020131659; - } - } - } - } - } - } - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 162))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 88))) { - result[0] += -0.01574367; - } else { - result[0] += 0.0011124586; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 46))) { - result[0] += -0.07613191; - } else { - result[0] += -0.01636207; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 74))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 24))) { - result[0] += 0.014280135; - } else { - result[0] += 0.037306037; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 24))) { - result[0] += -0.024091965; - } else { - result[0] += 0.0048261676; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 124))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 202))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { - result[0] += -0.04113116; - } else { - result[0] += -0.061350565; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 50))) { - result[0] += -0.03143156; - } else { - result[0] += -0.0036942845; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { - result[0] += 0.024413232; - } else { - result[0] += -0.01646574; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { - result[0] += -0.016089493; - } else { - result[0] += 0.006952781; - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 144))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 274))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 20))) { - result[0] += -0.023807703; - } else { - result[0] += 0.0015813807; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 332))) { - result[0] += 0.029507706; - } else { - result[0] += -0.009649085; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 36))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 24))) { - result[0] += 0.0048679006; - } else { - result[0] += -0.043877617; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { - result[0] += 0.014281779; - } else { - result[0] += 0.047004733; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 164))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { - result[0] += -0.0021749143; - } else { - result[0] += -0.012218022; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { - result[0] += 0.0060172398; - } else { - result[0] += 0.033813428; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 228))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { - result[0] += -0.029434139; - } else { - result[0] += -0.014660637; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - result[0] += -0.008835907; - } else { - result[0] += 0.0022141964; - } - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 310))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 212))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 42))) { - result[0] += 0.023493351; - } else { - result[0] += -0.0012283614; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - result[0] += 0.002782393; - } else { - result[0] += 0.019489653; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 116))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 160))) { - result[0] += 0.07486535; - } else { - result[0] += -0.009162361; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { - result[0] += -0.0022064948; - } else { - result[0] += 0.018015308; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { - result[0] += -0.01574024; - } else { - result[0] += -0.0039653988; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 140))) { - result[0] += 0.020983376; - } else { - result[0] += 0.0030928531; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 238))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 244))) { - result[0] += -0.018868484; - } else { - result[0] += -0.012392565; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 234))) { - result[0] += 0.00805167; - } else { - result[0] += -0.011693968; - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 86))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 96))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 84))) { - result[0] += -0.038049206; - } else { - result[0] += -0.002771721; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 320))) { - result[0] += 0.012239266; - } else { - result[0] += 0.03609361; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { - result[0] += 0.027658317; - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 136))) { - result[0] += 0.051759947; - } else { - result[0] += 0.094818436; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 230))) { - result[0] += 0.005940085; - } else { - result[0] += 0.017185425; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 300))) { - result[0] += -0.02267663; - } else { - result[0] += -0.00748341; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 116))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 22))) { - result[0] += 0.005678715; - } else { - result[0] += 0.02415133; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 76))) { - result[0] += -0.026114738; - } else { - result[0] += 0.06968238; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 102))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 144))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { - result[0] += -0.042726804; - } else { - result[0] += -0.0135103455; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { - result[0] += -0.007055785; - } else { - result[0] += 0.014698033; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { - result[0] += -0.007809716; - } else { - result[0] += -0.025070379; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - result[0] += -0; - } else { - result[0] += 0.02416715; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 242))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - result[0] += -0.004747329; - } else { - result[0] += -0.030286456; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 38))) { - result[0] += 0.004049407; - } else { - result[0] += -0.003883451; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 280))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 44))) { - result[0] += 0.0048826905; - } else { - result[0] += 0.01918045; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { - result[0] += -0.010770427; - } else { - result[0] += 0.010454954; - } - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 126))) { - result[0] += -0.04999471; - } else { - result[0] += -0.1126702; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 8))) { - result[0] += -0.0024832468; - } else { - result[0] += 0.034831993; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 82))) { - result[0] += 0.018345565; - } else { - result[0] += -0.03647999; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 72))) { - result[0] += -0.025135571; - } else { - result[0] += -0; - } - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 42))) { - result[0] += -0.031185156; - } else { - result[0] += -0.011169832; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { - result[0] += -0.014554634; - } else { - result[0] += 0.05174926; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 134))) { - result[0] += 0.033395614; - } else { - result[0] += -0.015813565; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 274))) { - result[0] += 0.00033646842; - } else { - result[0] += 0.0078946445; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 114))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 154))) { - result[0] += 0.008154803; - } else { - result[0] += -0.014786914; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.06375181; - } else { - result[0] += 0.014291716; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 46))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += -0.0068405718; - } else { - result[0] += -0.034137234; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 288))) { - result[0] += 0.027196486; - } else { - result[0] += -0.029254882; - } - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 304))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 104))) { - result[0] += -0.00011478314; - } else { - result[0] += -0.008755667; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { - result[0] += 0.00041385577; - } else { - result[0] += 0.01698925; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 324))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - result[0] += -0.008199277; - } else { - result[0] += -0.01730309; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { - result[0] += 0.0039300146; - } else { - result[0] += 0.015076749; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 232))) { - result[0] += -0.0025358626; - } else { - result[0] += -0.014611224; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 16))) { - result[0] += 0.021280123; - } else { - result[0] += -0.008725259; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { - result[0] += 0.04309545; - } else { - result[0] += 0.0142627945; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { - result[0] += 0.024396539; - } else { - result[0] += 0.0048766937; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 16))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 28))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 70))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 68))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { - result[0] += -0.024164472; - } else { - result[0] += 0.008937138; - } - } else { - result[0] += 0.038625453; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 76))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { - result[0] += -0.04647706; - } else { - result[0] += -0.019371232; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 154))) { - result[0] += -0.007284378; - } else { - result[0] += -0.041731447; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 14))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - result[0] += -0.049801502; - } else { - result[0] += 0.007428868; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { - result[0] += 0.061795365; - } else { - result[0] += 0.022740616; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - result[0] += 0.022688469; - } else { - result[0] += -0.016254207; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { - result[0] += 0.012587378; - } else { - result[0] += -0.019465229; - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 60))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 294))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 70))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += -0.016392779; - } else { - result[0] += 0.0076769763; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 132))) { - result[0] += -0.0070873285; - } else { - result[0] += -0.017376283; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 180))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 58))) { - result[0] += -0.008054349; - } else { - result[0] += 0.00049257686; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { - result[0] += 0.010894717; - } else { - result[0] += 0.0022375863; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 260))) { - result[0] += -0.018788002; - } else { - result[0] += 0.009024569; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 266))) { - result[0] += -0.0017262193; - } else { - result[0] += 0.015350415; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 172))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { - result[0] += 0.01940038; - } else { - result[0] += 0.0023542785; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { - result[0] += 0.003066004; - } else { - result[0] += -0.010361836; - } - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 290))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 104))) { - result[0] += -0.00023940679; - } else { - result[0] += -0.024299331; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 140))) { - result[0] += 0.018921724; - } else { - result[0] += -0.012017216; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 304))) { - result[0] += -0.009317097; - } else { - result[0] += 0.0070780194; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { - result[0] += 0.029872233; - } else { - result[0] += 0.0070561725; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - result[0] += -0.012874908; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 140))) { - result[0] += -0.03384575; - } else { - result[0] += -0.09957864; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 92))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - result[0] += 0.007720273; - } else { - result[0] += 0.050167393; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 288))) { - result[0] += 0.030057851; - } else { - result[0] += 0.012027963; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { - result[0] += -0.03683686; - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { - result[0] += -0.018220117; - } else { - result[0] += 0.0014780884; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 328))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { - result[0] += 0.02194166; - } else { - result[0] += -0.00030332725; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 84))) { - result[0] += -0.007878302; - } else { - result[0] += -0.016368117; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 104))) { - result[0] += -0.015569873; - } else { - result[0] += -0.00039696257; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 2))) { - result[0] += 0.022349734; - } else { - result[0] += 0.06912997; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - result[0] += -0.0011338064; - } else { - result[0] += -0.015420935; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { - result[0] += -0.0004335566; - } else { - result[0] += 0.012481102; - } - } - } - } - } - } - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 226))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 150))) { - result[0] += -0.008768398; - } else { - result[0] += -0.031017033; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - result[0] += -0.0019974862; - } else { - result[0] += -0.010033133; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 282))) { - result[0] += 0.0018652402; - } else { - result[0] += 0.028417757; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - result[0] += -0.02573219; - } else { - result[0] += 0.00014164243; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - result[0] += -0.012296872; - } else { - result[0] += 0.018667158; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 52))) { - result[0] += 0.0111978315; - } else { - result[0] += 0.00019159181; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 104))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - result[0] += -0.018547757; - } else { - result[0] += 0.00039879596; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { - result[0] += -0.025369791; - } else { - result[0] += -0.006152769; - } - } - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - result[0] += 0.0284749; - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 4))) { - result[0] += -0.12034156; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 90))) { - result[0] += 0.01555708; - } else { - result[0] += -0.04060086; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - result[0] += -0.00899754; - } else { - result[0] += -0.041497704; - } - } else { - result[0] += 0.012691744; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { - result[0] += -0.011100016; - } else { - result[0] += -0.065193616; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { - result[0] += -0.026525358; - } else { - result[0] += -0.007171394; - } - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 74))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { - result[0] += 0.017233951; - } else { - result[0] += 0.047289252; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 292))) { - result[0] += -0.020638349; - } else { - result[0] += 0.02203339; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { - result[0] += -0.028595468; - } else { - result[0] += 0.035886366; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - result[0] += 0.023079382; - } else { - result[0] += -0.0015904735; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { - result[0] += 0.01960182; - } else { - result[0] += 0.004229198; - } - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 110))) { - result[0] += 0.042509545; - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 78))) { - result[0] += 0.01140577; - } else { - result[0] += 0.03407; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 134))) { - result[0] += -0.002405419; - } else { - result[0] += -0.010636522; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { - result[0] += 0.012451133; - } else { - result[0] += -0.0039769937; - } - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { - result[0] += -0.01115589; - } else { - result[0] += 0.01320644; - } - } else { - result[0] += -0.013309436; - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 24))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 18))) { - result[0] += -0.054960348; - } else { - result[0] += -0.008308151; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 116))) { - result[0] += 0.040893547; - } else { - result[0] += -0.029395109; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 108))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 14))) { - result[0] += -0.011575009; - } else { - result[0] += 0.0045230165; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - result[0] += -0.01649066; - } else { - result[0] += 0.0117938975; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 28))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 92))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 44))) { - result[0] += -0.017786995; - } else { - result[0] += 0.0030950578; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 38))) { - result[0] += -0.044471394; - } else { - result[0] += 0.022061352; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 52))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { - result[0] += -0.014604069; - } else { - result[0] += 0.016241714; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { - result[0] += 0.018912865; - } else { - result[0] += 0.0019412668; - } - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 306))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 64))) { - result[0] += -0.006083592; - } else { - result[0] += 0.046224184; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += 0.015739193; - } else { - result[0] += -0.022769345; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { - result[0] += 0.03625956; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 212))) { - result[0] += 0.00036286406; - } else { - result[0] += 0.009678096; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 240))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { - result[0] += -0.010175624; - } else { - result[0] += 0.0050961208; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 46))) { - result[0] += -0.0105675245; - } else { - result[0] += -0.026643395; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 226))) { - result[0] += -0; - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 86))) { - result[0] += 0.023571983; - } else { - result[0] += -0; - } - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 222))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 106))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 54))) { - result[0] += -0.07242959; - } else { - result[0] += -0.00901923; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 190))) { - result[0] += 0.0003989732; - } else { - result[0] += -0.014672895; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 56))) { - result[0] += -0.0052175657; - } else { - result[0] += 0.038002204; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { - result[0] += -0.030148897; - } else { - result[0] += -0.006241556; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { - result[0] += -5.1738414e-05; - } else { - result[0] += 0.0063861213; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { - result[0] += 0.022810614; - } else { - result[0] += -0.016576035; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 234))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - result[0] += -0.0050715203; - } else { - result[0] += 0.005531682; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 238))) { - result[0] += -0.01301794; - } else { - result[0] += 0.00030336596; - } - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 180))) { - result[0] += -0.0094006555; - } else { - result[0] += 0.02063076; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - result[0] += 0.010461995; - } else { - result[0] += 0.0025512462; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 62))) { - result[0] += 0.010800346; - } else { - result[0] += 0.052793957; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 16))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { - result[0] += -0.02969619; - } else { - result[0] += -0.01354564; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 126))) { - result[0] += -0.0019324312; - } else { - result[0] += 0.018997952; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 228))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { - result[0] += 0.01697753; - } else { - result[0] += -0.0008568109; - } - } else { - result[0] += -0.0515616; - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 106))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 86))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { - result[0] += -0.036433112; - } else { - result[0] += -0.011925978; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 56))) { - result[0] += 0.03285799; - } else { - result[0] += 0.0010874722; - } - } - } else { - result[0] += -0.060846817; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 92))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 34))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += -0.012450369; - } else { - result[0] += -0.04985917; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 24))) { - result[0] += 0.0078088613; - } else { - result[0] += -0.00958295; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 168))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 24))) { - result[0] += -0.043195866; - } else { - result[0] += -0.014012662; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 26))) { - result[0] += -0.019311419; - } else { - result[0] += 0.007864608; - } - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 152))) { - result[0] += -0.012414033; - } else { - result[0] += 0.029668832; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - result[0] += -0.0030567853; - } else { - result[0] += 0.01868884; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 254))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { - result[0] += 0.026466904; - } else { - result[0] += -0.00078509795; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 256))) { - result[0] += -0.017463585; - } else { - result[0] += -0.004959351; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { - result[0] += 0.054318376; - } else { - result[0] += 0.016744314; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - result[0] += -0.008082875; - } else { - result[0] += 0.004647791; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 136))) { - result[0] += -0.019794006; - } else { - result[0] += 0.037679102; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - result[0] += 0.044813335; - } else { - result[0] += -0.0018291153; - } - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 68))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 76))) { - result[0] += -0.011341448; - } else { - result[0] += -0.060769796; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { - result[0] += -0.00012510354; - } else { - result[0] += 0.019792138; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 110))) { - result[0] += -0.005990359; - } else { - result[0] += 0.017361904; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - result[0] += 0.008660458; - } else { - result[0] += 0.0014748373; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 220))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 272))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 118))) { - result[0] += -0.0027236396; - } else { - result[0] += -0.029521126; - } - } else { - result[0] += -0; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += -0.058680933; - } else { - result[0] += 0.0022811424; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 206))) { - result[0] += -0.017125372; - } else { - result[0] += 0.008216227; - } - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { - result[0] += -0.01995611; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 30))) { - result[0] += 0.021725012; - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 220))) { - result[0] += 0.050011344; - } else { - result[0] += 0.014028007; - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 56))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 104))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 44))) { - result[0] += 0.021376295; - } else { - result[0] += 0.0036938381; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 274))) { - result[0] += -0.0331207; - } else { - result[0] += -0.005488401; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 8))) { - result[0] += 0.015083553; - } else { - result[0] += -0.016786192; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 30))) { - result[0] += -0.037689086; - } else { - result[0] += -0.019093947; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 8))) { - result[0] += 0.059979796; - } else { - result[0] += 0.0069023515; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 290))) { - result[0] += -0.024388736; - } else { - result[0] += -0; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 52))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 64))) { - result[0] += 0.030230714; - } else { - result[0] += -0.002469063; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += -0.00685675; - } else { - result[0] += -0.019919405; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 40))) { - result[0] += 0.007914934; - } else { - result[0] += -0.0072328295; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 168))) { - result[0] += -0.023456104; - } else { - result[0] += -0.007590213; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 278))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 268))) { - result[0] += -0.00084484427; - } else { - result[0] += 0.008248358; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 98))) { - result[0] += 0.008069568; - } else { - result[0] += -0.01922637; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 58))) { - result[0] += -0.0034377978; - } else { - result[0] += 0.02219229; - } - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { - result[0] += -0.0008113146; - } else { - result[0] += -0.011595509; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 30))) { - result[0] += -0.00054977357; - } else { - result[0] += 0.0060809394; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { - result[0] += -0.013630775; - } else { - result[0] += 0.029257402; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { - result[0] += 0.01473201; - } else { - result[0] += 0.004033634; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 326))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - result[0] += 0.0005843948; - } else { - result[0] += -0.015655208; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 368))) { - result[0] += -0.007192479; - } else { - result[0] += 0.006543958; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { - result[0] += 0.01452767; - } else { - result[0] += 0.0055364254; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { - result[0] += 0.026855484; - } else { - result[0] += -0.005540358; - } - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 88))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 92))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 60))) { - result[0] += 0.04485644; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { - result[0] += -0.042145204; - } else { - result[0] += 7.136203e-05; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 32))) { - result[0] += -0.005530101; - } else { - result[0] += -0.041934248; - } - } else { - result[0] += -0.07572525; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 246))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 282))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - result[0] += 0.025418038; - } else { - result[0] += -0.0084718885; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 214))) { - result[0] += -0.019256549; - } else { - result[0] += 0.00998631; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 98))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { - result[0] += -0.054605898; - } else { - result[0] += -0.011759637; - } - } else { - result[0] += -0.046184976; - } - } - } - } - } - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 176))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 36))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { - result[0] += -0.022035845; - } else { - result[0] += 0.046122007; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - result[0] += 0.0077522853; - } else { - result[0] += -0.02736899; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 128))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 120))) { - result[0] += -0.0018629817; - } else { - result[0] += 0.02747905; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += 0.02887457; - } else { - result[0] += -0.013981772; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { - result[0] += -0.01683332; - } else { - result[0] += -0.0050804014; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 52))) { - result[0] += 0.03178656; - } else { - result[0] += -0.0057087517; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 90))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 152))) { - result[0] += -0.014723192; - } else { - result[0] += -0.04805673; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 144))) { - result[0] += -0.017328734; - } else { - result[0] += -0.03817048; - } - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { - result[0] += -0.0028061648; - } else { - result[0] += 0.002789692; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - result[0] += -0.025860507; - } else { - result[0] += -0.0057830475; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 330))) { - result[0] += 0.006883178; - } else { - result[0] += -0.010597587; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { - result[0] += -0.03156317; - } else { - result[0] += -0.00012491019; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 36))) { - result[0] += -0.049023475; - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { - result[0] += -0.015445833; - } else { - result[0] += -0.038803425; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 164))) { - result[0] += -0.0042772954; - } else { - result[0] += -0.02979202; - } - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 4))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 18))) { - result[0] += -0.105805; - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 156))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 218))) { - result[0] += -0.013860464; - } else { - result[0] += -0.047610518; - } - } else { - result[0] += 0.015889838; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 150))) { - result[0] += 0.0039272998; - } else { - result[0] += 0.04100207; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { - result[0] += 0.066427186; - } else { - result[0] += 0.019468648; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - result[0] += -0.013523097; - } else { - result[0] += -0.035645615; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 302))) { - result[0] += -0.017095027; - } else { - result[0] += 0.008768201; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 84))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 88))) { - result[0] += -0.0047009015; - } else { - result[0] += 0.0095943585; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { - result[0] += -0.024147784; - } else { - result[0] += -0.007935442; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 100))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 360))) { - result[0] += -0.025299666; - } else { - result[0] += -0.015776524; - } - } else { - result[0] += -0.00817149; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { - result[0] += -0.013373877; - } else { - result[0] += 0.06051935; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 112))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 54))) { - result[0] += 0.013844582; - } else { - result[0] += -0.018000482; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 116))) { - result[0] += 0.025009016; - } else { - result[0] += 0.0011969743; - } - } - } - } - } - } - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 6))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 46))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 96))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { - result[0] += -0.005377132; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { - result[0] += 0.033936527; - } else { - result[0] += 0.06770065; - } - } else { - result[0] += -0; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 34))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 28))) { - result[0] += -0.016085489; - } else { - result[0] += 0.012783072; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 26))) { - result[0] += -0.038858794; - } else { - result[0] += -0.004968185; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 36))) { - result[0] += 0.04801505; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { - result[0] += 0.01772366; - } else { - result[0] += -0.026647871; - } - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 84))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - result[0] += -0.02566973; - } else { - result[0] += 0.028348282; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 120))) { - result[0] += -0.043836653; - } else { - result[0] += -0.012338279; - } - } - } else { - result[0] += -0.10324136; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 36))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 2))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 12))) { - result[0] += -0.033641063; - } else { - result[0] += 0.00022004887; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 116))) { - result[0] += -0.044643078; - } else { - result[0] += -0.008762143; - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { - result[0] += 0.0021203114; - } else { - result[0] += -0.017186115; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 88))) { - result[0] += -0.019821104; - } else { - result[0] += 0.0021318241; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 184))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 222))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { - result[0] += 0.0009353072; - } else { - result[0] += -0.0056581837; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { - result[0] += -0.0009079633; - } else { - result[0] += 0.008418952; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 108))) { - result[0] += -0; - } else { - result[0] += -0.03857514; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { - result[0] += 0.0028833076; - } else { - result[0] += 0.030077396; - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 86))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 66))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 44))) { - result[0] += 0.0097111445; - } else { - result[0] += -0.004844829; - } - } else { - result[0] += 0.022152675; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 58))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 36))) { - result[0] += 0.012857464; - } else { - result[0] += 0.04939564; - } - } else { - result[0] += 0.0038712837; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 96))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 142))) { - result[0] += 0.022889886; - } else { - result[0] += -0.025423696; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { - result[0] += 0.0012433341; - } else { - result[0] += -0.027690545; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 42))) { - result[0] += 0.019493537; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { - result[0] += 0.04888856; - } else { - result[0] += 0.0047187754; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { - result[0] += -0.038507808; - } else { - result[0] += -0.0059399325; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 190))) { - result[0] += -0.00081904867; - } else { - result[0] += 0.023750668; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 62))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 180))) { - result[0] += 0.007010121; - } else { - result[0] += 0.00020330278; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { - result[0] += 0.022976872; - } else { - result[0] += -0.008908423; - } - } - } - } - } - } - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 16))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 20))) { - result[0] += 0.04029838; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { - result[0] += -0.0076732375; - } else { - result[0] += -0.044110678; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 140))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 138))) { - result[0] += 0.013151512; - } else { - result[0] += 0.04138301; - } - } else { - result[0] += -0.031063614; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 38))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 244))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { - result[0] += -0.01187767; - } else { - result[0] += -0.024798945; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - result[0] += -0.014814547; - } else { - result[0] += -0.0693584; - } - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { - result[0] += -0.019700082; - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 12))) { - result[0] += 0.020823775; - } else { - result[0] += -0.0007175183; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { - result[0] += -0.04782397; - } else { - result[0] += -0.00023042485; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 18))) { - result[0] += 0.011482454; - } else { - result[0] += -0.025828404; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 68))) { - result[0] += 0.07567041; - } else { - result[0] += 0.03777515; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 36))) { - result[0] += 0.019245345; - } else { - result[0] += -0.007408004; - } - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { - result[0] += -0.0023852098; - } else { - result[0] += -0.01571614; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - result[0] += -0.0008245474; - } else { - result[0] += -0.01959662; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { - result[0] += 0.01735154; - } else { - result[0] += -0.01864585; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 104))) { - result[0] += -0.0019429872; - } else { - result[0] += 0.001837825; - } - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 86))) { - result[0] += 0.013189456; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { - result[0] += 0.007143569; - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 44))) { - result[0] += 0.025064811; - } else { - result[0] += 0.0470048; - } - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 138))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 2))) { - result[0] += -0.028255967; - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 330))) { - result[0] += 0.0038780514; - } else { - result[0] += 0.027806832; - } - } else { - result[0] += -0.030956248; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 144))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 98))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { - result[0] += 0.0049754553; - } else { - result[0] += 0.07370368; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 54))) { - result[0] += 0.0024109604; - } else { - result[0] += -0.021205995; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - result[0] += 0.006072202; - } else { - result[0] += 0.021960644; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { - result[0] += -0.016412217; - } else { - result[0] += 0.02891737; - } - } - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 176))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { - result[0] += -0.013008319; - } else { - result[0] += 0.014044277; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 8))) { - result[0] += -0.04008707; - } else { - result[0] += 0.011214378; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 8))) { - result[0] += -0.021072047; - } else { - result[0] += -0.032819312; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 16))) { - result[0] += -0.014122496; - } else { - result[0] += -0.0027466726; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { - result[0] += 0.003206402; - } else { - result[0] += 0.023088232; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { - result[0] += -0.029293472; - } else { - result[0] += -0.00044284094; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 88))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { - result[0] += -0.037337154; - } else { - result[0] += -0.054280948; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - result[0] += 0.000117012045; - } else { - result[0] += -0.009735559; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { - result[0] += -0.024764068; - } else { - result[0] += 0.0052268724; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { - result[0] += 0.014035463; - } else { - result[0] += 0.042725824; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 78))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 118))) { - result[0] += -0.016555734; - } else { - result[0] += 0.0024362335; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 132))) { - result[0] += 0.013627543; - } else { - result[0] += 0.0015473928; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 168))) { - result[0] += 0.04074187; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { - result[0] += 0.029437272; - } else { - result[0] += 0.0063492656; - } - } - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 36))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { - result[0] += -0.007086392; - } else { - result[0] += -0.028627744; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { - result[0] += -0.01253838; - } else { - result[0] += 0.020658642; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { - result[0] += -0.015626702; - } else { - result[0] += -0.07547299; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - result[0] += -0.098590024; - } else { - result[0] += -0.016383274; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 156))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 262))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 238))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 148))) { - result[0] += -0.005975528; - } else { - result[0] += -0.017585494; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 134))) { - result[0] += -0.06808423; - } else { - result[0] += -0.022014553; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 292))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 128))) { - result[0] += -0.010686524; - } else { - result[0] += 0.011010774; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 284))) { - result[0] += 0.03166331; - } else { - result[0] += 0.0053174673; - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 82))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 258))) { - result[0] += -0.049204163; - } else { - result[0] += -0.0052781263; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 108))) { - result[0] += 0.007684131; - } else { - result[0] += -0.01196891; - } - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 168))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 16))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 120))) { - result[0] += -0; - } else { - result[0] += 0.025176907; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { - result[0] += -0.009614422; - } else { - result[0] += -0.025206959; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 216))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 82))) { - result[0] += -0.0041216165; - } else { - result[0] += 0.042651124; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 84))) { - result[0] += -0.038773373; - } else { - result[0] += 0.023100061; - } - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.006539114; - } else { - result[0] += -0.062450953; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - result[0] += -0.004460583; - } else { - result[0] += 0.0019909479; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - result[0] += -0.00021329732; - } else { - result[0] += 0.011201916; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { - result[0] += 0.01424145; - } else { - result[0] += 0.0014522666; - } - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 254))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 50))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - result[0] += 0.033733904; - } else { - result[0] += -0.0006381403; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 28))) { - result[0] += -0.015036389; - } else { - result[0] += 0.0023278003; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 2))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 280))) { - result[0] += -0.020510865; - } else { - result[0] += 0.02703706; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 52))) { - result[0] += 0.022050345; - } else { - result[0] += -0.0019060083; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 118))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 314))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 300))) { - result[0] += -0.019168613; - } else { - result[0] += 0.006976162; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 264))) { - result[0] += -0.015453433; - } else { - result[0] += 0.0013623396; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 60))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 206))) { - result[0] += 0.004762128; - } else { - result[0] += -0.006663674; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { - result[0] += 0.025200857; - } else { - result[0] += -0.005030225; - } - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 54))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 44))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 42))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 2))) { - result[0] += -0.012792389; - } else { - result[0] += 0.017205698; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - result[0] += -0.042184174; - } else { - result[0] += 0.004857573; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 66))) { - result[0] += -0.028872743; - } else { - result[0] += -0.006002301; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { - result[0] += -0.0013448104; - } else { - result[0] += -0.04370745; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 214))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 302))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { - result[0] += 0.0069149467; - } else { - result[0] += -0.0046409685; - } - } else { - result[0] += 0.02378996; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { - result[0] += 0.012926725; - } else { - result[0] += -0.008747402; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { - result[0] += -0.046444967; - } else { - result[0] += -0.018540045; - } - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 134))) { - result[0] += -0.09067112; - } else { - result[0] += -0.010558448; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { - result[0] += -0.023714645; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 194))) { - result[0] += -0.010494131; - } else { - result[0] += 0.01534995; - } - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 308))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 110))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - result[0] += -0.03064735; - } else { - result[0] += 0.0034591525; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { - result[0] += 0.026732335; - } else { - result[0] += 0.008400806; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { - result[0] += -0.005901861; - } else { - result[0] += 0.009023737; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - result[0] += -0.012533008; - } else { - result[0] += -0.0020645715; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { - result[0] += -0.013980149; - } else { - result[0] += 0.0019120906; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 54))) { - result[0] += -0.0057765585; - } else { - result[0] += 0.017128821; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { - result[0] += -0.018316587; - } else { - result[0] += -0.005805789; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { - result[0] += 0.03265365; - } else { - result[0] += -0.0010156564; - } - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 240))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 122))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 184))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 132))) { - result[0] += 0.014779656; - } else { - result[0] += -0.020928297; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 206))) { - result[0] += 0.07677117; - } else { - result[0] += -0.004149875; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 254))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 108))) { - result[0] += 0.0061775236; - } else { - result[0] += -0.002984956; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 192))) { - result[0] += -0.008194422; - } else { - result[0] += -0.05728069; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 192))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 280))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { - result[0] += 0.022544114; - } else { - result[0] += 0.0022025593; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { - result[0] += -0.004058608; - } else { - result[0] += 0.014417312; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 318))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 338))) { - result[0] += 0.0165982; - } else { - result[0] += 0.038578045; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { - result[0] += -0.015475737; - } else { - result[0] += 0.01142684; - } - } - } - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 20))) { - result[0] += -0.0928144; - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 180))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 46))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { - result[0] += -0.020081904; - } else { - result[0] += 0.002259192; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - result[0] += -0.07451444; - } else { - result[0] += -0.0156236235; - } - } - } else { - result[0] += -0.07355082; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 108))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 38))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { - result[0] += -0.037877556; - } else { - result[0] += 0.0013853526; - } - } else { - result[0] += -0.050581098; - } - } else { - result[0] += -0; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 178))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 176))) { - result[0] += -0.0043627243; - } else { - result[0] += -0.020921404; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { - result[0] += 0.01141603; - } else { - result[0] += -0.0036946; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 228))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 94))) { - result[0] += -0.027430529; - } else { - result[0] += 0.00075300987; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 74))) { - result[0] += -0.05395777; - } else { - result[0] += -0.013114258; - } - } - } - } - } - } - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 226))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 270))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 266))) { - result[0] += -0.0013336329; - } else { - result[0] += -0.012101962; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { - result[0] += -0.012478279; - } else { - result[0] += -0.042821463; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 108))) { - result[0] += -0.00018370214; - } else { - result[0] += 0.018782655; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { - result[0] += 0.0019190798; - } else { - result[0] += -0.0032492876; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 114))) { - result[0] += -0.041382577; - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { - result[0] += 0.019699065; - } else { - result[0] += -0.023365228; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 242))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 152))) { - result[0] += -0.014805789; - } else { - result[0] += -5.5329445e-05; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 276))) { - result[0] += 0.031025484; - } else { - result[0] += 0.00553753; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 282))) { - result[0] += 0.03033769; - } else { - result[0] += 0.053280126; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - result[0] += 0.0059688077; - } else { - result[0] += -0.027727133; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 314))) { - result[0] += 0.009310781; - } else { - result[0] += -0.008495542; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 40))) { - result[0] += 0.038052257; - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 12))) { - result[0] += 0.012323047; - } else { - result[0] += -0.023586523; - } - } - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 144))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 130))) { - result[0] += 0.0018252156; - } else { - result[0] += -0.00692114; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 312))) { - result[0] += 0.032022245; - } else { - result[0] += -0.006053284; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 338))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 132))) { - result[0] += -0.019810393; - } else { - result[0] += -0.0073889843; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 36))) { - result[0] += 0.016521052; - } else { - result[0] += 0.0061617037; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 76))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 244))) { - result[0] += 0.012791178; - } else { - result[0] += -0.020304035; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 258))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { - result[0] += 0.009074631; - } else { - result[0] += -0.0016534543; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { - result[0] += 0.027765274; - } else { - result[0] += -0.0001559796; - } - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 234))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 258))) { - result[0] += -0.009072641; - } else { - result[0] += 0.008848451; - } - } else { - result[0] += -0.012117184; - } - } - } - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 6))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 148))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { - result[0] += 0.009714896; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 18))) { - result[0] += -0.035071608; - } else { - result[0] += -0.015023454; - } - } - } else { - result[0] += -0.073392645; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 132))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 38))) { - result[0] += -0.0044015315; - } else { - result[0] += 0.012927837; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 4))) { - result[0] += -0.0023782412; - } else { - result[0] += -0.021760104; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 42))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 112))) { - result[0] += -0.0147892255; - } else { - result[0] += 0.0073062084; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { - result[0] += -0.026250172; - } else { - result[0] += 0.0071073617; - } - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 0))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 162))) { - result[0] += 0.040685426; - } else { - result[0] += 0.0120480135; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 308))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 18))) { - result[0] += -0.06254916; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 98))) { - result[0] += -0.0067300186; - } else { - result[0] += -0.0331382; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - result[0] += -0.019220524; - } else { - result[0] += 0.009100321; - } - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 206))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - result[0] += 0.009733505; - } else { - result[0] += -0.004346859; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - result[0] += 8.9312605e-05; - } else { - result[0] += -0.003453482; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 280))) { - result[0] += -0.0062282663; - } else { - result[0] += 0.0049122893; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - result[0] += -0.002204797; - } else { - result[0] += 0.0064123496; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 44))) { - result[0] += 0.06116004; - } else { - result[0] += 0.010365079; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 146))) { - result[0] += -0.018945072; - } else { - result[0] += -0.010433878; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 118))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 168))) { - result[0] += 0.01741377; - } else { - result[0] += 0.07594368; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 170))) { - result[0] += -0.0018027859; - } else { - result[0] += 0.016592477; - } - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { - result[0] += 0.043439865; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 82))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 58))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { - result[0] += -0.036944382; - } else { - result[0] += -0.012312688; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - result[0] += 0.043840874; - } else { - result[0] += -0.014361912; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - result[0] += -0.004802558; - } else { - result[0] += 0.007698638; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { - result[0] += -0.017537218; - } else { - result[0] += -0.009052196; - } - } - } - } - } - } - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 10))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 18))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 16))) { - result[0] += -0.011083994; - } else { - result[0] += -0.033235386; - } - } else { - result[0] += 0.03099644; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { - result[0] += -0.020493729; - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 102))) { - result[0] += 0.03927904; - } else { - result[0] += -0.0013705323; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 172))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 80))) { - result[0] += 0.06984255; - } else { - result[0] += 0.024543915; - } - } else { - result[0] += 0.008717417; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - result[0] += -0.034200173; - } else { - result[0] += 0.020928657; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 40))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 34))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 30))) { - result[0] += -0.018539859; - } else { - result[0] += 0.006696008; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { - result[0] += 0.0006399334; - } else { - result[0] += 0.023955312; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 46))) { - result[0] += 0.0061688256; - } else { - result[0] += 0.044047784; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 156))) { - result[0] += -0.016056351; - } else { - result[0] += 0.0054231877; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { - result[0] += -0.0025266295; - } else { - result[0] += 0.018242473; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 270))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 104))) { - result[0] += -0.017715478; - } else { - result[0] += -0.04547394; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 152))) { - result[0] += 0.013087104; - } else { - result[0] += -0.029149592; - } - } - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 264))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 188))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 22))) { - result[0] += 0.0041657714; - } else { - result[0] += -0.022834495; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { - result[0] += 0.016674465; - } else { - result[0] += 0.032325614; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 176))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 154))) { - result[0] += -0.017698428; - } else { - result[0] += 0.03289035; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { - result[0] += 0.0020410677; - } else { - result[0] += -0.020820608; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 74))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 70))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 50))) { - result[0] += -4.355009e-05; - } else { - result[0] += -0.032748487; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 78))) { - result[0] += 0.035664223; - } else { - result[0] += -0.0039311326; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - result[0] += -0.0035012837; - } else { - result[0] += 0.02525419; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - result[0] += -0.0004375557; - } else { - result[0] += -0.032447618; - } - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 68))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 284))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 76))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 202))) { - result[0] += 0.06691403; - } else { - result[0] += 0.026116902; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 228))) { - result[0] += -0.0024726556; - } else { - result[0] += 0.0035805923; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 112))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { - result[0] += 0.008209762; - } else { - result[0] += 0.046135128; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 142))) { - result[0] += -0.0095024295; - } else { - result[0] += 0.0053031766; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 156))) { - result[0] += 0.02656824; - } else { - result[0] += -0.00404437; - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 40))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 46))) { - result[0] += 0.06587588; - } else { - result[0] += 0.0105197765; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 70))) { - result[0] += 0.0041911616; - } else { - result[0] += -0.012468158; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { - result[0] += 0.009541268; - } else { - result[0] += 0.058547605; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 152))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 290))) { - result[0] += -0.021359572; - } else { - result[0] += -0.004128001; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { - result[0] += 0.011844903; - } else { - result[0] += 0.023288697; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 316))) { - result[0] += -7.127906e-05; - } else { - result[0] += 0.0055441954; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 44))) { - result[0] += -0.033032846; - } else { - result[0] += 0.008700618; - } - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 28))) { - result[0] += -0.020282162; - } else { - result[0] += -0; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 252))) { - result[0] += 0.0372028; - } else { - result[0] += 0.020493207; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 114))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 192))) { - result[0] += 0.0023391566; - } else { - result[0] += -0.012876572; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { - result[0] += 0.028000802; - } else { - result[0] += 0.008097276; - } - } - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 12))) { - result[0] += -0.030859623; - } else { - result[0] += 0.059427258; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 296))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 12))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 158))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - result[0] += 0.023903606; - } else { - result[0] += -0.01342114; - } - } else { - result[0] += -0.03418419; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 52))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { - result[0] += -0.014690627; - } else { - result[0] += -0.046283614; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { - result[0] += -0.018795181; - } else { - result[0] += -0.005358623; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 162))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 300))) { - result[0] += -0.013268234; - } else { - result[0] += 0.011121328; - } - } else { - result[0] += -0.0279394; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 176))) { - result[0] += 0.03626206; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 326))) { - result[0] += -0.0057000555; - } else { - result[0] += 0.0147212865; - } - } - } - } - } else { - result[0] += -0.06643397; - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 48))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 126))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { - result[0] += -0.0010974932; - } else { - result[0] += 0.020823766; - } - } else { - result[0] += 0.0449019; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 34))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - result[0] += -0.008742712; - } else { - result[0] += 0.016725272; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 36))) { - result[0] += -0.026789738; - } else { - result[0] += 0.005821276; - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { - result[0] += -0.033222493; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 40))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - result[0] += -0.0058179474; - } else { - result[0] += 0.04347635; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 112))) { - result[0] += -0.015091679; - } else { - result[0] += 0.0024027491; - } - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 32))) { - result[0] += 0.0399952; - } else { - result[0] += -0.00027889368; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { - result[0] += -0.01870556; - } else { - result[0] += 0.0057035587; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { - result[0] += 0.02278183; - } else { - result[0] += -0.008421278; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { - result[0] += -0.042912465; - } else { - result[0] += -0; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 242))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 194))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 110))) { - result[0] += 0.003376493; - } else { - result[0] += 0.011122108; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { - result[0] += 0.03045251; - } else { - result[0] += 0.012394785; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { - result[0] += -0.027083784; - } else { - result[0] += 0.0126619935; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { - result[0] += 0.008825673; - } else { - result[0] += 0.044847894; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 144))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 10))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { - result[0] += 0.023729367; - } else { - result[0] += -0.016748834; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 64))) { - result[0] += -0.020931631; - } else { - result[0] += -0.05402078; - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 30))) { - result[0] += 0.020663498; - } else { - result[0] += 0.00616918; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 20))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 122))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { - result[0] += -0.017938469; - } else { - result[0] += 0.03910426; - } - } else { - result[0] += 0.0053874785; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 14))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.022196533; - } else { - result[0] += -0.00451347; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - result[0] += 0.00037458728; - } else { - result[0] += 0.01688151; - } - } - } - } - } - } - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 254))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 90))) { - result[0] += 0.027586067; - } else { - result[0] += -0.029394219; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { - result[0] += -0.008463705; - } else { - result[0] += -0.028911496; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 194))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 154))) { - result[0] += -0.0010301826; - } else { - result[0] += 0.0035315962; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { - result[0] += -0.0011929054; - } else { - result[0] += -0.00760438; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 62))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - result[0] += 0.011337014; - } else { - result[0] += 0.0015774952; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { - result[0] += -0.0043284963; - } else { - result[0] += 0.0071344683; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 256))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 206))) { - result[0] += 0.0046818857; - } else { - result[0] += 0.01623973; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 332))) { - result[0] += -0.025880137; - } else { - result[0] += 0.00024090149; - } - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 44))) { - result[0] += 0.062326457; - } else { - result[0] += -0.012353135; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 154))) { - result[0] += -0.01557938; - } else { - result[0] += -0.007663093; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 114))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 156))) { - result[0] += 0.07925903; - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 160))) { - result[0] += -0.022038413; - } else { - result[0] += 0.010596351; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { - result[0] += 0.009866733; - } else { - result[0] += 0.04229749; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 78))) { - result[0] += -0.027659763; - } else { - result[0] += -0.0035324506; - } - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 168))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 130))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 84))) { - result[0] += -0.01782277; - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 212))) { - result[0] += -0; - } else { - result[0] += -0.01930575; - } - } - } else { - result[0] += 0.0040967353; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 402))) { - result[0] += -0.021184763; - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 140))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - result[0] += -0.0055727926; - } else { - result[0] += 0.009987557; - } - } else { - result[0] += -0.016817084; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { - result[0] += -0.011004246; - } else { - result[0] += 0.0059432825; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { - result[0] += -0.0151095195; - } else { - result[0] += -0.0033481915; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 92))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { - result[0] += 0.0145778125; - } else { - result[0] += -0.0010819718; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 182))) { - result[0] += -0.01369258; - } else { - result[0] += 0.010227516; - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 238))) { - result[0] += -0.009665318; - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 180))) { - result[0] += 0.026355088; - } else { - result[0] += -0.0077078748; - } - } - } - } - } - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 230))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 268))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { - result[0] += -0.0125539; - } else { - result[0] += 0.014108579; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - result[0] += 0.021760508; - } else { - result[0] += -0.019894226; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 268))) { - result[0] += -0.029441832; - } else { - result[0] += -0.010534534; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 112))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 68))) { - result[0] += -0.031425703; - } else { - result[0] += -0.019633126; - } - } else { - result[0] += -0.01066619; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { - result[0] += -0.0006799324; - } else { - result[0] += -0.027934993; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - result[0] += -0.015189849; - } else { - result[0] += -0.0026831003; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 236))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 166))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - result[0] += -1.1786798e-05; - } else { - result[0] += 0.02334137; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 290))) { - result[0] += 0.0047730226; - } else { - result[0] += 0.01909187; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 284))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 246))) { - result[0] += 0.024219202; - } else { - result[0] += 0.04463927; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 28))) { - result[0] += -0; - } else { - result[0] += 0.057420652; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 240))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 202))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { - result[0] += 0.005736035; - } else { - result[0] += -0.018226644; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { - result[0] += -0.014074256; - } else { - result[0] += -0.00035597832; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 250))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { - result[0] += -0.00022620684; - } else { - result[0] += 0.021930484; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 212))) { - result[0] += -0.00031011814; - } else { - result[0] += 0.018323947; - } - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - result[0] += 0.029248917; - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 292))) { - result[0] += -0.014892972; - } else { - result[0] += 0.017006356; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 252))) { - result[0] += 0.0011380663; - } else { - result[0] += 0.0074514025; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { - result[0] += -0.027498512; - } else { - result[0] += -0.00029022736; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 256))) { - result[0] += 0.034170583; - } else { - result[0] += 0.022245234; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 328))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 324))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 298))) { - result[0] += -0.027805215; - } else { - result[0] += -0.017054409; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 294))) { - result[0] += -0.016550997; - } else { - result[0] += -0.0043134186; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - result[0] += -0.00029596835; - } else { - result[0] += -0.030671025; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - result[0] += 0.022249522; - } else { - result[0] += -0.014686073; - } - } - } - } - } - } - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 116))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 16))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { - result[0] += -0.014927131; - } else { - result[0] += -0.0050526834; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += 0.0031292983; - } else { - result[0] += -0.0021366577; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { - result[0] += -0.019448534; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - result[0] += 0.031444933; - } else { - result[0] += 0.010930794; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 152))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 20))) { - result[0] += -0.017589707; - } else { - result[0] += -0.00019221655; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - result[0] += -0.020101383; - } else { - result[0] += -0; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 170))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - result[0] += 0.0022330189; - } else { - result[0] += 0.012290857; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 14))) { - result[0] += -0.0006709326; - } else { - result[0] += 0.0086814575; - } - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 156))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 154))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0; - } else { - result[0] += 0.067005776; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 102))) { - result[0] += 0.07170707; - } else { - result[0] += -0.014048599; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 166))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 164))) { - result[0] += -0.030913204; - } else { - result[0] += -0.007337024; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 160))) { - result[0] += 0.065959476; - } else { - result[0] += 0.0030737682; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 176))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 112))) { - result[0] += -0.016045328; - } else { - result[0] += -0.008878782; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { - result[0] += 0.00972886; - } else { - result[0] += 0.03835055; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 130))) { - result[0] += -0.009170124; - } else { - result[0] += 0.0057587116; - } - } - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { - result[0] += -0.015629305; - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 142))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 140))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - result[0] += -0.004336093; - } else { - result[0] += 0.015490188; - } - } else { - result[0] += -0.01513085; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { - result[0] += 0.00940407; - } else { - result[0] += 0.032563876; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 178))) { - result[0] += -0.01413842; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 244))) { - result[0] += 0.0270089; - } else { - result[0] += -0.00225509; - } - } else { - result[0] += -0.008365121; - } - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 110))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 168))) { - result[0] += -0.0023444246; - } else { - result[0] += 0.007994309; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { - result[0] += 0.00061645266; - } else { - result[0] += -0.04576966; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 62))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 20))) { - result[0] += 0.01534583; - } else { - result[0] += 0.039503228; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 96))) { - result[0] += 0.01222286; - } else { - result[0] += -0.0050080977; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { - result[0] += -0.00016676041; - } else { - result[0] += 0.045995515; - } - } else { - result[0] += -0.019292504; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 270))) { - result[0] += -0.0106395595; - } else { - result[0] += 0.024322292; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 196))) { - result[0] += 0.0038009014; - } else { - result[0] += -0.006002572; - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 112))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 104))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { - result[0] += 0.00059230387; - } else { - result[0] += 0.02190473; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - result[0] += -0.0012604637; - } else { - result[0] += -0.025116524; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { - result[0] += 0.01818419; - } else { - result[0] += -0.0041689784; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 54))) { - result[0] += -0.0046932534; - } else { - result[0] += 0.012534521; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 100))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 212))) { - result[0] += -0.0053321766; - } else { - result[0] += 3.2746604e-05; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 160))) { - result[0] += 0.04412394; - } else { - result[0] += -0.019410737; - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 148))) { - result[0] += -0.015239002; - } else { - result[0] += -0.007977055; - } - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 134))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 150))) { - result[0] += 0.026527822; - } else { - result[0] += -0.015368457; - } - } else { - result[0] += -0.05488323; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 240))) { - result[0] += -0.07350321; - } else { - result[0] += -0.01900329; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 12))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 96))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { - result[0] += 0.0044366815; - } else { - result[0] += 0.05480617; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { - result[0] += -0.011877809; - } else { - result[0] += 0.01703535; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { - result[0] += -0.02123229; - } else { - result[0] += -0.07076668; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 242))) { - result[0] += -0.0073331865; - } else { - result[0] += -0.039508924; - } - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 32))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 108))) { - result[0] += 0.028880654; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 8))) { - result[0] += -0.015295302; - } else { - result[0] += 0.0052049416; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 100))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - result[0] += -0.0003331708; - } else { - result[0] += 0.025730435; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 280))) { - result[0] += -0.0062754015; - } else { - result[0] += 0.013416797; - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 82))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { - result[0] += -0; - } else { - result[0] += -0.031346504; - } - } else { - result[0] += 0.0021177256; - } - } - } - } - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { - result[0] += -0.0009686186; - } else { - result[0] += -0.027624989; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 222))) { - result[0] += 0.012920478; - } else { - result[0] += 0.03591418; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 262))) { - result[0] += -0.027306026; - } else { - result[0] += -0.010613002; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - result[0] += 0.028817087; - } else { - result[0] += -0.021536713; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 218))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 150))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 60))) { - result[0] += -0.014519043; - } else { - result[0] += -0.0013858235; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { - result[0] += 0.011594709; - } else { - result[0] += -0.0014000179; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 312))) { - result[0] += -0.005692505; - } else { - result[0] += 0.009161465; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { - result[0] += -3.463562e-05; - } else { - result[0] += 0.004579789; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 296))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 260))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 164))) { - result[0] += -0.00439987; - } else { - result[0] += -0.020940205; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += -0.062401365; - } else { - result[0] += -0.011520316; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 150))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 326))) { - result[0] += 0.016910944; - } else { - result[0] += 0.002540021; - } - } else { - result[0] += -0.010653024; - } - } - } else { - result[0] += -0.056521356; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 384))) { - result[0] += 0.03409591; - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { - result[0] += -0; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 386))) { - result[0] += -0.0031993948; - } else { - result[0] += -0.02678071; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 254))) { - result[0] += 0.031704232; - } else { - result[0] += 0.005365833; - } - } - } - } - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 6))) { - result[0] += 0.0014249064; - } else { - result[0] += -0.01947671; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 94))) { - result[0] += 0.026210293; - } else { - result[0] += 0.003069733; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - result[0] += -0.02217819; - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 40))) { - result[0] += 0.023008201; - } else { - result[0] += 0.0086527765; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 14))) { - result[0] += 0.028070768; - } else { - result[0] += -0; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 16))) { - result[0] += -0.025724366; - } else { - result[0] += -0.008844643; - } - } - } else { - result[0] += -0.047070276; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 134))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 44))) { - result[0] += 0.004072014; - } else { - result[0] += -0.0108394185; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { - result[0] += 0.03304889; - } else { - result[0] += 0.0103535205; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 114))) { - result[0] += -0.023122396; - } else { - result[0] += -0.010224661; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 178))) { - result[0] += 0.001644756; - } else { - result[0] += -0.002623936; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { - result[0] += -0.0045392714; - } else { - result[0] += -0.025003826; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 98))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 318))) { - result[0] += 0.015446856; - } else { - result[0] += -0.034815937; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 34))) { - result[0] += -0.00036249825; - } else { - result[0] += 0.004029076; - } - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 86))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 48))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { - result[0] += 0.0035753883; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 160))) { - result[0] += -0.046707038; - } else { - result[0] += -0.026563475; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 136))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 48))) { - result[0] += 0.011157422; - } else { - result[0] += 0.025159726; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 228))) { - result[0] += -0.0022385176; - } else { - result[0] += 0.009932112; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 214))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 186))) { - result[0] += -0.0044850186; - } else { - result[0] += 0.007804101; - } - } else { - result[0] += 0.023426604; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 312))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 90))) { - result[0] += -0.0033166704; - } else { - result[0] += -0.010364393; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 142))) { - result[0] += -0.013235124; - } else { - result[0] += 0.015832936; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 104))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 98))) { - result[0] += 0.022907645; - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 158))) { - result[0] += 0.00681618; - } else { - result[0] += -0.012386585; - } - } - } else { - result[0] += 0.04736806; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - result[0] += -0.0017811867; - } else { - result[0] += 0.024726752; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 300))) { - result[0] += -0.024868438; - } else { - result[0] += -0.008288312; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 300))) { - result[0] += 0.023393013; - } else { - result[0] += 0.000140571; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { - result[0] += -0.0001585513; - } else { - result[0] += 0.0125155775; - } - } - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 136))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 106))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 100))) { - result[0] += 0.00032556072; - } else { - result[0] += 0.02669864; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 144))) { - result[0] += -0.01502426; - } else { - result[0] += -0.007827666; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 108))) { - result[0] += -0.042772368; - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { - result[0] += 0.0073166452; - } else { - result[0] += -0.003959548; - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 104))) { - result[0] += 0.00850385; - } else { - result[0] += -0.0007850647; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 174))) { - result[0] += 0.022124942; - } else { - result[0] += 0.0037423302; - } - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 12))) { - result[0] += -0.03591499; - } else { - result[0] += 0.04963535; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { - result[0] += -0.014330697; - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 142))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 140))) { - result[0] += 0.00258422; - } else { - result[0] += -0.011271856; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { - result[0] += 0.008900406; - } else { - result[0] += 0.025473619; - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 226))) { - result[0] += -0.013987194; - } else { - result[0] += -0.0071587036; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 404))) { - result[0] += -0.007607888; - } else { - result[0] += 0.021506222; - } - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 164))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { - result[0] += 0.017460436; - } else { - result[0] += -0.0049960585; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 48))) { - result[0] += 0.03522423; - } else { - result[0] += 0.0048457957; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 92))) { - result[0] += -0.025125725; - } else { - result[0] += -0; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 4))) { - result[0] += -0.058159776; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 292))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { - result[0] += -0.0017305056; - } else { - result[0] += -0.01667433; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 306))) { - result[0] += -0.027741238; - } else { - result[0] += 0.009779043; - } - } - } - } - } else { - result[0] += 0.009787267; - } - } - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 342))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 136))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 218))) { - result[0] += -0.00073015277; - } else { - result[0] += 0.0008299645; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { - result[0] += -0.003797351; - } else { - result[0] += -0.026151488; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 164))) { - result[0] += -0.0062180604; - } else { - result[0] += -0.018368756; - } - } else { - result[0] += 0.03389421; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 202))) { - result[0] += -0.001612652; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 242))) { - result[0] += 0.029014328; - } else { - result[0] += 0.018497443; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 366))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 298))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 326))) { - result[0] += -0.02712582; - } else { - result[0] += -0.014455877; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 296))) { - result[0] += -0.017788123; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 306))) { - result[0] += 0.003964351; - } else { - result[0] += -0.0068141944; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 304))) { - result[0] += 0.046189606; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - result[0] += 0.024067063; - } else { - result[0] += -0; - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { - result[0] += 0.002387124; - } else { - result[0] += -0.021191007; - } - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 316))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 48))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 140))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { - result[0] += 0.017918406; - } else { - result[0] += 0.0007088012; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 90))) { - result[0] += 0.008024058; - } else { - result[0] += 0.03525953; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 50))) { - result[0] += -0.017354771; - } else { - result[0] += 0.011550955; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += 0.016495919; - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 50))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 82))) { - result[0] += 0.0069151595; - } else { - result[0] += -0.009860008; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - result[0] += -0.02526379; - } else { - result[0] += -0.010914913; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { - result[0] += 0.023363909; - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 188))) { - result[0] += -0.017829457; - } else { - result[0] += 0.0066434382; - } - } else { - result[0] += 0.031978313; - } - } else { - result[0] += -0.026702777; - } - } - } - } - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 18))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 72))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 0))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 28))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { - result[0] += 0.0016082926; - } else { - result[0] += -0.01875299; - } - } else { - result[0] += 0.0011138814; - } - } else { - result[0] += -0.05503887; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 116))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += -0.034357756; - } else { - result[0] += -0.00018943613; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 10))) { - result[0] += -0.015174734; - } else { - result[0] += 0.006123707; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 54))) { - result[0] += -0; - } else { - result[0] += -0.032295134; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { - result[0] += 0.022839814; - } else { - result[0] += -0.0064965202; - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 114))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 78))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 10))) { - result[0] += -0; - } else { - result[0] += -0.025930112; - } - } else { - result[0] += -0.040734638; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { - result[0] += 0.0056574023; - } else { - result[0] += 0.045485523; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 30))) { - result[0] += -0.014559778; - } else { - result[0] += -0.0022656727; - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 178))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 22))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { - result[0] += 0.009212374; - } else { - result[0] += -0.023791295; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 122))) { - result[0] += -0; - } else { - result[0] += -0.030348688; - } - } - } else { - result[0] += 0.011739956; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 116))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 318))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 80))) { - result[0] += 0.00070217456; - } else { - result[0] += -0.01572331; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { - result[0] += 0.0059441226; - } else { - result[0] += -0.015418323; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 86))) { - result[0] += 0.0038127878; - } else { - result[0] += -0.0063350433; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { - result[0] += 0.023485564; - } else { - result[0] += -0.0005745096; - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 28))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 396))) { - result[0] += -0.008549287; - } else { - result[0] += 0.03102727; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 76))) { - result[0] += 0.03656302; - } else { - result[0] += 0.0037815608; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { - result[0] += 0.036549788; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 80))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 202))) { - result[0] += -0.0316082; - } else { - result[0] += -0.011285119; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - result[0] += 0.044548456; - } else { - result[0] += -0.011524561; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - result[0] += -0.0036371031; - } else { - result[0] += 0.0054557663; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 250))) { - result[0] += -0.010413558; - } else { - result[0] += -0.005399991; - } - } - } - } - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 244))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 92))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 26))) { - result[0] += -0.02938572; - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 36))) { - result[0] += -0.0011291165; - } else { - result[0] += -0.023937851; - } - } else { - result[0] += 0.014182518; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 48))) { - result[0] += 0.09786916; - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 46))) { - result[0] += -0.039628785; - } else { - result[0] += -0; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 174))) { - result[0] += 0.008892446; - } else { - result[0] += -0.002574872; - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { - result[0] += 0.0067228116; - } else { - result[0] += 0.039215095; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 50))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - result[0] += -0.007054515; - } else { - result[0] += 0.035221007; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 274))) { - result[0] += -0.030953402; - } else { - result[0] += -0.008404141; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 146))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 106))) { - result[0] += -0.011643419; - } else { - result[0] += -0.05229321; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { - result[0] += -0.016430408; - } else { - result[0] += -0.039948575; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { - result[0] += -0.015282759; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 80))) { - result[0] += 0.007892172; - } else { - result[0] += 0.078224584; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 148))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { - result[0] += -0.0037962466; - } else { - result[0] += -0.015912866; - } - } else { - result[0] += 0.013302639; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 340))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { - result[0] += -0.00012223067; - } else { - result[0] += -0.012624851; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 256))) { - result[0] += 0.027083063; - } else { - result[0] += 0.015543193; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 20))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { - result[0] += -0.020818194; - } else { - result[0] += -0.006904413; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 320))) { - result[0] += 0.049202304; - } else { - result[0] += 0.009114965; - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 34))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 82))) { - result[0] += 0.003950732; - } else { - result[0] += -0.011314066; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - result[0] += 0.0068105734; - } else { - result[0] += 0.028154967; - } - } - } else { - result[0] += -0.021689637; - } - } - } - } - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 296))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { - result[0] += -0.004839664; - } else { - result[0] += 0.00013240986; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { - result[0] += 0.012408934; - } else { - result[0] += -0.00019027379; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 318))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 260))) { - result[0] += -0.0021018793; - } else { - result[0] += 0.013699087; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 262))) { - result[0] += -0.046322387; - } else { - result[0] += -0; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 260))) { - result[0] += 0.02145579; - } else { - result[0] += 0.010288753; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 358))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 286))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 112))) { - result[0] += -0.026122052; - } else { - result[0] += -0.014107632; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 12))) { - result[0] += -0.004089996; - } else { - result[0] += -0.017688856; - } - } - } else { - result[0] += 0.021606075; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 12))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 326))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 200))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 166))) { - result[0] += -0.00194148; - } else { - result[0] += -0.012741702; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 210))) { - result[0] += 0.036462914; - } else { - result[0] += -0.008751659; - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 102))) { - result[0] += -0.0035663412; - } else { - result[0] += -0.02093569; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 0))) { - result[0] += 0.014931956; - } else { - result[0] += 0.038147915; - } - } else { - result[0] += 0.0034323465; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 14))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { - result[0] += -0; - } else { - result[0] += 0.02190816; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 54))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - result[0] += 0.0031788598; - } else { - result[0] += -0.017724153; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 46))) { - result[0] += 0.00170696; - } else { - result[0] += -0.021163156; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 186))) { - result[0] += -0.015714565; - } else { - result[0] += 0.055118978; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { - result[0] += 0.0018268081; - } else { - result[0] += 0.012591888; - } - } - } - } - } - } - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 258))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 144))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += -0.005291102; - } else { - result[0] += -0.029125659; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { - result[0] += 0.003732911; - } else { - result[0] += -0.018547367; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 240))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 152))) { - result[0] += 0.0017839748; - } else { - result[0] += -0.008801985; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 118))) { - result[0] += 0.0042691375; - } else { - result[0] += 0.03554597; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 208))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 224))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 284))) { - result[0] += -0.003548905; - } else { - result[0] += -0.012224059; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 226))) { - result[0] += 0.0020274771; - } else { - result[0] += 0.019364875; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { - result[0] += 0.016102543; - } else { - result[0] += -0.013677455; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 248))) { - result[0] += -0.05339232; - } else { - result[0] += -0.00876738; - } - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 106))) { - result[0] += -0.013133674; - } else { - result[0] += -0.0521831; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { - result[0] += 0.008359418; - } else { - result[0] += 0.038568888; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 102))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 60))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 122))) { - result[0] += 0.0013149813; - } else { - result[0] += -0.0191629; - } - } else { - result[0] += -0.030204315; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 54))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 78))) { - result[0] += 0.024884596; - } else { - result[0] += 0.0094683515; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 50))) { - result[0] += 0.0068674767; - } else { - result[0] += -0.007323086; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 94))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 104))) { - result[0] += -0.0076974086; - } else { - result[0] += -0.029438093; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 110))) { - result[0] += 0.03408525; - } else { - result[0] += -0.006439855; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 62))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 76))) { - result[0] += 0.0068958416; - } else { - result[0] += 0.04007944; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { - result[0] += 0.00047964975; - } else { - result[0] += 0.0132646635; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 88))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 86))) { - result[0] += 0.0022136592; - } else { - result[0] += 0.026103059; - } - } else { - result[0] += -0.0099972095; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 120))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { - result[0] += -0.011941642; - } else { - result[0] += 0.0021292144; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 20))) { - result[0] += -0.014601464; - } else { - result[0] += -0.0005000638; - } - } - } - } - } - } - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 224))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 246))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { - result[0] += 0.000114969465; - } else { - result[0] += -0.0023860415; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { - result[0] += -0.035199355; - } else { - result[0] += -0.017500581; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 314))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - result[0] += 0.00627641; - } else { - result[0] += -0.0026602177; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { - result[0] += -0.008631534; - } else { - result[0] += 0.008578693; - } - } - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 30))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 216))) { - result[0] += 0.0028546187; - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 14))) { - result[0] += 0.014946878; - } else { - result[0] += 0.041622277; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 250))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - result[0] += 0.030365026; - } else { - result[0] += 0.005026304; - } - } else { - result[0] += -0.030540321; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 130))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - result[0] += -0.034515306; - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 150))) { - result[0] += 0.04119061; - } else { - result[0] += 0.018316824; - } - } else { - result[0] += -0.017891763; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 54))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { - result[0] += -0.028790927; - } else { - result[0] += -0.0069620805; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += 0.04099756; - } else { - result[0] += -0.003357871; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 260))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 118))) { - result[0] += -0.0026708224; - } else { - result[0] += -0.011060624; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - result[0] += 0.0011356926; - } else { - result[0] += 0.041363157; - } - } - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 190))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { - result[0] += 0.024280345; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 320))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 0))) { - result[0] += 0.010236905; - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 182))) { - result[0] += -0.02040131; - } else { - result[0] += -0.008407633; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 326))) { - result[0] += 0.012552517; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - result[0] += -0.021955565; - } else { - result[0] += -0.0035318558; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 120))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 302))) { - result[0] += 0.009495072; - } else { - result[0] += 0.00013142404; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { - result[0] += -0.006438227; - } else { - result[0] += -0.030158589; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 306))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 276))) { - result[0] += -0; - } else { - result[0] += 0.018983908; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { - result[0] += -0.044639092; - } else { - result[0] += 0.00017055031; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 346))) { - result[0] += -0.008808951; - } else { - result[0] += -0.032180067; - } - } else { - result[0] += 0.008250392; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { - result[0] += 0.056063425; - } else { - result[0] += -0.0065511647; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { - result[0] += -0.0092850765; - } else { - result[0] += 0.0021918796; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 30))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - result[0] += 0.007402791; - } else { - result[0] += -0.011684748; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += -0.006889455; - } else { - result[0] += 0.01800547; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 36))) { - result[0] += -0.024754832; - } else { - result[0] += -0.0028222399; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 30))) { - result[0] += -0.020031106; - } else { - result[0] += 0.0067768777; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 52))) { - result[0] += 0.041279774; - } else { - result[0] += 0.012748748; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 144))) { - result[0] += -0; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { - result[0] += 0.015250462; - } else { - result[0] += 0.026752282; - } - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 216))) { - result[0] += -0.02535589; - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 156))) { - result[0] += 0.006471269; - } else { - result[0] += 0.029563783; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 170))) { - result[0] += -0.025211362; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 186))) { - result[0] += -0.023914715; - } else { - result[0] += -0.009117383; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 154))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - result[0] += 0.0012569004; - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 118))) { - result[0] += -0.008115943; - } else { - result[0] += -0.026224598; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 340))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 196))) { - result[0] += 0.042526796; - } else { - result[0] += 0.022222739; - } - } else { - result[0] += 0.012392859; - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 12))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { - result[0] += -0.028314574; - } else { - result[0] += -0.002670089; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { - result[0] += 0.0057236534; - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 120))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 50))) { - result[0] += -0.017261336; - } else { - result[0] += -0.007837756; - } - } else { - result[0] += -0.0055499817; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 216))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 78))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 56))) { - result[0] += -0.0009610457; - } else { - result[0] += 0.0076379874; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 16))) { - result[0] += 0.05212106; - } else { - result[0] += 0.02506253; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - result[0] += -0.0006034739; - } else { - result[0] += 0.021560185; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { - result[0] += -0.034164276; - } else { - result[0] += -0.004662303; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 78))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { - result[0] += -0.001952683; - } else { - result[0] += 0.037713937; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 152))) { - result[0] += -0.008823862; - } else { - result[0] += -0.0014422481; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 76))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 242))) { - result[0] += 0.014311755; - } else { - result[0] += -0.018534161; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { - result[0] += 0.0027180712; - } else { - result[0] += -0.0037262347; - } - } - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { - result[0] += -0.00358946; - } else { - result[0] += 0.00086048665; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 298))) { - result[0] += -0.020779364; - } else { - result[0] += -0; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 184))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 34))) { - result[0] += -0.039312568; - } else { - result[0] += -0.005182448; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 82))) { - result[0] += 0.0015322726; - } else { - result[0] += -0.0016843865; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 312))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { - result[0] += -0.006095136; - } else { - result[0] += 0.028541317; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 188))) { - result[0] += 0.015441231; - } else { - result[0] += -0.01365337; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { - result[0] += 0.028764328; - } else { - result[0] += -0; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - result[0] += 0.01359984; - } else { - result[0] += -0.0053497027; - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 118))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 216))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 254))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 38))) { - result[0] += 0.0073710456; - } else { - result[0] += 0.00038945695; - } - } else { - result[0] += -0.00408677; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { - result[0] += -0.016966945; - } else { - result[0] += -0; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 130))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 106))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 70))) { - result[0] += 0.0059221475; - } else { - result[0] += 0.018299853; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { - result[0] += 0.005721331; - } else { - result[0] += -0.010450677; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 214))) { - result[0] += 0.03640103; - } else { - result[0] += 0.01191198; - } - } - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 96))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 88))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 70))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { - result[0] += -0.036111545; - } else { - result[0] += -0; - } - } else { - result[0] += 0.013476851; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 74))) { - result[0] += -0.02820078; - } else { - result[0] += -0.08100428; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { - result[0] += 0.001866552; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { - result[0] += -0.021744216; - } else { - result[0] += -0; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 48))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 42))) { - result[0] += -0.004817111; - } else { - result[0] += -0.019636374; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 70))) { - result[0] += 0.017743232; - } else { - result[0] += -0.010167501; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 66))) { - result[0] += -0.05105884; - } else { - result[0] += -0.011944066; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 102))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 68))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 24))) { - result[0] += -0; - } else { - result[0] += 0.02911661; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 66))) { - result[0] += -0.010146369; - } else { - result[0] += 0.009323521; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 2))) { - result[0] += -0.03494624; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { - result[0] += -0.003336961; - } else { - result[0] += -0.02113694; - } - } - } - } - } - } - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 166))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 114))) { - result[0] += -0.0013070774; - } else { - result[0] += 0.0005617281; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { - result[0] += -0.00045852616; - } else { - result[0] += 0.021694412; - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 190))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { - result[0] += -0.009019866; - } else { - result[0] += 0.0010552766; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 382))) { - result[0] += 0.042176425; - } else { - result[0] += -0.019407976; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 94))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 22))) { - result[0] += -0.008323395; - } else { - result[0] += -0.0172386; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 96))) { - result[0] += 0.0036288549; - } else { - result[0] += -0.013044089; - } - } - } else { - result[0] += 0.00021139935; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 158))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 74))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 78))) { - result[0] += -0.00079369405; - } else { - result[0] += 0.04720639; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 390))) { - result[0] += 0.05325351; - } else { - result[0] += -0.043466292; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 66))) { - result[0] += -0.032283835; - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 198))) { - result[0] += -0.0008110626; - } else { - result[0] += 0.050249096; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 68))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 158))) { - result[0] += -0.010387595; - } else { - result[0] += -0.0034609255; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 110))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 394))) { - result[0] += 0.0030221925; - } else { - result[0] += 0.028211419; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 114))) { - result[0] += -0.0093119275; - } else { - result[0] += 0.0069152773; - } - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 164))) { - result[0] += -0.009060017; - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 126))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 46))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { - result[0] += -0.006120693; - } else { - result[0] += 0.009823619; - } - } else { - result[0] += -0.009353681; - } - } else { - result[0] += -0.0127736945; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { - result[0] += 0.013670566; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 86))) { - result[0] += -0.004946747; - } else { - result[0] += 0.0048705908; - } - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 136))) { - result[0] += -0.010391137; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 92))) { - result[0] += 0.004601255; - } else { - result[0] += -0.0049207225; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 22))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { - result[0] += -0.018509652; - } else { - result[0] += -0.0028016684; - } - } else { - result[0] += 0.012803587; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 14))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.013896561; - } else { - result[0] += -0.009757071; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 2))) { - result[0] += 0.035226237; - } else { - result[0] += 0.003883094; - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 108))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 72))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { - result[0] += 0.009042918; - } else { - result[0] += 0.03158817; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 138))) { - result[0] += 0.007958132; - } else { - result[0] += 0.03468901; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 146))) { - result[0] += -0.0020058847; - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 174))) { - result[0] += 0.026864871; - } else { - result[0] += 0.014916946; - } - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 216))) { - result[0] += -0.022077216; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { - result[0] += 0.007497831; - } else { - result[0] += 0.028885541; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 250))) { - result[0] += -0.02360869; - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 174))) { - result[0] += -0.018704783; - } else { - result[0] += -0.0076525756; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - result[0] += 9.27179e-05; - } else { - result[0] += 0.031941853; - } - } else { - result[0] += 0.009628982; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 350))) { - result[0] += -0.021527562; - } else { - result[0] += -0.0072527933; - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 74))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 104))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 70))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 60))) { - result[0] += 0.02288001; - } else { - result[0] += -0.016261838; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 146))) { - result[0] += -0.03769898; - } else { - result[0] += -0.018141152; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 46))) { - result[0] += -0.0023420886; - } else { - result[0] += 0.021080358; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 140))) { - result[0] += -0.012038226; - } else { - result[0] += 0.008399843; - } - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 72))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { - result[0] += 0.002315519; - } else { - result[0] += 0.062828414; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { - result[0] += 0.0130506335; - } else { - result[0] += -0.007596428; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 40))) { - result[0] += 0.007198368; - } else { - result[0] += -0.0058715134; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - result[0] += 0.02399823; - } else { - result[0] += -0.008211115; - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 84))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 26))) { - result[0] += 0.02461793; - } else { - result[0] += 0.008668258; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 28))) { - result[0] += -0.030365562; - } else { - result[0] += 0.010669919; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 94))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 16))) { - result[0] += -0.018534234; - } else { - result[0] += -0.0031345394; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - result[0] += -0.010914135; - } else { - result[0] += -0.00012393964; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 214))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { - result[0] += 0.022872802; - } else { - result[0] += 0.0050913426; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 132))) { - result[0] += 0.0009941938; - } else { - result[0] += -0.0025577117; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += 0.012239212; - } else { - result[0] += 0.032537233; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 74))) { - result[0] += -0.012231956; - } else { - result[0] += 0.00096511666; - } - } - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 244))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 50))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 100))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 190))) { - result[0] += -0.015980741; - } else { - result[0] += 0.013686332; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 106))) { - result[0] += 0.030151868; - } else { - result[0] += 0.008428541; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 20))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 192))) { - result[0] += 0.001345482; - } else { - result[0] += 0.02078061; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 126))) { - result[0] += -0.029191677; - } else { - result[0] += 0.00064531795; - } - } - } - } else { - result[0] += 0.035184488; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 30))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 212))) { - result[0] += 0.009157064; - } else { - result[0] += -0.03418844; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - result[0] += -0.01716392; - } else { - result[0] += -0.045232523; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { - result[0] += -0.01179669; - } else { - result[0] += 0.06294518; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 140))) { - result[0] += -0.011332896; - } else { - result[0] += 0.01226867; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { - result[0] += 0.0011395406; - } else { - result[0] += -0.0025050612; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 138))) { - result[0] += -0.037857305; - } else { - result[0] += -0.00218284; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 226))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 102))) { - result[0] += -0.0008166955; - } else { - result[0] += 0.004205488; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.0056326287; - } else { - result[0] += 0.0007912867; - } - } - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { - result[0] += 0.023435187; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 40))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 42))) { - result[0] += 0.03141637; - } else { - result[0] += -0.0015305742; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 8))) { - result[0] += -0.017991172; - } else { - result[0] += 0.002195324; - } - } else { - result[0] += 0.013269196; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 44))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 120))) { - result[0] += -0.01257918; - } else { - result[0] += 0.0010349855; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { - result[0] += -0.051812388; - } else { - result[0] += -0.010208193; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 30))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 278))) { - result[0] += 0.0073652193; - } else { - result[0] += -0.013559197; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 56))) { - result[0] += -0.02485094; - } else { - result[0] += -0.0037162665; - } - } - } - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 12))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 42))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 2))) { - result[0] += -0.034541044; - } else { - result[0] += 0.0027651105; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { - result[0] += -0.019249316; - } else { - result[0] += -0.0034680355; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 8))) { - result[0] += -0.02443984; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += 0.013286717; - } else { - result[0] += -0.0025256465; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 46))) { - result[0] += 0.0061984803; - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 48))) { - result[0] += 0.079341814; - } else { - result[0] += 0.025329694; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 6))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 126))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { - result[0] += 0.021308701; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 118))) { - result[0] += -0.006669204; - } else { - result[0] += -0.019334236; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 140))) { - result[0] += 0.04223214; - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 74))) { - result[0] += -0.01822163; - } else { - result[0] += 0.011897653; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 12))) { - result[0] += -0.03937181; - } else { - result[0] += -0.021595992; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { - result[0] += 0.008440125; - } else { - result[0] += -0.018780667; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 32))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { - result[0] += -0.0025710927; - } else { - result[0] += -0.015579129; - } - } else { - result[0] += 0.013569686; - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 118))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 10))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 102))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 90))) { - result[0] += -0.008451057; - } else { - result[0] += 0.010793014; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 244))) { - result[0] += -0.007276828; - } else { - result[0] += -0.00065764156; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { - result[0] += 0.00065685576; - } else { - result[0] += 0.025356445; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 142))) { - result[0] += -0.012041635; - } else { - result[0] += -0.05446145; - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 84))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { - result[0] += 0.044229705; - } else { - result[0] += 0.0006534785; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 30))) { - result[0] += 0.005015543; - } else { - result[0] += -0.01020183; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += 0.06759548; - } else { - result[0] += 0.007752572; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { - result[0] += 0.011832262; - } else { - result[0] += 0.0015743548; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 208))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 196))) { - result[0] += -0.0024539565; - } else { - result[0] += 0.001751725; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { - result[0] += -0; - } else { - result[0] += 0.021780172; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 272))) { - result[0] += 0.008948053; - } else { - result[0] += -0.0063331993; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { - result[0] += -0.0073422543; - } else { - result[0] += -0.026588783; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 210))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 104))) { - result[0] += 0.0029568898; - } else { - result[0] += -0.042533282; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { - result[0] += -0.0074031386; - } else { - result[0] += 0.014356777; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - result[0] += 0.00092436554; - } else { - result[0] += 0.024809679; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 326))) { - result[0] += -0.00329214; - } else { - result[0] += 0.00526762; - } - } - } - } - } - } - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 10))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 200))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 72))) { - result[0] += 0.0010467954; - } else { - result[0] += 0.030300766; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 62))) { - result[0] += 0.02035891; - } else { - result[0] += -0.010212629; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { - result[0] += -0.001989516; - } else { - result[0] += -0.013169396; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 346))) { - result[0] += 0.020909293; - } else { - result[0] += -0.0079391515; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 306))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 262))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 116))) { - result[0] += -0.0052688015; - } else { - result[0] += 0.0019646974; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 286))) { - result[0] += -0.031940565; - } else { - result[0] += -0; - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 80))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 322))) { - result[0] += 0.020321852; - } else { - result[0] += 0.034146465; - } - } else { - result[0] += -0.03401268; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { - result[0] += 0.0016832197; - } else { - result[0] += 0.027038325; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { - result[0] += -0.0071495273; - } else { - result[0] += -0.0014786319; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 258))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { - result[0] += 0.01780109; - } else { - result[0] += -0.01263422; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 228))) { - result[0] += 0.039167188; - } else { - result[0] += 0.018858656; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 26))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { - result[0] += -0.0019396268; - } else { - result[0] += 0.005724409; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 6))) { - result[0] += 0.027276058; - } else { - result[0] += 0.008047543; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { - result[0] += -0.012420956; - } else { - result[0] += -0.0021616125; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - result[0] += -0.0018222294; - } else { - result[0] += 0.0021800934; - } - } - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 150))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 92))) { - result[0] += -0.006505067; - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 220))) { - result[0] += 0.040462743; - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 46))) { - result[0] += 0.015701324; - } else { - result[0] += -0.0201608; - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 116))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 52))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 94))) { - result[0] += 0.0005646117; - } else { - result[0] += 0.010826596; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += -0.013457968; - } else { - result[0] += -1.2906838e-05; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += -0.0008355248; - } else { - result[0] += -0.0226526; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 92))) { - result[0] += 0.020389868; - } else { - result[0] += 0.0054127253; - } - } - } - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 14))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 76))) { - result[0] += -0.032427344; - } else { - result[0] += -0; - } - } else { - result[0] += 0.04492506; - } - } - } - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 40))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 8))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { - result[0] += -0.008939014; - } else { - result[0] += -0.031956512; - } - } else { - result[0] += 0.017092446; - } - } else { - result[0] += 0.022510538; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 4))) { - result[0] += 0.042899013; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - result[0] += -0.0038211767; - } else { - result[0] += 0.029483128; - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 72))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 160))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 82))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { - result[0] += -0.007246212; - } else { - result[0] += 0.0062661204; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 88))) { - result[0] += -0.02025948; - } else { - result[0] += -0.005702542; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 116))) { - result[0] += -0.030373631; - } else { - result[0] += -0.004761403; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 90))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { - result[0] += 0.010341472; - } else { - result[0] += 0.0385042; - } - } else { - result[0] += -0.0025437989; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 204))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += -0.0027436535; - } else { - result[0] += -0.020981843; - } - } else { - result[0] += 0.011424384; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { - result[0] += -0.026424287; - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 22))) { - result[0] += 0.015828108; - } else { - result[0] += -0.004946768; - } - } - } else { - result[0] += 0.036206927; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 0))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 46))) { - result[0] += -0.0018861439; - } else { - result[0] += 0.019979624; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 16))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 64))) { - result[0] += -0.012420944; - } else { - result[0] += -0.04097703; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 46))) { - result[0] += -0.014519716; - } else { - result[0] += 0.0064661787; - } - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 116))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { - result[0] += -0.0007507774; - } else { - result[0] += 0.011509613; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 74))) { - result[0] += 0.0015937341; - } else { - result[0] += 0.026918387; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 268))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 76))) { - result[0] += -0.001206148; - } else { - result[0] += -0.01964714; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 98))) { - result[0] += 0.008147176; - } else { - result[0] += -0.0026112716; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { - result[0] += -0.00044672118; - } else { - result[0] += -0.009276496; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 156))) { - result[0] += 0.039460566; - } else { - result[0] += -0.0033604186; - } - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 148))) { - result[0] += -0.0112617565; - } else { - result[0] += -0.0053185816; - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 32))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 16))) { - result[0] += -0.027565295; - } else { - result[0] += -0.004120046; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - result[0] += -0.0030114742; - } else { - result[0] += 0.010990894; - } - } - } else { - result[0] += -0.018377742; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - result[0] += 0.0032660365; - } else { - result[0] += 0.014772459; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 240))) { - result[0] += 0.01916596; - } else { - result[0] += 0.043604817; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 188))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { - result[0] += 0.020365; - } else { - result[0] += -0.004660358; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 182))) { - result[0] += -0.026131554; - } else { - result[0] += -0.012585315; - } - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 208))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 144))) { - result[0] += -0.009168918; - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 186))) { - result[0] += -0.017280573; - } else { - result[0] += 0.013135247; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 294))) { - result[0] += 0.027347783; - } else { - result[0] += 0.0058005466; - } - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 228))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 262))) { - result[0] += -0.01863461; - } else { - result[0] += -0.009977842; - } - } else { - result[0] += 0.0074623013; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 18))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 82))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 10))) { - result[0] += -0.027271813; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - result[0] += 0.03428016; - } else { - result[0] += 0.005898777; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 124))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 120))) { - result[0] += -0.0031157867; - } else { - result[0] += 0.012381009; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 40))) { - result[0] += -0.032176975; - } else { - result[0] += -0.0047460934; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 128))) { - result[0] += -0.011950021; - } else { - result[0] += -0.029780094; - } - } else { - result[0] += -0.038288817; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 14))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 58))) { - result[0] += -0.013251813; - } else { - result[0] += 0.009832563; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 40))) { - result[0] += -0.0028266981; - } else { - result[0] += 0.010589391; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 26))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 0))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 144))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 8))) { - result[0] += -0; - } else { - result[0] += 0.02299802; - } - } else { - result[0] += -0.0005336401; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 16))) { - result[0] += 0.024464216; - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 24))) { - result[0] += -0.02506329; - } else { - result[0] += -0.00617628; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 146))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 228))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 168))) { - result[0] += 0.000546928; - } else { - result[0] += -0.0015107197; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 244))) { - result[0] += 0.0052600866; - } else { - result[0] += 0.0005289404; - } - } - } else { - result[0] += -0.0067100944; - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 92))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 56))) { - result[0] += 0.000776489; - } else { - result[0] += -0.00053459004; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.0025825037; - } else { - result[0] += 0.033680957; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 94))) { - result[0] += -0.010514084; - } else { - result[0] += 0.00012713655; - } - } else { - result[0] += -8.464566e-05; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 46))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 192))) { - result[0] += -0.008038435; - } else { - result[0] += 0.03207838; - } - } else { - result[0] += -0.0059311604; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 196))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 104))) { - result[0] += -0.0027816568; - } else { - result[0] += 0.008232006; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - result[0] += 0.03486943; - } else { - result[0] += -0.009670003; - } - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 78))) { - result[0] += -0.021940826; - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 160))) { - result[0] += -0.007266288; - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { - result[0] += -0.004397231; - } else { - result[0] += 0.0011502573; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { - result[0] += -0.005171144; - } else { - result[0] += 0.009149543; - } - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 132))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 68))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += 0.033398908; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 146))) { - result[0] += -0.011877987; - } else { - result[0] += 0.009033947; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 32))) { - result[0] += 0.017914457; - } else { - result[0] += 0.00086382544; - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 114))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 106))) { - result[0] += -0; - } else { - result[0] += -0.025332725; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 136))) { - result[0] += 0.010888358; - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 132))) { - result[0] += -0.002672884; - } else { - result[0] += -0.0126517145; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { - result[0] += -0.01176235; - } else { - result[0] += -0.04480323; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 132))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 146))) { - result[0] += 0.022533778; - } else { - result[0] += 0.0015053398; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - result[0] += -0.029711738; - } else { - result[0] += -0.00018641823; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 194))) { - result[0] += -0.0029512055; - } else { - result[0] += -0.014446958; - } - } else { - result[0] += -0.04525297; - } - } - } - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 116))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 126))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 108))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 104))) { - result[0] += 0.0049565816; - } else { - result[0] += 0.063526; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 114))) { - result[0] += -0.019255767; - } else { - result[0] += 0.0012636579; - } - } - } else { - result[0] += 0.008695122; - } - } else { - result[0] += 0.019830158; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 190))) { - result[0] += -0.007866302; - } else { - result[0] += 0.012916532; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 30))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - result[0] += 0.06366762; - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 98))) { - result[0] += -0.004975738; - } else { - result[0] += -0.026275873; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 238))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 148))) { - result[0] += -0.009829768; - } else { - result[0] += -0.028741485; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { - result[0] += -0.0046568112; - } else { - result[0] += -0.028392253; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - result[0] += -0.01022596; - } else { - result[0] += 0.057439942; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 142))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 10))) { - result[0] += -0.005370389; - } else { - result[0] += -0.019819807; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 70))) { - result[0] += 0.011075004; - } else { - result[0] += -0.0060161785; - } - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - result[0] += 0.062947415; - } else { - result[0] += 0.0032754538; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 146))) { - result[0] += -0.0023154307; - } else { - result[0] += 0.013836692; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { - result[0] += -0.0051080496; - } else { - result[0] += -0.026022715; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 130))) { - result[0] += -0.021521274; - } else { - result[0] += 0.0008544709; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 44))) { - result[0] += 0.011564689; - } else { - result[0] += 0.039744; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 134))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 202))) { - result[0] += 0.00052881095; - } else { - result[0] += -0.0008797978; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 56))) { - result[0] += 0.002648249; - } else { - result[0] += 0.02056101; - } - } - } - } - } - } - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 160))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 142))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 114))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 92))) { - result[0] += -0.0069403; - } else { - result[0] += 0.0036496245; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { - result[0] += -0.041129064; - } else { - result[0] += -0.007193537; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 138))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { - result[0] += -0.016804745; - } else { - result[0] += -0.035601314; - } - } else { - result[0] += 0.0045456374; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 30))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 154))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { - result[0] += 0.051934537; - } else { - result[0] += -0.0017292331; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 18))) { - result[0] += -0.018062782; - } else { - result[0] += -0.0065657706; - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 36))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 12))) { - result[0] += -0.0018053079; - } else { - result[0] += 0.008867439; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - result[0] += 0.024381882; - } else { - result[0] += 0.0078328205; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 126))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 118))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { - result[0] += -0.0050182706; - } else { - result[0] += 0.0074505755; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 48))) { - result[0] += -0.027363313; - } else { - result[0] += -0.008215737; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 238))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 132))) { - result[0] += 0.0108621; - } else { - result[0] += 0.001027461; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { - result[0] += 0.0018521305; - } else { - result[0] += -0.019366777; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 196))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 66))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 42))) { - result[0] += -0.021509835; - } else { - result[0] += -0.0051732687; - } - } else { - result[0] += -0.036974095; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 10))) { - result[0] += 0.013641315; - } else { - result[0] += -0.0012665674; - } - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 168))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 110))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 120))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 382))) { - result[0] += -0.0007951208; - } else { - result[0] += -0.012272722; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { - result[0] += 0.013259816; - } else { - result[0] += 0.00076070236; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 124))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { - result[0] += -0.023466855; - } else { - result[0] += -0.0015092399; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - result[0] += 0.0067120204; - } else { - result[0] += -0.0027905267; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 180))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 132))) { - result[0] += 0.0012523715; - } else { - result[0] += 0.019288411; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 72))) { - result[0] += -0.0024032074; - } else { - result[0] += 0.020834908; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 100))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 302))) { - result[0] += -0.0045833127; - } else { - result[0] += 0.002748456; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - result[0] += -0.00035413975; - } else { - result[0] += 0.0021194927; - } - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 352))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 196))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 36))) { - result[0] += 0.012191023; - } else { - result[0] += -0; - } - } else { - result[0] += 0.026147142; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 82))) { - result[0] += -0.0044075255; - } else { - result[0] += -0.018723859; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 368))) { - result[0] += 0.02115663; - } else { - result[0] += -0.008576654; - } - } - } - } - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 116))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 312))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 78))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 146))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 108))) { - result[0] += -0.0011369804; - } else { - result[0] += 0.007111168; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { - result[0] += -0.0066426345; - } else { - result[0] += 0.004026822; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 166))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 126))) { - result[0] += -0.016191296; - } else { - result[0] += -0.008390819; - } - } else { - result[0] += 0.039027248; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 338))) { - result[0] += 0.011132075; - } else { - result[0] += 0.00045876933; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 108))) { - result[0] += 0.03038956; - } else { - result[0] += 0.005012113; - } - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 128))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 126))) { - result[0] += -0.002966301; - } else { - result[0] += -0.012031978; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { - result[0] += 0.0043450063; - } else { - result[0] += -0.0033799019; - } - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 74))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 72))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 172))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { - result[0] += 0.00018884889; - } else { - result[0] += 0.004433022; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 140))) { - result[0] += -0.0016364238; - } else { - result[0] += 0.011106547; - } - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { - result[0] += 0.010573897; - } else { - result[0] += -0.046158385; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { - result[0] += 0.025685733; - } else { - result[0] += -0.012269548; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 298))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 258))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 328))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 132))) { - result[0] += 0.012633822; - } else { - result[0] += -0.004591215; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 68))) { - result[0] += 0.027154177; - } else { - result[0] += -0.0073612966; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 90))) { - result[0] += -0.009671869; - } else { - result[0] += 0.034116954; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 82))) { - result[0] += 0.003360726; - } else { - result[0] += -0.0010045286; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 242))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 80))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 294))) { - result[0] += -0.0067084306; - } else { - result[0] += 0.017508617; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 52))) { - result[0] += -0.025245184; - } else { - result[0] += -0.009711; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 266))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 96))) { - result[0] += 0.0024178706; - } else { - result[0] += 0.02038781; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { - result[0] += -0.005333036; - } else { - result[0] += -0.021810295; - } - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 314))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 148))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 44))) { - result[0] += 0.002272234; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 302))) { - result[0] += 0.0017241904; - } else { - result[0] += 0.014623227; - } - } - } else { - result[0] += -0.0057367506; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 140))) { - result[0] += -0.029135648; - } else { - result[0] += -0.0059048724; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 80))) { - result[0] += 0.0016456817; - } else { - result[0] += 0.036199924; - } - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 312))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - result[0] += 5.137796e-05; - } else { - result[0] += 0.016802512; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 344))) { - result[0] += -0.007183667; - } else { - result[0] += 0.0086045405; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { - result[0] += -0.0025281047; - } else { - result[0] += -0.022966359; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 250))) { - result[0] += 0.023195243; - } else { - result[0] += 0.003728645; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 186))) { - result[0] += 0.008694565; - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - result[0] += -0.012267268; - } else { - result[0] += 0.012734731; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { - result[0] += 0.023913363; - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 228))) { - result[0] += -0.0029125137; - } else { - result[0] += -0.030048529; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 226))) { - result[0] += -0; - } else { - result[0] += 0.04022684; - } - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 296))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 2))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - result[0] += 0.010619841; - } else { - result[0] += -0.015812444; - } - } else { - result[0] += 0.036461692; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 10))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 92))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 10))) { - result[0] += 0.006464646; - } else { - result[0] += -0.03794737; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { - result[0] += -0.014366518; - } else { - result[0] += 0.00065602706; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += -0.036709867; - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 26))) { - result[0] += 0.009573085; - } else { - result[0] += -0.0042258967; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { - result[0] += 0.025289237; - } else { - result[0] += 0.0031290874; - } - } - } - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 32))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 314))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 176))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 38))) { - result[0] += 0.0070433277; - } else { - result[0] += -0.0021675983; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.00069358293; - } else { - result[0] += 0.014411396; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 224))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 182))) { - result[0] += -0.021365121; - } else { - result[0] += -0.009108902; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 258))) { - result[0] += -0.002070323; - } else { - result[0] += -0.0070032175; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 58))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { - result[0] += 0.015421606; - } else { - result[0] += 0.0065408074; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 68))) { - result[0] += -0.00017713972; - } else { - result[0] += 0.0036349527; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 112))) { - result[0] += -0.015431674; - } else { - result[0] += 0.0069513856; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { - result[0] += -0.0017071629; - } else { - result[0] += 0.0010942215; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 328))) { - result[0] += -0.018242605; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 334))) { - result[0] += 0.013435415; - } else { - result[0] += 0.000363245; - } - } else { - result[0] += -0.011918341; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { - result[0] += 0.057550073; - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 64))) { - result[0] += -0.010455087; - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 292))) { - result[0] += 0.025410349; - } else { - result[0] += 0.0067503005; - } - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 48))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 250))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { - result[0] += 0.0038494274; - } else { - result[0] += -0.012583888; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { - result[0] += 0.0052445154; - } else { - result[0] += 0.022994613; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 110))) { - result[0] += 0.010131297; - } else { - result[0] += 0.030005908; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 36))) { - result[0] += -0.015509938; - } else { - result[0] += 0.033259317; - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 256))) { - result[0] += -0.018772317; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 324))) { - result[0] += -0.0010802757; - } else { - result[0] += -0.019551003; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - result[0] += 0.015505517; - } else { - result[0] += -0.014615647; - } - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 228))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 266))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - result[0] += -0.0011533442; - } else { - result[0] += 0.0037619993; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 42))) { - result[0] += -0.0050083552; - } else { - result[0] += -0.020327281; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += -0.01845751; - } else { - result[0] += -0.0027648432; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 270))) { - result[0] += 0.014990672; - } else { - result[0] += -0.0010248877; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { - result[0] += -0.00588067; - } else { - result[0] += -0.0011169165; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - result[0] += 0.0015550206; - } else { - result[0] += -0.0028203435; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 10))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { - result[0] += 0.00057445857; - } else { - result[0] += -0.020817637; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 176))) { - result[0] += 0.004375774; - } else { - result[0] += -0.00017658187; - } - } - } - } - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 116))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 92))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 40))) { - result[0] += -0.029479165; - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 36))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { - result[0] += -0.0015629175; - } else { - result[0] += 0.014696643; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { - result[0] += -0.012695557; - } else { - result[0] += 0.002633101; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 38))) { - result[0] += 0.088580444; - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 18))) { - result[0] += -0.0013535247; - } else { - result[0] += -0.041627154; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { - result[0] += 0.010081653; - } else { - result[0] += 0.0011864647; - } - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 158))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { - result[0] += 0.024709804; - } else { - result[0] += 0.006805406; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { - result[0] += -0.020788606; - } else { - result[0] += 0.010531965; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 22))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { - result[0] += 0.029835364; - } else { - result[0] += 0.072482795; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 98))) { - result[0] += -0.0033422098; - } else { - result[0] += -0.027484313; - } - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 198))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 130))) { - result[0] += -0.0117129935; - } else { - result[0] += -0.027794067; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { - result[0] += -0.0058866264; - } else { - result[0] += -0.022589795; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 96))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { - result[0] += -0.007361448; - } else { - result[0] += 0.05226938; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 152))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { - result[0] += -0.0011431018; - } else { - result[0] += -0.013976167; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 34))) { - result[0] += -0; - } else { - result[0] += 0.0123240035; - } - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 16))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 42))) { - result[0] += 0.00010176472; - } else { - result[0] += 0.0130502; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - result[0] += -0.00884224; - } else { - result[0] += -0.0399912; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 166))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 102))) { - result[0] += -0.0070857047; - } else { - result[0] += 0.0050304467; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - result[0] += -0.018932706; - } else { - result[0] += -0.0011952891; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { - result[0] += 0.03236731; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 66))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { - result[0] += 0.00022758842; - } else { - result[0] += 0.0045965556; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 40))) { - result[0] += 0.0054974416; - } else { - result[0] += -0.00038611452; - } - } - } - } - } - } - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 118))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 140))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - result[0] += -0.030644778; - } else { - result[0] += -0.0012927776; - } - } else { - result[0] += -0.027898073; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 270))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 10))) { - result[0] += -0.0026217196; - } else { - result[0] += 0.00031906084; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { - result[0] += 0.053565513; - } else { - result[0] += 0.011517114; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 242))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 206))) { - result[0] += 0.0022491028; - } else { - result[0] += 0.017666435; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 48))) { - result[0] += -0.039555483; - } else { - result[0] += -0.007261213; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 24))) { - result[0] += -0.0013352408; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { - result[0] += 0.019903926; - } else { - result[0] += -0.0018211514; - } - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 98))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 260))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { - result[0] += -0.003761566; - } else { - result[0] += 5.4693035e-05; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 44))) { - result[0] += 0.066539966; - } else { - result[0] += 0.0024916714; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 282))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - result[0] += 0.0024568306; - } else { - result[0] += 0.013490746; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 144))) { - result[0] += 0.0017763426; - } else { - result[0] += -0.0017636567; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 66))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += -0.016284099; - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { - result[0] += 0.022716116; - } else { - result[0] += 0.0030027088; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 108))) { - result[0] += -0.008329183; - } else { - result[0] += -0.02752207; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { - result[0] += 0.024196664; - } else { - result[0] += -0.0069028423; - } - } - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 268))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 266))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 150))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 50))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 6))) { - result[0] += 0.03250914; - } else { - result[0] += 0.0055702133; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { - result[0] += 0.008322871; - } else { - result[0] += -0.015406762; - } - } - } else { - result[0] += 0.0395282; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 262))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 260))) { - result[0] += -0.018292183; - } else { - result[0] += -0; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 294))) { - result[0] += -0.0053772978; - } else { - result[0] += 0.0050149076; - } - } - } else { - result[0] += 0.006939339; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 88))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 282))) { - result[0] += 0.00481271; - } else { - result[0] += -0.0038877416; - } - } else { - result[0] += -0.0105433585; - } - } - } - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 320))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 284))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 252))) { - result[0] += 8.760832e-05; - } else { - result[0] += 0.0031985168; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { - result[0] += -0.0029068196; - } else { - result[0] += 0.0005925631; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 242))) { - result[0] += 0.019893859; - } else { - result[0] += 0.009958875; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 266))) { - result[0] += -0.017648466; - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 304))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 302))) { - result[0] += -0.011696982; - } else { - result[0] += 0.004698274; - } - } else { - result[0] += -0.0020677263; - } - } - } - } else { - result[0] += 0.04254042; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 162))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 122))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 76))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 98))) { - result[0] += -0.0029215065; - } else { - result[0] += 0.011221336; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 112))) { - result[0] += 0.020535378; - } else { - result[0] += 0.0062516048; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 62))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 302))) { - result[0] += -0.0063436194; - } else { - result[0] += -0.01713322; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 162))) { - result[0] += 0.010334019; - } else { - result[0] += -0.0069331317; - } - } - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 322))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 182))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { - result[0] += -0.0077398308; - } else { - result[0] += -0.03391137; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 190))) { - result[0] += 0.018701833; - } else { - result[0] += 0.005074762; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 192))) { - result[0] += 0.05592671; - } else { - result[0] += 0.012150154; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 138))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 44))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 84))) { - result[0] += -0.004530545; - } else { - result[0] += -0.014109983; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 34))) { - result[0] += 0.016917644; - } else { - result[0] += -0.0003956896; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 168))) { - result[0] += 0.018351296; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 222))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 326))) { - result[0] += 0.009391616; - } else { - result[0] += -0.012847346; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - result[0] += 0.0028710503; - } else { - result[0] += -0.014146092; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 272))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 24))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { - result[0] += 0.004130573; - } else { - result[0] += -0.02545332; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 136))) { - result[0] += -0.011545375; - } else { - result[0] += 0.0030030604; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 124))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 76))) { - result[0] += 0.0053757923; - } else { - result[0] += 0.026795724; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += 0.0073435195; - } else { - result[0] += -0.011538415; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 84))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 14))) { - result[0] += 0.010773177; - } else { - result[0] += -0.0014024094; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 90))) { - result[0] += -0.00650954; - } else { - result[0] += 0.017750045; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 280))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 256))) { - result[0] += -0.01760956; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 184))) { - result[0] += -0.017441498; - } else { - result[0] += -0.0037122124; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 216))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 182))) { - result[0] += -0.024908429; - } else { - result[0] += -0.012610437; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { - result[0] += 0.020257859; - } else { - result[0] += -0.0005201062; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 20))) { - result[0] += 0.031078367; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 338))) { - result[0] += -0.03464001; - } else { - result[0] += 0.020424169; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 348))) { - result[0] += 0.0028230688; - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 114))) { - result[0] += -0.003942101; - } else { - result[0] += -0.017917613; - } - } - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 56))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 12))) { - result[0] += -0.018274682; - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 88))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { - result[0] += -0.014930627; - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 22))) { - result[0] += 0.009653761; - } else { - result[0] += -0.005647427; - } - } - } else { - result[0] += -0.017578453; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 216))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 116))) { - result[0] += -0.004387402; - } else { - result[0] += 0.004307063; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 244))) { - result[0] += 0.0050197043; - } else { - result[0] += -0.009600091; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 264))) { - result[0] += -0.0004044637; - } else { - result[0] += 0.013372946; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 138))) { - result[0] += -0.030934876; - } else { - result[0] += -0.005925291; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { - result[0] += -0.018131623; - } else { - result[0] += 0.007491207; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 158))) { - result[0] += 0.00448369; - } else { - result[0] += -0.004200383; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 124))) { - result[0] += 0.00013818998; - } else { - result[0] += -0.0060928585; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 226))) { - result[0] += 0.003693558; - } else { - result[0] += -0.0001647349; - } - } - } - } - } - } - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 400))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 388))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { - result[0] += 0.00025481472; - } else { - result[0] += 0.015072713; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 56))) { - result[0] += -0.0068883398; - } else { - result[0] += 0.005798895; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - result[0] += -0.012064995; - } else { - result[0] += -0.00055150193; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 236))) { - result[0] += 0.025693614; - } else { - result[0] += -0.0082374755; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 108))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 4))) { - result[0] += 0.020123675; - } else { - result[0] += 0.00013938252; - } - } else { - result[0] += -0.009953358; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 196))) { - result[0] += 0.016889976; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 236))) { - result[0] += -0.010803269; - } else { - result[0] += 0.010580909; - } - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 198))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 154))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 144))) { - result[0] += -0; - } else { - result[0] += 0.05305763; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 74))) { - result[0] += -0.0071868575; - } else { - result[0] += 0.06390004; - } - } - } else { - result[0] += 0.057614993; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 176))) { - result[0] += -0.0067237015; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 398))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 52))) { - result[0] += 0.0020582944; - } else { - result[0] += 0.009856465; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 86))) { - result[0] += -0.0054150624; - } else { - result[0] += 0.0054157143; - } - } - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 90))) { - result[0] += -0.010108463; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - result[0] += 0.0012366887; - } else { - result[0] += -0.005106163; - } - } - } - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 162))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 150))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 52))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - result[0] += -0.0030484337; - } else { - result[0] += 0.007103072; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { - result[0] += -0.020119462; - } else { - result[0] += 0.0049716155; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 18))) { - result[0] += -0.020776983; - } else { - result[0] += 0.0001524161; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - result[0] += 0.009727257; - } else { - result[0] += 0.0006957262; - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 54))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 158))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 152))) { - result[0] += -0.011365656; - } else { - result[0] += -0.0006019767; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 154))) { - result[0] += 0.02111136; - } else { - result[0] += 0.001328925; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 216))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 298))) { - result[0] += -0.009145065; - } else { - result[0] += 0.011142318; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { - result[0] += -0.05126782; - } else { - result[0] += -0.02713782; - } - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 138))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 268))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 66))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 42))) { - result[0] += -0.003784838; - } else { - result[0] += 0.0068541127; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 30))) { - result[0] += -0.029539952; - } else { - result[0] += -0.0067226845; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 166))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 44))) { - result[0] += 0.0031095946; - } else { - result[0] += -0.031267606; - } - } else { - result[0] += 0.03802244; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - result[0] += -0.008942714; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 332))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - result[0] += 0.018265491; - } else { - result[0] += 0.01069651; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - result[0] += 0.0040097563; - } else { - result[0] += -0.03305195; - } - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 118))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 70))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 30))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 60))) { - result[0] += -0.019308351; - } else { - result[0] += 0.0002994246; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { - result[0] += 0.0010901977; - } else { - result[0] += 0.0058678756; - } - } - } else { - result[0] += 0.024462478; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 74))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - result[0] += -0.012439872; - } else { - result[0] += 0.017688526; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 242))) { - result[0] += -0.0040794015; - } else { - result[0] += -0.009616309; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 246))) { - result[0] += 0.0013879368; - } else { - result[0] += 0.017187012; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 336))) { - result[0] += -0.0060414565; - } else { - result[0] += 0.00086067413; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 210))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 288))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { - result[0] += -0.022543944; - } else { - result[0] += 0.0002652502; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { - result[0] += 0.010252799; - } else { - result[0] += -0.0059008473; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 68))) { - result[0] += -0.00482871; - } else { - result[0] += -0.036633775; - } - } else { - result[0] += -0.020131879; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 88))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += 0.019643709; - } else { - result[0] += -0.0055163004; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 110))) { - result[0] += -0.023596639; - } else { - result[0] += -0.005768484; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 214))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 282))) { - result[0] += 0.024731327; - } else { - result[0] += 0.0059899557; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 216))) { - result[0] += -0.012860804; - } else { - result[0] += 0.00026836878; - } - } - } - } - } - } - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 250))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 270))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 216))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 188))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 20))) { - result[0] += -0.015227991; - } else { - result[0] += 0.007824044; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { - result[0] += -0.0030827313; - } else { - result[0] += 0.04585096; - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 44))) { - result[0] += 0.0038916196; - } else { - result[0] += -0.00018074422; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += -0; - } else { - result[0] += 0.01431708; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 8))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 40))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 254))) { - result[0] += -0.03443868; - } else { - result[0] += -0.016926734; - } - } else { - result[0] += 0.016495222; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 318))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 150))) { - result[0] += -0.0018124201; - } else { - result[0] += -0.008941532; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - result[0] += 0.010480117; - } else { - result[0] += -0.0053374222; - } - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 144))) { - result[0] += -0.0025247212; - } else { - result[0] += -0.021172313; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 278))) { - result[0] += 0.0136889; - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 24))) { - result[0] += -0; - } else { - result[0] += -0.028621003; - } - } else { - result[0] += 0.008883018; - } - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 272))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 178))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 120))) { - result[0] += 0.004496847; - } else { - result[0] += -0.0019887805; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 60))) { - result[0] += 0.011425511; - } else { - result[0] += 0.001471782; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 164))) { - result[0] += 0.004373122; - } else { - result[0] += -0.0050846357; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 312))) { - result[0] += -0.01642145; - } else { - result[0] += -0.0041971803; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 240))) { - result[0] += -0.019498728; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 296))) { - result[0] += 0.016818725; - } else { - result[0] += 0.031616382; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 256))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 152))) { - result[0] += 0.0041652615; - } else { - result[0] += 0.024725467; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 4))) { - result[0] += -0.017824838; - } else { - result[0] += 0.00069643237; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 104))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 346))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 138))) { - result[0] += 0.057328846; - } else { - result[0] += -0.009282811; - } - } else { - result[0] += -0.027812624; - } - } else { - result[0] += 0.006232637; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 208))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 184))) { - result[0] += -0.0007241729; - } else { - result[0] += 0.01345086; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 372))) { - result[0] += -0.01243884; - } else { - result[0] += -0.0010584429; - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 66))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 48))) { - result[0] += 0.012493608; - } else { - result[0] += 0.0014906918; - } - } else { - result[0] += -0.011661606; - } - } - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 110))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 2))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 216))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 58))) { - result[0] += 0.0059683393; - } else { - result[0] += 0.047656022; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 76))) { - result[0] += -0.01823411; - } else { - result[0] += 0.011243396; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 88))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 36))) { - result[0] += -0.015964573; - } else { - result[0] += 0.018772287; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 84))) { - result[0] += -0.024348408; - } else { - result[0] += 0.0071454565; - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 60))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 10))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 118))) { - result[0] += 0.0038073042; - } else { - result[0] += -0.010677494; - } - } else { - result[0] += -0.010760325; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 166))) { - result[0] += -0.013037783; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 196))) { - result[0] += 0.04078368; - } else { - result[0] += -0; - } - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 110))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 24))) { - result[0] += -0.019799097; - } else { - result[0] += 0.0018352288; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 14))) { - result[0] += 0.016346006; - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 266))) { - result[0] += -0.018813994; - } else { - result[0] += -0.007913149; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 174))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 296))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 280))) { - result[0] += 0.000356507; - } else { - result[0] += 0.004809927; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 298))) { - result[0] += -0.015981428; - } else { - result[0] += -0.0013808893; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 194))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 84))) { - result[0] += -0.0013645035; - } else { - result[0] += -0.009478527; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 196))) { - result[0] += 0.01796195; - } else { - result[0] += -0.00023798608; - } - } - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 142))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 210))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 26))) { - result[0] += -0.0045832377; - } else { - result[0] += -0.03190832; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { - result[0] += 0.0066647097; - } else { - result[0] += -0.002318192; - } - } - } else { - result[0] += -0.040041517; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 264))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 270))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 54))) { - result[0] += -0.0019740702; - } else { - result[0] += -0.024438191; - } - } else { - result[0] += -0; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 166))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 2))) { - result[0] += 0.029889846; - } else { - result[0] += 0.0023792638; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 268))) { - result[0] += 0.0048653954; - } else { - result[0] += -0.013145282; - } - } - } - } - } else { - result[0] += 0.026439697; - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 50))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 92))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 154))) { - result[0] += -0.023412313; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 164))) { - result[0] += 0.020196555; - } else { - result[0] += -0.012161604; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 38))) { - result[0] += 0.08170753; - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 94))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 18))) { - result[0] += -0.002143487; - } else { - result[0] += -0.038541686; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 50))) { - result[0] += -0.006611839; - } else { - result[0] += 0.008542123; - } - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 200))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 132))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 110))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 46))) { - result[0] += 0.0018188714; - } else { - result[0] += -0.015560075; - } - } else { - result[0] += 0.021364069; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 112))) { - result[0] += -0.029760977; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 148))) { - result[0] += 0.0078332; - } else { - result[0] += -0.011854128; - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 64))) { - result[0] += 0.014748133; - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { - result[0] += -0.012884839; - } else { - result[0] += 0.0029220346; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 42))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 148))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - result[0] += 0.015425849; - } else { - result[0] += -0.004185417; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 98))) { - result[0] += -0.0011755283; - } else { - result[0] += -0.03457963; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 266))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 48))) { - result[0] += -0.009746331; - } else { - result[0] += -0.030245284; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { - result[0] += -0.006777007; - } else { - result[0] += -0.02345091; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 92))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { - result[0] += -0.008947103; - } else { - result[0] += 0.03920984; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 148))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 6))) { - result[0] += 0.009707036; - } else { - result[0] += -0.0060035028; - } - } else { - result[0] += 0.01016276; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 28))) { - result[0] += -0; - } else { - result[0] += 0.032831904; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 98))) { - result[0] += 6.658281e-06; - } else { - result[0] += 0.0031512368; - } - } else { - result[0] += 0.02200143; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { - result[0] += 0.033468585; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 82))) { - result[0] += -0.008753168; - } else { - result[0] += -0.0016586205; - } - } - } - } - } - } - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 26))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - result[0] += -0.033794925; - } else { - result[0] += 0.0085956175; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { - result[0] += 0.02062062; - } else { - result[0] += -0.0024983874; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 30))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { - result[0] += -0; - } else { - result[0] += 0.02111803; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 66))) { - result[0] += 0.048572876; - } else { - result[0] += 0.007911704; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 112))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 68))) { - result[0] += -0.0077246563; - } else { - result[0] += 0.005386382; - } - } else { - result[0] += -0.020841127; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 48))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 4))) { - result[0] += -0.014042695; - } else { - result[0] += 0.008498053; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 12))) { - result[0] += -0.024558064; - } else { - result[0] += -0.0025908693; - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 32))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 24))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 170))) { - result[0] += 2.548086e-05; - } else { - result[0] += 0.026205242; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 92))) { - result[0] += 0.009812611; - } else { - result[0] += 0.00306112; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { - result[0] += 0.027721763; - } else { - result[0] += 0.006687283; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 16))) { - result[0] += 0.035541125; - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 18))) { - result[0] += -0.017473036; - } else { - result[0] += -0.0030920196; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { - result[0] += -0.00029661623; - } else { - result[0] += -0.006844879; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += 0.009076519; - } else { - result[0] += 0.00021811183; - } - } - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 36))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { - result[0] += 0.026330112; - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 248))) { - result[0] += 0.01319269; - } else { - result[0] += -0.004578778; - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 62))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { - result[0] += -0; - } else { - result[0] += -0.017771253; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 68))) { - result[0] += 0.041357838; - } else { - result[0] += 0.0004609677; - } - } - } - } - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 120))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 116))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 0))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { - result[0] += -0.03449745; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 10))) { - result[0] += -0.000830644; - } else { - result[0] += 0.027823929; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { - result[0] += -0.014675349; - } else { - result[0] += 0.002744519; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { - result[0] += 0.00021282148; - } else { - result[0] += 0.012012251; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { - result[0] += -0.023646448; - } else { - result[0] += -0.0012802199; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 64))) { - result[0] += 0.005379937; - } else { - result[0] += 0.020074537; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { - result[0] += -0.017483069; - } else { - result[0] += 0.00045991183; - } - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 28))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 118))) { - result[0] += -0.010277932; - } else { - result[0] += 0.020262284; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 78))) { - result[0] += 0.026524289; - } else { - result[0] += 0.0005923277; - } - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 78))) { - result[0] += -0.023216326; - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 160))) { - result[0] += -0.0063289674; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 80))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 56))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 124))) { - result[0] += -0.026424408; - } else { - result[0] += -0.00650081; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { - result[0] += 0.035873644; - } else { - result[0] += -0.0067583695; - } - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 134))) { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 122))) { - result[0] += 0.019181004; - } else { - result[0] += 0.0012452018; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - result[0] += -0.006571722; - } else { - result[0] += -0.00113232; - } - } - } - } - } - } - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 248))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 270))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 184))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 104))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 312))) { - result[0] += -0.001673722; - } else { - result[0] += 0.0015081331; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { - result[0] += 0.019442735; - } else { - result[0] += -0.0041994313; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 116))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 170))) { - result[0] += 0.00018035778; - } else { - result[0] += -0.012916463; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 356))) { - result[0] += 0.003256688; - } else { - result[0] += -0.0061490457; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { - result[0] += -0.002768964; - } else { - result[0] += 0.036568955; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { - result[0] += 0.0035748954; - } else { - result[0] += -0.0018361468; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { - result[0] += 0.0008316287; - } else { - result[0] += -0.012621154; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 212))) { - result[0] += 0.0009690163; - } else { - result[0] += -0.009806303; - } - } - } - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 226))) { - result[0] += -0.008160834; - } else { - result[0] += 0.010674962; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 66))) { - result[0] += -0.018799648; - } else { - result[0] += -0.0010747229; - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 274))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 252))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { - result[0] += 0.013668454; - } else { - result[0] += 0.0015821367; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 254))) { - result[0] += 0.0013118708; - } else { - result[0] += -0.0027904937; - } - } - } else { - result[0] += 0.013429761; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 150))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 278))) { - result[0] += 0.0026331868; - } else { - result[0] += 0.02152044; - } - } else { - result[0] += 0.010367058; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - result[0] += -0.009165699; - } else { - result[0] += 0.012586451; - } - } else { - result[0] += 0.017478531; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 354))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 56))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 138))) { - result[0] += 0.03761896; - } else { - result[0] += -0.009919451; - } - } else { - result[0] += -0.024488965; - } - } else { - result[0] += 0.0063245073; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 178))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 292))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 342))) { - result[0] += 0.004199187; - } else { - result[0] += -0.0270401; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 12))) { - result[0] += -0.034475163; - } else { - result[0] += 0.03449012; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 204))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 312))) { - result[0] += -0.008276438; - } else { - result[0] += -0.021829268; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 348))) { - result[0] += -0.00024369739; - } else { - result[0] += 0.018283892; - } - } - } - } - } - } - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 0))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 180))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - result[0] += 0.015541151; - } else { - result[0] += -0.004526817; - } - } else { - result[0] += 0.04517481; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 44))) { - result[0] += -0.008011468; - } else { - result[0] += -0.0023013349; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 16))) { - result[0] += 0.027930424; - } else { - result[0] += 0.001679267; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 92))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 20))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 32))) { - result[0] += 0.022154698; - } else { - result[0] += -0.01756174; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 76))) { - result[0] += -0.045003; - } else { - result[0] += -0.010276439; - } - } - } else { - result[0] += 0.001000655; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 116))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { - result[0] += 0.0012434544; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 160))) { - result[0] += 0.018014176; - } else { - result[0] += -0.00070313603; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 126))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { - result[0] += 0.0062645376; - } else { - result[0] += -0.015609448; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 16))) { - result[0] += 0.01479733; - } else { - result[0] += 0.0014734569; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 134))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 290))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 304))) { - result[0] += -0; - } else { - result[0] += -0.012774549; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 40))) { - result[0] += 0.014705256; - } else { - result[0] += -0.004232665; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 314))) { - result[0] += 0.0029532865; - } else { - result[0] += -0.0029254516; - } - } else { - result[0] += 0.026304556; - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 128))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 256))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 150))) { - result[0] += 0.00030722877; - } else { - result[0] += -0.0051551643; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - result[0] += 0.016633255; - } else { - result[0] += 0.001668491; - } - } - } else { - result[0] += -0.014828484; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 236))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 254))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 286))) { - result[0] += 0.0071044452; - } else { - result[0] += -0.0076894383; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 54))) { - result[0] += 0.02084865; - } else { - result[0] += 0.007473866; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 278))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 290))) { - result[0] += 0.005741518; - } else { - result[0] += 0.03405652; - } - } else { - result[0] += 0.025841344; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 22))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 150))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 154))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 152))) { - result[0] += 0.00015940488; - } else { - result[0] += 0.05232349; - } - } else { - result[0] += -0.018910853; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { - result[0] += -0.051273484; - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 106))) { - result[0] += -0.02290248; - } else { - result[0] += -0.0016118204; - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 64))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 192))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { - result[0] += 0.0010482629; - } else { - result[0] += -0.016297817; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 196))) { - result[0] += 0.013967775; - } else { - result[0] += 0.027914885; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 74))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 2))) { - result[0] += 0.038794536; - } else { - result[0] += -0.0062946193; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - result[0] += -2.64479e-05; - } else { - result[0] += 0.021996183; - } - } - } - } - } - } - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 104))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 40))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 32))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 28))) { - result[0] += -0.005473042; - } else { - result[0] += 0.011005775; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 136))) { - result[0] += -0.011721667; - } else { - result[0] += 0.0078737205; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 46))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 38))) { - result[0] += 0.0025443584; - } else { - result[0] += -0.016291086; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += 0.00024633956; - } else { - result[0] += 0.027108392; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 26))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 10))) { - result[0] += 0.03744457; - } else { - result[0] += 0.0014647435; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 136))) { - result[0] += -0.0058734766; - } else { - result[0] += -0.02578007; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 50))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += -0.00944722; - } else { - result[0] += -0.03411841; - } - } else { - result[0] += -0.001750114; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 14))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 100))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 24))) { - result[0] += 0.0006245327; - } else { - result[0] += 0.02487868; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 18))) { - result[0] += 0.003864918; - } else { - result[0] += 0.04562244; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 94))) { - result[0] += -0; - } else { - result[0] += -0.037629705; - } - } else { - result[0] += 0.017797282; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { - result[0] += 0.03347585; - } else { - result[0] += -0.00015754919; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - result[0] += 0.042547952; - } else { - result[0] += 0.008394706; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 304))) { - result[0] += -0.0053476547; - } else { - result[0] += 0.019664986; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 274))) { - result[0] += 0.00061302836; - } else { - result[0] += -0.008260283; - } - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 112))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 164))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - result[0] += -0.0029065635; - } else { - result[0] += 0.012601085; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { - result[0] += 0.011005281; - } else { - result[0] += -0.010062504; - } - } - } else { - result[0] += 0.015878765; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 202))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 182))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 154))) { - result[0] += -0.012548672; - } else { - result[0] += -0.036353104; - } - } else { - result[0] += -0.0037594133; - } - } else { - result[0] += -0.026142243; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 110))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 108))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 194))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 162))) { - result[0] += 0.0049291016; - } else { - result[0] += 0.02395081; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 234))) { - result[0] += -0.0030472444; - } else { - result[0] += 0.0040821205; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 100))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 114))) { - result[0] += -0.020026503; - } else { - result[0] += -0.0037345327; - } - } else { - result[0] += -0.025079226; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 128))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 128))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { - result[0] += 0.006035849; - } else { - result[0] += -0.005563633; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 134))) { - result[0] += -0.017343946; - } else { - result[0] += 0.014413935; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 308))) { - result[0] += 0.007634467; - } else { - result[0] += -0.024592249; - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 106))) { - result[0] += 0.0005188165; - } else { - result[0] += -0.006757953; - } - } - } - } - } - } - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 298))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 52))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { - result[0] += 0.00393571; - } else { - result[0] += 0.02043746; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 262))) { - result[0] += -0.01545644; - } else { - result[0] += -0.002095156; - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 210))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { - result[0] += -0.00013221658; - } else { - result[0] += -0.002142299; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - result[0] += -0.0012591215; - } else { - result[0] += 0.0015919211; - } - } - } - } else { - result[0] += 0.011080801; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 364))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 300))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 142))) { - result[0] += -0.030891055; - } else { - result[0] += -0.011523842; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 296))) { - result[0] += -0.0094786985; - } else { - result[0] += 0.00014606201; - } - } - } else { - result[0] += 0.045800555; - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 18))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 0))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 6))) { - result[0] += 0.021174032; - } else { - result[0] += -0; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 54))) { - result[0] += -0; - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 170))) { - result[0] += -0.02313884; - } else { - result[0] += -0; - } - } - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 310))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 120))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 170))) { - result[0] += 0.0034402236; - } else { - result[0] += 0.03586924; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 150))) { - result[0] += -0.007850896; - } else { - result[0] += 0.0023356122; - } - } - } else { - result[0] += 0.019080771; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 340))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 216))) { - result[0] += -0.006365792; - } else { - result[0] += 0.012724089; - } - } else { - result[0] += -0.03172406; - } - } - } - } - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 350))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 144))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 142))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 116))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 138))) { - result[0] += -0.00062813837; - } else { - result[0] += 0.002671611; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 86))) { - result[0] += 0.00652712; - } else { - result[0] += 0.00037897687; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 286))) { - result[0] += -0.00025623475; - } else { - result[0] += -0.004133305; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 82))) { - result[0] += -0.01589259; - } else { - result[0] += 0.019172618; - } - } - } - } else { - result[0] += 0.009757864; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 250))) { - result[0] += -0.0065281326; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 94))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 254))) { - result[0] += 0.0007117824; - } else { - result[0] += 0.027270088; - } - } else { - result[0] += -0.0023996648; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 248))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 48))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { - result[0] += -0.0019857192; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 222))) { - result[0] += 0.025358735; - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 232))) { - result[0] += -0.0021736256; - } else { - result[0] += 0.016332442; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 52))) { - result[0] += -0.0129296705; - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 224))) { - result[0] += -0; - } else { - result[0] += 0.018950501; - } - } - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 68))) { - result[0] += -0.0070409672; - } else { - result[0] += 0.010926886; - } - } - } - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 312))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 248))) { - result[0] += -0.00022246773; - } else { - result[0] += 0.00076635665; - } - } else { - result[0] += 0.012082838; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 286))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 298))) { - result[0] += -0.015522775; - } else { - result[0] += -0.0064152265; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 348))) { - result[0] += -0.0013468252; - } else { - result[0] += 0.020515444; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 208))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 276))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 150))) { - result[0] += 0.0077754804; - } else { - result[0] += -0.0030992497; - } - } else { - result[0] += -0.017411573; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 250))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { - result[0] += 0.003438398; - } else { - result[0] += 0.032958325; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 30))) { - result[0] += 0.013451094; - } else { - result[0] += -0.006546424; - } - } - } - } - } else { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 32))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 188))) { - result[0] += 0.009157052; - } else { - result[0] += -0.010829896; - } - } else { - result[0] += 0.015605274; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { - result[0] += 0.01869001; - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 228))) { - result[0] += -0.0042858357; - } else { - result[0] += -0.02192712; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 226))) { - result[0] += -0.0021264374; - } else { - result[0] += 0.028851261; - } - } - } - } - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 150))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 282))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 2))) { - result[0] += 0.008089608; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 252))) { - result[0] += -0.02760528; - } else { - result[0] += -0.014671764; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 248))) { - result[0] += 0.0007742315; - } else { - result[0] += -0.014570135; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 310))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 264))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 52))) { - result[0] += 0.004098795; - } else { - result[0] += -0.0013311962; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 250))) { - result[0] += -0.016194416; - } else { - result[0] += -0.004321107; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 246))) { - result[0] += 0.012578972; - } else { - result[0] += 0.031848185; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 200))) { - result[0] += 0.002893864; - } else { - result[0] += -0.0014323759; - } - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 48))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 24))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += -0.0058673653; - } else { - result[0] += 0.014972388; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 32))) { - result[0] += -0.016248314; - } else { - result[0] += -0; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 42))) { - result[0] += 0.001427743; - } else { - result[0] += 0.013427376; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 218))) { - result[0] += 0.004056875; - } else { - result[0] += -0.0111101195; - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 142))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 108))) { - result[0] += 0.009302656; - } else { - result[0] += 0.0009259971; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { - result[0] += -0.0071252473; - } else { - result[0] += 0.0005085042; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 118))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 100))) { - result[0] += 0.0055279406; - } else { - result[0] += -0.015505721; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 152))) { - result[0] += 0.022024345; - } else { - result[0] += 0.013201664; - } - } - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 30))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 140))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 24))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 154))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 34))) { - result[0] += -0.0031792417; - } else { - result[0] += -0.024321148; - } - } else { - result[0] += 0.0074777827; - } - } else { - if (UNLIKELY((data[15].missing != -1) && (data[15].qvalue < 2))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 52))) { - result[0] += -0.015280488; - } else { - result[0] += 0.004024551; - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 26))) { - result[0] += 0.013732604; - } else { - result[0] += -0; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 74))) { - result[0] += -0.028851384; - } else { - result[0] += -0.051958527; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 26))) { - result[0] += 0.010219454; - } else { - result[0] += -0.009982391; - } - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 162))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 256))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 158))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 24))) { - result[0] += 0.014080286; - } else { - result[0] += -0.004486435; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 136))) { - result[0] += 0.005933493; - } else { - result[0] += 0.02153199; - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 316))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - result[0] += -0.02032683; - } else { - result[0] += 0.002681443; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 214))) { - result[0] += -0.0042459033; - } else { - result[0] += 0.0068887584; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 196))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 392))) { - result[0] += 0.00047894806; - } else { - result[0] += -0.0061863232; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 230))) { - result[0] += 0.008450966; - } else { - result[0] += 0.0015639787; - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 200))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 224))) { - result[0] += -0.007651621; - } else { - result[0] += -0.0009728819; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { - result[0] += 0.0062414994; - } else { - result[0] += -0.0019789643; - } - } - } - } - } - } - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 314))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 312))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 306))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 84))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { - result[0] += -0.0028604511; - } else { - result[0] += 0.0004893718; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 224))) { - result[0] += 0.026684532; - } else { - result[0] += 0.0063786306; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 16))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 206))) { - result[0] += -0.009385272; - } else { - result[0] += 0.008824005; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 256))) { - result[0] += -0.00080077257; - } else { - result[0] += 0.0010740731; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 374))) { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 34))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 224))) { - result[0] += 0.014482776; - } else { - result[0] += -0.0036570956; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 308))) { - result[0] += 0.009366672; - } else { - result[0] += -0.00948626; - } - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 26))) { - result[0] += -0.005847118; - } else { - result[0] += 0.033575047; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 218))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 188))) { - result[0] += 0.0021236583; - } else { - result[0] += -0.01067119; - } - } else { - result[0] += 0.0088947555; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 222))) { - result[0] += 0.018932056; - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 64))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 230))) { - result[0] += -0.0033845974; - } else { - result[0] += -0.020902513; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 250))) { - result[0] += 0.0032020702; - } else { - result[0] += 0.042684905; - } - } - } - } - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 18))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 70))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 6))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 10))) { - result[0] += -0.008612228; - } else { - result[0] += 0.0040322477; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 22))) { - result[0] += -0.008511812; - } else { - result[0] += 0.011585411; - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 18))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 44))) { - result[0] += 1.8034565e-05; - } else { - result[0] += 0.027274786; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 64))) { - result[0] += 0.008447873; - } else { - result[0] += -0.025014887; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 130))) { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 0))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 58))) { - result[0] += -0.007062527; - } else { - result[0] += 0.014613422; - } - } else { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 24))) { - result[0] += 0.0011185434; - } else { - result[0] += 0.008517242; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { - result[0] += -0.025690308; - } else { - result[0] += -0.0029228963; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 120))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 46))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { - result[0] += -0.017073292; - } else { - result[0] += 0.0035216322; - } - } else { - result[0] += -0.023047855; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 22))) { - result[0] += -0.025554648; - } else { - result[0] += -0.0017376606; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 106))) { - result[0] += -0.0054964907; - } else { - result[0] += 0.037392512; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { - result[0] += -0.05761969; - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 34))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 98))) { - result[0] += -0.016015155; - } else { - result[0] += 0.0068704; - } - } else { - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 76))) { - result[0] += -0.015397488; - } else { - result[0] += -0.04052282; - } - } - } - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 138))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 370))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 86))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 4))) { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 52))) { - result[0] += 0.0003938696; - } else { - result[0] += 0.0045072534; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { - result[0] += -0.0024714952; - } else { - result[0] += 0.0049051414; - } - } - } else { - result[0] += 0.017899476; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 380))) { - result[0] += -0.008237792; - } else { - result[0] += -0.0031683035; - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 288))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 282))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 174))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 100))) { - result[0] += -0.0050061867; - } else { - result[0] += 0.00018127509; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 26))) { - result[0] += -0.0051624607; - } else { - result[0] += 0.00025673906; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 156))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 240))) { - result[0] += -0.0046179085; - } else { - result[0] += 0.026366374; - } - } else { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 8))) { - result[0] += -0; - } else { - result[0] += 0.018350547; - } - } - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 268))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 220))) { - result[0] += -0.0091535505; - } else { - result[0] += -0.042126443; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 248))) { - result[0] += -0.03904317; - } else { - result[0] += -0.0013817366; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 44))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 122))) { - result[0] += 0.0032674843; - } else { - result[0] += 0.038644753; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { - result[0] += -0.003548465; - } else { - result[0] += 0.044352487; - } - } - } - } - } - } - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 168))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 102))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { - result[0] += -0.0006659017; - } else { - result[0] += -0.017484738; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 124))) { - result[0] += 0.021091629; - } else { - result[0] += -0.0017686317; - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 0))) { - result[0] += -0.035111748; - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 46))) { - result[0] += -0.0162628; - } else { - result[0] += -0.004374349; - } - } - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 8))) { - result[0] += -0.031120077; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { - result[0] += -0.0055634947; - } else { - result[0] += -0.017814448; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 120))) { - result[0] += 0.042426206; - } else { - result[0] += -0.0013250493; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 58))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 132))) { - result[0] += 0.03963465; - } else { - result[0] += 0.018596584; - } - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 114))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 94))) { - result[0] += -0.0008957187; - } else { - result[0] += -0.03431357; - } - } else { - result[0] += 0.015205196; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 84))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 102))) { - result[0] += 0.012878765; - } else { - result[0] += -0.013091402; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 140))) { - result[0] += -0.022308733; - } else { - result[0] += 0.0014070682; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 38))) { - result[0] += -0.018751746; - } else { - result[0] += -0.041268926; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 52))) { - result[0] += -0.0035958923; - } else { - result[0] += 9.794186e-05; - } - } - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 352))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 198))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 120))) { - result[0] += 0.013142203; - } else { - result[0] += -0.0014631682; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += -0; - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 16))) { - result[0] += -0.01765822; - } else { - result[0] += 0.0049266764; - } - } - } - } else { - result[0] += -0.0039253565; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 368))) { - result[0] += 0.015282332; - } else { - result[0] += -0.0067160674; - } - } - } - if (LIKELY((data[10].missing != -1) && (data[10].qvalue < 84))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 214))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 176))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 160))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 32))) { - result[0] += -0.0012096122; - } else { - result[0] += 0.004681909; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 118))) { - result[0] += -0.013531153; - } else { - result[0] += 0.004223753; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 72))) { - result[0] += -0.0026232149; - } else { - result[0] += -0.01515016; - } - } else { - if (LIKELY((data[11].missing != -1) && (data[11].qvalue < 170))) { - result[0] += -0.00014745975; - } else { - result[0] += 0.0059427386; - } - } - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 86))) { - if (LIKELY((data[17].missing != -1) && (data[17].qvalue < 322))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 146))) { - result[0] += 0.011954216; - } else { - result[0] += -0.00069962896; - } - } else { - result[0] += 0.03788197; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - result[0] += -0.0036799812; - } else { - result[0] += -0.014614584; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 194))) { - result[0] += -0; - } else { - result[0] += 0.020192096; - } - } - } - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 246))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 218))) { - if (UNLIKELY((data[10].missing != -1) && (data[10].qvalue < 36))) { - result[0] += 0.010108634; - } else { - result[0] += 0.0014457417; - } - } else { - if (UNLIKELY((data[12].missing != -1) && (data[12].qvalue < 138))) { - result[0] += -0.0062636524; - } else { - result[0] += 0.00020081793; - } - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 6))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 74))) { - result[0] += -0.011251283; - } else { - result[0] += 0.0056470656; - } - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 232))) { - result[0] += 0.0014956547; - } else { - result[0] += 0.0059374445; - } - } - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 266))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 58))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 114))) { - result[0] += -0.010740235; - } else { - result[0] += 0.00111787; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - result[0] += -0.001297196; - } else { - result[0] += 0.0012389937; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 240))) { - result[0] += 0.01125375; - } else { - result[0] += -0.03845505; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 292))) { - result[0] += -0.008091416; - } else { - result[0] += 0.0020921114; - } - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 30))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 8))) { - result[0] += -0.040072184; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 136))) { - result[0] += 0.013737966; - } else { - result[0] += -0.00681287; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 132))) { - if (LIKELY((data[12].missing != -1) && (data[12].qvalue < 0))) { - result[0] += 0.028193597; - } else { - result[0] += -0.0037449375; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - result[0] += 0.015007503; - } else { - result[0] += -0.009241991; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 94))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 18))) { - result[0] += -0.003027987; - } else { - result[0] += -0.019359348; - } - } else { - result[0] += 0.0025615941; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - result[0] += 0.023024825; - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 218))) { - result[0] += -0.0007633574; - } else { - result[0] += -0.006595341; - } - } - } - } - } else { - if (LIKELY((data[15].missing != -1) && (data[15].qvalue < 6))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 146))) { - result[0] += -0.039751314; - } else { - if (UNLIKELY((data[11].missing != -1) && (data[11].qvalue < 108))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 308))) { - result[0] += 0.011828893; - } else { - result[0] += -0.009816745; - } - } else { - result[0] += -0.015453085; - } - } - } else { - result[0] += 0.0023617218; - } - } - } - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 322))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 282))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 276))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 274))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 272))) { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 268))) { - result[0] += -2.5651694e-05; - } else { - result[0] += 0.004297465; - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 234))) { - result[0] += -0.012060625; - } else { - result[0] += 0.027334878; - } - } - } else { - result[0] += 0.0077310125; - } - } else { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 112))) { - result[0] += -0.012176646; - } else { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 306))) { - result[0] += 0.0043146396; - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 278))) { - result[0] += -0.010458501; - } else { - result[0] += -0.0025602286; - } - } - } - } - } else { - result[0] += 0.026633803; - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 60))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 130))) { - if (UNLIKELY((data[14].missing != -1) && (data[14].qvalue < 14))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 122))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 106))) { - result[0] += 0.009240702; - } else { - result[0] += 0.02637141; - } - } else { - result[0] += -0.0056706998; - } - } else { - if (LIKELY((data[13].missing != -1) && (data[13].qvalue < 10))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 286))) { - result[0] += -0.003625507; - } else { - result[0] += 0.0070409435; - } - } else { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 280))) { - result[0] += 0.014894609; - } else { - result[0] += -0.003168697; - } - } - } - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 178))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - result[0] += -0.015371713; - } else { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 148))) { - result[0] += -0.009225684; - } else { - result[0] += 0.00080344983; - } - } - } else { - result[0] += -0.034244932; - } - } - } else { - if (UNLIKELY((data[13].missing != -1) && (data[13].qvalue < 72))) { - if (LIKELY((data[9].missing != -1) && (data[9].qvalue < 208))) { - if (UNLIKELY((data[9].missing != -1) && (data[9].qvalue < 184))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += -0.013472222; - } else { - result[0] += 0.00748849; - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 304))) { - result[0] += 0.0054984465; - } else { - result[0] += 0.048147988; - } - } - } else { - result[0] += -0.010059855; - } - } else { - if (LIKELY((data[16].missing != -1) && (data[16].qvalue < 346))) { - if (LIKELY((data[14].missing != -1) && (data[14].qvalue < 250))) { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 278))) { - result[0] += 0.0179956; - } else { - result[0] += 0.00028234924; - } - } else { - if (UNLIKELY((data[16].missing != -1) && (data[16].qvalue < 330))) { - result[0] += -0.005519641; - } else { - result[0] += -0.03736005; - } - } - } else { - if (UNLIKELY((data[17].missing != -1) && (data[17].qvalue < 226))) { - result[0] += -0.0048794984; - } else { - result[0] += 0.007994923; - } - } - } - } - } - - // Apply base_scores - result[0] += -1.882233619689941406; - result[0] = std::exp(result[0]); - - // Apply postprocessor - if (!pred_margin) { postprocess(result); } -} - -void dualsimplex_predictor::postprocess(double* result) -{ - // Do nothing -} - -// Feature names array -const char* dualsimplex_predictor::feature_names[dualsimplex_predictor::NUM_FEATURES] = { - "m", - "n", - "nnz", - "density", - "avg_nnz_col", - "avg_nnz_row", - "bounded", - "free", - "refact_freq", - "num_refacts", - "num_updates", - "sparse_dz", - "dense_dz", - "bound_flips", - "num_infeas", - "dy_nz_pct", - "byte_loads", - "byte_stores"}; diff --git a/cpp/src/utilities/models/dualsimplex_predictor/quantize.cpp b/cpp/src/utilities/models/dualsimplex_predictor/quantize.cpp deleted file mode 100644 index ab62e5687e..0000000000 --- a/cpp/src/utilities/models/dualsimplex_predictor/quantize.cpp +++ /dev/null @@ -1,1891 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "header.h" - -static const float threshold[] = { - 125, - 162, - 329, - 335, - 359, - 402, - 443, - 447, - 465, - 482, - 550, - 581, - 677, - 810, - 836, - 865, - 1004, - 1082, - 1388, - 1652, - 1690, - 1754, - 1820, - 1916, - 2387, - 2432, - 2471, - 2487, - 2676, - 3022, - 3128, - 3174, - 3472, - 3480, - 3897, - 4179, - 4193, - 4233, - 4240, - 4462, - 4480, - 4561, - 4661, - 4813, - 4934, - 5355, - 5593, - 6119, - 6332, - 6474, - 6504, - 6574, - 7260, - 8469, - 8488, - 8579, - 9001, - 9809, - 10400, - 10418, - 10711, - 10837, - 12702, - 13228, - 13552, - 14039, - 14653, - 15663, - 16026, - 16418, - 16488, - 16924, - 18083, - 18157, - 18525, - 18558, - 18584, - 18608, - 19134, - 20335, - 22063, - 24884, - 32736, - 33373, - 37617, - 39261, - 45220, - 48604, - 49951, - 53360, - 56116, - 57099, - 59576, - 67583, - 71393, - 88441, - 90924, - 99789, - 105209, - 105933, - 131865, - 163021, - 169576, - 224453, - 259962, - 265341, - 320520, - 349602, - 957446, - 1266, - 1314, - 2224, - 2246, - 2802, - 2825, - 2979, - 2984, - 4950, - 5769, - 5783, - 5970, - 6042, - 6519, - 6734, - 6742, - 7295, - 7784, - 8179, - 8393, - 8634, - 9240, - 9756, - 13075, - 13650, - 13802, - 14563, - 14619, - 15528, - 15655, - 15789, - 16634, - 16952, - 18099, - 18166, - 18178, - 18904, - 19457, - 19794, - 21196, - 22038, - 22645, - 24627, - 24951, - 25483, - 25720, - 30733, - 33499, - 33508, - 34436, - 37008, - 45637, - 47542, - 52526, - 56443, - 60016, - 60697, - 65771, - 65861, - 74900, - 75951, - 78534, - 88088, - 90196, - 114164, - 132419, - 141311, - 143793, - 152283, - 154622, - 159620, - 164010, - 164045, - 228960, - 233606, - 263018, - 266451, - 327865, - 408066, - 449518, - 471906, - 563256, - 1439711, - 1667610, - 2884312, - 2094, - 2530, - 2978, - 4192, - 5810, - 7766, - 8669, - 14451, - 15023, - 16817, - 19230, - 20237, - 20696, - 20930, - 21776, - 29160, - 36272, - 37026, - 40332, - 41996, - 43680, - 45193, - 50261, - 56378, - 77968, - 81543, - 81717, - 81884, - 84033, - 103839, - 109475, - 115299, - 120841, - 138700, - 141395, - 143805, - 144427, - 154634, - 171928, - 187719, - 188804, - 217438, - 217800, - 244409, - 260869, - 265420, - 280821, - 290749, - 292903, - 311318, - 352291, - 367831, - 373297, - 391677, - 394271, - 414508, - 435069, - 436515, - 466492, - 490740, - 580585, - 584200, - 819580, - 859035, - 1003742, - 1096149, - 1102562, - 1117965, - 1348523, - 1706240, - 1754504, - 1764516, - 1987895, - 2108293, - 2188365, - 4297708, - 5183486, - 7959863, - 11797396, - 1.01236e-05, - 1.146592e-05, - 1.7583379e-05, - 1.819257e-05, - 1.8350371e-05, - 2.181823e-05, - 2.1838039e-05, - 2.269307e-05, - 2.609327e-05, - 2.8569561e-05, - 3.116237e-05, - 3.2021151e-05, - 3.4602828e-05, - 3.5382109e-05, - 3.5911838e-05, - 3.710145e-05, - 3.8838469e-05, - 4.0873962e-05, - 4.824765e-05, - 5.1134e-05, - 5.5583841e-05, - 6.4015032e-05, - 7.5606709e-05, - 7.8921999e-05, - 8.1764643e-05, - 0.0001105412, - 0.0001109023, - 0.0001204482, - 0.0001509623, - 0.00016829019, - 0.0001782324, - 0.0001799585, - 0.0001880354, - 0.00019853171, - 0.0002269696, - 0.0002535714, - 0.0002745305, - 0.00028268999, - 0.00028290771, - 0.0002968506, - 0.0003150893, - 0.0003669617, - 0.00043851949, - 0.00044883709, - 0.00045417139, - 0.00049108238, - 0.00051668438, - 0.00067281991, - 0.00075661199, - 0.00083260372, - 0.0008424538, - 0.00090097258, - 0.001032864, - 0.001152342, - 0.001232837, - 0.001377049, - 0.001454633, - 0.001720742, - 0.001807331, - 0.0019736169, - 0.002305151, - 0.002327027, - 0.002512071, - 0.003719569, - 0.0038111249, - 0.0071778512, - 0.0079891831, - 0.0096212169, - 0.01194467, - 0.01507965, - 0.095635854, - 0.14575709, - 0.33324131, - 1.6, - 1.63, - 1.64, - 1.65, - 1.66, - 1.7, - 1.87, - 1.92, - 1.99, - 2, - 2.04, - 2.0899999, - 2.26, - 2.3199999, - 2.3299999, - 2.3399999, - 2.4000001, - 2.4100001, - 2.45, - 2.46, - 2.47, - 2.49, - 2.53, - 2.6900001, - 2.75, - 2.8099999, - 2.8199999, - 2.8299999, - 2.8900001, - 2.9000001, - 2.9400001, - 2.98, - 2.99, - 3, - 3.01, - 3.0599999, - 3.0999999, - 3.1500001, - 3.1700001, - 3.2, - 3.22, - 3.27, - 3.3099999, - 3.4200001, - 3.47, - 3.6199999, - 3.6400001, - 3.6900001, - 3.72, - 3.8499999, - 4.2800002, - 4.8200002, - 5.2600002, - 5.3200002, - 5.5, - 5.6599998, - 5.9099998, - 6.0300002, - 6.4000001, - 6.4200001, - 6.5300002, - 6.6799998, - 7.23, - 7.4899998, - 8.1599998, - 8.2799997, - 9.3400002, - 9.3900003, - 10.26, - 10.41, - 11.14, - 12.08, - 12.45, - 12.77, - 14.58, - 14.81, - 15.15, - 16.870001, - 17.219999, - 21.440001, - 25.24, - 34.73, - 37.759998, - 56.080002, - 69.279999, - 109.43, - 3.3299999, - 3.4200001, - 3.5, - 3.52, - 3.71, - 3.72, - 3.8, - 3.8599999, - 3.9400001, - 4.0100002, - 4.0300002, - 4.0900002, - 4.1100001, - 4.1300001, - 4.1999998, - 4.3099999, - 4.4200001, - 4.4299998, - 4.5700002, - 4.5999999, - 4.6300001, - 4.7800002, - 4.79, - 4.8200002, - 4.9099998, - 4.96, - 4.9699998, - 5, - 5.02, - 5.1500001, - 5.1799998, - 5.1900001, - 5.4099998, - 5.5900002, - 5.7399998, - 6.0700002, - 6.21, - 6.2800002, - 6.73, - 6.8200002, - 7.9400001, - 8.9200001, - 10.38, - 10.97, - 11, - 11.32, - 11.45, - 12.78, - 13.48, - 14.09, - 14.52, - 15.03, - 16.059999, - 18.5, - 20.07, - 20.99, - 25.85, - 42.599998, - 46.860001, - 50.740002, - 59.700001, - 67.800003, - 84.120003, - 89.410004, - 103.92, - 108.27, - 154.06, - 313.94, - 401.23999, - 742.96997, - 765.65997, - 214, - 363, - 630, - 792, - 955, - 1028, - 1110, - 1134, - 1143, - 1216, - 1302, - 1400, - 1414, - 1660, - 1683, - 1944, - 1946, - 2312, - 2376, - 2458, - 2595, - 2600, - 2795, - 2985, - 3034, - 3250, - 3252, - 3301, - 4136, - 4445, - 4914, - 5068, - 5376, - 5865, - 5936, - 5958, - 6730, - 7336, - 8214, - 8232, - 8450, - 8464, - 8955, - 8957, - 9520, - 10210, - 10716, - 11218, - 12414, - 12631, - 13265, - 13410, - 14099, - 15911, - 15977, - 16318, - 17187, - 18405, - 18865, - 20800, - 22480, - 24923, - 25096, - 26629, - 27542, - 29136, - 29435, - 30199, - 30731, - 31598, - 32428, - 40161, - 46727, - 53593, - 56994, - 65671, - 72468, - 73728, - 74860, - 78265, - 106260, - 122304, - 129171, - 129931, - 138134, - 167056, - 172094, - 185002, - 187879, - 242736, - 550539, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 19, - 20, - 21, - 22, - 23, - 25, - 27, - 29, - 32, - 33, - 35, - 36, - 38, - 39, - 41, - 43, - 45, - 48, - 50, - 51, - 53, - 54, - 56, - 58, - 60, - 64, - 68, - 70, - 72, - 74, - 76, - 78, - 83, - 85, - 87, - 89, - 91, - 94, - 96, - 98, - 100, - 102, - 104, - 107, - 114, - 117, - 119, - 122, - 124, - 127, - 129, - 132, - 137, - 142, - 145, - 148, - 150, - 155, - 157, - 160, - 163, - 165, - 168, - 171, - 182, - 185, - 188, - 191, - 194, - 197, - 200, - 202, - 205, - 208, - 211, - 214, - 217, - 220, - 223, - 226, - 229, - 233, - 236, - 243, - 246, - 249, - 252, - 255, - 263, - 266, - 270, - 274, - 278, - 281, - 285, - 294, - 298, - 306, - 315, - 320, - 324, - 329, - 338, - 343, - 347, - 351, - 360, - 365, - 370, - 374, - 379, - 388, - 393, - 398, - 402, - 407, - 412, - 422, - 427, - 438, - 443, - 449, - 453, - 459, - 470, - 476, - 482, - 489, - 495, - 501, - 520, - 526, - 533, - 540, - 547, - 553, - 560, - 567, - 574, - 582, - 589, - 597, - 604, - 612, - 637, - 645, - 653, - 661, - 669, - 678, - 687, - 696, - 704, - 742, - 751, - 762, - 772, - 783, - 796, - 808, - 821, - 835, - 850, - 864, - 881, - 897, - 913, - 1006, - 1025, - 1046, - 1098, - 1126, - 1154, - 1183, - 1240, - 1301, - 1340, - 1494, - 1550, - 1607, - 1722, - 2180, - 2294, - 2636, - 2750, - 3093, - 3, - 7, - 9, - 11, - 12, - 21, - 23, - 25, - 31, - 33, - 34, - 35, - 37, - 39, - 41, - 43, - 46, - 47, - 49, - 51, - 53, - 55, - 57, - 59, - 61, - 63, - 65, - 67, - 69, - 71, - 73, - 75, - 77, - 83, - 85, - 87, - 89, - 91, - 93, - 95, - 97, - 99, - 100, - 100, - 200, - 400, - 500, - 600, - 700, - 781, - 800, - 1100, - 1200, - 1300, - 1400, - 1500, - 1600, - 1800, - 1900, - 2000, - 2200, - 2631, - 2900, - 2991, - 3215, - 3229, - 3500, - 3600, - 3752, - 3900, - 4200, - 4334, - 4430, - 4993, - 5226, - 5400, - 5580, - 6800, - 7200, - 7600, - 8500, - 8700, - 9100, - 9389, - 9500, - 9700, - 9800, - 10088, - 10367, - 10621, - 10783, - 10900, - 11000, - 11189, - 11300, - 11467, - 11700, - 11853, - 11958, - 12068, - 12133, - 12200, - 12300, - 12700, - 12900, - 13100, - 13400, - 13900, - 14100, - 14400, - 14700, - 15700, - 15900, - 16200, - 16400, - 16700, - 16900, - 17400, - 17700, - 18500, - 19000, - 19500, - 20000, - 20300, - 20800, - 21600, - 21900, - 22100, - 22400, - 22700, - 22900, - 23401, - 23700, - 24000, - 24300, - 24600, - 24900, - 25100, - 25400, - 25700, - 26000, - 26900, - 27200, - 28100, - 28400, - 29000, - 29300, - 29900, - 30166, - 30800, - 31200, - 31859, - 32531, - 32900, - 33300, - 33638, - 34000, - 34348, - 34700, - 35031, - 35400, - 36100, - 37131, - 37700, - 39400, - 40500, - 42300, - 44300, - 45000, - 45700, - 48700, - 49531, - 50400, - 51200, - 52900, - 53700, - 54500, - 55300, - 56100, - 56900, - 57800, - 60550, - 61500, - 62600, - 65585, - 66455, - 67400, - 69531, - 70573, - 71700, - 75800, - 79153, - 83000, - 85200, - 87500, - 91100, - 98700, - 102620, - 106608, - 116538, - 128326, - 2, - 7, - 8, - 19, - 51, - 59, - 62, - 65, - 83, - 122, - 140, - 210, - 235, - 280, - 320, - 342, - 374, - 402, - 511, - 584, - 640, - 804, - 966, - 1055, - 1176, - 1365, - 1861, - 2032, - 2923, - 3175, - 3297, - 3691, - 5827, - 9936, - 10785, - 15388, - 15974, - 19297, - 20249, - 29707, - 32244, - 65988, - 75104, - 81627, - 102279, - 107471, - 115309, - 130401, - 136081, - 137992, - 139946, - 141867, - 147702, - 151565, - 157335, - 161066, - 163019, - 166929, - 189052, - 192643, - 196469, - 200185, - 203906, - 211277, - 218538, - 222118, - 225779, - 236984, - 244345, - 251930, - 255672, - 259464, - 263326, - 267093, - 1, - 2, - 4, - 6, - 7, - 8, - 11, - 13, - 16, - 20, - 24, - 29, - 33, - 35, - 40, - 45, - 50, - 55, - 58, - 61, - 65, - 68, - 74, - 79, - 86, - 92, - 99, - 103, - 110, - 114, - 120, - 123, - 134, - 140, - 142, - 159, - 217, - 238, - 257, - 262, - 271, - 305, - 340, - 351, - 384, - 445, - 525, - 626, - 685, - 766, - 881, - 950, - 1083, - 1116, - 1181, - 1221, - 1300, - 1371, - 1460, - 1526, - 1645, - 1748, - 1848, - 2023, - 2075, - 2130, - 2301, - 2553, - 2655, - 2825, - 2882, - 3055, - 3237, - 3664, - 4068, - 4554, - 4785, - 4844, - 4914, - 5244, - 5504, - 5741, - 5998, - 6427, - 6579, - 6711, - 6944, - 7199, - 7367, - 7614, - 7933, - 8753, - 9119, - 9522, - 9898, - 10181, - 11143, - 11318, - 11349, - 11401, - 11414, - 12040, - 12708, - 13433, - 14189, - 14430, - 14490, - 15578, - 16625, - 17045, - 17477, - 18091, - 18615, - 19381, - 19680, - 19741, - 21530, - 21730, - 22531, - 23178, - 23805, - 24531, - 25374, - 28481, - 28773, - 29314, - 36041, - 36592, - 41200, - 43679, - 48927, - 51948, - 54859, - 57680, - 59597, - 67687, - 69109, - 97630, - 121641, - 166257, - 188207, - 208963, - 231527, - 5, - 9, - 13, - 29, - 35, - 42, - 48, - 57, - 87, - 105, - 117, - 133, - 149, - 178, - 202, - 227, - 253, - 272, - 311, - 336, - 353, - 373, - 425, - 457, - 484, - 551, - 571, - 590, - 640, - 672, - 679, - 684, - 689, - 699, - 751, - 760, - 767, - 782, - 790, - 804, - 817, - 823, - 829, - 833, - 846, - 852, - 870, - 878, - 900, - 905, - 957, - 1001, - 1015, - 1029, - 1052, - 1070, - 1111, - 1209, - 1234, - 1256, - 1285, - 1307, - 1332, - 1354, - 1406, - 1433, - 1453, - 1536, - 1593, - 1617, - 1708, - 1740, - 1778, - 1815, - 2055, - 2100, - 2150, - 2207, - 2263, - 2312, - 2421, - 2472, - 2708, - 2790, - 2866, - 2904, - 2970, - 3041, - 3155, - 3448, - 3570, - 3678, - 3938, - 4009, - 4085, - 4220, - 4297, - 4470, - 4544, - 4631, - 4915, - 4992, - 5086, - 5175, - 5410, - 5515, - 5639, - 5871, - 6194, - 6322, - 6474, - 6674, - 7418, - 7842, - 8121, - 8513, - 8918, - 9387, - 9825, - 10305, - 10831, - 11319, - 11864, - 12390, - 12834, - 13338, - 13865, - 14407, - 14624, - 15346, - 17033, - 17329, - 17539, - 18028, - 18729, - 19229, - 19547, - 20016, - 20494, - 20821, - 21197, - 21552, - 22957, - 23882, - 24497, - 25451, - 26416, - 27670, - 29804, - 32934, - 36523, - 38379, - 39763, - 42089, - 45622, - 49111, - 55901, - 63904, - 0.0099999998, - 0.02, - 0.029999999, - 0.039999999, - 0.050000001, - 0.059999999, - 0.07, - 0.090000004, - 0.11, - 0.12, - 0.13, - 0.14, - 0.16, - 0.23, - 0.25, - 0.27000001, - 0.28999999, - 0.33000001, - 0.36000001, - 0.38999999, - 0.47999999, - 0.51999998, - 0.57999998, - 0.63999999, - 0.76999998, - 0.86000001, - 0.93000001, - 1, - 1.0599999, - 1.3200001, - 1.5, - 1.72, - 1.84, - 1.91, - 2.04, - 2.74, - 3.1900001, - 3.8, - 3.8800001, - 4.1500001, - 4.2800002, - 4.5799999, - 5.5700002, - 6.2399998, - 6.7199998, - 7.5599999, - 8.04, - 8.6300001, - 9.0100002, - 9.9899998, - 10.51, - 10.96, - 11.85, - 12.79, - 14.06, - 14.37, - 14.76, - 15.51, - 15.88, - 16.08, - 16.42, - 17.17, - 18.209999, - 19.51, - 20.52, - 20.82, - 21.1, - 22.299999, - 24.459999, - 26.08, - 53.470001, - 60.110001, - 61.09, - 61.849998, - 62.630001, - 62.919998, - 63.040001, - 63.169998, - 63.299999, - 63.389999, - 63.48, - 79.32, - 83.129997, - 83.849998, - 83.959999, - 100, - 1037650, - 1723504, - 2244653, - 2864825, - 3383987, - 3795480, - 4142277, - 4567104, - 5010256, - 5966615, - 6299370, - 7083277, - 7522126, - 8412871, - 8837549, - 9276948, - 9863821, - 10486369, - 11578704, - 12163923, - 13021873, - 13828478, - 14673878, - 15654338, - 16574689, - 17543426, - 18492992, - 20736132, - 21788372, - 22962064, - 23847060, - 24860524, - 25793028, - 26489768, - 27032812, - 27450752, - 27813360, - 28221620, - 29612440, - 30447920, - 31219590, - 31626088, - 32106968, - 32649468, - 33434304, - 34513800, - 35555364, - 38788752, - 39737744, - 40706704, - 41542668, - 43205512, - 44037980, - 46729864, - 50637396, - 53254524, - 54226704, - 55338660, - 59395504, - 60319136, - 61422976, - 63719232, - 64959840, - 68561256, - 70486456, - 71614280, - 72849216, - 74358072, - 75856336, - 77025160, - 78309984, - 79791192, - 83683880, - 85224608, - 86544872, - 87806552, - 88856952, - 91365568, - 92439280, - 93485088, - 94560704, - 1.0045851e+08, - 1.0065839e+08, - 1.0086424e+08, - 1.0106654e+08, - 1.0359994e+08, - 1.042553e+08, - 1.0508658e+08, - 1.0574689e+08, - 1.0658666e+08, - 1.0800078e+08, - 1.1096082e+08, - 1.1237282e+08, - 1.1344565e+08, - 1.1638846e+08, - 1.18122e+08, - 1.1955379e+08, - 1.2126572e+08, - 1.2230541e+08, - 1.2337086e+08, - 1.246013e+08, - 1.2752458e+08, - 1.2904266e+08, - 1.3026654e+08, - 1.3340886e+08, - 1.3488405e+08, - 1.3772952e+08, - 1.390135e+08, - 1.4265074e+08, - 1.457892e+08, - 1.4689109e+08, - 1.484849e+08, - 1.5004106e+08, - 1.5059768e+08, - 1.5118848e+08, - 1.5200856e+08, - 1.5250224e+08, - 1.5346341e+08, - 1.540247e+08, - 1.5532386e+08, - 1.567155e+08, - 1.5878211e+08, - 1.594644e+08, - 1.6018256e+08, - 1.6168109e+08, - 1.6223786e+08, - 1.6326845e+08, - 1.6384918e+08, - 1.649735e+08, - 1.656859e+08, - 1.7070971e+08, - 1.7694973e+08, - 1.7941701e+08, - 1.8222443e+08, - 1.8530262e+08, - 1.9251592e+08, - 2.0134139e+08, - 2.0541229e+08, - 2.1434315e+08, - 2.1878254e+08, - 2.2355106e+08, - 2.2831722e+08, - 2.3215955e+08, - 2.342463e+08, - 2.3843141e+08, - 2.4364762e+08, - 2.4820429e+08, - 2.5302923e+08, - 2.6208336e+08, - 2.6698949e+08, - 2.7137626e+08, - 2.8217254e+08, - 2.8691187e+08, - 2.9745187e+08, - 3.0660896e+08, - 3.1238829e+08, - 3.1902518e+08, - 3.229599e+08, - 3.329297e+08, - 3.5073504e+08, - 3.6027674e+08, - 3.7681318e+08, - 3.9339715e+08, - 4.0543194e+08, - 4.160361e+08, - 4.3077226e+08, - 4.523279e+08, - 4.8495926e+08, - 5.1632826e+08, - 5.5564128e+08, - 5.9271789e+08, - 6.411625e+08, - 7.372567e+08, - 8.6929702e+08, - 1.055401e+09, - 1.1658797e+09, - 240560, - 410076, - 711288, - 895492, - 1109492, - 1276528, - 1625728, - 1899768, - 2027364, - 2281644, - 2586332, - 2849488, - 3117988, - 3410364, - 3709888, - 3962256, - 4181556, - 4364328, - 4498032, - 4758348, - 4873952, - 5472904, - 5591664, - 5778728, - 6276504, - 6413356, - 6547728, - 6691128, - 6790740, - 7004992, - 7308272, - 7570344, - 8042524, - 8491408, - 8860504, - 9194476, - 9709736, - 10160616, - 10717016, - 11119504, - 11705964, - 12261312, - 12866664, - 13514312, - 14704776, - 15405152, - 16005068, - 16754080, - 17383300, - 18136364, - 19389944, - 20019916, - 20463948, - 21400196, - 21923612, - 22581916, - 22715788, - 23146444, - 23443700, - 23798364, - 24264244, - 24925016, - 25291860, - 25913100, - 26458836, - 27503132, - 28387432, - 29910644, - 31697792, - 32425572, - 33213116, - 34034288, - 35760708, - 38022176, - 38782176, - 40717416, - 41235180, - 41747264, - 43542308, - 43943804, - 44735816, - 45065864, - 46009976, - 46306288, - 46543792, - 47421788, - 48162160, - 48280112, - 48688764, - 48843128, - 49144836, - 49259852, - 49630964, - 51064148, - 51307316, - 51527992, - 53551800, - 53959488, - 54099240, - 54364560, - 57614644, - 58836712, - 60200060, - 61460216, - 64680100, - 65559320, - 66289320, - 66894620, - 72498960, - 73778352, - 75205760, - 76278448, - 77526400, - 78883072, - 81149424, - 82100688, - 83732032, - 85329040, - 86993952, - 87871808, - 88962104, - 90430704, - 92956960, - 96098048, - 97716312, - 99624112, - 1.0442499e+08, - 1.066803e+08, - 1.0837106e+08, - 1.105755e+08, - 1.1288506e+08, - 1.1562831e+08, - 1.1797418e+08, - 1.2039574e+08, - 1.2301378e+08, - 1.250115e+08, - 1.3212201e+08, - 1.3653286e+08, - 1.3967781e+08, - 1.4220454e+08, - 1.457472e+08, - 1.4944283e+08, - 1.5326578e+08, - 1.5793666e+08, - 1.625305e+08, - 1.6722179e+08, - 1.7208096e+08, - 1.7451046e+08, - 1.7936912e+08, - 1.8371443e+08, - 1.8765027e+08, - 1.9079603e+08, - 1.9395962e+08, - 2.0647309e+08, - 2.1130994e+08, - 2.1458229e+08, - 2.1853994e+08, - 2.3005197e+08, - 2.3600296e+08, - 2.4206338e+08, - 2.522703e+08, - 2.6565344e+08, - 2.7929763e+08, - 3.2687946e+08, - 3.9571389e+08, - 5.2705373e+08, -}; - -static const int th_begin[] = { - 0, - 109, - 194, - 273, - 346, - 432, - 503, - 594, - 594, - 594, - 797, - 840, - 998, - 1072, - 1215, - 1373, - 1459, - 1635, -}; - -static const int th_len[] = { - 109, - 85, - 79, - 73, - 86, - 71, - 91, - 0, - 0, - 203, - 43, - 158, - 74, - 143, - 158, - 86, - 176, - 166, -}; - -/* - * \brief Function to convert a feature value into bin index. - * \param val Feature value, in floating-point - * \param fid Feature identifier - * \return bin Index corresponding to given feature value - */ -int dualsimplex_predictor::quantize(float val, unsigned fid) -{ - const size_t offset = th_begin[fid]; - const float* array = &threshold[offset]; - int len = th_len[fid]; - int low = 0; - int high = len; - int mid; - float mval; - // It is possible th_begin[i] == [total_num_threshold]. This means that - // all features i, (i+1), ... are not used for any of the splits in the model. - // So in this case, just return something - if (offset == 1801 || val < array[0]) { return -10; } - while (low + 1 < high) { - mid = (low + high) / 2; - mval = array[mid]; - if (val == mval) { - return mid * 2; - } else if (val < mval) { - high = mid; - } else { - low = mid; - } - } - if (array[low] == val) { - return low * 2; - } else if (high == len) { - return len * 2; - } else { - return low * 2 + 1; - } -} diff --git a/cpp/src/utilities/models/pdlp_predictor/header.h b/cpp/src/utilities/models/pdlp_predictor/header.h deleted file mode 100644 index 09d2fddc5e..0000000000 --- a/cpp/src/utilities/models/pdlp_predictor/header.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -class pdlp_predictor { - public: - union Entry { - int missing; - float fvalue; - int qvalue; - }; - - static int32_t get_num_target(void); - static void get_num_class(int32_t* out); - static int32_t get_num_feature(void); - static const char* get_threshold_type(void); - static const char* get_leaf_output_type(void); - static void predict(union Entry* data, int pred_margin, double* result); - static void postprocess(double* result); - static int quantize(float val, unsigned fid); - - // Feature names - static constexpr int NUM_FEATURES = 8; - static const char* feature_names[NUM_FEATURES]; -}; // class pdlp_predictor diff --git a/cpp/src/utilities/models/pdlp_predictor/main.cpp b/cpp/src/utilities/models/pdlp_predictor/main.cpp deleted file mode 100644 index 6ae3486409..0000000000 --- a/cpp/src/utilities/models/pdlp_predictor/main.cpp +++ /dev/null @@ -1,16893 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "header.h" - -#if defined(__clang__) || defined(__GNUC__) -#define LIKELY(x) __builtin_expect(!!(x), 1) -#define UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define LIKELY(x) (x) -#define UNLIKELY(x) (x) -#endif -#define N_TARGET 1 -#define MAX_N_CLASS 1 - -const unsigned char is_categorical[] = { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, -}; -static const int32_t num_class[] = { - 1, -}; - -int32_t pdlp_predictor::get_num_target(void) { return N_TARGET; } -void pdlp_predictor::get_num_class(int32_t* out) -{ - for (int i = 0; i < N_TARGET; ++i) { - out[i] = num_class[i]; - } -} -int32_t pdlp_predictor::get_num_feature(void) { return 8; } -const char* pdlp_predictor::get_threshold_type(void) { return "float32"; } -const char* pdlp_predictor::get_leaf_output_type(void) { return "float32"; } - -void pdlp_predictor::predict(union Entry* data, int pred_margin, double* result) -{ - // Quantize data - for (int i = 0; i < 8; ++i) { - if (data[i].missing != -1 && !is_categorical[i]) { - data[i].qvalue = quantize(data[i].fvalue, i); - } - } - - unsigned int tmp; - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - result[0] += -0.014820111; - } else { - result[0] += 0.0105687855; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - result[0] += -0.053651925; - } else { - result[0] += -0.10176092; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 220))) { - result[0] += -0.012739944; - } else { - result[0] += -0.036262058; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { - result[0] += -0.0063560107; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { - result[0] += 0.08327574; - } else { - result[0] += 0.026058618; - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 230))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 164))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { - result[0] += 0.031961214; - } else { - result[0] += 0.04515064; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { - result[0] += 0.018566301; - } else { - result[0] += -0.009213644; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { - result[0] += 0.14206316; - } else { - result[0] += 0.07914438; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { - result[0] += 0.025240844; - } else { - result[0] += 0.058222026; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.26381668; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - result[0] += 0.085295476; - } else { - result[0] += 0.16802387; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { - result[0] += 0.19548021; - } else { - result[0] += 0.14702512; - } - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += 0.0039215386; - } else { - result[0] += -0.020411594; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { - result[0] += -0.030691355; - } else { - result[0] += -0.066723615; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { - result[0] += -0.039679535; - } else { - result[0] += -0.07033275; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 226))) { - result[0] += -0.017522344; - } else { - result[0] += 0.024236064; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { - result[0] += -0.01896934; - } else { - result[0] += 0.005805307; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += -0.05819216; - } else { - result[0] += -0.030700704; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 210))) { - result[0] += -0.016364044; - } else { - result[0] += -0.0079360735; - } - } else { - result[0] += 0.09915119; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 260))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { - result[0] += 0.0082174195; - } else { - result[0] += -0.0037611835; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { - result[0] += 0.05758765; - } else { - result[0] += 0.00845852; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 188))) { - result[0] += 0.08059614; - } else { - result[0] += 0.060384076; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { - result[0] += -0; - } else { - result[0] += 0.07759698; - } - } else { - result[0] += 0.12749861; - } - } else { - result[0] += 0.16187607; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 22))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += -0.0052639237; - } else { - result[0] += 0.022940176; - } - } else { - result[0] += -0.03480838; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { - result[0] += -0.04990984; - } else { - result[0] += -0.080560416; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - result[0] += -0.014295327; - } else { - result[0] += -0.03235691; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { - result[0] += -0; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { - result[0] += 0.07372898; - } else { - result[0] += 0.024104217; - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 230))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 176))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { - result[0] += 0.02912726; - } else { - result[0] += 0.04095058; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += 0.016825125; - } else { - result[0] += -0.007768286; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { - result[0] += 0.1265556; - } else { - result[0] += 0.07124209; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - result[0] += 0.040830098; - } else { - result[0] += 0.0559672; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.23753951; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - result[0] += 0.07685151; - } else { - result[0] += 0.1544579; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 260))) { - result[0] += 0.046362683; - } else { - result[0] += 0.17204122; - } - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { - result[0] += -0.022617795; - } else { - result[0] += -0.0122455275; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { - result[0] += -0.027298182; - } else { - result[0] += -0.059523176; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 248))) { - result[0] += -0.035259727; - } else { - result[0] += -0.061327398; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 236))) { - result[0] += -0.01425799; - } else { - result[0] += 0.017331526; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { - result[0] += 0.00193814; - } else { - result[0] += -0.017574918; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 196))) { - result[0] += -0.02869076; - } else { - result[0] += -0.065549426; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - result[0] += 0.0057360423; - } else { - result[0] += -0.013489055; - } - } else { - result[0] += 0.089799576; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 260))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { - result[0] += 0.007112731; - } else { - result[0] += -0.0027204938; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { - result[0] += 0.054216303; - } else { - result[0] += 0.007258077; - } - } - } else { - result[0] += 0.059579976; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { - result[0] += 0.0030776516; - } else { - result[0] += 0.05093806; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { - result[0] += 0.07593134; - } else { - result[0] += 0.11205458; - } - } - } else { - result[0] += 0.14210285; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 32))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += -0.014897979; - } else { - result[0] += 0.003548234; - } - } else { - result[0] += 0.018050188; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 88))) { - result[0] += -0.008003016; - } else { - result[0] += -0.029365538; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - result[0] += -0.008477488; - } else { - result[0] += -0.05125452; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { - result[0] += 0.02146909; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 8))) { - result[0] += -0.038308386; - } else { - result[0] += 0.0050441376; - } - } - } else { - result[0] += 0.045694016; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - result[0] += 0.010466478; - } else { - result[0] += 0.032060686; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - result[0] += 0.055632647; - } else { - result[0] += 0.10817957; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 244))) { - result[0] += 0.05290089; - } else { - result[0] += 0.15234004; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += 0.11994175; - } else { - result[0] += 0.16568963; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += 0.21408843; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { - result[0] += 0.109505884; - } else { - result[0] += 0.16057345; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += -0.021672187; - } else { - result[0] += -0.05343589; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += 0.0054487307; - } else { - result[0] += -0.017187914; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 92))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 188))) { - result[0] += -0.019816956; - } else { - result[0] += -0.0050099557; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 248))) { - result[0] += -0.03200326; - } else { - result[0] += -0.056725156; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - result[0] += -0.011511999; - } else { - result[0] += -0.028344637; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += 0.024851266; - } else { - result[0] += -0.05057622; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { - result[0] += -0.0015090223; - } else { - result[0] += 0.04174643; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 206))) { - result[0] += -0.014773312; - } else { - result[0] += -0.001539268; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 210))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - result[0] += 0.007884377; - } else { - result[0] += 0.061007004; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - result[0] += 0.021275358; - } else { - result[0] += 0.004744153; - } - } - } else { - result[0] += 0.053870168; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += 0.00918735; - } else { - result[0] += 0.06173278; - } - } else { - result[0] += 0.098605655; - } - } else { - result[0] += 0.13258949; - } - } - } - } - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 266))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { - result[0] += 0.0034634469; - } else { - result[0] += -0.01012047; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 202))) { - result[0] += 0.026168926; - } else { - result[0] += -0.003920808; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 34))) { - result[0] += 0.01599789; - } else { - result[0] += -0.02167696; - } - } else { - result[0] += -0.057240784; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 180))) { - result[0] += 0.016893527; - } else { - result[0] += -0.0182766; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { - result[0] += -0.027289895; - } else { - result[0] += -0.0045931367; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 236))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - result[0] += 0.06542163; - } else { - result[0] += 0.048862282; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 248))) { - result[0] += 0.012741444; - } else { - result[0] += -0.0075932243; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 250))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 64))) { - result[0] += 0.090353966; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 260))) { - result[0] += 0.042119607; - } else { - result[0] += 0.053020816; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { - result[0] += 0.03130157; - } else { - result[0] += 0.007205482; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - result[0] += 0.10881876; - } else { - result[0] += 0.054842055; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += 0.031206427; - } else { - result[0] += 0.093665555; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { - result[0] += 0.058885314; - } else { - result[0] += 0.009160067; - } - } - } else { - result[0] += 0.12904838; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 196))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 132))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 126))) { - result[0] += 0.009280808; - } else { - result[0] += 0.039755784; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 184))) { - result[0] += -0.0042881714; - } else { - result[0] += 0.02849459; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 176))) { - result[0] += 0.013534146; - } else { - result[0] += 0.042829912; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 196))) { - result[0] += 0.060934413; - } else { - result[0] += 0.04222716; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 166))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 252))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 242))) { - result[0] += -0.014791851; - } else { - result[0] += 0.03050575; - } - } else { - result[0] += -0.051454216; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 244))) { - result[0] += -0.00703993; - } else { - result[0] += 0.006275458; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 230))) { - result[0] += 0.05098787; - } else { - result[0] += 0.0045872494; - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 236))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 156))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { - result[0] += 0.010856108; - } else { - result[0] += -0.028078709; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 196))) { - result[0] += 0.059345823; - } else { - result[0] += 0.025444312; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 246))) { - result[0] += 0.1018459; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 226))) { - result[0] += 0.06307183; - } else { - result[0] += 0.049246445; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 192))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 248))) { - result[0] += -0.0057842466; - } else { - result[0] += 0.03709874; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 272))) { - result[0] += 0.10474528; - } else { - result[0] += 0.0065343017; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - result[0] += 0.14898759; - } else { - result[0] += 0.09902468; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 204))) { - result[0] += -0.020444617; - } else { - result[0] += 0.08464522; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - result[0] += -0.010948095; - } else { - result[0] += -0.023803001; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - result[0] += -0.011822949; - } else { - result[0] += 0.045119066; - } - } - } else { - result[0] += -0.06180344; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { - result[0] += -0.005668474; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { - result[0] += 0.06234616; - } else { - result[0] += 0.017185496; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { - result[0] += 0.024365185; - } else { - result[0] += 0.034411617; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - result[0] += 0.0037991663; - } else { - result[0] += 0.06540612; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - result[0] += 0.04412658; - } else { - result[0] += 0.09097751; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { - result[0] += 0.109255195; - } else { - result[0] += 0.15402773; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += 0.18228906; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { - result[0] += 0.091413066; - } else { - result[0] += 0.12995003; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 66))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - result[0] += -0.013535394; - } else { - result[0] += -0.030926574; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - result[0] += -0.02790393; - } else { - result[0] += -0.0042497306; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - result[0] += -0.016862048; - } else { - result[0] += -0.042121567; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += 0.043057855; - } else { - result[0] += -0.010520599; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 268))) { - result[0] += 0.0006321628; - } else { - result[0] += 0.043750294; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { - result[0] += 0.017152889; - } else { - result[0] += 0.0047487523; - } - } - } else { - result[0] += 0.047594197; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - result[0] += -0.0008511189; - } else { - result[0] += -0.015743343; - } - } else { - result[0] += 0.020423314; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { - result[0] += 0.071650326; - } else { - result[0] += 0.038795434; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.106261484; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { - result[0] += 0.05060423; - } else { - result[0] += 0.09152374; - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 32))) { - result[0] += -0.0061984994; - } else { - result[0] += 0.037181865; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { - result[0] += 0.05393646; - } else { - result[0] += 0.015753375; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 134))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - result[0] += -0.021209892; - } else { - result[0] += 0.0069300993; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - result[0] += -0.0077880546; - } else { - result[0] += -0.04197134; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 12))) { - result[0] += 0.040751807; - } else { - result[0] += -0.002328172; - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 254))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 198))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { - result[0] += 0.011177325; - } else { - result[0] += -0.0057386267; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { - result[0] += 0.022035722; - } else { - result[0] += 0.031592578; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { - result[0] += 0.046259467; - } else { - result[0] += 0.07964405; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.037900005; - } else { - result[0] += 0.006691271; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - result[0] += 0.045320172; - } else { - result[0] += 0.08683389; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - result[0] += 0.112302594; - } else { - result[0] += 0.15148906; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { - result[0] += 0.09804384; - } else { - result[0] += 0.15994717; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += -0.014513046; - } else { - result[0] += -0.04194841; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 60))) { - result[0] += -0.018427247; - } else { - result[0] += -0.009509578; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { - result[0] += -0.021712733; - } else { - result[0] += -0.033654116; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - result[0] += 0.017070105; - } else { - result[0] += -0.010606452; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { - result[0] += 0.003849056; - } else { - result[0] += -0.012776096; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += -0.01715595; - } else { - result[0] += -0.034953322; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - result[0] += 0.004928622; - } else { - result[0] += -0.009454594; - } - } else { - result[0] += 0.06586691; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - result[0] += 0.00017961742; - } else { - result[0] += -0.0068662055; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.034061044; - } else { - result[0] += 0.004285582; - } - } - } else { - result[0] += 0.041407876; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - result[0] += 0.0008129136; - } else { - result[0] += 0.046276525; - } - } else { - result[0] += 0.08599146; - } - } else { - result[0] += 0.092344016; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { - result[0] += 0.0022713204; - } else { - result[0] += -0.01581463; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - result[0] += -0.019155947; - } else { - result[0] += -0.031192351; - } - } - } else { - result[0] += -0.054840814; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += 0.034809362; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - result[0] += -0.03447778; - } else { - result[0] += -0.0010464619; - } - } - } else { - result[0] += 0.036184475; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { - result[0] += 0.011472473; - } else { - result[0] += -0.00536993; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { - result[0] += 0.017885301; - } else { - result[0] += 0.026684735; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 36))) { - result[0] += 0.050483853; - } else { - result[0] += 0.032498028; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { - result[0] += 0.107258014; - } else { - result[0] += 0.05922347; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += 0.14826694; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { - result[0] += 0.07493075; - } else { - result[0] += 0.105560794; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { - result[0] += -0.024180638; - } else { - result[0] += -0.010499556; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 218))) { - result[0] += 0.016562223; - } else { - result[0] += -0.013134924; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { - result[0] += -0.018523889; - } else { - result[0] += -0.030611722; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += -0.017535986; - } else { - result[0] += -0.004351136; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { - result[0] += -0.021926327; - } else { - result[0] += -0.008350995; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { - result[0] += -0.011140397; - } else { - result[0] += -0.04280148; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - result[0] += 0.03963171; - } else { - result[0] += 0.019383498; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 214))) { - result[0] += -0.00729556; - } else { - result[0] += -0.040553037; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.08876854; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.067758165; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { - result[0] += 0.03981258; - } else { - result[0] += 0.05752442; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { - result[0] += -0.00089354016; - } else { - result[0] += 0.0065446827; - } - } else { - result[0] += -0.014053066; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { - result[0] += 0.06474081; - } else { - result[0] += 0.017832294; - } - } - } - } - } - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 206))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 202))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 30))) { - result[0] += 0.0055920025; - } else { - result[0] += -0.013393702; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { - result[0] += 0.03213504; - } else { - result[0] += 0.004373153; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { - result[0] += -0.026502002; - } else { - result[0] += -0.0089536635; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 108))) { - result[0] += 0.023841746; - } else { - result[0] += -0.009630135; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 132))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 84))) { - result[0] += 0.03338425; - } else { - result[0] += -0.007993841; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 98))) { - result[0] += 0.039039984; - } else { - result[0] += 0.017829265; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 122))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - result[0] += -0.008180471; - } else { - result[0] += -0.017685762; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 172))) { - result[0] += 0.024588756; - } else { - result[0] += 0.00014103504; - } - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 120))) { - result[0] += -0.017513642; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 228))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 204))) { - result[0] += 0.053347778; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { - result[0] += 0.036463372; - } else { - result[0] += 0.019543491; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - result[0] += 0.06796604; - } else { - result[0] += 0.011818393; - } - } else { - result[0] += -0.003918262; - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 260))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 252))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 224))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 108))) { - result[0] += 0.055681136; - } else { - result[0] += 0.03390036; - } - } else { - result[0] += 0.014793222; - } - } else { - result[0] += 0.06464329; - } - } else { - result[0] += 0.1336614; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 214))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 200))) { - result[0] += -0.010694483; - } else { - result[0] += 0.0089774085; - } - } else { - result[0] += 0.09665075; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 214))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - result[0] += 0.005944577; - } else { - result[0] += 0.039748937; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { - result[0] += -0.036054015; - } else { - result[0] += -0.03007635; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { - result[0] += 0.07873906; - } else { - result[0] += 0.040747557; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 280))) { - result[0] += 0.10715278; - } else { - result[0] += 0.07093804; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 272))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 228))) { - result[0] += 0.0400591; - } else { - result[0] += 0.070340686; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { - result[0] += 0.006239193; - } else { - result[0] += 0.04050245; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - result[0] += 0.0143945515; - } else { - result[0] += -0.01724617; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { - result[0] += 0.06920057; - } else { - result[0] += 0.012906248; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { - result[0] += -0.028011957; - } else { - result[0] += -0.054441918; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 222))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += -0; - } else { - result[0] += -0.015584071; - } - } else { - result[0] += 0.04294205; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 176))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - result[0] += 0.0010215238; - } else { - result[0] += 0.018780788; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 36))) { - result[0] += 0.038763847; - } else { - result[0] += 0.023370415; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 224))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - result[0] += 0.032697503; - } else { - result[0] += 0.13195097; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += 0.074951015; - } else { - result[0] += 0.10605689; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += 0.12042389; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 6))) { - result[0] += 0.07597848; - } else { - result[0] += 0.059126675; - } - } else { - result[0] += 0.087247975; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - result[0] += -0.011189851; - } else { - result[0] += -0.023633793; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += -0.009847272; - } else { - result[0] += 0.0011721178; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - result[0] += -0.033823837; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - result[0] += -0.028593678; - } else { - result[0] += -0.023168823; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { - result[0] += 0.048958402; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 226))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { - result[0] += -0.0030530605; - } else { - result[0] += 0.009326304; - } - } else { - result[0] += 0.015771423; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - result[0] += -0.019048717; - } else { - result[0] += -0.00045981476; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - result[0] += 0.036297392; - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - result[0] += 0.004649827; - } else { - result[0] += 0.024824895; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { - result[0] += 0.0042260056; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - result[0] += 0.035964742; - } else { - result[0] += 0.05283689; - } - } - } else { - result[0] += 0.072465; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - result[0] += -0.012068967; - } else { - result[0] += -0.027094675; - } - } else { - result[0] += -0.066233106; - } - } else { - result[0] += 0.025606213; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { - result[0] += 0.0134207625; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - result[0] += -0.051151592; - } else { - result[0] += -0.013505876; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += 0.033789955; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 226))) { - result[0] += -0.0063351872; - } else { - result[0] += 0.01952872; - } - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { - result[0] += 0.017679365; - } else { - result[0] += 0.0012839021; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { - result[0] += 0.01832609; - } else { - result[0] += 0.031185871; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { - result[0] += 0.07488849; - } else { - result[0] += 0.046187967; - } - } else { - result[0] += 0.09955382; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { - result[0] += 0.069774024; - } else { - result[0] += 0.10838503; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { - result[0] += 0.05327609; - } else { - result[0] += 0.077984296; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - result[0] += 0.0047698705; - } else { - result[0] += 0.017100412; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - result[0] += -0.0088116955; - } else { - result[0] += 0.0046756053; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { - result[0] += -0.021546138; - } else { - result[0] += -0.06706627; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - result[0] += 0.0009891371; - } else { - result[0] += -0.015693253; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 76))) { - result[0] += -0.033967104; - } else { - result[0] += -0.010871164; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - result[0] += -0.01983557; - } else { - result[0] += -0.033842903; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += 3.485963e-05; - } else { - result[0] += 0.029934336; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { - result[0] += -0.009020819; - } else { - result[0] += -0.0016374525; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { - result[0] += -0.00035558367; - } else { - result[0] += 0.031120656; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { - result[0] += 0.0042784135; - } else { - result[0] += -0.003890027; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - result[0] += 0.03780892; - } else { - result[0] += 0.020871228; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { - result[0] += 0.032693; - } else { - result[0] += 0.016958345; - } - } else { - result[0] += 0.056264985; - } - } else { - result[0] += 0.061108418; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - result[0] += 0.016505312; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 58))) { - result[0] += -0.012738267; - } else { - result[0] += -0.020164208; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - result[0] += 0.0137371225; - } else { - result[0] += -0.010237254; - } - } - } else { - result[0] += -0.037308313; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - result[0] += 0.0016659134; - } else { - result[0] += 0.022526134; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { - result[0] += 0.02430934; - } else { - result[0] += 0.010316737; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - result[0] += 0.03054412; - } else { - result[0] += 0.013778204; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { - result[0] += 0.062106986; - } else { - result[0] += 0.08695674; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += 0.097699985; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { - result[0] += 0.048410773; - } else { - result[0] += 0.061695654; - } - } else { - result[0] += 0.0699871; - } - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { - result[0] += 0.017755916; - } else { - result[0] += -0.0076434105; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 80))) { - result[0] += -0.013907449; - } else { - result[0] += -0.028985953; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += -0.03517434; - } else { - result[0] += 0.02803059; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - result[0] += -0.005040298; - } else { - result[0] += 0.029063394; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 18))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 252))) { - result[0] += -0.0004860425; - } else { - result[0] += -0.00823578; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - result[0] += 0.020955933; - } else { - result[0] += -0.005362306; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - result[0] += -0.022735301; - } else { - result[0] += -0.006933318; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - result[0] += 0.008230773; - } else { - result[0] += -0.011880973; - } - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 232))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 34))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 192))) { - result[0] += 0.00536417; - } else { - result[0] += -0.020807503; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { - result[0] += 0.029748295; - } else { - result[0] += 0.041834693; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 196))) { - result[0] += -0.00087343634; - } else { - result[0] += -0.026689067; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 182))) { - result[0] += -0.0014600045; - } else { - result[0] += 0.013319497; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 256))) { - result[0] += 0.0012430011; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { - result[0] += -0.028792778; - } else { - result[0] += -0.024853468; - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { - result[0] += -0.009469046; - } else { - result[0] += 0.0108768735; - } - } else { - result[0] += -0.025166774; - } - } else { - result[0] += -0.061156314; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { - result[0] += -0.011232008; - } else { - result[0] += -0.025154117; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - result[0] += 0.008252529; - } else { - result[0] += 0.046288077; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 220))) { - result[0] += 0.040085025; - } else { - result[0] += -0.008784636; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - result[0] += -0.019954626; - } else { - result[0] += 0.0048681814; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += 0.007502618; - } else { - result[0] += -0.0053296923; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { - result[0] += 0.013098051; - } else { - result[0] += 0.022515437; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 222))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 234))) { - result[0] += 0.02392622; - } else { - result[0] += 0.006717711; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 92))) { - result[0] += 0.036931966; - } else { - result[0] += 0.024680862; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { - result[0] += 0.049618512; - } else { - result[0] += 0.030690536; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { - result[0] += 0.087034844; - } else { - result[0] += 0.06434413; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.076431125; - } else { - result[0] += 0.050094455; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.08487356; - } else { - result[0] += 0.016335858; - } - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { - result[0] += 0.0068731843; - } else { - result[0] += -0.027962524; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { - result[0] += -0.005616961; - } else { - result[0] += -0.020151896; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 160))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - result[0] += -0.011980721; - } else { - result[0] += -0.022607153; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - result[0] += 0.045720205; - } else { - result[0] += 0.011621847; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 58))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - result[0] += 0.02517539; - } else { - result[0] += -0.0003533575; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { - result[0] += 0.00938051; - } else { - result[0] += -0.011884357; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += -0.0057867505; - } else { - result[0] += -0.025664538; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { - result[0] += -0.008385401; - } else { - result[0] += -0.0017240072; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 266))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - result[0] += -0.000107964406; - } else { - result[0] += 0.0060515343; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 244))) { - result[0] += -0.005519016; - } else { - result[0] += 0.0014766084; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.02811285; - } else { - result[0] += 0.013522627; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { - result[0] += -0; - } else { - result[0] += 0.015318493; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { - result[0] += 0.02693863; - } else { - result[0] += 0.042594276; - } - } - } else { - result[0] += 0.05915534; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - result[0] += -0.015769325; - } else { - result[0] += 0.009167842; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - result[0] += -0.066482425; - } else { - result[0] += -0.029134443; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 58))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - result[0] += -0.010345658; - } else { - result[0] += -0.0004427325; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - result[0] += 0.00778659; - } else { - result[0] += -0.052467596; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += 0.025486384; - } else { - result[0] += 0.011895447; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - result[0] += 0.022794057; - } else { - result[0] += 0.048023786; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - result[0] += 0.024896143; - } else { - result[0] += 0.010721759; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += 0.04730309; - } else { - result[0] += 0.07061689; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += 0.079474784; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 6))) { - result[0] += 0.050025214; - } else { - result[0] += 0.03944496; - } - } else { - result[0] += 0.05712953; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 54))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += 0.0070698797; - } else { - result[0] += -0.009966139; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - result[0] += 0.015061869; - } else { - result[0] += -0.0023575744; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - result[0] += -0.021192541; - } else { - result[0] += -0.00409846; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 156))) { - result[0] += -0.01823308; - } else { - result[0] += 0.00014080759; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 206))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 100))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - result[0] += 0.02206879; - } else { - result[0] += -0.00015570642; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - result[0] += -0.015682366; - } else { - result[0] += -0.006868744; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { - result[0] += 0.0135331275; - } else { - result[0] += -0.023557609; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { - result[0] += 0.04654594; - } else { - result[0] += 0.0058658714; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - result[0] += -0.016888324; - } else { - result[0] += -0.00063805765; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - result[0] += 0.029826907; - } else { - result[0] += 0.007284993; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - result[0] += 0.001150948; - } else { - result[0] += 0.0069423555; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += -0.0042433054; - } else { - result[0] += 0.011705535; - } - } else { - result[0] += 0.025982574; - } - } else { - result[0] += 0.048386928; - } - } - } - } - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 206))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += -0.042859327; - } else { - result[0] += -0.0044231373; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { - result[0] += 0.049170107; - } else { - result[0] += 0.035520416; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { - result[0] += -0.0009743745; - } else { - result[0] += 0.009931285; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { - result[0] += -0.0017889539; - } else { - result[0] += -0.007930504; - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 226))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { - result[0] += 0.0063184327; - } else { - result[0] += -0.015213072; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += 0.014672543; - } else { - result[0] += 0.026392935; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { - result[0] += -0.037449803; - } else { - result[0] += -0.053623743; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { - result[0] += 0.002925502; - } else { - result[0] += -0.010573565; - } - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 264))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 152))) { - result[0] += 0.00568271; - } else { - result[0] += -0.004481957; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 212))) { - result[0] += 0.053150166; - } else { - result[0] += 0.014763187; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 194))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { - result[0] += -0.008710187; - } else { - result[0] += 0.075820915; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - result[0] += -0.023372917; - } else { - result[0] += -0.012099934; - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { - result[0] += 0.037964396; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 206))) { - result[0] += 0.0036865615; - } else { - result[0] += 0.021090202; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { - result[0] += -0.025623156; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 200))) { - result[0] += -0.004627872; - } else { - result[0] += 0.004686881; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 262))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { - result[0] += 0.0714253; - } else { - result[0] += 0.048554998; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 230))) { - result[0] += 0.02269533; - } else { - result[0] += 0.014301536; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 220))) { - result[0] += 0.0025220858; - } else { - result[0] += 0.011460328; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 56))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - result[0] += 0.019217266; - } else { - result[0] += 0.03249597; - } - } else { - result[0] += 0.035739582; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { - result[0] += -0.023269765; - } else { - result[0] += -0.01868113; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 180))) { - result[0] += -0.0065037594; - } else { - result[0] += -0.022253536; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { - result[0] += 0.0036995602; - } else { - result[0] += 0.0520568; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 146))) { - result[0] += 0.02432552; - } else { - result[0] += -0.01594592; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { - result[0] += 0.0497043; - } else { - result[0] += 0.022733098; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.055147793; - } else { - result[0] += 0.041854013; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - result[0] += 0.0019326921; - } else { - result[0] += -0.013451728; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 280))) { - result[0] += 0.029732933; - } else { - result[0] += 0.009648888; - } - } - } - } - } - } - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 206))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += -0.002109048; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 92))) { - result[0] += 0.04531033; - } else { - result[0] += 0.028058738; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { - result[0] += -0.0014130161; - } else { - result[0] += 0.009621711; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { - result[0] += -0.0014670437; - } else { - result[0] += -0.007695043; - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 226))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 156))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 192))) { - result[0] += 0.007604044; - } else { - result[0] += -0.0070991814; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 220))) { - result[0] += 0.020077487; - } else { - result[0] += 0.006828181; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { - result[0] += -0.03365531; - } else { - result[0] += -0.049779564; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { - result[0] += 0.0017921542; - } else { - result[0] += -0.009079668; - } - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 260))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 152))) { - result[0] += 0.005504231; - } else { - result[0] += -0.0039019831; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 212))) { - result[0] += 0.044337142; - } else { - result[0] += 0.0070573166; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 194))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { - result[0] += 0.028603448; - } else { - result[0] += -0.0078216465; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { - result[0] += 0.015729161; - } else { - result[0] += -0.016172249; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { - result[0] += 0.03005304; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { - result[0] += 0.016138185; - } else { - result[0] += 0.033333454; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { - result[0] += -0.02334422; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - result[0] += 0.0137236165; - } else { - result[0] += 0.003765919; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 260))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.06424896; - } else { - result[0] += 0.045115866; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { - result[0] += 0.020254442; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { - result[0] += 0.003927156; - } else { - result[0] += -0.00030379518; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 56))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - result[0] += 0.01626153; - } else { - result[0] += 0.030240763; - } - } else { - result[0] += 0.031179471; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { - result[0] += -0.019889602; - } else { - result[0] += -0.006115454; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { - result[0] += 0.02490984; - } else { - result[0] += 0.0096712075; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { - result[0] += -0.016214207; - } else { - result[0] += 0.0054753; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 162))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.04828751; - } else { - result[0] += 0.02183148; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - result[0] += 0.095508024; - } else { - result[0] += 0.037357938; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { - result[0] += 0.024429439; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += -0.032644305; - } else { - result[0] += -0.007228152; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 92))) { - result[0] += -0; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - result[0] += -0.0005319937; - } else { - result[0] += 0.04273183; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += -0; - } else { - result[0] += -0.015805844; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 220))) { - result[0] += 0.028671984; - } else { - result[0] += -0; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { - result[0] += -0.015004277; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { - result[0] += -0.043184254; - } else { - result[0] += -0.015434514; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { - result[0] += 0.027698422; - } else { - result[0] += 0.019098751; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { - result[0] += 0.0024283626; - } else { - result[0] += 0.019441115; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 58))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += 0.07004071; - } else { - result[0] += 0.022275856; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { - result[0] += -0.0013316347; - } else { - result[0] += 0.012960051; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 224))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.06203411; - } else { - result[0] += 0.035423633; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.057469275; - } else { - result[0] += 0.009981114; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 212))) { - result[0] += 0.023559755; - } else { - result[0] += 0.035965245; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { - result[0] += 0.058553632; - } else { - result[0] += 0.04478031; - } - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { - result[0] += 0.01235016; - } else { - result[0] += -0.0039201183; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 80))) { - result[0] += -0.007764698; - } else { - result[0] += -0.019835753; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += -0.006487711; - } else { - result[0] += 0.018991722; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - result[0] += -0.0039943634; - } else { - result[0] += 0.024546368; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { - result[0] += -0.0147830695; - } else { - result[0] += -0.008563632; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - result[0] += -0.030682612; - } else { - result[0] += 0.017968642; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 166))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - result[0] += 0.068739615; - } else { - result[0] += 0.00021970407; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - result[0] += -0.0028976346; - } else { - result[0] += -0.009871369; - } - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 236))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { - result[0] += -0.021564588; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { - result[0] += 0.0040140515; - } else { - result[0] += 0.016122099; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 210))) { - result[0] += -0.0033681232; - } else { - result[0] += 0.0073165386; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { - result[0] += -0.025544493; - } else { - result[0] += -0.01873306; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 230))) { - result[0] += -0.019029168; - } else { - result[0] += 0.004258202; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 254))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { - result[0] += -0.00011749373; - } else { - result[0] += -0.010187042; - } - } else { - result[0] += 0.037634984; - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - result[0] += -0.0064992644; - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 146))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { - result[0] += 0.022600017; - } else { - result[0] += -0.0016712642; - } - } else { - result[0] += 0.025729222; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 20))) { - result[0] += -0.007915151; - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += 0.0005776281; - } else { - result[0] += 0.031780552; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { - result[0] += -0.017479556; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 178))) { - result[0] += -0.04499294; - } else { - result[0] += -0.01988611; - } - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { - result[0] += 0.008722408; - } else { - result[0] += 0.032495603; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += -0.0015038172; - } else { - result[0] += 0.009706586; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.08136292; - } else { - result[0] += 0.015997507; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { - result[0] += 0.03901923; - } else { - result[0] += 0.0564677; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - result[0] += 0.0416153; - } else { - result[0] += 0.026928617; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { - result[0] += 0.041233912; - } else { - result[0] += 0.05209862; - } - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { - result[0] += 0.00972145; - } else { - result[0] += 0.04575323; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - result[0] += 0.02476694; - } else { - result[0] += -0.03629762; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.021036565; - } else { - result[0] += -0.045760524; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 224))) { - result[0] += 0.007162188; - } else { - result[0] += -0.018060943; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 22))) { - result[0] += -0.007815191; - } else { - result[0] += -0.017675815; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { - result[0] += -0.0029981257; - } else { - result[0] += 0.020084158; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { - result[0] += 0.02050746; - } else { - result[0] += -0.0026271623; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - result[0] += -0.011671978; - } else { - result[0] += -0.0055001257; - } - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 228))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { - result[0] += -0.019566214; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { - result[0] += 0.0054924428; - } else { - result[0] += -0.00093082106; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - result[0] += 0.0126124; - } else { - result[0] += 0.032813326; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 272))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - result[0] += -0.023916144; - } else { - result[0] += -0.017258545; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.05414641; - } else { - result[0] += 0.000113278526; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 254))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { - result[0] += -0.00055813155; - } else { - result[0] += -0.008351603; - } - } else { - result[0] += 0.032734703; - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - result[0] += -0.0139853405; - } else { - result[0] += 0.003684853; - } - } else { - result[0] += 0.008061817; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - result[0] += -0.06050358; - } else { - result[0] += -0.0023646136; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.028764948; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += -0.028082406; - } else { - result[0] += -0.0058664638; - } - } else { - result[0] += 0.023923343; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 170))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - result[0] += 0.009201097; - } else { - result[0] += -0.010257622; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - result[0] += 0.012351765; - } else { - result[0] += 0.08077949; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { - result[0] += 0.037493166; - } else { - result[0] += 0.052167382; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { - result[0] += 0.036415916; - } else { - result[0] += 0.046912543; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { - result[0] += 0.023727143; - } else { - result[0] += 0.036457557; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { - result[0] += -0.002685583; - } else { - result[0] += -0.006960124; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { - result[0] += 0.04494014; - } else { - result[0] += 0.0043056607; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 220))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - result[0] += -0.0039729676; - } else { - result[0] += -0.014122757; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { - result[0] += -0.025312701; - } else { - result[0] += -0.014867464; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - result[0] += 0.015924158; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 190))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 184))) { - result[0] += -0.0075506503; - } else { - result[0] += 0.010475333; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - result[0] += -0.027276382; - } else { - result[0] += -0.004030099; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 98))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { - result[0] += 0.014477782; - } else { - result[0] += -0.015422763; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { - result[0] += 0.03197563; - } else { - result[0] += 0.020037709; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { - result[0] += -0.019650823; - } else { - result[0] += 0.002236197; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 150))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { - result[0] += -0.02055483; - } else { - result[0] += -0.0014581191; - } - } else { - result[0] += -0.016465666; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 242))) { - result[0] += -0.05338929; - } else { - result[0] += -0.014811342; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { - result[0] += 0.01168551; - } else { - result[0] += 0.0010410798; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - result[0] += -0.005663588; - } else { - result[0] += 0.012237726; - } - } else { - result[0] += -0.015190408; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { - result[0] += -0.048400734; - } else { - result[0] += -0.01567449; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { - result[0] += 0.034811612; - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += -0.005241947; - } else { - result[0] += 0.0036905694; - } - } else { - result[0] += 0.027789101; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 172))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 168))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - result[0] += -0.0023323249; - } else { - result[0] += 0.008576729; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - result[0] += -0.02920859; - } else { - result[0] += -0.005879428; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 236))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { - result[0] += 0.01825313; - } else { - result[0] += 0.01146; - } - } else { - result[0] += -0.0015520846; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - result[0] += 0.050887883; - } else { - result[0] += 0.0008823246; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { - result[0] += 0.04027082; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { - result[0] += 0.02058533; - } else { - result[0] += 0.039607793; - } - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += 0.0065161586; - } else { - result[0] += -0.007997109; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { - result[0] += 0.005459733; - } else { - result[0] += -0.0032214918; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { - result[0] += -0.015889943; - } else { - result[0] += -0.054882165; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 160))) { - result[0] += 0.013258442; - } else { - result[0] += -0.008985414; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { - result[0] += 0.03941143; - } else { - result[0] += 0.01124495; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { - result[0] += -0.01534603; - } else { - result[0] += 0.0053413953; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { - result[0] += -0.017410088; - } else { - result[0] += 0.013132173; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { - result[0] += -0.0060942764; - } else { - result[0] += -0.0017843047; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { - result[0] += 0.035340164; - } else { - result[0] += 0.0032424498; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.02138096; - } else { - result[0] += 0.011583105; - } - } - } else { - result[0] += -0.0026775673; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { - result[0] += 0.0032441877; - } else { - result[0] += 0.013359073; - } - } else { - result[0] += 0.030153606; - } - } else { - result[0] += 0.036456186; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - result[0] += -0.0117252795; - } else { - result[0] += 0.0043645753; - } - } else { - result[0] += -0.041562933; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - result[0] += 0.0008401942; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - result[0] += -0.01901227; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { - result[0] += 0.014339037; - } else { - result[0] += -0.00501071; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 16))) { - result[0] += 0.012573126; - } else { - result[0] += 0.0028887743; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 38))) { - result[0] += 0.046177443; - } else { - result[0] += 0.015051349; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { - result[0] += 0.010199251; - } else { - result[0] += -0.039779864; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { - result[0] += -0.004799234; - } else { - result[0] += 0.008129753; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += 0.04275175; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - result[0] += -0.008011124; - } else { - result[0] += 0.002239405; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 268))) { - result[0] += 0.017378718; - } else { - result[0] += 0.03415826; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { - result[0] += 0.02640838; - } else { - result[0] += 0.039306883; - } - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += 0.0055097975; - } else { - result[0] += -0.007307568; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += 0.0022932815; - } else { - result[0] += -0.0064788023; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { - result[0] += -0.014033759; - } else { - result[0] += -0.049122956; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { - result[0] += 0.005159875; - } else { - result[0] += -0.009883177; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { - result[0] += -0.013826482; - } else { - result[0] += 0.022371221; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { - result[0] += -0.0141291395; - } else { - result[0] += 0.004760613; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 132))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { - result[0] += -0.007064409; - } else { - result[0] += 0.023824794; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - result[0] += -0.00016228954; - } else { - result[0] += -0.007653934; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 208))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { - result[0] += 0.004959669; - } else { - result[0] += -0.001015551; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { - result[0] += 0.027703479; - } else { - result[0] += 0.0036117423; - } - } - } else { - result[0] += -0.0026553113; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { - result[0] += 0.013676906; - } else { - result[0] += -0.0007326856; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - result[0] += -0; - } else { - result[0] += 0.030973986; - } - } - } else { - result[0] += 0.029461041; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 8))) { - result[0] += -0.009251536; - } else { - result[0] += 0.009845628; - } - } else { - result[0] += -0.04083568; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.029976163; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - result[0] += -0.027244702; - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { - result[0] += -0.004565258; - } else { - result[0] += 0.025788173; - } - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 254))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - result[0] += 0.011977387; - } else { - result[0] += 0.00021163402; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { - result[0] += 0.038581323; - } else { - result[0] += 0.013672948; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { - result[0] += 0.0043165404; - } else { - result[0] += -0.035845544; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { - result[0] += -0.0024726556; - } else { - result[0] += 0.008141091; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += 0.03638802; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { - result[0] += 0.0001506592; - } else { - result[0] += 0.009602265; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { - result[0] += 0.022467962; - } else { - result[0] += 0.03540038; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.06481004; - } else { - result[0] += 0.017584896; - } - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 140))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 104))) { - result[0] += 0.045290753; - } else { - result[0] += -0.01791346; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - result[0] += -0.058552768; - } else { - result[0] += 0.0349788; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 256))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 198))) { - result[0] += 0.0336964; - } else { - result[0] += 0.0018466607; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 176))) { - result[0] += -0.0049308436; - } else { - result[0] += -0.012199649; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 100))) { - result[0] += -0.007085; - } else { - result[0] += -0.016483508; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - result[0] += 0.006847045; - } else { - result[0] += -0.0070480793; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { - result[0] += -0.0041096415; - } else { - result[0] += 0.0021135767; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { - result[0] += -0.0060591055; - } else { - result[0] += -0.0011625281; - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 138))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - result[0] += 0.02211509; - } else { - result[0] += 0.060023166; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { - result[0] += -0.013461961; - } else { - result[0] += 0.04670935; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - result[0] += -0; - } else { - result[0] += 0.025248567; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 154))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 148))) { - result[0] += -0.008415373; - } else { - result[0] += 0.013447831; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 138))) { - result[0] += -0.016349984; - } else { - result[0] += -0.030504474; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += 0.00021573504; - } else { - result[0] += -0.013515745; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { - result[0] += 8.818205e-05; - } else { - result[0] += 0.010008925; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 18))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - result[0] += 0.0037289502; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { - result[0] += 0.054951753; - } else { - result[0] += -0.0039979555; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - result[0] += -0.00068969437; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - result[0] += -0.012617968; - } else { - result[0] += -0.027266933; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 160))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 48))) { - result[0] += 0.0068334453; - } else { - result[0] += -0.0039404687; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { - result[0] += -0; - } else { - result[0] += 0.039604023; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 178))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 36))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { - result[0] += -0.0074695237; - } else { - result[0] += 0.0059394534; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 218))) { - result[0] += 0.02302828; - } else { - result[0] += -0.047223628; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 156))) { - result[0] += -0.016071025; - } else { - result[0] += 0.0044617057; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - result[0] += -0.0016089712; - } else { - result[0] += 0.0069275023; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.030206159; - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { - result[0] += 0.010797313; - } else { - result[0] += -0.002882598; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { - result[0] += 0.020259693; - } else { - result[0] += 0.0355315; - } - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 192))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 78))) { - result[0] += -0.0044226176; - } else { - result[0] += -0.02530554; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - result[0] += 0.008079353; - } else { - result[0] += -0.003107704; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { - result[0] += -0.026262758; - } else { - result[0] += -0.013569482; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { - result[0] += -0.007361694; - } else { - result[0] += 0.01589146; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 186))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - result[0] += 0.0066897767; - } else { - result[0] += -0.03870764; - } - } else { - result[0] += 0.022245135; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 156))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 46))) { - result[0] += 0.0011395493; - } else { - result[0] += -0.0057673557; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { - result[0] += 0.022744471; - } else { - result[0] += 0.008478357; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.028410172; - } else { - result[0] += 0.014004901; - } - } else { - result[0] += 0.0010523595; - } - } else { - result[0] += 0.02651517; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 176))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { - result[0] += -0.0015872531; - } else { - result[0] += -0.017748317; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += -0.0032637853; - } else { - result[0] += 0.010829237; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 242))) { - result[0] += 0.034405578; - } else { - result[0] += 0.00891859; - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 20))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 254))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { - result[0] += 0.007226427; - } else { - result[0] += -0.012499331; - } - } else { - result[0] += 0.04246553; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 88))) { - result[0] += 0.002265221; - } else { - result[0] += -0.0032362868; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - result[0] += -0.011394487; - } else { - result[0] += -0.0008440514; - } - } - } else { - result[0] += -0.028067807; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { - result[0] += 0.08105516; - } else { - result[0] += 0.018269602; - } - } else { - result[0] += 0.015147194; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { - result[0] += 0.014643987; - } else { - result[0] += 0.00034420882; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { - result[0] += 0.019848578; - } else { - result[0] += 0.0055640773; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.0281445; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { - result[0] += 0.016479755; - } else { - result[0] += 0.023855714; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 252))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - result[0] += 0.0043632514; - } else { - result[0] += -0.0032153563; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += -0.0008953023; - } else { - result[0] += -0.012973676; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 224))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { - result[0] += 0.06142653; - } else { - result[0] += 0.014740917; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - result[0] += 0.036935415; - } else { - result[0] += -0.0027422463; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 206))) { - result[0] += -0.043081205; - } else { - result[0] += -0.020138515; - } - } else { - result[0] += -0.012991915; - } - } else { - result[0] += 0.005466835; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { - result[0] += -0.00065770803; - } else { - result[0] += 0.020950794; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 98))) { - result[0] += -0.0020690185; - } else { - result[0] += 0.0022426536; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.01779854; - } else { - result[0] += 0.007740788; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - result[0] += 0.010289395; - } else { - result[0] += 0.022736436; - } - } else { - result[0] += 0.02622193; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { - result[0] += -0.0030147363; - } else { - result[0] += -0.01984936; - } - } else { - result[0] += -0.014292531; - } - } else { - result[0] += 0.019834789; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { - result[0] += 0.012920295; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { - result[0] += 0.07497602; - } else { - result[0] += 0.01954972; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { - result[0] += 0.010222583; - } else { - result[0] += 0.00049927505; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - result[0] += -0.0032804606; - } else { - result[0] += 0.0070060194; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.024341205; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { - result[0] += 0.015133323; - } else { - result[0] += 0.021515321; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += 0.0021268248; - } else { - result[0] += -0.004917339; - } - } else { - result[0] += 0.020086113; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.023110032; - } else { - result[0] += -0.008982231; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 206))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - result[0] += -0.01757216; - } else { - result[0] += -5.0931576e-05; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - result[0] += 0.017951632; - } else { - result[0] += 0.0057268315; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 62))) { - result[0] += 0.0030992; - } else { - result[0] += -0.010930783; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - result[0] += -0.00038469338; - } else { - result[0] += -0.004318268; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { - result[0] += -0; - } else { - result[0] += 0.017621709; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - result[0] += 0.0036806122; - } else { - result[0] += -4.4085446e-05; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.021112496; - } else { - result[0] += 0.007883241; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - result[0] += 0.007880076; - } else { - result[0] += 0.02039106; - } - } else { - result[0] += 0.023736782; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 14))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - result[0] += -0; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { - result[0] += 0.022109; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { - result[0] += -0.011896599; - } else { - result[0] += -0.02695978; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 28))) { - result[0] += 0.017046606; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { - result[0] += -0.022864131; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { - result[0] += 0.01655387; - } else { - result[0] += -0.0029186176; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - result[0] += 0.006224805; - } else { - result[0] += -0.0006160491; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { - result[0] += 0.03912334; - } else { - result[0] += 0.0064465962; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - result[0] += 0.03801068; - } else { - result[0] += -0; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { - result[0] += -0.0056696637; - } else { - result[0] += 0.002394879; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { - result[0] += 0.0026905416; - } else { - result[0] += 0.014434603; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 18))) { - result[0] += -0.0066401684; - } else { - result[0] += -0.02399191; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 216))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - result[0] += 0.01420292; - } else { - result[0] += 0.007666865; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 258))) { - result[0] += 0.015625248; - } else { - result[0] += 0.035386283; - } - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 168))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 164))) { - result[0] += -0.0026949244; - } else { - result[0] += 0.010536998; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 172))) { - result[0] += -0.02333055; - } else { - result[0] += -0.004258865; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 190))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 256))) { - result[0] += 0.010322237; - } else { - result[0] += -0.004513755; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 194))) { - result[0] += -0.013793135; - } else { - result[0] += 0.00041879248; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { - result[0] += -0.0012282294; - } else { - result[0] += 0.012586914; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { - result[0] += -0.013370514; - } else { - result[0] += -0.009561422; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.020673957; - } else { - result[0] += 0.01021281; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += 0.029428396; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - result[0] += -0.0008754981; - } else { - result[0] += -0.0100570135; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { - result[0] += 0.016708953; - } else { - result[0] += 0.009689736; - } - } else { - result[0] += 0.00040766338; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { - result[0] += 0.024215741; - } else { - result[0] += 0.012051038; - } - } - } - } - } - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - result[0] += 0.0013062642; - } else { - result[0] += 0.019394014; - } - } else { - result[0] += -0.0104289735; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 48))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += -0.05682643; - } else { - result[0] += 0.026358023; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 126))) { - result[0] += 0.0067068855; - } else { - result[0] += -0.0033787123; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += -0.017422771; - } else { - result[0] += -0.013566106; - } - } else { - result[0] += 0.05098955; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 264))) { - result[0] += 0.0021389506; - } else { - result[0] += 0.022104675; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 196))) { - result[0] += -0.0034482577; - } else { - result[0] += -0.04897159; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += 0.04547104; - } else { - result[0] += -0.0036854; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.031185264; - } else { - result[0] += -0.00029240636; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - result[0] += 0.048903078; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { - result[0] += -0.012713486; - } else { - result[0] += -0.0045942473; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { - result[0] += -0.024384957; - } else { - result[0] += 0.010758282; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { - result[0] += 0.030021293; - } else { - result[0] += 0.017209774; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 220))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 88))) { - result[0] += -0.0025992983; - } else { - result[0] += 0.0014243874; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += -0.014005645; - } else { - result[0] += 0.00015922675; - } - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 184))) { - result[0] += -0.0023512202; - } else { - result[0] += 0.00030087968; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { - result[0] += 0.015333219; - } else { - result[0] += 0.050305735; - } - } else { - result[0] += 0.0038954068; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { - result[0] += 0.005740883; - } else { - result[0] += -0.011134521; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { - result[0] += -0.0102155125; - } else { - result[0] += -0.01861443; - } - } else { - result[0] += -0.008878123; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 36))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { - result[0] += 0.07084565; - } else { - result[0] += 0.019467566; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 106))) { - result[0] += -0.005714934; - } else { - result[0] += 0.0072955615; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { - result[0] += 0.0217909; - } else { - result[0] += 0.008551905; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - result[0] += 0.016447848; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { - result[0] += 0.028782597; - } else { - result[0] += -0.027478626; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { - result[0] += 0.007580962; - } else { - result[0] += 0.0014997128; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 214))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { - result[0] += -0.0022094585; - } else { - result[0] += -0.02451445; - } - } else { - result[0] += 0.024397848; - } - } else { - result[0] += -0.01616343; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - result[0] += 0.011657309; - } else { - result[0] += 0.0012069183; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += 0.040601656; - } else { - result[0] += 0.009451126; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - result[0] += -0.022806326; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - result[0] += 0.03292076; - } else { - result[0] += 0.001937497; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += 0.0035285496; - } else { - result[0] += -0.0058691064; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - result[0] += 0.012688639; - } else { - result[0] += 0.005491004; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { - result[0] += 0.0044394704; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 192))) { - result[0] += 0.012408857; - } else { - result[0] += 0.019069476; - } - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { - result[0] += 0.006046584; - } else { - result[0] += -0.015792418; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += 0.027976854; - } else { - result[0] += 0.012421743; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += 0.054596562; - } else { - result[0] += 0.01953054; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { - result[0] += -0.003245589; - } else { - result[0] += 0.013587843; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.021004315; - } else { - result[0] += -0.008656485; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - result[0] += 0.01555752; - } else { - result[0] += -0.0006588964; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { - result[0] += -0.0020559; - } else { - result[0] += -0.010479237; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += 0.021051032; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 220))) { - result[0] += 0.006872528; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { - result[0] += -0.00075457775; - } else { - result[0] += -0.008546843; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { - result[0] += 0.009004579; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 2))) { - result[0] += 0.013524537; - } else { - result[0] += 0.02246733; - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 202))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { - result[0] += -0.004806658; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - result[0] += -0.02193972; - } else { - result[0] += -0.0058797724; - } - } - } else { - result[0] += -0.023569103; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 264))) { - result[0] += 0.016511796; - } else { - result[0] += -0.0023302487; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += 0.004636476; - } else { - result[0] += 0.03744141; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { - result[0] += -0.0021920146; - } else { - result[0] += 0.0044151954; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += 0.013442422; - } else { - result[0] += 0.025949672; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 252))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { - result[0] += 0.012082203; - } else { - result[0] += 0.017159306; - } - } else { - result[0] += 0.018528607; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - result[0] += 0.012809316; - } else { - result[0] += 0.0020744025; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - result[0] += 0.02068119; - } else { - result[0] += 0.012666582; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.025207102; - } else { - result[0] += -0.0042135157; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - result[0] += 0.019998292; - } else { - result[0] += -0.011731009; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += 0.04935061; - } else { - result[0] += 0.00985283; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 2))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 10))) { - result[0] += -0.0114418; - } else { - result[0] += 0.001085467; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { - result[0] += 0.005336402; - } else { - result[0] += 0.040240493; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - result[0] += -0.0003519562; - } else { - result[0] += -0.016158544; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 22))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 186))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += -0.052682735; - } else { - result[0] += -0.024834817; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - result[0] += 0.0076021785; - } else { - result[0] += -0.009852809; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 24))) { - result[0] += 0.07371118; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - result[0] += -0.0062063127; - } else { - result[0] += -0.0016409116; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 138))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 96))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += -0.0056977295; - } else { - result[0] += -0.0020788263; - } - } else { - result[0] += 0.013674322; - } - } else { - result[0] += -0.012557744; - } - } else { - result[0] += 0.020858606; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 100))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { - result[0] += 0.004243218; - } else { - result[0] += -0.0012915577; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { - result[0] += 0.012041297; - } else { - result[0] += 0.0036917683; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 226))) { - result[0] += 0.021245888; - } else { - result[0] += 0.010770134; - } - } else { - result[0] += 0.026461909; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 252))) { - result[0] += 0.011941756; - } else { - result[0] += 0.01697006; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 190))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - result[0] += 0.0059637506; - } else { - result[0] += 0.0009332538; - } - } else { - result[0] += -0.01690738; - } - } else { - result[0] += 0.018049363; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { - result[0] += -0.003757357; - } else { - result[0] += -0.010965187; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 138))) { - result[0] += 0.054250848; - } else { - result[0] += -0.00039252196; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - result[0] += 0.01451879; - } else { - result[0] += 0.037112333; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 36))) { - result[0] += -0.017370671; - } else { - result[0] += -0.0013852807; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 222))) { - result[0] += 0.009491138; - } else { - result[0] += 0.03599734; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - result[0] += 0.0021581096; - } else { - result[0] += -0.0014275697; - } - } else { - result[0] += 0.009684578; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { - result[0] += 0.009782214; - } else { - result[0] += -0.0025362705; - } - } else { - result[0] += 0.015987474; - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { - result[0] += 0.031121805; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - result[0] += -0.0017203381; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 10))) { - result[0] += 0.030785752; - } else { - result[0] += -0.005102741; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - result[0] += -0.005013264; - } else { - result[0] += 0.027989835; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - result[0] += -0.024730321; - } else { - result[0] += -0.0032983057; - } - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 244))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += 0.009897011; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { - result[0] += -0.0021574632; - } else { - result[0] += 0.057048213; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { - result[0] += 0.010313834; - } else { - result[0] += 0.002621749; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.015189426; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { - result[0] += 0.009792722; - } else { - result[0] += 0.014417933; - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.0014002819; - } else { - result[0] += 0.006653227; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - result[0] += -0.023881285; - } else { - result[0] += -0; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - result[0] += 0.018696984; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.0133892; - } else { - result[0] += 0.008133677; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += 0.043852713; - } else { - result[0] += 0.0058281594; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 212))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 144))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 174))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { - result[0] += -0.0026800435; - } else { - result[0] += 0.0020921542; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - result[0] += 0.0018816863; - } else { - result[0] += -0.027228499; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - result[0] += -0.014789981; - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 150))) { - result[0] += -0.009289259; - } else { - result[0] += -0.0037157678; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 158))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - result[0] += -0.009539143; - } else { - result[0] += 0.006927042; - } - } else { - result[0] += 0.039872702; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 160))) { - result[0] += -0.039126202; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 166))) { - result[0] += -0.002670629; - } else { - result[0] += 0.001439321; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - result[0] += -5.299445e-05; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - result[0] += -0.019934397; - } else { - result[0] += -0.0012183964; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { - result[0] += -0.0013647849; - } else { - result[0] += 0.038733196; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - result[0] += 0.008902398; - } else { - result[0] += -0.00011572992; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 14))) { - result[0] += 3.4862278e-06; - } else { - result[0] += 0.012706413; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { - result[0] += 0.007615597; - } else { - result[0] += -0.0051007196; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 48))) { - result[0] += 0.015438475; - } else { - result[0] += 0.0018905745; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 246))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 230))) { - result[0] += 0.008593912; - } else { - result[0] += 0.014445068; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += -0.00014397338; - } else { - result[0] += 0.0083941175; - } - } - } else { - result[0] += -0.004308312; - } - } else { - result[0] += 0.046102088; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { - result[0] += -0.0013631996; - } else { - result[0] += -0.011290415; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 200))) { - result[0] += 0.00060525746; - } else { - result[0] += 0.013914026; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 248))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - result[0] += -0.023959834; - } else { - result[0] += -0.010296635; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { - result[0] += 0.008832153; - } else { - result[0] += 0.0023550882; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.015583684; - } else { - result[0] += 0.010421765; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { - result[0] += -0.00011256654; - } else { - result[0] += -0.02525649; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 232))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 230))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { - result[0] += 0.005643142; - } else { - result[0] += -0.00010354039; - } - } else { - result[0] += -0.015879504; - } - } else { - result[0] += 0.014167144; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 246))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.049543332; - } else { - result[0] += 0.0046836296; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { - result[0] += -0.017134476; - } else { - result[0] += -0.007789578; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += -0.008826947; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 250))) { - result[0] += -0.0007550689; - } else { - result[0] += 0.0071488456; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 238))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 106))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { - result[0] += -0.019138163; - } else { - result[0] += 0.008177842; - } - } else { - result[0] += 0.03202384; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { - result[0] += 0.03914841; - } else { - result[0] += 0.027094522; - } - } else { - result[0] += 0.0053101527; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += 0.0403822; - } else { - result[0] += 0.013740751; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { - result[0] += -0.030392349; - } else { - result[0] += 0.0028747516; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 80))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { - result[0] += -0.008877286; - } else { - result[0] += 0.012316696; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 118))) { - result[0] += 0.004029528; - } else { - result[0] += -0.00029411144; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { - result[0] += 0.011664437; - } else { - result[0] += 0.0006082279; - } - } else { - result[0] += -0.006005393; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - result[0] += 0.09773924; - } else { - result[0] += 0.02065113; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 200))) { - result[0] += 0.0035237612; - } else { - result[0] += 0.009829774; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 208))) { - result[0] += -0.007697291; - } else { - result[0] += 0.0015825549; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - result[0] += 0.0036124797; - } else { - result[0] += 0.014532133; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - result[0] += 0.0012666411; - } else { - result[0] += -0.03202522; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { - result[0] += 0.020200811; - } else { - result[0] += 0.004590917; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 28))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { - result[0] += 0.03727207; - } else { - result[0] += 0.016316103; - } - } else { - result[0] += -0.003320934; - } - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += 0.053239834; - } else { - result[0] += 0.015470129; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - result[0] += -0.0021254455; - } else { - result[0] += -0.009888625; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - result[0] += 0.062025126; - } else { - result[0] += -0.00554525; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { - result[0] += 0.057511933; - } else { - result[0] += -0.008578778; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { - result[0] += -0.046820056; - } else { - result[0] += -0.02672096; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 70))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - result[0] += -0.03445236; - } else { - result[0] += 0.01575507; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { - result[0] += -0.029678077; - } else { - result[0] += -0.0074849683; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - result[0] += 0.013723537; - } else { - result[0] += -0.0020312674; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { - result[0] += -0.004165123; - } else { - result[0] += -0.00078670814; - } - } - } - } - } - } - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += -0; - } else { - result[0] += -0.03194522; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { - result[0] += 0.025147652; - } else { - result[0] += 0.0062306984; - } - } - } else { - result[0] += -0.0008928859; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += 0.13331777; - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 206))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 144))) { - result[0] += 0.030818671; - } else { - result[0] += 0.011000312; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - result[0] += 0.0052034413; - } else { - result[0] += -0.002728651; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += 0.0031897593; - } else { - result[0] += 0.017273722; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { - result[0] += -0.02854721; - } else { - result[0] += 0.00037428603; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - result[0] += -0.0019407201; - } else { - result[0] += -0.007221583; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 62))) { - result[0] += 0.047634736; - } else { - result[0] += -0.017209576; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { - result[0] += 0.025280062; - } else { - result[0] += 0.012840076; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { - result[0] += 0.038194098; - } else { - result[0] += 0.008874618; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { - result[0] += -0.03347216; - } else { - result[0] += -0.0004954495; - } - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 202))) { - result[0] += -0.009764884; - } else { - result[0] += -0.027206961; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 202))) { - result[0] += 0.00784516; - } else { - result[0] += 0.0012178732; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 170))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 168))) { - result[0] += 0.024105137; - } else { - result[0] += -0.0019981433; - } - } else { - result[0] += 0.031363543; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - result[0] += -0.0072048977; - } else { - result[0] += -0.022911293; - } - } else { - result[0] += 0.010616104; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { - result[0] += 0.008845729; - } else { - result[0] += -0.0013868009; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 246))) { - result[0] += -0.0024586073; - } else { - result[0] += -0.0086732535; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 196))) { - result[0] += 0.01107409; - } else { - result[0] += -0.0011529119; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - result[0] += 0.013766219; - } else { - result[0] += 0.026060035; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 210))) { - result[0] += -0.004132199; - } else { - result[0] += -0.01266666; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 264))) { - result[0] += 0.011719443; - } else { - result[0] += -0.0067507224; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 90))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 188))) { - result[0] += 0.0155691; - } else { - result[0] += 0.010912938; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 198))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 188))) { - result[0] += 0.0051367334; - } else { - result[0] += -0.00038397807; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.044876117; - } else { - result[0] += 0.005380819; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 124))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 250))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - result[0] += -0.01634503; - } else { - result[0] += -0; - } - } else { - result[0] += -0.002735744; - } - } else { - result[0] += 0.012586943; - } - } else { - result[0] += -0.0009229357; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 106))) { - result[0] += -0.013541499; - } else { - result[0] += 0.016169539; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += -0; - } else { - result[0] += 0.026296902; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { - result[0] += 0.0010251206; - } else { - result[0] += 0.007284665; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 100))) { - result[0] += -0.0034584904; - } else { - result[0] += 0.0022243506; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { - result[0] += 0.008507351; - } else { - result[0] += 0.00037087366; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { - result[0] += -0.0015936692; - } else { - result[0] += -0.00584162; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.03250199; - } else { - result[0] += 0.014099632; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 174))) { - result[0] += 0.0007955474; - } else { - result[0] += 0.0064386777; - } - } - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { - result[0] += -0.032964613; - } else { - result[0] += 0.017446639; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { - result[0] += -0.015205093; - } else { - result[0] += -0; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - result[0] += 0.003139617; - } else { - result[0] += -0.014359586; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += -0.0014178219; - } else { - result[0] += 0.002840144; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 62))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - result[0] += 0.041497182; - } else { - result[0] += 0.001743556; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { - result[0] += -0.014737419; - } else { - result[0] += -0.0060345526; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 188))) { - result[0] += -0.0014039263; - } else { - result[0] += 0.009091877; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { - result[0] += -0.013735535; - } else { - result[0] += -0.0066661197; - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - result[0] += 0.05542008; - } else { - result[0] += 0.0056177597; - } - } else { - result[0] += 0.021063056; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += -0.008560927; - } else { - result[0] += 0.007372921; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - result[0] += 0.0033190027; - } else { - result[0] += 0.02229972; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 162))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { - result[0] += 0.0031475432; - } else { - result[0] += 0.01872689; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 42))) { - result[0] += -0.0027235162; - } else { - result[0] += -0.011199118; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { - result[0] += -0.013897911; - } else { - result[0] += -0.0017742496; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { - result[0] += 0.045032453; - } else { - result[0] += 0.0035365508; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { - result[0] += 0.015004215; - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += -0.02693589; - } else { - result[0] += 0.0030036382; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 236))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 220))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { - result[0] += -0.005196058; - } else { - result[0] += -0.0010462991; - } - } else { - result[0] += -0.036202352; - } - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += 0.009205636; - } else { - result[0] += -0.008914505; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - result[0] += 0.00077702984; - } else { - result[0] += -0.006057858; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 78))) { - result[0] += 0.011342842; - } else { - result[0] += 0.003530657; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - result[0] += 0.03748219; - } else { - result[0] += 0.014057839; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 102))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += -0.0016399727; - } else { - result[0] += -0.011802055; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += -0.0016713021; - } else { - result[0] += 0.0056822044; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - result[0] += 0.00386166; - } else { - result[0] += -0.0027170626; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 46))) { - result[0] += 0.019643234; - } else { - result[0] += 0.0028665168; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { - result[0] += 0.0005789458; - } else { - result[0] += 0.0143351955; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { - result[0] += 0.011239639; - } else { - result[0] += -0.016711237; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { - result[0] += 0.019091167; - } else { - result[0] += 0.0046307947; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.050568253; - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += 0.03797291; - } else { - result[0] += 0.007982335; - } - } - } else { - result[0] += 0.0035218038; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - result[0] += -0.016012253; - } else { - result[0] += 0.0023404376; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - result[0] += -0.001683798; - } else { - result[0] += 0.013443462; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 232))) { - result[0] += -0.0012087579; - } else { - result[0] += 0.009097977; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 192))) { - result[0] += -0.010690723; - } else { - result[0] += -0.001824428; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += 0.017058346; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 220))) { - result[0] += 0.004044805; - } else { - result[0] += -0.0013507138; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 242))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { - result[0] += 0.008160069; - } else { - result[0] += 0.015147629; - } - } else { - result[0] += 0.024462283; - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { - result[0] += -0.000107747815; - } else { - result[0] += 0.0067256675; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { - result[0] += -0.0076294737; - } else { - result[0] += -0.043650094; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 178))) { - result[0] += 0.034688678; - } else { - result[0] += -0.02312972; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { - result[0] += -0.002360337; - } else { - result[0] += 0.012530643; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - result[0] += -0.01826018; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { - result[0] += 0.0056892973; - } else { - result[0] += 0.03400869; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 80))) { - result[0] += -0.009709366; - } else { - result[0] += -0.00013415277; - } - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 156))) { - result[0] += 0.010154608; - } else { - result[0] += 0.001891119; - } - } else { - result[0] += 0.03333105; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { - result[0] += -0.022681896; - } else { - result[0] += -0.03837011; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { - result[0] += 0.007119199; - } else { - result[0] += -0.015321928; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 134))) { - result[0] += 0.08953688; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - result[0] += -0.022155154; - } else { - result[0] += 0.004006593; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - result[0] += 0.013605259; - } else { - result[0] += 0.027043974; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { - result[0] += -0.0115665; - } else { - result[0] += 0.007095266; - } - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += 0.035634317; - } else { - result[0] += 0.017916704; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - result[0] += -0.0043744547; - } else { - result[0] += -0.00026779916; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 236))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - result[0] += 0.0133640645; - } else { - result[0] += 0.004636778; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += 0.012312482; - } else { - result[0] += 0.06577807; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 132))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += 0.0068262727; - } else { - result[0] += -0.04068326; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { - result[0] += -0.0010328897; - } else { - result[0] += -0.005453972; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { - result[0] += -0.0008340759; - } else { - result[0] += -0.008702307; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 16))) { - result[0] += 0.040576342; - } else { - result[0] += 0.0024016355; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 226))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - result[0] += 0.029419197; - } else { - result[0] += 0.0010929945; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 156))) { - result[0] += -0.0012289739; - } else { - result[0] += -0.0070381216; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.0115409745; - } else { - result[0] += 0.0048038764; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 242))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 222))) { - result[0] += 0.023705222; - } else { - result[0] += 0.010082607; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.012382432; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 228))) { - result[0] += 0.004983134; - } else { - result[0] += -0; - } - } - } - } - } - } - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 208))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += 0.061853677; - } else { - result[0] += -0.0063631344; - } - } else { - result[0] += -0.036711987; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 28))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { - result[0] += 0.0031359557; - } else { - result[0] += -0.0016080218; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { - result[0] += 0.036603495; - } else { - result[0] += 0.015373263; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += -0.055269193; - } else { - result[0] += 0.019025035; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - result[0] += -0.022278577; - } else { - result[0] += 0.016397122; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.01897105; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { - result[0] += 0.00051737565; - } else { - result[0] += 0.0041289385; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += -0.036939483; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { - result[0] += 0.009198739; - } else { - result[0] += -0.013264345; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { - result[0] += 0.022775693; - } else { - result[0] += 0.007945242; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 156))) { - result[0] += -0.0018210205; - } else { - result[0] += 0.0037362184; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 128))) { - result[0] += -0.012762427; - } else { - result[0] += -0.0007420523; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - result[0] += 0.017958501; - } else { - result[0] += 0.0061608446; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { - result[0] += -0.0049462705; - } else { - result[0] += 0.0019659882; - } - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { - result[0] += 0.00022622973; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 250))) { - result[0] += 0.005413284; - } else { - result[0] += 0.030752031; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { - result[0] += 0.003555048; - } else { - result[0] += -0.0069270222; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { - result[0] += -0.008863601; - } else { - result[0] += -0.015783666; - } - } else { - result[0] += -0.0067091286; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 36))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { - result[0] += 0.062294442; - } else { - result[0] += 0.01808921; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { - result[0] += 0.08561305; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 190))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - result[0] += 0.0024828026; - } else { - result[0] += 0.018881498; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 204))) { - result[0] += -0.007572981; - } else { - result[0] += 0.001793948; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 126))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { - result[0] += -0.000932767; - } else { - result[0] += 0.006823505; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - result[0] += -0.036651295; - } else { - result[0] += -0.0058056363; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { - result[0] += -0.0055914307; - } else { - result[0] += 0.03341748; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { - result[0] += -0.00073259696; - } else { - result[0] += 0.010309592; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - result[0] += -0.019160004; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { - result[0] += 0.0046965308; - } else { - result[0] += 0.026486978; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 80))) { - result[0] += -0.008773739; - } else { - result[0] += -0.0007050743; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { - result[0] += 0.025857493; - } else { - result[0] += -0.0017104832; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { - result[0] += -0.04572242; - } else { - result[0] += -0.013417631; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 128))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - result[0] += 0.02735805; - } else { - result[0] += 0.008384274; - } - } else { - result[0] += 0.025471484; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 146))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 144))) { - result[0] += -0.0019103719; - } else { - result[0] += -0.015735812; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - result[0] += 0.003526498; - } else { - result[0] += 0.0004870697; - } - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 252))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 230))) { - result[0] += -0.0010278291; - } else { - result[0] += 0.0035127073; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - result[0] += 0.0007956819; - } else { - result[0] += -0.007078775; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { - result[0] += 0.06692124; - } else { - result[0] += 0.012191664; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { - result[0] += -0.0037893832; - } else { - result[0] += 0.0049554044; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 262))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.0026184532; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - result[0] += -0.03463073; - } else { - result[0] += -0.01464231; - } - } - } else { - result[0] += 0.01621621; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { - result[0] += -0.0019204362; - } else { - result[0] += 0.012864575; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += -0.008148334; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { - result[0] += 0.04631585; - } else { - result[0] += 0.008748725; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { - result[0] += -0.0012717935; - } else { - result[0] += 0.0031659238; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { - result[0] += 0.023349551; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - result[0] += 0.0070865126; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 220))) { - result[0] += -0.0010378936; - } else { - result[0] += -0.03451775; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 58))) { - result[0] += -0.0030549995; - } else { - result[0] += 0.042464044; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 196))) { - result[0] += -0.0068810317; - } else { - result[0] += -0.01983464; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - result[0] += 0.021494946; - } else { - result[0] += 0.046589624; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 246))) { - result[0] += 0.0016733175; - } else { - result[0] += -0.002286189; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { - result[0] += -0.023560746; - } else { - result[0] += 0.025176898; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - result[0] += -0.0032716715; - } else { - result[0] += -0.0107773235; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 158))) { - result[0] += 9.897955e-07; - } else { - result[0] += 0.0075915637; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 238))) { - result[0] += -0.018133821; - } else { - result[0] += -0.003638253; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { - result[0] += 0.013451889; - } else { - result[0] += 0.006885908; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 2))) { - result[0] += 0.016480822; - } else { - result[0] += 0.0028402929; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 182))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - result[0] += 0.009219227; - } else { - result[0] += -0.025390211; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += 0.025732726; - } else { - result[0] += -0.0023830726; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { - result[0] += 0.0035509118; - } else { - result[0] += 0.013229172; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.007854169; - } else { - result[0] += 0.0072692037; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.047711357; - } else { - result[0] += 0.025087982; - } - } else { - result[0] += 0.0016552185; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 36))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.0398016; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - result[0] += 0.0008522939; - } else { - result[0] += 0.022818817; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 8))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { - result[0] += -0.047148183; - } else { - result[0] += -0.014362188; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - result[0] += 0.033707537; - } else { - result[0] += -0.005045968; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 38))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 52))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - result[0] += -0.015981285; - } else { - result[0] += 0.061333533; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.011986034; - } else { - result[0] += 0.014650764; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 156))) { - result[0] += 0.03378779; - } else { - result[0] += 0.001349721; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - result[0] += -0.00046623495; - } else { - result[0] += -0.0032709301; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += 0.0026227154; - } else { - result[0] += -0.004871029; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += 0.024236446; - } else { - result[0] += 0.00875364; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 86))) { - result[0] += -0.010391722; - } else { - result[0] += -0.0003864034; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { - result[0] += 0.0091726575; - } else { - result[0] += 0.00034549323; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += -0.016140882; - } else { - result[0] += -0.0051438985; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 86))) { - result[0] += 0.0077548027; - } else { - result[0] += -0.0023856275; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 96))) { - result[0] += 0.0072452063; - } else { - result[0] += 0.021926673; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { - result[0] += 0.007803672; - } else { - result[0] += 0.07842522; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - result[0] += 0.0030498195; - } else { - result[0] += -0.030465296; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 138))) { - result[0] += 0.027338846; - } else { - result[0] += 0.018519042; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 104))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 150))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - result[0] += -0.0038131357; - } else { - result[0] += -0.014322984; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - result[0] += 0.011482879; - } else { - result[0] += -0.0018702493; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { - result[0] += 0.04208499; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 48))) { - result[0] += -0.013598467; - } else { - result[0] += 0.0022218376; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.014965734; - } else { - result[0] += 0.006420965; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - result[0] += 0.0035459124; - } else { - result[0] += -0.03692886; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.00581108; - } else { - result[0] += 0.0022818777; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += 0.02711085; - } else { - result[0] += 0.0037428073; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 232))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 230))) { - result[0] += -0.0006429512; - } else { - result[0] += -0.013558619; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - result[0] += 0.006967464; - } else { - result[0] += 0.020120773; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - result[0] += -0.0188818; - } else { - result[0] += -0.011276356; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 248))) { - result[0] += -0.0036909913; - } else { - result[0] += 0.0023398104; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 228))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 220))) { - result[0] += 0.0067386203; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += 0.009782809; - } else { - result[0] += -0.0003835666; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 242))) { - result[0] += 0.008386671; - } else { - result[0] += 0.01509656; - } - } - } - } - } - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 230))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += 0.0035304844; - } else { - result[0] += 0.00860195; - } - } else { - result[0] += 0.044866074; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += -0.0027610434; - } else { - result[0] += 0.032996777; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 30))) { - result[0] += 0.009394963; - } else { - result[0] += -0.0004232897; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { - result[0] += 0.019008147; - } else { - result[0] += 0.009637523; - } - } else { - result[0] += 0.041156612; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { - result[0] += 0.0076490478; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 182))) { - result[0] += 0.01141602; - } else { - result[0] += 0.0032564194; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - result[0] += 0.0054725604; - } else { - result[0] += -0.006845514; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - result[0] += -0; - } else { - result[0] += 0.04772381; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += -0.016645087; - } else { - result[0] += -0.032690536; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { - result[0] += 0.003037467; - } else { - result[0] += -0.009953394; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { - result[0] += 0.01011019; - } else { - result[0] += 0.017591367; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { - result[0] += 0.0020601144; - } else { - result[0] += -0.0052700583; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 172))) { - result[0] += -0.0001318003; - } else { - result[0] += -0.0058084927; - } - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 160))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 158))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { - result[0] += -0.021778304; - } else { - result[0] += -0.05481277; - } - } else { - result[0] += 0.01922178; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += -0.02884279; - } else { - result[0] += -0.05432384; - } - } else { - result[0] += -0.008790418; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += -0.0039755395; - } else { - result[0] += 0.0508219; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 128))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 28))) { - result[0] += -0.047565266; - } else { - result[0] += -0.034321543; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - result[0] += 0.011836532; - } else { - result[0] += -0.0025003483; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 58))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { - result[0] += 0.0057843057; - } else { - result[0] += 0.036940213; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - result[0] += -0.011010564; - } else { - result[0] += 0.014086082; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 226))) { - result[0] += -0.00865682; - } else { - result[0] += 0.018309247; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { - result[0] += 0.00883958; - } else { - result[0] += -0.0026359025; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 98))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { - result[0] += -0.0024397147; - } else { - result[0] += -0.023152852; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - result[0] += 0.0015076617; - } else { - result[0] += -0.009175034; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { - result[0] += 0.02444943; - } else { - result[0] += 0.0023199369; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - result[0] += -0.0028377601; - } else { - result[0] += 0.00046475232; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - result[0] += 0.00531006; - } else { - result[0] += -0.00012114655; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { - result[0] += -0.004693984; - } else { - result[0] += -0.032999605; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - result[0] += 0.029490167; - } else { - result[0] += 0.007921627; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 58))) { - result[0] += -0.0028598853; - } else { - result[0] += 0.0077966005; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 66))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 70))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - result[0] += 0.014692311; - } else { - result[0] += -0.021459384; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { - result[0] += -0.016421737; - } else { - result[0] += -0.0066403346; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += 0.0074493513; - } else { - result[0] += -0.0040894877; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - result[0] += 0.01759093; - } else { - result[0] += -0.00061179395; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += 0.0076502063; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 236))) { - result[0] += 0.00010772177; - } else { - result[0] += -0.003537047; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { - result[0] += -0.012459938; - } else { - result[0] += -0.0037218079; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 260))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - result[0] += 0.002977729; - } else { - result[0] += -0.019293662; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - result[0] += 0.015566354; - } else { - result[0] += 0.0037241564; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.03374357; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += 0.0041862135; - } else { - result[0] += 0.009111217; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 204))) { - result[0] += 0.001820308; - } else { - result[0] += 0.0113331815; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - result[0] += -0.01062336; - } else { - result[0] += 0.0023687228; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - result[0] += 0.0050820797; - } else { - result[0] += -0.010689573; - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.039545674; - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += 0.023330238; - } else { - result[0] += 0.0062547647; - } - } - } else { - result[0] += 0.00083993434; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 58))) { - result[0] += -0.0019025843; - } else { - result[0] += 0.0075600627; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - result[0] += 0.042078517; - } else { - result[0] += 0.0026668767; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.015797917; - } else { - result[0] += -0.006350769; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 96))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { - result[0] += 0.048628982; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { - result[0] += 0.021388667; - } else { - result[0] += 0.0020165527; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 110))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { - result[0] += -0.0051917243; - } else { - result[0] += 0.010212748; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += 0.0015873375; - } else { - result[0] += -0.0008199594; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 64))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 212))) { - result[0] += 0.01901365; - } else { - result[0] += -0.0028177812; - } - } else { - result[0] += 0.02409505; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 206))) { - result[0] += -0.0063365176; - } else { - result[0] += 0.00086170074; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - result[0] += -0.016780075; - } else { - result[0] += 0.012628958; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - result[0] += -0.009542297; - } else { - result[0] += 0.053097267; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 250))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 238))) { - result[0] += 0.003721561; - } else { - result[0] += 0.019598227; - } - } else { - result[0] += 0.03498596; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { - result[0] += 0.00025514522; - } else { - result[0] += 0.0040203123; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { - result[0] += -0.0074286456; - } else { - result[0] += 0.00093450863; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 166))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - result[0] += -0.00087522157; - } else { - result[0] += 0.007016174; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { - result[0] += -0.034118887; - } else { - result[0] += -0.011294309; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 260))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 256))) { - result[0] += 0.022198912; - } else { - result[0] += 0.012400496; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { - result[0] += -0.010423508; - } else { - result[0] += 0.004261658; - } - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 96))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 52))) { - result[0] += -0.001626932; - } else { - result[0] += 0.0061591933; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - result[0] += -0.0022012205; - } else { - result[0] += -0.0123000285; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 144))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { - result[0] += 0.007472606; - } else { - result[0] += 0.05678388; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 196))) { - result[0] += 3.3709428e-05; - } else { - result[0] += -0.0143476175; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { - result[0] += 0.03503921; - } else { - result[0] += -0.036016237; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { - result[0] += -0.0048608086; - } else { - result[0] += 0.022741511; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.035393443; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 88))) { - result[0] += -0.002815163; - } else { - result[0] += -2.7660133e-05; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 174))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 158))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 148))) { - result[0] += 0.0017332671; - } else { - result[0] += 0.026441107; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 222))) { - result[0] += -0.0073182597; - } else { - result[0] += 0.0006405428; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { - result[0] += 0.02126172; - } else { - result[0] += 0.011408723; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 68))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - result[0] += -0.014773398; - } else { - result[0] += 0.009008159; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += 0.0047272076; - } else { - result[0] += -0.01409996; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 160))) { - result[0] += 0.009959228; - } else { - result[0] += 0.04201186; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { - result[0] += -0.008849121; - } else { - result[0] += -0.00033711825; - } - } - } - } - } - } - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 16))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - result[0] += 0.002547614; - } else { - result[0] += -0.016982568; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - result[0] += 0.0087383045; - } else { - result[0] += -0.0019732136; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - result[0] += -0.027964113; - } else { - result[0] += -0.0070363963; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { - result[0] += 0.015301444; - } else { - result[0] += -0.0009437213; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 218))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 22))) { - result[0] += 0.011154254; - } else { - result[0] += 0.03460535; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { - result[0] += 0.006955503; - } else { - result[0] += 0.002656106; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 30))) { - result[0] += 0.008213889; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 234))) { - result[0] += -0.0014978464; - } else { - result[0] += 0.0018082943; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 166))) { - result[0] += -0.010998022; - } else { - result[0] += -0.028136352; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 28))) { - result[0] += 0.056712236; - } else { - result[0] += 0.00025265655; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += -0.032531813; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.012461627; - } else { - result[0] += -0.0026103533; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 62))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - result[0] += 0.003723057; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 44))) { - result[0] += 0.050172295; - } else { - result[0] += 0.03318845; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - result[0] += 0.038616996; - } else { - result[0] += -0.010049296; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { - result[0] += -0.0062160105; - } else { - result[0] += -4.625852e-05; - } - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 228))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 220))) { - result[0] += 0.027368566; - } else { - result[0] += -0.0007781659; - } - } else { - result[0] += -0.010964031; - } - } else { - result[0] += 0.0031334688; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 286))) { - result[0] += 0.023848334; - } else { - result[0] += 0.008595723; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - result[0] += 0.0113761; - } else { - result[0] += 0.0038027503; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - result[0] += 0.028975934; - } else { - result[0] += 0.01324735; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - result[0] += -0; - } else { - result[0] += 0.011437596; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { - result[0] += 0.01134281; - } else { - result[0] += 0.0018215694; - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 242))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 230))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 114))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { - result[0] += 0.0014854757; - } else { - result[0] += -0.0047508436; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { - result[0] += -0.012526001; - } else { - result[0] += -0.0042038257; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - result[0] += 0.027467722; - } else { - result[0] += 0.005847614; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - result[0] += -0.016319519; - } else { - result[0] += 0.0018143759; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { - result[0] += 0.0017114735; - } else { - result[0] += -0.04394586; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { - result[0] += 0.014061573; - } else { - result[0] += 0.0076026404; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { - result[0] += -0.0018655244; - } else { - result[0] += 0.011241974; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 54))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - result[0] += -0.013273408; - } else { - result[0] += 0.015772188; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 90))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 58))) { - result[0] += -0.00058818224; - } else { - result[0] += 0.008676103; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - result[0] += -0.010151858; - } else { - result[0] += 0.001280522; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - result[0] += -0.010351776; - } else { - result[0] += 0.012125977; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { - result[0] += -0.005976769; - } else { - result[0] += 0.0058461004; - } - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 22))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += 0.0014196447; - } else { - result[0] += -0.016857633; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 18))) { - result[0] += 0.0388976; - } else { - result[0] += 0.0022651523; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - result[0] += -0.0031505611; - } else { - result[0] += -0.02737777; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - result[0] += 0.027305475; - } else { - result[0] += -0.00032103845; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 28))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - result[0] += -0.033979844; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 44))) { - result[0] += -0.00036602697; - } else { - result[0] += -0.009034148; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { - result[0] += 0.02605836; - } else { - result[0] += 0.0035190487; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - result[0] += 0.0030745373; - } else { - result[0] += 0.021341039; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 112))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - result[0] += -0.00765642; - } else { - result[0] += 0.012637125; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 92))) { - result[0] += -0.031098261; - } else { - result[0] += -0.018325815; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 48))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 74))) { - result[0] += 0.007115057; - } else { - result[0] += 0.025569752; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 50))) { - result[0] += -0.030833412; - } else { - result[0] += -0.0027387291; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 118))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += -0.010184347; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { - result[0] += 0.009563519; - } else { - result[0] += 0.0019127353; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { - result[0] += -0.0016026145; - } else { - result[0] += -0.012827705; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { - result[0] += 0.006836459; - } else { - result[0] += -0.00047072658; - } - } - } - } - } - } - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { - result[0] += 0.016250825; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { - result[0] += -0.009732073; - } else { - result[0] += 0.017143272; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += -0.009671597; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 270))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 134))) { - result[0] += -0.00035089705; - } else { - result[0] += 0.004113893; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - result[0] += 0.01820572; - } else { - result[0] += 0.009231458; - } - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += 0.00027064016; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.0017144865; - } else { - result[0] += -0.0095933005; - } - } else { - result[0] += -0.012053235; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 194))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 250))) { - result[0] += -0.0034071447; - } else { - result[0] += 0.046097945; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 132))) { - result[0] += -0.0055225524; - } else { - result[0] += -0.0019266952; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 250))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 86))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - result[0] += 0.0048346436; - } else { - result[0] += 0.01435117; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { - result[0] += -0.019338546; - } else { - result[0] += -0.0010408615; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 178))) { - result[0] += 0.011773284; - } else { - result[0] += 0.002931762; - } - } else { - result[0] += 0.040974855; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - result[0] += 0.08329225; - } else { - result[0] += -0.0002780066; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { - result[0] += -0.0042001484; - } else { - result[0] += -0.02084507; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += -0.00010066136; - } else { - result[0] += 0.0027292925; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { - result[0] += -0.0122973155; - } else { - result[0] += 0.035760295; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += -0.001996606; - } else { - result[0] += 0.021067668; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 118))) { - result[0] += -0.013935153; - } else { - result[0] += 0.031183664; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += 0.061950255; - } else { - result[0] += 0.0058407323; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 142))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { - result[0] += 0.0829713; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.03793059; - } else { - result[0] += -0.00907617; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 68))) { - result[0] += 0.0025514578; - } else { - result[0] += -0.0012025639; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 16))) { - result[0] += 0.046701174; - } else { - result[0] += 0.0046325247; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { - result[0] += 0.027825123; - } else { - result[0] += -0; - } - } else { - result[0] += -0.021053893; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 82))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 152))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { - result[0] += -0.0013631139; - } else { - result[0] += -0.012569079; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - result[0] += -0.0008349316; - } else { - result[0] += 0.022382598; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 156))) { - result[0] += 0.013735485; - } else { - result[0] += 0.0033138485; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - result[0] += -0.0037839809; - } else { - result[0] += 0.002062925; - } - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += 0.0013772444; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { - result[0] += -0.011463809; - } else { - result[0] += -0.0047910134; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { - result[0] += 0.060537297; - } else { - result[0] += 0.010533939; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.016672423; - } else { - result[0] += -0.00052434875; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 250))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - result[0] += 0.0070519983; - } else { - result[0] += 0.016810419; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { - result[0] += -0.026641011; - } else { - result[0] += 0.00010854311; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { - result[0] += 0.03818845; - } else { - result[0] += 0.012029546; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { - result[0] += -0.007278714; - } else { - result[0] += -0.03333238; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 46))) { - result[0] += 0.007880951; - } else { - result[0] += -0.00031094736; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - result[0] += 0.013625564; - } else { - result[0] += 0.03885763; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 214))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - result[0] += -0.0029387611; - } else { - result[0] += 7.662349e-05; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += 0.029973779; - } else { - result[0] += 0.0043506036; - } - } - } - } - } - } - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 78))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - result[0] += -0.017686712; - } else { - result[0] += 0.015614047; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 242))) { - result[0] += 0.0010816348; - } else { - result[0] += 0.010603439; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += -0.013882413; - } else { - result[0] += -3.773386e-06; - } - } else { - result[0] += -0.029591149; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 52))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - result[0] += 0.0029416822; - } else { - result[0] += 0.060454708; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { - result[0] += -0.03178696; - } else { - result[0] += -0.011132187; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 164))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - result[0] += 0.06373953; - } else { - result[0] += -0.0049581574; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { - result[0] += -0.011388632; - } else { - result[0] += 0.0049218778; - } - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 14))) { - result[0] += 0.0012467568; - } else { - result[0] += -0.019324591; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { - result[0] += 0.001289832; - } else { - result[0] += -0.00096984406; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { - result[0] += -0.011180186; - } else { - result[0] += -0.0001193571; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { - result[0] += 0.017248346; - } else { - result[0] += 0.003171424; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 18))) { - result[0] += 0.04394267; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 176))) { - result[0] += -0.026358157; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.0084524695; - } else { - result[0] += -0.008720714; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 286))) { - result[0] += 0.02462693; - } else { - result[0] += 0.0102696465; - } - } - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - result[0] += -9.075111e-05; - } else { - result[0] += -0.0032513805; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { - result[0] += 0.0011170026; - } else { - result[0] += 0.023266837; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 204))) { - result[0] += -0.012011125; - } else { - result[0] += -0.0051451973; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 202))) { - result[0] += 0.023120182; - } else { - result[0] += -0.008411562; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 212))) { - result[0] += 0.017263098; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 230))) { - result[0] += 0.003063401; - } else { - result[0] += 0.021211077; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 234))) { - result[0] += -0.0117486585; - } else { - result[0] += -0.0008188842; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 248))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 40))) { - result[0] += -0.025011426; - } else { - result[0] += -0.01188937; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { - result[0] += 0.000581751; - } else { - result[0] += 0.035827655; - } - } - } else { - result[0] += -0.056811877; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { - result[0] += 0.03448122; - } else { - result[0] += 0.0075843707; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 120))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 150))) { - result[0] += 0.02317158; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { - result[0] += -0.011207158; - } else { - result[0] += 0.00603795; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - result[0] += 1.6565642e-05; - } else { - result[0] += 0.0018936057; - } - } else { - result[0] += -0.0073121726; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += 0.013665982; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - result[0] += 0.02345163; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 90))) { - result[0] += 0.031935133; - } else { - result[0] += 0.05211563; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 108))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 150))) { - result[0] += 0.006929166; - } else { - result[0] += 0.0018365175; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { - result[0] += -0.028940136; - } else { - result[0] += -0.0025783076; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.010843903; - } else { - result[0] += 0.006668359; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 84))) { - result[0] += -0.022552337; - } else { - result[0] += -0.0126125915; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 188))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { - result[0] += -0.0033015904; - } else { - result[0] += 0.0023345288; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 172))) { - result[0] += 0.027362315; - } else { - result[0] += 0.008473503; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 198))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += -0.0086487485; - } else { - result[0] += 0.0011204499; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - result[0] += -0.0040217172; - } else { - result[0] += 0.0013360606; - } - } - } - } - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { - result[0] += -0.0038944704; - } else { - result[0] += -0.024639545; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - result[0] += 0.010567961; - } else { - result[0] += -0.0008910143; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 222))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 208))) { - result[0] += 0.0022678128; - } else { - result[0] += 0.010532487; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { - result[0] += 0.0032915582; - } else { - result[0] += -0.0005411969; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 152))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - result[0] += -0.01407851; - } else { - result[0] += 0.026896363; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { - result[0] += -0.007195405; - } else { - result[0] += 0.004968326; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 174))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { - result[0] += 0.008754817; - } else { - result[0] += -0.0017400451; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { - result[0] += -0.00865253; - } else { - result[0] += 0.0009031243; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 244))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { - result[0] += 0.00038980006; - } else { - result[0] += -0.016266255; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 18))) { - result[0] += -0.011384182; - } else { - result[0] += -0.005449654; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - result[0] += 0.029928738; - } else { - result[0] += -0.005479242; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { - result[0] += -0.02587893; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 182))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - result[0] += -0.005359716; - } else { - result[0] += 0.06967212; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 236))) { - result[0] += 0.0018839792; - } else { - result[0] += 0.00916981; - } - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 122))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 104))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.0006954739; - } else { - result[0] += 0.048395343; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { - result[0] += 1.917278e-05; - } else { - result[0] += 0.03755577; - } - } - } else { - result[0] += -0.011912975; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 118))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { - result[0] += 0.00019196502; - } else { - result[0] += 0.014448823; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - result[0] += 0.019970976; - } else { - result[0] += -0.0011340084; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { - result[0] += -0.009018285; - } else { - result[0] += 0.020422988; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - result[0] += 0.02001476; - } else { - result[0] += 0.034766663; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 152))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - result[0] += -0.0060089673; - } else { - result[0] += -0.026388485; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 102))) { - result[0] += -0.0338698; - } else { - result[0] += -0.0058778613; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { - result[0] += 0.025475418; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - result[0] += 0.0019010755; - } else { - result[0] += 0.011219644; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - result[0] += -0.021041693; - } else { - result[0] += 0.008361343; - } - } else { - result[0] += 0.03766183; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - result[0] += -0.010491718; - } else { - result[0] += 0.003890305; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.011144113; - } else { - result[0] += -8.4675696e-05; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 212))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 166))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { - result[0] += 0.001330308; - } else { - result[0] += 0.02066918; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 130))) { - result[0] += 6.2536885e-05; - } else { - result[0] += -0.013168806; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { - result[0] += 0.019082962; - } else { - result[0] += 0.0053747944; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - result[0] += -0.0076252148; - } else { - result[0] += 0.0009393127; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 168))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.011447157; - } else { - result[0] += 0.0061047096; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { - result[0] += -0; - } else { - result[0] += -0.020285398; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += 0.01896581; - } else { - result[0] += 0.00095191033; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 172))) { - result[0] += 0.0154045075; - } else { - result[0] += -0.001370645; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += 0.006603177; - } else { - result[0] += 0.00034456272; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - result[0] += -0.0029918782; - } else { - result[0] += -0.012741557; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 166))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 164))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - result[0] += 0.0071618394; - } else { - result[0] += -0.012161553; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - result[0] += 0.03479741; - } else { - result[0] += 0.00893333; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 178))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 228))) { - result[0] += 0.0005804447; - } else { - result[0] += -0.006457042; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 186))) { - result[0] += 0.016656972; - } else { - result[0] += 0.002414147; - } - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 124))) { - result[0] += 0.022543216; - } else { - result[0] += 0.0024742563; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 50))) { - result[0] += -0.025850767; - } else { - result[0] += -0.0073662284; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { - result[0] += 0.0006718464; - } else { - result[0] += -0.0011177754; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { - result[0] += 0.003656535; - } else { - result[0] += -0.0034576224; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 122))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { - result[0] += 0.01316909; - } else { - result[0] += -0.00022220907; - } - } else { - result[0] += 0.0322718; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 132))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += 0.0029699726; - } else { - result[0] += -0.009400754; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - result[0] += 0.0157076; - } else { - result[0] += -0.0039490475; - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 136))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { - result[0] += -0.030938972; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 174))) { - result[0] += 0.027307421; - } else { - result[0] += -0.00040336605; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - result[0] += 0.029921725; - } else { - result[0] += 0.013266409; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 168))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 62))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 144))) { - result[0] += 0.044555373; - } else { - result[0] += 0.0020996393; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 90))) { - result[0] += -0.0010202875; - } else { - result[0] += -0.0097485995; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 170))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - result[0] += 0.024518076; - } else { - result[0] += -0.0032617298; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += -0.002727569; - } else { - result[0] += -0.00034776315; - } - } - } - } - } - } - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 188))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - result[0] += -0.00013192062; - } else { - result[0] += -0.007137733; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - result[0] += 0.0058402964; - } else { - result[0] += 0.00019643598; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 170))) { - result[0] += -0.015256891; - } else { - result[0] += 0.027069787; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { - result[0] += 0.016238226; - } else { - result[0] += 0.045753922; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { - result[0] += -0.019134747; - } else { - result[0] += 0.010413033; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { - result[0] += -0.0016630454; - } else { - result[0] += -0.024183424; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - result[0] += 0.012589683; - } else { - result[0] += 0.0009457501; - } - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 210))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 208))) { - result[0] += -0.0066352724; - } else { - result[0] += -0.017913656; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.0011060528; - } else { - result[0] += 0.0039828527; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 200))) { - result[0] += -0.0031051391; - } else { - result[0] += -0.016724786; - } - } else { - result[0] += -0.024875712; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 162))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 116))) { - result[0] += -0.016162679; - } else { - result[0] += 0.0026748257; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 160))) { - result[0] += 0.01718258; - } else { - result[0] += 0.0035327424; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 114))) { - result[0] += 0.0027622434; - } else { - result[0] += -0.019732043; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { - result[0] += -0.009568975; - } else { - result[0] += -0.0024784; - } - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 212))) { - result[0] += 0.01407246; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - result[0] += -0.04716774; - } else { - result[0] += -0.008298879; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { - result[0] += -0.012182956; - } else { - result[0] += -0.0051582577; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 128))) { - result[0] += -0.0067259143; - } else { - result[0] += 0.013526896; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { - result[0] += 0.002809637; - } else { - result[0] += -0.0016421535; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 82))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - result[0] += -0.0011062829; - } else { - result[0] += 0.014348181; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { - result[0] += 0.0076159174; - } else { - result[0] += -0.0151206255; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 124))) { - result[0] += 0.0045484654; - } else { - result[0] += 0.015772928; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - result[0] += -0.0059127775; - } else { - result[0] += 0.0025872302; - } - } - } - } - } - } - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 206))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += 0.115350716; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.030523414; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { - result[0] += 0.00818327; - } else { - result[0] += 0.042636707; - } - } else { - result[0] += 0.002394322; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += -0; - } else { - result[0] += 0.022605298; - } - } else { - result[0] += -0.018712113; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - result[0] += -0.018390236; - } else { - result[0] += 0.038224217; - } - } else { - result[0] += 0.0023876673; - } - } - } else { - result[0] += -0.0001913241; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 182))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - result[0] += 0.002910438; - } else { - result[0] += -0.036367122; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += 0.0029667527; - } else { - result[0] += 0.04206534; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.012119245; - } else { - result[0] += -0.023951335; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 178))) { - result[0] += -0.034575075; - } else { - result[0] += 0.007937021; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 138))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { - result[0] += -0.0042928117; - } else { - result[0] += -0.0012923562; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 184))) { - result[0] += -0.028960615; - } else { - result[0] += -0.016311336; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += -0.053593513; - } else { - result[0] += 0.009981692; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 78))) { - result[0] += -0.017819297; - } else { - result[0] += -0.00070253096; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 44))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 32))) { - result[0] += 0.004783728; - } else { - result[0] += 0.016091399; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { - result[0] += 0.029074514; - } else { - result[0] += 0.002160437; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 250))) { - result[0] += 0.055964243; - } else { - result[0] += 0.01622332; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 250))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { - result[0] += -0.008321747; - } else { - result[0] += -0.0040490143; - } - } else { - result[0] += -0.018693618; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 250))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - result[0] += 6.4203756e-05; - } else { - result[0] += -0.0021725388; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 268))) { - result[0] += 0.0055606547; - } else { - result[0] += -0.0023256661; - } - } - } - } - } - } - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 182))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - result[0] += -6.4768305e-06; - } else { - result[0] += -0.013116406; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += 0.019466847; - } else { - result[0] += 0.006013643; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - result[0] += -0.026702961; - } else { - result[0] += 0.0020189236; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { - result[0] += -0.0006026015; - } else { - result[0] += 0.01105925; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - result[0] += 0.029843587; - } else { - result[0] += 0.0661096; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 100))) { - result[0] += 0.00522026; - } else { - result[0] += -0.0006855772; - } - } else { - result[0] += 0.013178887; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 124))) { - result[0] += -0.020214727; - } else { - result[0] += 0.0038588562; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 200))) { - result[0] += -0.00505647; - } else { - result[0] += -0.026435608; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 270))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { - result[0] += -0.0028923058; - } else { - result[0] += -0.008900782; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { - result[0] += 0.050368883; - } else { - result[0] += 0.0024831446; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 42))) { - result[0] += 0.011402026; - } else { - result[0] += -0.031253975; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { - result[0] += -0.012896495; - } else { - result[0] += -0.0015336399; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 68))) { - result[0] += 0.011413305; - } else { - result[0] += 0.02147497; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 212))) { - result[0] += 0.013494618; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 46))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 260))) { - result[0] += 0.0066242903; - } else { - result[0] += 0.0001638933; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - result[0] += -0.009525633; - } else { - result[0] += 0.0022819052; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 176))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 70))) { - result[0] += 0.0022807613; - } else { - result[0] += -0.0062286295; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - result[0] += -0.01628782; - } else { - result[0] += -0.004168268; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 104))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { - result[0] += 0.011599141; - } else { - result[0] += 0.0024420898; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 162))) { - result[0] += 0.013803795; - } else { - result[0] += -0.0013663528; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 146))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { - result[0] += -0.005095913; - } else { - result[0] += 0.00041055848; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 78))) { - result[0] += -0.004392452; - } else { - result[0] += 0.0040476876; - } - } - } - } - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { - result[0] += -0.00044016525; - } else { - result[0] += 0.0027606068; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 180))) { - result[0] += -0.0027404772; - } else { - result[0] += -0.012484104; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += 0.1066999; - } else { - result[0] += 0.005611764; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { - result[0] += -0.0044804937; - } else { - result[0] += -0.00094216457; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 86))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 20))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += -0.016515603; - } else { - result[0] += -0.0019510689; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += 0.04879087; - } else { - result[0] += 0.019489786; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 174))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - result[0] += 0.028882284; - } else { - result[0] += 0.0029704724; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { - result[0] += -0.008776897; - } else { - result[0] += 0.00076872477; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 22))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 246))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += -0.00024617615; - } else { - result[0] += -0.013363178; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.012836638; - } else { - result[0] += -0.00578899; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 74))) { - result[0] += 0.02552943; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 74))) { - result[0] += 0.014522381; - } else { - result[0] += -0.0055740518; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { - result[0] += -0.024675723; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - result[0] += 0.036256976; - } else { - result[0] += -0.010159162; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 186))) { - result[0] += -0.0034239579; - } else { - result[0] += 0.0019339178; - } - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 130))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 126))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 124))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { - result[0] += 0.0021977962; - } else { - result[0] += -0.0026835771; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { - result[0] += 0.014414914; - } else { - result[0] += 0.010211523; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - result[0] += -0.008403643; - } else { - result[0] += -0.024761902; - } - } else { - result[0] += 0.0032713003; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - result[0] += -0.02039499; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.009458961; - } else { - result[0] += -0.0018666508; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - result[0] += 0.0327911; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.036110453; - } else { - result[0] += 0.0033109114; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 48))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 150))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 48))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 104))) { - result[0] += 0.010583217; - } else { - result[0] += -0.01392099; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - result[0] += 0.032042317; - } else { - result[0] += 0.011768063; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { - result[0] += -0.04997726; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - result[0] += -0.008824881; - } else { - result[0] += 0.0048329984; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - result[0] += 0.00096224155; - } else { - result[0] += -0.009902506; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 146))) { - result[0] += 0.02890099; - } else { - result[0] += 5.074874e-05; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += -0.0082296785; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 96))) { - result[0] += 0.0020531386; - } else { - result[0] += -0.001512293; - } - } - } - } - } - } - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { - result[0] += -0.009371125; - } else { - result[0] += 0.016960299; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += -0.008832243; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 254))) { - result[0] += 0.008071503; - } else { - result[0] += 0.013252865; - } - } else { - result[0] += -0.0039770114; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { - result[0] += -0.0074452083; - } else { - result[0] += 0.0010280218; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - result[0] += 0.020828962; - } else { - result[0] += 0.006464574; - } - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 244))) { - result[0] += 0.044012897; - } else { - result[0] += -0.01173665; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - result[0] += 0.013455394; - } else { - result[0] += -0.014296198; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - result[0] += 0.00890102; - } else { - result[0] += -0.00038118512; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { - result[0] += 0.0076601445; - } else { - result[0] += 0.037503462; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { - result[0] += 0.00028127801; - } else { - result[0] += -0.0077372934; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { - result[0] += -0.0058348128; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - result[0] += -0.0035285298; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += 0.0027004466; - } else { - result[0] += 0.009092315; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 4))) { - result[0] += 0.009767297; - } else { - result[0] += -0.008808373; - } - } else { - result[0] += 0.009121577; - } - } else { - result[0] += -0.0003377227; - } - } else { - result[0] += -0.0081705395; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 110))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - result[0] += 0.012397877; - } else { - result[0] += 0.003961118; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { - result[0] += -0.003340742; - } else { - result[0] += 0.001318198; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { - result[0] += 0.04594936; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - result[0] += -0.012992064; - } else { - result[0] += 0.013072202; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 146))) { - result[0] += -0.016053863; - } else { - result[0] += -0.0013210791; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 120))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { - result[0] += -0.0011712526; - } else { - result[0] += 0.010021153; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 124))) { - result[0] += -0.007040714; - } else { - result[0] += 0.0005415445; - } - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 8))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - result[0] += 0.0068953778; - } else { - result[0] += -0.0090838885; - } - } else { - result[0] += 0.024001935; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - result[0] += -0.009051267; - } else { - result[0] += -0.02782002; - } - } else { - result[0] += 0.007848546; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 8))) { - result[0] += 0.0028921093; - } else { - result[0] += 0.023708088; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - result[0] += 0.0013097594; - } else { - result[0] += -0.001009873; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 190))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - result[0] += 0.017365938; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += -0.026752142; - } else { - result[0] += -0.00315145; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - result[0] += -0.0013150068; - } else { - result[0] += 0.05037552; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - result[0] += -0.02626193; - } else { - result[0] += 0.030513177; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 8))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 44))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.0053047254; - } else { - result[0] += -0.027583335; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 54))) { - result[0] += 0.043980025; - } else { - result[0] += -0.006328375; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { - result[0] += 0.0048042852; - } else { - result[0] += 0.042036824; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.012095607; - } else { - result[0] += -0.00045656387; - } - } - } - } - } - } - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 112))) { - result[0] += -0.0005225084; - } else { - result[0] += 0.0013189358; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - result[0] += -0.01325873; - } else { - result[0] += 0.008976769; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { - result[0] += -0.004084352; - } else { - result[0] += 0.0038890864; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 204))) { - result[0] += -0.010790448; - } else { - result[0] += -0.0041690473; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 214))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += 0.00092866377; - } else { - result[0] += -0.021240106; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { - result[0] += 0.016957764; - } else { - result[0] += 0.009066544; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 56))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 120))) { - result[0] += -0.008545898; - } else { - result[0] += -0.0013856884; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 216))) { - result[0] += -0.0058118394; - } else { - result[0] += 0.0030270133; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.004897514; - } else { - result[0] += 0.012878601; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 136))) { - result[0] += -0.009626532; - } else { - result[0] += -0.005689178; - } - } - } else { - result[0] += -0.04376392; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 136))) { - result[0] += 0.03378177; - } else { - result[0] += 0.01186064; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 120))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { - result[0] += -0.0032555745; - } else { - result[0] += 0.0158323; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { - result[0] += 0.00046772003; - } else { - result[0] += 0.0077147195; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - result[0] += 0.0011735325; - } else { - result[0] += -0.006349434; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 146))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.023203839; - } else { - result[0] += 0.00827275; - } - } else { - result[0] += 0.039015923; - } - } else { - result[0] += 0.016087564; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 156))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 206))) { - result[0] += -0.0005247008; - } else { - result[0] += -0.0068671047; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 188))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 182))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - result[0] += -0.00041580052; - } else { - result[0] += 0.005611544; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - result[0] += -0.015564476; - } else { - result[0] += -0.005183963; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 172))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 218))) { - result[0] += 0.023443257; - } else { - result[0] += -0.002142666; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { - result[0] += 0.05721947; - } else { - result[0] += 0.000846235; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 8))) { - result[0] += -0.010377513; - } else { - result[0] += 0.002864465; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { - result[0] += -0.041931864; - } else { - result[0] += -0.01965585; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { - result[0] += -0.0147984475; - } else { - result[0] += 0.031754512; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { - result[0] += 0.0027872298; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { - result[0] += -0.0018927314; - } else { - result[0] += 0.00035012572; - } - } - } else { - result[0] += 0.011900772; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += 0.02755776; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { - result[0] += -0.03298561; - } else { - result[0] += -0.008792006; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 260))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { - result[0] += -0.00033489856; - } else { - result[0] += 0.006294869; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 216))) { - result[0] += -0.00025057443; - } else { - result[0] += 0.005475367; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 22))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 194))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += 0.034542117; - } else { - result[0] += 0.0051070247; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { - result[0] += -0.011878603; - } else { - result[0] += 0.015853336; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 12))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 204))) { - result[0] += -0.017843468; - } else { - result[0] += 0.0025024624; - } - } else { - result[0] += -0.038026553; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 0))) { - result[0] += 0.0004767517; - } else { - result[0] += -0.005413917; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { - result[0] += -0.00014004558; - } else { - result[0] += 0.0038309437; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { - result[0] += -0.025853286; - } else { - result[0] += 0.016463885; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { - result[0] += 0.008300542; - } else { - result[0] += -0.0022710604; - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 26))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { - result[0] += 0.06824603; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - result[0] += 0.0344267; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 24))) { - result[0] += -0.02413011; - } else { - result[0] += 0.009757657; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 240))) { - result[0] += 0.04187591; - } else { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += 0.021772258; - } else { - result[0] += 0.00023637361; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - result[0] += -0.00018605802; - } else { - result[0] += 0.00892628; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - result[0] += -0.0029304621; - } else { - result[0] += 0.005322406; - } - } - } - } - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { - result[0] += 0.058786936; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 204))) { - result[0] += -0.001987611; - } else { - result[0] += 0.0012095956; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += -0.013887591; - } else { - result[0] += -0.005378675; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - result[0] += 0.004469014; - } else { - result[0] += -0.0019160692; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - result[0] += -0.052244443; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 20))) { - result[0] += 0.034024667; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 184))) { - result[0] += -0.028885717; - } else { - result[0] += -0.016080601; - } - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 28))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 196))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 206))) { - result[0] += 0.004683802; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - result[0] += 0.0038840864; - } else { - result[0] += -0.0010689719; - } - } - } else { - result[0] += 0.040237945; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 174))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { - result[0] += 0.009488398; - } else { - result[0] += 0.0063879974; - } - } else { - result[0] += 0.040055256; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 240))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 124))) { - result[0] += 0.0063549983; - } else { - result[0] += 0.0503232; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { - result[0] += -0.0018827593; - } else { - result[0] += 0.0051954878; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { - result[0] += 0.032296978; - } else { - result[0] += 0.0139222685; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { - result[0] += 0.04671686; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - result[0] += 0.0009886044; - } else { - result[0] += 0.006239516; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 30))) { - result[0] += 0.031537697; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { - result[0] += -0.019081848; - } else { - result[0] += -0.0026591418; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - result[0] += 0.026619418; - } else { - result[0] += 0.0041615735; - } - } else { - result[0] += 0.047726057; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - result[0] += 0.001571185; - } else { - result[0] += -0.00050108694; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 140))) { - result[0] += 0.009636164; - } else { - result[0] += 0.0010100096; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - result[0] += -0.0026403468; - } else { - result[0] += 0.0114326365; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 50))) { - result[0] += -0.021934291; - } else { - result[0] += -0.0050015096; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 238))) { - result[0] += -0.007851736; - } else { - result[0] += 0.03168834; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - result[0] += 0.033044577; - } else { - result[0] += 9.3673785e-05; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 188))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 156))) { - result[0] += 0.00030188911; - } else { - result[0] += 0.002446159; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - result[0] += -0.0055051325; - } else { - result[0] += 0.0012621379; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - result[0] += 0.014215629; - } else { - result[0] += 0.005867033; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { - result[0] += -0.006319243; - } else { - result[0] += 0.0017076422; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 166))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 218))) { - result[0] += -0.0017003525; - } else { - result[0] += 0.010260985; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 236))) { - result[0] += -0.015775895; - } else { - result[0] += -0.007966612; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - result[0] += 0.0038435606; - } else { - result[0] += 0.0005556598; - } - } else { - result[0] += 0.0073271217; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 230))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 196))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - result[0] += -0.00010884991; - } else { - result[0] += -0.0013555246; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - result[0] += 0.00045378748; - } else { - result[0] += 0.00696068; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - result[0] += 0.0034289937; - } else { - result[0] += -0.005728068; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { - result[0] += -0.009315341; - } else { - result[0] += -0.004357322; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { - result[0] += -0.024190784; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { - result[0] += -0; - } else { - result[0] += -0.019713525; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 220))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 64))) { - result[0] += -0.0078073195; - } else { - result[0] += 5.9448754e-05; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 186))) { - result[0] += -0.006281379; - } else { - result[0] += -0.010314364; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - result[0] += 0.001542045; - } else { - result[0] += -0.0030066923; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { - result[0] += 0.045451414; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 250))) { - result[0] += -0.0048594624; - } else { - result[0] += 0.002339422; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 236))) { - result[0] += 0.0038202107; - } else { - result[0] += -0.0043049804; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 222))) { - result[0] += 0.035619907; - } else { - result[0] += 0.011654694; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - result[0] += 0.011329205; - } else { - result[0] += -0.0026530891; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - result[0] += 0.00091164187; - } else { - result[0] += 0.0065599764; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 180))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - result[0] += 0.0003863835; - } else { - result[0] += 0.012511316; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { - result[0] += -0.012533942; - } else { - result[0] += 0.000940961; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - result[0] += 0.02408296; - } else { - result[0] += 0.006255468; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 188))) { - result[0] += 0.01437206; - } else { - result[0] += 0.005517607; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 156))) { - result[0] += -0.004864832; - } else { - result[0] += 0.004785055; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 206))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - result[0] += -0.008446374; - } else { - result[0] += 3.6487032e-05; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { - result[0] += -0.013399954; - } else { - result[0] += 0.0013180381; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 56))) { - result[0] += -0.01676119; - } else { - result[0] += 0.019005135; - } - } else { - result[0] += 0.026536051; - } - } else { - result[0] += -0.017303595; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 234))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { - result[0] += -0.005325236; - } else { - result[0] += 0.0026667507; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { - result[0] += 0.006688422; - } else { - result[0] += 0.0130848605; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 200))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { - result[0] += -6.232897e-05; - } else { - result[0] += -0.011703821; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { - result[0] += 0.01631069; - } else { - result[0] += 0.0024924437; - } - } - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 118))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.020456076; - } else { - result[0] += 1.7096074e-05; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += -0.0043996517; - } else { - result[0] += 0.037505116; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { - result[0] += 0.03947706; - } else { - result[0] += 0.00013893798; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { - result[0] += -0.00020466889; - } else { - result[0] += -0.0056823995; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 90))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { - result[0] += 0.0015122422; - } else { - result[0] += 0.014326178; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 98))) { - result[0] += -0.03659178; - } else { - result[0] += -0.00897372; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 234))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { - result[0] += -0.0012049631; - } else { - result[0] += -0.010048345; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - result[0] += 0.023757316; - } else { - result[0] += -0.00096084067; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - result[0] += 0.007543098; - } else { - result[0] += 0.05037853; - } - } else { - result[0] += -0.020506142; - } - } else { - result[0] += 0.041784402; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 106))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 168))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - result[0] += 0.022379946; - } else { - result[0] += 0.001123256; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 176))) { - result[0] += -0.0040573548; - } else { - result[0] += -0.00018302095; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { - result[0] += 0.026662258; - } else { - result[0] += 0.001635097; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { - result[0] += 0.0157876; - } else { - result[0] += 0.00078219484; - } - } - } - } - } - } - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 168))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { - result[0] += -0.003070421; - } else { - result[0] += 0.003986802; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - result[0] += -0.016073924; - } else { - result[0] += -0.00033199994; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 82))) { - result[0] += -0.0026495587; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 180))) { - result[0] += 0.01088603; - } else { - result[0] += 0.0028157744; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { - result[0] += 0.035645917; - } else { - result[0] += 0.011242066; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 158))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - result[0] += -0.004172708; - } else { - result[0] += -0.013315861; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - result[0] += 0.0016847762; - } else { - result[0] += -0.019520441; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 116))) { - result[0] += -0.0012042717; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - result[0] += 0.010144642; - } else { - result[0] += 0.021312661; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 122))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { - result[0] += -0.0060045626; - } else { - result[0] += -0.019477958; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 154))) { - result[0] += -0.010557742; - } else { - result[0] += -0.019895343; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 164))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { - result[0] += 0.0023980096; - } else { - result[0] += -0.0058308984; - } - } else { - result[0] += 0.0054435167; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 170))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 144))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 52))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - result[0] += 0.01054426; - } else { - result[0] += -0.013723351; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 190))) { - result[0] += 0.0035272748; - } else { - result[0] += -0.0014124735; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 128))) { - result[0] += -0.020531978; - } else { - result[0] += -0.008468418; - } - } - } else { - result[0] += 0.027421832; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 136))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { - result[0] += -0.0014708912; - } else { - result[0] += 0.010910965; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { - result[0] += 0.02383196; - } else { - result[0] += -0.005580553; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 192))) { - result[0] += -0.015012925; - } else { - result[0] += -0.038420197; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 102))) { - result[0] += 0.027166018; - } else { - result[0] += -0.0051050577; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 158))) { - result[0] += 0.005120454; - } else { - result[0] += -0.0066694873; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - result[0] += 0.040612247; - } else { - result[0] += 0.010608687; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 206))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 186))) { - result[0] += 9.48829e-05; - } else { - result[0] += -0.0028726817; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 222))) { - result[0] += 0.0036865217; - } else { - result[0] += -0.0017621756; - } - } - } - } - } - } - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { - result[0] += 0.010326921; - } else { - result[0] += -6.580556e-05; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 14))) { - result[0] += 0.035937645; - } else { - result[0] += 0.0061092353; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += 0.09306122; - } else { - result[0] += 0.004483877; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { - result[0] += -0.0021131127; - } else { - result[0] += -0.0002122157; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 16))) { - result[0] += -0.0010388399; - } else { - result[0] += 0.04628381; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 170))) { - result[0] += -0.012791668; - } else { - result[0] += 0.012744421; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 14))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - result[0] += 0.001258762; - } else { - result[0] += -0.010475395; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 18))) { - result[0] += 0.025982514; - } else { - result[0] += 0.0025854723; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 26))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 96))) { - result[0] += -0.020725088; - } else { - result[0] += 0.014970714; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 176))) { - result[0] += -0.0017299574; - } else { - result[0] += 0.026476989; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 158))) { - result[0] += -0.004242038; - } else { - result[0] += -0.029603764; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 100))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 126))) { - result[0] += 0.0020087094; - } else { - result[0] += -0.001871386; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 44))) { - result[0] += 0.014256491; - } else { - result[0] += 0.007524089; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 94))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 206))) { - result[0] += -0.014824388; - } else { - result[0] += -0.0017853121; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - result[0] += -0.004817825; - } else { - result[0] += 0.00016148947; - } - } - } - } - } - } else { - result[0] += 0.0088046715; - } - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 168))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 154))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - result[0] += 9.3958115e-05; - } else { - result[0] += 0.0067572976; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 222))) { - result[0] += -0.0049092458; - } else { - result[0] += -0.0005594916; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 178))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 108))) { - result[0] += 0.00977688; - } else { - result[0] += 0.0015517873; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 204))) { - result[0] += -0.008679122; - } else { - result[0] += -0.002352368; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 158))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - result[0] += 0.009798234; - } else { - result[0] += 0.0032348556; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 182))) { - result[0] += 0.014364332; - } else { - result[0] += 0.025256773; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 118))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { - result[0] += -0.019178228; - } else { - result[0] += -0.0023992255; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { - result[0] += 0.026579231; - } else { - result[0] += 0.0040463707; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 172))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 170))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - result[0] += -0.012679273; - } else { - result[0] += -0.03488743; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 162))) { - result[0] += 0.012128734; - } else { - result[0] += -0.0036993285; - } - } - } else { - result[0] += -0.026699487; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 164))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 46))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { - result[0] += 0.008520793; - } else { - result[0] += -0.0012307116; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 174))) { - result[0] += -0.0020589442; - } else { - result[0] += -0.0074132094; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 22))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 258))) { - result[0] += -0.00428136; - } else { - result[0] += 0.042908087; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { - result[0] += 0.034873586; - } else { - result[0] += 0.0018836738; - } - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 190))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { - result[0] += -0.0036578246; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 208))) { - result[0] += 0.020423703; - } else { - result[0] += 0.030946225; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += -0.006327176; - } else { - result[0] += -0.015637185; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 258))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { - result[0] += 0.0020563873; - } else { - result[0] += 0.009788949; - } - } else { - result[0] += -0.0091839805; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 194))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - result[0] += 0.051798742; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 252))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += -0.006234445; - } else { - result[0] += 0.008611916; - } - } else { - result[0] += 0.0055716326; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 196))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 194))) { - result[0] += 0.003886501; - } else { - result[0] += 0.008659224; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 198))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { - result[0] += -0.03154314; - } else { - result[0] += -0.0031137222; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - result[0] += 0.00038270376; - } else { - result[0] += 0.0020157003; - } - } - } - } - } - } - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - result[0] += 0.026274556; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - result[0] += -0.014014624; - } else { - result[0] += 0.0017416707; - } - } else { - result[0] += -0.020999283; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { - result[0] += 0.019021433; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 2))) { - result[0] += 0.00134224; - } else { - result[0] += 0.0040796655; - } - } else { - result[0] += -0.0030546128; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 90))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 46))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - result[0] += -0.0019856154; - } else { - result[0] += -0.0002087965; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - result[0] += 0.013158979; - } else { - result[0] += -0.0030576333; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - result[0] += 0.003060523; - } else { - result[0] += 0.0005930412; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { - result[0] += 0.03627108; - } else { - result[0] += -0.0035451804; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - result[0] += -0.0046488056; - } else { - result[0] += 0.00013075671; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 120))) { - result[0] += 0.012213302; - } else { - result[0] += 0.04960065; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - result[0] += -0.0069404887; - } else { - result[0] += -0.01752409; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - result[0] += 0.0142224645; - } else { - result[0] += -0.0028541964; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - result[0] += -0.00048694783; - } else { - result[0] += -0.01978219; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - result[0] += 0.023798453; - } else { - result[0] += 0.0028695017; - } - } - } else { - result[0] += 0.049870964; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { - result[0] += -0.011660507; - } else { - result[0] += 0.020355195; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - result[0] += -0.010959279; - } else { - result[0] += -0.022352552; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 104))) { - result[0] += 0.0019975018; - } else { - result[0] += 0.02552997; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.005593583; - } else { - result[0] += 0.0001567727; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 36))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - result[0] += 0.0025115781; - } else { - result[0] += 0.01276709; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += 0.0009829837; - } else { - result[0] += -0.0020405638; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 176))) { - result[0] += -0.0027318567; - } else { - result[0] += 0.00036724083; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { - result[0] += 0.00952085; - } else { - result[0] += 0.00021925311; - } - } - } - } else { - result[0] += 0.0028065902; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 264))) { - result[0] += -0.010198384; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 68))) { - result[0] += 0.008980814; - } else { - result[0] += -0.00083386357; - } - } - } else { - result[0] += 0.034579825; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - result[0] += -0.0005227964; - } else { - result[0] += 0.00078901247; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 264))) { - result[0] += -0.0031115639; - } else { - result[0] += -0.008557741; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { - result[0] += 0.044274148; - } else { - result[0] += 0.004722073; - } - } else { - result[0] += -0.020457005; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 208))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 214))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { - result[0] += -0.011751657; - } else { - result[0] += -0.004513866; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 174))) { - result[0] += 0.0004462137; - } else { - result[0] += 0.0044212313; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 202))) { - result[0] += -0.015617072; - } else { - result[0] += -0.0095054805; - } - } else { - result[0] += -3.9380015e-05; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 160))) { - result[0] += 0.002189336; - } else { - result[0] += 0.011331045; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 230))) { - result[0] += 0.0016538227; - } else { - result[0] += -0.0024343396; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 186))) { - result[0] += 0.030128485; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - result[0] += 0.0012932423; - } else { - result[0] += 0.004705872; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += 0.001994251; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 252))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 138))) { - result[0] += -0.00304999; - } else { - result[0] += 0.011378044; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { - result[0] += 0.004555874; - } else { - result[0] += -0.00066039886; - } - } - } - } - } - } - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - result[0] += 0.020459097; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 234))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 56))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - result[0] += -0.006080057; - } else { - result[0] += 0.0015269167; - } - } else { - result[0] += 0.004850093; - } - } else { - result[0] += 0.0372449; - } - } else { - result[0] += -0.0033630708; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - result[0] += 0.0020560354; - } else { - result[0] += -0.01611949; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { - result[0] += 0.02037514; - } else { - result[0] += 0.0024216073; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { - result[0] += -1.3009435e-06; - } else { - result[0] += -0.010717098; - } - } else { - result[0] += -0.026966467; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { - result[0] += -0.035333917; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 60))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 34))) { - result[0] += -0.0039970325; - } else { - result[0] += -0.018680593; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { - result[0] += 0.060415726; - } else { - result[0] += -0.0035995846; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { - result[0] += 0.079136275; - } else { - result[0] += 0.006364348; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - result[0] += 0.009026817; - } else { - result[0] += -0.00017536264; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { - result[0] += 0.0025470634; - } else { - result[0] += -0.0051941304; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 78))) { - result[0] += -0.0008757123; - } else { - result[0] += -0.008570026; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 52))) { - result[0] += 0.005017677; - } else { - result[0] += -7.974629e-05; - } - } - } - } - } - } - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 212))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 190))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 174))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - result[0] += -8.178638e-05; - } else { - result[0] += 0.0078121508; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { - result[0] += -0.005830926; - } else { - result[0] += -0.0010021594; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 262))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 244))) { - result[0] += 0.0016010083; - } else { - result[0] += 0.00825313; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 206))) { - result[0] += -0.003538803; - } else { - result[0] += 0.0063129514; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 198))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 190))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 170))) { - result[0] += -0.005545571; - } else { - result[0] += -0.0232496; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 52))) { - result[0] += 0.012393202; - } else { - result[0] += -4.4827804e-05; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 208))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 200))) { - result[0] += 0.003246121; - } else { - result[0] += -0.00059273635; - } - } else { - result[0] += -0.013444054; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 70))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 180))) { - result[0] += 0.00011026966; - } else { - result[0] += -0.012288004; - } - } else { - result[0] += 0.009097963; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 210))) { - result[0] += -0.004248265; - } else { - result[0] += -0.008213413; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.041847315; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 214))) { - result[0] += 0.008025631; - } else { - result[0] += 0.0023118325; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 238))) { - result[0] += -0.004090266; - } else { - result[0] += 0.001676759; - } - } - } - } - } - } else { - result[0] += 0.0124376435; - } - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 148))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 130))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 126))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - result[0] += 8.788087e-05; - } else { - result[0] += -0.0007693504; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 62))) { - result[0] += 0.028994996; - } else { - result[0] += 0.0019165861; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 82))) { - result[0] += 0.0018343481; - } else { - result[0] += -0.001085312; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - result[0] += -0.00844089; - } else { - result[0] += -0.0010173704; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 118))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { - result[0] += 0.03216304; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.019660346; - } else { - result[0] += 0.010661392; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - result[0] += -0.011746697; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 120))) { - result[0] += 0.040727697; - } else { - result[0] += 0.007775008; - } - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 146))) { - result[0] += -0.017095553; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += -0.010490341; - } else { - result[0] += -0.0029303418; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - result[0] += 0.0037032042; - } else { - result[0] += 0.019236496; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 88))) { - result[0] += -0.00065161986; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 154))) { - result[0] += 0.013047007; - } else { - result[0] += 0.018410644; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { - result[0] += -0.0025795808; - } else { - result[0] += 0.0025427365; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 44))) { - result[0] += 0.0063687847; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 158))) { - result[0] += 0.021263849; - } else { - result[0] += 0.037114166; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 12))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { - result[0] += -0.007888737; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.0043152072; - } else { - result[0] += -0.00065306626; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 184))) { - result[0] += 0.0002832889; - } else { - result[0] += 0.023496693; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 164))) { - result[0] += -0.004027024; - } else { - result[0] += 0.0036689022; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 52))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 168))) { - result[0] += -0.006764188; - } else { - result[0] += -0.015382841; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 188))) { - result[0] += -0.0006603152; - } else { - result[0] += -0.0053936313; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { - result[0] += 0.0016898311; - } else { - result[0] += 0.010389189; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { - result[0] += -0.0190918; - } else { - result[0] += 6.013682e-05; - } - } - } - } - } - } - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 284))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 30))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += 0.0022224819; - } else { - result[0] += -9.1448324e-05; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { - result[0] += 0.008647813; - } else { - result[0] += 0.0013861794; - } - } - } else { - result[0] += -0.0051472313; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { - result[0] += 0.022298444; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { - result[0] += 0.014823428; - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 78))) { - result[0] += 0.0028555428; - } else { - result[0] += -0.0060178605; - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 8))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 204))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 144))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { - result[0] += -0.011365654; - } else { - result[0] += 0.008808569; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 166))) { - result[0] += -0.024276486; - } else { - result[0] += -0.0131767215; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 86))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - result[0] += 0.00949425; - } else { - result[0] += -0.013411551; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - result[0] += -0; - } else { - result[0] += 0.012317988; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - result[0] += 0.0853606; - } else { - result[0] += 0.0014590746; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 42))) { - result[0] += -0.04024057; - } else { - result[0] += 0.025248704; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { - result[0] += -0.00020014495; - } else { - result[0] += -0.008825987; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { - result[0] += -0.0026628354; - } else { - result[0] += 0.00010321446; - } - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { - result[0] += -0.002153416; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - result[0] += -0.0011085857; - } else { - result[0] += 0.00558898; - } - } - } - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - result[0] += -0.0072398344; - } else { - result[0] += -0.00012400204; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { - result[0] += 0.025649467; - } else { - result[0] += -0.0001327928; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 94))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { - result[0] += -0.0015077029; - } else { - result[0] += -0.014433789; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += 0.026124759; - } else { - result[0] += 0.0094175115; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 98))) { - result[0] += -0.009028183; - } else { - result[0] += -0.05055972; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - result[0] += 0.008288766; - } else { - result[0] += -0.03171199; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 74))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += 0.029759515; - } else { - result[0] += -0.019024173; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 86))) { - result[0] += 0.0045653116; - } else { - result[0] += 6.555731e-05; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 106))) { - result[0] += 0.029950727; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.0046233633; - } else { - result[0] += 0.020635402; - } - } else { - result[0] += 0.0056214184; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += -0.03250946; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - result[0] += 0.013577218; - } else { - result[0] += -0.031636387; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 62))) { - result[0] += -0.005065638; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.004201682; - } else { - result[0] += 0.0023832265; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 84))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 84))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 152))) { - result[0] += -0.023720464; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - result[0] += -0.0023072253; - } else { - result[0] += 0.0019047937; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 158))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { - result[0] += -0.0026599832; - } else { - result[0] += 0.03371409; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 98))) { - result[0] += -0.010501557; - } else { - result[0] += 0.03556975; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 92))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - result[0] += 0.005952064; - } else { - result[0] += 0.024354918; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - result[0] += 0.0024803034; - } else { - result[0] += -0.007694894; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 110))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 56))) { - result[0] += 0.016107908; - } else { - result[0] += -0.0016387564; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { - result[0] += 0.003588716; - } else { - result[0] += -1.9828594e-05; - } - } - } - } - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 206))) { - result[0] += 0.0046148184; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { - result[0] += 0.0024027901; - } else { - result[0] += 0.00039783234; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += 0.044661757; - } else { - result[0] += 0.0023485457; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - result[0] += -0.04294611; - } else { - result[0] += -0.02216121; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += 0.0008537049; - } else { - result[0] += -0.020757614; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 192))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 134))) { - result[0] += -0.0010473432; - } else { - result[0] += -0.012557432; - } - } else { - result[0] += 0.0345631; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 10))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { - result[0] += 0.009390167; - } else { - result[0] += 0.0029766764; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 12))) { - result[0] += -0.00874328; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - result[0] += -0.011705816; - } else { - result[0] += 0.0072610932; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { - result[0] += -0.00043213202; - } else { - result[0] += 0.0003012484; - } - } - } - } - } - } - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 14))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - result[0] += -0.0078075933; - } else { - result[0] += -0.03296116; - } - } else { - result[0] += 0.0045325574; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 12))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - result[0] += 0.0027738758; - } else { - result[0] += -0.0002652021; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.017156854; - } else { - result[0] += 0.0061222934; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - result[0] += 0.009275707; - } else { - result[0] += -0.008091964; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - result[0] += 0.02927331; - } else { - result[0] += 0.0036204413; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - result[0] += -0.009472274; - } else { - result[0] += -0.036276244; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { - result[0] += -0.0055947257; - } else { - result[0] += -0.016915802; - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - result[0] += 0.0031531143; - } else { - result[0] += -0.020101098; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 10))) { - result[0] += -0.019649826; - } else { - result[0] += -0.0027359962; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { - result[0] += 0.02526692; - } else { - result[0] += -0; - } - } else { - result[0] += 0.0012190904; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 12))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { - result[0] += 0.079478934; - } else { - result[0] += 0.004041801; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.05100611; - } else { - result[0] += 0.013841884; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - result[0] += -0.0001244545; - } else { - result[0] += 0.0013780014; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - result[0] += 0.004001136; - } else { - result[0] += -0.011721842; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - result[0] += 0.015507097; - } else { - result[0] += -0.002207271; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 68))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 174))) { - result[0] += -0.0029817559; - } else { - result[0] += 0.0030468712; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { - result[0] += 0.008323856; - } else { - result[0] += 0.0378311; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - result[0] += 0.019646537; - } else { - result[0] += -0.009434113; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { - result[0] += 0.015092474; - } else { - result[0] += 0.00023066562; - } - } - } - } - } - } - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 26))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 12))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 14))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { - result[0] += 0.00044216277; - } else { - result[0] += 0.0030401708; - } - } else { - result[0] += -0.053345073; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - result[0] += 0.024727738; - } else { - result[0] += -0.008567747; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 38))) { - result[0] += 0.036883164; - } else { - result[0] += 0.014384936; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.004501282; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 14))) { - result[0] += -0.023340857; - } else { - result[0] += 0.0061771907; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.024513956; - } else { - result[0] += -0.0015961519; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { - result[0] += 0.0052294037; - } else { - result[0] += 0.00016616772; - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 218))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 62))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { - result[0] += 0.0042944904; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { - result[0] += -0.035668056; - } else { - result[0] += 0.033606566; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { - result[0] += 0.013674865; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 98))) { - result[0] += 0.0019538465; - } else { - result[0] += -0.001950983; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 6))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += 0.0020924758; - } else { - result[0] += 0.00730929; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 78))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 10))) { - result[0] += 0.00075499614; - } else { - result[0] += 0.0019689128; - } - } else { - result[0] += -0.0016815666; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 58))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 226))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 8))) { - result[0] += -0.0115894945; - } else { - result[0] += -0.051073868; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - result[0] += 0.011092906; - } else { - result[0] += 0.00051702786; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 210))) { - result[0] += -0.013087346; - } else { - result[0] += -0.0062652132; - } - } else { - result[0] += 0.009260368; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 92))) { - result[0] += -0.013211399; - } else { - result[0] += -0.005936895; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 64))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += 0.018809779; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { - result[0] += -0.00044671824; - } else { - result[0] += 0.0025783246; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { - result[0] += -0.008156588; - } else { - result[0] += 0.011215605; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - result[0] += 0.009001787; - } else { - result[0] += 0.027359499; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += 0.03523052; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { - result[0] += -0.016149698; - } else { - result[0] += -0.0014326718; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 82))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { - result[0] += 0.009203473; - } else { - result[0] += -0.01101023; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 30))) { - result[0] += 0.007282652; - } else { - result[0] += -3.0036234e-05; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 240))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 160))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { - result[0] += 0.00011574587; - } else { - result[0] += 0.021244911; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { - result[0] += 0.0015066937; - } else { - result[0] += 0.01799863; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 116))) { - result[0] += 0.002711843; - } else { - result[0] += 0.01596604; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 178))) { - result[0] += -0.0002499475; - } else { - result[0] += 0.0038768928; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - result[0] += 0.022616291; - } else { - result[0] += -0; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { - result[0] += -0.018833179; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { - result[0] += -0.007184063; - } else { - result[0] += -0.00079069706; - } - } - } - } - } else { - result[0] += 0.0025515545; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 254))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 208))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 230))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 218))) { - result[0] += -9.1387315e-05; - } else { - result[0] += -0.0014537438; - } - } else { - result[0] += 0.0155031625; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 142))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 140))) { - result[0] += -0.003084584; - } else { - result[0] += -0.01318902; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 160))) { - result[0] += 0.0037594668; - } else { - result[0] += -0.0014401844; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 88))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { - result[0] += 0.0035882175; - } else { - result[0] += -0.0048747966; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 162))) { - result[0] += 0.0041894084; - } else { - result[0] += 0.0011686251; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 210))) { - result[0] += -0.0026981125; - } else { - result[0] += -0.009141969; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { - result[0] += 0.0005261341; - } else { - result[0] += -0.0018949058; - } - } - } - } - } else { - result[0] += 0.018716332; - } - } - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { - result[0] += 2.3203478e-05; - } else { - result[0] += -0.0077802497; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - result[0] += 0.031958427; - } else { - result[0] += 0.0035587456; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 128))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { - result[0] += 0.002486235; - } else { - result[0] += 0.011185441; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 204))) { - result[0] += -0.0068403906; - } else { - result[0] += -0.001759151; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 140))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.0073994272; - } else { - result[0] += -0.016759716; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 108))) { - result[0] += 0.028997958; - } else { - result[0] += -0.00054327224; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { - result[0] += 0.02114836; - } else { - result[0] += -0.011946307; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 264))) { - result[0] += 0.002376872; - } else { - result[0] += -0.0016341202; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - result[0] += -0.03481881; - } else { - result[0] += -0.007797651; - } - } else { - result[0] += -0.00034481628; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - result[0] += 0.0057917973; - } else { - result[0] += 0.0218565; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 152))) { - result[0] += -0.001166375; - } else { - result[0] += 0.0030204598; - } - } - } - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 100))) { - result[0] += -8.485738e-05; - } else { - result[0] += 0.0070774257; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 148))) { - result[0] += -0.0021743942; - } else { - result[0] += -0.029421; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 172))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - result[0] += 0.0006834134; - } else { - result[0] += 0.010325692; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { - result[0] += -0.001693792; - } else { - result[0] += 0.0011259892; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - result[0] += -0.0077881524; - } else { - result[0] += 0.0026733195; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 166))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 32))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 72))) { - result[0] += 0.028013662; - } else { - result[0] += 0.0070042475; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { - result[0] += 0.004207076; - } else { - result[0] += -0.016646465; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 182))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - result[0] += 0.014890812; - } else { - result[0] += -0.0032379036; - } - } else { - result[0] += 0.0058797766; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 166))) { - result[0] += -0.004874174; - } else { - result[0] += -0.014910466; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 194))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { - result[0] += -0.0024306828; - } else { - result[0] += -0.021217367; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 232))) { - result[0] += 0.0033338335; - } else { - result[0] += -0.00023477618; - } - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 194))) { - result[0] += -0.037188523; - } else { - result[0] += -0.012556768; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 4))) { - result[0] += 0.0015031213; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - result[0] += 0.038557947; - } else { - result[0] += 0.018226711; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 84))) { - result[0] += -0.02505053; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 204))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { - result[0] += -0.0005712052; - } else { - result[0] += -0.0054953173; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 216))) { - result[0] += 0.0013088783; - } else { - result[0] += -0.0013262478; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 214))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 92))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 246))) { - result[0] += -0.0016769763; - } else { - result[0] += 0.004949599; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - result[0] += -0.022474889; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - result[0] += 0.011304743; - } else { - result[0] += 0.0049235076; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 86))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 82))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - result[0] += -0.0013205075; - } else { - result[0] += 0.005631945; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { - result[0] += -0.013254325; - } else { - result[0] += -0.0048218477; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 234))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - result[0] += 0.0018386835; - } else { - result[0] += -0.010448969; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { - result[0] += -0.0026523883; - } else { - result[0] += 0.00015089083; - } - } - } - } - } - } - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 198))) { - result[0] += 0.0024612967; - } else { - result[0] += -0.00267146; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 134))) { - result[0] += 0.0072369473; - } else { - result[0] += 0.03716041; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { - result[0] += 0.016798072; - } else { - result[0] += -0.020668425; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - result[0] += -0.0026954925; - } else { - result[0] += 0.00023960511; - } - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 140))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.009252391; - } else { - result[0] += 0.0032986738; - } - } else { - result[0] += -0.01665429; - } - } else { - result[0] += 0.0028180957; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 128))) { - result[0] += 0.02164252; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - result[0] += -0.012067605; - } else { - result[0] += 0.0026951; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 48))) { - result[0] += 0.015092296; - } else { - result[0] += -0.038058575; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { - result[0] += 0.00892017; - } else { - result[0] += 0.031467702; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 216))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 154))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - result[0] += 0.0022178197; - } else { - result[0] += -0.005610294; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - result[0] += 0.011955344; - } else { - result[0] += -0.0074412758; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - result[0] += 0.0014451804; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.0032450047; - } else { - result[0] += 0.00080591295; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 164))) { - result[0] += 0.010952067; - } else { - result[0] += -0.003597115; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 236))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 144))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 230))) { - result[0] += 0.0010651236; - } else { - result[0] += 0.019118952; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { - result[0] += -0.0010132345; - } else { - result[0] += 0.0005361346; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - result[0] += 0.0063631246; - } else { - result[0] += -0.005013407; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 216))) { - result[0] += -0.0018120991; - } else { - result[0] += 0.0013921553; - } - } - } - } - } - } - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 6))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { - result[0] += -0.0027856524; - } else { - result[0] += 0.011518478; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 52))) { - result[0] += -0.024774041; - } else { - result[0] += -0.0019057058; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 10))) { - result[0] += -0.01394673; - } else { - result[0] += 0.006964297; - } - } else { - result[0] += 0.05194057; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 152))) { - result[0] += -0.028471593; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 186))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { - result[0] += -0.0014699342; - } else { - result[0] += 0.024541676; - } - } else { - result[0] += -0.010517646; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 210))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - result[0] += -0.006757573; - } else { - result[0] += 0.0052805874; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 52))) { - result[0] += -0.01105545; - } else { - result[0] += 0.001222587; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 72))) { - result[0] += -0.011149263; - } else { - result[0] += -0.031825926; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 80))) { - result[0] += -6.1052204e-05; - } else { - result[0] += -0.0018558489; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += -0; - } else { - result[0] += 0.037658174; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 184))) { - result[0] += -0.022644173; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 46))) { - result[0] += -0.011395801; - } else { - result[0] += -0.004704497; - } - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 40))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 26))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 116))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { - result[0] += 0.018175911; - } else { - result[0] += 0.0022316524; - } - } else { - result[0] += -0.01448056; - } - } else { - result[0] += 0.03850323; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 10))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { - result[0] += -0.0056385025; - } else { - result[0] += -0.001911032; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 142))) { - result[0] += 0.006841138; - } else { - result[0] += 0.0015013752; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - result[0] += -0.003987257; - } else { - result[0] += 0.0014403629; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 140))) { - result[0] += 0.0001458845; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 168))) { - result[0] += -0.018207422; - } else { - result[0] += 0.0007071493; - } - } - } else { - result[0] += -0.014048204; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { - result[0] += 0.024706252; - } else { - result[0] += 0.012115999; - } - } else { - result[0] += -0.010074422; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 160))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - result[0] += 0.00012816093; - } else { - result[0] += -0.0048219212; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - result[0] += 0.00042065704; - } else { - result[0] += -0.00044052277; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 2))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - result[0] += 0.012054322; - } else { - result[0] += -0.012439433; - } - } else { - result[0] += 0.004142578; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 246))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { - result[0] += 0.0005554498; - } else { - result[0] += 0.0034215685; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - result[0] += 0.00583103; - } else { - result[0] += 0.00022540719; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 0))) { - result[0] += 0.003226608; - } else { - result[0] += 0.029922882; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 20))) { - result[0] += -0.006730991; - } else { - result[0] += -0.001442519; - } - } - } - } - } else { - result[0] += 0.020426724; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 254))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 16))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - result[0] += 0.0003546644; - } else { - result[0] += -0.0044249105; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 132))) { - result[0] += 0.0020044944; - } else { - result[0] += -0.0026672701; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 10))) { - result[0] += -0.007007061; - } else { - result[0] += -0.0024277617; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 156))) { - result[0] += 0.0035193504; - } else { - result[0] += 0.016300239; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - result[0] += -0.002883124; - } else { - result[0] += 0.0034831774; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 32))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 222))) { - result[0] += -0.004244165; - } else { - result[0] += -0.02417766; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 36))) { - result[0] += -0.0009843176; - } else { - result[0] += -0.00013388977; - } - } - } - } - } else { - result[0] += 0.013616696; - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 44))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 42))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 20))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 28))) { - result[0] += 0.00050606584; - } else { - result[0] += -0.0017093392; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - result[0] += -0.0051450143; - } else { - result[0] += 0.0035812582; - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.010360922; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 6))) { - result[0] += 0.0013417737; - } else { - result[0] += -0.0003085478; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 102))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += 0.06123736; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { - result[0] += -0.010781719; - } else { - result[0] += -0.002857905; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 52))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - result[0] += 0.013251043; - } else { - result[0] += -0.020714538; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 76))) { - result[0] += -0.0037599222; - } else { - result[0] += -0.0004986952; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 24))) { - result[0] += 0.08917741; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 38))) { - result[0] += -0.030496597; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 252))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 274))) { - result[0] += -0.0025217582; - } else { - result[0] += 0.00018528964; - } - } else { - result[0] += -0.014542065; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 222))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.009864878; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { - result[0] += 0.017176276; - } else { - result[0] += 0.030590722; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 92))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { - result[0] += 0.0058373446; - } else { - result[0] += -0.012375738; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += 0.047506116; - } else { - result[0] += 0.011469667; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { - result[0] += 0.004630673; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 252))) { - result[0] += -0.004851036; - } else { - result[0] += -0.023372188; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 50))) { - result[0] += 0.036052324; - } else { - result[0] += -0.002185328; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 60))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 56))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 224))) { - result[0] += -0.00040355555; - } else { - result[0] += -0.0124766445; - } - } else { - result[0] += 0.008253347; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 92))) { - result[0] += -0.011534996; - } else { - result[0] += -0.0021382947; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { - result[0] += -0.0014395117; - } else { - result[0] += 0.019073278; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - result[0] += 0.019316873; - } else { - result[0] += 0.0020871195; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - result[0] += 0.004829779; - } else { - result[0] += 0.021349184; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += -0.0045497594; - } else { - result[0] += 0.015120694; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 66))) { - result[0] += 0.0015102568; - } else { - result[0] += -3.608707e-06; - } - } - } - } - } - } - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 54))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - result[0] += 0.00032929177; - } else { - result[0] += 0.029328126; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 216))) { - result[0] += -0.00073194504; - } else { - result[0] += -0.009144339; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += -0.032370485; - } else { - result[0] += 0.0061941766; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 50))) { - result[0] += 0.053673845; - } else { - result[0] += 0.018453414; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 178))) { - result[0] += -0.010862867; - } else { - result[0] += 0.0031142721; - } - } else { - result[0] += 0.04961296; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 28))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 150))) { - result[0] += -0.035367988; - } else { - result[0] += -0.014287199; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 18))) { - result[0] += 0.0022301266; - } else { - result[0] += -0.0076341094; - } - } - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 74))) { - result[0] += 0.009890891; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 32))) { - result[0] += -0.034998115; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { - result[0] += 0.003447539; - } else { - result[0] += 0.0015428506; - } - } - } - } else { - result[0] += 0.021317383; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { - result[0] += 0.06219096; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 106))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 64))) { - result[0] += 0.0083704125; - } else { - result[0] += 0.0011106033; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += -0.006979663; - } else { - result[0] += -0.004151942; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - result[0] += 0.017166223; - } else { - result[0] += 0.0019738798; - } - } else { - result[0] += -0.025185201; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 70))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 114))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 66))) { - result[0] += 0.0008634852; - } else { - result[0] += -0.008104631; - } - } else { - result[0] += 0.0040236074; - } - } else { - result[0] += 0.010268874; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 82))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 74))) { - result[0] += -0.029302115; - } else { - result[0] += 0.027285008; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { - result[0] += -0.004545824; - } else { - result[0] += -0.00053453137; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - result[0] += 0.0019230042; - } else { - result[0] += -4.2119464e-06; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { - result[0] += -0.010225962; - } else { - result[0] += -0.00024979832; - } - } - } - } - } - } - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 34))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 36))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 26))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 200))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 4))) { - result[0] += -0.0014482096; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 196))) { - result[0] += 0.0071594575; - } else { - result[0] += 0.027249644; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - result[0] += 0.011023153; - } else { - result[0] += -0.010220974; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - result[0] += 0.0010131482; - } else { - result[0] += -0.00073193485; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 208))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - result[0] += -0.049486402; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - result[0] += -0.00315987; - } else { - result[0] += -0.014758741; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 26))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 22))) { - result[0] += 0.004376946; - } else { - result[0] += 0.040119316; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 16))) { - result[0] += -0.010627906; - } else { - result[0] += 0.0038438165; - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 16))) { - result[0] += 0.0069011683; - } else { - result[0] += 0.016726343; - } - } else { - result[0] += 0.0049917004; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 258))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 186))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - result[0] += 0.01811142; - } else { - result[0] += 0.0030194859; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.002317766; - } else { - result[0] += 0.00087052287; - } - } - } else { - result[0] += -0.0039971955; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 40))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 162))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { - result[0] += -0.0014173501; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 154))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 152))) { - result[0] += -0.0050792135; - } else { - result[0] += 0.006009587; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { - result[0] += -0.00039048417; - } else { - result[0] += -0.009521704; - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 182))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 182))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { - result[0] += -0.0080691455; - } else { - result[0] += 0.016010879; - } - } else { - result[0] += -0.01479948; - } - } else { - result[0] += -0.02853345; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 46))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 82))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 38))) { - result[0] += -0.015648594; - } else { - result[0] += 0.005108214; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - result[0] += 0.011618692; - } else { - result[0] += 0.0042830543; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 200))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { - result[0] += 0.005279971; - } else { - result[0] += -0.009966015; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { - result[0] += -0.0035748226; - } else { - result[0] += 0.0021337403; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 172))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { - result[0] += -0.025904989; - } else { - result[0] += 0.0019193542; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - result[0] += -0.00022157452; - } else { - result[0] += -0.0015951125; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 28))) { - result[0] += -0.00094731955; - } else { - result[0] += 0.029716676; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - result[0] += 0.00062934245; - } else { - result[0] += -0.00041988047; - } - } - } - } - } - } - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 112))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 110))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 112))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.0063841576; - } else { - result[0] += 0.0017673693; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - result[0] += -0.0013868456; - } else { - result[0] += 0.00015485496; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 72))) { - result[0] += -0.009897975; - } else { - result[0] += -0.030677944; - } - } else { - result[0] += 0.021359773; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - result[0] += 0.021139013; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.0034971682; - } else { - result[0] += -0.0043157833; - } - } else { - result[0] += -0.010502246; - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 144))) { - result[0] += 0.02943166; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.005999833; - } else { - result[0] += -0.0019856528; - } - } else { - result[0] += 0.013738169; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 70))) { - result[0] += 0.008470384; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - result[0] += -0.003441482; - } else { - result[0] += -0.011388111; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 110))) { - result[0] += 0.0024586066; - } else { - result[0] += -0.021055488; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 50))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 26))) { - result[0] += -0.026231218; - } else { - result[0] += -0.0044914987; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - result[0] += 0.027324816; - } else { - result[0] += 0.0072242687; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 186))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { - result[0] += -0.0015850182; - } else { - result[0] += -0.010035633; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 134))) { - result[0] += 0.054534066; - } else { - result[0] += 0.0008897401; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 166))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += 0.075174846; - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += -0.0014329397; - } else { - result[0] += 0.002409567; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 180))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - result[0] += -0.007922634; - } else { - result[0] += -0.00042070524; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 158))) { - result[0] += -0.00044648108; - } else { - result[0] += 0.0013399239; - } - } - } - } - } - } - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 180))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 168))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += 0.0013971846; - } else { - result[0] += -6.5496147e-06; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - result[0] += -0.02558857; - } else { - result[0] += -0.0035198168; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 68))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { - result[0] += -0.0007052002; - } else { - result[0] += 0.011330684; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 110))) { - result[0] += 0.008754961; - } else { - result[0] += 0.017457347; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 6))) { - result[0] += 0.021995483; - } else { - result[0] += 0.0037829764; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.013063647; - } else { - result[0] += -0.008626144; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 218))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 210))) { - result[0] += -0.00048324605; - } else { - result[0] += 0.014737451; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 168))) { - result[0] += 0.03996015; - } else { - result[0] += -0.004091793; - } - } - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 170))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 144))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - result[0] += 0.0020200752; - } else { - result[0] += 0.016987968; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 192))) { - result[0] += -0.013594501; - } else { - result[0] += 0.0012423365; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - result[0] += -0.0187645; - } else { - result[0] += -0.007347082; - } - } - } else { - result[0] += 0.021339634; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 198))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 112))) { - result[0] += 0.06586228; - } else { - result[0] += 0.021907127; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 94))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 142))) { - result[0] += 0.0030923286; - } else { - result[0] += 0.00036826293; - } - } else { - result[0] += 0.04356821; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += 0.01220308; - } else { - result[0] += -0.007081172; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 54))) { - result[0] += 3.0494935e-05; - } else { - result[0] += -0.006945651; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 16))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 10))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - result[0] += 0.00072253105; - } else { - result[0] += -0.020929243; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 194))) { - result[0] += 0.057745833; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 32))) { - result[0] += -0.0037926536; - } else { - result[0] += 0.042418174; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.034033656; - } else { - result[0] += -0.021374384; - } - } else { - result[0] += -0; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 40))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - result[0] += -0.0036783994; - } else { - result[0] += -0.012444709; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 156))) { - result[0] += -0.00028157423; - } else { - result[0] += -0.0030722509; - } - } - } - } - } - } - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 6))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 248))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - result[0] += -0.018552605; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 212))) { - result[0] += 0.013609043; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - result[0] += -0.0035600197; - } else { - result[0] += 0.005957694; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 254))) { - result[0] += -0.0039241104; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 42))) { - result[0] += -0.0053416244; - } else { - result[0] += 0.0076100454; - } - } else { - result[0] += 0.0010679305; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 266))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 8))) { - result[0] += -0.005295289; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 234))) { - result[0] += -1.6435115e-05; - } else { - result[0] += -0.0010428902; - } - } - } else { - result[0] += -0.0050287596; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 228))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 274))) { - result[0] += 0.025240282; - } else { - result[0] += 0.006482261; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 264))) { - result[0] += 0.0052149496; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 202))) { - result[0] += 0.006406661; - } else { - result[0] += -0.0011082895; - } - } - } - } - } - } else { - result[0] += 0.010802027; - } - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 168))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 140))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 136))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 132))) { - result[0] += 2.1751774e-05; - } else { - result[0] += -0.0076580383; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 68))) { - result[0] += -0.00075408455; - } else { - result[0] += 0.015074461; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - result[0] += -0.0018395454; - } else { - result[0] += 0.015883565; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - result[0] += -0.0091815125; - } else { - result[0] += 0.0014608675; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 172))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - result[0] += 0.0026474108; - } else { - result[0] += 0.01692606; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 140))) { - result[0] += 0.02202346; - } else { - result[0] += -0.012683655; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 228))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { - result[0] += -0.0071168602; - } else { - result[0] += -0.00039252793; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - result[0] += 0.001928371; - } else { - result[0] += -0.0002336656; - } - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 148))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 30))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 26))) { - result[0] += -0.025342045; - } else { - result[0] += 0.020604817; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { - result[0] += -0.013844515; - } else { - result[0] += -0.0027904052; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 142))) { - result[0] += 0.019259404; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 170))) { - result[0] += 0.0053436733; - } else { - result[0] += 0.0006968994; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 72))) { - result[0] += -0.013023679; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 96))) { - result[0] += -0.0089388685; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 140))) { - result[0] += -0.013734943; - } else { - result[0] += 0.0016584283; - } - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 158))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 4))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - result[0] += -0.00055690337; - } else { - result[0] += -0.016434593; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - result[0] += 0.076187894; - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 144))) { - result[0] += 0.0040504658; - } else { - result[0] += 0.010756901; - } - } - } - } else { - result[0] += 0.023556888; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 184))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 0))) { - result[0] += 0.06953845; - } else { - result[0] += -0.0023770647; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 186))) { - result[0] += 0.021977352; - } else { - result[0] += 0.0001357905; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 44))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 72))) { - result[0] += 0.0026280086; - } else { - result[0] += 0.015929218; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 44))) { - result[0] += 0.0037350792; - } else { - result[0] += 0.00039957697; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 18))) { - result[0] += 0.03222883; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 176))) { - result[0] += -0.011439302; - } else { - result[0] += -0.0039858185; - } - } - } - } - } - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 0))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 16))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - result[0] += -0.0012402734; - } else { - result[0] += -0.019989545; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 8))) { - result[0] += 0.0026303197; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.040178392; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.0065984237; - } else { - result[0] += 0.02927506; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.020329256; - } else { - result[0] += -0; - } - } else { - result[0] += 0.012915528; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 2))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - result[0] += 0.009343422; - } else { - result[0] += -0.012758999; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - result[0] += -0.027498607; - } else { - result[0] += -0.012787706; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 134))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 24))) { - result[0] += 0.013563978; - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - result[0] += -0; - } else { - result[0] += 0.017173748; - } - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 88))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 42))) { - result[0] += -0; - } else { - result[0] += -0.018669667; - } - } else { - result[0] += -0; - } - } - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 6))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - result[0] += 0.0013438625; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 94))) { - result[0] += 0.0075284163; - } else { - result[0] += 0.020848578; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 10))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 224))) { - result[0] += 0.0053101867; - } else { - result[0] += -0.005638622; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 226))) { - result[0] += 0.00015324964; - } else { - result[0] += 0.0012270033; - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 240))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 238))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 208))) { - result[0] += -0.0004296944; - } else { - result[0] += 0.00052584225; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 174))) { - result[0] += 0.0010627261; - } else { - result[0] += -0.00823095; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 242))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 256))) { - result[0] += 0.0063353493; - } else { - result[0] += 0.00052647333; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 190))) { - result[0] += 0.0017266994; - } else { - result[0] += -0.0006555208; - } - } - } - } - } - } - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 94))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 34))) { - result[0] += 0.0004357934; - } else { - result[0] += -0.0016803583; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - result[0] += 0.013963197; - } else { - result[0] += 0.0010920282; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 70))) { - result[0] += -0.0020354139; - } else { - result[0] += -0.0084240325; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { - result[0] += 0.00018836466; - } else { - result[0] += -0.001789179; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 98))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { - result[0] += -0.0021141667; - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 142))) { - result[0] += 0.0008162449; - } else { - result[0] += -0.0007128279; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 118))) { - result[0] += 0.005020843; - } else { - result[0] += 0.015004709; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - result[0] += 0.004781536; - } else { - result[0] += 0.0005883411; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 130))) { - result[0] += -0.035336528; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - result[0] += 0.023583809; - } else { - result[0] += 0.0007455337; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 90))) { - result[0] += 0.0023794405; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - result[0] += 0.023832815; - } else { - result[0] += 0.010761608; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 116))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 146))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 196))) { - result[0] += -0.010108376; - } else { - result[0] += -0.0007176217; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { - result[0] += 0.0009595863; - } else { - result[0] += -0.004075228; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 148))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 144))) { - result[0] += -0; - } else { - result[0] += 0.01168954; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 142))) { - result[0] += -0.0033294098; - } else { - result[0] += -6.6527915e-05; - } - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 108))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 118))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 48))) { - result[0] += 0.033734445; - } else { - result[0] += 0.0013792046; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { - result[0] += 0.011527828; - } else { - result[0] += 0.0049809148; - } - } - } else { - result[0] += 0.013943759; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 178))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - result[0] += -0.008578103; - } else { - result[0] += 0.0029740678; - } - } else { - result[0] += -0.018524783; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 140))) { - result[0] += -0.00042957798; - } else { - result[0] += -0.009110951; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 132))) { - result[0] += 0.021989485; - } else { - result[0] += 0.0028953014; - } - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { - result[0] += -0.0064930986; - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 188))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 184))) { - result[0] += 0.00023702074; - } else { - result[0] += 0.0032349098; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 232))) { - result[0] += -0.0035499786; - } else { - result[0] += -0; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 216))) { - result[0] += -0.008582824; - } else { - result[0] += 0.0003269389; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 56))) { - result[0] += 0.006198867; - } else { - result[0] += 0.0029896083; - } - } - } - } - } - } - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 198))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 194))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 148))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 76))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 72))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 24))) { - result[0] += -0.008292492; - } else { - result[0] += 0.048605617; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 22))) { - result[0] += -0.011937882; - } else { - result[0] += -0.0048466916; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 138))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 36))) { - result[0] += -0.0032074524; - } else { - result[0] += 6.964723e-05; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 52))) { - result[0] += 0.024765873; - } else { - result[0] += -0.0021545556; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 110))) { - result[0] += -1.2665853e-05; - } else { - result[0] += -0.023850983; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 154))) { - result[0] += 0.0025796972; - } else { - result[0] += 0.00040402517; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 160))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.0170327; - } else { - result[0] += -0.00829741; - } - } else { - result[0] += -0.0026483634; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 86))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 188))) { - result[0] += 0.0010909947; - } else { - result[0] += 0.0053282953; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 110))) { - result[0] += 0.026514; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 100))) { - result[0] += 0.037232116; - } else { - result[0] += 0.004267938; - } - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 78))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 248))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 126))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 202))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 18))) { - result[0] += 0.026404385; - } else { - result[0] += -0.001118703; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - result[0] += 0.0017804791; - } else { - result[0] += -0.007409166; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 130))) { - result[0] += -0.004484429; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 222))) { - result[0] += 0.031472586; - } else { - result[0] += 0.0001007054; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 252))) { - result[0] += -0.003407167; - } else { - result[0] += 0.001687183; - } - } else { - result[0] += 0.014353302; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 28))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 28))) { - result[0] += -0.0015060778; - } else { - result[0] += -0.0003457068; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 10))) { - result[0] += -0.013887319; - } else { - result[0] += -0.0035536517; - } - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 238))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 160))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 74))) { - result[0] += -0.0055618854; - } else { - result[0] += -0.0014347414; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 224))) { - result[0] += -0.00070218346; - } else { - result[0] += 0.0014398324; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 8))) { - result[0] += 0.023142483; - } else { - result[0] += -0.000307219; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 24))) { - result[0] += 0.0010922215; - } else { - result[0] += 0.017112693; - } - } - } - } - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 214))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 12))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 2))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - result[0] += -0.010769006; - } else { - result[0] += 0.0069697066; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 24))) { - result[0] += 0.02430601; - } else { - result[0] += 0.00639099; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 62))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 20))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 34))) { - result[0] += 0.015791424; - } else { - result[0] += -0.0046518813; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 36))) { - result[0] += -0.03668975; - } else { - result[0] += -0.012895368; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 142))) { - result[0] += 0.03604609; - } else { - result[0] += 0.012836625; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 50))) { - result[0] += -0.010758377; - } else { - result[0] += -0.001967916; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 220))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 80))) { - result[0] += 0.034449853; - } else { - result[0] += 0.0030996487; - } - } else { - result[0] += -0.015721142; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 254))) { - result[0] += 0.039080422; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 222))) { - result[0] += 0.020187657; - } else { - result[0] += 0.0016309306; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 224))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { - result[0] += -0.01591835; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 64))) { - result[0] += -0.006518031; - } else { - result[0] += -0.0026554007; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 248))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - result[0] += 0.00103551; - } else { - result[0] += 0.00901093; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - result[0] += -0.0012550136; - } else { - result[0] += -1.612309e-05; - } - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 116))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 114))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 130))) { - result[0] += 0.00788109; - } else { - result[0] += -0.0018598955; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 60))) { - result[0] += 0.005137967; - } else { - result[0] += -0.0032950975; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 98))) { - result[0] += -0.042013966; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 72))) { - result[0] += -0.0046039354; - } else { - result[0] += 0.011869277; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 172))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - result[0] += -0.026546886; - } else { - result[0] += 0.009092043; - } - } else { - result[0] += 0.017844815; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 230))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 102))) { - result[0] += 0.0068409294; - } else { - result[0] += 0.01327271; - } - } else { - result[0] += 0.000659167; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 40))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 84))) { - result[0] += 0.004663289; - } else { - result[0] += -0.006825484; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 56))) { - result[0] += 0.008568982; - } else { - result[0] += 0.0030761992; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 150))) { - result[0] += -0.014622196; - } else { - result[0] += 0.00041277093; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 42))) { - result[0] += 0.0016062713; - } else { - result[0] += 0.046605274; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 172))) { - result[0] += -0.004711694; - } else { - result[0] += -0.0015061334; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 54))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 222))) { - result[0] += 0.012894141; - } else { - result[0] += -0.0020437974; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 124))) { - result[0] += -0.0009532698; - } else { - result[0] += 0.00017820772; - } - } - } - } - } - } - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 274))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 120))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += 0.0047425376; - } else { - result[0] += 0.009442864; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 58))) { - result[0] += -0.016419962; - } else { - result[0] += 0.00078140077; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { - result[0] += -0.016464185; - } else { - result[0] += -0.0012191174; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 106))) { - result[0] += 0.00046863809; - } else { - result[0] += 0.0035006986; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 124))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 160))) { - result[0] += -0.0128822; - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 170))) { - result[0] += -0.0021174157; - } else { - result[0] += -0.01799887; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 80))) { - result[0] += 0.043405965; - } else { - result[0] += 0.0019986776; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += -0.00859444; - } else { - result[0] += 0.00027344722; - } - } - } - } - } else { - result[0] += 0.013666634; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 88))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 70))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 228))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 0))) { - result[0] += 0.0035516606; - } else { - result[0] += -1.4989321e-05; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 230))) { - result[0] += -0.003034349; - } else { - result[0] += -0.0006231525; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 34))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 244))) { - result[0] += 0.0052153706; - } else { - result[0] += -0.0051266435; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 84))) { - result[0] += -0.0033818122; - } else { - result[0] += -0.0005771067; - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 30))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 244))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { - result[0] += 0.042181622; - } else { - result[0] += 0.004492736; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { - result[0] += -0.01460122; - } else { - result[0] += 0.00014612076; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 146))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 92))) { - result[0] += -0.001969084; - } else { - result[0] += -6.331263e-05; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 160))) { - result[0] += 0.0028343403; - } else { - result[0] += 0.00031744837; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { - result[0] += -0.006944129; - } else { - result[0] += -0.0027726921; - } - } - } - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 278))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 246))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 232))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 66))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 60))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { - result[0] += 0.0005400666; - } else { - result[0] += -0.0019886708; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { - result[0] += -0.009072294; - } else { - result[0] += -0.0026447035; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 76))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - result[0] += -0.0027691973; - } else { - result[0] += 0.00404422; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 276))) { - result[0] += -2.3040471e-05; - } else { - result[0] += 0.012937434; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 22))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += -0.015312892; - } else { - result[0] += -0.004679353; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 102))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { - result[0] += 0.006205307; - } else { - result[0] += 0.0010604822; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { - result[0] += -0.0003646382; - } else { - result[0] += 0.0056393673; - } - } - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 246))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 98))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - result[0] += 0.0009229826; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 262))) { - result[0] += -0.002368063; - } else { - result[0] += -0.00024388141; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 250))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - result[0] += -0.012024042; - } else { - result[0] += -0.0031967615; - } - } else { - result[0] += -0.00085695874; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 260))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 204))) { - result[0] += -0.00046735056; - } else { - result[0] += 0.0040739644; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 258))) { - result[0] += -0.0032260884; - } else { - result[0] += 0.0010031442; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - result[0] += -0.009778283; - } else { - result[0] += 0.019902207; - } - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 120))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 282))) { - result[0] += -0.0005751638; - } else { - result[0] += -0.01039412; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 230))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 122))) { - result[0] += -0.01584022; - } else { - result[0] += 0.0018113088; - } - } else { - result[0] += 0.0046630637; - } - } - } - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 24))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 250))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 4))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 36))) { - result[0] += -0.00047560144; - } else { - result[0] += 0.0013354685; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 90))) { - result[0] += -0.0026048406; - } else { - result[0] += 0.00038235518; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 256))) { - result[0] += 0.0044472557; - } else { - result[0] += 0.03397901; - } - } else { - result[0] += -0.0003922035; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 114))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 14))) { - result[0] += 0.031751767; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 100))) { - result[0] += -0.0036668777; - } else { - result[0] += 0.00452039; - } - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 22))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 18))) { - result[0] += -0.002395912; - } else { - result[0] += 0.030751167; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 42))) { - result[0] += -0.015407211; - } else { - result[0] += 0.011755302; - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 32))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 6))) { - result[0] += -0.03115373; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 8))) { - result[0] += 0.0073042098; - } else { - result[0] += 0.033008788; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 94))) { - result[0] += -0.0064700614; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 4))) { - result[0] += -0.007395641; - } else { - result[0] += -0.02954722; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 14))) { - result[0] += -0.01630216; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 184))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 32))) { - result[0] += 0.008391211; - } else { - result[0] += -0.0021036936; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { - result[0] += -0.00060320273; - } else { - result[0] += 0.0022427232; - } - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 60))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 24))) { - if (LIKELY((data[6].missing != -1) && (data[6].qvalue < 6))) { - result[0] += -0.000107325846; - } else { - result[0] += 0.029023886; - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 48))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 50))) { - result[0] += -0.003524834; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 20))) { - result[0] += 0.002008538; - } else { - result[0] += 0.00040226732; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 132))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 164))) { - result[0] += 0.0028264725; - } else { - result[0] += 0.0078762965; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 190))) { - result[0] += -0.0036010318; - } else { - result[0] += -0.020989217; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 10))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 76))) { - result[0] += -0.025969082; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 76))) { - result[0] += -0.0037539073; - } else { - result[0] += -0.015046534; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 52))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 46))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - result[0] += 0.008154599; - } else { - result[0] += -0.00034012404; - } - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.00019781369; - } else { - result[0] += -0.0046178605; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 60))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 122))) { - result[0] += 0.0022514695; - } else { - result[0] += 0.02182677; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 66))) { - result[0] += -0.002138548; - } else { - result[0] += 0.00019392448; - } - } - } - } - } - } - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 2))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 4))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 4))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 2))) { - result[0] += -0.018533966; - } else { - result[0] += 0.009372647; - } - } else { - result[0] += -0.013326846; - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 0))) { - result[0] += 0.017056702; - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 250))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 260))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 36))) { - result[0] += 0.0010617184; - } else { - result[0] += 0.003547955; - } - } else { - result[0] += -0.002409021; - } - } else { - result[0] += 0.022888973; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 178))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 218))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 240))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 238))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 176))) { - result[0] += -6.381352e-05; - } else { - result[0] += 0.01608517; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - result[0] += 0.031114805; - } else { - result[0] += 0.0031871856; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { - result[0] += 0.0010918784; - } else { - result[0] += 0.010223559; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - result[0] += -0.0053569055; - } else { - result[0] += -0.00082102185; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 140))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 122))) { - result[0] += 0.017271513; - } else { - result[0] += -0.0010735758; - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 210))) { - result[0] += -0.0022431507; - } else { - result[0] += -0.0044196337; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 114))) { - result[0] += 0.03313246; - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 198))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 166))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 116))) { - result[0] += 0.0011855942; - } else { - result[0] += 0.0060151634; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 142))) { - result[0] += -0.007734927; - } else { - result[0] += 0.0012392174; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 200))) { - result[0] += -0.0054051043; - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 252))) { - result[0] += -0.00018920467; - } else { - result[0] += 0.0010806855; - } - } - } - } - } - } - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 270))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 210))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 186))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 184))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 174))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 170))) { - result[0] += -5.627934e-05; - } else { - result[0] += 0.004937595; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 176))) { - result[0] += -0.0081005385; - } else { - result[0] += -0.00034580208; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 214))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 112))) { - result[0] += 0.0017502941; - } else { - result[0] += 0.0067032403; - } - } else { - result[0] += 0.05561105; - } - } - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 114))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 148))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 104))) { - result[0] += -0.017684242; - } else { - result[0] += -0.008623506; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 210))) { - result[0] += -0.0030512868; - } else { - result[0] += 0.00268173; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 138))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 116))) { - result[0] += -0.008280813; - } else { - result[0] += -0.0006436382; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 68))) { - result[0] += 0.005869223; - } else { - result[0] += 0.0157161; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 218))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 38))) { - result[0] += -0.040014755; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 68))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 64))) { - result[0] += 0.0034462882; - } else { - result[0] += 0.012366778; - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 126))) { - result[0] += 0.0029618521; - } else { - result[0] += -0.0031078772; - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 78))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 68))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 156))) { - result[0] += -0.0013473729; - } else { - result[0] += 0.0062149167; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { - result[0] += 0.017057097; - } else { - result[0] += -0.010900224; - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 144))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 212))) { - result[0] += 0.0012661046; - } else { - result[0] += 0.0069153532; - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { - result[0] += -0.016097846; - } else { - result[0] += -0.00017735653; - } - } - } - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 0))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 272))) { - result[0] += 0.010808589; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 218))) { - result[0] += -0.009866092; - } else { - result[0] += 0.0047606938; - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 216))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 108))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 266))) { - result[0] += -0; - } else { - result[0] += 0.014268956; - } - } else { - result[0] += 0.0023136109; - } - } else { - result[0] += -0.0024249533; - } - } - } - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 96))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 74))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 64))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 60))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 40))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 136))) { - result[0] += -0.0002939115; - } else { - result[0] += 0.03095895; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 36))) { - result[0] += -0.016124493; - } else { - result[0] += 0.0012605732; - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 128))) { - result[0] += 0.00055091607; - } else { - result[0] += -0.0038312187; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 62))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - result[0] += 0.020991528; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 30))) { - result[0] += 0.00425994; - } else { - result[0] += -0.012923174; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 72))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 112))) { - result[0] += -0.008987924; - } else { - result[0] += 0.0027231926; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 136))) { - result[0] += 0.00033489117; - } else { - result[0] += 0.0031028683; - } - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 94))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 76))) { - result[0] += -0.023087258; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 40))) { - result[0] += 0.040452894; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 20))) { - result[0] += -0.004355786; - } else { - result[0] += 0.00052141724; - } - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 98))) { - result[0] += -0.026337389; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 78))) { - result[0] += -0.011440942; - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { - result[0] += 0.0051072245; - } else { - result[0] += -0.0016136405; - } - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 94))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 104))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 66))) { - result[0] += -0.016175192; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 90))) { - result[0] += -0.003506745; - } else { - result[0] += -0.0008637124; - } - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 164))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 80))) { - result[0] += 0.039352115; - } else { - result[0] += 0.009527031; - } - } else { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 168))) { - result[0] += -0.014133029; - } else { - result[0] += 0.0009379142; - } - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 124))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - result[0] += 0.0014655776; - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 94))) { - result[0] += 0.00799122; - } else { - result[0] += 0.02205122; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 150))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 116))) { - result[0] += 0.00084181456; - } else { - result[0] += 0.03656875; - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - result[0] += -0.0324972; - } else { - result[0] += -0.0043041403; - } - } - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 108))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 130))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 106))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 120))) { - result[0] += -0.014786902; - } else { - result[0] += -0.027247995; - } - } else { - result[0] += -0.013442961; - } - } else { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 106))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 96))) { - result[0] += 0.014649215; - } else { - result[0] += 0.00018173472; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 82))) { - result[0] += -0.003195444; - } else { - result[0] += -0.017632857; - } - } - } - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 218))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 172))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 180))) { - result[0] += 0.0011816436; - } else { - result[0] += 0.006784923; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 174))) { - result[0] += -0.0025611892; - } else { - result[0] += 0.00039800836; - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 222))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 108))) { - result[0] += -0.015747108; - } else { - result[0] += -0.0025709863; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 222))) { - result[0] += 0.00016036122; - } else { - result[0] += -0.004456304; - } - } - } - } - } - } - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 116))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 44))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 0))) { - result[0] += -0.043082695; - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 2))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 14))) { - result[0] += -0.015321686; - } else { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 18))) { - result[0] += 0.009543039; - } else { - result[0] += -0.00492233; - } - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 60))) { - result[0] += -0.018702324; - } else { - result[0] += 0.0030421054; - } - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 110))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 96))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 104))) { - result[0] += 0.023666665; - } else { - result[0] += -0.003085631; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 102))) { - result[0] += 0.0025167426; - } else { - result[0] += -0.0006228736; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 126))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 52))) { - result[0] += 0.025037153; - } else { - result[0] += 0.00086976105; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 130))) { - result[0] += -0.00833582; - } else { - result[0] += 6.457727e-05; - } - } - } - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 48))) { - result[0] += -0.034489177; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 50))) { - result[0] += 0.059084423; - } else { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 54))) { - result[0] += -0.008554864; - } else { - result[0] += -0.000933964; - } - } - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 98))) { - if (UNLIKELY((data[6].missing != -1) && (data[6].qvalue < 2))) { - result[0] += -0.005567677; - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 74))) { - result[0] += 0.026860246; - } else { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 240))) { - if (LIKELY((data[1].missing != -1) && (data[1].qvalue < 238))) { - result[0] += 0.003906974; - } else { - result[0] += 0.016403912; - } - } else { - result[0] += -0.0005779523; - } - } - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 102))) { - if (UNLIKELY((data[1].missing != -1) && (data[1].qvalue < 68))) { - result[0] += -0.021588845; - } else { - result[0] += -0.0072186044; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 152))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 88))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 56))) { - result[0] += 0.00058102736; - } else { - result[0] += 0.0042956616; - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { - result[0] += -0.0021127523; - } else { - result[0] += 0.0012841078; - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 162))) { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 186))) { - result[0] += -0.004427323; - } else { - result[0] += -0.0004450692; - } - } else { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 188))) { - result[0] += 0.0005094443; - } else { - result[0] += -0.00016178952; - } - } - } - } - } - } - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 162))) { - if (LIKELY((data[4].missing != -1) && (data[4].qvalue < 152))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 232))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 230))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 272))) { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 50))) { - result[0] += -0.00010068305; - } else { - result[0] += 0.0004361026; - } - } else { - result[0] += 0.027371911; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 130))) { - result[0] += 0.023174508; - } else { - result[0] += 0.011960031; - } - } - } else { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 242))) { - if (LIKELY((data[2].missing != -1) && (data[2].qvalue < 100))) { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 62))) { - result[0] += -0.015924754; - } else { - result[0] += -0.0012669711; - } - } else { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 188))) { - result[0] += 0.018523319; - } else { - result[0] += -0.00022792198; - } - } - } else { - result[0] += 0.039226815; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 112))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 146))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 102))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 90))) { - result[0] += 0.022155268; - } else { - result[0] += -0.0030770514; - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 66))) { - result[0] += 0.03438976; - } else { - result[0] += 0.016168883; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 80))) { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 128))) { - result[0] += 0.023656188; - } else { - result[0] += 0.009587833; - } - } else { - if (LIKELY((data[7].missing != -1) && (data[7].qvalue < 118))) { - result[0] += 0.003995668; - } else { - result[0] += -0.00010666515; - } - } - } - } else { - if (UNLIKELY((data[0].missing != -1) && (data[0].qvalue < 108))) { - if (UNLIKELY((data[7].missing != -1) && (data[7].qvalue < 150))) { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 136))) { - result[0] += 0.004109849; - } else { - result[0] += 0.027041653; - } - } else { - result[0] += -9.42766e-05; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 154))) { - result[0] += 0.0130727235; - } else { - if (LIKELY((data[3].missing != -1) && (data[3].qvalue < 78))) { - result[0] += -0.0015822932; - } else { - result[0] += -0.008224895; - } - } - } - } - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 164))) { - result[0] += -0.012683782; - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 38))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 268))) { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 146))) { - result[0] += -0.0033396825; - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 162))) { - result[0] += 0.01649657; - } else { - result[0] += 0.0024240483; - } - } - } else { - if (UNLIKELY((data[2].missing != -1) && (data[2].qvalue < 186))) { - result[0] += -0.004931916; - } else { - result[0] += 4.3331627e-05; - } - } - } else { - if (UNLIKELY((data[3].missing != -1) && (data[3].qvalue < 52))) { - if (LIKELY((data[0].missing != -1) && (data[0].qvalue < 202))) { - if (UNLIKELY((data[5].missing != -1) && (data[5].qvalue < 254))) { - result[0] += -0.0077444026; - } else { - result[0] += -0.0008859424; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 178))) { - result[0] += 0.0073036547; - } else { - result[0] += -0.007702441; - } - } - } else { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 228))) { - if (LIKELY((data[5].missing != -1) && (data[5].qvalue < 220))) { - result[0] += -0.00019439656; - } else { - result[0] += -0.0056553436; - } - } else { - if (UNLIKELY((data[4].missing != -1) && (data[4].qvalue < 180))) { - result[0] += 0.0066337893; - } else { - result[0] += 0.00068436674; - } - } - } - } - } - } - - // Apply base_scores - result[0] += 3.356329679489135742; - - // Apply postprocessor - if (!pred_margin) { postprocess(result); } -} - -void pdlp_predictor::postprocess(double* result) -{ - // Do nothing -} - -// Feature names array -const char* pdlp_predictor::feature_names[pdlp_predictor::NUM_FEATURES] = { - "n_vars", "n_cstrs", "nnz", "sparsity", "nnz_stddev", "unbalancedness", "spmv_ops", "total_nnz"}; diff --git a/cpp/src/utilities/models/pdlp_predictor/quantize.cpp b/cpp/src/utilities/models/pdlp_predictor/quantize.cpp deleted file mode 100644 index b0c1356158..0000000000 --- a/cpp/src/utilities/models/pdlp_predictor/quantize.cpp +++ /dev/null @@ -1,1006 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "header.h" - -static const float threshold[] = { - 17, - 26, - 31, - 52, - 56, - 58, - 60, - 69, - 86, - 145, - 156, - 160, - 210, - 214, - 219, - 253, - 269, - 272, - 365, - 420, - 479, - 504, - 511, - 649, - 655, - 770, - 780, - 784, - 875, - 888, - 955, - 1024, - 1106, - 1184, - 1187, - 1216, - 1344, - 1400, - 1455, - 1635, - 2025, - 2036, - 2164, - 2272, - 2303, - 2376, - 2442, - 2595, - 2600, - 3034, - 3060, - 3073, - 3079, - 3261, - 3338, - 3472, - 3510, - 3561, - 3654, - 4009, - 4171, - 4272, - 4462, - 4484, - 4704, - 4745, - 4768, - 4798, - 5068, - 5376, - 5662, - 5942, - 5951, - 5956, - 5958, - 6260, - 6481, - 6483, - 6730, - 6868, - 7389, - 7418, - 7745, - 8538, - 10218, - 10720, - 11185, - 11928, - 12414, - 12631, - 12927, - 13410, - 13688, - 13839, - 13869, - 14060, - 14099, - 14370, - 14636, - 15977, - 17187, - 17188, - 18405, - 18865, - 20602, - 20849, - 22480, - 23530, - 23836, - 24923, - 27542, - 29397, - 29435, - 30199, - 32450, - 33154, - 35734, - 37616, - 37648, - 37816, - 40272, - 53593, - 62171, - 63708, - 72468, - 74873, - 74884, - 74918, - 78287, - 86262, - 94595, - 129171, - 129931, - 138056, - 167056, - 184865, - 268358, - 1428996, - 8, - 12, - 13, - 40, - 41, - 45, - 46, - 49, - 61, - 108, - 112, - 126, - 130, - 158, - 162, - 225, - 234, - 241, - 242, - 260, - 270, - 296, - 322, - 332, - 359, - 360, - 397, - 403, - 443, - 444, - 447, - 448, - 482, - 483, - 484, - 487, - 566, - 569, - 577, - 582, - 643, - 659, - 711, - 721, - 723, - 764, - 811, - 867, - 879, - 900, - 1005, - 1025, - 1299, - 1389, - 1500, - 1513, - 1573, - 1634, - 1653, - 1691, - 1759, - 1863, - 1898, - 1917, - 1947, - 2225, - 2301, - 2344, - 2362, - 2366, - 2372, - 2376, - 2433, - 2472, - 2522, - 2526, - 2898, - 3174, - 3249, - 3250, - 3480, - 3491, - 3492, - 3760, - 3897, - 3925, - 4234, - 4241, - 4424, - 4454, - 4463, - 4634, - 4660, - 4811, - 4813, - 5190, - 5594, - 6120, - 6323, - 6333, - 6654, - 6754, - 6894, - 6956, - 8469, - 8620, - 10427, - 10479, - 11334, - 13361, - 14040, - 14766, - 16027, - 16488, - 16925, - 16927, - 18304, - 18609, - 22191, - 24885, - 24971, - 30499, - 32737, - 32933, - 37617, - 43882, - 45221, - 48604, - 53361, - 59577, - 67584, - 99790, - 131866, - 146326, - 169577, - 259962, - 314413, - 2881228, - 11, - 90, - 130, - 300, - 403, - 463, - 480, - 840, - 916, - 1011, - 1023, - 1035, - 1602, - 2221, - 2298, - 2710, - 3120, - 3382, - 3456, - 3718, - 4448, - 4496, - 4875, - 5096, - 5849, - 5940, - 6208, - 6498, - 7023, - 7490, - 7592, - 7755, - 7875, - 8379, - 8991, - 9168, - 9180, - 9714, - 10300, - 10530, - 12612, - 12613, - 14030, - 14576, - 15688, - 17774, - 19143, - 20810, - 22736, - 23965, - 26394, - 29040, - 30477, - 30489, - 31720, - 39920, - 40392, - 43256, - 43305, - 43345, - 43357, - 47777, - 48695, - 50150, - 58368, - 59790, - 65145, - 78260, - 83617, - 94254, - 101208, - 102473, - 110022, - 113048, - 118141, - 119459, - 127416, - 132112, - 147024, - 152533, - 154570, - 178965, - 182574, - 185099, - 187236, - 187968, - 188780, - 203770, - 210725, - 228952, - 228988, - 229044, - 247107, - 271063, - 283189, - 283914, - 293410, - 295358, - 302709, - 319705, - 346211, - 409850, - 433829, - 518476, - 648112, - 648715, - 834722, - 858766, - 1024230, - 1632480, - 1697945, - 2087795, - 2192958, - 4313301, - 2.4999999e-05, - 2.9000001e-05, - 3.7999998e-05, - 4.7000001e-05, - 5.3e-05, - 5.6000001e-05, - 5.9000002e-05, - 7.8999998e-05, - 7.9999998e-05, - 0.000109, - 0.00012899999, - 0.000157, - 0.00017499999, - 0.000181, - 0.000199, - 0.000256, - 0.00028000001, - 0.000283, - 0.00028400001, - 0.000287, - 0.000397, - 0.000401, - 0.00045200001, - 0.00047299999, - 0.00050600001, - 0.00053899997, - 0.00056000001, - 0.00062599999, - 0.00067400001, - 0.00072399998, - 0.00079299998, - 0.00090599997, - 0.001052, - 0.001054, - 0.001064, - 0.00107, - 0.001084, - 0.00122, - 0.001233, - 0.001236, - 0.001297, - 0.001321, - 0.001404, - 0.001618, - 0.001706, - 0.001785, - 0.001862, - 0.0020570001, - 0.0021569999, - 0.002267, - 0.0025269999, - 0.002664, - 0.00278, - 0.0029440001, - 0.003064, - 0.0030789999, - 0.0031069999, - 0.003267, - 0.0032800001, - 0.003491, - 0.0038409999, - 0.0039599999, - 0.0040600002, - 0.0042429999, - 0.004373, - 0.0047650002, - 0.0049749999, - 0.0053010001, - 0.005316, - 0.0054930001, - 0.0055149999, - 0.005903, - 0.0061130002, - 0.0071859998, - 0.007557, - 0.0079190005, - 0.0082029998, - 0.0083109997, - 0.0085450001, - 0.0088409996, - 0.008986, - 0.0096119996, - 0.010158, - 0.010454, - 0.010491, - 0.010588, - 0.011062, - 0.011189, - 0.01136, - 0.011947, - 0.012388, - 0.01285, - 0.013316, - 0.014028, - 0.014862, - 0.017344, - 0.017529, - 0.018440999, - 0.019753, - 0.020732, - 0.021379, - 0.024308, - 0.028986, - 0.029084001, - 0.029973, - 0.031771, - 0.032212, - 0.032758001, - 0.036036, - 0.036471002, - 0.048811998, - 0.048967998, - 0.057353001, - 0.064516, - 0.087884001, - 0.101821, - 0.13953499, - 0.14706101, - 0.229167, - 0.231547, - 0.258656, - 0.26689899, - 0.329083, - 0.34957099, - 0.35977599, - 0.42875499, - 0.91632003, - 0.073445998, - 0.233594, - 0.27638501, - 0.38584599, - 0.39129001, - 0.39430001, - 0.47627199, - 0.49999601, - 0.64261901, - 0.75658399, - 0.767389, - 0.84350997, - 1.015707, - 1.1055419, - 1.15354, - 1.4087991, - 1.722888, - 1.833473, - 1.891134, - 1.944816, - 2.2556751, - 2.4810159, - 2.66401, - 3.046876, - 3.5996871, - 3.6403749, - 4.273489, - 4.50634, - 5.4227982, - 5.6764622, - 6.6741581, - 6.9359412, - 6.9459491, - 9.0884533, - 9.3204279, - 11.047834, - 11.05003, - 11.34829, - 11.977816, - 12.56616, - 12.896968, - 13.082605, - 13.121695, - 15.218937, - 15.219805, - 15.422595, - 15.908748, - 16.333834, - 16.442045, - 17.222109, - 18.255583, - 18.337471, - 18.482592, - 19.853386, - 20.08359, - 20.493109, - 20.575439, - 21.781368, - 21.850649, - 23.025219, - 23.394861, - 23.543165, - 24.943169, - 26.903715, - 29.262121, - 30.220488, - 30.346951, - 30.993811, - 31.029835, - 32.219078, - 34.479328, - 39.028454, - 39.205608, - 39.3573, - 43.941006, - 44.133533, - 49.547768, - 52.339855, - 53.529537, - 56.239407, - 57.230946, - 60.911472, - 61.411575, - 66.006073, - 75.513832, - 76.959404, - 79.399498, - 94.297729, - 101.50806, - 104.90943, - 108.45773, - 125.96421, - 131.78522, - 149.06885, - 149.10501, - 159.80826, - 166.47375, - 190.44113, - 193.85234, - 214.411, - 228.99097, - 243.77342, - 251.07321, - 265.90009, - 277.75555, - 290.5871, - 321.94229, - 349.17996, - 435.58398, - 461.15714, - 480.75259, - 555.98761, - 556.20483, - 632.65863, - 828.06183, - 836.67456, - 857.39771, - 1038.6504, - 1445.1395, - 2359.3203, - 2750.1714, - 3077.2048, - 3270.0989, - 3929.8506, - 3954.1118, - 5246.2725, - 7567.3979, - 14218.984, - 0.0041490002, - 0.036821999, - 0.049214002, - 0.130913, - 0.177389, - 0.18044201, - 0.183018, - 0.19227199, - 0.244839, - 0.30151099, - 0.33716401, - 0.33797899, - 0.33922401, - 0.376284, - 0.436288, - 0.488325, - 0.50635898, - 0.51155603, - 0.53070199, - 0.55729002, - 0.576186, - 0.60621798, - 0.62735999, - 0.63285798, - 0.64827198, - 0.69477397, - 0.727198, - 0.74622601, - 0.76021701, - 0.77570999, - 0.817267, - 0.84039903, - 0.874892, - 0.88309401, - 0.91301, - 0.914644, - 0.92933702, - 0.94280899, - 1.017756, - 1.11876, - 1.1953419, - 1.205152, - 1.207976, - 1.208634, - 1.295553, - 1.2959141, - 1.326723, - 1.347288, - 1.3574491, - 1.390246, - 1.390783, - 1.428421, - 1.4407949, - 1.45542, - 1.502932, - 1.5636179, - 1.581139, - 1.78348, - 1.78504, - 1.801465, - 1.873001, - 2.0050731, - 2.062494, - 2.1306751, - 2.1410699, - 2.142792, - 2.1604609, - 2.301403, - 2.3202839, - 2.320334, - 2.342221, - 2.3494289, - 2.392698, - 2.4694431, - 2.4709809, - 2.548645, - 2.7524309, - 2.8433869, - 2.8817151, - 2.9853411, - 3.0516059, - 3.0590241, - 3.1028869, - 3.2358611, - 3.3461559, - 3.357758, - 3.364897, - 3.3847311, - 3.4308331, - 3.538095, - 3.712795, - 3.7464709, - 3.7710979, - 4.0210929, - 4.3894181, - 4.4247661, - 4.4356871, - 4.64078, - 4.7781639, - 5.100903, - 5.298172, - 5.2985978, - 5.4433179, - 5.4726572, - 5.5151229, - 5.6058269, - 5.6186318, - 5.738615, - 5.7462459, - 5.792984, - 5.9997821, - 6.0248361, - 6.1309428, - 6.1651001, - 6.6284609, - 7.162158, - 7.2263432, - 7.5340571, - 7.7937908, - 8.3784914, - 9.5885639, - 9.5996771, - 9.8454266, - 9.853548, - 10.502564, - 12.32793, - 13.122874, - 14.426889, - 15.470113, - 16.773743, - 19.316851, - 19.475534, - 22.584642, - 28.079082, - 46.668407, - 9, - 12, - 15, - 18, - 21, - 24, - 27, - 30, - 60, - 90, - 120, - 150, - 1620, - 7800, - 13500, - 24200, - 25700, - 28800, - 45400, - 55000, - 60400, - 61900, - 72000, - 96100, - 104000, - 106000, - 133000, - 137000, - 138000, - 152000, - 155000, - 177000, - 191000, - 222000, - 240000, - 264000, - 288000, - 304000, - 333000, - 345000, - 391000, - 408000, - 425000, - 449000, - 468000, - 518000, - 554000, - 583000, - 665000, - 719000, - 731000, - 756000, - 757000, - 772000, - 837000, - 931000, - 975000, - 1050000, - 1060000, - 1120000, - 1150000, - 1160000, - 1210000, - 1350000, - 1440000, - 1740000, - 1820000, - 1830000, - 1890000, - 2020000, - 2400000, - 2430000, - 2550000, - 2600000, - 2680000, - 2760000, - 2870000, - 2920000, - 3050000, - 3310000, - 3410000, - 3500000, - 3590000, - 3630000, - 3740000, - 3850000, - 3910000, - 4360000, - 4570000, - 4760000, - 5080000, - 5660000, - 6060000, - 6070000, - 6150000, - 6490000, - 6500000, - 6600000, - 6780000, - 7090000, - 7170000, - 8100000, - 8760000, - 9040000, - 9270000, - 10700000, - 11300000, - 11600000, - 12100000, - 12600000, - 12700000, - 13700000, - 14100000, - 15200000, - 15900000, - 16500000, - 17100000, - 17600000, - 17900000, - 19200000, - 20800000, - 22500000, - 23200000, - 26000000, - 26800000, - 27800000, - 31100000, - 31400000, - 31600000, - 31700000, - 34300000, - 34400000, - 37100000, - 39800000, - 42800000, - 44300000, - 45400000, - 51500000, - 51900000, - 56600000, - 61500000, - 65100000, - 77500000, - 78400000, - 97200000, - 1.07e+08, - 1.24e+08, - 1.29e+08, - 1.34e+08, - 1.54e+08, - 2.14e+08, - 2.49e+08, - 3.13e+08, - 3.34e+08, - 5.06e+08, - 1.34e+09, -}; - -static const int th_begin[] = { - 0, - 138, - 276, - 390, - 517, - 645, - 780, - 792, -}; - -static const int th_len[] = { - 138, - 138, - 114, - 127, - 128, - 135, - 12, - 144, -}; - -/* - * \brief Function to convert a feature value into bin index. - * \param val Feature value, in floating-point - * \param fid Feature identifier - * \return bin Index corresponding to given feature value - */ -int pdlp_predictor::quantize(float val, unsigned fid) -{ - const size_t offset = th_begin[fid]; - const float* array = &threshold[offset]; - int len = th_len[fid]; - int low = 0; - int high = len; - int mid; - float mval; - // It is possible th_begin[i] == [total_num_threshold]. This means that - // all features i, (i+1), ... are not used for any of the splits in the model. - // So in this case, just return something - if (offset == 936 || val < array[0]) { return -10; } - while (low + 1 < high) { - mid = (low + high) / 2; - mval = array[mid]; - if (val == mval) { - return mid * 2; - } else if (val < mval) { - high = mid; - } else { - low = mid; - } - } - if (array[low] == val) { - return low * 2; - } else if (high == len) { - return len * 2; - } else { - return low * 2 + 1; - } -} diff --git a/cpp/src/utilities/seed_generator.cu b/cpp/src/utilities/seed_generator.cu index 9e7e48bebf..612093a7a8 100644 --- a/cpp/src/utilities/seed_generator.cu +++ b/cpp/src/utilities/seed_generator.cu @@ -8,3 +8,4 @@ #include int64_t cuopt::seed_generator::base_seed_ = 0; +std::atomic cuopt::seed_generator::epoch_{0}; diff --git a/cpp/src/utilities/seed_generator.cuh b/cpp/src/utilities/seed_generator.cuh index aaf08830f8..888936eb79 100644 --- a/cpp/src/utilities/seed_generator.cuh +++ b/cpp/src/utilities/seed_generator.cuh @@ -6,6 +6,7 @@ /* clang-format on */ #pragma once +#include #include #include @@ -13,20 +14,23 @@ namespace cuopt { class seed_generator { static int64_t base_seed_; + // Monotonically increasing epoch; incremented on every set_seed() call. + // Thread-local state compares against this to detect resets, even when + // the same seed value is set again (e.g., repeated solve_mip() calls). + static std::atomic epoch_; struct thread_state_t { int64_t counter{0}; - int64_t last_base{0}; - bool initialized{false}; + int64_t last_epoch{-1}; }; static thread_state_t& local_state() { thread_local thread_state_t state; - if (!state.initialized || state.last_base != base_seed_) { - state.counter = base_seed_; - state.last_base = base_seed_; - state.initialized = true; + int64_t current_epoch = epoch_.load(std::memory_order_acquire); + if (state.last_epoch != current_epoch) { + state.counter = base_seed_; + state.last_epoch = current_epoch; } return state; } @@ -40,6 +44,7 @@ class seed_generator { #else base_seed_ = static_cast(seed); #endif + epoch_.fetch_add(1, std::memory_order_release); } template static void set_seed(arg0 seed0, arg1 seed1, args... seeds) diff --git a/cpp/src/utilities/timer.hpp b/cpp/src/utilities/timer.hpp index 41153cbfb6..ccfab4c57f 100644 --- a/cpp/src/utilities/timer.hpp +++ b/cpp/src/utilities/timer.hpp @@ -39,14 +39,14 @@ class timer_t { int line = __builtin_LINE()) const noexcept { bool elapsed = elapsed_time() >= time_limit; - if (elapsed) { - printf("************ TIME LIMIT (%.2gs) REACHED BY %s:%d: %s() ***\n", - time_limit, - file, - line, - caller); - //__builtin_trap(); - } + // if (elapsed) { + // printf("************ TIME LIMIT (%.2gs) REACHED BY %s:%d: %s() ***\n", + // time_limit, + // file, + // line, + // caller); + // //__builtin_trap(); + // } return elapsed; } diff --git a/cpp/src/utilities/work_unit_predictor.cpp b/cpp/src/utilities/work_unit_predictor.cpp index ebce702283..4c9512768d 100644 --- a/cpp/src/utilities/work_unit_predictor.cpp +++ b/cpp/src/utilities/work_unit_predictor.cpp @@ -23,12 +23,8 @@ #include #include #include -#include -#include "models/cpufj_predictor/header.h" -#include "models/dualsimplex_predictor/header.h" #include "models/fj_predictor/header.h" -#include "models/pdlp_predictor/header.h" namespace cuopt { @@ -89,8 +85,5 @@ float work_unit_predictor_t::predict_scalar( } template class work_unit_predictor_t; -template class work_unit_predictor_t; -template class work_unit_predictor_t; -template class work_unit_predictor_t; } // namespace cuopt diff --git a/cpp/tsan_suppressions.txt b/cpp/tsan_suppressions.txt deleted file mode 100644 index b6f413e370..0000000000 --- a/cpp/tsan_suppressions.txt +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. -# SPDX-License-Identifier: Apache-2.0 - -# Ignore races in external header-only libraries -race:tbb -race:Papilo diff --git a/scripts/README_PREDICTOR_WORKFLOW.md b/scripts/README_PREDICTOR_WORKFLOW.md deleted file mode 100644 index 7cc1fbc8a3..0000000000 --- a/scripts/README_PREDICTOR_WORKFLOW.md +++ /dev/null @@ -1,214 +0,0 @@ -# Predictor Training Workflow - -Quick reference for training iteration predictors from solver logs. - -## Prerequisites - -```bash -pip install pandas scikit-learn xgboost lightgbm treelite tl2cgen joblib -``` - -## Workflow - -### 1. Collect Logs - -Run solver with `INFO` log level to capture feature logs: - -```bash -export CUOPT_LOG_LEVEL=INFO -./solver problem.mps > logs/problem.log 2>&1 -``` - -### 2. Parse Logs - -Extract training data for specific algorithm: - -```bash -# Parse Feasibility Pump logs -python scripts/determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl - -# Parse PDLP (LP solver) logs -python scripts/determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.pkl - -# Parse Constraint Propagation logs -python scripts/determinism_logs_parse.py logs/ --algorithm CP -o cp_data.pkl - -# Parse Feasibility Jump legacy logs -python scripts/determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.pkl -``` - -**The parser shows real-time progress:** -- File scanning progress -- Grep execution on N files -- Line processing progress (updates every 10,000 lines) -- Pairing progress (updates every 10 files) -- Final statistics and file size - -**IMPORTANT - Log Filtering:** -The parser uses grep with EXACT pattern matching to be highly efficient: -- Pattern: `FP_FEATURES:` and `FP_RESULT:` (with colon suffix) -- Only matches predictor log lines, ignores all other FP-related logs -- Example: A log with 100,000 lines might have 10,000 lines with "FP" but only 200 predictor lines -- Grep filters these down BEFORE Python processing, making it extremely fast even with noisy logs - -### 3. Inspect Features (Optional) - -See what features are available: - -```bash -python scripts/train_regressor.py fp_data.pkl --regressor xgboost --list-features -``` - -### 4. Train Model - -Train with XGBoost or LightGBM: - -```bash -# XGBoost with early stopping and C++ code generation -python scripts/train_regressor.py fp_data.pkl \ - --regressor xgboost \ - --seed 42 \ - --early-stopping 20 \ - --treelite-compile 8 - -# LightGBM with stratified split -python scripts/train_regressor.py pdlp_data.pkl \ - --regressor lightgbm \ - --seed 42 \ - --stratify-split \ - --early-stopping 20 \ - --treelite-compile 8 -``` - -### 5. Use Generated Code - -The trained model will be exported to C++ code in `./models/_c_code/`: - -- `header.h` - Class declaration -- `main.cpp` - Implementation -- `quantize.cpp` - Quantization helpers - -Copy to `cpp/src/utilities/models/_predictor/` and integrate into solver. - -## Log Format Reference - -### FP (Feasibility Pump) -``` -FP_FEATURES: n_variables=100 n_constraints=50 n_integer_vars=80 ... -FP_RESULT: iterations=142 time_taken=5.234 termination=FEASIBLE_LP_PROJECTION -``` - -### PDLP (LP Solver) -``` -PDLP_FEATURES: n_variables=100 n_constraints=50 nnz=450 ... -PDLP_RESULT: iterations=237 time_ms=1234 termination=2 -``` - -### CP (Constraint Propagation) -``` -CP_FEATURES: n_variables=100 n_constraints=50 n_unset_vars=25 ... -CP_RESULT: time_ms=567 termination=SUCCESS iterations=0 -``` - -## Common Options - -### Training Script - -- `--regressor {xgboost,lightgbm,linear,poly2,...}` - Model type -- `--seed N` - Random seed for reproducibility -- `--test-size 0.2` - Test set proportion (default 20%) -- `--stratify-split` - Balance train/test by target distribution -- `--early-stopping N` - Early stopping patience (prevents overfitting) -- `--treelite-compile N` - Generate C++ code with N threads -- `--list-features` - Show available features and exit -- `--tune` - Use tuned hyperparameters - -### Parsing Script - -- `--algorithm {FP,PDLP,CP,FJ}` - Which algorithm to parse -- `-o FILE` - Output pickle file (default: `_data.pkl`) -- `--verbose` - Show warnings and detailed output - -## Tips - -1. **Collect diverse problems**: Train on variety of problem types/sizes -2. **Check train/test split**: Use `--stratify-split` if targets are imbalanced -3. **Prevent overfitting**: Use `--early-stopping` with tree models -4. **Feature selection**: Edit `FEATURES_TO_EXCLUDE` in `train_regressor.py` -5. **Reproducibility**: Always set `--seed` for consistent results - -## Troubleshooting - -**No entries found for algorithm X** -- Check log level is set to INFO or DEBUG -- Verify solver is executing the algorithm -- Look for `X_FEATURES` and `X_RESULT` lines in logs - -**Poor model performance** -- Collect more training data -- Try different regressor types -- Use `--list-features` to identify important features -- Enable `--stratify-split` for balanced splits - -**C++ code generation fails** -- Install: `pip install treelite tl2cgen` -- Only works with XGBoost and LightGBM -- Check model trained successfully first - -## Example Output - -```bash -$ python scripts/determinism_logs_parse.py logs/ --algorithm FP -o fp_data.pkl - -Scanning logs/ for .log files... -Found 42 log files - -Parsing FP (Feasibility Pump) logs... - Running grep on 42 files... - Processing 3046 matching lines... - Processed 3046 lines from 42 files - Pairing features with results... - Found 1523 complete entries from 42 files - - Total entries: 1523 - Unique files: 42 - Avg entries per file: 36.26 - Iterations (target): min=1, max=847, avg=142.35 - -Saving 1523 entries to fp_data.pkl... - -====================================================================== -✓ Success! Saved 1523 entries to fp_data.pkl - File size: 2.34 MB -====================================================================== - -$ python scripts/train_regressor.py fp_data.pkl --regressor xgboost --seed 42 - -Loading data from: fp_data.pkl -Loaded 1523 entries with 29 columns - -Data Split: - Total entries: 1523 - Train entries: 1218 (34 files) - Test entries: 305 (8 files) - -Training xgboost regressor... - Training complete! - -Test Set Metrics: - MSE: 1234.56 - RMSE: 35.14 - MAE: 22.67 - R²: 0.8542 - -Feature Importance: - 1. n_variables : 0.245123 - 2. n_constraints : 0.187456 - 3. initial_ratio_of_integers : 0.156234 - ... - -C source code generated to: ./models/fp_data_c_code/ - Contains optimized model source code (branch-annotated, quantized) - -Success! Saved 1523 entries to fp_data.pkl -``` diff --git a/scripts/README_REGRESSION.md b/scripts/README_REGRESSION.md deleted file mode 100644 index 9c8e236bff..0000000000 --- a/scripts/README_REGRESSION.md +++ /dev/null @@ -1,354 +0,0 @@ -# Regression Model Training Scripts - -This directory contains scripts for parsing algorithm log files and training regression models to predict iteration counts. - -## Overview - -The workflow consists of two steps: -1. **Parse log files** → Extract key-value pairs into a pickle file -2. **Train models** → Learn to predict iterations from features - -## Installation - -Install required dependencies: - -```bash -pip install numpy pandas scikit-learn joblib -``` - -Optional model backends: - -```bash -pip install xgboost lightgbm -``` - -Optional C source export dependencies: - -```bash -pip install treelite tl2cgen -``` - -## Usage - -### Step 1: Parse Log Files - -Extract features from log files containing `FJ:` entries: - -```bash -python determinism_logs_parse.py /path/to/logs/directory -o parsed_data.pkl -``` - -**Arguments:** -- `input_dir`: Directory containing `.log` files -- `-o, --output`: Output pickle file (default: `output.pkl`) - -**Output:** -- Pickle file containing list of dictionaries with all key-value pairs -- Each entry includes `file=` field - -### Step 2: Train Regression Model - -Train a model to predict `iter` values from other features: - -```bash -python train_regressor.py parsed_data.pkl --regressor xgboost --seed 42 -``` - -**Arguments:** -- `input_pkl`: Input pickle file from step 1 -- `--regressor, -r`: Type of regressor (required) - - `linear` - Linear Regression - - `poly2`, `poly3`, `poly4` - Polynomial Regression (degree 2, 3, 4) - - `xgboost` - XGBoost Regressor - - `lightgbm` - LightGBM Regressor - - `random_forest` - Random Forest Regressor - - `gradient_boosting` - Gradient Boosting Regressor -- `--output-dir, -o`: Directory to save models (default: `./models`) -- `--seed, -s`: Random seed for reproducibility (optional) -- `--tune`: Enable hyperparameter tuning -- `--cv-folds`: Number of cross-validation folds (default: 5) -- `--test-size`: Test set proportion (default: 0.2) -- `--no-progress`: Disable training progress output -- `--list-features`: List all available features in the dataset and exit -- `--stratify-split`: Stratify train/test split by target distribution -- `--early-stopping N`: Early stopping patience in rounds (default: 20, use 0 to disable) -- `--treelite-compile N`: Export XGBoost/LightGBM as optimized C source code with TL2cgen (N threads, default: 1, includes branch annotation and quantization) - -## Features - -### Data Splitting -- **File-based split**: Ensures entries from the same file go exclusively to train OR test -- **Prevents data leakage**: Improves generalization and reduces overfitting -- **Default**: 20% of files for testing - -### Preprocessing -- **Automatic scaling**: Applied to linear/polynomial models (not tree-based) -- **Polynomial features**: All numeric features expanded for polynomial regression -- **Clean data assumption**: Script expects valid pickle data - -### Feature Selection -- **Manual feature selection**: Edit `FEATURES_TO_EXCLUDE` or `FEATURES_TO_INCLUDE_ONLY` directly in the script -- **Exclude specific features**: Add feature names to `FEATURES_TO_EXCLUDE` list -- **Include only specific features**: Add feature names to `FEATURES_TO_INCLUDE_ONLY` list (overrides exclusion) -- **List available features**: Run with `--list-features` to see all features in your dataset -- **No command-line config**: Intentionally not exposed as CLI args for cleaner configuration file management - -### Model Evaluation -- **Cross-validation**: K-fold CV on training set with progress output -- **Comprehensive metrics**: MSE, RMSE, MAE, R² -- **Feature importance**: All features ranked by importance (top 50 for polynomial models) -- **Sample predictions**: 20 random test predictions with errors - -### Training Progress -- **Progress indicators**: Tree-based models (XGBoost, LightGBM, Random Forest, Gradient Boosting) show real-time training progress -- **Polynomial feature tracking**: Shows number of polynomial features being generated -- **CV progress**: Cross-validation shows progress for each fold -- **Disable option**: Use `--no-progress` flag to suppress all progress output - -### Overfitting Prevention -- **Early stopping**: Enabled by default for XGBoost and LightGBM (20 rounds patience) to prevent overfitting -- **Regularization**: XGBoost and LightGBM include L1/L2 regularization, subsampling, and minimum child weight -- **Stratified splitting**: Use `--stratify-split` to ensure balanced target distributions -- **Disable early stopping**: Use `--early-stopping 0` if you want full training without early stopping - -### Model Persistence -- **XGBoost**: Saved as `.ubj.gz` (UBJ format with gzip compression) -- **LightGBM**: Saved as `.txt` (text format, human-readable) -- **Sklearn models**: Saved as `.joblib` (efficient for numpy arrays) -- **Metadata**: Feature names and preprocessing info saved separately -- **Scaler**: Saved for models requiring normalization - -### TL2cgen Source Export (Optional) -- **C source code export**: Export XGBoost/LightGBM models as optimized C source code using TL2cgen -- **Portable and fast**: Compile the source on any platform for 10-100x faster predictions -- **Enabled by default**: Automatically exports C source with 1 thread (use `--treelite-compile N` for more threads) -- **Requires**: `treelite>=4.0` and `tl2cgen` packages (optional dependencies) -- **Output**: Optimized C source files in dedicated directory -- **Note**: Treelite 4.0+ moved C compilation to TL2cgen ([migration guide](https://tl2cgen.readthedocs.io/en/latest/treelite-migration.html)) - -### Built-in TL2cgen Optimizations -The following optimizations are **automatically applied** when using TL2cgen: - -- **Branch annotation**: - - Analyzes which branches are taken during training - - Optimizes C code with branch prediction hints - - Improves inference speed by 10-30% - - Saves annotation file for inspection -- **Quantization**: - - Reduces model memory footprint by ~75% - - Uses 8-bit integers instead of 32-bit floats where possible - - Minimal accuracy loss (typically <0.1% R²) - - Faster inference on memory-constrained systems - -**Combined effect**: 1.2-1.5x faster inference with 75% less memory - -## Example Workflow - -```bash -# 1. Parse logs -python determinism_logs_parse.py /data/algorithm_logs -o data.pkl - -# 2. List available features -python train_regressor.py data.pkl --regressor xgboost --list-features - -# 3. Train XGBoost model -python train_regressor.py data.pkl --regressor xgboost --seed 42 -o ./models - -# 4. Train polynomial model with tuning -python train_regressor.py data.pkl --regressor poly3 --tune --seed 42 - -# 5. Compare different models -for model in linear poly2 poly3 xgboost lightgbm random_forest gradient_boosting; do - echo "Training $model..." - python train_regressor.py data.pkl --regressor $model --seed 42 -done - -# 6. Train LightGBM model -python train_regressor.py data.pkl --regressor lightgbm --seed 42 - -# 7. Export XGBoost model as C source for production deployment (enabled by default) -python train_regressor.py data.pkl --regressor xgboost --seed 42 - -# Or specify more threads for compilation -python train_regressor.py data.pkl --regressor xgboost --treelite-compile 8 --seed 42 -``` - -## TL2cgen Source Export Example - -For production deployments requiring fast inference, models are **automatically exported as optimized C source code** (if `treelite` and `tl2cgen` are installed): - -```bash -# Install treelite and tl2cgen (optional) -pip install treelite tl2cgen - -# Train model - optimized C source is automatically exported with branch annotation and quantization -python train_regressor.py data.pkl --regressor xgboost -o ./models - -# Use more threads for faster parallel compilation -python train_regressor.py data.pkl --regressor xgboost --treelite-compile 8 -o ./models - -# The C source files will be in: models/xgboost_c_code/ -# Contains optimized C source code with branch annotation and quantization ready for compilation - -# Same process works for LightGBM -python train_regressor.py data.pkl --regressor lightgbm --treelite-compile 8 -o ./models -# Output: models/lightgbm_c_code/ -``` - -### Optimization Impact - -All TL2cgen exports include the following optimizations automatically: - -1. **Branch Annotation**: Uses training data statistics to add branch prediction hints -2. **Quantization**: Reduces memory footprint by converting floating-point to integers -3. **Missing Data Removal**: Removes unnecessary missing data checks (assumes all features provided) - -| Configuration | Speed | Memory | Accuracy | -|---------------|-------|--------|----------| -| Standard XGBoost/LightGBM | 1x | 100% | 100% | -| **TL2cgen optimized (default)** | **1.2-1.5x** | **25%** | **>99.9%** | - -**Note:** Treelite 4.0+ moved C code generation to TL2cgen. See the [migration guide](https://tl2cgen.readthedocs.io/en/latest/treelite-migration.html) for details. - -### Output Files - -When TL2cgen is enabled, the following files are automatically created: - -``` -models/ -├── xgboost_model.ubj.gz # Standard XGBoost model -├── xgboost_metadata.pkl # Feature names and config -├── xgboost_annotation.json # Branch statistics (automatic) -└── xgboost_c_code/ # TL2cgen generated optimized C++ source - ├── header.h # Header with feature names declaration - ├── main.cpp # Implementation with feature names array - └── *.cpp / *.h # Other C++ source files (quantized + annotated) -``` - -**Class Wrapping**: All generated files are automatically wrapped in a C++ class with the model name (derived from the input pickle file basename) to avoid naming conflicts when using multiple models in the same project. For example, if the input is `my_dataset.pkl`: -- All functions and data are in `class my_dataset { public: ... };` -- All class members are `static` - no instantiation required -- Access functions as `my_dataset::predict()`, `my_dataset::get_num_features()`, etc. -- All `.c` files are renamed to `.cpp` for C++ compilation -- Header includes `#pragma once` for include guards - -The generated `header.h` includes: -- `#pragma once` at the top -- `#include` statements (outside the class) -- `class { public: ... };` wrapping all declarations -- `static constexpr int NUM_FEATURES` - Number of features -- `static const char* feature_names[]` - Feature names declaration -- Function declarations (e.g., `predict()`, `get_num_features()`) as public static members - -The generated `main.cpp` includes: -- `#include` statements at the top -- Macro definitions (`LIKELY`, `UNLIKELY`, `N_TARGET`, `MAX_N_CLASS`) - moved from header for implementation-only use -- Function implementations with `::function_name` qualification -- `const char* ::feature_names[]` - Feature names array definition (at the end of file) - -### Example Generated Code Structure - -**header.h:** - -```cpp -#pragma once - -#include - -class my_dataset { -public: - static float predict(float* data, int pred_margin); - static int get_num_feature(); - // ... other function declarations ... - - static constexpr int NUM_FEATURES = 42; - static const char* feature_names[NUM_FEATURES]; -}; -``` - -**main.cpp:** - -```cpp -#include "header.h" - -#define LIKELY(x) __builtin_expect(!!(x), 1) -#define N_TARGET 1 - -float my_dataset::predict(float* data, int pred_margin) { - // implementation -} - -int my_dataset::get_num_feature() { - return NUM_FEATURES; -} - -// Feature names array -const char* my_dataset::feature_names[my_dataset::NUM_FEATURES] = { - "n_variables", - "n_constraints", - // ... -}; -``` - -**Usage:** - -```cpp -#include "xgboost_c_code/header.h" - -// Call static methods directly - no instantiation needed -float result = my_dataset::predict(features, 0); -int num = my_dataset::get_num_feature(); -``` - -## Feature Selection Examples - -To perform feature selection, edit the configuration section at the top of `train_regressor.py`: - -### Example 1: Exclude specific features - -```python -FEATURES_TO_EXCLUDE = [ - 'time', # Exclude time as it may not be available at prediction time - 'avg_constraint_range', - 'binary_ratio', -] - -FEATURES_TO_INCLUDE_ONLY = [] -``` - -### Example 2: Use only specific features - -```python -FEATURES_TO_EXCLUDE = [] - -FEATURES_TO_INCLUDE_ONLY = [ - 'n_variables', - 'n_constraints', - 'sparsity', - 'structural_complexity', -] -``` - -**Note:** If `FEATURES_TO_INCLUDE_ONLY` is non-empty, it overrides `FEATURES_TO_EXCLUDE`. - -## Output Structure - -After training, the output directory contains: - -``` -models/ -├── xgboost_model.ubj.gz # Compressed XGBoost model -├── xgboost_metadata.pkl # Feature names and config -├── linear_model.joblib # Linear regression model -├── linear_scaler.pkl # StandardScaler for linear model -├── linear_metadata.pkl # Metadata -└── ... -``` - -## Notes - -- The train/test split is based on **unique files**, not individual entries -- Models requiring scaling (linear, polynomial) automatically apply `StandardScaler` -- Tree-based models (XGBoost, Random Forest, Gradient Boosting) don't use scaling -- Feature importance shows the most predictive features for iteration count -- Use `--seed` for reproducible results across runs diff --git a/scripts/determinism_logs_parse.py b/scripts/determinism_logs_parse.py deleted file mode 100755 index c5eeefda87..0000000000 --- a/scripts/determinism_logs_parse.py +++ /dev/null @@ -1,925 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. -# SPDX-License-Identifier: Apache-2.0 -# 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. - -""" -Parse log files containing algorithm feature logs and export to pickle format for training. - -Supports parsing of: -- FP (Feasibility Pump): FP_FEATURES and FP_RESULT logs -- PDLP (LP Solver): PDLP_RESULT single-line logs -- CP (Constraint Propagation): CP_FEATURES and CP_RESULT logs -- FJ (Feasibility Jump): Legacy FJ: format -- CPUFJ (CPU Feasibility Jump): CPUFJ_FEATURES single-line logs -- BB (Branch and Bound): BB_NODE_FEATURES single-line logs -- DS (Dual Simplex): DS_FEATURES single-line logs - -IMPORTANT - Grep Specificity: -The parser uses EXACT pattern matching with grep to filter logs efficiently. -For example, when parsing FP logs: -- Grep pattern: 'FP_FEATURES:' and 'FP_RESULT:' (with colon) -- Matches ONLY predictor log lines, NOT general FP debug/info lines -- A log with 10,000 lines containing "FP" might have only 100 predictor lines -- Grep filters down to only the relevant lines before Python processing - -Performance optimizations for very large log files: -- Single grep call per algorithm (instead of separate calls for features/results) -- Uses grep's -n flag to get line numbers for efficient pairing -- Minimal Python string processing (split instead of regex) -- Single-pass parsing with dictionary accumulation -- Avoids redundant string operations -- DRY refactoring: Generic parser eliminates duplicate code (~105 lines removed) -- Real-time progress indicators (every 10K lines, every 10 files) - -Usage: - python determinism_logs_parse.py --algorithm FP [-o output.feather] - python determinism_logs_parse.py --algorithm PDLP [-o output.feather] - python determinism_logs_parse.py --algorithm CP [-o output.feather] - python determinism_logs_parse.py --algorithm FJ [-o output.feather] - python determinism_logs_parse.py --algorithm CPUFJ [-o output.feather] - python determinism_logs_parse.py --algorithm BB [-o output.feather] - python determinism_logs_parse.py --algorithm DS [-o output.feather] -""" - -import argparse -import subprocess -import os -import glob -import numpy as np -import pandas as pd -from typing import List, Dict, Any, Optional - - -SUPPORTED_ALGORITHMS = [ - "FP", - "PDLP", - "CP", - "FJ", - "CPUFJ", - "BB", - "DS", - "BOUNDS_STRENGTH", -] - - -def parse_value(value_str: str) -> Any: - """Convert string value to appropriate type (int, float, or str).""" - try: - # Handle special float values first (nan, inf, -inf) - value_lower = value_str.lower() - if value_lower in ("nan", "inf", "-inf", "+inf"): - return float(value_str) - - # Try to parse as float if it contains a decimal point or scientific notation - if "." in value_str or "e" in value_str.lower(): - return float(value_str) - else: - return int(value_str) - except ValueError: - # Keep as string if conversion fails - return value_str - - -def parse_key_value_line(line: str, prefix: str) -> Dict[str, Any]: - """ - Parse a line containing key=value pairs after removing prefix. - - Example - ------- - "FP_FEATURES: n_variables=100 n_constraints=50" - -> {'n_variables': 100, 'n_constraints': 50} - """ - entry = {} - - # Remove prefix - if prefix in line: - line = line.split(prefix, 1)[1].strip() - - # Parse key=value pairs - # Handle both space-separated and comma-separated - for kv_pair in line.split(): - if "=" in kv_pair: - key, value = kv_pair.split("=", 1) - # Remove trailing commas - value = value.rstrip(",") - entry[key] = parse_value(value) - - return entry - - -def parse_generic_algorithm_logs( - log_files: List[str], algorithm: str, algorithm_name: str -) -> List[Dict[str, Any]]: - """ - Generic parser for algorithm feature and result logs. - - Matches _FEATURES lines with subsequent _RESULT lines. - Uses grep efficiently to minimize Python-side processing. - - Args: - log_files: List of log file paths to parse - algorithm: Algorithm prefix (e.g., 'FP', 'PDLP', 'CP') - algorithm_name: Full name for display (e.g., 'Feasibility Pump') - - Returns - ------- - List of dictionaries with combined features and results - """ - print(f"\nParsing {algorithm} ({algorithm_name}) logs...") - print(f" Running grep on {len(log_files)} files...") - - # Construct grep patterns with EXACT match requirements - # The colon at the end ensures we ONLY match the feature/result log lines - # and ignore all other lines containing the algorithm name - features_pattern = f"{algorithm}_FEATURES:" - result_pattern = f"{algorithm}_RESULT:" - - # Use grep with: - # -H: Always print filename (even with single file) - # -n: Print line numbers for correct pairing - # -e: Multiple patterns to match - # This ensures we ONLY get the specific predictor log lines, not debug/info lines - cmd = [ - "grep", - "-Hn", - "-e", - features_pattern, - "-e", - result_pattern, - ] + log_files - - result = subprocess.run(cmd, capture_output=True, text=True) - - if not result.stdout: - print(f" No {algorithm} logs found") - return [] - - # Count lines for progress indication - total_lines = result.stdout.count("\n") - print(f" Processing {total_lines} matching lines...") - - # Process grep output efficiently - # Format: filename:linenum:_FEATURES: key1=value1 ... - entries_by_file = {} - lines_processed = 0 - files_seen = set() - - for line in result.stdout.split("\n"): - if not line: - continue - - lines_processed += 1 - - # Progress update every 10000 lines - if lines_processed % 10000 == 0: - pct = ( - (lines_processed / total_lines * 100) if total_lines > 0 else 0 - ) - print( - f" Progress: {pct:.1f}% ({lines_processed}/{total_lines} lines, {len(files_seen)} files)", - end="\r", - ) - - # Split on first two colons to get filename, linenum, and content - # The rest of the line after the second colon is the log content - parts = line.split(":", 2) - if len(parts) < 3: - continue - - filename = os.path.basename(parts[0]) - linenum = int(parts[1]) - content = parts[2] # This includes everything after linenum - - if filename not in entries_by_file: - entries_by_file[filename] = {"features": [], "results": []} - files_seen.add(filename) - - # Double-check pattern match (grep already filtered, but be extra safe) - # This ensures we ONLY process lines with the exact patterns we want - if features_pattern in content: - # Parse features - only if pattern is present - features = parse_key_value_line(content, features_pattern) - if features: # Only add if parsing succeeded - entries_by_file[filename]["features"].append( - (linenum, features) - ) - elif result_pattern in content: - # Parse results - only if pattern is present - results = parse_key_value_line(content, result_pattern) - if results: # Only add if parsing succeeded - entries_by_file[filename]["results"].append((linenum, results)) - - # Clear progress line - if lines_processed > 0: - print( - f" Processed {lines_processed} lines from {len(files_seen)} files " - ) - - # Match features with results - # IMPORTANT: Multiple FEATURES lines followed by multiple RESULT lines form ONE complete entry - # We need to merge consecutive lines of the same type, then combine them - print(" Merging features and results...") - entries = [] - files_processed = 0 - total_files = len(entries_by_file) - - for filename, data in entries_by_file.items(): - files_processed += 1 - - # Progress update every 10 files - if files_processed % 10 == 0 or files_processed == total_files: - print( - f" Merging: {files_processed}/{total_files} files, {len(entries)} entries found", - end="\r", - ) - - # Combine features and results by line number - # Group consecutive FEATURES lines and consecutive RESULT lines - all_items = [] - for linenum, features in data["features"]: - all_items.append((linenum, "features", features)) - for linenum, results in data["results"]: - all_items.append((linenum, "results", results)) - - # Sort by line number - all_items.sort(key=lambda x: x[0]) - - # Merge consecutive items of the same type - current_features = {} - current_results = {} - last_type = None - - for linenum, item_type, content in all_items: - # If we transition from RESULT back to FEATURES, save the previous entry - if item_type == "features" and last_type == "results": - if current_features and current_results: - # Create combined entry - entry = {"file": filename} - entry.update(current_features) - entry.update(current_results) - - # Rename 'iterations' to 'iter' for consistency - if "iterations" in entry: - entry["iter"] = entry.pop("iterations") - - entries.append(entry) - - # Reset for next entry - current_features = {} - current_results = {} - - if item_type == "features": - # Accumulate features - current_features.update(content) - else: # results - # Accumulate results - current_results.update(content) - - last_type = item_type - - # Don't forget the last entry in the file - if current_features and current_results: - entry = {"file": filename} - entry.update(current_features) - entry.update(current_results) - - if "iterations" in entry: - entry["iter"] = entry.pop("iterations") - - entries.append(entry) - - # Clear progress line and show final count - if total_files > 0: - print( - f" Found {len(entries)} complete entries from {total_files} files " - ) - - return entries - - -# Algorithm-specific wrappers for the generic parser -# These provide a clean API and eliminate code duplication - - -def parse_fp_logs(log_files: List[str]) -> List[Dict[str, Any]]: - """Parse Feasibility Pump feature and result logs.""" - return parse_generic_algorithm_logs(log_files, "FP", "Feasibility Pump") - - -def parse_pdlp_logs(log_files: List[str]) -> List[Dict[str, Any]]: - """Parse PDLP (LP Solver) result logs.""" - return parse_single_line_logs( - log_files, "PDLP_RESULT:", "PDLP (LP Solver)", "PDLP_RESULT:" - ) - - -def parse_cp_logs(log_files: List[str]) -> List[Dict[str, Any]]: - """Parse Constraint Propagation feature and result logs.""" - return parse_generic_algorithm_logs( - log_files, "CP", "Constraint Propagation" - ) - - -def parse_single_line_logs( - log_files: List[str], - pattern: str, - algorithm_name: str, - prefix_to_remove: Optional[str] = None, -) -> List[Dict[str, Any]]: - """ - Generic parser for single-line logs with key=value pairs. - - Used for legacy formats that don't have separate FEATURES/RESULT lines. - - Args: - log_files: List of log file paths to parse - pattern: Grep pattern to match (e.g., 'FJ:', 'CPUFJ_FEATURES') - algorithm_name: Full name for display - prefix_to_remove: Optional prefix to strip from content (e.g., 'FJ:') - - Returns - ------- - List of dictionaries with parsed key-value pairs - """ - print(f"\nParsing {algorithm_name} logs...") - print(f" Running grep on {len(log_files)} files...") - - # Use grep to efficiently extract ONLY lines with the exact pattern - cmd = ["grep", "-H", pattern] + log_files - result = subprocess.run(cmd, capture_output=True, text=True) - - if not result.stdout: - print(f" No {algorithm_name} logs found") - return [] - - # Count lines for progress indication - total_lines = result.stdout.count("\n") - print(f" Processing {total_lines} matching lines...") - - # Parse grep output efficiently - entries = [] - lines_processed = 0 - - for line in result.stdout.split("\n"): - if not line: - continue - - lines_processed += 1 - - # Progress update every 10000 lines - if lines_processed % 10000 == 0: - pct = ( - (lines_processed / total_lines * 100) if total_lines > 0 else 0 - ) - print( - f" Progress: {pct:.1f}% ({lines_processed}/{total_lines} lines, {len(entries)} entries)", - end="\r", - ) - - # Grep output format: filename:content - parts = line.split(":", 2) - if len(parts) < 3: - continue - - filename = os.path.basename(parts[0]) - content = parts[2] - - # Remove prefix if specified - if prefix_to_remove and content.startswith(prefix_to_remove): - content = content[len(prefix_to_remove) :].strip() - - # Parse key-value pairs - entry = {"file": filename} - for kv_pair in content.split(): - if "=" in kv_pair: - key, value = kv_pair.split("=", 1) - # Remove trailing commas - value = value.rstrip(",") - entry[key] = parse_value(value) - if key == "runtime": - entry["runtime_ms"] = entry["runtime"] * 1000.0 - - # Only add entry if it has more than just the filename - if len(entry) > 1: - entries.append(entry) - - # Clear progress line - if lines_processed > 0: - print( - f" Found {len(entries)} entries from {total_lines} lines " - ) - - return entries - - -def parse_fj_logs(log_files: List[str]) -> List[Dict[str, Any]]: - """Parse legacy Feasibility Jump logs (original format).""" - return parse_single_line_logs( - log_files, "FJ:", "FJ (Feasibility Jump)", "FJ:" - ) - - -def parse_cpufj_logs(log_files: List[str]) -> List[Dict[str, Any]]: - """Parse CPU Feasibility Jump feature logs.""" - return parse_single_line_logs( - log_files, "CPUFJ_FEATURES", "CPUFJ (CPU Feasibility Jump)" - ) - - -def parse_bb_logs(log_files: List[str]) -> List[Dict[str, Any]]: - """Parse Branch and Bound node feature logs.""" - return parse_single_line_logs( - log_files, "BB_NODE_FEATURES", "BB (Branch and Bound)" - ) - - -def parse_ds_logs(log_files: List[str]) -> List[Dict[str, Any]]: - """Parse Dual Simplex feature logs.""" - return parse_single_line_logs( - log_files, "DS_FEATURES:", "DS (Dual Simplex)", "DS_FEATURES:" - ) - - -def parse_bounds_strengthening_logs( - log_files: List[str], -) -> List[Dict[str, Any]]: - """Parse Bounds Strengthening feature and result logs.""" - return parse_single_line_logs( - log_files, - "BOUNDS_STRENGTH_FEATURES:", - "Bounds Strengthening", - "BOUNDS_STRENGTH_FEATURES:", - ) - - -def print_statistics(entries: List[Dict[str, Any]], algorithm: str) -> None: - """Print statistics about parsed entries.""" - if not entries: - print(f"\n No entries found for {algorithm}") - return - - unique_files = set(entry["file"] for entry in entries) - avg_entries_per_file = ( - len(entries) / len(unique_files) if unique_files else 0 - ) - - # Check if 'iter' field exists - has_iter = all("iter" in entry for entry in entries) - - if has_iter: - iter_values = [entry["iter"] for entry in entries] - min_iter = min(iter_values) - max_iter = max(iter_values) - avg_iter = sum(iter_values) / len(iter_values) - - print(f"\n Total entries: {len(entries)}") - print(f" Unique files: {len(unique_files)}") - print(f" Avg entries per file: {avg_entries_per_file:.2f}") - print( - f" Iterations (target): min={min_iter}, max={max_iter}, avg={avg_iter:.2f}" - ) - else: - print(f"\n Total entries: {len(entries)}") - print(f" Unique files: {len(unique_files)}") - print(f" Avg entries per file: {avg_entries_per_file:.2f}") - - # Show sample entry - if entries: - print("\n Sample entry (first):") - sample = entries[0] - for key, value in sorted(sample.items()): - print(f" {key}: {value}") - - -def main(): - parser = argparse.ArgumentParser( - description="Parse algorithm feature logs and export to Feather format for training", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -Supported Algorithms: - FP - Feasibility Pump (parses FP_FEATURES and FP_RESULT logs) - PDLP - LP Solver (parses PDLP_RESULT single-line logs) - CP - Constraint Propagation (parses CP_FEATURES and CP_RESULT logs) - FJ - Feasibility Jump (parses legacy FJ: format) - CPUFJ - CPU Feasibility Jump (parses CPUFJ_FEATURES single-line logs) - BB - Branch and Bound (parses BB_NODE_FEATURES single-line logs) - DS - Dual Simplex (parses DS_FEATURES single-line logs) - BOUNDS_STRENGTH - Bounds Strengthening (parses BOUNDS_STRENGTHENING_FEATURES and BOUNDS_STRENGTHENING_RESULT logs) - -Examples: - python determinism_logs_parse.py logs/ --algorithm FP -o fp_data.feather - python determinism_logs_parse.py logs/ --algorithm PDLP -o pdlp_data.feather - python determinism_logs_parse.py logs/ --algorithm CP -o cp_data.feather - python determinism_logs_parse.py logs/ --algorithm FJ -o fj_data.feather - python determinism_logs_parse.py logs/ --algorithm CPUFJ -o cpufj_data.feather - python determinism_logs_parse.py logs/ --algorithm BB -o bb_data.feather - python determinism_logs_parse.py logs/ --algorithm DS -o ds_data.feather - python determinism_logs_parse.py logs/ --algorithm BOUNDS_STRENGTH -o bounds_strengthening_data.feather - - # Limit to first 10 files for testing - python determinism_logs_parse.py logs/ --algorithm FP --max-files 10 - """, - ) - - parser.add_argument( - "input_dir", help="Directory containing .log files to parse" - ) - parser.add_argument( - "--algorithm", - "-a", - required=True, - choices=SUPPORTED_ALGORITHMS, - help="Algorithm to parse logs for", - ) - parser.add_argument( - "-o", - "--output", - default=None, - help="Output Feather file path (default: _data.feather)", - ) - parser.add_argument( - "--verbose", - "-v", - action="store_true", - help="Print verbose output including warnings", - ) - parser.add_argument( - "--max-files", - type=int, - default=None, - help="Limit number of log files to process (useful for testing)", - ) - - args = parser.parse_args() - - # Set default output filename based on algorithm - if args.output is None: - args.output = f"{args.algorithm.lower()}_data.feather" - - # Find all .log files in the input directory - print(f"\nScanning {args.input_dir} for .log files...") - log_files = glob.glob(os.path.join(args.input_dir, "*.log")) - - if not log_files: - print(f"Error: No .log files found in {args.input_dir}") - return 1 - - print(f"Found {len(log_files)} log files") - - # Apply max-files limit if specified - if args.max_files is not None and args.max_files > 0: - if args.max_files < len(log_files): - log_files = log_files[: args.max_files] - print(f"Limiting to first {args.max_files} files (--max-files)") - else: - print( - f"Note: --max-files={args.max_files} is >= total files, using all files" - ) - - # Parse logs based on algorithm - if args.algorithm == "FP": - entries = parse_fp_logs(log_files) - elif args.algorithm == "PDLP": - entries = parse_pdlp_logs(log_files) - elif args.algorithm == "CP": - entries = parse_cp_logs(log_files) - elif args.algorithm == "FJ": - entries = parse_fj_logs(log_files) - elif args.algorithm == "CPUFJ": - entries = parse_cpufj_logs(log_files) - elif args.algorithm == "BB": - entries = parse_bb_logs(log_files) - elif args.algorithm == "DS": - entries = parse_ds_logs(log_files) - elif args.algorithm == "BOUNDS_STRENGTH": - entries = parse_bounds_strengthening_logs(log_files) - else: - print(f"Error: Unsupported algorithm: {args.algorithm}") - return 1 - - if not entries: - print(f"\nError: No entries found for {args.algorithm}") - if args.algorithm in ["FP", "CP"]: - print( - f"Make sure your logs contain {args.algorithm}_FEATURES and {args.algorithm}_RESULT lines" - ) - elif args.algorithm == "PDLP": - print( - "Make sure your logs contain PDLP_RESULT: lines with key=value pairs" - ) - elif args.algorithm == "FJ": - print("Make sure your logs contain FJ: lines with key=value pairs") - elif args.algorithm == "CPUFJ": - print( - "Make sure your logs contain CPUFJ_FEATURES lines with key=value pairs" - ) - elif args.algorithm == "BB": - print( - "Make sure your logs contain BB_NODE_FEATURES lines with key=value pairs" - ) - elif args.algorithm == "DS": - print( - "Make sure your logs contain DS_FEATURES: lines with key=value pairs" - ) - elif args.algorithm == "BOUNDS_STRENGTH": - print( - "Make sure your logs contain BOUNDS_STRENGTH_FEATURES and BOUNDS_STRENGTH_RESULT lines" - ) - return 1 - - # Print statistics - print_statistics(entries, args.algorithm) - - # Convert to DataFrame - df = pd.DataFrame(entries) - - # Convert all non-string columns to numeric types FIRST - # This ensures proper type inference before validation - print("\nConverting column types...") - for col in df.columns: - if col not in ["file"]: # Keep 'file' as string - # Try to convert to numeric, coercing errors to NaN - df[col] = pd.to_numeric(df[col], errors="coerce") - print( - f" ✓ Converted {len([c for c in df.columns if c != 'file'])} columns to numeric types" - ) - - # Validate: Check for NaN and infinite values - print("\nValidating data integrity...") - - # Check for NaN - nan_counts = df.isna().sum() - columns_with_nan = nan_counts[nan_counts > 0] - - # Check for infinite values in numeric columns - numeric_cols = df.select_dtypes(include=[np.number]).columns - inf_counts = {} - for col in numeric_cols: - inf_count = np.isinf(df[col]).sum() - if inf_count > 0: - inf_counts[col] = inf_count - - # Collect all problematic columns - problematic_columns = set() - problematic_columns.update(columns_with_nan.index) - problematic_columns.update(inf_counts.keys()) - - if problematic_columns: - print(f"\n{'=' * 70}") - print("⚠️ WARNING: Invalid data detected (NaN/inf values)!") - print(f"{'=' * 70}") - print("\nColumns with invalid values (will be removed):") - for col in sorted(problematic_columns): - nan_count = ( - nan_counts.get(col, 0) if col in columns_with_nan.index else 0 - ) - inf_count = inf_counts.get(col, 0) - total_invalid = nan_count + inf_count - pct = (total_invalid / len(df)) * 100 - - issues = [] - if nan_count > 0: - issues.append(f"{nan_count} NaN") - if inf_count > 0: - issues.append(f"{inf_count} inf") - - print(f" ❌ {col}: {', '.join(issues)} ({pct:.1f}%)") - - # Show which log files have the issues - rows_with_issues_mask = df.isna().any(axis=1) - for col in inf_counts.keys(): - if col in df.columns: - rows_with_issues_mask |= np.isinf(df[col]) - rows_with_issues = df[rows_with_issues_mask] - problematic_files = rows_with_issues["file"].unique() - print( - f"\nAffected log files: {len(problematic_files)} files, {len(rows_with_issues)} entries" - ) - if len(problematic_files) <= 10: - for filename in sorted(problematic_files): - count_in_file = len( - rows_with_issues[rows_with_issues["file"] == filename] - ) - print( - f" - {filename}: {count_in_file} entries with invalid values" - ) - else: - print(f" (Showing first 10 of {len(problematic_files)} files)") - for i, filename in enumerate(sorted(problematic_files)[:10], 1): - count_in_file = len( - rows_with_issues[rows_with_issues["file"] == filename] - ) - print(f" {i}. {filename}: {count_in_file} entries") - - # Remove problematic columns - original_column_count = len(df.columns) - df = df.drop(columns=list(problematic_columns)) - removed_count = len(problematic_columns) - remaining_count = len(df.columns) - - print(f"\n✓ Removed {removed_count} problematic column(s)") - print(f" Original columns: {original_column_count}") - print(f" Remaining columns: {remaining_count}") - print(f" Kept all {len(df)} entries") - print(f"{'=' * 70}") - - # Check if we have any meaningful columns left (excluding metadata) - feature_cols_remaining = [ - col - for col in df.columns - if col not in ["file", "iter", "iterations"] - ] - if len(feature_cols_remaining) < 5: - print( - f"\n⚠️ WARNING: Very few features remaining ({len(feature_cols_remaining)})!" - ) - print( - " This may not be sufficient for training a regression model." - ) - print(" Consider fixing the underlying issues in your logs.") - else: - print(" ✅ No invalid values detected") - - # Filter out negative iterations (invalid data) - if "iter" in df.columns: - negative_mask = df["iter"] < 0 - negative_count = negative_mask.sum() - - if negative_count > 0: - print( - f"\n⚠️ Found {negative_count} entries with negative iterations ({negative_count / len(df) * 100:.2f}%)" - ) - - # Show which files have negative iterations - negative_entries = df[negative_mask] - problematic_files = negative_entries["file"].unique() - if len(problematic_files) <= 10: - print( - f" Affected files: {', '.join(sorted(problematic_files))}" - ) - else: - print(f" Affected files: {len(problematic_files)} files") - - # Drop negative iterations - df = df[~negative_mask].reset_index(drop=True) - print( - f" → Dropped negative entries, remaining: {len(df)} entries" - ) - - # Check if we have any data left - if len(df) == 0: - print( - "\n❌ Error: No valid entries remaining after filtering!" - ) - print(" All entries had negative iterations.") - return 1 - - # Print obtained features (all columns) - print(f"\nObtained Features ({len(df.columns)} total):") - print(f"{'=' * 70}") - - # Separate metadata from actual features - metadata_cols = ["file"] - target_cols = ["iter", "iterations"] # Target variable (if present) - - feature_cols = [ - col - for col in df.columns - if col not in metadata_cols and col not in target_cols - ] - - # Print in categories - if metadata_cols: - meta_present = [col for col in metadata_cols if col in df.columns] - if meta_present: - print(f" Metadata: {', '.join(meta_present)}") - - target_present = [col for col in target_cols if col in df.columns] - if target_present: - print(f" Target: {', '.join(target_present)}") - - if feature_cols: - print(f" Features ({len(feature_cols)}):") - for i, col in enumerate(sorted(feature_cols), 1): - # Print 3 features per line for readability - if i % 3 == 1: - print(" ", end="") - print(f"{col:30s}", end="") - if i % 3 == 0: - print() # Newline after 3 features - if len(feature_cols) % 3 != 0: - print() # Final newline if needed - - print(f"{'=' * 70}") - - # Final validation: Ensure NO NaN values remain in the DataFrame - print("\nFinal validation before saving...") - remaining_nans = df.isna().sum() - columns_with_remaining_nans = remaining_nans[remaining_nans > 0] - - if len(columns_with_remaining_nans) > 0: - print(f"\n{'=' * 70}") - print("❌ CRITICAL ERROR: NaN values still present after cleaning!") - print(f"{'=' * 70}") - print("\nColumns with remaining NaN values:") - for col, count in columns_with_remaining_nans.items(): - pct = (count / len(df)) * 100 - print(f" - {col}: {count} NaN values ({pct:.1f}%)") - - print("\nRemoving these columns to ensure clean output...") - df = df.drop(columns=list(columns_with_remaining_nans.index)) - print( - f" ✓ Removed {len(columns_with_remaining_nans)} additional column(s)" - ) - print(f" Remaining columns: {len(df.columns)}") - - # Check if we have any meaningful columns left - feature_cols_remaining = [ - col - for col in df.columns - if col not in ["file", "iter", "iterations"] - ] - if len(feature_cols_remaining) == 0: - print( - "\n❌ FATAL ERROR: No feature columns remaining after NaN removal!" - ) - print(" All columns contained NaN values.") - print(" Cannot proceed with saving - no valid data to save.") - return 1 - - print(f"{'=' * 70}") - - # Double-check: verify no NaN or inf values exist - total_nans = df.isna().sum().sum() - if total_nans > 0: - print( - f"\n❌ FATAL ERROR: {total_nans} NaN values still present despite cleaning!" - ) - print(" This should not happen - please report this as a bug.") - return 1 - - # Check for infinite values - numeric_cols = df.select_dtypes(include=[np.number]).columns - total_infs = 0 - for col in numeric_cols: - total_infs += np.isinf(df[col]).sum() - - if total_infs > 0: - print( - f"\n❌ FATAL ERROR: {total_infs} infinite values still present despite cleaning!" - ) - print(" This should not happen - please report this as a bug.") - # Show which columns still have inf - for col in numeric_cols: - inf_count = np.isinf(df[col]).sum() - if inf_count > 0: - print(f" - {col}: {inf_count} inf values") - return 1 - - print(" ✅ No NaN or infinite values detected - data is clean") - - # Save to Feather file (Apache Arrow format for fast I/O) - print(f"\nSaving {len(df)} entries to {args.output}...") - df.to_feather(args.output, compression="lz4") - - # Get file size - file_size_bytes = os.path.getsize(args.output) - file_size_mb = file_size_bytes / (1024 * 1024) - - print(f"\n{'=' * 70}") - print(f"✓ Success! Saved {len(df)} entries to {args.output}") - print(f" File size: {file_size_mb:.2f} MB") - print(f"{'=' * 70}") - print("\nNext steps:") - print(" 1. View available features:") - print( - f" python scripts/train_regressor.py {args.output} --regressor xgboost --list-features" - ) - print(" 2. Train a model:") - print( - f" python scripts/train_regressor.py {args.output} --regressor xgboost --seed 42" - ) - print(" 3. Train with early stopping and C++ export:") - print( - f" python scripts/train_regressor.py {args.output} --regressor xgboost --seed 42 --early-stopping 20 --treelite-compile 8" - ) - - return 0 - - -if __name__ == "__main__": - exit(main()) diff --git a/scripts/train_regressor.py b/scripts/train_regressor.py deleted file mode 100755 index dcb9492cca..0000000000 --- a/scripts/train_regressor.py +++ /dev/null @@ -1,2221 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. -# SPDX-License-Identifier: Apache-2.0 -# 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. - -""" -Train regression models to predict algorithm iterations from log features. - -Usage: - python train_regressor.py --regressor [options] -""" - -import argparse -import pickle -import numpy as np -import pandas as pd -from sklearn.model_selection import train_test_split, cross_val_score, KFold -from sklearn.preprocessing import StandardScaler, PolynomialFeatures -from sklearn.linear_model import LinearRegression, Ridge -from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor -from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score -from sklearn.pipeline import Pipeline -import joblib -import os -from typing import List, Dict, Any, Tuple -import warnings - -warnings.filterwarnings("ignore", category=UserWarning) - - -AVAILABLE_REGRESSORS = [ - "linear", - "poly2", - "poly3", - "poly4", - "xgboost", - "lightgbm", - "random_forest", - "gradient_boosting", -] - -# ============================================================================ -# FEATURE SELECTION CONFIGURATION -# Edit this list to exclude specific features from training -# Leave empty to use all features (except 'file' and 'iter') -# ============================================================================ -FEATURES_TO_EXCLUDE = [ - # Example usage (uncomment to exclude): - # 'time', - # 'avg_constraint_range', - # 'binary_ratio', - "avg_obj_coeff_magnitude", - "n_of_minimums_for_exit", - "feasibility_run", - "fixed_var_ratio", - "unbounded_var_ratio", - "obj_var_ratio", - "avg_related_vars_per_var", - "avg_constraint_range", - "nnz_variance", - "avg_variable_range", - "min_nnz_per_row", - "constraint_var_ratio", - "avg_var_degree", - "equality_ratio", - "integer_ratio", - "binary_ratio", - "max_related_vars", - "problem_size_score", - "structural_complexity", - "tight_constraint_ratio", - "tolerance", - "time_limit", - "tolerance", - "primal_objective", - "dual_objective", - "gap", - "l2_primal_residual", - "l2_dual_residual", - "detect_infeasibility", - "iteration_limit", - "termination", - "check_infeasibility", - "iter_since_best", - "tid", - "curr_obj", - # "obj_weight", - "L3_miss", - "L2_miss", - "L1_miss", - "stores_per_iter", - "loads_per_iter", - # "is_feas", - # "feas_found", - # "viol_ratio", - # "eval_intensity", - # "nnz_per_move", - "iter", - # "max_weight", - "avg_cstr_deg", - "avg_var_deg", - "lp_time", - "node_id", - "var_sel_time", - "bound_str_time", - "sb_time", - "lp_status", - "node_status", - "depth", - "cutoff_gap", - "mem_bandwidth_gb_s", - "max_cstr_deg", - "viol_ratio", - "nnz_per_move", - "h_cstr_right_weights_loads", - "h_cstr_left_weights_loads", - "h_cstr_right_weights_stores", - "h_cstr_left_weights_stores", - "total_viol", - "feas_found", - "obj_weight", - "h_tabu_nodec_until_stores", - "h_tabu_nodec_until_loads", - "h_tabu_noinc_until_stores", - "h_tabu_noinc_until_loads", - "h_tabu_lastdec_stores", - "h_tabu_lastdec_loads", - "h_tabu_lastinc_stores", - "h_tabu_lastinc_loads", - "max_weight", - "fixed", - "phase", - "iters", - "nnz/s", - "nnz/iter", - "runtime", -] - -# Alternatively, specify ONLY the features you want to use -# If non-empty, only these features will be used (overrides FEATURES_TO_EXCLUDE) -FEATURES_TO_INCLUDE_ONLY = [ - # Example usage (uncomment to use only specific features): - # 'n_variables', - # 'n_constraints', - #'sparsity', - # "n_vars", - # "n_cstrs", - # #"total_nnz", - # "mem_total_mb", - # #"cache_hit_rate", - # #"cstr_deg_cv" - # "mem_stores_mb", - # "mem_loads_mb", -] -# ============================================================================ - - -def load_data(data_path: str, target_col: str = "iter") -> pd.DataFrame: - """Load data file (supports .feather and legacy .pkl formats).""" - ext = os.path.splitext(data_path)[1].lower() - - if ext == ".feather": - # Fast Apache Arrow format - df = pd.read_feather(data_path) - elif ext == ".pkl": - # Legacy pickle support - with open(data_path, "rb") as f: - data = pickle.load(f) - - if not isinstance(data, list): - raise ValueError( - f"Expected list of dictionaries, got {type(data)}" - ) - - if len(data) == 0: - raise ValueError("Empty dataset") - - df = pd.DataFrame(data) - else: - raise ValueError( - f"Unsupported file format: {ext}. Use .feather (recommended) or .pkl" - ) - - # Validate required columns - if "file" not in df.columns: - raise ValueError("Missing required 'file' column in data") - if target_col not in df.columns: - raise ValueError( - f"Missing target column '{target_col}' in data. Available columns: {list(df.columns)}" - ) - - return df - - -def split_by_files( - df: pd.DataFrame, - test_size: float = 0.2, - random_state: int = None, - stratify_by: str = None, -) -> Tuple[pd.DataFrame, pd.DataFrame]: - """ - Split data into train/test sets based on unique files. - Ensures all entries from a file go to either train or test, not both. - - Args: - ---- - stratify_by: Optional column name to stratify split (e.g., 'iter' for balanced target distribution) - """ - unique_files = df["file"].unique() - - # Optionally stratify by target distribution - if stratify_by: - # Create stratification labels based on quantiles of the specified column - file_stats = df.groupby("file")[stratify_by].median() - stratify_labels = pd.qcut( - file_stats, - q=min(5, len(unique_files)), - labels=False, - duplicates="drop", - ) - train_files, test_files = train_test_split( - unique_files, - test_size=test_size, - random_state=random_state, - stratify=stratify_labels, - ) - else: - train_files, test_files = train_test_split( - unique_files, test_size=test_size, random_state=random_state - ) - - train_df = df[df["file"].isin(train_files)].copy() - test_df = df[df["file"].isin(test_files)].copy() - - # Validate no data leakage: ensure no overlap between train and test files - train_files_set = set(train_files) - test_files_set = set(test_files) - overlap = train_files_set.intersection(test_files_set) - - if overlap: - raise ValueError( - f"Data leakage detected! {len(overlap)} file(s) appear in both train and test sets:\n" - f" {list(overlap)[:10]}{'...' if len(overlap) > 10 else ''}" - ) - - # Verify the actual dataframes have no file overlap - train_files_in_df = set(train_df["file"].unique()) - test_files_in_df = set(test_df["file"].unique()) - actual_overlap = train_files_in_df.intersection(test_files_in_df) - - if actual_overlap: - raise ValueError( - f"Data leakage detected in dataframes! {len(actual_overlap)} file(s) appear in both:\n" - f" {list(actual_overlap)[:10]}{'...' if len(actual_overlap) > 10 else ''}" - ) - - print("\nData Split:") - print(f" Total entries: {len(df)}") - print(f" Train entries: {len(train_df)} ({len(train_files)} files)") - print(f" Test entries: {len(test_df)} ({len(test_files)} files)") - print(" ✓ Verified: Zero file overlap between train and test sets") - - # Check distribution similarity (use stratify_by column if provided, otherwise first numeric column) - target_col = ( - stratify_by - if stratify_by - else df.select_dtypes(include=[np.number]).columns[0] - ) - if target_col in train_df.columns and target_col in test_df.columns: - train_target_mean = train_df[target_col].mean() - test_target_mean = test_df[target_col].mean() - train_target_std = train_df[target_col].std() - test_target_std = test_df[target_col].std() - - print(f"\nTarget ('{target_col}') Distribution:") - print( - f" Train: mean={train_target_mean:.2f}, std={train_target_std:.2f}" - ) - print( - f" Test: mean={test_target_mean:.2f}, std={test_target_std:.2f}" - ) - - mean_diff_pct = ( - abs(train_target_mean - test_target_mean) / train_target_mean * 100 - if train_target_mean != 0 - else 0 - ) - if mean_diff_pct > 10: - print( - f" ⚠️ Warning: Train/test target means differ by {mean_diff_pct:.1f}%" - ) - print( - " Consider using stratified split or different random seed" - ) - - return train_df, test_df - - -def list_available_features( - df: pd.DataFrame, target_col: str = "iter" -) -> List[str]: - """ - List all available numeric features in the dataset. - Helper function to see what features can be selected/excluded. - """ - # Drop target and metadata columns - cols_to_drop = [ - target_col, - "file", - "iter", - "iterations", - ] # Drop common target column names - X = df.drop( - columns=[c for c in cols_to_drop if c in df.columns], errors="ignore" - ) - X = X.select_dtypes(include=[np.number]) - return sorted(X.columns.tolist()) - - -def validate_data_quality( - df: pd.DataFrame, target_col: str = "iter", verbose: bool = True -) -> Tuple[bool, Dict[str, Any]]: - """ - Validate data quality and report issues. - - Returns - ------- - (is_valid, report_dict) - """ - report = { - "target_issues": [], - "feature_issues": [], - "rows_with_issues": [], - "is_valid": True, - } - - # Check target variable - if target_col not in df.columns: - report["is_valid"] = False - report["target_issues"].append(f"Missing '{target_col}' column") - return False, report - - y = df[target_col] - - # Check for NaN in target - nan_count = y.isna().sum() - if nan_count > 0: - report["is_valid"] = False - report["target_issues"].append( - f"Target has {nan_count} NaN values ({nan_count / len(y) * 100:.1f}%)" - ) - report["rows_with_issues"].extend(df[y.isna()].index.tolist()) - - # Check for inf in target - inf_count = np.isinf(y).sum() - if inf_count > 0: - report["is_valid"] = False - report["target_issues"].append( - f"Target has {inf_count} infinite values ({inf_count / len(y) * 100:.1f}%)" - ) - report["rows_with_issues"].extend(df[np.isinf(y)].index.tolist()) - - # Check for extreme values in target - if not y.isna().all() and not np.isinf(y).all(): - y_clean = y[~(y.isna() | np.isinf(y))] - if len(y_clean) > 0: - y_max = y_clean.max() - y_min = y_clean.min() - if y_max > 1e10: - report["target_issues"].append( - f"Target has very large values (max={y_max:.2e})" - ) - if y_min < -1e10: - report["target_issues"].append( - f"Target has very large negative values (min={y_min:.2e})" - ) - - # Check features - X = df.drop(columns=[target_col, "file"], errors="ignore") - X_numeric = X.select_dtypes(include=[np.number]) - - for col in X_numeric.columns: - col_data = X_numeric[col] - - # Check for NaN - nan_count = col_data.isna().sum() - if nan_count > 0: - pct = nan_count / len(col_data) * 100 - if pct > 10: # Only report if > 10% are NaN - report["feature_issues"].append( - f"{col}: {nan_count} NaN ({pct:.1f}%)" - ) - - # Check for inf - inf_count = np.isinf(col_data).sum() - if inf_count > 0: - report["is_valid"] = False - pct = inf_count / len(col_data) * 100 - report["feature_issues"].append( - f"{col}: {inf_count} infinite ({pct:.1f}%)" - ) - report["rows_with_issues"].extend( - df[np.isinf(col_data)].index.tolist() - ) - - # Deduplicate row indices - report["rows_with_issues"] = sorted(list(set(report["rows_with_issues"]))) - - # Print report if verbose - if verbose and (report["target_issues"] or report["feature_issues"]): - print("\n" + "=" * 70) - print("DATA QUALITY ISSUES DETECTED") - print("=" * 70) - - if report["target_issues"]: - print("\nTarget Variable Issues:") - for issue in report["target_issues"]: - print(f" ❌ {issue}") - - if report["feature_issues"]: - print( - f"\nFeature Issues ({len(report['feature_issues'])} features affected):" - ) - for issue in report["feature_issues"][:10]: # Show first 10 - print(f" ⚠️ {issue}") - if len(report["feature_issues"]) > 10: - print( - f" ... and {len(report['feature_issues']) - 10} more features" - ) - - if report["rows_with_issues"]: - print(f"\nAffected Rows: {len(report['rows_with_issues'])} total") - if len(report["rows_with_issues"]) <= 10: - print(f" Row indices: {report['rows_with_issues']}") - else: - print(f" First 10: {report['rows_with_issues'][:10]}") - - # Show sample problematic rows with filenames - if "file" in df.columns: - print("\n Sample problematic entries:") - sample_indices = report["rows_with_issues"][:5] - for idx in sample_indices: - if idx < len(df): - filename = df.iloc[idx].get("file", "unknown") - iter_val = df.iloc[idx].get("iter", "N/A") - print( - f" Row {idx}: file={filename}, iter={iter_val}" - ) - - print("\nSuggested Actions:") - if not report["is_valid"]: - print(" 1. Remove rows with invalid data: --drop-invalid-rows") - print(" 2. Check your log files for data collection issues") - print(" 3. Verify algorithm didn't produce invalid results") - else: - print( - " Data is valid but has some NaN values in features (will be handled)" - ) - - print("=" * 70 + "\n") - - return report["is_valid"], report - - -def prepare_features( - df: pd.DataFrame, target_col: str = "iter" -) -> Tuple[pd.DataFrame, pd.Series, List[str]]: - """ - Prepare features and target from DataFrame. - Excludes 'file' and target column from features. - Applies feature selection based on FEATURES_TO_EXCLUDE and FEATURES_TO_INCLUDE_ONLY. - """ - # Separate target - y = df[target_col].copy() - - # Drop non-feature columns - X = df.drop(columns=[target_col, "file"]) - - # Ensure all features are numeric - non_numeric = X.select_dtypes(exclude=[np.number]).columns.tolist() - if non_numeric: - print(f"Warning: Dropping non-numeric columns: {non_numeric}") - X = X.select_dtypes(include=[np.number]) - - # Apply feature selection - original_feature_count = len(X.columns) - - if FEATURES_TO_INCLUDE_ONLY: - # Use only specified features - available_features = [ - f for f in FEATURES_TO_INCLUDE_ONLY if f in X.columns - ] - missing_features = [ - f for f in FEATURES_TO_INCLUDE_ONLY if f not in X.columns - ] - - if missing_features: - print( - f"Warning: Requested features not found in data: {missing_features}" - ) - - X = X[available_features] - print( - f"Feature selection: Using only {len(available_features)} specified features" - ) - - elif FEATURES_TO_EXCLUDE: - # Exclude specified features - features_to_drop = [f for f in FEATURES_TO_EXCLUDE if f in X.columns] - if features_to_drop: - X = X.drop(columns=features_to_drop) - print( - f"Feature selection: Excluded {len(features_to_drop)} features: {features_to_drop}" - ) - - feature_names = X.columns.tolist() - - if len(feature_names) == 0: - raise ValueError( - "No features remaining after feature selection! " - "Check FEATURES_TO_EXCLUDE and FEATURES_TO_INCLUDE_ONLY settings." - ) - - if len(feature_names) != original_feature_count: - print( - f" Using {len(feature_names)} of {original_feature_count} available features" - ) - - return X, y, feature_names - - -def create_regressor( - regressor_type: str, - random_state: int = None, - tune_hyperparams: bool = False, - verbose: bool = True, -): - """ - Create a regression model with optional preprocessing pipeline. - - Returns: (model, needs_scaling) - """ - if regressor_type == "linear": - model = LinearRegression() - needs_scaling = True - - elif regressor_type.startswith("poly"): - degree = int(regressor_type[-1]) - model = Pipeline( - [ - ( - "poly", - PolynomialFeatures(degree=degree, include_bias=False), - ), - ( - "regressor", - Ridge(alpha=1.0), - ), # Ridge to handle multicollinearity - ] - ) - needs_scaling = True - - elif regressor_type == "xgboost": - try: - import xgboost as xgb - except ImportError: - raise ImportError( - "XGBoost not installed. Install with: pip install xgboost" - ) - - params = { - "objective": "reg:squarederror", - "random_state": random_state, - "n_estimators": 100, - "max_depth": 6, - "tree_method": "hist", - "learning_rate": 0.1, - "verbosity": 1 if verbose else 0, - # Regularization to prevent overfitting - "min_child_weight": 3, # Minimum sum of weights in a leaf - "gamma": 0.1, # Minimum loss reduction for split - "subsample": 0.8, # Fraction of samples per tree - "colsample_bytree": 0.8, # Fraction of features per tree - "reg_alpha": 0.1, # L1 regularization - "reg_lambda": 1.0, # L2 regularization - } - - if tune_hyperparams: - # Stronger regularization for tuned version - params.update( - { - "n_estimators": 200, - "max_depth": 5, # Shallower trees - "learning_rate": 0.05, # Lower learning rate - "min_child_weight": 5, # Higher minimum weight - "gamma": 0.2, # More conservative splits - "subsample": 0.7, # More aggressive subsampling - "colsample_bytree": 0.7, - "reg_alpha": 0.5, # Stronger L1 - "reg_lambda": 2.0, # Stronger L2 - } - ) - - model = xgb.XGBRegressor(**params) - needs_scaling = True - - elif regressor_type == "lightgbm": - try: - import lightgbm as lgb - except ImportError: - raise ImportError( - "LightGBM not installed. Install with: pip install lightgbm" - ) - - params = { - "objective": "regression", - "random_state": random_state, - "n_estimators": 150, - "max_depth": 6, - "learning_rate": 0.1, - "verbosity": 1 if verbose else -1, - # Regularization to prevent overfitting - "min_child_weight": 3, - "min_split_gain": 0.1, - "subsample": 0.8, - "colsample_bytree": 0.8, - "reg_alpha": 0.1, - "reg_lambda": 1.0, - } - - if tune_hyperparams: - # Stronger regularization for tuned version - params.update( - { - "n_estimators": 200, - "max_depth": 5, - "learning_rate": 0.05, - "min_child_weight": 5, - "min_split_gain": 0.2, - "subsample": 0.7, - "colsample_bytree": 0.7, - "reg_alpha": 0.5, - "reg_lambda": 2.0, - } - ) - - model = lgb.LGBMRegressor(**params) - needs_scaling = True - - elif regressor_type == "random_forest": - params = { - "random_state": random_state, - "n_estimators": 100, - "max_depth": None, - "min_samples_split": 2, - "verbose": 1 if verbose else 0, - } - - if tune_hyperparams: - params.update( - {"n_estimators": 200, "max_depth": 20, "min_samples_split": 5} - ) - - model = RandomForestRegressor(**params) - needs_scaling = False - - elif regressor_type == "gradient_boosting": - params = { - "random_state": random_state, - "n_estimators": 100, - "max_depth": 5, - "learning_rate": 0.1, - "verbose": 1 if verbose else 0, - } - - if tune_hyperparams: - params.update( - {"n_estimators": 200, "max_depth": 7, "learning_rate": 0.05} - ) - - model = GradientBoostingRegressor(**params) - needs_scaling = False - - else: - raise ValueError(f"Unknown regressor type: {regressor_type}") - - return model, needs_scaling - - -def get_feature_importance( - model, feature_names: List[str], regressor_type: str -) -> None: - """Extract and print feature importance if available.""" - print("\nFeature Importance:") - - try: - if regressor_type in [ - "xgboost", - "lightgbm", - "random_forest", - "gradient_boosting", - ]: - # Tree-based models have feature_importances_ - importances = model.feature_importances_ - indices = np.argsort(importances)[::-1] - - for i, idx in enumerate(indices, 1): - print( - f" {i:3d}. {feature_names[idx]:40s}: {importances[idx]:.6f}" - ) - - elif regressor_type == "linear": - # Linear regression coefficients and intercept - print(f"\nIntercept: {model.intercept_:.6f}\n") - print("Coefficients:") - - # Print each feature with its coefficient - for i, (feature_name, coef) in enumerate( - zip(feature_names, model.coef_), 1 - ): - print(f" {i:3d}. {feature_name:40s}: {coef:.6f}") - - # Also show sorted by absolute value for importance ranking - print("\nRanked by absolute magnitude:") - coefs_abs = np.abs(model.coef_) - indices = np.argsort(coefs_abs)[::-1] - for i, idx in enumerate(indices, 1): - print( - f" {i:3d}. {feature_names[idx]:40s}: {model.coef_[idx]:.6f} (|{coefs_abs[idx]:.6f}|)" - ) - - elif regressor_type.startswith("poly"): - # For polynomial, get feature names and coefficients from the Ridge step - poly_features = model.named_steps["poly"].get_feature_names_out( - feature_names - ) - coefs = np.abs(model.named_steps["regressor"].coef_) - indices = np.argsort(coefs)[::-1] - - print(f" (Showing top 50 of {len(indices)} polynomial features)") - for i, idx in enumerate(indices[:50], 1): - feat_name = poly_features[idx] - # Truncate very long polynomial feature names - if len(feat_name) > 60: - feat_name = feat_name[:57] + "..." - print(f" {i:3d}. {feat_name:60s}: {coefs[idx]:.6f}") - else: - print(" Feature importance not available for this model type") - - except Exception as e: - print(f" Could not extract feature importance: {e}") - - -def evaluate_model( - model, - X_train, - y_train, - X_test, - y_test, - feature_names: List[str], - regressor_type: str, - cv_folds: int = 5, - verbose: int = 0, - skip_cv: bool = False, - X_test_original: pd.DataFrame = None, - test_df: pd.DataFrame = None, - log_transform: bool = False, - y_train_original: pd.Series = None, - y_test_original: pd.Series = None, - log_transform_offset: float = 0.0, -) -> Tuple[float, float]: - """Evaluate model and print metrics. Returns (train_r2, test_r2). - - Args: - ---- - X_test_original: Unscaled X_test for displaying feature values - test_df: Original test dataframe with 'file' column - log_transform: If True, predictions are in log-space and need inverse transform - y_train_original: Original (non-log) training targets (if log_transform=True) - y_test_original: Original (non-log) test targets (if log_transform=True) - log_transform_offset: Constant added before log transform (if log_transform=True) - """ - # Cross-validation on training set (skip if using early stopping) - if not skip_cv: - print(f"\nCross-Validation on Training Set ({cv_folds}-fold):") - try: - # Compute RMSE and R² in log-space - cv_scores_mse = cross_val_score( - model, - X_train, - y_train, - cv=cv_folds, - scoring="neg_mean_squared_error", - n_jobs=-1, - verbose=verbose, - ) - cv_scores_r2 = cross_val_score( - model, - X_train, - y_train, - cv=cv_folds, - scoring="r2", - n_jobs=-1, - verbose=verbose, - ) - cv_rmse = np.sqrt(-cv_scores_mse) - - if log_transform: - print(" Log-space metrics:") - print( - f" CV RMSE: {cv_rmse.mean():.4f} (+/- {cv_rmse.std():.4f})" - ) - print( - f" CV R²: {cv_scores_r2.mean():.4f} (+/- {cv_scores_r2.std():.4f})" - ) - - # Also compute metrics in original space - kfold = KFold(n_splits=cv_folds, shuffle=True, random_state=42) - - cv_mape_scores = [] - cv_r2_original_scores = [] - - for train_idx, val_idx in kfold.split(X_train): - X_train_fold = ( - X_train.iloc[train_idx] - if hasattr(X_train, "iloc") - else X_train[train_idx] - ) - y_train_fold = ( - y_train.iloc[train_idx] - if hasattr(y_train, "iloc") - else y_train[train_idx] - ) - X_val_fold = ( - X_train.iloc[val_idx] - if hasattr(X_train, "iloc") - else X_train[val_idx] - ) - y_val_original_fold = ( - y_train_original.iloc[val_idx] - if hasattr(y_train_original, "iloc") - else y_train_original[val_idx] - ) - - # Train on fold - model_fold = type(model)(**model.get_params()) - model_fold.fit(X_train_fold, y_train_fold) - - # Predict and transform back - y_pred_log = model_fold.predict(X_val_fold) - y_pred_original = np.exp(y_pred_log) - log_transform_offset - - # Compute metrics in original space - mape = ( - np.mean( - np.abs( - (y_val_original_fold - y_pred_original) - / y_val_original_fold - ) - ) - * 100 - ) - r2_original = r2_score( - y_val_original_fold, y_pred_original - ) - - cv_mape_scores.append(mape) - cv_r2_original_scores.append(r2_original) - - cv_mape = np.array(cv_mape_scores) - cv_r2_original = np.array(cv_r2_original_scores) - - print(" Original-space metrics:") - print( - f" CV MAPE: {cv_mape.mean():.2f}% (+/- {cv_mape.std():.2f}%)" - ) - print( - f" CV R²: {cv_r2_original.mean():.4f} (+/- {cv_r2_original.std():.4f})" - ) - else: - print( - f" CV RMSE: {cv_rmse.mean():.4f} (+/- {cv_rmse.std():.4f})" - ) - print( - f" CV R²: {cv_scores_r2.mean():.4f} (+/- {cv_scores_r2.std():.4f})" - ) - - except Exception as e: - print( - f" CV failed (likely due to early stopping): {str(e)[:100]}" - ) - print(" Skipping cross-validation...") - else: - print("\nSkipping cross-validation (incompatible with early stopping)") - - # Training set metrics - y_train_pred = model.predict(X_train) - - # If log-transformed, also compute metrics in original space - if log_transform: - # Inverse transform predictions - y_train_pred_original = np.exp(y_train_pred) - log_transform_offset - y_test_pred_log = model.predict(X_test) - y_test_pred_original = np.exp(y_test_pred_log) - log_transform_offset - - # Metrics in log-space - train_mse_log = mean_squared_error(y_train, y_train_pred) - train_rmse_log = np.sqrt(train_mse_log) - train_r2_log = r2_score(y_train, y_train_pred) - - # Metrics in original space - train_mse = mean_squared_error(y_train_original, y_train_pred_original) - train_rmse = np.sqrt(train_mse) - train_mae = mean_absolute_error( - y_train_original, y_train_pred_original - ) - train_r2 = r2_score(y_train_original, y_train_pred_original) - train_mape = ( - np.mean( - np.abs( - (y_train_original - y_train_pred_original) - / y_train_original - ) - ) - * 100 - ) - - print("\nTraining Set Metrics (Original Space):") - print(f" MSE: {train_mse:.4f}") - print(f" RMSE: {train_rmse:.4f}") - print(f" MAE: {train_mae:.4f}") - print(f" MAPE: {train_mape:.2f}% (optimized metric)") - print(f" R²: {train_r2:.4f}") - print("\nTraining Set Metrics (Log Space):") - print(f" RMSE: {train_rmse_log:.4f}") - print(f" R²: {train_r2_log:.4f}") - else: - train_mse = mean_squared_error(y_train, y_train_pred) - train_rmse = np.sqrt(train_mse) - train_mae = mean_absolute_error(y_train, y_train_pred) - train_r2 = r2_score(y_train, y_train_pred) - - print("\nTraining Set Metrics:") - print(f" MSE: {train_mse:.4f}") - print(f" RMSE: {train_rmse:.4f}") - print(f" MAE: {train_mae:.4f}") - print(f" R²: {train_r2:.4f}") - - # Test set metrics - if log_transform: - # Already computed above - test_mse_log = mean_squared_error(y_test, y_test_pred_log) - test_rmse_log = np.sqrt(test_mse_log) - test_r2_log = r2_score(y_test, y_test_pred_log) - - # Metrics in original space - test_mse = mean_squared_error(y_test_original, y_test_pred_original) - test_rmse = np.sqrt(test_mse) - test_mae = mean_absolute_error(y_test_original, y_test_pred_original) - test_r2 = r2_score(y_test_original, y_test_pred_original) - test_mape = ( - np.mean( - np.abs( - (y_test_original - y_test_pred_original) / y_test_original - ) - ) - * 100 - ) - - print("\nTest Set Metrics (Original Space):") - print(f" MSE: {test_mse:.4f}") - print(f" RMSE: {test_rmse:.4f}") - print(f" MAE: {test_mae:.4f}") - print(f" MAPE: {test_mape:.2f}% (optimized metric)") - print(f" R²: {test_r2:.4f}") - print("\nTest Set Metrics (Log Space):") - print(f" RMSE: {test_rmse_log:.4f}") - print(f" R²: {test_r2_log:.4f}") - - # Use original space predictions for sample display - y_test_pred = y_test_pred_original - y_test = y_test_original - else: - y_test_pred = model.predict(X_test) - test_mse = mean_squared_error(y_test, y_test_pred) - test_rmse = np.sqrt(test_mse) - test_mae = mean_absolute_error(y_test, y_test_pred) - test_r2 = r2_score(y_test, y_test_pred) - - print("\nTest Set Metrics:") - print(f" MSE: {test_mse:.4f}") - print(f" RMSE: {test_rmse:.4f}") - print(f" MAE: {test_mae:.4f}") - print(f" R²: {test_r2:.4f}") - - # If R² is negative, show baseline comparison for debugging - if test_r2 < 0: - y_test_mean = np.mean(y_test) - baseline_pred = np.full_like(y_test_pred, y_test_mean) - baseline_mse = mean_squared_error(y_test, baseline_pred) - baseline_rmse = np.sqrt(baseline_mse) - print("\n WARNING: Negative R² detected!") - print( - f" This means the model is worse than predicting the mean: {y_test_mean:.4f}" - ) - print(f" Baseline (mean) RMSE: {baseline_rmse:.4f}") - print(f" Model RMSE: {test_rmse:.4f}") - print( - f" Model is {test_rmse / baseline_rmse:.2f}x worse than baseline" - ) - - # Feature importance - get_feature_importance(model, feature_names, regressor_type) - - # Sample predictions - print("\n20 Sample Predictions from Test Set:") - print( - f" {'Actual':>10s} {'Predicted':>10s} {'Error':>10s} {'Error %':>10s}" - ) - print(f" {'-' * 10} {'-' * 10} {'-' * 10} {'-' * 10}") - - sample_indices = np.random.choice( - len(y_test), min(20, len(y_test)), replace=False - ) - for idx in sample_indices: - actual = y_test.iloc[idx] - predicted = y_test_pred[idx] - error = actual - predicted - error_pct = (error / actual * 100) if actual != 0 else 0 - print( - f" {actual:10.2f} {predicted:10.2f} {error:10.2f} {error_pct:9.2f}%" - ) - - # Show worst predictions with feature values - print("\n5 Worst Predictions (Largest Absolute Error):") - abs_errors = np.abs(y_test_pred - y_test.values) - worst_indices = np.argsort(abs_errors)[-5:][::-1] - - # Use original (unscaled) features if available, otherwise use X_test - X_display = X_test_original if X_test_original is not None else X_test - - for rank, idx in enumerate(worst_indices, 1): - actual = y_test.iloc[idx] - predicted = y_test_pred[idx] - error = actual - predicted - error_pct = (error / actual * 100) if actual != 0 else 0 - - # Get filename if available - filename = "" - if test_df is not None and "file" in test_df.columns: - filename = f" (file: {test_df.iloc[idx]['file']})" - - print( - f"\n #{rank} - Actual: {actual:.2f}, Predicted: {predicted:.2f}, " - f"Error: {error:.2f} ({error_pct:.1f}%){filename}" - ) - - # Get feature values (handle both DataFrame and array) - if isinstance(X_display, pd.DataFrame): - feature_values = X_display.iloc[idx].values - elif isinstance(X_display, np.ndarray): - feature_values = X_display[idx] - else: - feature_values = X_display[idx] - - # Display features compactly (5 per line) - print(" Features:", end="") - for i, (feat_name, feat_val) in enumerate( - zip(feature_names, feature_values) - ): - if i % 5 == 0: - print("\n ", end="") - # Format feature value - if isinstance(feat_val, (int, np.integer)): - print(f"{feat_name}={feat_val}", end=" ") - else: - print(f"{feat_name}={feat_val:.3g}", end=" ") - print() # Final newline - - return train_r2, test_r2 - - -def compile_model_treelite( - model, - regressor_type: str, - output_dir: str, - num_threads: int, - X_train=None, - annotate: bool = False, - quantize: bool = False, - feature_names: List[str] = None, - model_name: str = None, - log_transform: bool = False, - log_transform_offset: float = 0.0, -) -> None: - """Compile XGBoost/LightGBM model to C source files using TL2cgen. - - Args: - ---- - model: Trained model - regressor_type: Type of regressor - output_dir: Output directory - num_threads: Number of parallel compilation threads - X_train: Training data for branch annotation (optional) - annotate: Whether to annotate branches for optimization - quantize: Whether to use quantization in code generation - feature_names: List of feature names in expected order (optional) - model_name: Name prefix for functions (optional, derived from training file) - log_transform: Whether model predicts in log-space (will add exp() wrapper) - log_transform_offset: Constant added before log transform (if log_transform=True) - """ - if regressor_type not in ["xgboost", "lightgbm"]: - print( - f"Warning: TL2cgen compilation only supported for XGBoost and LightGBM, skipping for {regressor_type}" - ) - return - - try: - import treelite - import tl2cgen - except ImportError: - missing = [] - try: - import treelite - except ImportError: - missing.append("treelite") - try: - import tl2cgen - except ImportError: - missing.append("tl2cgen") - - print( - f"Warning: {', '.join(missing)} not installed. Install with: pip install {' '.join(missing)}" - ) - print("Skipping C code generation.") - return - - optimization_info = [] - if annotate: - optimization_info.append("branch annotation") - if quantize: - optimization_info.append("quantization") - - opt_str = ( - f" with {', '.join(optimization_info)}" if optimization_info else "" - ) - print( - f"\nGenerating C source code with TL2cgen (threads={num_threads}){opt_str}..." - ) - - # Convert model to treelite format using frontend API - try: - if regressor_type == "xgboost": - tl_model = treelite.frontend.from_xgboost(model.get_booster()) - elif regressor_type == "lightgbm": - tl_model = treelite.frontend.from_lightgbm(model.booster_) - except Exception as e: - print( - f"Warning: Failed to convert {regressor_type} model to treelite: {e}" - ) - return - - # Annotate branches if requested and training data is available - annotation_path = None - if annotate and X_train is not None: - try: - print(" Annotating branches with training data...") - # Convert to numpy array if it's a DataFrame - if hasattr(X_train, "values"): - X_train_array = X_train.values.astype(np.float32) - else: - X_train_array = np.asarray(X_train, dtype=np.float32) - - dmat = tl2cgen.DMatrix(X_train_array, dtype="float32") - annotation_path = os.path.join( - output_dir, f"{regressor_type}_annotation.json" - ) - tl2cgen.annotate_branch( - tl_model, dmat=dmat, path=annotation_path, verbose=False - ) - print(f" Branch annotations saved to: {annotation_path}") - except Exception as e: - print(f" Warning: Branch annotation failed: {e}") - print(" Continuing without branch annotation") - annotation_path = None - elif annotate and X_train is None: - print( - " Warning: Branch annotation requested but no training data available" - ) - print(" Skipping branch annotation") - - # Generate C source files using TL2cgen - source_dir = os.path.join(output_dir, f"{regressor_type}_c_code") - - try: - # params = {'parallel_comp': num_threads} - params = {} - - # Add quantization parameter if requested - if quantize: - params["quantize"] = 1 # Enable quantization in code generation - - # Add annotation file if available - if annotation_path: - params["annotate_in"] = annotation_path - - tl2cgen.generate_c_code( - tl_model, dirpath=source_dir, params=params, verbose=False - ) - - # Post-process generated files - header_path = os.path.join(source_dir, "header.h") - main_path = os.path.join(source_dir, "main.c") - quantize_path = os.path.join(source_dir, "quantize.c") - recipe_path = os.path.join(source_dir, "recipe.json") - - # Rename all .c files to .cpp and wrap in class - if model_name: - try: - import glob - - c_files = glob.glob(os.path.join(source_dir, "*.c")) - - for c_file in c_files: - cpp_file = c_file[:-2] + ".cpp" - - # Read content - with open(c_file, "r") as f: - content = f.read() - - # Split content into includes and rest - lines = content.split("\n") - include_lines = [] - code_lines = [] - in_includes = True - - for line in lines: - if in_includes and ( - line.strip().startswith("#include") - or line.strip().startswith("#") - or line.strip() == "" - ): - include_lines.append(line) - else: - in_includes = False - code_lines.append(line) - - # Prefix function definitions with ClassName:: (for .cpp files, not class wrapping) - import re - - processed_lines = [] - for line in code_lines: - # Detect function definitions (return_type function_name(...)) - if ( - line - and not line.strip().startswith("//") - and not line.strip().startswith("/*") - ): - # Check if it's a function definition - # Pattern: type name(...) or type* name(...) etc. - func_pattern = r"^(\s*)((?:const\s+)?(?:unsigned\s+)?(?:struct\s+)?[\w_]+(?:\s*\*)*\s+)([\w_]+)(\s*\()" - match = re.match(func_pattern, line) - if ( - match and "::" not in line - ): # Don't add if already qualified - indent = match.group(1) - return_type = match.group(2) - func_name = match.group(3) - rest = line[match.end(3) :] - # Prefix function name with class name - line = f"{indent}{return_type}{model_name}::{func_name}{rest}" - processed_lines.append(line) - code_lines = processed_lines - - # Don't wrap in class for .cpp files - just output the definitions - includes_str = "\n".join(include_lines) - code_str = "\n".join(code_lines) - - # For .cpp files, no class wrapper needed - cpp_content = f"{includes_str}\n\n{code_str}\n" - - # Write to .cpp file - with open(cpp_file, "w") as f: - f.write(cpp_content) - - # Remove original .c file - os.remove(c_file) - - # Update paths for further processing - main_path = main_path[:-2] + ".cpp" - quantize_path = quantize_path[:-2] + ".cpp" - - print(f" Renamed {len(c_files)} .c files to .cpp") - except Exception as e: - print(f" Warning: Failed to rename .c files: {e}") - - # Optimize main.cpp by removing unnecessary missing data checks - # Since all features are always provided, replace !(data[X].missing != -1) with false - if os.path.exists(main_path): - try: - with open(main_path, "r") as f: - content = f.read() - - # Replace pattern !(data[N].missing != -1) with false - import re - - original_content = content - content = re.sub( - r"!\(data\[\d+\]\.missing != -1\)", "false", content - ) - - # If log_transform is used, modify postprocess to apply exp/offset - if log_transform: - offset_literal = f"{log_transform_offset:.12g}" - if log_transform_offset != 0: - offset_expr = f" - ({offset_literal})" - else: - offset_expr = "" - # Add include if not present - if ( - "#include " not in content - and "#include" not in content - ): - # Find the last #include and add after it - include_match = None - for match in re.finditer( - r'#include\s*[<"].*?[>"]', content - ): - include_match = match - if include_match: - insert_pos = include_match.end() - content = ( - content[:insert_pos] - + "\n#include " - + content[insert_pos:] - ) - - postprocess_pattern = re.compile( - r"void\s+(\w+)::postprocess\s*\(\s*double\*\s*result\s*\)\s*\{\n.*?\n\}", - re.DOTALL, - ) - - def replace_postprocess(match: re.Match) -> str: - class_name = match.group(1) - return ( - f"void {class_name}::postprocess(double* result)\n" - "{\n" - " for (int i = 0; i < N_TARGET; ++i) {\n" - f" result[i] = std::exp(result[i]){offset_expr};\n" - " }\n" - "}" - ) - - updated_content = postprocess_pattern.sub( - replace_postprocess, content, count=1 - ) - - if updated_content != content: - content = updated_content - print( - " Updated postprocess to convert log-space predictions to original space" - ) - else: - print( - " Warning: Failed to update postprocess for log-transform" - ) - - if content != original_content: - with open(main_path, "w") as f: - f.write(content) - if not log_transform: - print( - " Optimized main.cpp by removing unnecessary missing data checks" - ) - except Exception as e: - print(f" Warning: Failed to optimize main.cpp: {e}") - - # Wrap header.h content in class with #pragma once - defines_to_move = [] - if model_name and os.path.exists(header_path): - try: - with open(header_path, "r") as f: - content = f.read() - - # Split content into includes, defines to move, and rest - lines = content.split("\n") - include_lines = [] - code_lines = [] - in_includes = True - i = 0 - - while i < len(lines): - line = lines[i] - - if in_includes and ( - line.strip().startswith("#include") - or line.strip() == "" - ): - include_lines.append(line) - i += 1 - # Detect macros to move to main.cpp - elif ( - line.strip().startswith("#if defined(__clang__)") - or line.strip().startswith("#define N_TARGET") - or line.strip().startswith("#define MAX_N_CLASS") - ): - in_includes = False - # Capture the entire #if block or single #define - if line.strip().startswith("#if defined(__clang__)"): - # Capture the entire #if...#endif block - macro_block = [] - macro_block.append(line) - i += 1 - while i < len(lines) and not lines[ - i - ].strip().startswith("#endif"): - macro_block.append(lines[i]) - i += 1 - if i < len(lines): - macro_block.append(lines[i]) # Include #endif - i += 1 - defines_to_move.append("\n".join(macro_block)) - else: - # Single #define line - defines_to_move.append(line) - i += 1 - else: - in_includes = False - code_lines.append(line) - i += 1 - - # Add static keyword to function declarations - import re - - processed_lines = [] - for line in code_lines: - # Detect function declarations/definitions (return_type function_name(...)) - # Match lines that look like function declarations but don't already have static - if ( - line - and not line.strip().startswith("//") - and not line.strip().startswith("/*") - ): - # Check if it's a function declaration/definition - # Pattern: type name(...) or type* name(...) or type name[...](...) etc. - func_pattern = r"^(\s*)((?:const\s+)?(?:unsigned\s+)?(?:struct\s+)?[\w_]+(?:\s*\*)*\s+)([\w_]+)\s*\(" - match = re.match(func_pattern, line) - if match and "static" not in line: - indent = match.group(1) - return_type = match.group(2) - # Add static keyword - line = f"{indent}static {return_type}{line[len(indent) + len(return_type) :]}" - processed_lines.append(line) - code_lines = processed_lines - - # Wrap code in class declaration - includes_str = "\n".join(include_lines) - code_str = "\n".join(code_lines) - - wrapped_content = f"#pragma once\n\n{includes_str}\n\nclass {model_name} {{\npublic:\n{code_str}\n}}; // class {model_name}\n" - - with open(header_path, "w") as f: - f.write(wrapped_content) - - print( - f" Wrapped header.h in class '{model_name}' with #pragma once" - ) - except Exception as e: - print(f" Warning: Failed to wrap header.h: {e}") - - # Add defines to main.cpp (moved from header.h) - if defines_to_move and os.path.exists(main_path): - try: - with open(main_path, "r") as f: - content = f.read() - - # Insert defines after includes (look for where code starts - typically after blank line after includes) - defines_str = "\n".join(defines_to_move) - - # Find the first non-include, non-blank line to insert before - lines = content.split("\n") - insert_pos = 0 - for i, line in enumerate(lines): - if line.strip() and not line.strip().startswith( - "#include" - ): - insert_pos = i - break - - # Insert defines at the position - lines.insert(insert_pos, defines_str) - lines.insert( - insert_pos + 1, "" - ) # Add blank line after defines - - content = "\n".join(lines) - with open(main_path, "w") as f: - f.write(content) - print( - f" Moved {len(defines_to_move)} macro definition(s) from header.h to main.cpp" - ) - except Exception as e: - print(f" Warning: Failed to add defines to main.cpp: {e}") - - # Add feature names to header and implementation - if ( - feature_names - and os.path.exists(header_path) - and os.path.exists(main_path) - ): - try: - # Append to header.h (inside class) - with open(header_path, "r") as f: - content = f.read() - - # Insert before closing class - insertion = f"\n // Feature names\n static constexpr int NUM_FEATURES = {len(feature_names)};\n static const char* feature_names[NUM_FEATURES];\n" - content = content.replace( - f"}}; // class {model_name}\n", - f"{insertion}}}; // class {model_name}\n", - ) - - with open(header_path, "w") as f: - f.write(content) - - # Append to main.cpp (at the end of the file, outside any class) - with open(main_path, "r") as f: - content = f.read() - - # Append feature array definition at the end of the file - feature_array = f"\n// Feature names array\nconst char* {model_name}::feature_names[{model_name}::NUM_FEATURES] = {{\n" - for i, name in enumerate(feature_names): - comma = "," if i < len(feature_names) - 1 else "" - feature_array += f' "{name}"{comma}\n' - feature_array += "};\n" - - # Append to end of file - content = content.rstrip() + "\n" + feature_array - - with open(main_path, "w") as f: - f.write(content) - - print( - f" Added {len(feature_names)} feature names to header.h and main.cpp" - ) - except Exception as e: - print(f" Warning: Failed to add feature names: {e}") - - # Remove recipe.json if it exists - if os.path.exists(recipe_path): - try: - os.remove(recipe_path) - print(" Removed recipe.json") - except Exception as e: - print(f" Warning: Failed to remove recipe.json: {e}") - - opt_msg = [] - if annotation_path: - opt_msg.append("branch-annotated") - if quantize: - opt_msg.append("quantized") - opt_suffix = f" ({', '.join(opt_msg)})" if opt_msg else "" - - print(f"C source code generated to: {source_dir}/") - print( - f" Contains optimized model source code{opt_suffix} ready for compilation" - ) - except Exception as e: - print(f"Warning: TL2cgen code generation failed: {e}") - print(" Model saved in standard format only.") - - -def save_model( - model, - scaler, - regressor_type: str, - output_dir: str, - feature_names: List[str], - log_transform: bool = False, - log_transform_offset: float = 0.0, -) -> None: - """Save trained model and preprocessing components to disk.""" - os.makedirs(output_dir, exist_ok=True) - - # Save metadata - metadata = { - "regressor_type": regressor_type, - "feature_names": feature_names, - "has_scaler": scaler is not None, - "log_transform": log_transform, - "log_transform_offset": log_transform_offset, - } - - metadata_path = os.path.join(output_dir, f"{regressor_type}_metadata.pkl") - with open(metadata_path, "wb") as f: - pickle.dump(metadata, f) - print(f"\nSaved metadata to: {metadata_path}") - - # Save scaler if exists - if scaler is not None: - scaler_path = os.path.join(output_dir, f"{regressor_type}_scaler.pkl") - joblib.dump(scaler, scaler_path) - print(f"Saved scaler to: {scaler_path}") - - # Save model - if regressor_type == "xgboost": - # Save as UBJ with gzip compression - model_path = os.path.join(output_dir, f"{regressor_type}_model.ubj") - model.save_model(model_path) - - # Gzip the file - import gzip - import shutil - - with open(model_path, "rb") as f_in: - with gzip.open(model_path + ".gz", "wb") as f_out: - shutil.copyfileobj(f_in, f_out) - os.remove(model_path) # Remove uncompressed version - print(f"Saved model to: {model_path}.gz") - elif regressor_type == "lightgbm": - # Save LightGBM model as text file - model_path = os.path.join(output_dir, f"{regressor_type}_model.txt") - model.booster_.save_model(model_path) - print(f"Saved model to: {model_path}") - else: - # Save sklearn models as joblib (more efficient than pickle for large arrays) - model_path = os.path.join(output_dir, f"{regressor_type}_model.joblib") - joblib.dump(model, model_path) - print(f"Saved model to: {model_path}") - - -def main(): - parser = argparse.ArgumentParser( - description="Train regression models to predict algorithm iterations", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=""" -Available regressors: - linear - Linear Regression - poly2, poly3, poly4 - Polynomial Regression (degree 2, 3, 4) - xgboost - XGBoost Regressor - lightgbm - LightGBM Regressor - random_forest - Random Forest Regressor - gradient_boosting - Gradient Boosting Regressor - -Examples: - # Train a model (default target: iter) - python train_regressor.py data.feather --regressor xgboost --seed 42 - python train_regressor.py data.feather --regressor lightgbm --seed 42 - - # Train to predict time instead of iterations - python train_regressor.py data.feather --regressor xgboost --target time_ms --seed 42 - - # Check data quality before training - python train_regressor.py data.feather --regressor xgboost --check-data - - # Train with automatic removal of invalid rows - python train_regressor.py data.feather --regressor xgboost --drop-invalid-rows --seed 42 - - # Stratify train/test split by target column (ensures balanced distribution) - python train_regressor.py data.feather --regressor xgboost --stratify-split --seed 42 - - # Stratify split by a specific column (e.g., time_ms) - python train_regressor.py data.feather --regressor xgboost --stratify-split time_ms --seed 42 - - # Optimize for relative error (recommended for targets spanning multiple orders of magnitude) - python train_regressor.py data.feather --regressor xgboost --log-transform --seed 42 - - # Legacy pickle format - python train_regressor.py data.pkl --regressor xgboost --seed 42 - """, - ) - - parser.add_argument( - "input_pkl", help="Input data file (.feather or .pkl) with log data" - ) - parser.add_argument( - "--regressor", - "-r", - required=True, - choices=AVAILABLE_REGRESSORS, - help="Type of regressor to train", - ) - parser.add_argument( - "--output-dir", - "-o", - default="./models", - help="Output directory for saved models (default: ./models)", - ) - parser.add_argument( - "--seed", - "-s", - type=int, - default=None, - help="Random seed for reproducibility (optional)", - ) - parser.add_argument( - "--tune", - action="store_true", - help="Enable hyperparameter tuning (uses predefined tuned parameters)", - ) - parser.add_argument( - "--cv-folds", - type=int, - default=5, - help="Number of cross-validation folds (default: 5)", - ) - parser.add_argument( - "--test-size", - type=float, - default=0.2, - help="Proportion of files to use for testing (default: 0.2)", - ) - parser.add_argument( - "--no-progress", - action="store_true", - help="Disable training progress output", - ) - parser.add_argument( - "--list-features", - action="store_true", - help="List all available features in the dataset and exit", - ) - parser.add_argument( - "--stratify-split", - type=str, - nargs="?", - const="__target__", - default=None, - metavar="COLUMN", - help="Stratify train/test split by specified column distribution. If no column specified, uses target column. Example: --stratify-split time_ms or just --stratify-split for target column", - ) - parser.add_argument( - "--early-stopping", - type=int, - default=0, - metavar="N", - help="Enable early stopping for tree models (default: 20 rounds, use 0 to disable)", - ) - parser.add_argument( - "--treelite-compile", - type=int, - default=1, - metavar="THREADS", - help="Export XGBoost/LightGBM model as optimized C source code with TL2cgen (includes branch annotation and quantization)", - ) - parser.add_argument( - "--drop-invalid-rows", - action="store_true", - help="Drop rows with NaN or infinite values in target variable (instead of failing)", - ) - parser.add_argument( - "--check-data", - action="store_true", - help="Run data quality checks and exit (no training)", - ) - parser.add_argument( - "--target", - type=str, - default="iter", - help="Target column to predict (default: iter). Examples: iter, time_ms, iterations", - ) - parser.add_argument( - "--log-transform", - action="store_true", - help="Use log-transform on target variable to optimize for relative error instead of absolute error. Recommended when target values span multiple orders of magnitude.", - ) - parser.add_argument( - "--log-transform-offset", - type=float, - default=0.0, - metavar="C", - help="Add constant C to target values before log transform (useful to avoid non-positive values). Only used with --log-transform.", - ) - - args = parser.parse_args() - - # Set random seed if provided - if args.seed is not None: - np.random.seed(args.seed) - print(f"Random seed set to: {args.seed}") - - # Load data - print(f"\nLoading data from: {args.input_pkl}") - print(f"Target column: '{args.target}'") - df = load_data(args.input_pkl, target_col=args.target) - print(f"Loaded {len(df)} entries with {len(df.columns)} columns") - - # Report file format - ext = os.path.splitext(args.input_pkl)[1].lower() - if ext == ".feather": - print(" Format: Apache Arrow/Feather (fast I/O)") - elif ext == ".pkl": - print(" Format: Pickle (legacy)") - - # Extract model name from input file (for prefixing generated C functions) - model_name = os.path.splitext(os.path.basename(args.input_pkl))[0] - - # Validate data quality - print("\nValidating data quality...") - is_valid, report = validate_data_quality( - df, target_col=args.target, verbose=True - ) - - # If just checking data, exit here - if args.check_data: - if is_valid: - print("\n✅ Data quality check passed! Ready for training.") - return 0 - else: - print( - "\n❌ Data quality check failed! Fix issues before training." - ) - return 1 - - # Handle invalid data - if not is_valid: - if args.drop_invalid_rows: - print( - f"\nDropping {len(report['rows_with_issues'])} rows with invalid data..." - ) - df_clean = df.drop(index=report["rows_with_issues"]) - df = df_clean.reset_index(drop=True) - print(f" Remaining: {len(df)} entries") - - # Re-validate - is_valid_after, _ = validate_data_quality(df, verbose=False) - if not is_valid_after: - print( - "❌ Error: Data still invalid after dropping rows. Check your data." - ) - return 1 - print(" ✅ Data is now valid") - else: - print("\n❌ Training aborted due to invalid data.") - print( - " Use --drop-invalid-rows to automatically remove invalid rows, or" - ) - print( - " Use --check-data to just run validation without training" - ) - return 1 - - # If listing features, do that and exit - if args.list_features: - features = list_available_features(df, target_col=args.target) - - # Also list potential target columns - numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist() - potential_targets = [ - col for col in numeric_cols if col not in features - ] - - print(f"\n{'=' * 70}") - print(f"Available features in dataset ({len(features)} total):") - print(f"{'=' * 70}") - for i, feat in enumerate(features, 1): - print(f" {i:3d}. {feat}") - - if potential_targets: - print(f"\n{'=' * 70}") - print( - f"Potential target columns ({len(potential_targets)} total):" - ) - print(f"{'=' * 70}") - for i, col in enumerate(potential_targets, 1): - marker = " (current)" if col == args.target else "" - print(f" {i:3d}. {col}{marker}") - - print("\nTo exclude features, edit FEATURES_TO_EXCLUDE in the script:") - print(f" {__file__}") - print("\nTo use only specific features, edit FEATURES_TO_INCLUDE_ONLY") - print("\nTo change target column, use: --target ") - return - - # Split data by files - # Handle stratify-split argument - if args.stratify_split is None: - stratify_by = None - elif args.stratify_split == "__target__": - stratify_by = args.target - print(f"Stratifying split by target column: '{args.target}'") - else: - stratify_by = args.stratify_split - if stratify_by not in df.columns: - print( - f"\n❌ Error: Stratify column '{stratify_by}' not found in dataset" - ) - print(f"Available columns: {list(df.columns)}") - return 1 - print(f"Stratifying split by column: '{stratify_by}'") - - train_df, test_df = split_by_files( - df, - test_size=args.test_size, - random_state=args.seed, - stratify_by=stratify_by, - ) - - # Prepare features - X_train, y_train, feature_names = prepare_features( - train_df, target_col=args.target - ) - X_test, y_test, _ = prepare_features(test_df, target_col=args.target) - - print(f"\nFeatures: {len(feature_names)}") - print(f"Target: {args.target} (prediction target)") - - # Apply log transform if requested (for relative error optimization) - if args.log_transform: - log_transform_offset = args.log_transform_offset - y_train_original = y_train.copy() - y_test_original = y_test.copy() - - if log_transform_offset != 0.0: - print( - f" Applying log-transform offset: {log_transform_offset:+.6g}" - ) - y_train = y_train + log_transform_offset - y_test = y_test + log_transform_offset - - # Check for non-positive values before log transform - if np.any(y_train <= 0) or np.any(y_test <= 0): - n_nonpositive_train = np.sum(y_train <= 0) - n_nonpositive_test = np.sum(y_test <= 0) - print( - "\n❌ Error: Cannot apply log-transform with non-positive target values!" - ) - print(f" Train set: {n_nonpositive_train} non-positive values") - print(f" Test set: {n_nonpositive_test} non-positive values") - print( - f" Target range: [{np.min(y_train):.2f}, {np.max(y_train):.2f}]" - ) - print( - "\nSuggestion: Use --log-transform-offset to add a small constant before log" - ) - return 1 - - print( - "\nApplying log-transform to target variable (optimizes for relative error)" - ) - y_train = np.log(y_train) - y_test = np.log(y_test) - - print( - f" Original target range: [{np.min(y_train_original):.2f}, {np.max(y_train_original):.2f}]" - ) - print( - f" Log-space target range: [{np.min(y_train):.4f}, {np.max(y_train):.4f}]" - ) - else: - y_train_original = None - y_test_original = None - log_transform_offset = 0.0 - - # Enhanced diagnostics for XGBoost compatibility (only show if problems found) - X_train_array = X_train.values if hasattr(X_train, "values") else X_train - - # XGBoost internal limits (approximate) - xgb_max_safe = 1e38 # XGBoost uses float32 internally - - problematic_features = [] - problem_details = [] - - for i, col_name in enumerate(feature_names): - col_data = X_train_array[:, i] - - n_nan = np.sum(np.isnan(col_data)) - n_inf = np.sum(np.isinf(col_data)) - n_posinf = np.sum(np.isposinf(col_data)) - n_neginf = np.sum(np.isneginf(col_data)) - - # Get statistics on finite values - finite_mask = np.isfinite(col_data) - if np.any(finite_mask): - finite_data = col_data[finite_mask] - col_min = np.min(finite_data) - col_max = np.max(finite_data) - col_mean = np.mean(finite_data) - col_std = np.std(finite_data) - abs_max = max(abs(col_min), abs(col_max)) - else: - col_min = col_max = col_mean = col_std = abs_max = np.nan - - # Check if values are too large for XGBoost - is_problematic = ( - n_nan > 0 - or n_inf > 0 - or (not np.isnan(abs_max) and abs_max > xgb_max_safe) - ) - - if is_problematic: - problematic_features.append(col_name) - detail = f"\n⚠️ '{col_name}':" - if n_nan > 0: - detail += f"\n NaN: {n_nan:8d} ({100 * n_nan / len(col_data):6.2f}%)" - if n_posinf > 0: - detail += f"\n +Inf: {n_posinf:8d} ({100 * n_posinf / len(col_data):6.2f}%)" - if n_neginf > 0: - detail += f"\n -Inf: {n_neginf:8d} ({100 * n_neginf / len(col_data):6.2f}%)" - if not np.isnan(abs_max): - detail += f"\n Range: [{col_min:.6e}, {col_max:.6e}]" - detail += f"\n Max abs: {abs_max:.6e}" - if abs_max > xgb_max_safe: - detail += f"\n ❌ TOO LARGE! Exceeds XGBoost safe limit (~{xgb_max_safe:.2e})" - detail += f"\n Mean: {col_mean:.6e}" - detail += f"\n Std: {col_std:.6e}" - problem_details.append(detail) - - # Check target variable - n_nan_target = np.sum(np.isnan(y_train)) - n_inf_target = np.sum(np.isinf(y_train)) - if n_nan_target > 0 or n_inf_target > 0: - problematic_features.append(f"TARGET[{args.target}]") - detail = f"\n⚠️ Target '{args.target}':" - if n_nan_target > 0: - detail += f"\n NaN: {n_nan_target} ({100 * n_nan_target / len(y_train):.2f}%)" - if n_inf_target > 0: - detail += f"\n Inf: {n_inf_target} ({100 * n_inf_target / len(y_train):.2f}%)" - problem_details.append(detail) - - # Only print if problems found - if len(problematic_features) > 0: - print("\n" + "=" * 70) - print("⚠️ FEATURE VALUE PROBLEMS DETECTED") - print("=" * 70) - for detail in problem_details: - print(detail) - print("\n" + "=" * 70) - print(f"❌ Found {len(problematic_features)} problematic feature(s):") - for feat in problematic_features: - print(f" - {feat}") - print("\nTo fix:") - print( - " 1. Add these features to FEATURES_TO_EXCLUDE at top of script" - ) - print( - " 2. Or investigate why these features have extreme/invalid values" - ) - print("=" * 70 + "\n") - - # Create model - print(f"\nTraining {args.regressor} regressor...") - model, needs_scaling = create_regressor( - args.regressor, - random_state=args.seed, - tune_hyperparams=args.tune, - verbose=not args.no_progress, - ) - - # Apply scaling if needed - scaler = None - X_test_original = X_test.copy() # Keep unscaled version for display - if needs_scaling: - print(" Applying StandardScaler to features...") - scaler = StandardScaler() - X_train_scaled = scaler.fit_transform(X_train) - X_test_scaled = scaler.transform(X_test) - - # Sanity check: verify scaling worked correctly - print( - f" Scaled features - mean: {np.mean(X_train_scaled):.6f}, std: {np.std(X_train_scaled):.6f}" - ) - if np.any(np.isnan(X_train_scaled)) or np.any( - np.isinf(X_train_scaled) - ): - print(" WARNING: NaN or Inf detected in scaled training data!") - if np.any(np.isnan(X_test_scaled)) or np.any(np.isinf(X_test_scaled)): - print(" WARNING: NaN or Inf detected in scaled test data!") - else: - X_train_scaled = X_train - X_test_scaled = X_test - - # Train model - if args.regressor.startswith("poly"): - degree = int(args.regressor[-1]) - n_features = X_train_scaled.shape[1] - from math import comb - - n_poly_features = sum( - comb(n_features + d - 1, d) for d in range(1, degree + 1) - ) - print( - f" Generating {n_poly_features} polynomial features (degree {degree})..." - ) - - # Use early stopping for tree-based models if requested - if ( - args.early_stopping - and args.early_stopping > 0 - and args.regressor in ["xgboost", "lightgbm", "gradient_boosting"] - ): - if args.regressor == "xgboost": - print( - f" Using early stopping (patience={args.early_stopping} rounds)..." - ) - # Set early stopping parameter - model.set_params(early_stopping_rounds=args.early_stopping) - model.fit( - X_train_scaled, - y_train, - eval_set=[(X_test_scaled, y_test)], - verbose=False, - ) - # Report best iteration - best_iteration = ( - model.best_iteration - if hasattr(model, "best_iteration") - else model.n_estimators - ) - print( - f" Best iteration: {best_iteration} (out of {model.n_estimators} max)" - ) - elif args.regressor == "lightgbm": - print( - f" Using early stopping (patience={args.early_stopping} rounds)..." - ) - model.fit( - X_train_scaled, - y_train, - eval_set=[(X_test_scaled, y_test)], - callbacks=[ - __import__("lightgbm").early_stopping( - stopping_rounds=args.early_stopping, verbose=False - ) - ], - ) - # Report best iteration - best_iteration = ( - model.best_iteration_ - if hasattr(model, "best_iteration_") - else model.n_estimators - ) - print( - f" Best iteration: {best_iteration} (out of {model.n_estimators} max)" - ) - else: # gradient_boosting - # Gradient Boosting uses n_iter_no_change parameter - print( - " Note: Use --tune with gradient_boosting for early stopping" - ) - model.fit(X_train_scaled, y_train) - else: - model.fit(X_train_scaled, y_train) - - print(" Training complete!") - - # Evaluate model - skip_cv = args.early_stopping is not None and args.early_stopping > 0 - train_r2, test_r2 = evaluate_model( - model, - X_train_scaled, - y_train, - X_test_scaled, - y_test, - feature_names, - args.regressor, - cv_folds=args.cv_folds, - verbose=2 if not args.no_progress else 0, - skip_cv=skip_cv, - X_test_original=X_test_original, - test_df=test_df, - log_transform=args.log_transform, - y_train_original=y_train_original, - y_test_original=y_test_original, - log_transform_offset=log_transform_offset, - ) - - # Save model - save_model( - model, - scaler, - args.regressor, - args.output_dir, - feature_names, - log_transform=args.log_transform, - log_transform_offset=log_transform_offset, - ) - - # Compile with TL2cgen if requested (with optimizations enabled by default) - if args.treelite_compile is not None: - # Use unscaled training data for branch annotation when scaling is not applied - # Note: All models now use scaling for consistency - X_train_for_annotation = X_train if not needs_scaling else None - - compile_model_treelite( - model, - args.regressor, - args.output_dir, - args.treelite_compile, - X_train=X_train_for_annotation, - annotate=True, # Always enable branch annotation - quantize=True, # Always enable quantization - feature_names=feature_names, - model_name=model_name, - log_transform=args.log_transform, - log_transform_offset=log_transform_offset, - ) - - print("\n" + "=" * 70) - print("Training completed successfully!") - print("=" * 70) - print("\nFinal R² Scores:") - print(f" Train R²: {train_r2:.4f}") - print(f" Test R²: {test_r2:.4f}") - - -if __name__ == "__main__": - main() From e80e5453ff962f621653c5797ae231a88089d74b Mon Sep 17 00:00:00 2001 From: Alice Boucher Date: Wed, 18 Mar 2026 05:19:03 -0700 Subject: [PATCH 225/225] bump1 --- benchmarks/linear_programming/cuopt/run_mip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/linear_programming/cuopt/run_mip.cpp b/benchmarks/linear_programming/cuopt/run_mip.cpp index a8e9f7e9c5..fe237846c8 100644 --- a/benchmarks/linear_programming/cuopt/run_mip.cpp +++ b/benchmarks/linear_programming/cuopt/run_mip.cpp @@ -296,7 +296,7 @@ int run_single_file(std::string file_path, benchmark_info.objective_of_initial_population, benchmark_info.last_improvement_of_best_feasible, benchmark_info.last_improvement_after_recombination); - // 1solution.write_to_sol_file(base_filename + ".sol", handle_.get_stream()); + // solution.write_to_sol_file(base_filename + ".sol", handle_.get_stream()); std::chrono::milliseconds duration; auto end = std::chrono::high_resolution_clock::now(); duration = std::chrono::duration_cast(end - start_run_solver);