Skip to content
This repository has been archived by the owner on Apr 21, 2022. It is now read-only.

[MOF] Begin formal JSON schema #47

Merged
merged 20 commits into from
Mar 16, 2019
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.jl.cov
*.jl.*.cov
*.jl.mem
Manifest.toml
15 changes: 15 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name = "MathOptFormat"
uuid = "f4570300-c277-12e8-125c-4912f86ce65d"
authors = ["Oscar Dowson <[email protected]"]
version = "0.0.0"

[deps]
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
GZip = "92fee26a-97fe-5a0c-ad85-20a5f3185b63"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[compat]
julia = "1.0"
23 changes: 23 additions & 0 deletions src/MOF/MOF.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ const VERSION = 0

using DataStructures, JSON, MathOptInterface

import ..MathOptFormat
import JSONSchema
const SCHEMA_PATH = joinpath(@__DIR__, "schema", "mof.schema.json")

# we use an ordered dict to make the JSON printing nicer
const Object = OrderedDict{String, Any}

Expand Down Expand Up @@ -49,6 +53,25 @@ function Base.show(io::IO, ::Model)
return
end

"""
validate(filename::String)

Validate that the MOF file `filename` conforms to the MOF JSON schema. Returns
`nothing` if the file is valid, otherwise throws an error describing why the
file is not valid.
"""
function validate(filename::String)
MathOptFormat.gzip_open(filename, "r") do io
object = JSON.parse(io)
mof_schema = JSONSchema.Schema(JSON.parsefile(SCHEMA_PATH))
if !JSONSchema.isvalid(object, mof_schema)
error("Unable to read file because it does not conform to the MOF " *
"schema: ", JSONSchema.diagnose(object, mof_schema))
end
end
return
end

include("nonlinear.jl")

include("read.jl")
Expand Down
32 changes: 32 additions & 0 deletions src/MOF/schema/examples/biobjective.mof.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"description": "The problem: [min{2x - y + 1}, max{y}]",
"version": 0,
"variables": [{
"name": "x"
}, {
"name": "y"
}],
"objectives": [{
"sense": "min",
"function": {
"head": "ScalarAffineFunction",
"terms": [{
"coefficient": 2,
"variable": "x"
},
{
"coefficient": -1,
"variable": "y"
}
],
"constant": 1
}
}, {
"sense": "max",
"function": {
"head": "SingleVariable",
"variable": "y"
}
}],
"constraints": []
}
56 changes: 56 additions & 0 deletions src/MOF/schema/examples/milp.mof.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"description": "The problem: min{x | x + y >= 1, x ∈ [0, 1], y ∈ {0, 1}}",
"version": 0,
"variables": [{
"name": "x"
}, {
"name": "y"
}],
"objectives": [{
"sense": "min",
"function": {
"head": "SingleVariable",
"variable": "x"
}
}],
"constraints": [{
"name": "x + y >= 1",
"function": {
"head": "ScalarAffineFunction",
"terms": [{
"coefficient": 1,
"variable": "x"
},
{
"coefficient": 1,
"variable": "y"
}
],
"constant": 0
},
"set": {
"head": "GreaterThan",
"lower": 1
}
}, {
"name": "x ∈ [0, 1]",
"function": {
"head": "SingleVariable",
"variable": "x"
},
"set": {
"head": "Interval",
"lower": 0,
"upper": 1
}
}, {
"name": "y ∈ {0, 1}",
"function": {
"head": "SingleVariable",
"variable": "y"
},
"set": {
"head": "ZeroOne"
}
}]
}
57 changes: 57 additions & 0 deletions src/MOF/schema/examples/nlp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"description": "The problem: min{2x + sin(x)^2 + y}.",
"version": 0,
"variables": [{
"name": "x"
}, {
"name": "y"
}],
"objectives": [{
"sense": "min",
"function": {
"head": "Nonlinear",
"root": {
"head": "node",
"index": 4
},
"node_list": [{
"head": "*",
"args": [{
"head": "real",
"value": 2
}, {
"head": "variable",
"name": "x"
}]
}, {
"head": "sin",
"args": [{
"head": "variable",
"name": "x"
}]
}, {
"head": "^",
"args": [{
"head": "node",
"index": 2
}, {
"head": "real",
"value": 2
}]
}, {
"head": "+",
"args": [{
"head": "node",
"index": 1
}, {
"head": "node",
"index": 3
}, {
"head": "variable",
"name": "y"
}]
}]
}
}],
"constraints": []
}
34 changes: 34 additions & 0 deletions src/MOF/schema/examples/quadratic.mof.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"description": "The problem: min{x^2 + x * y + y^2}",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on

{
     "coefficient": 2,
     "variable_1": "x",
     "variable_2": "y"
 }

it should be min{x^2 + 2 * x * y + y^2}, right?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops. You're right that there is a mistake but that is the wrong one. I doubled the wrong value.

This is an annoying point that has come up a few times (e.g., JuliaOpt/LinQuadOptInterface.jl#22).

MOI defines the ScalarQuadraticFunction as 0.5 x' Q x.

"version": 0,
"variables": [{
"name": "x"
}, {
"name": "y"
}],
"objectives": [{
"sense": "min",
"function": {
"head": "ScalarQuadraticFunction",
"affine_terms": [],
"quadratic_terms": [{
"coefficient": 1,
"variable_1": "x",
"variable_2": "x"
},
{
"coefficient": 2,
"variable_1": "x",
"variable_2": "y"
},
{
"coefficient": 1,
"variable_1": "y",
"variable_2": "y"
}
],
"constant": 0
}
}],
"constraints": []
}
45 changes: 45 additions & 0 deletions src/MOF/schema/examples/vector.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"description": "The problem: min{0 | [1 2; 3 4][x, y] + [5, 6] ∈ R+.",
"version": 0,
"variables": [{
"name": "x"
}, {
"name": "y"
}],
"objectives": [],
"constraints": [{
"function": {
"head": "VectorAffineFunction",
"terms": [{
"output_index": 1,
"scalar_term": {
"coefficient": 1,
"variable": "x"
}
}, {
"output_index": 1,
"scalar_term": {
"coefficient": 2,
"variable": "y"
}
}, {
"output_index": 2,
"scalar_term": {
"coefficient": 3,
"variable": "x"
}
}, {
"output_index": 2,
"scalar_term": {
"coefficient": 4,
"variable": "y"
}
}],
"constants": [5, 6]
},
"set": {
"head": "Nonnegatives",
"dimension": 2
}
}]
}
Loading