@@ -9,6 +9,7 @@ module MPS
99import .. FileFormats
1010
1111import MathOptInterface as MOI
12+ import DataStructures: OrderedDict
1213
1314const _NUM_TO_STRING = [string (i) for i in - 10 : 10 ]
1415
@@ -209,7 +210,8 @@ Write `model` to `io` in the MPS file format.
209210function Base. write (io:: IO , model:: Model )
210211 options = get_options (model)
211212 if options. generic_names
212- FileFormats. create_generic_names (model)
213+ # Generic variable names handled in this writer.
214+ FileFormats. create_generic_constraint_names (model)
213215 else
214216 FileFormats. create_unique_names (
215217 model;
@@ -218,11 +220,8 @@ function Base.write(io::IO, model::Model)
218220 )
219221 end
220222 variables = MOI. get (model, MOI. ListOfVariableIndices ())
221- ordered_names = Vector {String} (undef, length (variables))
222- var_to_column = Dict {MOI.VariableIndex,Int} ()
223+ var_to_column = OrderedDict {MOI.VariableIndex,Int} ()
223224 for (i, x) in enumerate (variables)
224- n = MOI. get (model, MOI. VariableName (), x)
225- ordered_names[i] = n
226225 var_to_column[x] = i
227226 end
228227 write_model_name (io, model)
@@ -237,20 +236,19 @@ function Base.write(io::IO, model::Model)
237236 flip_obj = MOI. get (model, MOI. ObjectiveSense ()) == MOI. MAX_SENSE
238237 end
239238 write_rows (io, model)
240- obj_const, indicators =
241- write_columns (io, model, flip_obj, ordered_names, var_to_column)
239+ obj_const, indicators = write_columns (io, model, flip_obj, var_to_column)
242240 write_rhs (io, model, obj_const)
243241 write_ranges (io, model)
244- write_bounds (io, model, ordered_names, var_to_column)
245- write_quadobj (io, model, ordered_names, var_to_column)
242+ write_bounds (io, model, var_to_column)
243+ write_quadobj (io, model, var_to_column)
246244 if options. quadratic_format != kQuadraticFormatCPLEX
247245 # Gurobi needs qcons _after_ quadobj and _before_ SOS.
248- write_quadcons (io, model, ordered_names, var_to_column)
246+ write_quadcons (io, model, var_to_column)
249247 end
250- write_sos (io, model, ordered_names, var_to_column)
248+ write_sos (io, model, var_to_column)
251249 if options. quadratic_format == kQuadraticFormatCPLEX
252250 # CPLEX needs qcons _after_ SOS.
253- write_quadcons (io, model, ordered_names, var_to_column)
251+ write_quadcons (io, model, var_to_column)
254252 end
255253 write_indicators (io, indicators)
256254 println (io, " ENDATA" )
@@ -388,7 +386,7 @@ function list_of_integer_variables(model::Model, var_to_column)
388386end
389387
390388function _extract_terms (
391- var_to_column:: Dict {MOI.VariableIndex,Int} ,
389+ var_to_column:: OrderedDict {MOI.VariableIndex,Int} ,
392390 coefficients:: Vector{Vector{Tuple{String,Float64}}} ,
393391 row_name:: String ,
394392 func:: MOI.ScalarAffineFunction ,
@@ -403,7 +401,7 @@ function _extract_terms(
403401end
404402
405403function _extract_terms (
406- var_to_column:: Dict {MOI.VariableIndex,Int} ,
404+ var_to_column:: OrderedDict {MOI.VariableIndex,Int} ,
407405 coefficients:: Vector{Vector{Tuple{String,Float64}}} ,
408406 row_name:: String ,
409407 func:: MOI.ScalarQuadraticFunction ,
@@ -421,7 +419,7 @@ function _collect_coefficients(
421419 model,
422420 :: Type{F} ,
423421 :: Type{S} ,
424- var_to_column:: Dict {MOI.VariableIndex,Int} ,
422+ var_to_column:: OrderedDict {MOI.VariableIndex,Int} ,
425423 coefficients:: Vector{Vector{Tuple{String,Float64}}} ,
426424) where {F,S}
427425 for index in MOI. get (model, MOI. ListOfConstraintIndices {F,S} ())
@@ -441,6 +439,7 @@ function _collect_indicator(
441439 coefficients,
442440 indicators,
443441) where {S}
442+ options = get_options (model)
444443 F = MOI. VectorAffineFunction{Float64}
445444 for index in MOI. get (model, MOI. ListOfConstraintIndices {F,S} ())
446445 row_name = MOI. get (model, MOI. ConstraintName (), index)
@@ -449,10 +448,8 @@ function _collect_indicator(
449448 z = convert (MOI. VariableIndex, funcs[1 ])
450449 _extract_terms (var_to_column, coefficients, row_name, funcs[2 ])
451450 condition = _activation_condition (S)
452- push! (
453- indicators,
454- (row_name, MOI. get (model, MOI. VariableName (), z), condition),
455- )
451+ var_name = _var_name (model, z, var_to_column[z], options. generic_names)
452+ push! (indicators, (row_name, var_name, condition))
456453 end
457454 return
458455end
@@ -468,16 +465,24 @@ function _extract_terms_objective(model, var_to_column, coefficients, flip_obj)
468465 return obj_func. constant
469466end
470467
471- function write_columns (
472- io:: IO ,
468+ function _var_name (
473469 model:: Model ,
474- flip_obj,
475- ordered_names,
476- var_to_column,
477- )
470+ variable:: MOI.VariableIndex ,
471+ column:: Int ,
472+ generic_name:: Bool ,
473+ ):: String
474+ if generic_name
475+ return " C$column "
476+ else
477+ return MOI. get (model, MOI. VariableName (), variable)
478+ end
479+ end
480+
481+ function write_columns (io:: IO , model:: Model , flip_obj, var_to_column)
482+ options = get_options (model)
478483 indicators = Tuple{String,String,MOI. ActivationCondition}[]
479484 coefficients = Vector{Tuple{String,Float64}}[
480- Tuple{String,Float64}[] for _ in ordered_names
485+ Tuple{String,Float64}[] for _ in 1 : length (var_to_column)
481486 ]
482487 # Build constraint coefficients
483488 # The functions and sets are given explicitly so that this function is
@@ -511,7 +516,8 @@ function write_columns(
511516 integer_variables = list_of_integer_variables (model, var_to_column)
512517 println (io, " COLUMNS" )
513518 int_open = false
514- for (column, variable) in enumerate (ordered_names)
519+ for (variable, column) in var_to_column
520+ var_name = _var_name (model, variable, column, options. generic_names)
515521 is_int = column in integer_variables
516522 if is_int && ! int_open
517523 println (io, Card (f2 = " MARKER" , f3 = " 'MARKER'" , f5 = " 'INTORG'" ))
@@ -523,13 +529,13 @@ function write_columns(
523529 if length (coefficients[column]) == 0
524530 # Every variable must appear in the COLUMNS section. Add a 0
525531 # objective coefficient instead.
526- println (io, Card (f2 = variable , f3 = " OBJ" , f4 = " 0" ))
532+ println (io, Card (f2 = var_name , f3 = " OBJ" , f4 = " 0" ))
527533 end
528534 for (constraint, coefficient) in coefficients[column]
529535 println (
530536 io,
531537 Card (
532- f2 = variable ,
538+ f2 = var_name ,
533539 f3 = constraint,
534540 f4 = _to_string (coefficient),
535541 ),
@@ -750,9 +756,10 @@ function _collect_bounds(bounds, model, ::Type{S}, var_to_column) where {S}
750756 return
751757end
752758
753- function write_bounds (io:: IO , model:: Model , ordered_names, var_to_column)
759+ function write_bounds (io:: IO , model:: Model , var_to_column)
760+ options = get_options (model)
754761 println (io, " BOUNDS" )
755- bounds = [(- Inf , Inf , VTYPE_CONTINUOUS) for _ in ordered_names ]
762+ bounds = [(- Inf , Inf , VTYPE_CONTINUOUS) for _ in 1 : length (var_to_column) ]
756763 @_unroll for S in (
757764 MOI. LessThan{Float64},
758765 MOI. GreaterThan{Float64},
@@ -762,7 +769,8 @@ function write_bounds(io::IO, model::Model, ordered_names, var_to_column)
762769 )
763770 _collect_bounds (bounds, model, S, var_to_column)
764771 end
765- for (column, var_name) in enumerate (ordered_names)
772+ for (variable, column) in var_to_column
773+ var_name = _var_name (model, variable, column, options. generic_names)
766774 lower, upper, vtype = bounds[column]
767775 if vtype == VTYPE_BINARY
768776 println (io, Card (f1 = " BV" , f2 = " bounds" , f3 = var_name))
782790# QUADRATIC OBJECTIVE
783791# ==============================================================================
784792
785- function write_quadobj (io:: IO , model:: Model , ordered_names, var_to_column)
793+ function write_quadobj (io:: IO , model:: Model , var_to_column)
786794 f = _get_objective (model)
787795 if isempty (f. quadratic_terms)
788796 return
@@ -798,8 +806,8 @@ function write_quadobj(io::IO, model::Model, ordered_names, var_to_column)
798806 end
799807 _write_q_matrix (
800808 io,
809+ model,
801810 f,
802- ordered_names,
803811 var_to_column;
804812 duplicate_off_diagonal = options. quadratic_format ==
805813 kQuadraticFormatCPLEX,
@@ -809,18 +817,20 @@ end
809817
810818function _write_q_matrix (
811819 io:: IO ,
820+ model:: Model ,
812821 f,
813- ordered_names,
814822 var_to_column;
815823 duplicate_off_diagonal:: Bool ,
816824)
825+ options = get_options (model)
817826 # Convert the quadratic terms into matrix form. We don't need to scale
818827 # because MOI uses the same Q/2 format as Gurobi, but we do need to ensure
819828 # we collate off-diagonal terms in the lower-triangular.
820- terms = Dict {Tuple{Int,Int },Float64} ()
829+ terms = Dict {Tuple{MOI.VariableIndex,MOI.VariableIndex },Float64} ()
821830 for term in f. quadratic_terms
822- x, y = var_to_column[term. variable_1], var_to_column[term. variable_2]
823- if x > y
831+ x = term. variable_1
832+ y = term. variable_2
833+ if var_to_column[x] > var_to_column[y]
824834 x, y = y, x
825835 end
826836 if haskey (terms, (x, y))
@@ -830,23 +840,20 @@ function _write_q_matrix(
830840 end
831841 end
832842 # Use sort for reproducibility, and so the Q matrix is given in order.
833- for (x, y) in sort! (collect (keys (terms)))
843+ for (x, y) in sort! (
844+ collect (keys (terms)),
845+ by = ((x, y),) -> (var_to_column[x], var_to_column[y]),
846+ )
847+ x_name = _var_name (model, x, var_to_column[x], options. generic_names)
848+ y_name = _var_name (model, y, var_to_column[y], options. generic_names)
834849 println (
835850 io,
836- Card (
837- f2 = ordered_names[x],
838- f3 = ordered_names[y],
839- f4 = _to_string (terms[(x, y)]),
840- ),
851+ Card (f2 = x_name, f3 = y_name, f4 = _to_string (terms[(x, y)])),
841852 )
842853 if x != y && duplicate_off_diagonal
843854 println (
844855 io,
845- Card (
846- f2 = ordered_names[y],
847- f3 = ordered_names[x],
848- f4 = _to_string (terms[(x, y)]),
849- ),
856+ Card (f2 = y_name, f3 = x_name, f4 = _to_string (terms[(x, y)])),
850857 )
851858 end
852859 end
857864# QUADRATIC CONSTRAINTS
858865# ==============================================================================
859866
860- function write_quadcons (io:: IO , model:: Model , ordered_names, var_to_column)
867+ function write_quadcons (io:: IO , model:: Model , var_to_column)
861868 options = get_options (model)
862869 F = MOI. ScalarQuadraticFunction{Float64}
863870 for S in (
@@ -876,8 +883,8 @@ function write_quadcons(io::IO, model::Model, ordered_names, var_to_column)
876883 f = MOI. get (model, MOI. ConstraintFunction (), ci)
877884 _write_q_matrix (
878885 io,
886+ model,
879887 f,
880- ordered_names,
881888 var_to_column;
882889 duplicate_off_diagonal = options. quadratic_format !=
883890 kQuadraticFormatMosek,
@@ -891,22 +898,22 @@ end
891898# SOS
892899# ==============================================================================
893900
894- function write_sos_constraint (
895- io:: IO ,
896- model:: Model ,
897- index,
898- ordered_names,
899- var_to_column,
900- )
901+ function write_sos_constraint (io:: IO , model:: Model , index, var_to_column)
902+ options = get_options (model)
901903 func = MOI. get (model, MOI. ConstraintFunction (), index)
902904 set = MOI. get (model, MOI. ConstraintSet (), index)
903905 for (variable, weight) in zip (func. variables, set. weights)
904- column = var_to_column[variable]
905- println (io, Card (f2 = ordered_names[column], f3 = _to_string (weight)))
906+ var_name = _var_name (
907+ model,
908+ variable,
909+ var_to_column[variable],
910+ options. generic_names,
911+ )
912+ println (io, Card (f2 = var_name, f3 = _to_string (weight)))
906913 end
907914end
908915
909- function write_sos (io:: IO , model:: Model , ordered_names, var_to_column)
916+ function write_sos (io:: IO , model:: Model , var_to_column)
910917 sos1_indices = MOI. get (
911918 model,
912919 MOI. ListOfConstraintIndices {MOI.VectorOfVariables,MOI.SOS1{Float64}} (),
@@ -921,13 +928,7 @@ function write_sos(io::IO, model::Model, ordered_names, var_to_column)
921928 for (sos_type, indices) in enumerate ([sos1_indices, sos2_indices])
922929 for index in indices
923930 println (io, Card (f1 = " S$(sos_type) " , f2 = " SOS$(idx) " ))
924- write_sos_constraint (
925- io,
926- model,
927- index,
928- ordered_names,
929- var_to_column,
930- )
931+ write_sos_constraint (io, model, index, var_to_column)
931932 idx += 1
932933 end
933934 end
0 commit comments