diff --git a/src/FileFormats/MOF/MOF.jl b/src/FileFormats/MOF/MOF.jl index 88cef7841f..d6c77103b1 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__, "v0.4.0.json") +const SCHEMA_PATH = joinpath(@__DIR__, "mof.0.5.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/v0.4.0.json b/src/FileFormats/MOF/mof.0.5.schema.json similarity index 87% rename from src/FileFormats/MOF/v0.4.0.json rename to src/FileFormats/MOF/mof.0.5.schema.json index 149796a59b..42630b1deb 100644 --- a/src/FileFormats/MOF/v0.4.0.json +++ b/src/FileFormats/MOF/mof.0.5.schema.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/schema#", - "$id": "https://github.com/odow/MathOptFormat/blob/master/mof.schema.json", + "$id": "https://jump.dev/MathOptFormat/schemas/mof.0.5.schema.json", "title": "The schema for MathOptFormat", "type": "object", "required": ["version", "variables", "objective", "constraints"], @@ -11,7 +11,7 @@ "required": ["minor", "major"], "properties": { "minor": { - "const": 4 + "const": 5 }, "major": { "const": 0 @@ -170,12 +170,12 @@ "NonlinearTerm": { "description": "A node in an expresion graph representing a nonlinear function.", "type": "object", - "required": ["head"], + "required": ["type"], "oneOf": [{ "description": "Unary operators", "required": ["args"], "properties": { - "head": { + "type": { "enum": [ "log", "log10", "exp", "sqrt", "floor", "ceil", "abs", "cos", "sin", "tan", "acos", "asin", "atan", @@ -195,7 +195,7 @@ "description": "Binary operators", "required": ["args"], "properties": { - "head": { + "type": { "enum": ["/", "^"] }, "args": { @@ -211,7 +211,7 @@ "description": "N-ary operators", "required": ["args"], "properties": { - "head": { + "type": { "enum": ["+", "-", "*", "min", "max"] }, "args": { @@ -224,10 +224,10 @@ } }, { "description": "A real-valued numeric constant", - "examples": ["{\"head\": \"real\", \"value\": 1.0}"], + "examples": ["{\"type\": \"real\", \"value\": 1.0}"], "required": ["value"], "properties": { - "head": { + "type": { "const": "real" }, "value": { @@ -236,10 +236,10 @@ } }, { "description": "A complex-valued numeric constant", - "examples": ["{\"head\": \"complex\", \"real\": 1.0, \"imag\": 2.0}"], + "examples": ["{\"type\": \"complex\", \"real\": 1.0, \"imag\": 2.0}"], "required": ["real", "imag"], "properties": { - "head": { + "type": { "const": "complex" }, "real": { @@ -251,10 +251,10 @@ } }, { "description": "A reference to an optimization variable", - "examples": ["{\"head\": \"variable\", \"name\": \"x\"}"], + "examples": ["{\"type\": \"variable\", \"name\": \"x\"}"], "required": ["name"], "properties": { - "head": { + "type": { "const": "variable" }, "name": { @@ -263,10 +263,10 @@ } }, { "description": "A pointer to a (1-indexed) element in the `node_list` field in a nonlinear function", - "examples": ["{\"head\": \"node\", \"index\": 2}"], + "examples": ["{\"type\": \"node\", \"index\": 2}"], "required": ["index"], "properties": { - "head": { + "type": { "const": "node" }, "index": { @@ -279,13 +279,13 @@ "scalar_functions": { "description": "A schema for the scalar-valued functions defined by MathOptFormat.See http://www.juliaopt.org/MathOptInterface.jl/v0.8/apireference/#Functions-and-function-modifications-1 for a list of the functions and their meanings.", "type": "object", - "required": ["head"], + "required": ["type"], "oneOf": [{ "description": "The scalar variable `variable`.", - "examples": ["{\"head\": \"SingleVariable\", \"variable\": \"x\"}"], + "examples": ["{\"type\": \"SingleVariable\", \"variable\": \"x\"}"], "required": ["variable"], "properties": { - "head": { + "type": { "const": "SingleVariable" }, "variable": { @@ -294,10 +294,10 @@ } }, { "description": "The function `a'x + b`, where `a` is a sparse vector specified by a list of `ScalarAffineTerm`s in `terms` and `b` is the scalar in `constant`. Duplicate variables in `terms` are accepted, and the corresponding coefficients are summed together.", - "examples": ["{\"head\": \"ScalarAffineFunction\", \"constant\": 1.0, \"terms\": [{\"coefficient\": 2.5, \"variable\": \"x\"}]}"], + "examples": ["{\"type\": \"ScalarAffineFunction\", \"constant\": 1.0, \"terms\": [{\"coefficient\": 2.5, \"variable\": \"x\"}]}"], "required": ["constant", "terms"], "properties": { - "head": { + "type": { "const": "ScalarAffineFunction" }, "constant": { @@ -312,10 +312,10 @@ } }, { "description": "The function `0.5x'Qx + a'x + b`, where `a` is a sparse vector of `ScalarAffineTerm`s in `affine_terms`, `b` is the scalar `constant`, and `Q` is a symmetric matrix specified by a list of `ScalarQuadraticTerm`s in `quadratic_terms`. Duplicate indices in `affine_terms` and `quadratic` are accepted, and the corresponding coefficients are summed together. Mirrored indices in `quadratic_terms` (i.e., `(i,j)` and `(j, i)`) are considered duplicates; only one need to be specified.", - "examples": ["{\"head\": \"ScalarQuadraticFunction\", \"constant\": 1.0, \"affine_terms\": [{\"coefficient\": 2.5, \"variable\": \"x\"}], \"quadratic_terms\": [{\"coefficient\": 2.0, \"variable_1\": \"x\", \"variable_2\": \"y\"}]}"], + "examples": ["{\"type\": \"ScalarQuadraticFunction\", \"constant\": 1.0, \"affine_terms\": [{\"coefficient\": 2.5, \"variable\": \"x\"}], \"quadratic_terms\": [{\"coefficient\": 2.0, \"variable_1\": \"x\", \"variable_2\": \"y\"}]}"], "required": ["constant", "affine_terms", "quadratic_terms"], "properties": { - "head": { + "type": { "const": "ScalarQuadraticFunction" }, "constant": { @@ -338,7 +338,7 @@ "description": "An expression graph representing a scalar nonlinear function.", "required": ["root", "node_list"], "properties": { - "head": { + "type": { "const": "ScalarNonlinearFunction" }, "root": { @@ -356,13 +356,13 @@ "vector_functions": { "description": "A schema for the vector-valued functions defined by MathOptFormat.See http://www.juliaopt.org/MathOptInterface.jl/v0.8/apireference/#Functions-and-function-modifications-1 for a list of the functions and their meanings.", "type": "object", - "required": ["head"], + "required": ["type"], "oneOf": [{ "description": "An ordered list of variables.", - "examples": ["{\"head\": \"VectorOfVariables\", \"variables\": [\"x\", \"y\"]}"], + "examples": ["{\"type\": \"VectorOfVariables\", \"variables\": [\"x\", \"y\"]}"], "required": ["variables"], "properties": { - "head": { + "type": { "const": "VectorOfVariables" }, "variables": { @@ -374,10 +374,10 @@ } }, { "description": "The function `Ax + b`, where `A` is a sparse matrix specified by a list of `VectorAffineTerm`s in `terms` and `b` is a dense vector specified by `constants`.", - "examples": ["{\"head\": \"VectorAffineFunction\", \"constants\": [1.0], \"terms\": [{\"output_index\": 1, \"scalar_term\": {\"coefficient\": 2.5, \"variable\": \"x\"}}]}"], + "examples": ["{\"type\": \"VectorAffineFunction\", \"constants\": [1.0], \"terms\": [{\"output_index\": 1, \"scalar_term\": {\"coefficient\": 2.5, \"variable\": \"x\"}}]}"], "required": ["constants", "terms"], "properties": { - "head": { + "type": { "const": "VectorAffineFunction" }, "constants": { @@ -397,7 +397,7 @@ "description": "The vector-valued quadratic function `q(x) + Ax + b`, where `q(x)` is specified by a list of `VectorQuadraticTerm`s in `quadratic_terms`, `A` is a sparse matrix specified by a list of `VectorAffineTerm`s in `affine_terms` and `b` is a dense vector specified by `constants`.", "required": ["constants", "affine_terms", "quadratic_terms"], "properties": { - "head": { + "type": { "const": "VectorQuadraticFunction" }, "constants": { @@ -424,13 +424,13 @@ "scalar_sets": { "description": "A schema for the scalar-valued sets defined by MathOptFormat. See http: //www.juliaopt.org/MathOptInterface.jl/v0.8/apireference/#Sets-1 for a list of the sets and their meanings.", "type": "object", - "required": ["head"], + "required": ["type"], "oneOf": [{ "description": "(-∞, upper]", - "examples": ["{\"head\": \"LessThan\", \"upper\": 2.1}"], + "examples": ["{\"type\": \"LessThan\", \"upper\": 2.1}"], "required": ["upper"], "properties": { - "head": { + "type": { "const": "LessThan" }, "upper": { @@ -439,10 +439,10 @@ } }, { "description": "[lower, ∞)", - "examples": ["{\"head\": \"GreaterThan\", \"lower\": 2.1}"], + "examples": ["{\"type\": \"GreaterThan\", \"lower\": 2.1}"], "required": ["lower"], "properties": { - "head": { + "type": { "const": "GreaterThan" }, "lower": { @@ -451,10 +451,10 @@ } }, { "description": "{value}", - "examples": ["{\"head\": \"EqualTo\", \"value\": 2.1}"], + "examples": ["{\"type\": \"EqualTo\", \"value\": 2.1}"], "required": ["value"], "properties": { - "head": { + "type": { "const": "EqualTo" }, "value": { @@ -463,10 +463,10 @@ } }, { "description": "[lower, upper]", - "examples": ["{\"head\": \"Interval\", \"lower\": 2.1, \"upper\": 3.4}"], + "examples": ["{\"type\": \"Interval\", \"lower\": 2.1, \"upper\": 3.4}"], "required": ["lower", "upper"], "properties": { - "head": { + "type": { "const": "Interval" }, "lower": { @@ -478,10 +478,10 @@ } }, { "description": "{0} ∪ {lower, lower + 1, ..., upper}", - "examples": ["{\"head\": \"Semiinteger\", \"lower\": 2, \"upper\": 4}"], + "examples": ["{\"type\": \"Semiinteger\", \"lower\": 2, \"upper\": 4}"], "required": ["lower", "upper"], "properties": { - "head": { + "type": { "const": "Semiinteger" }, "lower": { @@ -493,10 +493,10 @@ } }, { "description": "{0} ∪ [lower, upper]", - "examples": ["{\"head\": \"Semicontinuous\", \"lower\": 2.1, \"upper\": 3.4}"], + "examples": ["{\"type\": \"Semicontinuous\", \"lower\": 2.1, \"upper\": 3.4}"], "required": ["lower", "upper"], "properties": { - "head": { + "type": { "const": "Semicontinuous" }, "lower": { @@ -508,17 +508,17 @@ } }, { "description": "{0, 1}", - "examples": ["{\"head\": \"ZeroOne\"}"], + "examples": ["{\"type\": \"ZeroOne\"}"], "properties": { - "head": { + "type": { "const": "ZeroOne" } } }, { "description": "ℤ", - "examples": ["{\"head\": \"Integer\"}"], + "examples": ["{\"type\": \"Integer\"}"], "properties": { - "head": { + "type": { "const": "Integer" } } @@ -527,29 +527,29 @@ "vector_sets": { "description": "A schema for the vector-valued sets defined by MathOptFormat. See http: //www.juliaopt.org/MathOptInterface.jl/v0.8/apireference/#Sets-1 for a list of the sets and their meanings.", "type": "object", - "required": ["head"], + "required": ["type"], "oneOf": [{ "description": "[x, y, z] ∈ {R³: y * exp(x / y) ≤ z, y ≥ 0}", - "examples": ["{\"head\": \"ExponentialCone\"}"], + "examples": ["{\"type\": \"ExponentialCone\"}"], "properties": { - "head": { + "type": { "const": "ExponentialCone" } } }, { "description": "[u, v, w] ∈ {R³: -u * exp(v / u) ≤ exp(1) * w, u < 0}", - "examples": ["{\"head\": \"DualExponentialCone\"}"], + "examples": ["{\"type\": \"DualExponentialCone\"}"], "properties": { - "head": { + "type": { "const": "DualExponentialCone" } } }, { "description": "A special ordered set of type I.", - "examples": ["{\"head\": \"SOS1\", \"weights\": [1, 3, 2]}"], + "examples": ["{\"type\": \"SOS1\", \"weights\": [1, 3, 2]}"], "required": ["weights"], "properties": { - "head": { + "type": { "const": "SOS1" }, "weights": { @@ -561,10 +561,10 @@ } }, { "description": "A special ordered set of type II.", - "examples": ["{\"head\": \"SOS2\", \"weights\": [1, 3, 2]}"], + "examples": ["{\"type\": \"SOS2\", \"weights\": [1, 3, 2]}"], "required": ["weights"], "properties": { - "head": { + "type": { "const": "SOS2" }, "weights": { @@ -576,10 +576,10 @@ } }, { "description": "[t, x] ∈ {R^{dimension}: t ≤ (Πxᵢ)^{1 / (dimension-1)}}", - "examples": ["{\"head\": \"GeometricMeanCone\", \"dimension\": 3}"], + "examples": ["{\"type\": \"GeometricMeanCone\", \"dimension\": 3}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "GeometricMeanCone" }, "dimension": { @@ -589,10 +589,10 @@ } }, { "description": "[t, x] ∈ {R^{dimension} : t ≥ ||x||₂", - "examples": ["{\"head\": \"SecondOrderCone\", \"dimension\": 3}"], + "examples": ["{\"type\": \"SecondOrderCone\", \"dimension\": 3}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "SecondOrderCone" }, "dimension": { @@ -602,10 +602,10 @@ } }, { "description": "[t, u, x] ∈ {R^{dimension} : 2tu ≥ (||x||₂)²; t, u ≥ 0}", - "examples": ["{\"head\": \"RotatedSecondOrderCone\", \"dimension\": 3}"], + "examples": ["{\"type\": \"RotatedSecondOrderCone\", \"dimension\": 3}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "RotatedSecondOrderCone" }, "dimension": { @@ -615,10 +615,10 @@ } }, { "description": "{0}^{dimension}", - "examples": ["{\"head\": \"Zeros\", \"dimension\": 3}"], + "examples": ["{\"type\": \"Zeros\", \"dimension\": 3}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "Zeros" }, "dimension": { @@ -628,10 +628,10 @@ } }, { "description": "R^{dimension}", - "examples": ["{\"head\": \"Reals\", \"dimension\": 3}"], + "examples": ["{\"type\": \"Reals\", \"dimension\": 3}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "Reals" }, "dimension": { @@ -641,10 +641,10 @@ } }, { "description": "R₋^{dimension}", - "examples": ["{\"head\": \"Nonpositives\", \"dimension\": 3}"], + "examples": ["{\"type\": \"Nonpositives\", \"dimension\": 3}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "Nonpositives" }, "dimension": { @@ -654,10 +654,10 @@ } }, { "description": "R₊^{dimension}", - "examples": ["{\"head\": \"Nonnegatives\", \"dimension\": 3}"], + "examples": ["{\"type\": \"Nonnegatives\", \"dimension\": 3}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "Nonnegatives" }, "dimension": { @@ -667,10 +667,10 @@ } }, { "description": "{[t, X] ∈ R^{1 + d(d+1)/2} : t ≤ det(X)^{1/d}}, where the matrix `X` is represented in the same symmetric packed format as in the `PositiveSemidefiniteConeTriangle`. The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its number of rows or columns.", - "examples": ["{\"head\": \"RootDetConeTriangle\", \"side_dimension\": 2}"], + "examples": ["{\"type\": \"RootDetConeTriangle\", \"side_dimension\": 2}"], "required": ["side_dimension"], "properties": { - "head": { + "type": { "const": "RootDetConeTriangle" }, "side_dimension": { @@ -680,10 +680,10 @@ } }, { "description": "{[t, X] ∈ R^{1 + d^2} : t ≤ det(X)^{1/d}, X symmetric}, where the matrix `X` is represented in the same symmetric packed format as in the `PositiveSemidefiniteConeSquare`. The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its number of rows or columns.", - "examples": ["{\"head\": \"RootDetConeSquare\", \"side_dimension\": 2}"], + "examples": ["{\"type\": \"RootDetConeSquare\", \"side_dimension\": 2}"], "required": ["side_dimension"], "properties": { - "head": { + "type": { "const": "RootDetConeSquare" }, "side_dimension": { @@ -693,10 +693,10 @@ } }, { "description": "{[t, u, X] ∈ R^{2 + d(d+1)/2} : t ≤ u log(det(X/u)), u > 0}, where the matrix `X` is represented in the same symmetric packed format as in the `PositiveSemidefiniteConeTriangle`. The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its number of rows or columns.", - "examples": ["{\"head\": \"LogDetConeTriangle\", \"side_dimension\": 2}"], + "examples": ["{\"type\": \"LogDetConeTriangle\", \"side_dimension\": 2}"], "required": ["side_dimension"], "properties": { - "head": { + "type": { "const": "LogDetConeTriangle" }, "side_dimension": { @@ -706,10 +706,10 @@ } }, { "description": "{[t, u, X] ∈ R^{2 + d^2} : t ≤ u log(det(X/u)), X symmetric, u > 0}, where the matrix `X` is represented in the same symmetric packed format as in the `PositiveSemidefiniteConeSquare`. The argument `side_dimension` is the side dimension of the matrix `X`, i.e., its number of rows or columns.", - "examples": ["{\"head\": \"LogDetConeSquare\", \"side_dimension\": 2}"], + "examples": ["{\"type\": \"LogDetConeSquare\", \"side_dimension\": 2}"], "required": ["side_dimension"], "properties": { - "head": { + "type": { "const": "LogDetConeSquare" }, "side_dimension": { @@ -719,10 +719,10 @@ } }, { "description": "The (vectorized) cone of symmetric positive semidefinite matrices, with `side_dimension` rows and columns. The entries of the upper-right triangular part of the matrix are given column by column (or equivalently, the entries of the lower-left triangular part are given row by row).", - "examples": ["{\"head\": \"PositiveSemidefiniteConeTriangle\", \"side_dimension\": 2}"], + "examples": ["{\"type\": \"PositiveSemidefiniteConeTriangle\", \"side_dimension\": 2}"], "required": ["side_dimension"], "properties": { - "head": { + "type": { "const": "PositiveSemidefiniteConeTriangle" }, "side_dimension": { @@ -732,10 +732,10 @@ } }, { "description": "The cone of symmetric positive semidefinite matrices, with side length `side_dimension`. The entries of the matrix are given column by column (or equivalently, row by row). The matrix is both constrained to be symmetric and to be positive semidefinite. That is, if the functions in entries `(i, j)` and `(j, i)` are different, then a constraint will be added to make sure that the entries are equal.", - "examples": ["{\"head\": \"PositiveSemidefiniteConeSquare\", \"side_dimension\": 2}"], + "examples": ["{\"type\": \"PositiveSemidefiniteConeSquare\", \"side_dimension\": 2}"], "required": ["side_dimension"], "properties": { - "head": { + "type": { "const": "PositiveSemidefiniteConeSquare" }, "side_dimension": { @@ -745,10 +745,10 @@ } }, { "description": "[x, y, z] ∈ {R³: x^{exponent} y^{1-exponent} ≥ |z|; x, y ≥ 0}", - "examples": ["{\"head\": \"PowerCone\", \"exponent\": 2.0}"], + "examples": ["{\"type\": \"PowerCone\", \"exponent\": 2.0}"], "required": ["exponent"], "properties": { - "head": { + "type": { "const": "PowerCone" }, "exponent": { @@ -757,10 +757,10 @@ } }, { "description": "[u, v, w] ∈ {R³: (u / exponent)^{exponent} (v / (1-exponent))^{1-exponent} ≥ |w|; u, v ≥ 0}", - "examples": ["{\"head\": \"DualPowerCone\", \"exponent\": 2.0}"], + "examples": ["{\"type\": \"DualPowerCone\", \"exponent\": 2.0}"], "required": ["exponent"], "properties": { - "head": { + "type": { "const": "DualPowerCone" }, "exponent": { @@ -769,10 +769,10 @@ } }, { "description": "If `activate_on=one`: (y, x) ∈ {0,1}×Rᴺ: y = 0 ⟹ x ∈ S, otherwise when `activate_on=zero`: (y, x) ∈ {0,1}×Rᴺ: y = 1 ⟹ x ∈ S.", - "examples": ["{\"head\": \"IndicatorSet\", \"set\": {\"head\": \"LessThan\", \"upper\": 2.0}, \"activate_on\": \"one\"}"], + "examples": ["{\"type\": \"IndicatorSet\", \"set\": {\"type\": \"LessThan\", \"upper\": 2.0}, \"activate_on\": \"one\"}"], "required": ["set", "activate_on"], "properties": { - "head": { + "type": { "const": "IndicatorSet" }, "set": { @@ -788,10 +788,10 @@ } }, { "description": "(t, x) ∈ {R^{dimension}: t ≥ Σᵢ|xᵢ|}", - "examples": ["{\"head\": \"NormOneCone\", \"dimension\": 2}"], + "examples": ["{\"type\": \"NormOneCone\", \"dimension\": 2}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "NormOneCone" }, "dimension": { @@ -801,10 +801,10 @@ } }, { "description": "(t, x) ∈ {R^{dimension}: t ≥ maxᵢ|xᵢ|}", - "examples": ["{\"head\": \"NormInfinityCone\", \"dimension\": 2}"], + "examples": ["{\"type\": \"NormInfinityCone\", \"dimension\": 2}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "NormInfinityCone" }, "dimension": { @@ -814,10 +814,10 @@ } }, { "description": "(u, v, w) ∈ {R^{dimension}: u ≥ sumᵢ wᵢlog(wᵢ/vᵢ), vᵢ ≥ 0, wᵢ ≥ 0}", - "examples": ["{\"head\": \"RelativeEntropyCone\", \"dimension\": 3}"], + "examples": ["{\"type\": \"RelativeEntropyCone\", \"dimension\": 3}"], "required": ["dimension"], "properties": { - "head": { + "type": { "const": "RelativeEntropyCone" }, "dimension": { @@ -827,10 +827,10 @@ } }, { "description": "(t, X) ∈ {R^{1+row_dim×column_dim}: t ≥ σ₁(X)}", - "examples": ["{\"head\": \"NormSpectralCone\", \"row_dim\": 1, \"column_dim\": 2}"], + "examples": ["{\"type\": \"NormSpectralCone\", \"row_dim\": 1, \"column_dim\": 2}"], "required": ["row_dim", "column_dim"], "properties": { - "head": { + "type": { "const": "NormSpectralCone" }, "row_dim": { @@ -844,10 +844,10 @@ } }, { "description": "(t, X) ∈ {R^{1+row_dim×column_dim}: t ≥ σ₁(X)}", - "examples": ["{\"head\": \"NormNuclearCone\", \"row_dim\": 1, \"column_dim\": 2}"], + "examples": ["{\"type\": \"NormNuclearCone\", \"row_dim\": 1, \"column_dim\": 2}"], "required": ["row_dim", "column_dim"], "properties": { - "head": { + "type": { "const": "NormNuclearCone" }, "row_dim": { diff --git a/src/FileFormats/MOF/nonlinear.jl b/src/FileFormats/MOF/nonlinear.jl index 0bf829d9e0..c1a0334986 100644 --- a/src/FileFormats/MOF/nonlinear.jl +++ b/src/FileFormats/MOF/nonlinear.jl @@ -5,7 +5,7 @@ function moi_to_object( node_list = OrderedObject[] foo_object = convert_expr_to_mof(foo.expr, node_list, name_map) return OrderedObject( - "head" => "ScalarNonlinearFunction", + "type" => "ScalarNonlinearFunction", "root" => foo_object, "node_list" => node_list, ) @@ -111,34 +111,34 @@ Tree form: MOF format: { - "head": "nonlinear", - "root": {"head": "node", "index": 4}, + "type": "nonlinear", + "root": {"type": "node", "index": 4}, "node_list": [ { - "head": "*", "args": [ - {"head": "real", "value": 2}, - {"head": "variable", "name": "x"} + "type": "*", "args": [ + {"type": "real", "value": 2}, + {"type": "variable", "name": "x"} ] }, { - "head": "sin", + "type": "sin", "args": [ - {"head": "variable", "name", "x"} + {"type": "variable", "name", "x"} ] } { - "head": "^", + "type": "^", "args": [ - {"head": "node", "index": 2}, - {"head": "real", "value": 2} + {"type": "node", "index": 2}, + {"type": "real", "value": 2} ] }, { - "head": "+", + "type": "+", "args": [ - {"head": "node", "index": 1}, - {"head": "node", "index": 3}, - {"head": "variable", "name": "y"} + {"type": "node", "index": 1}, + {"type": "node", "index": 3}, + {"type": "variable", "name": "y"} ] } ] @@ -172,7 +172,7 @@ end SUPPORTED_FUNCTIONS A vector of string-symbol pairs that map the MathOptFormat string representation -(i.e, the value of the `"head"` field) to the name of a Julia function (in +(i.e, the value of the `"type"` field) to the name of a Julia function (in Symbol form). """ const SUPPORTED_FUNCTIONS = Pair{String, Tuple{Symbol, ARITY}}[ @@ -276,7 +276,8 @@ to their variable index. function convert_mof_to_expr( node::T, node_list::Vector{T}, name_map::Dict{String, MOI.VariableIndex} ) where {T <: Object} - head = node["head"] + # TODO(odow): remove when v0.4 no longer supported. + head = haskey(node, "type") ? node["type"] : node["head"] if head == "real" return node["value"] elseif head == "complex" @@ -324,12 +325,12 @@ function convert_expr_to_mof( end (mathoptformat_string, arity) = FUNCTION_TO_STRING[function_name] validate_arguments(function_name, arity, length(expr.args) - 1) - node = T("head" => mathoptformat_string, "args" => T[]) + node = T("type" => mathoptformat_string, "args" => T[]) for arg in @view(expr.args[2:end]) push!(node["args"], convert_expr_to_mof(arg, node_list, name_map)) end push!(node_list, node) - return T("head" => "node", "index" => length(node_list)) + return T("type" => "node", "index" => length(node_list)) end # Recursion end for variables. @@ -338,21 +339,21 @@ function convert_expr_to_mof( ::Vector{T}, name_map::Dict{MOI.VariableIndex, String}, ) where {T <: Object} - return T("head" => "variable", "name" => name_map[variable]) + return T("type" => "variable", "name" => name_map[variable]) end # Recursion end for real constants. function convert_expr_to_mof( value::Real, ::Vector{T}, name_map::Dict{MOI.VariableIndex, String} ) where {T <: Object} - return T("head" => "real", "value" => value) + return T("type" => "real", "value" => value) end # Recursion end for complex numbers. function convert_expr_to_mof( value::Complex, ::Vector{T}, ::Dict{MOI.VariableIndex, String} ) where {T <: Object} - return T("head" => "complex", "real" => real(value), "imag" => imag(value)) + return T("type" => "complex", "real" => real(value), "imag" => imag(value)) end # Recursion fallback. diff --git a/src/FileFormats/MOF/read.jl b/src/FileFormats/MOF/read.jl index 56765d1658..f61a135e41 100644 --- a/src/FileFormats/MOF/read.jl +++ b/src/FileFormats/MOF/read.jl @@ -13,7 +13,7 @@ function Base.read!(io::IO, model::Model) end object = JSON.parse(io; dicttype = UnorderedObject) file_version = _parse_mof_version(object["version"]::UnorderedObject) - if file_version > VERSION + if file_version.major != VERSION.major || file_version.minor > VERSION.minor error( "Sorry, the file can't be read because this library supports " * "v$(VERSION) of MathOptFormat, but the file you are trying to " * @@ -141,7 +141,12 @@ Convert `x` from an MOF representation into a MOI representation. function function_to_moi( x::Object, name_map::Dict{String, MOI.VariableIndex} ) - val = head_to_function(x["head"]::String) + val = if haskey(x, "type") + head_to_function(x["type"]::String) + else + # TODO(odow): remove when v0.4 no longer supported. + head_to_function(x["head"]::String) + end return function_to_moi(val, x, name_map) end @@ -169,7 +174,7 @@ end # ========== Typed scalar functions ========== # Here, we deal with a special case: ScalarAffineTerm, ScalarQuadraticTerm, -# VectorAffineTerm, and VectorQuadraticTerm do not contain a "head" field +# VectorAffineTerm, and VectorQuadraticTerm do not contain a "type" field # (because it is unnecessary at the JSON level). function parse_scalar_affine_term( @@ -312,7 +317,14 @@ end Convert `x` from an OrderedDict representation into a MOI representation. """ -set_to_moi(x::Object) = set_to_moi(head_to_set(x["head"]::String), x) +function set_to_moi(x::Object) + if haskey(x, "type") + return set_to_moi(head_to_set(x["type"]::String), x) + else + # TODO(odow): remove when v0.4 no longer supported. + return set_to_moi(head_to_set(x["head"]::String), x) + end +end # ========== Non-typed scalar sets ========== diff --git a/src/FileFormats/MOF/write.jl b/src/FileFormats/MOF/write.jl index d7ee76bbb7..8c421cad4b 100644 --- a/src/FileFormats/MOF/write.jl +++ b/src/FileFormats/MOF/write.jl @@ -112,7 +112,7 @@ function moi_to_object( foo::MOI.SingleVariable, name_map::Dict{MOI.VariableIndex, String} ) return OrderedObject( - "head" => "SingleVariable", + "type" => "SingleVariable", "variable" => name_map[foo.variable] ) end @@ -134,7 +134,7 @@ function moi_to_object( name_map::Dict{MOI.VariableIndex, String}, ) return OrderedObject( - "head" => "ScalarAffineFunction", + "type" => "ScalarAffineFunction", "terms" => moi_to_object.(foo.terms, Ref(name_map)), "constant" => foo.constant, ) @@ -156,7 +156,7 @@ function moi_to_object( name_map::Dict{MOI.VariableIndex, String}, ) return OrderedObject( - "head" => "ScalarQuadraticFunction", + "type" => "ScalarQuadraticFunction", "affine_terms" => moi_to_object.(foo.affine_terms, Ref(name_map)), "quadratic_terms" => moi_to_object.(foo.quadratic_terms, Ref(name_map)), "constant" => foo.constant, @@ -169,7 +169,7 @@ function moi_to_object( foo::MOI.VectorOfVariables, name_map::Dict{MOI.VariableIndex, String} ) return OrderedObject( - "head" => "VectorOfVariables", + "type" => "VectorOfVariables", "variables" => [name_map[variable] for variable in foo.variables], ) end @@ -189,7 +189,7 @@ function moi_to_object( foo::MOI.VectorAffineFunction, name_map::Dict{MOI.VariableIndex, String} ) return OrderedObject( - "head" => "VectorAffineFunction", + "type" => "VectorAffineFunction", "terms" => moi_to_object.(foo.terms, Ref(name_map)), "constants" => foo.constants, ) @@ -208,7 +208,7 @@ function moi_to_object( foo::MOI.VectorQuadraticFunction, name_map::Dict{MOI.VariableIndex, String} ) return OrderedObject( - "head" => "VectorQuadraticFunction", + "type" => "VectorQuadraticFunction", "affine_terms" => moi_to_object.(foo.affine_terms, Ref(name_map)), "quadratic_terms" => moi_to_object.(foo.quadratic_terms, Ref(name_map)), "constants" => foo.constants, @@ -219,7 +219,7 @@ end """ head_name(::Type{SetType}) where SetType <: MOI.AbstractSet -Return the string that is stored in the `"head"` field of the MOF object for a +Return the string that is stored in the `"type"` field of the MOF object for a set of type `SetType`. """ function head_name end @@ -230,7 +230,7 @@ function head_name end function moi_to_object( set::SetType, ::Dict{MOI.VariableIndex, String} ) where {SetType} - object = OrderedObject("head" => head_name(SetType)) + object = OrderedObject("type" => head_name(SetType)) for key in fieldnames(SetType) object[string(key)] = getfield(set, key) end @@ -286,7 +286,7 @@ function moi_to_object( ) where {I, S} @assert I == MOI.ACTIVATE_ON_ONE || I == MOI.ACTIVATE_ON_ZERO return OrderedObject( - "head" => "IndicatorSet", + "type" => "IndicatorSet", "set" => moi_to_object(set.set, name_map), "activate_on" => (I == MOI.ACTIVATE_ON_ONE) ? "one" : "zero" ) diff --git a/test/FileFormats/MOF/MOF.jl b/test/FileFormats/MOF/MOF.jl index f7b0637c15..6b14434eee 100644 --- a/test/FileFormats/MOF/MOF.jl +++ b/test/FileFormats/MOF/MOF.jl @@ -96,6 +96,14 @@ end @test MOF.moi_to_object(c1, model, name_map)["name"] == "c_1" @test MOF.moi_to_object(c2, model, name_map)["name"] == "c" end + @testset "v0.4" begin + filename = joinpath(@__DIR__, "v0.4.mof.json") + model = MOF.Model(validate = true) + @test_throws ErrorException MOI.read_from_file(model, filename) + model = MOF.Model(validate = false) + MOI.read_from_file(model, filename) + @test MOI.get(model, MOI.NumberOfVariables()) == 2 + end end @testset "round trips" begin @testset "Empty model" begin diff --git a/test/FileFormats/MOF/empty_model.mof.json b/test/FileFormats/MOF/empty_model.mof.json index 409ead7059..9ffe78963e 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": 4 + "minor": 5 }, "variables": [], "objective": {"sense": "feasibility"}, 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 03d17307a2..5db754148a 100644 --- a/test/FileFormats/MOF/failing_models/blank_variable_name.mof.json +++ b/test/FileFormats/MOF/failing_models/blank_variable_name.mof.json @@ -2,12 +2,12 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 4 + "minor": 5 }, "variables": [{"name": ""}], "objective": { "sense": "min", - "function": {"head": "SingleVariable", "variable": ""} + "function": {"type": "SingleVariable", "variable": ""} }, "constraints": [] } 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 1f35c79cdb..b31f23d407 100644 --- a/test/FileFormats/MOF/failing_models/missing_variable_name.mof.json +++ b/test/FileFormats/MOF/failing_models/missing_variable_name.mof.json @@ -2,12 +2,12 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 4 + "minor": 5 }, "variables": [{}], "objective": { "sense": "min", - "function": {"head": "SingleVariable", "variable": "x"} + "function": {"type": "SingleVariable", "variable": "x"} }, "constraints": [] } 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 da4c7fcfee..1dea9c4210 100644 --- a/test/FileFormats/MOF/failing_models/unsupported_constraint_function.mof.json +++ b/test/FileFormats/MOF/failing_models/unsupported_constraint_function.mof.json @@ -2,15 +2,15 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 4 + "minor": 5 }, "variables": [{"name": "x"}], "objective": { "sense": "min", - "function": {"head": "SingleVariable", "variable": "x"} + "function": {"type": "SingleVariable", "variable": "x"} }, "constraints": [{ - "function": {"head": "UnsupportedConstraintFunction"}, - "set": {"head": "ZeroOne"} + "function": {"type": "UnsupportedConstraintFunction"}, + "set": {"type": "ZeroOne"} }] } 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 99b05d4d11..e7c05c577c 100644 --- a/test/FileFormats/MOF/failing_models/unsupported_constraint_set.mof.json +++ b/test/FileFormats/MOF/failing_models/unsupported_constraint_set.mof.json @@ -2,15 +2,15 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 4 + "minor": 5 }, "variables": [{"name": "x"}], "objective": { "sense": "min", - "function": {"head": "SingleVariable", "variable": "x"} + "function": {"type": "SingleVariable", "variable": "x"} }, "constraints": [{ - "function": {"head": "SingleVariable", "variable": "x"}, - "set": {"head": "UnsupportedConstraintSet"} + "function": {"type": "SingleVariable", "variable": "x"}, + "set": {"type": "UnsupportedConstraintSet"} }] } 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 3209d0a9f4..b1c0806cf5 100644 --- a/test/FileFormats/MOF/failing_models/unsupported_objective_function.mof.json +++ b/test/FileFormats/MOF/failing_models/unsupported_objective_function.mof.json @@ -2,13 +2,13 @@ "name": "MathOptFormat Model", "version": { "major": 0, - "minor": 4 + "minor": 5 }, "variables": [{"name": "x"}], "objective": { "sense": "min", "function": { - "head": "UnsupportedObjectiveFunction" + "type": "UnsupportedObjectiveFunction" } }, "constraints": [] 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 5f26d6d489..7307a3add7 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": 4 + "minor": 5 }, "variables": [{"name": "x"}], "objective": { diff --git a/test/FileFormats/MOF/nlp.mof.json b/test/FileFormats/MOF/nlp.mof.json index 222770b9e3..7e2633822e 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": 4 + "minor": 5 }, "variables": [ { @@ -21,55 +21,55 @@ "objective": { "sense": "min", "function": { - "head": "ScalarNonlinearFunction", + "type": "ScalarNonlinearFunction", "root": { - "head": "node", + "type": "node", "index": 3 }, "node_list": [ { - "head": "+", + "type": "+", "args": [ { - "head": "variable", + "type": "variable", "name": "var_1" }, { - "head": "variable", + "type": "variable", "name": "var_2" }, { - "head": "variable", + "type": "variable", "name": "var_3" } ] }, { - "head": "*", + "type": "*", "args": [ { - "head": "variable", + "type": "variable", "name": "var_1" }, { - "head": "variable", + "type": "variable", "name": "var_4" }, { - "head": "node", + "type": "node", "index": 1 } ] }, { - "head": "+", + "type": "+", "args": [ { - "head": "node", + "type": "node", "index": 2 }, { - "head": "variable", + "type": "variable", "name": "var_3" } ] @@ -80,29 +80,29 @@ "constraints": [ { "function": { - "head": "ScalarNonlinearFunction", + "type": "ScalarNonlinearFunction", "root": { - "head": "node", + "type": "node", "index": 1 }, "node_list": [ { - "head": "*", + "type": "*", "args": [ { - "head": "variable", + "type": "variable", "name": "var_1" }, { - "head": "variable", + "type": "variable", "name": "var_2" }, { - "head": "variable", + "type": "variable", "name": "var_3" }, { - "head": "variable", + "type": "variable", "name": "var_4" } ] @@ -110,87 +110,87 @@ ] }, "set": { - "head": "GreaterThan", + "type": "GreaterThan", "lower": 25 } }, { "function": { - "head": "ScalarNonlinearFunction", + "type": "ScalarNonlinearFunction", "root": { - "head": "node", + "type": "node", "index": 5 }, "node_list": [ { - "head": "^", + "type": "^", "args": [ { - "head": "variable", + "type": "variable", "name": "var_1" }, { - "head": "real", + "type": "real", "value": 2 } ] }, { - "head": "^", + "type": "^", "args": [ { - "head": "variable", + "type": "variable", "name": "var_2" }, { - "head": "real", + "type": "real", "value": 2 } ] }, { - "head": "^", + "type": "^", "args": [ { - "head": "variable", + "type": "variable", "name": "var_3" }, { - "head": "real", + "type": "real", "value": 2 } ] }, { - "head": "^", + "type": "^", "args": [ { - "head": "variable", + "type": "variable", "name": "var_4" }, { - "head": "real", + "type": "real", "value": 2 } ] }, { - "head": "+", + "type": "+", "args": [ { - "head": "node", + "type": "node", "index": 1 }, { - "head": "node", + "type": "node", "index": 2 }, { - "head": "node", + "type": "node", "index": 3 }, { - "head": "node", + "type": "node", "index": 4 } ] @@ -198,18 +198,18 @@ ] }, "set": { - "head": "EqualTo", + "type": "EqualTo", "value": 40 } }, { "name": "c1", "function": { - "head": "SingleVariable", + "type": "SingleVariable", "variable": "var_1" }, "set": { - "head": "Interval", + "type": "Interval", "lower": 1.0, "upper": 5.0 } @@ -217,11 +217,11 @@ { "name": "c2", "function": { - "head": "SingleVariable", + "type": "SingleVariable", "variable": "var_2" }, "set": { - "head": "Interval", + "type": "Interval", "lower": 1.0, "upper": 5.0 } @@ -229,11 +229,11 @@ { "name": "c3", "function": { - "head": "SingleVariable", + "type": "SingleVariable", "variable": "var_3" }, "set": { - "head": "Interval", + "type": "Interval", "lower": 1.0, "upper": 5.0 } @@ -241,11 +241,11 @@ { "name": "c4", "function": { - "head": "SingleVariable", + "type": "SingleVariable", "variable": "var_4" }, "set": { - "head": "Interval", + "type": "Interval", "lower": 1.0, "upper": 5.0 } diff --git a/test/FileFormats/MOF/nonlinear.jl b/test/FileFormats/MOF/nonlinear.jl index 6159e9c7c3..16bd017b61 100644 --- a/test/FileFormats/MOF/nonlinear.jl +++ b/test/FileFormats/MOF/nonlinear.jl @@ -60,7 +60,7 @@ end :(not_supported_function(x)), node_list, variable_to_string) # Test unsupported function for MOF -> Expr. @test_throws Exception MOF.convert_mof_to_expr( - MOF.OrderedObject("head"=>"not_supported_function", "value"=>1), + MOF.OrderedObject("type"=>"not_supported_function", "value"=>1), node_list, string_to_variable) # Test n-ary function with no arguments. @test_throws Exception MOF.convert_expr_to_mof( diff --git a/test/FileFormats/MOF/v0.4.mof.json b/test/FileFormats/MOF/v0.4.mof.json new file mode 100644 index 0000000000..2ff13c54f0 --- /dev/null +++ b/test/FileFormats/MOF/v0.4.mof.json @@ -0,0 +1,30 @@ +{ + "description": "The problem: min{x | x + y >= 1, x ∈ [0, 1], y ∈ {0, 1}}", + "version": {"major": 0, "minor": 4}, + "variables": [ + {"name": "x", "primal_start": 0.0}, {"name": "y", "primal_start": 1.0} + ], + "objective": { + "sense": "min", "function": {"head": "SingleVariable", "variable": "x"} + }, + "constraints": [{ + "name": "x + y >= 1", + "function": { + "head": "ScalarAffineFunction", + "terms": [ + {"coefficient": 1.0, "variable": "x"}, + {"coefficient": 1.0, "variable": "y"} + ], + "constant": 0.0 + }, + "set": {"head": "GreaterThan", "lower": 1.0} + }, { + "name": "x ∈ [0, 1]", + "function": {"head": "SingleVariable", "variable": "x"}, + "set": {"head": "Interval", "lower": 0.0, "upper": 1.0} + }, { + "name": "y ∈ {0, 1}", + "function": {"head": "SingleVariable", "variable": "y"}, + "set": {"head": "ZeroOne"} + }] +}