5
5
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6
6
7
7
mutable struct _CacheModel
8
+ is_binary:: Bool
8
9
cache:: Vector{UInt8}
9
10
variable_type:: Vector{_VariableType}
10
11
variable_primal:: Vector{Float64}
@@ -17,6 +18,7 @@ mutable struct _CacheModel
17
18
sense:: MOI.OptimizationSense
18
19
function _CacheModel ()
19
20
return new (
21
+ false ,
20
22
zeros (UInt8, 64 ),
21
23
_VariableType[],
22
24
Float64[],
@@ -95,12 +97,18 @@ function _next_token(::Type{T}, io::IO, cache::Vector{UInt8}) where {T}
95
97
end
96
98
97
99
function _next (:: Type{Float64} , io:: IO , model:: _CacheModel )
100
+ if model. is_binary
101
+ return read (io, Float64)
102
+ end
98
103
nnz = _next_token (Float64, io, model. cache)
99
104
@assert nnz > 0
100
105
return parse (Float64, String (model. cache[1 : nnz]))
101
106
end
102
107
103
108
function _next (:: Type{Int} , io:: IO , model:: _CacheModel )
109
+ if model. is_binary
110
+ return convert (Int, read (io, Int32))
111
+ end
104
112
nnz = _next_token (Int, io, model. cache)
105
113
@assert nnz > 0
106
114
y = 0
@@ -112,13 +120,27 @@ function _next(::Type{Int}, io::IO, model::_CacheModel)
112
120
return y
113
121
end
114
122
123
+ function _next (:: Type{Cchar} , io:: IO , model:: _CacheModel )
124
+ if model. is_binary
125
+ return read (io, Cchar)
126
+ end
127
+ byte = UInt8 (' ' )
128
+ while byte == UInt8 (' ' )
129
+ byte = read (io, UInt8)
130
+ end
131
+ return Cchar (byte)
132
+ end
133
+
115
134
"""
116
- _read_til_newline(io::IO)
135
+ _read_til_newline(io::IO, model::_CacheModel )
117
136
118
137
This function reads until it finds a new line character. This is useful for
119
138
skipping comments.
120
139
"""
121
- function _read_til_newline (io:: IO )
140
+ function _read_til_newline (io:: IO , model:: _CacheModel )
141
+ if model. is_binary
142
+ return
143
+ end
122
144
while read (io, UInt8) != UInt8 (' \n ' )
123
145
end
124
146
return
@@ -131,12 +153,12 @@ function _parse_expr(io::IO, model::_CacheModel)
131
153
char = Char (read (io, UInt8))
132
154
if char == ' o'
133
155
opcode = _next (Int, io, model)
134
- _read_til_newline (io)
156
+ _read_til_newline (io, model )
135
157
arity, op_func = _AMPL_TO_JULIA[opcode]
136
158
op_sym = Symbol (op_func)
137
159
if arity == - 1
138
160
arity = _next (Int, io, model)
139
- _read_til_newline (io)
161
+ _read_til_newline (io, model )
140
162
if op_sym == :sum
141
163
op_sym = :+
142
164
elseif op_sym == :minimum
@@ -153,12 +175,12 @@ function _parse_expr(io::IO, model::_CacheModel)
153
175
return parent
154
176
elseif char == ' v'
155
177
index = _next (Int, io, model)
156
- _read_til_newline (io)
178
+ _read_til_newline (io, model )
157
179
return MOI. VariableIndex (index + 1 )
158
180
else
159
181
@assert char == ' n'
160
182
ret = _next (Float64, io, model)
161
- _read_til_newline (io)
183
+ _read_til_newline (io, model )
162
184
return ret
163
185
end
164
186
end
@@ -270,13 +292,16 @@ function _parse_header(io::IO, model::_CacheModel)
270
292
# Line 1
271
293
# We don't support the binary format.
272
294
byte = read (io, UInt8)
273
- if byte != UInt8 (' g' )
295
+ is_binary = false
296
+ if byte == UInt8 (' b' )
297
+ is_binary = true
298
+ elseif byte != UInt8 (' g' )
274
299
error (" Unable to parse NL file : unsupported mode $(Char (byte)) " )
275
300
end
276
301
# L1 has some magic bytes for AMPL internals (to quote David, "The numbers
277
302
# on the first line matter to AMPL; for other uses, it is best simply to
278
303
# supply the ones shown above.")
279
- _read_til_newline (io)
304
+ _read_til_newline (io, model )
280
305
# Line 2
281
306
# The number of variables
282
307
n_var = _next (Int, io, model)
@@ -292,39 +317,39 @@ function _parse_header(io::IO, model::_CacheModel)
292
317
# The number of logical constraints. This one is optional, so just read til
293
318
# the end of the line.
294
319
# @assert _next(Int, io, model) == 0
295
- _read_til_newline (io)
320
+ _read_til_newline (io, model )
296
321
# Line 3
297
322
# The number of nonlinear constraints
298
323
@assert _next (Int, io, model) >= 0
299
324
# The number of nonlinear objectives
300
325
@assert 0 <= _next (Int, io, model) <= 1
301
- _read_til_newline (io)
326
+ _read_til_newline (io, model )
302
327
# Line 4
303
328
# The number of nonlinear network constraints
304
329
@assert _next (Int, io, model) == 0
305
330
# The number of linear network constraints
306
331
@assert _next (Int, io, model) == 0
307
- _read_til_newline (io)
332
+ _read_til_newline (io, model )
308
333
# Line 5
309
334
# The number of nonlienar variables in constraints
310
335
nlvc = _next (Int, io, model)
311
336
# The number of nonlienar variables in objectives
312
337
nlvo = _next (Int, io, model)
313
338
# The number of nonlienar variables in constraints and objectives (both)
314
339
nlvb = _next (Int, io, model)
315
- _read_til_newline (io)
340
+ _read_til_newline (io, model )
316
341
# Line 6
317
342
# The number of linear network variables
318
343
@assert _next (Int, io, model) == 0
319
344
# The number of user-defined functions
320
345
@assert _next (Int, io, model) == 0
321
346
# The number of "arith"
322
347
# TODO (odow): I don't know what this is.
323
- @assert _next (Int, io, model) == 0
348
+ _next (Int, io, model)
324
349
# The "flags" entry. This is mainly used for specifying that we want duals.
325
350
# Ignore when reading.
326
351
_next (Int, io, model)
327
- _read_til_newline (io)
352
+ _read_til_newline (io, model )
328
353
# Line 7
329
354
# Number of binary variables
330
355
nbv = _next (Int, io, model)
@@ -336,25 +361,25 @@ function _parse_header(io::IO, model::_CacheModel)
336
361
nlvci = _next (Int, io, model)
337
362
# Number of integer variables in nonlinear objectives
338
363
nlvoi = _next (Int, io, model)
339
- _read_til_newline (io)
364
+ _read_til_newline (io, model )
340
365
# Line 8
341
366
# Read the number of nonzeros in Jacobian and gradient, but don't do
342
367
# anything with that information.
343
368
@assert _next (Int, io, model) >= 0
344
369
@assert _next (Int, io, model) >= 0
345
- _read_til_newline (io)
370
+ _read_til_newline (io, model )
346
371
# Line 9
347
372
# We don't support reading variable and constraint names, so just ignore
348
373
# them
349
- _read_til_newline (io)
374
+ _read_til_newline (io, model )
350
375
# Line 10
351
376
# We don't support reading common subexpressions
352
377
for _ in 1 : 5
353
378
if _next (Int, io, model) > 0
354
379
error (" Unable to parse NL file : we don't support common exprs" )
355
380
end
356
381
end
357
- _read_til_newline (io)
382
+ _read_til_newline (io, model )
358
383
# ==========================================================================
359
384
# Deal with the integrality of variables. This is quite complicated, so go
360
385
# read the README in this folder.
@@ -387,6 +412,8 @@ function _parse_header(io::IO, model::_CacheModel)
387
412
model. variable_type[offset] = types[i]
388
413
end
389
414
end
415
+ # Delay setting is_binary until the end of the header section
416
+ model. is_binary = is_binary
390
417
return
391
418
end
392
419
@@ -412,10 +439,20 @@ end
412
439
function _parse_section (io:: IO , :: Val{'S'} , model:: _CacheModel )
413
440
k = _next (Int, io, model)
414
441
n = _next (Int, io, model)
415
- suffix = readline (io)
416
- @warn (" Skipping suffix: `S$k $n$suffix `" )
442
+ suffix_name = if model. is_binary
443
+ len = _next (Int, io, model)
444
+ String (read (io, len))
445
+ else
446
+ strip (readline (io))
447
+ end
448
+ @warn (" Skipping suffix: `S$k $n $suffix_name `" )
449
+ # The “4” bit of k indicates whether the suffix is real (that is, double)
450
+ # valued or integer valued: (k&4) != 0 --> real valued.
451
+ T = ifelse (k & 4 != 0 , Float64, Int)
417
452
for _ in 1 : n
418
- _read_til_newline (io)
453
+ _ = _next (Int, io, model)
454
+ _ = _next (T, io, model)
455
+ _read_til_newline (io, model)
419
456
end
420
457
return
421
458
end
440
477
441
478
function _parse_section (io:: IO , :: Val{'C'} , model:: _CacheModel )
442
479
index = _next (Int, io, model) + 1
443
- _read_til_newline (io)
480
+ _read_til_newline (io, model )
444
481
expr = _force_expr (_parse_expr (io, model))
445
482
current = model. constraints[index]
446
483
if current == :()
@@ -460,7 +497,7 @@ function _parse_section(io::IO, ::Val{'O'}, model::_CacheModel)
460
497
@assert sense == 0
461
498
model. sense = MOI. MIN_SENSE
462
499
end
463
- _read_til_newline (io)
500
+ _read_til_newline (io, model )
464
501
expr = _force_expr (_parse_expr (io, model))
465
502
if model. objective == :()
466
503
model. objective = expr
@@ -472,95 +509,99 @@ end
472
509
473
510
function _parse_section (io:: IO , :: Val{'x'} , model:: _CacheModel )
474
511
index = _next (Int, io, model)
475
- _read_til_newline (io)
512
+ _read_til_newline (io, model )
476
513
for _ in 1 : index
477
514
xi = _next (Int, io, model) + 1
478
515
v = _next (Float64, io, model)
479
516
model. variable_primal[xi] = v
480
- _read_til_newline (io)
517
+ _read_til_newline (io, model )
481
518
end
482
519
return
483
520
end
484
521
485
522
# TODO (odow): we don't read in dual starts.
486
523
function _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)
524
+ n = _next (Int, io, model)
525
+ _read_til_newline (io, model)
526
+ for _ in 1 : n
527
+ _ = _next (Int, io, model)
528
+ _ = _next (Float64, io, model)
529
+ _read_til_newline (io, model)
491
530
end
492
531
return
493
532
end
494
533
495
534
function _parse_section (io:: IO , :: Val{'r'} , model:: _CacheModel )
496
- _read_til_newline (io)
535
+ _read_til_newline (io, model )
497
536
for i in 1 : length (model. constraint_lower)
498
- type = _next (Int , io, model)
499
- if type == 0
537
+ type = _next (Cchar , io, model)
538
+ if type == Cchar ( ' 0 ' )
500
539
model. constraint_lower[i] = _next (Float64, io, model)
501
540
model. constraint_upper[i] = _next (Float64, io, model)
502
- elseif type == 1
541
+ elseif type == Cchar ( ' 1 ' )
503
542
model. constraint_upper[i] = _next (Float64, io, model)
504
- elseif type == 2
543
+ elseif type == Cchar ( ' 2 ' )
505
544
model. constraint_lower[i] = _next (Float64, io, model)
506
- elseif type == 3
545
+ elseif type == Cchar ( ' 3 ' )
507
546
# Free constraint
508
547
else
509
- @assert type == 4
548
+ @assert type == Cchar ( ' 4 ' )
510
549
value = _next (Float64, io, model)
511
550
model. constraint_lower[i] = value
512
551
model. constraint_upper[i] = value
513
552
end
514
- _read_til_newline (io)
553
+ _read_til_newline (io, model )
515
554
end
516
555
return
517
556
end
518
557
519
558
function _parse_section (io:: IO , :: Val{'b'} , model:: _CacheModel )
520
- _read_til_newline (io)
559
+ _read_til_newline (io, model )
521
560
for i in 1 : length (model. variable_lower)
522
- type = _next (Int , io, model)
523
- if type == 0
561
+ type = _next (Cchar , io, model)
562
+ if type == Cchar ( ' 0 ' )
524
563
model. variable_lower[i] = _next (Float64, io, model)
525
564
model. variable_upper[i] = _next (Float64, io, model)
526
- elseif type == 1
565
+ elseif type == Cchar ( ' 1 ' )
527
566
model. variable_upper[i] = _next (Float64, io, model)
528
- elseif type == 2
567
+ elseif type == Cchar ( ' 2 ' )
529
568
model. variable_lower[i] = _next (Float64, io, model)
530
- elseif type == 3
569
+ elseif type == Cchar ( ' 3 ' )
531
570
# Free variable
532
571
else
533
- @assert type == 4
572
+ @assert type == Cchar ( ' 4 ' )
534
573
value = _next (Float64, io, model)
535
574
model. variable_lower[i] = value
536
575
model. variable_upper[i] = value
537
576
end
538
- _read_til_newline (io)
577
+ _read_til_newline (io, model )
539
578
end
540
579
return
541
580
end
542
581
543
582
# We ignore jacobian counts for now
544
583
function _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)
584
+ n = _next (Int, io, model)
585
+ _read_til_newline (io, model)
586
+ for _ in 1 : n
587
+ _ = _next (Int, io, model)
588
+ _read_til_newline (io, model)
548
589
end
549
590
return
550
591
end
551
592
552
593
function _parse_section (io:: IO , :: Val{'J'} , model:: _CacheModel )
553
594
i = _next (Int, io, model) + 1
554
595
nnz = _next (Int, io, model)
555
- _read_til_newline (io)
596
+ _read_til_newline (io, model )
556
597
expr = Expr (:call , :+ )
557
598
for _ in 1 : nnz
558
599
x = _next (Int, io, model)
559
600
c = _next (Float64, io, model)
560
601
if ! iszero (c)
561
602
push! (expr. args, Expr (:call , :* , c, MOI. VariableIndex (x + 1 )))
562
603
end
563
- _read_til_newline (io)
604
+ _read_til_newline (io, model )
564
605
end
565
606
if length (expr. args) == 1
566
607
# Linear part is just zeros
@@ -575,15 +616,15 @@ end
575
616
function _parse_section (io:: IO , :: Val{'G'} , model:: _CacheModel )
576
617
i = _next (Int, io, model) + 1
577
618
nnz = _next (Int, io, model)
578
- _read_til_newline (io)
619
+ _read_til_newline (io, model )
579
620
expr = Expr (:call , :+ )
580
621
for _ in 1 : nnz
581
622
x = _next (Int, io, model)
582
623
c = _next (Float64, io, model)
583
624
if ! iszero (c)
584
625
push! (expr. args, Expr (:call , :* , c, MOI. VariableIndex (x + 1 )))
585
626
end
586
- _read_til_newline (io)
627
+ _read_til_newline (io, model )
587
628
end
588
629
if length (expr. args) == 1
589
630
# Linear part is just zeros
0 commit comments