24 #include "absl/container/flat_hash_set.h"
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/strings/ascii.h"
28 #include "absl/strings/match.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/str_format.h"
35 #include "ortools/linear_solver/linear_solver.pb.h"
38 ABSL_FLAG(
bool, lp_log_invalid_name,
false,
"DEPRECATED.");
43 constexpr
double kInfinity = std::numeric_limits<double>::infinity();
47 explicit LineBreaker(
int max_line_size)
48 : max_line_size_(max_line_size), line_size_(0), output_() {}
54 void Append(
const std::string& s);
58 bool WillFit(
const std::string& s) {
59 return line_size_ +
static_cast<int>(s.size()) < max_line_size_;
64 void Consume(
int size) { line_size_ += size; }
66 std::string GetOutput()
const {
return output_; }
74 void LineBreaker::Append(
const std::string& s) {
75 line_size_ += s.size();
76 if (line_size_ > max_line_size_) {
77 line_size_ = s.size();
78 absl::StrAppend(&output_,
"\n ");
80 absl::StrAppend(&output_, s);
83 class MPModelProtoExporter {
85 explicit MPModelProtoExporter(
const MPModelProto&
model);
97 void ComputeMpsSmartColumnWidths(
bool obfuscated);
115 template <
class ListOfProtosWithNameFields>
116 std::vector<std::string> ExtractAndProcessNames(
117 const ListOfProtosWithNameFields&
proto,
const std::string& prefix,
118 bool obfuscate,
bool log_invalid_names,
119 const std::string& forbidden_first_chars,
120 const std::string& forbidden_chars);
129 void AppendComments(
const std::string& separator, std::string* output)
const;
135 bool AppendConstraint(
const MPConstraintProto& ct_proto,
136 const std::string&
name, LineBreaker& line_breaker,
137 std::vector<bool>& show_variable, std::string* output);
141 bool WriteLpTerm(
int var_index,
double coefficient,
142 std::string* output)
const;
146 void AppendMpsPair(
const std::string&
name,
double value,
147 std::string* output)
const;
150 void AppendMpsLineHeader(
const std::string&
id,
const std::string&
name,
151 std::string* output)
const;
155 void AppendMpsLineHeaderWithNewLine(
const std::string&
id,
156 const std::string&
name,
157 std::string* output)
const;
163 void AppendMpsTermWithContext(
const std::string& head_name,
165 std::string* output);
169 void AppendNewLineIfTwoColumns(std::string* output);
174 void AppendMpsColumns(
176 const std::vector<std::vector<std::pair<int, double>>>& transpose,
177 std::string* output);
182 void AppendMpsBound(
const std::string& bound_type,
const std::string&
name,
183 double value, std::string* output)
const;
185 const MPModelProto& proto_;
188 std::vector<std::string> exported_variable_names_;
191 std::vector<std::string> exported_constraint_names_;
194 std::vector<std::string> exported_general_constraint_names_;
197 int num_integer_variables_;
200 int num_binary_variables_;
203 int num_continuous_variables_;
206 int current_mps_column_;
209 std::unique_ptr<absl::ParsedFormat<'s', 's'>> mps_header_format_;
210 std::unique_ptr<absl::ParsedFormat<'s', 's'>> mps_format_;
219 for (
const MPGeneralConstraintProto& general_constraint :
220 model.general_constraint()) {
221 if (!general_constraint.has_indicator_constraint()) {
222 return absl::InvalidArgumentError(
223 "Non-indicator general constraints are not supported.");
226 MPModelProtoExporter exporter(
model);
228 if (!exporter.ExportModelAsLpFormat(options, &output)) {
229 return absl::InvalidArgumentError(
"Unable to export model.");
236 if (
model.general_constraint_size() > 0) {
237 return absl::InvalidArgumentError(
"General constraints are not supported.");
239 MPModelProtoExporter exporter(
model);
241 if (!exporter.ExportModelAsMpsFormat(options, &output)) {
242 return absl::InvalidArgumentError(
"Unable to export model.");
248 MPModelProtoExporter::MPModelProtoExporter(
const MPModelProto&
model)
250 num_integer_variables_(0),
251 num_binary_variables_(0),
252 num_continuous_variables_(0),
253 current_mps_column_(0) {}
258 NameManager() : names_set_(), last_n_(1) {}
259 std::string MakeUniqueName(
const std::string&
name);
262 absl::flat_hash_set<std::string> names_set_;
266 std::string NameManager::MakeUniqueName(
const std::string&
name) {
267 std::string result =
name;
270 while (!names_set_.insert(result).second) {
271 result = absl::StrCat(
name,
"_", n);
282 std::string MakeExportableName(
const std::string&
name,
283 const std::string& forbidden_first_chars,
284 const std::string& forbidden_chars,
285 bool* found_forbidden_char) {
287 *found_forbidden_char =
288 name.empty() || absl::StrContains(forbidden_first_chars,
name[0]);
289 std::string exportable_name =
290 *found_forbidden_char ? absl::StrCat(
"_",
name) :
name;
293 for (
char& c : exportable_name) {
294 if (absl::StrContains(forbidden_chars, c)) {
296 *found_forbidden_char =
true;
299 return exportable_name;
303 template <
class ListOfProtosWithNameFields>
304 std::vector<std::string> MPModelProtoExporter::ExtractAndProcessNames(
305 const ListOfProtosWithNameFields&
proto,
const std::string& prefix,
306 bool obfuscate,
bool log_invalid_names,
307 const std::string& forbidden_first_chars,
308 const std::string& forbidden_chars) {
309 const int num_items =
proto.size();
310 std::vector<std::string> result(num_items);
312 const int num_digits = absl::StrCat(num_items).size();
314 for (
const auto& item :
proto) {
315 const std::string obfuscated_name =
316 absl::StrFormat(
"%s%0*d", prefix, num_digits, i);
317 if (obfuscate || !item.has_name()) {
318 result[i] = namer.MakeUniqueName(obfuscated_name);
319 LOG_IF(WARNING, log_invalid_names && !item.has_name())
320 <<
"Empty name detected, created new name: " << result[i];
322 bool found_forbidden_char =
false;
323 const std::string exportable_name =
324 MakeExportableName(item.name(), forbidden_first_chars,
325 forbidden_chars, &found_forbidden_char);
326 result[i] = namer.MakeUniqueName(exportable_name);
327 LOG_IF(WARNING, log_invalid_names && found_forbidden_char)
328 <<
"Invalid character detected in " << item.name() <<
". Changed to "
333 const int kMaxNameLength = 255;
336 const int kMargin = 4;
337 if (result[i].size() > kMaxNameLength - kMargin) {
338 const std::string old_name = std::move(result[i]);
339 result[i] = namer.MakeUniqueName(obfuscated_name);
340 LOG_IF(WARNING, log_invalid_names) <<
"Name is too long: " << old_name
341 <<
" exported as: " << result[i];
351 void MPModelProtoExporter::AppendComments(
const std::string& separator,
352 std::string* output)
const {
353 const char*
const sep = separator.c_str();
354 absl::StrAppendFormat(output,
"%s Generated by MPModelProtoExporter\n", sep);
355 absl::StrAppendFormat(output,
"%s %-16s : %s\n", sep,
"Name",
356 proto_.has_name() ? proto_.name().c_str() :
"NoName");
357 absl::StrAppendFormat(output,
"%s %-16s : %s\n", sep,
"Format",
"Free");
358 absl::StrAppendFormat(
359 output,
"%s %-16s : %d\n", sep,
"Constraints",
360 proto_.constraint_size() + proto_.general_constraint_size());
361 absl::StrAppendFormat(output,
"%s %-16s : %d\n", sep,
"Variables",
362 proto_.variable_size());
363 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Binary",
364 num_binary_variables_);
365 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Integer",
366 num_integer_variables_);
367 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Continuous",
368 num_continuous_variables_);
373 std::string DoubleToStringWithForcedSign(
double d) {
374 return absl::StrCat((d < 0 ?
"" :
"+"), (d));
377 std::string DoubleToString(
double d) {
return absl::StrCat((d)); }
381 bool MPModelProtoExporter::AppendConstraint(
const MPConstraintProto& ct_proto,
382 const std::string&
name,
383 LineBreaker& line_breaker,
384 std::vector<bool>& show_variable,
385 std::string* output) {
386 for (
int i = 0; i < ct_proto.var_index_size(); ++i) {
387 const int var_index = ct_proto.var_index(i);
388 const double coeff = ct_proto.coefficient(i);
390 if (!WriteLpTerm(var_index, coeff, &term)) {
393 line_breaker.Append(term);
394 show_variable[var_index] = coeff != 0.0 || show_variable[var_index];
397 const double lb = ct_proto.lower_bound();
398 const double ub = ct_proto.upper_bound();
400 line_breaker.Append(absl::StrCat(
" = ", DoubleToString(ub),
"\n"));
401 absl::StrAppend(output,
" ",
name,
": ", line_breaker.GetOutput());
404 std::string rhs_name =
name;
406 absl::StrAppend(&rhs_name,
"_rhs");
408 absl::StrAppend(output,
" ", rhs_name,
": ", line_breaker.GetOutput());
409 const std::string relation =
410 absl::StrCat(
" <= ", DoubleToString(ub),
"\n");
413 if (!line_breaker.WillFit(relation)) absl::StrAppend(output,
"\n ");
414 absl::StrAppend(output, relation);
417 std::string lhs_name =
name;
419 absl::StrAppend(&lhs_name,
"_lhs");
421 absl::StrAppend(output,
" ", lhs_name,
": ", line_breaker.GetOutput());
422 const std::string relation =
423 absl::StrCat(
" >= ", DoubleToString(lb),
"\n");
424 if (!line_breaker.WillFit(relation)) absl::StrAppend(output,
"\n ");
425 absl::StrAppend(output, relation);
432 bool MPModelProtoExporter::WriteLpTerm(
int var_index,
double coefficient,
433 std::string* output)
const {
435 if (var_index < 0 || var_index >= proto_.variable_size()) {
436 LOG(DFATAL) <<
"Reference to out-of-bounds variable index # " << var_index;
440 *output = absl::StrCat(DoubleToStringWithForcedSign(
coefficient),
" ",
441 exported_variable_names_[var_index],
" ");
447 bool IsBoolean(
const MPVariableProto&
var) {
448 return var.is_integer() && ceil(
var.lower_bound()) == 0.0 &&
449 floor(
var.upper_bound()) == 1.0;
452 void UpdateMaxSize(
const std::string& new_string,
int* size) {
453 const int new_size = new_string.size();
454 if (new_size > *size) *size = new_size;
457 void UpdateMaxSize(
double new_number,
int* size) {
458 UpdateMaxSize(DoubleToString(new_number), size);
462 void MPModelProtoExporter::Setup() {
463 if (absl::GetFlag(FLAGS_lp_log_invalid_name)) {
464 LOG(WARNING) <<
"The \"lp_log_invalid_name\" flag is deprecated. Use "
465 "MPModelProtoExportOptions instead.";
467 num_binary_variables_ = 0;
468 num_integer_variables_ = 0;
469 for (
const MPVariableProto&
var : proto_.variable()) {
470 if (
var.is_integer()) {
471 if (IsBoolean(
var)) {
472 ++num_binary_variables_;
474 ++num_integer_variables_;
478 num_continuous_variables_ =
479 proto_.variable_size() - num_binary_variables_ - num_integer_variables_;
482 void MPModelProtoExporter::ComputeMpsSmartColumnWidths(
bool obfuscated) {
485 int string_field_size = 6;
486 int number_field_size = 6;
488 for (
const MPVariableProto&
var : proto_.variable()) {
489 UpdateMaxSize(
var.name(), &string_field_size);
490 UpdateMaxSize(
var.objective_coefficient(), &number_field_size);
491 UpdateMaxSize(
var.lower_bound(), &number_field_size);
492 UpdateMaxSize(
var.upper_bound(), &number_field_size);
495 for (
const MPConstraintProto& cst : proto_.constraint()) {
496 UpdateMaxSize(cst.name(), &string_field_size);
497 UpdateMaxSize(cst.lower_bound(), &number_field_size);
498 UpdateMaxSize(cst.upper_bound(), &number_field_size);
499 for (
const double coeff : cst.coefficient()) {
500 UpdateMaxSize(coeff, &number_field_size);
506 string_field_size =
std::min(string_field_size, 255);
507 number_field_size =
std::min(number_field_size, 255);
514 std::max(proto_.variable_size(), proto_.constraint_size()) - 1)
516 string_field_size =
std::max(6, max_digits + 1);
519 mps_header_format_ = absl::ParsedFormat<'s', 's'>::New(
520 absl::StrCat(
" %-2s %-", string_field_size,
"s"));
521 mps_format_ = absl::ParsedFormat<'s', 's'>::New(
522 absl::StrCat(
" %-", string_field_size,
"s %", number_field_size,
"s"));
526 const MPModelExportOptions& options, std::string* output) {
529 const std::string kForbiddenFirstChars =
"$.0123456789";
530 const std::string kForbiddenChars =
" +-*/<>=:\\";
531 exported_constraint_names_ = ExtractAndProcessNames(
532 proto_.constraint(),
"C", options.obfuscate, options.log_invalid_names,
533 kForbiddenFirstChars, kForbiddenChars);
534 exported_general_constraint_names_ = ExtractAndProcessNames(
535 proto_.general_constraint(),
"C", options.obfuscate,
536 options.log_invalid_names, kForbiddenFirstChars, kForbiddenChars);
537 exported_variable_names_ = ExtractAndProcessNames(
538 proto_.variable(),
"V", options.obfuscate, options.log_invalid_names,
539 kForbiddenFirstChars, kForbiddenChars);
542 AppendComments(
"\\", output);
543 if (options.show_unused_variables) {
544 absl::StrAppendFormat(output,
"\\ Unused variables are shown\n");
548 absl::StrAppend(output, proto_.maximize() ?
"Maximize\n" :
"Minimize\n");
549 LineBreaker obj_line_breaker(options.max_line_length);
550 obj_line_breaker.Append(
" Obj: ");
551 if (proto_.objective_offset() != 0.0) {
552 obj_line_breaker.Append(absl::StrCat(
553 DoubleToStringWithForcedSign(proto_.objective_offset()),
" Constant "));
555 std::vector<bool> show_variable(proto_.variable_size(),
556 options.show_unused_variables);
557 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
558 const double coeff = proto_.variable(var_index).objective_coefficient();
560 if (!WriteLpTerm(var_index, coeff, &term)) {
563 obj_line_breaker.Append(term);
564 show_variable[var_index] = coeff != 0.0 || show_variable[var_index];
567 absl::StrAppend(output, obj_line_breaker.GetOutput(),
"\nSubject to\n");
568 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
569 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
570 const std::string&
name = exported_constraint_names_[cst_index];
571 LineBreaker line_breaker(options.max_line_length);
572 const int kNumFormattingChars = 10;
575 line_breaker.Consume(kNumFormattingChars +
name.size());
576 if (!AppendConstraint(ct_proto,
name, line_breaker, show_variable,
583 for (
int cst_index = 0; cst_index < proto_.general_constraint_size();
585 const MPGeneralConstraintProto& ct_proto =
586 proto_.general_constraint(cst_index);
587 const std::string&
name = exported_general_constraint_names_[cst_index];
588 LineBreaker line_breaker(options.max_line_length);
589 const int kNumFormattingChars = 10;
592 line_breaker.Consume(kNumFormattingChars +
name.size());
594 if (!ct_proto.has_indicator_constraint())
return false;
595 const MPIndicatorConstraint& indicator_ct = ct_proto.indicator_constraint();
596 const int binary_var_index = indicator_ct.var_index();
597 const int binary_var_value = indicator_ct.var_value();
598 if (binary_var_index < 0 || binary_var_index >= proto_.variable_size()) {
601 line_breaker.Append(absl::StrFormat(
602 "%s = %d -> ", exported_variable_names_[binary_var_index],
604 if (!AppendConstraint(indicator_ct.constraint(),
name, line_breaker,
605 show_variable, output)) {
611 absl::StrAppend(output,
"Bounds\n");
612 if (proto_.objective_offset() != 0.0) {
613 absl::StrAppend(output,
" 1 <= Constant <= 1\n");
615 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
616 if (!show_variable[var_index])
continue;
617 const MPVariableProto& var_proto = proto_.variable(var_index);
618 const double lb = var_proto.lower_bound();
619 const double ub = var_proto.upper_bound();
620 if (var_proto.is_integer() && lb == round(lb) && ub == round(ub)) {
621 absl::StrAppendFormat(output,
" %.0f <= %s <= %.0f\n", lb,
622 exported_variable_names_[var_index], ub);
624 absl::StrAppend(output,
" ");
626 absl::StrAppend(output, exported_variable_names_[var_index],
" free");
629 absl::StrAppend(output, DoubleToString(lb),
" <= ");
631 absl::StrAppend(output, exported_variable_names_[var_index]);
633 absl::StrAppend(output,
" <= ", DoubleToString(ub));
636 absl::StrAppend(output,
"\n");
641 if (num_binary_variables_ > 0) {
642 absl::StrAppend(output,
"Binaries\n");
643 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
644 if (!show_variable[var_index])
continue;
645 const MPVariableProto& var_proto = proto_.variable(var_index);
646 if (IsBoolean(var_proto)) {
647 absl::StrAppendFormat(output,
" %s\n",
648 exported_variable_names_[var_index]);
654 if (num_integer_variables_ > 0) {
655 absl::StrAppend(output,
"Generals\n");
656 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
657 if (!show_variable[var_index])
continue;
658 const MPVariableProto& var_proto = proto_.variable(var_index);
659 if (var_proto.is_integer() && !IsBoolean(var_proto)) {
660 absl::StrAppend(output,
" ", exported_variable_names_[var_index],
"\n");
664 absl::StrAppend(output,
"End\n");
668 void MPModelProtoExporter::AppendMpsPair(
const std::string&
name,
double value,
669 std::string* output)
const {
670 absl::StrAppendFormat(output, *mps_format_,
name, DoubleToString(
value));
673 void MPModelProtoExporter::AppendMpsLineHeader(
const std::string&
id,
674 const std::string&
name,
675 std::string* output)
const {
676 absl::StrAppendFormat(output, *mps_header_format_,
id,
name);
679 void MPModelProtoExporter::AppendMpsLineHeaderWithNewLine(
680 const std::string&
id,
const std::string&
name, std::string* output)
const {
681 AppendMpsLineHeader(
id,
name, output);
682 absl::StripTrailingAsciiWhitespace(output);
683 absl::StrAppend(output,
"\n");
686 void MPModelProtoExporter::AppendMpsTermWithContext(
687 const std::string& head_name,
const std::string&
name,
double value,
688 std::string* output) {
689 if (current_mps_column_ == 0) {
690 AppendMpsLineHeader(
"", head_name, output);
693 AppendNewLineIfTwoColumns(output);
696 void MPModelProtoExporter::AppendMpsBound(
const std::string& bound_type,
698 std::string* output)
const {
699 AppendMpsLineHeader(bound_type,
"BOUND", output);
701 absl::StripTrailingAsciiWhitespace(output);
702 absl::StrAppend(output,
"\n");
705 void MPModelProtoExporter::AppendNewLineIfTwoColumns(std::string* output) {
706 ++current_mps_column_;
707 if (current_mps_column_ == 2) {
708 absl::StripTrailingAsciiWhitespace(output);
709 absl::StrAppend(output,
"\n");
710 current_mps_column_ = 0;
714 void MPModelProtoExporter::AppendMpsColumns(
716 const std::vector<std::vector<std::pair<int, double>>>& transpose,
717 std::string* output) {
718 current_mps_column_ = 0;
719 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
720 const MPVariableProto& var_proto = proto_.variable(var_index);
721 if (var_proto.is_integer() != integrality)
continue;
722 const std::string& var_name = exported_variable_names_[var_index];
723 current_mps_column_ = 0;
724 if (var_proto.objective_coefficient() != 0.0) {
725 AppendMpsTermWithContext(var_name,
"COST",
726 var_proto.objective_coefficient(), output);
728 for (
const std::pair<int, double>& cst_index_and_coeff :
729 transpose[var_index]) {
730 const std::string& cst_name =
731 exported_constraint_names_[cst_index_and_coeff.first];
732 AppendMpsTermWithContext(var_name, cst_name, cst_index_and_coeff.second,
735 AppendNewLineIfTwoColumns(output);
740 const MPModelExportOptions& options, std::string* output) {
743 ComputeMpsSmartColumnWidths(options.obfuscate);
744 const std::string kForbiddenFirstChars =
"";
745 const std::string kForbiddenChars =
" ";
746 exported_constraint_names_ = ExtractAndProcessNames(
747 proto_.constraint(),
"C", options.obfuscate, options.log_invalid_names,
748 kForbiddenFirstChars, kForbiddenChars);
749 exported_variable_names_ = ExtractAndProcessNames(
750 proto_.variable(),
"V", options.obfuscate, options.log_invalid_names,
751 kForbiddenFirstChars, kForbiddenChars);
754 AppendComments(
"*", output);
758 absl::StrAppendFormat(output,
"%-14s%s\n",
"NAME", proto_.name());
760 if (proto_.maximize()) {
761 absl::StrAppendFormat(output,
"OBJSENSE\n MAX\n");
765 current_mps_column_ = 0;
766 std::string rows_section;
767 AppendMpsLineHeaderWithNewLine(
"N",
"COST", &rows_section);
768 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
769 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
770 const double lb = ct_proto.lower_bound();
771 const double ub = ct_proto.upper_bound();
772 const std::string& cst_name = exported_constraint_names_[cst_index];
774 AppendMpsLineHeaderWithNewLine(
"N", cst_name, &rows_section);
775 }
else if (lb == ub) {
776 AppendMpsLineHeaderWithNewLine(
"E", cst_name, &rows_section);
778 AppendMpsLineHeaderWithNewLine(
"L", cst_name, &rows_section);
780 AppendMpsLineHeaderWithNewLine(
"G", cst_name, &rows_section);
783 if (!rows_section.empty()) {
784 absl::StrAppend(output,
"ROWS\n", rows_section);
790 std::vector<std::vector<std::pair<int, double>>> transpose(
791 proto_.variable_size());
792 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
793 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
794 for (
int k = 0; k < ct_proto.var_index_size(); ++k) {
795 const int var_index = ct_proto.var_index(k);
796 if (var_index < 0 || var_index >= proto_.variable_size()) {
797 LOG(DFATAL) <<
"In constraint #" << cst_index <<
", var_index #" << k
798 <<
" is " << var_index <<
", which is out of bounds.";
801 const double coeff = ct_proto.coefficient(k);
803 transpose[var_index].push_back(
804 std::pair<int, double>(cst_index, coeff));
810 std::string columns_section;
811 AppendMpsColumns(
true, transpose, &columns_section);
812 if (!columns_section.empty()) {
813 constexpr
const char kIntMarkerFormat[] =
" %-10s%-36s%-8s\n";
815 absl::StrFormat(kIntMarkerFormat,
"INTSTART",
"'MARKER'",
"'INTORG'") +
817 absl::StrAppendFormat(&columns_section, kIntMarkerFormat,
"INTEND",
818 "'MARKER'",
"'INTEND'");
820 AppendMpsColumns(
false, transpose, &columns_section);
821 if (!columns_section.empty()) {
822 absl::StrAppend(output,
"COLUMNS\n", columns_section);
826 current_mps_column_ = 0;
827 std::string rhs_section;
830 if (proto_.objective_offset() != 0) {
831 AppendMpsTermWithContext(
"RHS",
"COST", -proto_.objective_offset(),
834 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
835 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
836 const double lb = ct_proto.lower_bound();
837 const double ub = ct_proto.upper_bound();
838 const std::string& cst_name = exported_constraint_names_[cst_index];
840 AppendMpsTermWithContext(
"RHS", cst_name, lb, &rhs_section);
842 AppendMpsTermWithContext(
"RHS", cst_name, ub, &rhs_section);
845 AppendNewLineIfTwoColumns(&rhs_section);
846 if (!rhs_section.empty()) {
847 absl::StrAppend(output,
"RHS\n", rhs_section);
851 current_mps_column_ = 0;
852 std::string ranges_section;
853 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
854 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
855 const double range = fabs(ct_proto.upper_bound() - ct_proto.lower_bound());
857 const std::string& cst_name = exported_constraint_names_[cst_index];
858 AppendMpsTermWithContext(
"RANGE", cst_name,
range, &ranges_section);
861 AppendNewLineIfTwoColumns(&ranges_section);
862 if (!ranges_section.empty()) {
863 absl::StrAppend(output,
"RANGES\n", ranges_section);
867 current_mps_column_ = 0;
868 std::string bounds_section;
869 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
870 const MPVariableProto& var_proto = proto_.variable(var_index);
871 const double lb = var_proto.lower_bound();
872 const double ub = var_proto.upper_bound();
873 const std::string& var_name = exported_variable_names_[var_index];
876 AppendMpsLineHeader(
"FR",
"BOUND", &bounds_section);
877 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
881 if (var_proto.is_integer()) {
882 if (IsBoolean(var_proto)) {
883 AppendMpsLineHeader(
"BV",
"BOUND", &bounds_section);
884 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
887 AppendMpsBound(
"FX", var_name, lb, &bounds_section);
890 AppendMpsLineHeader(
"MI",
"BOUND", &bounds_section);
891 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
892 }
else if (lb != 0.0 || ub ==
kInfinity) {
896 AppendMpsBound(
"LI", var_name, lb, &bounds_section);
899 AppendMpsBound(
"UI", var_name, ub, &bounds_section);
905 AppendMpsBound(
"FX", var_name, lb, &bounds_section);
908 AppendMpsLineHeader(
"MI",
"BOUND", &bounds_section);
909 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
910 }
else if (lb != 0.0) {
911 AppendMpsBound(
"LO", var_name, lb, &bounds_section);
914 AppendMpsLineHeader(
"PL",
"BOUND", &bounds_section);
915 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
917 AppendMpsBound(
"UP", var_name, ub, &bounds_section);
922 if (!bounds_section.empty()) {
923 absl::StrAppend(output,
"BOUNDS\n", bounds_section);
926 absl::StrAppend(output,
"ENDATA\n");
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
ABSL_FLAG(bool, lp_log_invalid_name, false, "DEPRECATED.")
Collection of objects used to extend the Constraint Solver library.
absl::StatusOr< std::string > ExportModelAsMpsFormat(const MPModelProto &model, const MPModelExportOptions &options)
Outputs the current model (variables, constraints, objective) as a string encoded in MPS file format,...
absl::StatusOr< std::string > ExportModelAsLpFormat(const MPModelProto &model, const MPModelExportOptions &options)
Outputs the current model (variables, constraints, objective) as a string encoded in the so-called "C...
constexpr double kInfinity
const std::optional< Range > & range