2222FilereaderRetcode FilereaderLp::readModelFromFile (const HighsOptions& options,
2323 const std::string filename,
2424 HighsModel& model) {
25+ bool warning_issued = false ;
2526 HighsLp& lp = model.lp_ ;
2627 HighsHessian& hessian = model.hessian_ ;
2728 try {
@@ -168,26 +169,126 @@ FilereaderRetcode FilereaderLp::readModelFromFile(const HighsOptions& options,
168169 " with same prefix: row names cleared\n " );
169170 }
170171
171- HighsInt nz = 0 ;
172+ HighsInt num_nz = 0 ;
172173 // lp.a_matrix_ is initialised with start_[0] for fictitious
173174 // column 0, so have to clear this before pushing back start
174175 lp.a_matrix_ .start_ .clear ();
175176 assert ((int )lp.a_matrix_ .start_ .size () == 0 );
176177 for (const auto & var : m.variables ) {
177- lp.a_matrix_ .start_ .push_back (nz );
178+ lp.a_matrix_ .start_ .push_back (num_nz );
178179 for (size_t j = 0 ; j < consofvarmap_index[var].size (); j++) {
179180 double value = consofvarmap_value[var][j];
180181 if (value) {
181182 lp.a_matrix_ .index_ .push_back (consofvarmap_index[var][j]);
182183 lp.a_matrix_ .value_ .push_back (value);
183- nz ++;
184+ num_nz ++;
184185 }
185186 }
186187 }
187- lp.a_matrix_ .start_ .push_back (nz );
188+ lp.a_matrix_ .start_ .push_back (num_nz );
188189 lp.a_matrix_ .format_ = MatrixFormat::kColwise ;
189190 lp.sense_ = m.sense == ObjectiveSense::MIN ? ObjSense::kMinimize
190191 : ObjSense::kMaximize ;
192+ // In a .lp file, more than one term involving the same variable
193+ // can appear in a constraint, resulting in repeated row indices
194+ // in a column
195+ HighsSparseMatrix& matrix = lp.a_matrix_ ;
196+ assert (matrix.isColwise ());
197+ num_nz = 0 ;
198+ std::vector<double > column (lp.num_row_ , 0 );
199+ std::vector<HighsInt> nz_count (lp.num_row_ , 0 );
200+ std::vector<HighsInt> zero_count (lp.num_row_ , 0 );
201+ HighsInt sum_num_duplicate = 0 ;
202+ HighsInt sum_num_zero = 0 ;
203+ HighsInt sum_cancellation = 0 ;
204+ HighsInt num_report = 0 ;
205+ HighsInt max_num_report = 10 ;
206+ for (HighsInt iCol = 0 ; iCol < lp.num_col_ ; iCol++) {
207+ // Save the start, since this will be reduced if there are
208+ // repeated row indices in a column
209+ const HighsInt from_el = matrix.start_ [iCol];
210+ for (HighsInt iEl = from_el; iEl < matrix.start_ [iCol + 1 ]; iEl++) {
211+ // Add in the value to zero or any previous nonzero in this
212+ // row
213+ HighsInt iRow = matrix.index_ [iEl];
214+ double value = matrix.value_ [iEl];
215+ if (value) {
216+ column[iRow] += value;
217+ nz_count[iRow]++;
218+ } else {
219+ zero_count[iRow]++;
220+ }
221+ }
222+ // Pass through the column again, storing and then zeroing the
223+ // entries in column - both to eliminate the duplicate and
224+ // ensure that column is zeroed for the next matrix column.
225+ matrix.start_ [iCol] = num_nz;
226+ for (HighsInt iEl = from_el; iEl < matrix.start_ [iCol + 1 ]; iEl++) {
227+ HighsInt iRow = matrix.index_ [iEl];
228+ if (column[iRow]) {
229+ assert (num_nz <= iEl);
230+ matrix.index_ [num_nz] = iRow;
231+ matrix.value_ [num_nz] = column[iRow];
232+ num_nz++;
233+ }
234+ // Report explicit zeros and/or sum to a nonzero value
235+ HighsInt num_ocurrence = zero_count[iRow] + nz_count[iRow];
236+ if (num_ocurrence > 1 ) {
237+ if (nz_count[iRow] > 1 ) {
238+ if (num_report < max_num_report)
239+ highsLogUser (options.log_options , HighsLogType::kWarning ,
240+ " Column %d (name \" %s\" ) occurs %d times in row %d "
241+ " (name \" %s\" ): values summed to %g\n " ,
242+ int (iCol), lp.col_names_ [iCol].c_str (),
243+ int (num_ocurrence), int (iRow),
244+ lp.row_names_ [iRow].c_str (), column[iRow]);
245+ num_report++;
246+ }
247+ if (zero_count[iRow] > 0 ) {
248+ if (num_report < max_num_report)
249+ highsLogUser (options.log_options , HighsLogType::kWarning ,
250+ " Column %d (name \" %s\" ) contains %d explicit zero "
251+ " coefficient%s in row %d (name \" %s\" )\n " ,
252+ int (iCol), lp.col_names_ [iCol].c_str (),
253+ int (zero_count[iRow]),
254+ zero_count[iRow] > 1 ? " s" : " " , int (iRow),
255+ lp.row_names_ [iRow].c_str ());
256+ num_report++;
257+ }
258+ sum_num_duplicate += (num_ocurrence - 1 );
259+ sum_num_zero += zero_count[iRow];
260+ if (column[iRow] == 0 && nz_count[iRow] > 0 ) sum_cancellation++;
261+ }
262+ zero_count[iRow] = 0 ;
263+ nz_count[iRow] = 0 ;
264+ column[iRow] = 0 ;
265+ }
266+ for (HighsInt iRow = 0 ; iRow < lp.num_row_ ; iRow++) {
267+ assert (zero_count[iRow] == 0 );
268+ assert (nz_count[iRow] == 0 );
269+ assert (column[iRow] == 0 );
270+ }
271+ }
272+ matrix.start_ [lp.num_col_ ] = num_nz;
273+ warning_issued = sum_num_duplicate > 0 || sum_num_zero > 0 ;
274+ HighsInt num_report_skipped = num_report - max_num_report;
275+ if (num_report_skipped > 0 )
276+ highsLogUser (options.log_options , HighsLogType::kInfo ,
277+ " Skipped %d further warning%s of this kind\n " ,
278+ int (num_report_skipped), num_report_skipped > 1 ? " s" : " " );
279+
280+ if (sum_num_duplicate > 0 )
281+ highsLogUser (options.log_options , HighsLogType::kWarning ,
282+ " lp file contains %d repeated variable%s in constraints: "
283+ " summing them yielded %d cancellation%s\n " ,
284+ int (sum_num_duplicate), sum_num_duplicate > 1 ? " s" : " " ,
285+ int (sum_cancellation),
286+ (sum_cancellation == 0 || sum_cancellation > 1 ) ? " s" : " " );
287+ if (sum_num_zero > 0 )
288+ highsLogUser (options.log_options , HighsLogType::kWarning ,
289+ " lp file contains %d explicit zero%s\n " , int (sum_num_zero),
290+ sum_num_zero > 1 ? " s" : " " );
291+
191292 } catch (std::invalid_argument& ex) {
192293 // lpassert in extern/filereaderlp/def.hpp throws
193294 // std::invalid_argument whatever the error. Hence, unless
@@ -202,7 +303,7 @@ FilereaderRetcode FilereaderLp::readModelFromFile(const HighsOptions& options,
202303 return FilereaderRetcode::kParserError ;
203304 }
204305 lp.ensureColwise ();
205- return FilereaderRetcode::kOk ;
306+ return warning_issued ? FilereaderRetcode:: kWarning : FilereaderRetcode::kOk ;
206307}
207308
208309void FilereaderLp::writeToFile (FILE* file, const char * format, ...) {
0 commit comments