55# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
66
77mutable struct _CacheModel
8+ is_binary:: Bool
89 cache:: Vector{UInt8}
910 variable_type:: Vector{_VariableType}
1011 variable_primal:: Vector{Float64}
@@ -17,6 +18,7 @@ mutable struct _CacheModel
1718 sense:: MOI.OptimizationSense
1819 function _CacheModel ()
1920 return new (
21+ false ,
2022 zeros (UInt8, 64 ),
2123 _VariableType[],
2224 Float64[],
@@ -95,12 +97,18 @@ function _next_token(::Type{T}, io::IO, cache::Vector{UInt8}) where {T}
9597end
9698
9799function _next (:: Type{Float64} , io:: IO , model:: _CacheModel )
100+ if model. is_binary
101+ return read (io, Float64)
102+ end
98103 nnz = _next_token (Float64, io, model. cache)
99104 @assert nnz > 0
100105 return parse (Float64, String (model. cache[1 : nnz]))
101106end
102107
103108function _next (:: Type{Int} , io:: IO , model:: _CacheModel )
109+ if model. is_binary
110+ return convert (Int, read (io, Int32))
111+ end
104112 nnz = _next_token (Int, io, model. cache)
105113 @assert nnz > 0
106114 y = 0
@@ -113,12 +121,15 @@ function _next(::Type{Int}, io::IO, model::_CacheModel)
113121end
114122
115123"""
116- _read_til_newline(io::IO)
124+ _read_til_newline(io::IO, model::_CacheModel )
117125
118126This function reads until it finds a new line character. This is useful for
119127skipping comments.
120128"""
121- function _read_til_newline (io:: IO )
129+ function _read_til_newline (io:: IO , model:: _CacheModel )
130+ if model. is_binary
131+ return
132+ end
122133 while read (io, UInt8) != UInt8 (' \n ' )
123134 end
124135 return
@@ -131,12 +142,12 @@ function _parse_expr(io::IO, model::_CacheModel)
131142 char = Char (read (io, UInt8))
132143 if char == ' o'
133144 opcode = _next (Int, io, model)
134- _read_til_newline (io)
145+ _read_til_newline (io, model )
135146 arity, op_func = _AMPL_TO_JULIA[opcode]
136147 op_sym = Symbol (op_func)
137148 if arity == - 1
138149 arity = _next (Int, io, model)
139- _read_til_newline (io)
150+ _read_til_newline (io, model )
140151 if op_sym == :sum
141152 op_sym = :+
142153 elseif op_sym == :minimum
@@ -153,12 +164,12 @@ function _parse_expr(io::IO, model::_CacheModel)
153164 return parent
154165 elseif char == ' v'
155166 index = _next (Int, io, model)
156- _read_til_newline (io)
167+ _read_til_newline (io, model )
157168 return MOI. VariableIndex (index + 1 )
158169 else
159170 @assert char == ' n'
160171 ret = _next (Float64, io, model)
161- _read_til_newline (io)
172+ _read_til_newline (io, model )
162173 return ret
163174 end
164175end
@@ -270,13 +281,16 @@ function _parse_header(io::IO, model::_CacheModel)
270281 # Line 1
271282 # We don't support the binary format.
272283 byte = read (io, UInt8)
273- if byte != UInt8 (' g' )
284+ is_binary = false
285+ if byte == UInt8 (' b' )
286+ is_binary = true
287+ elseif byte != UInt8 (' g' )
274288 error (" Unable to parse NL file : unsupported mode $(Char (byte)) " )
275289 end
276290 # L1 has some magic bytes for AMPL internals (to quote David, "The numbers
277291 # on the first line matter to AMPL; for other uses, it is best simply to
278292 # supply the ones shown above.")
279- _read_til_newline (io)
293+ _read_til_newline (io, model )
280294 # Line 2
281295 # The number of variables
282296 n_var = _next (Int, io, model)
@@ -292,39 +306,39 @@ function _parse_header(io::IO, model::_CacheModel)
292306 # The number of logical constraints. This one is optional, so just read til
293307 # the end of the line.
294308 # @assert _next(Int, io, model) == 0
295- _read_til_newline (io)
309+ _read_til_newline (io, model )
296310 # Line 3
297311 # The number of nonlinear constraints
298312 @assert _next (Int, io, model) >= 0
299313 # The number of nonlinear objectives
300314 @assert 0 <= _next (Int, io, model) <= 1
301- _read_til_newline (io)
315+ _read_til_newline (io, model )
302316 # Line 4
303317 # The number of nonlinear network constraints
304318 @assert _next (Int, io, model) == 0
305319 # The number of linear network constraints
306320 @assert _next (Int, io, model) == 0
307- _read_til_newline (io)
321+ _read_til_newline (io, model )
308322 # Line 5
309323 # The number of nonlienar variables in constraints
310324 nlvc = _next (Int, io, model)
311325 # The number of nonlienar variables in objectives
312326 nlvo = _next (Int, io, model)
313327 # The number of nonlienar variables in constraints and objectives (both)
314328 nlvb = _next (Int, io, model)
315- _read_til_newline (io)
329+ _read_til_newline (io, model )
316330 # Line 6
317331 # The number of linear network variables
318332 @assert _next (Int, io, model) == 0
319333 # The number of user-defined functions
320334 @assert _next (Int, io, model) == 0
321335 # The number of "arith"
322336 # TODO (odow): I don't know what this is.
323- @assert _next (Int, io, model) == 0
337+ _next (Int, io, model)
324338 # The "flags" entry. This is mainly used for specifying that we want duals.
325339 # Ignore when reading.
326340 _next (Int, io, model)
327- _read_til_newline (io)
341+ _read_til_newline (io, model )
328342 # Line 7
329343 # Number of binary variables
330344 nbv = _next (Int, io, model)
@@ -336,25 +350,25 @@ function _parse_header(io::IO, model::_CacheModel)
336350 nlvci = _next (Int, io, model)
337351 # Number of integer variables in nonlinear objectives
338352 nlvoi = _next (Int, io, model)
339- _read_til_newline (io)
353+ _read_til_newline (io, model )
340354 # Line 8
341355 # Read the number of nonzeros in Jacobian and gradient, but don't do
342356 # anything with that information.
343357 @assert _next (Int, io, model) >= 0
344358 @assert _next (Int, io, model) >= 0
345- _read_til_newline (io)
359+ _read_til_newline (io, model )
346360 # Line 9
347361 # We don't support reading variable and constraint names, so just ignore
348362 # them
349- _read_til_newline (io)
363+ _read_til_newline (io, model )
350364 # Line 10
351365 # We don't support reading common subexpressions
352366 for _ in 1 : 5
353367 if _next (Int, io, model) > 0
354368 error (" Unable to parse NL file : we don't support common exprs" )
355369 end
356370 end
357- _read_til_newline (io)
371+ _read_til_newline (io, model )
358372 # ==========================================================================
359373 # Deal with the integrality of variables. This is quite complicated, so go
360374 # read the README in this folder.
@@ -387,6 +401,8 @@ function _parse_header(io::IO, model::_CacheModel)
387401 model. variable_type[offset] = types[i]
388402 end
389403 end
404+ # Delay setting is_binary until the end of the header section
405+ model. is_binary = is_binary
390406 return
391407end
392408
@@ -415,7 +431,7 @@ function _parse_section(io::IO, ::Val{'S'}, model::_CacheModel)
415431 suffix = readline (io)
416432 @warn (" Skipping suffix: `S$k $n$suffix `" )
417433 for _ in 1 : n
418- _read_til_newline (io)
434+ _read_til_newline (io, model )
419435 end
420436 return
421437end
440456
441457function _parse_section (io:: IO , :: Val{'C'} , model:: _CacheModel )
442458 index = _next (Int, io, model) + 1
443- _read_til_newline (io)
459+ _read_til_newline (io, model )
444460 expr = _force_expr (_parse_expr (io, model))
445461 current = model. constraints[index]
446462 if current == :()
@@ -460,7 +476,7 @@ function _parse_section(io::IO, ::Val{'O'}, model::_CacheModel)
460476 @assert sense == 0
461477 model. sense = MOI. MIN_SENSE
462478 end
463- _read_til_newline (io)
479+ _read_til_newline (io, model )
464480 expr = _force_expr (_parse_expr (io, model))
465481 if model. objective == :()
466482 model. objective = expr
@@ -472,30 +488,36 @@ end
472488
473489function _parse_section (io:: IO , :: Val{'x'} , model:: _CacheModel )
474490 index = _next (Int, io, model)
475- _read_til_newline (io)
491+ _read_til_newline (io, model )
476492 for _ in 1 : index
477493 xi = _next (Int, io, model) + 1
478494 v = _next (Float64, io, model)
479495 model. variable_primal[xi] = v
480- _read_til_newline (io)
496+ _read_til_newline (io, model )
481497 end
482498 return
483499end
484500
485501# TODO (odow): we don't read in dual starts.
486502function _parse_section (io:: IO , :: Val{'d'} , model:: _CacheModel )
487- index = _next (Int, io, model)
488- _read_til_newline (io)
489- for _ in 1 : index
490- _read_til_newline (io)
503+ n = _next (Int, io, model)
504+ _read_til_newline (io, model)
505+ for _ in 1 : n
506+ _ = _next (Int, io, model)
507+ _ = _next (Float64, io, model)
508+ _read_til_newline (io, model)
491509 end
492510 return
493511end
494512
495513function _parse_section (io:: IO , :: Val{'r'} , model:: _CacheModel )
496- _read_til_newline (io)
514+ _read_til_newline (io, model )
497515 for i in 1 : length (model. constraint_lower)
498- type = _next (Int, io, model)
516+ type = if model. is_binary
517+ parse (Int, read (io, Char))
518+ else
519+ _next (Int, io, model)
520+ end
499521 if type == 0
500522 model. constraint_lower[i] = _next (Float64, io, model)
501523 model. constraint_upper[i] = _next (Float64, io, model)
@@ -511,15 +533,19 @@ function _parse_section(io::IO, ::Val{'r'}, model::_CacheModel)
511533 model. constraint_lower[i] = value
512534 model. constraint_upper[i] = value
513535 end
514- _read_til_newline (io)
536+ _read_til_newline (io, model )
515537 end
516538 return
517539end
518540
519541function _parse_section (io:: IO , :: Val{'b'} , model:: _CacheModel )
520- _read_til_newline (io)
542+ _read_til_newline (io, model )
521543 for i in 1 : length (model. variable_lower)
522- type = _next (Int, io, model)
544+ type = if model. is_binary
545+ parse (Int, read (io, Char))
546+ else
547+ _next (Int, io, model)
548+ end
523549 if type == 0
524550 model. variable_lower[i] = _next (Float64, io, model)
525551 model. variable_upper[i] = _next (Float64, io, model)
@@ -535,32 +561,34 @@ function _parse_section(io::IO, ::Val{'b'}, model::_CacheModel)
535561 model. variable_lower[i] = value
536562 model. variable_upper[i] = value
537563 end
538- _read_til_newline (io)
564+ _read_til_newline (io, model )
539565 end
540566 return
541567end
542568
543569# We ignore jacobian counts for now
544570function _parse_section (io:: IO , :: Val{'k'} , model:: _CacheModel )
545- _read_til_newline (io)
546- for _ in 2 : length (model. variable_lower)
547- _read_til_newline (io)
571+ n = _next (Int, io, model)
572+ _read_til_newline (io, model)
573+ for _ in 1 : n
574+ _ = _next (Int, io, model)
575+ _read_til_newline (io, model)
548576 end
549577 return
550578end
551579
552580function _parse_section (io:: IO , :: Val{'J'} , model:: _CacheModel )
553581 i = _next (Int, io, model) + 1
554582 nnz = _next (Int, io, model)
555- _read_til_newline (io)
583+ _read_til_newline (io, model )
556584 expr = Expr (:call , :+ )
557585 for _ in 1 : nnz
558586 x = _next (Int, io, model)
559587 c = _next (Float64, io, model)
560588 if ! iszero (c)
561589 push! (expr. args, Expr (:call , :* , c, MOI. VariableIndex (x + 1 )))
562590 end
563- _read_til_newline (io)
591+ _read_til_newline (io, model )
564592 end
565593 if length (expr. args) == 1
566594 # Linear part is just zeros
@@ -575,15 +603,15 @@ end
575603function _parse_section (io:: IO , :: Val{'G'} , model:: _CacheModel )
576604 i = _next (Int, io, model) + 1
577605 nnz = _next (Int, io, model)
578- _read_til_newline (io)
606+ _read_til_newline (io, model )
579607 expr = Expr (:call , :+ )
580608 for _ in 1 : nnz
581609 x = _next (Int, io, model)
582610 c = _next (Float64, io, model)
583611 if ! iszero (c)
584612 push! (expr. args, Expr (:call , :* , c, MOI. VariableIndex (x + 1 )))
585613 end
586- _read_til_newline (io)
614+ _read_til_newline (io, model )
587615 end
588616 if length (expr. args) == 1
589617 # Linear part is just zeros
0 commit comments