Skip to content

Commit 5349cab

Browse files
committed
Update
1 parent 9b9c175 commit 5349cab

File tree

2 files changed

+78
-37
lines changed

2 files changed

+78
-37
lines changed

src/FileFormats/LP/read.jl

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ const _KEYWORDS = Dict(
133133
_TOKEN_LESS_THAN,
134134
_TOKEN_EQUAL_TO,
135135
_TOKEN_COLON,
136+
_TOKEN_IMPLIES,
136137
_TOKEN_NEWLINE,
137138
_TOKEN_UNKNOWN,
138139
)
@@ -270,8 +271,11 @@ function _peek_inner(state::LexerState)
270271
end
271272
return Token(_TOKEN_IDENTIFIER, val)
272273
elseif (op = get(_OPERATORS, c, nothing)) !== nothing
273-
read(state, Char)
274-
if c in ('<', '>', '=') && peek(state, Char) == '='
274+
read(state, Char) # Skip c
275+
if c == '-' && peek(state, Char) == '>'
276+
read(state, Char)
277+
return Token(_TOKEN_IMPLIES, "->")
278+
elseif c in ('<', '>', '=') && peek(state, Char) == '='
275279
read(state, Char) # Allow <=, >=, and ==
276280
end
277281
return Token(op, string(c))
@@ -718,14 +722,52 @@ function _is_sos_constraint(state)
718722
_next_token_is(state, _TOKEN_COLON, 3)
719723
end
720724

725+
function _is_indicator_constraint(state)
726+
return _next_token_is(state, _TOKEN_IDENTIFIER, 1) &&
727+
_next_token_is(state, _TOKEN_EQUAL_TO, 2) &&
728+
_next_token_is(state, _TOKEN_NUMBER, 3) &&
729+
_next_token_is(state, _TOKEN_IMPLIES, 4)
730+
end
731+
732+
# INDICATOR_CONSTRAINT :=
733+
# IDENTIFIER "=" "0" "->" EXPRESSION SET_SUFFIX
734+
# | IDENTIFIER "=" "1" "->" EXPRESSION SET_SUFFIX
735+
function _parse_indicator_constraint(
736+
state::LexerState,
737+
cache::Cache{T},
738+
) where {T}
739+
z = _parse_variable(state, cache)
740+
_expect(read(state, Token), _TOKEN_EQUAL_TO)
741+
t = read(state, Token)
742+
_expect(t, _TOKEN_NUMBER)
743+
indicator = if t.value == "0"
744+
MOI.ACTIVATE_ON_ZERO
745+
elseif t.value == "1"
746+
MOI.ACTIVATE_ON_ONE
747+
else
748+
throw(UnexpectedToken(t))
749+
end
750+
_expect(read(state, Token), _TOKEN_IMPLIES)
751+
f = _parse_expression(state, cache)
752+
set = _parse_set_suffix(state, cache)
753+
return MOI.add_constraint(
754+
cache.model,
755+
MOI.Utilities.operate(vcat, T, z, f),
756+
MOI.Indicator{indicator}(set),
757+
)
758+
end
759+
721760
# CONSTRAINT :=
722761
# [NAME] EXPRESSION SET_SUFFIX
723762
# | [NAME] SOS_CONSTRAINT
763+
# | [NAME] INDICATOR_CONSTRAINT
724764
function _parse_constraint(state::LexerState, cache::Cache)
725765
name = _parse_optional_name(state, cache)
726766
# Check if this is an SOS constraint
727767
c = if _is_sos_constraint(state)
728768
_parse_sos_constraint(state, cache)
769+
elseif _is_indicator_constraint(state)
770+
_parse_indicator_constraint(state, cache)
729771
else
730772
f = _parse_expression(state, cache)
731773
set = _parse_set_suffix(state, cache)

test/FileFormats/LP/LP.jl

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,41 +1034,40 @@ function test_read_variable_bounds()
10341034
return
10351035
end
10361036

1037-
# TODO(odow): FIXME
1038-
# function test_read_indicator()
1039-
# io = IOBuffer("""
1040-
# minimize
1041-
# obj: 1 x
1042-
# subject to
1043-
# c: z = 1 -> x >= 0
1044-
# d: z = 0 -> x - y <= 1.2
1045-
# bounds
1046-
# x free
1047-
# z free
1048-
# binary
1049-
# z
1050-
# end
1051-
# """)
1052-
# model = MOI.FileFormats.Model(format = MOI.FileFormats.FORMAT_LP)
1053-
# read!(io, model)
1054-
# io = IOBuffer()
1055-
# write(io, model)
1056-
# seekstart(io)
1057-
# @test read(io, String) == """
1058-
# minimize
1059-
# obj: 1 x
1060-
# subject to
1061-
# d: z = 0 -> 1 x - 1 y <= 1.2
1062-
# c: z = 1 -> 1 x >= 0
1063-
# Bounds
1064-
# x free
1065-
# y >= 0
1066-
# Binary
1067-
# z
1068-
# End
1069-
# """
1070-
# return
1071-
# end
1037+
function test_read_indicator()
1038+
io = IOBuffer("""
1039+
minimize
1040+
obj: 1 x
1041+
subject to
1042+
c: z = 1 -> x >= 0
1043+
d: z = 0 -> x - y <= 1.2
1044+
bounds
1045+
x free
1046+
z free
1047+
binary
1048+
z
1049+
end
1050+
""")
1051+
model = MOI.FileFormats.Model(format = MOI.FileFormats.FORMAT_LP)
1052+
read!(io, model)
1053+
io = IOBuffer()
1054+
write(io, model)
1055+
seekstart(io)
1056+
@test read(io, String) == """
1057+
minimize
1058+
obj: 1 x
1059+
subject to
1060+
d: z = 0 -> 1 x - 1 y <= 1.2
1061+
c: z = 1 -> 1 x >= 0
1062+
Bounds
1063+
x free
1064+
y >= 0
1065+
Binary
1066+
z
1067+
End
1068+
"""
1069+
return
1070+
end
10721071

10731072
function test_VectorAffineFunction_SOS()
10741073
model = MOI.FileFormats.LP.Model()

0 commit comments

Comments
 (0)