@@ -220,15 +220,46 @@ f_t trial_branching(const lp_problem_t<i_t, f_t>& original_lp,
220220
221221template <typename i_t , typename f_t >
222222static cuopt::mps_parser::mps_data_model_t <i_t , f_t > simplex_problem_to_mps_data_model (
223- const dual_simplex::user_problem_t <i_t , f_t >& user_problem)
223+ const dual_simplex::lp_problem_t <i_t , f_t >& lp,
224+ const std::vector<i_t >& new_slacks,
225+ const std::vector<f_t >& root_soln,
226+ std::vector<f_t >& original_root_soln_x)
224227{
228+
229+ // Branch and bound has a problem of the form:
230+ // minimize c^T x
231+ // subject to A*x + Es = b
232+ // l <= x <= u
233+ // E_{jj} = sigma_j, where sigma_j is +1 or -1
234+
235+ // We need to convert this into a problem that is better for PDLP
236+ // to solve. PDLP perfers inequality constraints. Thus, we want
237+ // to convert the above into the problem:
238+ // minimize c^T x
239+ // subject to lb <= A*x <= ub
240+ // l <= x <= u
241+
242+
225243 cuopt::mps_parser::mps_data_model_t <i_t , f_t > mps_model;
226- int m = user_problem.num_rows ;
227- int n = user_problem.num_cols ;
244+ int m = lp.num_rows ;
245+ int n = lp.num_cols - new_slacks.size ();
246+ original_root_soln_x.resize (n);
247+
248+ // Remove slacks from A
249+ dual_simplex::csc_matrix_t <i_t , f_t > A_no_slacks = lp.A ;
250+ std::vector<i_t > cols_to_remove (lp.A .n , 0 );
251+ for (i_t j : new_slacks) {
252+ cols_to_remove[j] = 1 ;
253+ }
254+ A_no_slacks.remove_columns (cols_to_remove);
255+
256+ for (i_t j = 0 ; j < n; j++) {
257+ original_root_soln_x[j] = root_soln[j];
258+ }
228259
229260 // Convert CSC to CSR using built-in method
230261 dual_simplex::csr_matrix_t <i_t , f_t > csr_A (m, n, 0 );
231- user_problem. A .to_compressed_row (csr_A);
262+ A_no_slacks .to_compressed_row (csr_A);
232263
233264 int nz = csr_A.row_start [m];
234265
@@ -237,70 +268,74 @@ static cuopt::mps_parser::mps_data_model_t<i_t, f_t> simplex_problem_to_mps_data
237268 csr_A.x .data (), nz, csr_A.j .data (), nz, csr_A.row_start .data (), m + 1 );
238269
239270 // Set objective coefficients
240- mps_model.set_objective_coefficients (user_problem .objective .data (), n);
271+ mps_model.set_objective_coefficients (lp .objective .data (), n);
241272
242273 // Set objective scaling and offset
243- mps_model.set_objective_scaling_factor (user_problem .obj_scale );
244- mps_model.set_objective_offset (user_problem .obj_constant );
274+ mps_model.set_objective_scaling_factor (lp .obj_scale );
275+ mps_model.set_objective_offset (lp .obj_constant );
245276
246277 // Set variable bounds
247- mps_model.set_variable_lower_bounds (user_problem .lower .data (), n);
248- mps_model.set_variable_upper_bounds (user_problem .upper .data (), n);
278+ mps_model.set_variable_lower_bounds (lp .lower .data (), n);
279+ mps_model.set_variable_upper_bounds (lp .upper .data (), n);
249280
250281 // Convert row sense and RHS to constraint bounds
251282 std::vector<f_t > constraint_lower (m);
252283 std::vector<f_t > constraint_upper (m);
253284
254- for (i_t i = 0 ; i < m; ++i) {
255- if (user_problem.row_sense [i] == ' L' ) {
256- constraint_lower[i] = -std::numeric_limits<f_t >::infinity ();
257- constraint_upper[i] = user_problem.rhs [i];
258- } else if (user_problem.row_sense [i] == ' G' ) {
259- constraint_lower[i] = user_problem.rhs [i];
260- constraint_upper[i] = std::numeric_limits<f_t >::infinity ();
261- } else {
262- constraint_lower[i] = user_problem.rhs [i];
263- constraint_upper[i] = user_problem.rhs [i];
264- }
285+ std::vector<i_t > slack_map (m, -1 );
286+ for (i_t j : new_slacks) {
287+ const i_t col_start = lp.A .col_start [j];
288+ const i_t i = lp.A .i [col_start];
289+ slack_map[i] = j;
265290 }
266291
267- for (i_t k = 0 ; k < user_problem.num_range_rows ; ++k) {
268- i_t i = user_problem.range_rows [k];
269- f_t r = user_problem.range_value [k];
270- f_t b = user_problem.rhs [i];
271- f_t h = -std::numeric_limits<f_t >::infinity ();
272- f_t u = std::numeric_limits<f_t >::infinity ();
273- if (user_problem.row_sense [i] == ' L' ) {
274- h = b - std::abs (r);
275- u = b;
276- } else if (user_problem.row_sense [i] == ' G' ) {
277- h = b;
278- u = b + std::abs (r);
279- } else if (user_problem.row_sense [i] == ' E' ) {
280- if (r > 0 ) {
281- h = b;
282- u = b + std::abs (r);
283- } else {
284- h = b - std::abs (r);
285- u = b;
286- }
292+ for (i_t i = 0 ; i < m; ++i) {
293+ // Each row is of the form a_i^T x + sigma * s_i = b_i
294+ // with sigma = +1 or -1
295+ // and l_i <= s_i <= u_i
296+ // We have that a_i^T x - b_i = -sigma * s_i
297+ // If sigma = -1, then we have
298+ // a_i^T x - b_i = s_i
299+ // l_i <= a_i^T x - b_i <= u_i
300+ // l_i + b_i <= a_i^T x <= u_i + b_i
301+ //
302+ // If sigma = +1, then we have
303+ // a_i^T x - b_i = -s_i
304+ // -a_i^T x + b_i = s_i
305+ // l_i <= -a_i^T x + b_i <= u_i
306+ // l_i - b_i <= -a_i^T x <= u_i - b_i
307+ // -u_i + b_i <= a_i^T x <= -l_i + b_i
308+
309+ const i_t slack = slack_map[i];
310+ assert (slack != -1 );
311+ const i_t col_start = lp.A .col_start [slack];
312+ const f_t sigma = lp.A .x [col_start];
313+ const f_t slack_lower = lp.lower [slack];
314+ const f_t slack_upper = lp.upper [slack];
315+
316+ if (sigma == -1 ) {
317+ constraint_lower[i] = slack_lower + lp.rhs [i];
318+ constraint_upper[i] = slack_upper + lp.rhs [i];
319+ } else if (sigma == 1 ) {
320+ constraint_lower[i] = -slack_upper + lp.rhs [i];
321+ constraint_upper[i] = -slack_lower + lp.rhs [i];
322+ } else {
323+ assert (sigma == 1.0 || sigma == -1.0 );
287324 }
288- constraint_lower[i] = h;
289- constraint_upper[i] = u;
290325 }
291326
292327 mps_model.set_constraint_lower_bounds (constraint_lower.data (), m);
293328 mps_model.set_constraint_upper_bounds (constraint_upper.data (), m);
294- mps_model.set_maximize (user_problem .obj_scale < 0 );
329+ mps_model.set_maximize (lp .obj_scale < 0 );
295330
296331 return mps_model;
297332}
298333
299334template <typename i_t , typename f_t >
300- void strong_branching (const user_problem_t <i_t , f_t >& original_problem,
301- const lp_problem_t <i_t , f_t >& original_lp,
335+ void strong_branching (const lp_problem_t <i_t , f_t >& original_lp,
302336 const simplex_solver_settings_t <i_t , f_t >& settings,
303337 f_t start_time,
338+ const std::vector<i_t >& new_slacks,
304339 const std::vector<variable_type_t >& var_types,
305340 const std::vector<f_t > root_soln,
306341 const std::vector<i_t >& fractional,
@@ -321,14 +356,10 @@ void strong_branching(const user_problem_t<i_t, f_t>& original_problem,
321356 settings.log .printf (" Batch PDLP strong branching enabled\n " );
322357
323358 f_t start_batch = tic ();
359+ std::vector<f_t > original_root_soln_x;
324360
325- // Use original_problem to create the BatchLP problem
326- csr_matrix_t <i_t , f_t > A_row (original_problem.A .m , original_problem.A .n , 0 );
327- original_problem.A .to_compressed_row (A_row);
361+ const auto mps_model = simplex_problem_to_mps_data_model (original_lp, new_slacks, root_soln, original_root_soln_x);
328362
329- // Convert the root_soln to the original problem space
330- std::vector<f_t > original_root_soln_x;
331- uncrush_primal_solution (original_problem, original_lp, root_soln, original_root_soln_x);
332363
333364 std::vector<f_t > fraction_values;
334365
@@ -337,7 +368,6 @@ void strong_branching(const user_problem_t<i_t, f_t>& original_problem,
337368 fraction_values.push_back (original_root_soln_x[j]);
338369 }
339370
340- const auto mps_model = simplex_problem_to_mps_data_model (original_problem);
341371 const f_t batch_elapsed_time = toc (start_time);
342372 const f_t batch_remaining_time =
343373 std::max (static_cast <f_t >(0.0 ), settings.time_limit - batch_elapsed_time);
@@ -776,10 +806,10 @@ void pseudo_costs_t<i_t, f_t>::update_pseudo_costs_from_strong_branching(
776806
777807template class pseudo_costs_t <int , double >;
778808
779- template void strong_branching<int , double >(const user_problem_t <int , double >& original_problem,
780- const lp_problem_t <int , double >& original_lp,
809+ template void strong_branching<int , double >(const lp_problem_t <int , double >& original_lp,
781810 const simplex_solver_settings_t <int , double >& settings,
782811 double start_time,
812+ const std::vector<int >& new_slacks,
783813 const std::vector<variable_type_t >& var_types,
784814 const std::vector<double > root_soln,
785815 const std::vector<int >& fractional,
0 commit comments