diff --git a/docs/src/submodules/FileFormats/overview.md b/docs/src/submodules/FileFormats/overview.md index b7d77c4318..64a56a2c48 100644 --- a/docs/src/submodules/FileFormats/overview.md +++ b/docs/src/submodules/FileFormats/overview.md @@ -84,7 +84,7 @@ julia> print(read("file.mof.json", String)) "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 5 + "minor": 6 }, "variables": [ { diff --git a/src/FileFormats/MOF/MOF.jl b/src/FileFormats/MOF/MOF.jl index 332cea9d67..0db934f969 100644 --- a/src/FileFormats/MOF/MOF.jl +++ b/src/FileFormats/MOF/MOF.jl @@ -7,7 +7,7 @@ import JSONSchema import MathOptInterface const MOI = MathOptInterface -const SCHEMA_PATH = joinpath(@__DIR__, "mof.0.5.schema.json") +const SCHEMA_PATH = joinpath(@__DIR__, "mof.0.6.schema.json") const VERSION = let data = JSON.parsefile(SCHEMA_PATH, use_mmap = false) VersionNumber( data["properties"]["version"]["properties"]["major"]["const"], diff --git a/src/FileFormats/MOF/mof.0.5.schema.json b/src/FileFormats/MOF/mof.0.6.schema.json similarity index 96% rename from src/FileFormats/MOF/mof.0.5.schema.json rename to src/FileFormats/MOF/mof.0.6.schema.json index 42630b1deb..031e10ab37 100644 --- a/src/FileFormats/MOF/mof.0.5.schema.json +++ b/src/FileFormats/MOF/mof.0.6.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/schema#", - "$id": "https://jump.dev/MathOptFormat/schemas/mof.0.5.schema.json", + "$id": "https://jump.dev/MathOptFormat/schemas/mof.0.6.schema.json", "title": "The schema for MathOptFormat", "type": "object", "required": ["version", "variables", "objective", "constraints"], @@ -11,7 +11,7 @@ "required": ["minor", "major"], "properties": { "minor": { - "const": 5 + "const": 6 }, "major": { "const": 0 @@ -859,6 +859,19 @@ "minimum": 1 } } + }, { + "description": "The set corresponding to a mixed complementarity constraint. Complementarity constraints should be specified with an AbstractVectorFunction-in-Complements(dimension) constraint. The dimension of the vector-valued function `F` must be `dimension`. This defines a complementarity constraint between the scalar function `F[i]` and the variable in `F[i + dimension/2]`. Thus, `F[i + dimension/2]` must be interpretable as a single variable `x_i` (e.g., `1.0 * x + 0.0`). The mixed complementarity problem consists of finding `x_i` in the interval `[lb, ub]` (i.e., in the set `Interval(lb, ub)`), such that the following holds: 1. `F_i(x) == 0` if `lb_i < x_i < ub_i`; 2. `F_i(x) >= 0` if `lb_i == x_i`; 3. `F_i(x) <= 0` if `x_i == ub_i`. Classically, the bounding set for `x_i` is `Interval(0, Inf)`, which recovers: `0 <= F_i(x) ⟂ x_i >= 0`, where the `⟂` operator implies `F_i(x) * x_i = 0`.", + "examples": ["{\"type\": \"Complements\", \"dimension\": 2}"], + "required": ["dimension"], + "properties": { + "type": { + "const": "Complements" + }, + "dimension": { + "type": "integer", + "minimum": 2 + } + } }] } } diff --git a/src/FileFormats/MOF/read.jl b/src/FileFormats/MOF/read.jl index 1568ac58ca..0bbe4a7c50 100644 --- a/src/FileFormats/MOF/read.jl +++ b/src/FileFormats/MOF/read.jl @@ -316,6 +316,7 @@ end SOS1, SOS2, IndicatorSet, + Complements, ) """ @@ -450,6 +451,10 @@ function set_to_moi(::Val{:DualExponentialCone}, ::Object) return MOI.DualExponentialCone() end +function set_to_moi(::Val{:Complements}, object::Object) + return MOI.Complements(div(object["dimension"], 2)) +end + # ========== Typed vector sets ========== function set_to_moi(::Val{:PowerCone}, object::Object) diff --git a/src/FileFormats/MOF/write.jl b/src/FileFormats/MOF/write.jl index f2417e48d1..2c3b893e53 100644 --- a/src/FileFormats/MOF/write.jl +++ b/src/FileFormats/MOF/write.jl @@ -302,3 +302,10 @@ function moi_to_object( "activate_on" => (I == MOI.ACTIVATE_ON_ONE) ? "one" : "zero", ) end + +function moi_to_object(set::MOI.Complements, ::Dict{MOI.VariableIndex,String}) + return OrderedObject( + "type" => "Complements", + "dimension" => MOI.dimension(set), + ) +end diff --git a/test/FileFormats/MOF/MOF.jl b/test/FileFormats/MOF/MOF.jl index ea55374620..072e284b70 100644 --- a/test/FileFormats/MOF/MOF.jl +++ b/test/FileFormats/MOF/MOF.jl @@ -388,6 +388,13 @@ end ["c1"], ) end + @testset "Complements" begin + test_model_equality( + "variables: x, y\nc1: [x, y] in Complements(1)", + ["x", "y"], + ["c1"], + ) + end @testset "vectoraffine-in-zeros" begin test_model_equality( """ diff --git a/test/FileFormats/MOF/empty_model.mof.json b/test/FileFormats/MOF/empty_model.mof.json index 9ffe78963e..fd2bdbb2e4 100644 --- a/test/FileFormats/MOF/empty_model.mof.json +++ b/test/FileFormats/MOF/empty_model.mof.json @@ -2,7 +2,7 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 5 + "minor": 6 }, "variables": [], "objective": {"sense": "feasibility"}, diff --git a/test/FileFormats/MOF/empty_model.mof.json.gz b/test/FileFormats/MOF/empty_model.mof.json.gz deleted file mode 100644 index f5bd8e4089..0000000000 Binary files a/test/FileFormats/MOF/empty_model.mof.json.gz and /dev/null differ diff --git a/test/FileFormats/MOF/failing_models/blank_variable_name.mof.json b/test/FileFormats/MOF/failing_models/blank_variable_name.mof.json index 5db754148a..6087061742 100644 --- a/test/FileFormats/MOF/failing_models/blank_variable_name.mof.json +++ b/test/FileFormats/MOF/failing_models/blank_variable_name.mof.json @@ -2,7 +2,7 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 5 + "minor": 6 }, "variables": [{"name": ""}], "objective": { diff --git a/test/FileFormats/MOF/failing_models/missing_variable_name.mof.json b/test/FileFormats/MOF/failing_models/missing_variable_name.mof.json index b31f23d407..109994f1ce 100644 --- a/test/FileFormats/MOF/failing_models/missing_variable_name.mof.json +++ b/test/FileFormats/MOF/failing_models/missing_variable_name.mof.json @@ -2,7 +2,7 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 5 + "minor": 6 }, "variables": [{}], "objective": { diff --git a/test/FileFormats/MOF/failing_models/unsupported_constraint_function.mof.json b/test/FileFormats/MOF/failing_models/unsupported_constraint_function.mof.json index 1dea9c4210..339d4291c6 100644 --- a/test/FileFormats/MOF/failing_models/unsupported_constraint_function.mof.json +++ b/test/FileFormats/MOF/failing_models/unsupported_constraint_function.mof.json @@ -2,7 +2,7 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 5 + "minor": 6 }, "variables": [{"name": "x"}], "objective": { diff --git a/test/FileFormats/MOF/failing_models/unsupported_constraint_set.mof.json b/test/FileFormats/MOF/failing_models/unsupported_constraint_set.mof.json index e7c05c577c..9d9ba2fc5a 100644 --- a/test/FileFormats/MOF/failing_models/unsupported_constraint_set.mof.json +++ b/test/FileFormats/MOF/failing_models/unsupported_constraint_set.mof.json @@ -2,7 +2,7 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 5 + "minor": 6 }, "variables": [{"name": "x"}], "objective": { diff --git a/test/FileFormats/MOF/failing_models/unsupported_objective_function.mof.json b/test/FileFormats/MOF/failing_models/unsupported_objective_function.mof.json index b1c0806cf5..137e534849 100644 --- a/test/FileFormats/MOF/failing_models/unsupported_objective_function.mof.json +++ b/test/FileFormats/MOF/failing_models/unsupported_objective_function.mof.json @@ -2,7 +2,7 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 5 + "minor": 6 }, "variables": [{"name": "x"}], "objective": { diff --git a/test/FileFormats/MOF/failing_models/unsupported_objective_sense.mof.json b/test/FileFormats/MOF/failing_models/unsupported_objective_sense.mof.json index 7307a3add7..7fca972376 100644 --- a/test/FileFormats/MOF/failing_models/unsupported_objective_sense.mof.json +++ b/test/FileFormats/MOF/failing_models/unsupported_objective_sense.mof.json @@ -2,7 +2,7 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 5 + "minor": 6 }, "variables": [{"name": "x"}], "objective": { diff --git a/test/FileFormats/MOF/nlp.mof.json b/test/FileFormats/MOF/nlp.mof.json index 7e2633822e..0c7c6cbe5c 100644 --- a/test/FileFormats/MOF/nlp.mof.json +++ b/test/FileFormats/MOF/nlp.mof.json @@ -2,7 +2,7 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 5 + "minor": 6 }, "variables": [ {