20#include "absl/container/flat_hash_set.h"
21#include "absl/status/status.h"
22#include "absl/status/statusor.h"
23#include "absl/strings/ascii.h"
24#include "absl/strings/str_cat.h"
25#include "absl/strings/str_format.h"
30#include "ortools/linear_solver/linear_solver.pb.h"
33ABSL_FLAG(
bool, lp_log_invalid_name,
false,
"DEPRECATED.");
38constexpr double kInfinity = std::numeric_limits<double>::infinity();
42 explicit LineBreaker(
int max_line_size)
43 : max_line_size_(max_line_size), line_size_(0), output_() {}
49 void Append(
const std::string& s);
53 bool WillFit(
const std::string& s) {
54 return line_size_ + s.size() < max_line_size_;
59 void Consume(
int size) { line_size_ += size; }
61 std::string GetOutput()
const {
return output_; }
69void LineBreaker::Append(
const std::string& s) {
70 line_size_ += s.size();
71 if (line_size_ > max_line_size_) {
72 line_size_ = s.size();
73 absl::StrAppend(&output_,
"\n ");
75 absl::StrAppend(&output_, s);
78class MPModelProtoExporter {
80 explicit MPModelProtoExporter(
const MPModelProto&
model);
92 void ComputeMpsSmartColumnWidths(
bool obfuscated);
110 template <
class ListOfProtosWithNameFields>
111 std::vector<std::string> ExtractAndProcessNames(
112 const ListOfProtosWithNameFields&
proto,
const std::string& prefix,
113 bool obfuscate,
bool log_invalid_names,
114 const std::string& forbidden_first_chars,
115 const std::string& forbidden_chars);
124 void AppendComments(
const std::string& separator, std::string* output)
const;
130 bool AppendConstraint(
const MPConstraintProto& ct_proto,
131 const std::string&
name,
132 const MPModelExportOptions& options,
133 LineBreaker& line_breaker,
134 std::vector<bool>& show_variable, std::string* output);
138 bool WriteLpTerm(
int var_index,
double coefficient,
139 std::string* output)
const;
143 void AppendMpsPair(
const std::string&
name,
double value,
144 std::string* output)
const;
147 void AppendMpsLineHeader(
const std::string&
id,
const std::string&
name,
148 std::string* output)
const;
152 void AppendMpsLineHeaderWithNewLine(
const std::string&
id,
153 const std::string&
name,
154 std::string* output)
const;
160 void AppendMpsTermWithContext(
const std::string& head_name,
162 std::string* output);
166 void AppendNewLineIfTwoColumns(std::string* output);
171 void AppendMpsColumns(
173 const std::vector<std::vector<std::pair<int, double>>>& transpose,
174 std::string* output);
179 void AppendMpsBound(
const std::string& bound_type,
const std::string&
name,
180 double value, std::string* output)
const;
182 const MPModelProto& proto_;
185 std::vector<std::string> exported_variable_names_;
188 std::vector<std::string> exported_constraint_names_;
191 std::vector<std::string> exported_general_constraint_names_;
194 int num_integer_variables_;
197 int num_binary_variables_;
200 int num_continuous_variables_;
203 int current_mps_column_;
206 std::unique_ptr<absl::ParsedFormat<'s', 's'>> mps_header_format_;
207 std::unique_ptr<absl::ParsedFormat<'s', 's'>> mps_format_;
216 for (
const MPGeneralConstraintProto& general_constraint :
217 model.general_constraint()) {
218 if (!general_constraint.has_indicator_constraint()) {
219 return absl::InvalidArgumentError(
220 "Non-indicator general constraints are not supported.");
223 MPModelProtoExporter exporter(
model);
225 if (!exporter.ExportModelAsLpFormat(options, &output)) {
226 return absl::InvalidArgumentError(
"Unable to export model.");
233 if (
model.general_constraint_size() > 0) {
234 return absl::InvalidArgumentError(
"General constraints are not supported.");
236 MPModelProtoExporter exporter(
model);
238 if (!exporter.ExportModelAsMpsFormat(options, &output)) {
239 return absl::InvalidArgumentError(
"Unable to export model.");
245MPModelProtoExporter::MPModelProtoExporter(
const MPModelProto&
model)
247 num_integer_variables_(0),
248 num_binary_variables_(0),
249 num_continuous_variables_(0),
250 current_mps_column_(0) {}
255 NameManager() : names_set_(), last_n_(1) {}
256 std::string MakeUniqueName(
const std::string&
name);
259 absl::flat_hash_set<std::string> names_set_;
263std::string NameManager::MakeUniqueName(
const std::string&
name) {
264 std::string result =
name;
267 while (!names_set_.insert(result).second) {
268 result = absl::StrCat(
name,
"_", n);
277std::string MakeExportableName(
const std::string&
name,
278 const std::string& forbidden_first_chars,
279 const std::string& forbidden_chars,
280 bool* found_forbidden_char) {
282 *found_forbidden_char =
283 forbidden_first_chars.find(
name[0]) != std::string::npos;
284 std::string exportable_name =
285 *found_forbidden_char ? absl::StrCat(
"_",
name) :
name;
288 for (
char& c : exportable_name) {
289 if (forbidden_chars.find(c) != std::string::npos) {
291 *found_forbidden_char =
true;
294 return exportable_name;
298template <
class ListOfProtosWithNameFields>
299std::vector<std::string> MPModelProtoExporter::ExtractAndProcessNames(
300 const ListOfProtosWithNameFields&
proto,
const std::string& prefix,
301 bool obfuscate,
bool log_invalid_names,
302 const std::string& forbidden_first_chars,
303 const std::string& forbidden_chars) {
304 const int num_items =
proto.size();
305 std::vector<std::string> result(num_items);
307 const int num_digits = absl::StrCat(num_items).size();
309 for (
const auto& item :
proto) {
310 const std::string obfuscated_name =
311 absl::StrFormat(
"%s%0*d", prefix, num_digits, i);
312 if (obfuscate || !item.has_name()) {
313 result[i] = namer.MakeUniqueName(obfuscated_name);
315 <<
"Empty name detected, created new name: " << result[i];
317 bool found_forbidden_char =
false;
318 const std::string exportable_name =
319 MakeExportableName(item.name(), forbidden_first_chars,
320 forbidden_chars, &found_forbidden_char);
321 result[i] = namer.MakeUniqueName(exportable_name);
323 <<
"Invalid character detected in " << item.name() <<
". Changed to "
328 const int kMaxNameLength = 255;
331 const int kMargin = 4;
332 if (result[i].size() > kMaxNameLength - kMargin) {
333 const std::string old_name = std::move(result[i]);
334 result[i] = namer.MakeUniqueName(obfuscated_name);
335 LOG_IF(
WARNING, log_invalid_names) <<
"Name is too long: " << old_name
336 <<
" exported as: " << result[i];
346void MPModelProtoExporter::AppendComments(
const std::string& separator,
347 std::string* output)
const {
348 const char*
const sep = separator.c_str();
349 absl::StrAppendFormat(output,
"%s Generated by MPModelProtoExporter\n", sep);
350 absl::StrAppendFormat(output,
"%s %-16s : %s\n", sep,
"Name",
351 proto_.has_name() ? proto_.name().c_str() :
"NoName");
352 absl::StrAppendFormat(output,
"%s %-16s : %s\n", sep,
"Format",
"Free");
353 absl::StrAppendFormat(
354 output,
"%s %-16s : %d\n", sep,
"Constraints",
355 proto_.constraint_size() + proto_.general_constraint_size());
356 absl::StrAppendFormat(output,
"%s %-16s : %d\n", sep,
"Variables",
357 proto_.variable_size());
358 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Binary",
359 num_binary_variables_);
360 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Integer",
361 num_integer_variables_);
362 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Continuous",
363 num_continuous_variables_);
368std::string DoubleToStringWithForcedSign(
double d) {
369 return absl::StrCat((d < 0 ?
"" :
"+"), (d));
372std::string DoubleToString(
double d) {
return absl::StrCat((d)); }
376bool MPModelProtoExporter::AppendConstraint(
const MPConstraintProto& ct_proto,
377 const std::string&
name,
378 const MPModelExportOptions& options,
379 LineBreaker& line_breaker,
380 std::vector<bool>& show_variable,
381 std::string* output) {
382 for (
int i = 0; i < ct_proto.var_index_size(); ++i) {
383 const int var_index = ct_proto.var_index(i);
384 const double coeff = ct_proto.coefficient(i);
386 if (!WriteLpTerm(var_index, coeff, &term)) {
389 line_breaker.Append(term);
390 show_variable[var_index] = coeff != 0.0 || options.show_unused_variables;
393 const double lb = ct_proto.lower_bound();
394 const double ub = ct_proto.upper_bound();
396 line_breaker.Append(absl::StrCat(
" = ", DoubleToString(ub),
"\n"));
397 absl::StrAppend(output,
" ",
name,
": ", line_breaker.GetOutput());
400 std::string rhs_name =
name;
402 absl::StrAppend(&rhs_name,
"_rhs");
404 absl::StrAppend(output,
" ", rhs_name,
": ", line_breaker.GetOutput());
405 const std::string relation =
406 absl::StrCat(
" <= ", DoubleToString(ub),
"\n");
409 if (!line_breaker.WillFit(relation)) absl::StrAppend(output,
"\n ");
410 absl::StrAppend(output, relation);
413 std::string lhs_name =
name;
415 absl::StrAppend(&lhs_name,
"_lhs");
417 absl::StrAppend(output,
" ", lhs_name,
": ", line_breaker.GetOutput());
418 const std::string relation =
419 absl::StrCat(
" >= ", DoubleToString(lb),
"\n");
420 if (!line_breaker.WillFit(relation)) absl::StrAppend(output,
"\n ");
421 absl::StrAppend(output, relation);
428bool MPModelProtoExporter::WriteLpTerm(
int var_index,
double coefficient,
429 std::string* output)
const {
431 if (var_index < 0 || var_index >= proto_.variable_size()) {
432 LOG(DFATAL) <<
"Reference to out-of-bounds variable index # " << var_index;
436 *output = absl::StrCat(DoubleToStringWithForcedSign(
coefficient),
" ",
437 exported_variable_names_[var_index],
" ");
443bool IsBoolean(
const MPVariableProto&
var) {
444 return var.is_integer() && ceil(
var.lower_bound()) == 0.0 &&
445 floor(
var.upper_bound()) == 1.0;
448void UpdateMaxSize(
const std::string& new_string,
int* size) {
449 if (new_string.size() > *size) *size = new_string.size();
452void UpdateMaxSize(
double new_number,
int* size) {
453 UpdateMaxSize(DoubleToString(new_number), size);
457void MPModelProtoExporter::Setup() {
458 if (absl::GetFlag(FLAGS_lp_log_invalid_name)) {
459 LOG(
WARNING) <<
"The \"lp_log_invalid_name\" flag is deprecated. Use "
460 "MPModelProtoExportOptions instead.";
462 num_binary_variables_ = 0;
463 num_integer_variables_ = 0;
464 for (
const MPVariableProto&
var : proto_.variable()) {
465 if (
var.is_integer()) {
466 if (IsBoolean(
var)) {
467 ++num_binary_variables_;
469 ++num_integer_variables_;
473 num_continuous_variables_ =
474 proto_.variable_size() - num_binary_variables_ - num_integer_variables_;
477void MPModelProtoExporter::ComputeMpsSmartColumnWidths(
bool obfuscated) {
480 int string_field_size = 6;
481 int number_field_size = 6;
483 for (
const MPVariableProto&
var : proto_.variable()) {
484 UpdateMaxSize(
var.name(), &string_field_size);
485 UpdateMaxSize(
var.objective_coefficient(), &number_field_size);
486 UpdateMaxSize(
var.lower_bound(), &number_field_size);
487 UpdateMaxSize(
var.upper_bound(), &number_field_size);
490 for (
const MPConstraintProto& cst : proto_.constraint()) {
491 UpdateMaxSize(cst.name(), &string_field_size);
492 UpdateMaxSize(cst.lower_bound(), &number_field_size);
493 UpdateMaxSize(cst.upper_bound(), &number_field_size);
494 for (
const double coeff : cst.coefficient()) {
495 UpdateMaxSize(coeff, &number_field_size);
501 string_field_size =
std::min(string_field_size, 255);
502 number_field_size =
std::min(number_field_size, 255);
509 std::max(proto_.variable_size(), proto_.constraint_size()) - 1)
511 string_field_size =
std::max(6, max_digits + 1);
514 mps_header_format_ = absl::ParsedFormat<'s', 's'>::New(
515 absl::StrCat(
" %-2s %-", string_field_size,
"s"));
516 mps_format_ = absl::ParsedFormat<'s', 's'>::New(
517 absl::StrCat(
" %-", string_field_size,
"s %", number_field_size,
"s"));
521 const MPModelExportOptions& options, std::string* output) {
524 const std::string kForbiddenFirstChars =
"$.0123456789";
525 const std::string kForbiddenChars =
" +-*/<>=:\\";
526 exported_constraint_names_ = ExtractAndProcessNames(
527 proto_.constraint(),
"C", options.obfuscate, options.log_invalid_names,
528 kForbiddenFirstChars, kForbiddenChars);
529 exported_general_constraint_names_ = ExtractAndProcessNames(
530 proto_.general_constraint(),
"C", options.obfuscate,
531 options.log_invalid_names, kForbiddenFirstChars, kForbiddenChars);
532 exported_variable_names_ = ExtractAndProcessNames(
533 proto_.variable(),
"V", options.obfuscate, options.log_invalid_names,
534 kForbiddenFirstChars, kForbiddenChars);
537 AppendComments(
"\\", output);
538 if (options.show_unused_variables) {
539 absl::StrAppendFormat(output,
"\\ Unused variables are shown\n");
543 absl::StrAppend(output, proto_.maximize() ?
"Maximize\n" :
"Minimize\n");
544 LineBreaker obj_line_breaker(options.max_line_length);
545 obj_line_breaker.Append(
" Obj: ");
546 if (proto_.objective_offset() != 0.0) {
547 obj_line_breaker.Append(absl::StrCat(
548 DoubleToStringWithForcedSign(proto_.objective_offset()),
" Constant "));
550 std::vector<bool> show_variable(proto_.variable_size(),
551 options.show_unused_variables);
552 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
553 const double coeff = proto_.variable(var_index).objective_coefficient();
555 if (!WriteLpTerm(var_index, coeff, &term)) {
558 obj_line_breaker.Append(term);
559 show_variable[var_index] = coeff != 0.0 || options.show_unused_variables;
562 absl::StrAppend(output, obj_line_breaker.GetOutput(),
"\nSubject to\n");
563 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
564 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
565 const std::string&
name = exported_constraint_names_[cst_index];
566 LineBreaker line_breaker(options.max_line_length);
567 const int kNumFormattingChars = 10;
570 line_breaker.Consume(kNumFormattingChars +
name.size());
571 if (!AppendConstraint(ct_proto,
name, options, line_breaker, show_variable,
578 for (
int cst_index = 0; cst_index < proto_.general_constraint_size();
580 const MPGeneralConstraintProto& ct_proto =
581 proto_.general_constraint(cst_index);
582 const std::string&
name = exported_general_constraint_names_[cst_index];
583 LineBreaker line_breaker(options.max_line_length);
584 const int kNumFormattingChars = 10;
587 line_breaker.Consume(kNumFormattingChars +
name.size());
589 if (!ct_proto.has_indicator_constraint())
return false;
590 const MPIndicatorConstraint& indicator_ct = ct_proto.indicator_constraint();
591 const int binary_var_index = indicator_ct.var_index();
592 const int binary_var_value = indicator_ct.var_value();
593 if (binary_var_index < 0 || binary_var_index >= proto_.variable_size()) {
596 line_breaker.Append(absl::StrFormat(
597 "%s = %d -> ", exported_variable_names_[binary_var_index],
599 if (!AppendConstraint(indicator_ct.constraint(),
name, options,
600 line_breaker, show_variable, output)) {
606 absl::StrAppend(output,
"Bounds\n");
607 if (proto_.objective_offset() != 0.0) {
608 absl::StrAppend(output,
" 1 <= Constant <= 1\n");
610 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
611 if (!show_variable[var_index])
continue;
612 const MPVariableProto& var_proto = proto_.variable(var_index);
613 const double lb = var_proto.lower_bound();
614 const double ub = var_proto.upper_bound();
615 if (var_proto.is_integer() && lb == round(lb) && ub == round(ub)) {
616 absl::StrAppendFormat(output,
" %.0f <= %s <= %.0f\n", lb,
617 exported_variable_names_[var_index], ub);
619 absl::StrAppend(output,
" ");
621 absl::StrAppend(output, exported_variable_names_[var_index],
" free");
624 absl::StrAppend(output, DoubleToString(lb),
" <= ");
626 absl::StrAppend(output, exported_variable_names_[var_index]);
628 absl::StrAppend(output,
" <= ", DoubleToString(ub));
631 absl::StrAppend(output,
"\n");
636 if (num_binary_variables_ > 0) {
637 absl::StrAppend(output,
"Binaries\n");
638 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
639 if (!show_variable[var_index])
continue;
640 const MPVariableProto& var_proto = proto_.variable(var_index);
641 if (IsBoolean(var_proto)) {
642 absl::StrAppendFormat(output,
" %s\n",
643 exported_variable_names_[var_index]);
649 if (num_integer_variables_ > 0) {
650 absl::StrAppend(output,
"Generals\n");
651 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
652 if (!show_variable[var_index])
continue;
653 const MPVariableProto& var_proto = proto_.variable(var_index);
654 if (var_proto.is_integer() && !IsBoolean(var_proto)) {
655 absl::StrAppend(output,
" ", exported_variable_names_[var_index],
"\n");
659 absl::StrAppend(output,
"End\n");
663void MPModelProtoExporter::AppendMpsPair(
const std::string&
name,
double value,
664 std::string* output)
const {
665 absl::StrAppendFormat(output, *mps_format_,
name, DoubleToString(
value));
668void MPModelProtoExporter::AppendMpsLineHeader(
const std::string&
id,
669 const std::string&
name,
670 std::string* output)
const {
671 absl::StrAppendFormat(output, *mps_header_format_,
id,
name);
674void MPModelProtoExporter::AppendMpsLineHeaderWithNewLine(
675 const std::string&
id,
const std::string&
name, std::string* output)
const {
676 AppendMpsLineHeader(
id,
name, output);
677 absl::StripTrailingAsciiWhitespace(output);
678 absl::StrAppend(output,
"\n");
681void MPModelProtoExporter::AppendMpsTermWithContext(
682 const std::string& head_name,
const std::string&
name,
double value,
683 std::string* output) {
684 if (current_mps_column_ == 0) {
685 AppendMpsLineHeader(
"", head_name, output);
688 AppendNewLineIfTwoColumns(output);
691void MPModelProtoExporter::AppendMpsBound(
const std::string& bound_type,
693 std::string* output)
const {
694 AppendMpsLineHeader(bound_type,
"BOUND", output);
696 absl::StripTrailingAsciiWhitespace(output);
697 absl::StrAppend(output,
"\n");
700void MPModelProtoExporter::AppendNewLineIfTwoColumns(std::string* output) {
701 ++current_mps_column_;
702 if (current_mps_column_ == 2) {
703 absl::StripTrailingAsciiWhitespace(output);
704 absl::StrAppend(output,
"\n");
705 current_mps_column_ = 0;
709void MPModelProtoExporter::AppendMpsColumns(
711 const std::vector<std::vector<std::pair<int, double>>>& transpose,
712 std::string* output) {
713 current_mps_column_ = 0;
714 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
715 const MPVariableProto& var_proto = proto_.variable(var_index);
716 if (var_proto.is_integer() != integrality)
continue;
717 const std::string& var_name = exported_variable_names_[var_index];
718 current_mps_column_ = 0;
719 if (var_proto.objective_coefficient() != 0.0) {
720 AppendMpsTermWithContext(var_name,
"COST",
721 var_proto.objective_coefficient(), output);
723 for (
const std::pair<int, double>& cst_index_and_coeff :
724 transpose[var_index]) {
725 const std::string& cst_name =
726 exported_constraint_names_[cst_index_and_coeff.first];
727 AppendMpsTermWithContext(var_name, cst_name, cst_index_and_coeff.second,
730 AppendNewLineIfTwoColumns(output);
735 const MPModelExportOptions& options, std::string* output) {
738 ComputeMpsSmartColumnWidths(options.obfuscate);
739 const std::string kForbiddenFirstChars =
"";
740 const std::string kForbiddenChars =
" ";
741 exported_constraint_names_ = ExtractAndProcessNames(
742 proto_.constraint(),
"C", options.obfuscate, options.log_invalid_names,
743 kForbiddenFirstChars, kForbiddenChars);
744 exported_variable_names_ = ExtractAndProcessNames(
745 proto_.variable(),
"V", options.obfuscate, options.log_invalid_names,
746 kForbiddenFirstChars, kForbiddenChars);
749 AppendComments(
"*", output);
753 absl::StrAppendFormat(output,
"%-14s%s\n",
"NAME", proto_.name());
755 if (proto_.maximize()) {
756 absl::StrAppendFormat(output,
"OBJSENSE\n MAX\n");
760 current_mps_column_ = 0;
761 std::string rows_section;
762 AppendMpsLineHeaderWithNewLine(
"N",
"COST", &rows_section);
763 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
764 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
765 const double lb = ct_proto.lower_bound();
766 const double ub = ct_proto.upper_bound();
767 const std::string& cst_name = exported_constraint_names_[cst_index];
769 AppendMpsLineHeaderWithNewLine(
"N", cst_name, &rows_section);
770 }
else if (lb == ub) {
771 AppendMpsLineHeaderWithNewLine(
"E", cst_name, &rows_section);
773 AppendMpsLineHeaderWithNewLine(
"L", cst_name, &rows_section);
775 AppendMpsLineHeaderWithNewLine(
"G", cst_name, &rows_section);
778 if (!rows_section.empty()) {
779 absl::StrAppend(output,
"ROWS\n", rows_section);
785 std::vector<std::vector<std::pair<int, double>>> transpose(
786 proto_.variable_size());
787 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
788 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
789 for (
int k = 0; k < ct_proto.var_index_size(); ++k) {
790 const int var_index = ct_proto.var_index(k);
791 if (var_index < 0 || var_index >= proto_.variable_size()) {
792 LOG(DFATAL) <<
"In constraint #" << cst_index <<
", var_index #" << k
793 <<
" is " << var_index <<
", which is out of bounds.";
796 const double coeff = ct_proto.coefficient(k);
798 transpose[var_index].push_back(
799 std::pair<int, double>(cst_index, coeff));
805 std::string columns_section;
806 AppendMpsColumns(
true, transpose, &columns_section);
807 if (!columns_section.empty()) {
808 constexpr const char kIntMarkerFormat[] =
" %-10s%-36s%-8s\n";
810 absl::StrFormat(kIntMarkerFormat,
"INTSTART",
"'MARKER'",
"'INTORG'") +
812 absl::StrAppendFormat(&columns_section, kIntMarkerFormat,
"INTEND",
813 "'MARKER'",
"'INTEND'");
815 AppendMpsColumns(
false, transpose, &columns_section);
816 if (!columns_section.empty()) {
817 absl::StrAppend(output,
"COLUMNS\n", columns_section);
821 current_mps_column_ = 0;
822 std::string rhs_section;
823 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
824 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
825 const double lb = ct_proto.lower_bound();
826 const double ub = ct_proto.upper_bound();
827 const std::string& cst_name = exported_constraint_names_[cst_index];
829 AppendMpsTermWithContext(
"RHS", cst_name, lb, &rhs_section);
831 AppendMpsTermWithContext(
"RHS", cst_name, ub, &rhs_section);
834 AppendNewLineIfTwoColumns(&rhs_section);
835 if (!rhs_section.empty()) {
836 absl::StrAppend(output,
"RHS\n", rhs_section);
840 current_mps_column_ = 0;
841 std::string ranges_section;
842 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
843 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
844 const double range = fabs(ct_proto.upper_bound() - ct_proto.lower_bound());
845 if (range != 0.0 && range != +
kInfinity) {
846 const std::string& cst_name = exported_constraint_names_[cst_index];
847 AppendMpsTermWithContext(
"RANGE", cst_name, range, &ranges_section);
850 AppendNewLineIfTwoColumns(&ranges_section);
851 if (!ranges_section.empty()) {
852 absl::StrAppend(output,
"RANGES\n", ranges_section);
856 current_mps_column_ = 0;
857 std::string bounds_section;
858 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
859 const MPVariableProto& var_proto = proto_.variable(var_index);
860 const double lb = var_proto.lower_bound();
861 const double ub = var_proto.upper_bound();
862 const std::string& var_name = exported_variable_names_[var_index];
865 AppendMpsLineHeader(
"FR",
"BOUND", &bounds_section);
866 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
870 if (var_proto.is_integer()) {
871 if (IsBoolean(var_proto)) {
872 AppendMpsLineHeader(
"BV",
"BOUND", &bounds_section);
873 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
876 AppendMpsBound(
"FX", var_name, lb, &bounds_section);
879 AppendMpsLineHeader(
"MI",
"BOUND", &bounds_section);
880 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
881 }
else if (lb != 0.0 || ub ==
kInfinity) {
885 AppendMpsBound(
"LI", var_name, lb, &bounds_section);
888 AppendMpsBound(
"UI", var_name, ub, &bounds_section);
894 AppendMpsBound(
"FX", var_name, lb, &bounds_section);
897 AppendMpsLineHeader(
"MI",
"BOUND", &bounds_section);
898 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
899 }
else if (lb != 0.0) {
900 AppendMpsBound(
"LO", var_name, lb, &bounds_section);
903 AppendMpsLineHeader(
"PL",
"BOUND", &bounds_section);
904 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
906 AppendMpsBound(
"UP", var_name, ub, &bounds_section);
911 if (!bounds_section.empty()) {
912 absl::StrAppend(output,
"BOUNDS\n", bounds_section);
915 absl::StrAppend(output,
"ENDATA\n");
#define LOG_IF(severity, condition)
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
ABSL_FLAG(bool, lp_log_invalid_name, false, "DEPRECATED.")
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
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...