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"
37#include "ortools/linear_solver/linear_solver.pb.h"
44constexpr double kInfinity = std::numeric_limits<double>::infinity();
48 explicit LineBreaker(
int max_line_size)
49 : max_line_size_(max_line_size), line_size_(0), output_() {}
55 void Append(absl::string_view s);
59 bool WillFit(
const std::string& s) {
60 return line_size_ +
static_cast<int>(s.size()) < max_line_size_;
65 void Consume(
int size) { line_size_ +=
size; }
67 std::string GetOutput()
const {
return output_; }
75void LineBreaker::Append(absl::string_view s) {
76 line_size_ += s.size();
77 if (line_size_ > max_line_size_) {
78 line_size_ = s.size();
79 absl::StrAppend(&output_,
"\n ");
81 absl::StrAppend(&output_, s);
84class MPModelProtoExporter {
86 explicit MPModelProtoExporter(
const MPModelProto&
model);
89 MPModelProtoExporter(
const MPModelProtoExporter&) =
delete;
90 MPModelProtoExporter& operator=(
const MPModelProtoExporter&) =
delete;
103 void ComputeMpsSmartColumnWidths(
bool obfuscated);
121 template <
class ListOfProtosWithNameFields>
122 std::vector<std::string> ExtractAndProcessNames(
123 const ListOfProtosWithNameFields&
proto, absl::string_view prefix,
124 bool obfuscate,
bool log_invalid_names,
125 const std::string& forbidden_first_chars,
126 const std::string& forbidden_chars);
135 void AppendComments(
const std::string& separator, std::string* output)
const;
141 bool AppendConstraint(
const MPConstraintProto& ct_proto,
142 const std::string&
name, LineBreaker& line_breaker,
143 std::vector<bool>& show_variable, std::string* output);
148 std::string* output)
const;
152 void AppendMpsPair(
const std::string&
name,
double value,
153 std::string* output)
const;
156 void AppendMpsLineHeader(
const std::string&
id,
const std::string&
name,
157 std::string* output)
const;
161 void AppendMpsLineHeaderWithNewLine(
const std::string&
id,
162 const std::string&
name,
163 std::string* output)
const;
169 void AppendMpsTermWithContext(
const std::string& head_name,
171 std::string* output);
175 void AppendNewLineIfTwoColumns(std::string* output);
180 void AppendMpsColumns(
182 const std::vector<std::vector<std::pair<int, double>>>& transpose,
183 std::string* output);
188 void AppendMpsBound(
const std::string& bound_type,
const std::string&
name,
189 double value, std::string* output)
const;
191 const MPModelProto& proto_;
194 std::vector<std::string> exported_variable_names_;
197 std::vector<std::string> exported_constraint_names_;
200 std::vector<std::string> exported_general_constraint_names_;
203 int num_integer_variables_;
206 int num_binary_variables_;
209 int num_continuous_variables_;
212 int current_mps_column_;
215 std::unique_ptr<absl::ParsedFormat<
's',
's'>> mps_header_format_;
216 std::unique_ptr<absl::ParsedFormat<
's',
's'>> mps_format_;
223 for (
const MPGeneralConstraintProto& general_constraint :
224 model.general_constraint()) {
225 if (!general_constraint.has_indicator_constraint()) {
226 return absl::InvalidArgumentError(
227 "Non-indicator general constraints are not supported.");
230 MPModelProtoExporter exporter(
model);
232 if (!exporter.ExportModelAsLpFormat(options, &output)) {
233 return absl::InvalidArgumentError(
"Unable to export model.");
240 if (
model.general_constraint_size() > 0) {
241 return absl::InvalidArgumentError(
"General constraints are not supported.");
243 MPModelProtoExporter exporter(
model);
245 if (!exporter.ExportModelAsMpsFormat(options, &output)) {
246 return absl::InvalidArgumentError(
"Unable to export model.");
252 const MPModelProto&
model,
260MPModelProtoExporter::MPModelProtoExporter(
const MPModelProto&
model)
262 num_integer_variables_(0),
263 num_binary_variables_(0),
264 num_continuous_variables_(0),
265 current_mps_column_(0) {}
270 NameManager() : names_set_(), last_n_(1) {}
271 std::string MakeUniqueName(absl::string_view
name);
274 absl::flat_hash_set<std::string> names_set_;
278std::string NameManager::MakeUniqueName(absl::string_view
name) {
279 std::string result(
name);
282 while (!names_set_.insert(result).second) {
283 result = absl::StrCat(
name,
"_", n);
294std::string MakeExportableName(
const std::string&
name,
295 const std::string& forbidden_first_chars,
296 const std::string& forbidden_chars,
297 bool* found_forbidden_char) {
299 *found_forbidden_char =
300 name.empty() || absl::StrContains(forbidden_first_chars,
name[0]);
301 std::string exportable_name =
302 *found_forbidden_char ? absl::StrCat(
"_",
name) :
name;
305 for (
char& c : exportable_name) {
306 if (absl::StrContains(forbidden_chars, c)) {
308 *found_forbidden_char =
true;
311 return exportable_name;
315template <
class ListOfProtosWithNameFields>
316std::vector<std::string> MPModelProtoExporter::ExtractAndProcessNames(
317 const ListOfProtosWithNameFields&
proto, absl::string_view prefix,
318 bool obfuscate,
bool log_invalid_names,
319 const std::string& forbidden_first_chars,
320 const std::string& forbidden_chars) {
321 const int num_items =
proto.size();
322 std::vector<std::string> result(num_items);
324 const int num_digits = absl::StrCat(num_items).size();
326 for (
const auto& item :
proto) {
327 const std::string obfuscated_name =
328 absl::StrFormat(
"%s%0*d", prefix, num_digits, i);
329 if (obfuscate || !item.has_name()) {
330 result[
i] = namer.MakeUniqueName(obfuscated_name);
331 LOG_IF(WARNING, log_invalid_names && !item.has_name())
332 <<
"Empty name detected, created new name: " << result[
i];
334 bool found_forbidden_char =
false;
335 const std::string exportable_name =
336 MakeExportableName(item.name(), forbidden_first_chars,
337 forbidden_chars, &found_forbidden_char);
338 result[
i] = namer.MakeUniqueName(exportable_name);
339 LOG_IF(WARNING, log_invalid_names && found_forbidden_char)
340 <<
"Invalid character detected in " << item.name() <<
". Changed to "
345 const int kMaxNameLength = 255;
348 const int kMargin = 4;
349 if (result[i].
size() > kMaxNameLength - kMargin) {
350 const std::string old_name = std::move(result[i]);
351 result[
i] = namer.MakeUniqueName(obfuscated_name);
352 LOG_IF(WARNING, log_invalid_names) <<
"Name is too long: " << old_name
353 <<
" exported as: " << result[
i];
363void MPModelProtoExporter::AppendComments(
const std::string& separator,
364 std::string* output)
const {
365 const char*
const sep = separator.c_str();
366 absl::StrAppendFormat(output,
"%s Generated by MPModelProtoExporter\n", sep);
367 absl::StrAppendFormat(output,
"%s %-16s : %s\n", sep,
"Name",
368 proto_.has_name() ? proto_.name().c_str() :
"NoName");
369 absl::StrAppendFormat(output,
"%s %-16s : %s\n", sep,
"Format",
"Free");
370 absl::StrAppendFormat(
371 output,
"%s %-16s : %d\n", sep,
"Constraints",
372 proto_.constraint_size() + proto_.general_constraint_size());
373 absl::StrAppendFormat(output,
"%s %-16s : %d\n", sep,
"Variables",
374 proto_.variable_size());
375 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Binary",
376 num_binary_variables_);
377 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Integer",
378 num_integer_variables_);
379 absl::StrAppendFormat(output,
"%s %-14s : %d\n", sep,
"Continuous",
380 num_continuous_variables_);
385std::string DoubleToStringWithForcedSign(
double d) {
386 return absl::StrCat((d < 0 ?
"" :
"+"), (d));
389std::string DoubleToString(
double d) {
return absl::StrCat((d)); }
393bool MPModelProtoExporter::AppendConstraint(
const MPConstraintProto& ct_proto,
394 const std::string&
name,
395 LineBreaker& line_breaker,
396 std::vector<bool>& show_variable,
397 std::string* output) {
398 for (
int i = 0;
i < ct_proto.var_index_size(); ++
i) {
399 const int var_index = ct_proto.var_index(i);
400 const double coeff = ct_proto.coefficient(i);
402 if (!WriteLpTerm(
var_index, coeff, &term)) {
405 line_breaker.Append(term);
409 const double lb = ct_proto.lower_bound();
410 const double ub = ct_proto.upper_bound();
412 line_breaker.Append(absl::StrCat(
" = ", DoubleToString(ub),
"\n"));
413 absl::StrAppend(output,
" ",
name,
": ", line_breaker.GetOutput());
415 if (ub != +kInfinity) {
416 std::string rhs_name =
name;
417 if (lb != -kInfinity) {
418 absl::StrAppend(&rhs_name,
"_rhs");
420 absl::StrAppend(output,
" ", rhs_name,
": ", line_breaker.GetOutput());
421 const std::string relation =
422 absl::StrCat(
" <= ", DoubleToString(ub),
"\n");
425 if (!line_breaker.WillFit(relation)) absl::StrAppend(output,
"\n ");
426 absl::StrAppend(output, relation);
428 if (lb != -kInfinity) {
429 std::string lhs_name =
name;
430 if (ub != +kInfinity) {
431 absl::StrAppend(&lhs_name,
"_lhs");
433 absl::StrAppend(output,
" ", lhs_name,
": ", line_breaker.GetOutput());
434 const std::string relation =
435 absl::StrCat(
" >= ", DoubleToString(lb),
"\n");
436 if (!line_breaker.WillFit(relation)) absl::StrAppend(output,
"\n ");
437 absl::StrAppend(output, relation);
445 std::string* output)
const {
448 LOG(DFATAL) <<
"Reference to out-of-bounds variable index # " <<
var_index;
452 *output = absl::StrCat(DoubleToStringWithForcedSign(
coefficient),
" ",
453 exported_variable_names_[
var_index],
" ");
459bool IsBoolean(
const MPVariableProto&
var) {
460 return var.is_integer() && ceil(
var.lower_bound()) == 0.0 &&
464void UpdateMaxSize(
const std::string& new_string,
int*
size) {
465 const int new_size = new_string.size();
466 if (new_size > *
size) *
size = new_size;
469void UpdateMaxSize(
double new_number,
int*
size) {
470 UpdateMaxSize(DoubleToString(new_number),
size);
474void MPModelProtoExporter::Setup() {
475 num_binary_variables_ = 0;
476 num_integer_variables_ = 0;
477 for (
const MPVariableProto&
var : proto_.variable()) {
478 if (
var.is_integer()) {
479 if (IsBoolean(
var)) {
480 ++num_binary_variables_;
482 ++num_integer_variables_;
486 num_continuous_variables_ =
487 proto_.variable_size() - num_binary_variables_ - num_integer_variables_;
490void MPModelProtoExporter::ComputeMpsSmartColumnWidths(
bool obfuscated) {
493 int string_field_size = 6;
494 int number_field_size = 6;
496 for (
const MPVariableProto&
var : proto_.variable()) {
497 UpdateMaxSize(
var.name(), &string_field_size);
498 UpdateMaxSize(
var.objective_coefficient(), &number_field_size);
499 UpdateMaxSize(
var.lower_bound(), &number_field_size);
500 UpdateMaxSize(
var.upper_bound(), &number_field_size);
503 for (
const MPConstraintProto& cst : proto_.constraint()) {
504 UpdateMaxSize(cst.name(), &string_field_size);
505 UpdateMaxSize(cst.lower_bound(), &number_field_size);
506 UpdateMaxSize(cst.upper_bound(), &number_field_size);
507 for (
const double coeff : cst.coefficient()) {
508 UpdateMaxSize(coeff, &number_field_size);
514 string_field_size = std::min(string_field_size, 255);
515 number_field_size = std::min(number_field_size, 255);
522 std::max(proto_.variable_size(), proto_.constraint_size()) - 1)
524 string_field_size = std::max(6, max_digits + 1);
527 mps_header_format_ = absl::ParsedFormat<'s', 's'>::New(
528 absl::StrCat(
" %-2s %-", string_field_size,
"s"));
529 mps_format_ = absl::ParsedFormat<'s', 's'>::New(
530 absl::StrCat(
" %-", string_field_size,
"s %", number_field_size,
"s"));
533bool MPModelProtoExporter::ExportModelAsLpFormat(
534 const MPModelExportOptions& options, std::string* output) {
537 const std::string kForbiddenFirstChars =
"$.0123456789";
538 const std::string kForbiddenChars =
" +-*/<>=:\\";
539 exported_constraint_names_ = ExtractAndProcessNames(
540 proto_.constraint(),
"C", options.obfuscate, options.log_invalid_names,
541 kForbiddenFirstChars, kForbiddenChars);
542 exported_general_constraint_names_ = ExtractAndProcessNames(
543 proto_.general_constraint(),
"C", options.obfuscate,
544 options.log_invalid_names, kForbiddenFirstChars, kForbiddenChars);
545 exported_variable_names_ = ExtractAndProcessNames(
546 proto_.variable(),
"V", options.obfuscate, options.log_invalid_names,
547 kForbiddenFirstChars, kForbiddenChars);
550 AppendComments(
"\\", output);
551 if (options.show_unused_variables) {
552 absl::StrAppendFormat(output,
"\\ Unused variables are shown\n");
556 absl::StrAppend(output, proto_.maximize() ?
"Maximize\n" :
"Minimize\n");
557 LineBreaker obj_line_breaker(options.max_line_length);
558 obj_line_breaker.Append(
" Obj: ");
559 if (proto_.objective_offset() != 0.0) {
560 obj_line_breaker.Append(absl::StrCat(
561 DoubleToStringWithForcedSign(proto_.objective_offset()),
" Constant "));
563 std::vector<bool> show_variable(proto_.variable_size(),
564 options.show_unused_variables);
566 const double coeff = proto_.variable(
var_index).objective_coefficient();
568 if (!WriteLpTerm(
var_index, coeff, &term)) {
571 obj_line_breaker.Append(term);
575 absl::StrAppend(output, obj_line_breaker.GetOutput(),
"\nSubject to\n");
576 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
577 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
578 const std::string&
name = exported_constraint_names_[cst_index];
579 LineBreaker line_breaker(options.max_line_length);
580 const int kNumFormattingChars = 10;
583 line_breaker.Consume(kNumFormattingChars +
name.size());
584 if (!AppendConstraint(ct_proto,
name, line_breaker, show_variable,
591 for (
int cst_index = 0; cst_index < proto_.general_constraint_size();
593 const MPGeneralConstraintProto& ct_proto =
594 proto_.general_constraint(cst_index);
595 const std::string&
name = exported_general_constraint_names_[cst_index];
596 LineBreaker line_breaker(options.max_line_length);
597 const int kNumFormattingChars = 10;
600 line_breaker.Consume(kNumFormattingChars +
name.size());
602 if (!ct_proto.has_indicator_constraint())
return false;
603 const MPIndicatorConstraint& indicator_ct = ct_proto.indicator_constraint();
604 const int binary_var_index = indicator_ct.var_index();
605 const int binary_var_value = indicator_ct.var_value();
606 if (binary_var_index < 0 || binary_var_index >= proto_.variable_size()) {
609 show_variable[binary_var_index] =
true;
610 line_breaker.Append(absl::StrFormat(
611 "%s = %d -> ", exported_variable_names_[binary_var_index],
613 if (!AppendConstraint(indicator_ct.constraint(),
name, line_breaker,
614 show_variable, output)) {
620 absl::StrAppend(output,
"Bounds\n");
621 if (proto_.objective_offset() != 0.0) {
622 absl::StrAppend(output,
" 1 <= Constant <= 1\n");
626 const MPVariableProto& var_proto = proto_.variable(
var_index);
627 const double lb = var_proto.lower_bound();
628 const double ub = var_proto.upper_bound();
629 if (var_proto.is_integer() && lb == round(lb) && ub == round(ub)) {
630 absl::StrAppendFormat(output,
" %.0f <= %s <= %.0f\n", lb,
631 exported_variable_names_[
var_index], ub);
633 absl::StrAppend(output,
" ");
634 if (lb == -kInfinity && ub == kInfinity) {
635 absl::StrAppend(output, exported_variable_names_[
var_index],
" free");
637 if (lb != -kInfinity) {
638 absl::StrAppend(output, DoubleToString(lb),
" <= ");
640 absl::StrAppend(output, exported_variable_names_[
var_index]);
641 if (ub != kInfinity) {
642 absl::StrAppend(output,
" <= ", DoubleToString(ub));
645 absl::StrAppend(output,
"\n");
650 if (num_binary_variables_ > 0) {
651 absl::StrAppend(output,
"Binaries\n");
654 const MPVariableProto& var_proto = proto_.variable(
var_index);
655 if (IsBoolean(var_proto)) {
656 absl::StrAppendFormat(output,
" %s\n",
663 if (num_integer_variables_ > 0) {
664 absl::StrAppend(output,
"Generals\n");
667 const MPVariableProto& var_proto = proto_.variable(
var_index);
668 if (var_proto.is_integer() && !IsBoolean(var_proto)) {
669 absl::StrAppend(output,
" ", exported_variable_names_[
var_index],
"\n");
673 absl::StrAppend(output,
"End\n");
677void MPModelProtoExporter::AppendMpsPair(
const std::string&
name,
double value,
678 std::string* output)
const {
679 absl::StrAppendFormat(output, *mps_format_,
name, DoubleToString(
value));
682void MPModelProtoExporter::AppendMpsLineHeader(
const std::string&
id,
683 const std::string&
name,
684 std::string* output)
const {
685 absl::StrAppendFormat(output, *mps_header_format_,
id,
name);
688void MPModelProtoExporter::AppendMpsLineHeaderWithNewLine(
689 const std::string&
id,
const std::string&
name, std::string* output)
const {
690 AppendMpsLineHeader(
id,
name, output);
691 absl::StripTrailingAsciiWhitespace(output);
692 absl::StrAppend(output,
"\n");
695void MPModelProtoExporter::AppendMpsTermWithContext(
696 const std::string& head_name,
const std::string&
name,
double value,
697 std::string* output) {
698 if (current_mps_column_ == 0) {
699 AppendMpsLineHeader(
"", head_name, output);
702 AppendNewLineIfTwoColumns(output);
705void MPModelProtoExporter::AppendMpsBound(
const std::string& bound_type,
707 std::string* output)
const {
708 AppendMpsLineHeader(bound_type,
"BOUND", output);
710 absl::StripTrailingAsciiWhitespace(output);
711 absl::StrAppend(output,
"\n");
714void MPModelProtoExporter::AppendNewLineIfTwoColumns(std::string* output) {
715 ++current_mps_column_;
716 if (current_mps_column_ == 2) {
717 absl::StripTrailingAsciiWhitespace(output);
718 absl::StrAppend(output,
"\n");
719 current_mps_column_ = 0;
723void MPModelProtoExporter::AppendMpsColumns(
725 const std::vector<std::vector<std::pair<int, double>>>& transpose,
726 std::string* output) {
727 current_mps_column_ = 0;
729 const MPVariableProto& var_proto = proto_.variable(
var_index);
730 if (var_proto.is_integer() != integrality)
continue;
731 const std::string& var_name = exported_variable_names_[
var_index];
732 current_mps_column_ = 0;
733 if (var_proto.objective_coefficient() != 0.0) {
734 AppendMpsTermWithContext(var_name,
"COST",
735 var_proto.objective_coefficient(), output);
737 for (
const std::pair<int, double>& cst_index_and_coeff :
739 const std::string& cst_name =
740 exported_constraint_names_[cst_index_and_coeff.first];
741 AppendMpsTermWithContext(var_name, cst_name, cst_index_and_coeff.second,
744 AppendNewLineIfTwoColumns(output);
748bool MPModelProtoExporter::ExportModelAsMpsFormat(
749 const MPModelExportOptions& options, std::string* output) {
752 ComputeMpsSmartColumnWidths(options.obfuscate);
753 const std::string kForbiddenFirstChars =
"";
754 const std::string kForbiddenChars =
" ";
755 exported_constraint_names_ = ExtractAndProcessNames(
756 proto_.constraint(),
"C", options.obfuscate, options.log_invalid_names,
757 kForbiddenFirstChars, kForbiddenChars);
758 exported_variable_names_ = ExtractAndProcessNames(
759 proto_.variable(),
"V", options.obfuscate, options.log_invalid_names,
760 kForbiddenFirstChars, kForbiddenChars);
763 AppendComments(
"*", output);
767 absl::StrAppendFormat(output,
"%-14s%s\n",
"NAME", proto_.name());
769 if (proto_.maximize()) {
770 absl::StrAppendFormat(output,
"OBJSENSE\n MAX\n");
774 current_mps_column_ = 0;
775 std::string rows_section;
776 AppendMpsLineHeaderWithNewLine(
"N",
"COST", &rows_section);
777 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
778 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
779 const double lb = ct_proto.lower_bound();
780 const double ub = ct_proto.upper_bound();
781 const std::string& cst_name = exported_constraint_names_[cst_index];
782 if (lb == -kInfinity && ub == kInfinity) {
783 AppendMpsLineHeaderWithNewLine(
"N", cst_name, &rows_section);
784 }
else if (lb == ub) {
785 AppendMpsLineHeaderWithNewLine(
"E", cst_name, &rows_section);
786 }
else if (lb == -kInfinity) {
787 AppendMpsLineHeaderWithNewLine(
"L", cst_name, &rows_section);
789 AppendMpsLineHeaderWithNewLine(
"G", cst_name, &rows_section);
792 if (!rows_section.empty()) {
793 absl::StrAppend(output,
"ROWS\n", rows_section);
799 std::vector<std::vector<std::pair<int, double>>> transpose(
800 proto_.variable_size());
801 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
802 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
803 for (
int k = 0; k < ct_proto.var_index_size(); ++k) {
804 const int var_index = ct_proto.var_index(k);
806 LOG(DFATAL) <<
"In constraint #" << cst_index <<
", var_index #" << k
807 <<
" is " <<
var_index <<
", which is out of bounds.";
810 const double coeff = ct_proto.coefficient(k);
813 std::pair<int, double>(cst_index, coeff));
819 std::string columns_section;
820 AppendMpsColumns(
true, transpose, &columns_section);
821 if (!columns_section.empty()) {
822 constexpr const char kIntMarkerFormat[] =
" %-10s%-36s%-8s\n";
824 absl::StrFormat(kIntMarkerFormat,
"INTSTART",
"'MARKER'",
"'INTORG'") +
826 absl::StrAppendFormat(&columns_section, kIntMarkerFormat,
"INTEND",
827 "'MARKER'",
"'INTEND'");
829 AppendMpsColumns(
false, transpose, &columns_section);
830 if (!columns_section.empty()) {
831 absl::StrAppend(output,
"COLUMNS\n", columns_section);
835 current_mps_column_ = 0;
836 std::string rhs_section;
839 if (proto_.objective_offset() != 0) {
840 AppendMpsTermWithContext(
"RHS",
"COST", -proto_.objective_offset(),
843 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
844 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
845 const double lb = ct_proto.lower_bound();
846 const double ub = ct_proto.upper_bound();
847 const std::string& cst_name = exported_constraint_names_[cst_index];
848 if (lb != -kInfinity) {
849 AppendMpsTermWithContext(
"RHS", cst_name, lb, &rhs_section);
850 }
else if (ub != +kInfinity) {
851 AppendMpsTermWithContext(
"RHS", cst_name, ub, &rhs_section);
854 AppendNewLineIfTwoColumns(&rhs_section);
855 if (!rhs_section.empty()) {
856 absl::StrAppend(output,
"RHS\n", rhs_section);
860 current_mps_column_ = 0;
861 std::string ranges_section;
862 for (
int cst_index = 0; cst_index < proto_.constraint_size(); ++cst_index) {
863 const MPConstraintProto& ct_proto = proto_.constraint(cst_index);
864 const double range = fabs(ct_proto.upper_bound() - ct_proto.lower_bound());
866 const std::string& cst_name = exported_constraint_names_[cst_index];
867 AppendMpsTermWithContext(
"RANGE", cst_name,
range, &ranges_section);
870 AppendNewLineIfTwoColumns(&ranges_section);
871 if (!ranges_section.empty()) {
872 absl::StrAppend(output,
"RANGES\n", ranges_section);
876 current_mps_column_ = 0;
877 std::string bounds_section;
879 const MPVariableProto& var_proto = proto_.variable(
var_index);
880 const double lb = var_proto.lower_bound();
881 const double ub = var_proto.upper_bound();
882 const std::string& var_name = exported_variable_names_[
var_index];
884 if (lb == -kInfinity && ub == +kInfinity) {
885 AppendMpsLineHeader(
"FR",
"BOUND", &bounds_section);
886 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
890 if (var_proto.is_integer()) {
891 if (IsBoolean(var_proto)) {
892 AppendMpsLineHeader(
"BV",
"BOUND", &bounds_section);
893 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
896 AppendMpsBound(
"FX", var_name, lb, &bounds_section);
898 if (lb == -kInfinity) {
899 AppendMpsLineHeader(
"MI",
"BOUND", &bounds_section);
900 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
901 }
else if (lb != 0.0 || ub == kInfinity) {
905 AppendMpsBound(
"LI", var_name, lb, &bounds_section);
907 if (ub != kInfinity) {
908 AppendMpsBound(
"UI", var_name, ub, &bounds_section);
914 AppendMpsBound(
"FX", var_name, lb, &bounds_section);
916 if (lb == -kInfinity) {
917 AppendMpsLineHeader(
"MI",
"BOUND", &bounds_section);
918 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
919 }
else if (lb != 0.0) {
920 AppendMpsBound(
"LO", var_name, lb, &bounds_section);
922 if (lb == 0.0 && ub == +kInfinity) {
923 AppendMpsLineHeader(
"PL",
"BOUND", &bounds_section);
924 absl::StrAppendFormat(&bounds_section,
" %s\n", var_name);
925 }
else if (ub != +kInfinity) {
926 AppendMpsBound(
"UP", var_name, ub, &bounds_section);
931 if (!bounds_section.empty()) {
932 absl::StrAppend(output,
"BOUNDS\n", bounds_section);
935 absl::StrAppend(output,
"ENDATA\n");
#define ASSIGN_OR_RETURN(lhs, rexpr)
CpModelProto proto
The output proto.
const std::string name
A name for logging purposes.
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)
absl::StatusOr< std::string > ExportModelAsLpFormat(const MPModelProto &model, const MPModelExportOptions &options)
absl::StatusOr< std::string > ExportModelAsMpsFormat(const MPModelProto &model, const MPModelExportOptions &options)
const std::optional< Range > & range