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