Exporting and importing constraint systems
To ease communication with other software, it is possible to export constraint trees to a file and re-import them. That may serve various purposes:
- you can exchange the systems in constraint trees more easily than by sharing the "builder" scripts, and more reliably and portably than with Julia's general serialization capabilities (in package
Serialization
) - you can exchange the system with different software (and different programming environments) which may e.g. implement other solving and analysis methods
- you may separate your software into a tree-building and tree-solving part, both more lightweight and more suitable to use in resource-constrained environments (such as on HPCs).
The package exports functions serialize
and deserialize
that convert the constraint trees to and from Julia containers (dictionaries and vectors that only contain simple data such as strings and numbers). These can be further converted to JSON or other data exchange formats.
For demonstration, we are going to make a simple constraint tree:
import ConstraintTrees as C
t = C.variables(keys = [:x, :y], bounds = C.Between(-10, 10))
t =
:point^t *
:difference^C.Constraint(t.x.value - t.y.value, C.EqualTo(3)) *
:length^C.Constraint(C.squared(t.x.value) + C.squared(t.y.value))
ConstraintTree with 3 elements:
:difference => Constraint(LinearValue(#= ... =#), EqualTo(3.0))
:length => Constraint(QuadraticValue(#= ... =#))
:point => ConstraintTree(#= 2 elements =#)
In the "nice formatting" the tree looks as follows:
C.pretty(t)
┬─difference: 1.0*x[1] + -1.0*x[2] = 3.0
├─length: 1.0*x[1]*x[1] + 1.0*x[2]*x[2]
╰─point
╰─┬─x: 1.0*x[1] ∈ [-10.0, 10.0]
╰─y: 1.0*x[2] ∈ [-10.0, 10.0]
Serialization
Serialization converts the tree to a Dict with appropriate contents:
C.serialize(t)
Dict{String, Dict{String, Dict{String}}} with 1 entry:
"tree" => Dict("length"=>Dict{String, Any}("value"=>Dict{String, Vector}("weights"=>[1.0, 1.0], "idxs"=>[[1, 1], [2, 2]]), "value_type"=>"quadratic…
The arguably easiest way to "export" the file is to convert it to JSON:
import JSON
JSON.print(C.serialize(t), 2)
{
"tree": {
"length": {
"value": {
"weights": [
1.0,
1.0
],
"idxs": [
[
1,
1
],
[
2,
2
]
]
},
"value_type": "quadratic"
},
"point": {
"tree": {
"x": {
"bound_type": "between",
"value": {
"weights": [
1.0
],
"idxs": [
1
]
},
"bound": [
-10.0,
10.0
],
"value_type": "linear"
},
"y": {
"bound_type": "between",
"value": {
"weights": [
1.0
],
"idxs": [
2
]
},
"bound": [
-10.0,
10.0
],
"value_type": "linear"
}
}
},
"difference": {
"bound_type": "equal_to",
"value": {
"weights": [
1.0,
-1.0
],
"idxs": [
1,
2
]
},
"bound": 3.0,
"value_type": "linear"
}
}
}
...and with appropriate functions, save the JSON to disk:
open("ct-test.json", "w") do f
JSON.print(f, C.serialize(t))
end
De-serialization
Because of the involved polymorphism, deserialization functions need to know the type of what is actually being parsed. With that in hand, the JSON can be re-loaded as follows, giving the same tree:
t2 = C.deserialize(C.ConstraintTree, JSON.parsefile("ct-test.json"))
C.pretty(t2)
┬─difference: 1.0*x[1] + -1.0*x[2] = 3.0
├─length: 1.0*x[1]*x[1] + 1.0*x[2]*x[2]
╰─point
╰─┬─x: 1.0*x[1] ∈ [-10.0, 10.0]
╰─y: 1.0*x[2] ∈ [-10.0, 10.0]
This page was generated using Literate.jl.