24#include "absl/container/flat_hash_set.h"
25#include "absl/flags/flag.h"
26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/ascii.h"
29#include "absl/strings/match.h"
30#include "absl/strings/str_cat.h"
31#include "absl/strings/str_format.h"
32#include "absl/strings/string_view.h"
33#include "absl/types/span.h"
38#include "ortools/linear_solver/linear_solver.pb.h"
45constexpr double kInfinity = std::numeric_limits<double>::infinity();
49 explicit LineBreaker(
int max_line_size)
50 : max_line_size_(max_line_size), line_size_(0), output_() {}
56 void Append(absl::string_view s);
60 bool WillFit(
const std::string& s) {
61 return line_size_ +
static_cast<int>(s.size()) < max_line_size_;
66 void Consume(
int size) { line_size_ += size; }
68 std::string GetOutput()
const {
return output_; }
76void LineBreaker::Append(absl::string_view s) {
77 line_size_ += s.size();
78 if (line_size_ > max_line_size_) {
79 line_size_ = s.size();
80 absl::StrAppend(&output_,
"\n ");
82 absl::StrAppend(&output_, s);
85class MPModelProtoExporter {
87 explicit MPModelProtoExporter(
const MPModelProto& model);
90 MPModelProtoExporter(
const MPModelProtoExporter&) =
delete;
91 MPModelProtoExporter& operator=(
const MPModelProtoExporter&) =
delete;
104 void ComputeMpsSmartColumnWidths(
bool obfuscated);
122 template <
class ListOfProtosWithNameFields>
123 std::vector<std::string> ExtractAndProcessNames(
124 const ListOfProtosWithNameFields& proto, absl::string_view prefix,
125 bool obfuscate,
bool log_invalid_names,
126 const std::string& forbidden_first_chars,
127 const std::string& forbidden_chars);
136 void AppendComments(
const std::string& separator, std::string* output)
const;
142 bool AppendConstraint(
const MPConstraintProto& ct_proto,
143 absl::string_view name, LineBreaker& line_breaker,
144 std::vector<bool>& show_variable, std::string* output);
148 bool WriteLpTerm(
int var_index,
double coefficient,
149 std::string* output)
const;
153 void AppendMpsPair(absl::string_view name,
double value,
154 std::string* output)
const;
157 void AppendMpsLineHeader(absl::string_view
id, absl::string_view name,
158 std::string* output)
const;
162 void AppendMpsLineHeaderWithNewLine(absl::string_view
id,
163 absl::string_view name,
164 std::string* output)
const;
170 void AppendMpsTermWithContext(absl::string_view head_name,
171 absl::string_view name,
double value,
172 std::string* output);
176 void AppendNewLineIfTwoColumns(std::string* output);
181 void AppendMpsColumns(
183 absl::Span<
const std::vector<std::pair<int, double>>> transpose,
184 std::string* output);
189 void AppendMpsBound(absl::string_view bound_type, absl::string_view name,
190 double value, std::string* output)
const;
192 const MPModelProto& proto_;
195 std::vector<std::string> exported_variable_names_;
198 std::vector<std::string> exported_constraint_names_;
201 std::vector<std::string> exported_general_constraint_names_;
204 int num_integer_variables_;
207 int num_binary_variables_;
210 int num_continuous_variables_;
213 int current_mps_column_;
216 std::unique_ptr<absl::ParsedFormat<
's',
's'>> mps_header_format_;
217 std::unique_ptr<absl::ParsedFormat<
's',
's'>> mps_format_;
224 for (
const MPGeneralConstraintProto& general_constraint :
225 model.general_constraint()) {
226 if (!general_constraint.has_indicator_constraint()) {
227 return absl::InvalidArgumentError(
228 "Non-indicator general constraints are not supported.");
231 MPModelProtoExporter exporter(model);
233 if (!exporter.ExportModelAsLpFormat(options, &output)) {
234 return absl::InvalidArgumentError(
"Unable to export model.");
241 if (model.general_constraint_size() > 0) {
242 return absl::InvalidArgumentError(
"General constraints are not supported.");
244 MPModelProtoExporter exporter(model);
246 if (!exporter.ExportModelAsMpsFormat(options, &output)) {
247 return absl::InvalidArgumentError(
"Unable to export model.");
253 const MPModelProto& model,
261MPModelProtoExporter::MPModelProtoExporter(
const MPModelProto& model)
263 num_integer_variables_(0),
264 num_binary_variables_(0),
265 num_continuous_variables_(0),
266 current_mps_column_(0) {}
271 NameManager() : names_set_(), last_n_(1) {}
272 std::string MakeUniqueName(absl::string_view name);
275 absl::flat_hash_set<std::string> names_set_;
279std::string NameManager::MakeUniqueName(absl::string_view name) {
280 std::string result(name);
283 while (!names_set_.insert(result).second) {
284 result = absl::StrCat(name,
"_", n);
295std::string MakeExportableName(
const std::string& name,
296 const std::string& forbidden_first_chars,
297 const std::string& forbidden_chars,
298 bool* found_forbidden_char) {
300 *found_forbidden_char =
301 name.empty() || absl::StrContains(forbidden_first_chars, name[0]);
302 std::string exportable_name =
303 *found_forbidden_char ? absl::StrCat(
"_", name) : name;
306 for (
char& c : exportable_name) {
307 if (absl::StrContains(forbidden_chars, c)) {
309 *found_forbidden_char =
true;
312 return exportable_name;
316template <
class ListOfProtosWithNameFields>
317std::vector<std::string> MPModelProtoExporter::ExtractAndProcessNames(
318 const ListOfProtosWithNameFields& proto, absl::string_view prefix,
319 bool obfuscate,
bool log_invalid_names,
320 const std::string& forbidden_first_chars,
321 const std::string& forbidden_chars) {
322 const int num_items = proto.size();
323 std::vector<std::string> result(num_items);
325 const int num_digits = absl::StrCat(num_items).size();
327 for (
const auto& item : proto) {
328 const std::string obfuscated_name =
329 absl::StrFormat(
"%s%0*d", prefix, num_digits, i);
330 if (obfuscate || !item.has_name()) {
331 result[
i] = namer.MakeUniqueName(obfuscated_name);
332 LOG_IF(WARNING, log_invalid_names && !item.has_name())
333 <<
"Empty name detected, created new name: " << result[
i];
335 bool found_forbidden_char =
false;
336 const std::string exportable_name =
337 MakeExportableName(item.name(), forbidden_first_chars,
338 forbidden_chars, &found_forbidden_char);
339 result[
i] = namer.MakeUniqueName(exportable_name);
340 LOG_IF(WARNING, log_invalid_names && found_forbidden_char)
341 <<
"Invalid character detected in " << item.name() <<
". Changed to "
346 const int kMaxNameLength = 255;
349 const int kMargin = 4;
350 if (result[i].size() > kMaxNameLength - kMargin) {
351 const std::string old_name = std::move(result[i]);
352 result[
i] = namer.MakeUniqueName(obfuscated_name);
353 LOG_IF(WARNING, log_invalid_names) <<
"Name is too long: " << old_name
354 <<
" exported as: " << result[
i];
364void MPModelProtoExporter::AppendComments(
const std::string& separator,
365 std::string* output)
const {
366 const char*
const sep = separator.c_str();
367 absl::StrAppendFormat(output,
"%s Generated by MPModelProtoExporter\n", sep);
368 absl::StrAppendFormat(output,
"%s %-16s : %s\n", sep,
"Name",
369 proto_.has_name() ? proto_.name().c_str() :
"NoName");
370 absl::StrAppendFormat(output,
"%s %-16s : %s\n", sep,
"Format",
"Free");
371 absl::StrAppendFormat(
372 output,
"%s %-16s : %d\n", sep,
"Constraints",
373 proto_.constraint_size() + proto_.general_constraint_size());
374 absl::StrAppendFormat(output,
"%s %-16s : %d\n", sep,
"Variables",
375 proto_.variable_size());
376 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Binary",
377 num_binary_variables_);
378 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Integer",
379 num_integer_variables_);
380 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Continuous",
381 num_continuous_variables_);
386std::string DoubleToStringWithForcedSign(
double d) {
387 return absl::StrCat((d < 0 ?
"" :
"+"), (d));
390std::string DoubleToString(
double d) {
return absl::StrCat((d)); }
394bool MPModelProtoExporter::AppendConstraint(
const MPConstraintProto& ct_proto,
395 absl::string_view name,
396 LineBreaker& line_breaker,
397 std::vector<bool>& show_variable,
398 std::string* output) {
399 for (
int i = 0;
i < ct_proto.var_index_size(); ++
i) {
400 const int var_index = ct_proto.var_index(i);
401 const double coeff = ct_proto.coefficient(i);
403 if (!WriteLpTerm(var_index, coeff, &term)) {
406 line_breaker.Append(term);
407 show_variable[var_index] = coeff != 0.0 || show_variable[var_index];
410 const double lb = ct_proto.lower_bound();
411 const double ub = ct_proto.upper_bound();
413 line_breaker.Append(absl::StrCat(
" = ", DoubleToString(ub),
"\n"));
414 absl::StrAppend(output,
" ", name,
": ", line_breaker.GetOutput());
416 if (ub != +kInfinity) {
417 std::string rhs_name(name);
418 if (lb != -kInfinity) {
419 absl::StrAppend(&rhs_name,
"_rhs");
421 absl::StrAppend(output,
" ", rhs_name,
": ", line_breaker.GetOutput());
422 const std::string relation =
423 absl::StrCat(
" <= ", DoubleToString(ub),
"\n");
426 if (!line_breaker.WillFit(relation)) absl::StrAppend(output,
"\n ");
427 absl::StrAppend(output, relation);
429 if (lb != -kInfinity) {
430 std::string lhs_name(name);
431 if (ub != +kInfinity) {
432 absl::StrAppend(&lhs_name,
"_lhs");
434 absl::StrAppend(output,
" ", lhs_name,
": ", line_breaker.GetOutput());
435 const std::string relation =
436 absl::StrCat(
" >= ", DoubleToString(lb),
"\n");
437 if (!line_breaker.WillFit(relation)) absl::StrAppend(output,
"\n ");
438 absl::StrAppend(output, relation);
445bool MPModelProtoExporter::WriteLpTerm(
int var_index,
double coefficient,
446 std::string* output)
const {
448 if (var_index < 0 || var_index >= proto_.variable_size()) {
449 LOG(DFATAL) <<
"Reference to out-of-bounds variable index # " << var_index;
452 if (coefficient != 0.0) {
453 *output = absl::StrCat(DoubleToStringWithForcedSign(coefficient),
" ",
454 exported_variable_names_[var_index],
" ");
460bool IsBoolean(
const MPVariableProto& var) {
461 return var.is_integer() && ceil(var.lower_bound()) == 0.0 &&
462 floor(var.upper_bound()) == 1.0;
465void UpdateMaxSize(absl::string_view new_string,
int* size) {
466 const int new_size = new_string.size();
467 if (new_size > *size) *size = new_size;
470void UpdateMaxSize(
double new_number,
int* size) {
471 UpdateMaxSize(DoubleToString(new_number), size);
475void MPModelProtoExporter::Setup() {
476 num_binary_variables_ = 0;
477 num_integer_variables_ = 0;
478 for (
const MPVariableProto& var : proto_.variable()) {
479 if (var.is_integer()) {
480 if (IsBoolean(var)) {
481 ++num_binary_variables_;
483 ++num_integer_variables_;
487 num_continuous_variables_ =
488 proto_.variable_size() - num_binary_variables_ - num_integer_variables_;
491void MPModelProtoExporter::ComputeMpsSmartColumnWidths(
bool obfuscated) {
494 int string_field_size = 6;
495 int number_field_size = 6;
497 for (
const MPVariableProto& var : proto_.variable()) {
498 UpdateMaxSize(var.name(), &string_field_size);
499 UpdateMaxSize(var.objective_coefficient(), &number_field_size);
500 UpdateMaxSize(var.lower_bound(), &number_field_size);
501 UpdateMaxSize(var.upper_bound(), &number_field_size);
504 for (
const MPConstraintProto& cst : proto_.constraint()) {
505 UpdateMaxSize(cst.name(), &string_field_size);
506 UpdateMaxSize(cst.lower_bound(), &number_field_size);
507 UpdateMaxSize(cst.upper_bound(), &number_field_size);
508 for (
const double coeff : cst.coefficient()) {
509 UpdateMaxSize(coeff, &number_field_size);
515 string_field_size = std::min(string_field_size, 255);
516 number_field_size = std::min(number_field_size, 255);
523 std::max(proto_.variable_size(), proto_.constraint_size()) - 1)
525 string_field_size = std::max(6, max_digits + 1);
528 mps_header_format_ = absl::ParsedFormat<'s', 's'>::New(
529 absl::StrCat(
" %-2s %-", string_field_size,
"s"));
530 mps_format_ = absl::ParsedFormat<'s', 's'>::New(
531 absl::StrCat(
" %-", string_field_size,
"s %", number_field_size,
"s"));
534bool MPModelProtoExporter::ExportModelAsLpFormat(
535 const MPModelExportOptions& options, std::string* output) {
538 const std::string kForbiddenFirstChars =
"$.0123456789";
539 const std::string kForbiddenChars =
" +-*/<>=:\\";
540 exported_constraint_names_ = ExtractAndProcessNames(
541 proto_.constraint(),
"C", options.obfuscate, options.log_invalid_names,
542 kForbiddenFirstChars, kForbiddenChars);
543 exported_general_constraint_names_ = ExtractAndProcessNames(
544 proto_.general_constraint(),
"C", options.obfuscate,
545 options.log_invalid_names, kForbiddenFirstChars, kForbiddenChars);
546 exported_variable_names_ = ExtractAndProcessNames(
547 proto_.variable(),
"V", options.obfuscate, options.log_invalid_names,
548 kForbiddenFirstChars, kForbiddenChars);
551 AppendComments(
"\\", output);
552 if (options.show_unused_variables) {
553 absl::StrAppendFormat(output,
"\\ Unused variables are shown\n");
557 absl::StrAppend(output, proto_.maximize() ?
"Maximize\n" :
"Minimize\n");
558 LineBreaker obj_line_breaker(options.max_line_length);
559 obj_line_breaker.Append(
" Obj: ");
560 if (proto_.objective_offset() != 0.0) {
561 obj_line_breaker.Append(absl::StrCat(
562 DoubleToStringWithForcedSign(proto_.objective_offset()),
" Constant "));
564 std::vector<bool> show_variable(proto_.variable_size(),
565 options.show_unused_variables);
566 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
567 const double coeff = proto_.variable(var_index).objective_coefficient();
569 if (!WriteLpTerm(var_index, coeff, &term)) {
572 obj_line_breaker.Append(term);
573 show_variable[var_index] = coeff != 0.0 || show_variable[var_index];
576 absl::StrAppend(output, obj_line_breaker.GetOutput(),
"\nSubject to\n");
577 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
578 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
579 const std::string& name = exported_constraint_names_[cst_index];
580 LineBreaker line_breaker(options.max_line_length);
581 const int kNumFormattingChars = 10;
584 line_breaker.Consume(kNumFormattingChars + name.size());
585 if (!AppendConstraint(ct_proto, name, line_breaker, show_variable,
592 for (
int cst_index = 0; cst_index < proto_.general_constraint_size();
594 const MPGeneralConstraintProto& ct_proto =
595 proto_.general_constraint(cst_index);
596 const std::string& name = exported_general_constraint_names_[cst_index];
597 LineBreaker line_breaker(options.max_line_length);
598 const int kNumFormattingChars = 10;
601 line_breaker.Consume(kNumFormattingChars + name.size());
603 if (!ct_proto.has_indicator_constraint())
return false;
604 const MPIndicatorConstraint& indicator_ct = ct_proto.indicator_constraint();
605 const int binary_var_index = indicator_ct.var_index();
606 const int binary_var_value = indicator_ct.var_value();
607 if (binary_var_index < 0 || binary_var_index >= proto_.variable_size()) {
610 show_variable[binary_var_index] =
true;
611 line_breaker.Append(absl::StrFormat(
612 "%s = %d -> ", exported_variable_names_[binary_var_index],
614 if (!AppendConstraint(indicator_ct.constraint(), name, line_breaker,
615 show_variable, output)) {
621 absl::StrAppend(output,
"Bounds\n");
622 if (proto_.objective_offset() != 0.0) {
623 absl::StrAppend(output,
" 1 <= Constant <= 1\n");
625 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
626 if (!show_variable[var_index])
continue;
627 const MPVariableProto& var_proto = proto_.variable(var_index);
628 const double lb = var_proto.lower_bound();
629 const double ub = var_proto.upper_bound();
630 if (var_proto.is_integer() && lb == round(lb) && ub == round(ub)) {
631 absl::StrAppendFormat(output,
" %.0f <= %s <= %.0f\n", lb,
632 exported_variable_names_[var_index], ub);
634 absl::StrAppend(output,
" ");
635 if (lb == -kInfinity && ub == kInfinity) {
636 absl::StrAppend(output, exported_variable_names_[var_index],
" free");
638 if (lb != -kInfinity) {
639 absl::StrAppend(output, DoubleToString(lb),
" <= ");
641 absl::StrAppend(output, exported_variable_names_[var_index]);
642 if (ub != kInfinity) {
643 absl::StrAppend(output,
" <= ", DoubleToString(ub));
646 absl::StrAppend(output,
"\n");
651 if (num_binary_variables_ > 0) {
652 absl::StrAppend(output,
"Binaries\n");
653 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
654 if (!show_variable[var_index])
continue;
655 const MPVariableProto& var_proto = proto_.variable(var_index);
656 if (IsBoolean(var_proto)) {
657 absl::StrAppendFormat(output,
" %s\n",
658 exported_variable_names_[var_index]);
664 if (num_integer_variables_ > 0) {
665 absl::StrAppend(output,
"Generals\n");
666 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
667 if (!show_variable[var_index])
continue;
668 const MPVariableProto& var_proto = proto_.variable(var_index);
669 if (var_proto.is_integer() && !IsBoolean(var_proto)) {
670 absl::StrAppend(output,
" ", exported_variable_names_[var_index],
"\n");
674 absl::StrAppend(output,
"End\n");
678void MPModelProtoExporter::AppendMpsPair(absl::string_view name,
double value,
679 std::string* output)
const {
680 absl::StrAppendFormat(output, *mps_format_, name, DoubleToString(value));
683void MPModelProtoExporter::AppendMpsLineHeader(absl::string_view
id,
684 absl::string_view name,
685 std::string* output)
const {
686 absl::StrAppendFormat(output, *mps_header_format_,
id, name);
689void MPModelProtoExporter::AppendMpsLineHeaderWithNewLine(
690 absl::string_view
id, absl::string_view name, std::string* output)
const {
691 AppendMpsLineHeader(
id, name, output);
692 absl::StripTrailingAsciiWhitespace(output);
693 absl::StrAppend(output,
"\n");
696void MPModelProtoExporter::AppendMpsTermWithContext(absl::string_view head_name,
697 absl::string_view name,
699 std::string* output) {
700 if (current_mps_column_ == 0) {
701 AppendMpsLineHeader(
"", head_name, output);
703 AppendMpsPair(name, value, output);
704 AppendNewLineIfTwoColumns(output);
707void MPModelProtoExporter::AppendMpsBound(absl::string_view bound_type,
708 absl::string_view name,
double value,
709 std::string* output)
const {
710 AppendMpsLineHeader(bound_type,
"BOUND", output);
711 AppendMpsPair(name, value, output);
712 absl::StripTrailingAsciiWhitespace(output);
713 absl::StrAppend(output,
"\n");
716void MPModelProtoExporter::AppendNewLineIfTwoColumns(std::string* output) {
717 ++current_mps_column_;
718 if (current_mps_column_ == 2) {
719 absl::StripTrailingAsciiWhitespace(output);
720 absl::StrAppend(output,
"\n");
721 current_mps_column_ = 0;
725void MPModelProtoExporter::AppendMpsColumns(
727 absl::Span<
const std::vector<std::pair<int, double>>> transpose,
728 std::string* output) {
729 current_mps_column_ = 0;
730 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
731 const MPVariableProto& var_proto = proto_.variable(var_index);
732 if (var_proto.is_integer() != integrality)
continue;
733 const std::string& var_name = exported_variable_names_[var_index];
734 current_mps_column_ = 0;
735 if (var_proto.objective_coefficient() != 0.0) {
736 AppendMpsTermWithContext(var_name,
"COST",
737 var_proto.objective_coefficient(), output);
739 for (
const std::pair<int, double>& cst_index_and_coeff :
740 transpose[var_index]) {
741 const std::string& cst_name =
742 exported_constraint_names_[cst_index_and_coeff.first];
743 AppendMpsTermWithContext(var_name, cst_name, cst_index_and_coeff.second,
746 AppendNewLineIfTwoColumns(output);
750bool MPModelProtoExporter::ExportModelAsMpsFormat(
751 const MPModelExportOptions& options, std::string* output) {
754 ComputeMpsSmartColumnWidths(options.obfuscate);
755 const std::string kForbiddenFirstChars =
"";
756 const std::string kForbiddenChars =
" ";
757 exported_constraint_names_ = ExtractAndProcessNames(
758 proto_.constraint(),
"C", options.obfuscate, options.log_invalid_names,
759 kForbiddenFirstChars, kForbiddenChars);
760 exported_variable_names_ = ExtractAndProcessNames(
761 proto_.variable(),
"V", options.obfuscate, options.log_invalid_names,
762 kForbiddenFirstChars, kForbiddenChars);
765 AppendComments(
"*", output);
769 absl::StrAppendFormat(output,
"%-14s%s\n",
"NAME", proto_.name());
771 if (proto_.maximize()) {
772 absl::StrAppendFormat(output,
"OBJSENSE\n MAX\n");
776 current_mps_column_ = 0;
777 std::string rows_section;
778 AppendMpsLineHeaderWithNewLine(
"N",
"COST", &rows_section);
779 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
780 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
781 const double lb = ct_proto.lower_bound();
782 const double ub = ct_proto.upper_bound();
783 const std::string& cst_name = exported_constraint_names_[cst_index];
784 if (lb == -kInfinity && ub == kInfinity) {
785 AppendMpsLineHeaderWithNewLine(
"N", cst_name, &rows_section);
786 }
else if (lb == ub) {
787 AppendMpsLineHeaderWithNewLine(
"E", cst_name, &rows_section);
788 }
else if (lb == -kInfinity) {
789 AppendMpsLineHeaderWithNewLine(
"L", cst_name, &rows_section);
791 AppendMpsLineHeaderWithNewLine(
"G", cst_name, &rows_section);
794 if (!rows_section.empty()) {
795 absl::StrAppend(output,
"ROWS\n", rows_section);
801 std::vector<std::vector<std::pair<int, double>>> transpose(
802 proto_.variable_size());
803 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
804 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
805 for (
int k = 0; k < ct_proto.var_index_size(); ++k) {
806 const int var_index = ct_proto.var_index(k);
807 if (var_index < 0 || var_index >= proto_.variable_size()) {
808 LOG(DFATAL) <<
"In constraint #" << cst_index <<
", var_index #" << k
809 <<
" is " << var_index <<
", which is out of bounds.";
812 const double coeff = ct_proto.coefficient(k);
814 transpose[var_index].push_back(
815 std::pair<int, double>(cst_index, coeff));
821 std::string columns_section;
822 AppendMpsColumns(
true, transpose, &columns_section);
823 if (!columns_section.empty()) {
824 constexpr const char kIntMarkerFormat[] =
" %-10s%-36s%-8s\n";
826 absl::StrFormat(kIntMarkerFormat,
"INTSTART",
"'MARKER'",
"'INTORG'") +
828 absl::StrAppendFormat(&columns_section, kIntMarkerFormat,
"INTEND",
829 "'MARKER'",
"'INTEND'");
831 AppendMpsColumns(
false, transpose, &columns_section);
832 if (!columns_section.empty()) {
833 absl::StrAppend(output,
"COLUMNS\n", columns_section);
837 current_mps_column_ = 0;
838 std::string rhs_section;
841 if (proto_.objective_offset() != 0) {
842 AppendMpsTermWithContext(
"RHS",
"COST", -proto_.objective_offset(),
845 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
846 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
847 const double lb = ct_proto.lower_bound();
848 const double ub = ct_proto.upper_bound();
849 const std::string& cst_name = exported_constraint_names_[cst_index];
850 if (lb != -kInfinity) {
851 AppendMpsTermWithContext(
"RHS", cst_name, lb, &rhs_section);
852 }
else if (ub != +kInfinity) {
853 AppendMpsTermWithContext(
"RHS", cst_name, ub, &rhs_section);
856 AppendNewLineIfTwoColumns(&rhs_section);
857 if (!rhs_section.empty()) {
858 absl::StrAppend(output,
"RHS\n", rhs_section);
862 current_mps_column_ = 0;
863 std::string ranges_section;
864 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
865 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
866 const double range = fabs(ct_proto.upper_bound() - ct_proto.lower_bound());
867 if (range != 0.0 && range != +kInfinity) {
868 const std::string& cst_name = exported_constraint_names_[cst_index];
869 AppendMpsTermWithContext(
"RANGE", cst_name, range, &ranges_section);
872 AppendNewLineIfTwoColumns(&ranges_section);
873 if (!ranges_section.empty()) {
874 absl::StrAppend(output,
"RANGES\n", ranges_section);
878 current_mps_column_ = 0;
879 std::string bounds_section;
880 for (
int var_index = 0; var_index < proto_.variable_size(); ++var_index) {
881 const MPVariableProto& var_proto = proto_.variable(var_index);
882 const double lb = var_proto.lower_bound();
883 const double ub = var_proto.upper_bound();
884 const std::string& var_name = exported_variable_names_[var_index];
886 if (lb == -kInfinity && ub == +kInfinity) {
887 AppendMpsLineHeader(
"FR",
"BOUND", &bounds_section);
888 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
892 if (var_proto.is_integer()) {
893 if (IsBoolean(var_proto)) {
894 AppendMpsLineHeader(
"BV",
"BOUND", &bounds_section);
895 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
898 AppendMpsBound(
"FX", var_name, lb, &bounds_section);
900 if (lb == -kInfinity) {
901 AppendMpsLineHeader(
"MI",
"BOUND", &bounds_section);
902 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
903 }
else if (lb != 0.0 || ub == kInfinity) {
907 AppendMpsBound(
"LI", var_name, lb, &bounds_section);
909 if (ub != kInfinity) {
910 AppendMpsBound(
"UI", var_name, ub, &bounds_section);
916 AppendMpsBound(
"FX", var_name, lb, &bounds_section);
918 if (lb == -kInfinity) {
919 AppendMpsLineHeader(
"MI",
"BOUND", &bounds_section);
920 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
921 }
else if (lb != 0.0) {
922 AppendMpsBound(
"LO", var_name, lb, &bounds_section);
924 if (lb == 0.0 && ub == +kInfinity) {
925 AppendMpsLineHeader(
"PL",
"BOUND", &bounds_section);
926 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
927 }
else if (ub != +kInfinity) {
928 AppendMpsBound(
"UP", var_name, ub, &bounds_section);
933 if (!bounds_section.empty()) {
934 absl::StrAppend(output,
"BOUNDS\n", bounds_section);
937 absl::StrAppend(output,
"ENDATA\n");
#define ASSIGN_OR_RETURN(lhs, rexpr)
ABSL_RETIRED_FLAG(bool, lp_log_invalid_name, false, "DEPRECATED.")
absl::Status SetContents(absl::string_view filename, absl::string_view contents, Options options)
In SWIG mode, we don't want anything besides these top-level includes.
absl::Status WriteModelToMpsFile(absl::string_view filename, const MPModelProto &model, const MPModelExportOptions &options)
static constexpr double kInfinity
absl::StatusOr< std::string > ExportModelAsLpFormat(const MPModelProto &model, const MPModelExportOptions &options)
absl::StatusOr< std::string > ExportModelAsMpsFormat(const MPModelProto &model, const MPModelExportOptions &options)