@@ -39,7 +39,7 @@ void PDLPSolver::preprocessLp() {
3939 }
4040
4141 int num_new_cols = 0 ;
42- int nEqs = 0 ; // Count of equality-like constraints (EQ, BOUND, FREE)
42+ int nEqs = 0 ;
4343 constraint_types_.resize (nRows_orig);
4444
4545 // 1. First pass: Classify constraints and count slack variables needed
@@ -53,16 +53,15 @@ void PDLPSolver::preprocessLp() {
5353 nEqs++;
5454 } else {
5555 constraint_types_[i] = BOUND;
56- num_new_cols++; // Need one slack variable for each ranged constraint
56+ num_new_cols++;
5757 nEqs++;
5858 }
5959 } else if (has_lower) {
6060 constraint_types_[i] = GEQ;
6161 } else if (has_upper) {
6262 constraint_types_[i] = LEQ;
6363 } else {
64- constraint_types_[i] =
65- FREE; // Free rows become bounded equalities Ax=0, z=[-inf,inf]
64+ constraint_types_[i] = FREE;
6665 num_new_cols++;
6766 nEqs++;
6867 }
@@ -72,7 +71,7 @@ void PDLPSolver::preprocessLp() {
7271 HighsLp& processed_lp = lp_;
7372 processed_lp.num_col_ = nCols_orig + num_new_cols;
7473 processed_lp.num_row_ = nRows_orig;
75- original_num_col_ = nCols_orig; // store for postsolve
74+ original_num_col_ = nCols_orig;
7675 constraint_new_idx_.resize (nRows_orig);
7776
7877 processed_lp.col_cost_ .resize (processed_lp.num_col_ );
@@ -93,7 +92,16 @@ void PDLPSolver::preprocessLp() {
9392 }
9493 }
9594
96- // 4. Populate costs and bounds for original and new slack variables
95+ // 4. Create is_equality_row_ vector based on NEW ordering
96+ is_equality_row_.resize (nRows_orig, false );
97+ for (int i = 0 ; i < nRows_orig; ++i) {
98+ int new_row_idx = constraint_new_idx_[i];
99+ is_equality_row_[new_row_idx] = (constraint_types_[i] == EQ ||
100+ constraint_types_[i] == BOUND ||
101+ constraint_types_[i] == FREE);
102+ }
103+
104+ // 5. Populate costs and bounds for original and new slack variables
97105 for (int i = 0 ; i < nCols_orig; ++i) {
98106 processed_lp.col_cost_ [i] = original_lp_->col_cost_ [i];
99107 processed_lp.col_lower_ [i] = original_lp_->col_lower_ [i];
@@ -110,87 +118,100 @@ void PDLPSolver::preprocessLp() {
110118 }
111119 }
112120
113- // 5. Formulate the new constraints matrix (A') and RHS (b')
114- //
115- // Take a copy of the original LP's constraint matrix since we
116- // need it rowwise and it's const
117- HighsSparseMatrix original_matrix = original_lp_->a_matrix_ ;
118- original_matrix.ensureRowwise ();
119- // Set up the processed constraint matrix as an empty row-wise
120- // matrix that can have nCols_orig columns
121- HighsSparseMatrix& processed_matrix = processed_lp.a_matrix_ ;
122- processed_matrix.clear ();
123- processed_matrix.num_col_ = nCols_orig;
124- processed_matrix.format_ = MatrixFormat::kRowwise ;
125- HighsSparseMatrix row;
126- const double negative_one = -1.0 ;
127- const double * negative_one_p = &negative_one;
128- for (int i = 0 ; i < nRows_orig; ++i) {
129- // Get the row from the original constraint matrix
130- original_matrix.getRow (i, row);
131- // Scale the row by -1 if it's a LEQ
132- if (constraint_types_[i] == LEQ) row.scaleRows (negative_one_p);
133- // Add the row to the processed constraint matrix
134- processed_matrix.addRows (row);
135- }
136- // Convert the processed constraint matrix to column-wise orientation
137- processed_matrix.ensureColwise ();
138-
139- // Add slack variable entries (-1) for BOUND and FREE constraints
140- current_slack_col = nCols_orig;
141- // Set up a negated identity column as a column-wise matrix with
142- // nRows_orig rows and a single column containing -1 in row 0 (for
143- // now)
144- HighsSparseMatrix col;
145- assert (col.isColwise ());
146- col.num_col_ = 1 ;
147- col.num_row_ = nRows_orig;
148- col.start_ .push_back (1 );
149- col.index_ .push_back (0 );
150- col.value_ .push_back (-1 );
151- for (int iRow = 0 ; iRow < nRows_orig; ++iRow) {
152- if (constraint_types_[iRow] == BOUND || constraint_types_[iRow] == FREE) {
153- // Put the -1 in row iRow, and add the column to the matrix
154- col.index_ [0 ] = iRow;
155- processed_matrix.addCols (col);
156- current_slack_col++;
157- }
158- }
159-
160- // 6. Set the new RHS (row bounds)
121+ // 6. Set the new RHS (row bounds) in PERMUTED order
161122 for (int i = 0 ; i < nRows_orig; ++i) {
162- const int new_idx = constraint_new_idx_[i];
123+ int new_row_idx = constraint_new_idx_[i];
163124 switch (constraint_types_[i]) {
164125 case EQ:
165- processed_lp.row_lower_ [i ] = original_lp_->row_lower_ [i];
166- processed_lp.row_upper_ [i ] = original_lp_->row_upper_ [i];
126+ processed_lp.row_lower_ [new_row_idx ] = original_lp_->row_lower_ [i];
127+ processed_lp.row_upper_ [new_row_idx ] = original_lp_->row_upper_ [i];
167128 break ;
168129 case GEQ:
169- processed_lp.row_lower_ [i ] = original_lp_->row_lower_ [i];
170- processed_lp.row_upper_ [i ] = kHighsInf ;
130+ processed_lp.row_lower_ [new_row_idx ] = original_lp_->row_lower_ [i];
131+ processed_lp.row_upper_ [new_row_idx ] = kHighsInf ;
171132 break ;
172133 case LEQ:
173134 // Becomes -Ax >= -b
174- processed_lp.row_lower_ [i ] = -original_lp_->row_upper_ [i];
175- processed_lp.row_upper_ [i ] = kHighsInf ;
135+ processed_lp.row_lower_ [new_row_idx ] = -original_lp_->row_upper_ [i];
136+ processed_lp.row_upper_ [new_row_idx ] = kHighsInf ;
176137 break ;
177138 case BOUND:
178139 case FREE:
179140 // Becomes Ax - z = 0
180- processed_lp.row_lower_ [i ] = 0.0 ;
181- processed_lp.row_upper_ [i ] = 0.0 ;
141+ processed_lp.row_lower_ [new_row_idx ] = 0.0 ;
142+ processed_lp.row_upper_ [new_row_idx ] = 0.0 ;
182143 break ;
183144 }
184145 }
146+
147+ // 7. Build the constraint matrix with proper row ordering
148+ // First, get the original matrix in column-wise format
149+ HighsSparseMatrix original_matrix = original_lp_->a_matrix_ ;
150+ original_matrix.ensureColwise ();
151+
152+ // Create new matrix
153+ HighsSparseMatrix& processed_matrix = processed_lp.a_matrix_ ;
154+ processed_matrix.clear ();
155+ processed_matrix.format_ = MatrixFormat::kColwise ;
156+ processed_matrix.num_col_ = processed_lp.num_col_ ;
157+ processed_matrix.num_row_ = processed_lp.num_row_ ;
158+
159+ // Reserve space
160+ processed_matrix.start_ .resize (processed_lp.num_col_ + 1 );
161+ processed_matrix.index_ .reserve (original_matrix.numNz () + num_new_cols);
162+ processed_matrix.value_ .reserve (original_matrix.numNz () + num_new_cols);
163+
164+ // Build column by column
165+ processed_matrix.start_ [0 ] = 0 ;
166+ for (int col = 0 ; col < nCols_orig; ++col) {
167+ // For each column, add entries in the new row order
168+ std::vector<std::pair<int , double >> col_entries;
169+
170+ for (int el = original_matrix.start_ [col]; el < original_matrix.start_ [col + 1 ]; ++el) {
171+ int old_row = original_matrix.index_ [el];
172+ int new_row = constraint_new_idx_[old_row];
173+ double val = original_matrix.value_ [el];
174+
175+ // Apply scaling for LEQ constraints
176+ if (constraint_types_[old_row] == LEQ) {
177+ val = -val;
178+ }
179+
180+ col_entries.push_back ({new_row, val});
181+ }
182+
183+ // Sort by row index to maintain proper sparse matrix format
184+ std::sort (col_entries.begin (), col_entries.end ());
185+
186+ // Add to matrix
187+ for (const auto & entry : col_entries) {
188+ processed_matrix.index_ .push_back (entry.first );
189+ processed_matrix.value_ .push_back (entry.second );
190+ }
191+
192+ processed_matrix.start_ [col + 1 ] = processed_matrix.index_ .size ();
193+ }
194+
195+ // Add slack variable columns
196+ current_slack_col = nCols_orig;
197+ for (int i = 0 ; i < nRows_orig; ++i) {
198+ if (constraint_types_[i] == BOUND || constraint_types_[i] == FREE) {
199+ int row_idx = constraint_new_idx_[i];
200+ processed_matrix.index_ .push_back (row_idx);
201+ processed_matrix.value_ .push_back (-1.0 );
202+ processed_matrix.start_ [current_slack_col + 1 ] = processed_matrix.index_ .size ();
203+ current_slack_col++;
204+ }
205+ }
206+
185207 num_eq_rows_ = nEqs;
186208 scaling_.passLp (&processed_lp);
187- unscaled_processed_lp_ = processed_lp; // store for postsolve
209+ unscaled_processed_lp_ = processed_lp;
188210
189- // 7 . Compute and store norms of unscaled cost and rhs
211+ // 8 . Compute and store norms of unscaled cost and rhs
190212 unscaled_c_norm_ = linalg::vector_norm (processed_lp.col_cost_ );
191213 unscaled_rhs_norm_ = linalg::vector_norm (processed_lp.row_lower_ );
192214
193- // Already achieved by construction
194215 logger_.info (" Preprocessing complete. New dimensions: " +
195216 std::to_string (processed_lp.num_row_ ) + " rows, " +
196217 std::to_string (processed_lp.num_col_ ) + " cols." );
@@ -683,8 +704,8 @@ double PDLPSolver::ComputePrimalFeasibility(
683704 for (HighsInt i = 0 ; i < lp_.num_row_ ; ++i) {
684705 primal_residual[i] = Ax_vector[i] - lp_.row_lower_ [i];
685706
686- // For inequality constraints (Ax >= b) , project to negative part
687- if (i >= num_eq_rows_ ) {
707+ // For inequality constraints, project to negative part
708+ if (!is_equality_row_[i] ) {
688709 primal_residual[i] = std::min (0.0 , primal_residual[i]);
689710 }
690711 }
@@ -697,9 +718,7 @@ double PDLPSolver::ComputePrimalFeasibility(
697718 }
698719 }
699720
700- double primal_feasibility = linalg::vector_norm (primal_residual);
701-
702- return primal_feasibility;
721+ return linalg::vector_norm (primal_residual);
703722}
704723
705724void PDLPSolver::ComputeDualSlacks (const std::vector<double >& ATy_vector) {
0 commit comments