Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
20 changes: 20 additions & 0 deletions docs/src/formats/bqpjson.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,26 @@ QUBOTools.write_model("output.bool.json", model)

```

## Synthesis Metadata

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Only the Wishart shape is documented. The generic contract now enforced for every other generator (model = any string other than Wishart, plus a parameters object) gates reads but is undocumented. Consider adding a short note. Nonblocking.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in 548379c. The BQPJSON docs now describe the generic non-Wishart metadata.synthesis contract and keep the Wishart-specific shape documented separately.


Generated models can record provenance under `metadata.synthesis`. The schema
requires a `model` string and a `parameters` object. Wishart-generated models
use `model = "Wishart"` and integer `n` and `m` parameters:

```json
{
"metadata": {
"synthesis": {
"model": "Wishart",
"parameters": {
"n": 100,
"m": 10
}
}
}
}
```

## References

- [BQPJSON Documentation](https://bqpjson.readthedocs.io)
Expand Down
66 changes: 65 additions & 1 deletion src/library/format/bqpjson/bqpjson.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@
},
"dwig_generator": {
"type": "string"
},
"synthesis": {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Backwards-compatibility note: previously metadata.synthesis was unconstrained and read_model accepted any value here. With this oneOf, every file carrying a synthesis key must now match one of the two definitions or read_model throws FormatError. Package-generated files conform, but hand-authored/third-party files using a different synthesis shape will newly fail to read. Worth a CHANGELOG/release note. Nonblocking.

@bernalde bernalde Jun 23, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in 548379c. I added an Unreleased changelog note calling out the stricter BQPJSON metadata.synthesis validation in CHANGELOG.md.

"oneOf": [
{
"$ref": "#/definitions/wishart_synthesis_metadata"
},
{
"$ref": "#/definitions/generic_synthesis_metadata"
}
]
}
}
},
Expand Down Expand Up @@ -159,5 +169,59 @@
}
}
}
},
"definitions": {
"generic_synthesis_metadata": {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This generic branch is the only thing keeping non-Wishart synthesis metadata valid after the tightening (e.g. Sherrington-Kirkpatrick emits model = "Sherrington-Kirkpatrick" with parameters {n, mu, sigma}), but no test exercises it. I verified an SK model round-trips through write_model/read_model today; please add a regression test so a future edit here cannot silently break non-Wishart reads. Nonblocking.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in 548379c. I added a Sherrington-Kirkpatrick BQPJSON write/read regression test in test/unit/library/formats.jl to cover the generic synthesis metadata branch.

"type": "object",
"required": [
"model",
"parameters"
],
"properties": {
"model": {
"type": "string",
"not": {
"enum": [
"Wishart"
]
}
},
"parameters": {
"type": "object"
}
}
},
"wishart_synthesis_metadata": {
"type": "object",
"required": [
"model",
"parameters"
],
"properties": {
"model": {
"type": "string",
"enum": [
"Wishart"
]
},
"parameters": {
"type": "object",
"required": [
"n",
"m"
],
"properties": {
"n": {
"type": "integer",
"minimum": 0
},
"m": {
"type": "integer",
"minimum": 0
}
}
}
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/library/synthesis/wishart.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function generate(rng, problem::Wishart{T}) where {T}
f;
metadata = Dict{String,Any}(
"origin" => "Generated by QUBOTools.jl",
"synthesis" => Dict{String,Any}( # TODO: Add this to the Schema
"synthesis" => Dict{String,Any}(
"model" => "Wishart",
"parameters" => Dict{String,Any}(
"n" => problem.n,
Expand Down
53 changes: 53 additions & 0 deletions test/unit/library/formats.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,59 @@ function test_bqpjson_format()
QUBOTools.value(dst_model, [1, 1, 0])
end
end

@testset "Wishart synthesis metadata" begin
n = 6
m = 3

src_model = @test_logs (:warn, r"Deprecation Warning:.*QUBOLib") QUBOTools.generate(
Random.MersenneTwister(110),
QUBOTools.Wishart(n, m),
)
synthesis = QUBOTools.metadata(src_model)["synthesis"]

@test synthesis["model"] == "Wishart"
@test synthesis["parameters"] == Dict{String,Any}(
"n" => n,
"m" => m,
)

_with_temp_path("wishart.bool.json") do temp_path
QUBOTools.write_model(temp_path, src_model)

dst_model = QUBOTools.read_model(temp_path)

@test QUBOTools.metadata(dst_model)["synthesis"] == synthesis
end
end

@testset "Wishart synthesis schema validation" begin
_with_temp_path("invalid-wishart.bool.json") do temp_path
write(temp_path, """
{
"version": "1.0.0",
"id": 0,
"variable_ids": [1],
"variable_domain": "boolean",
"scale": 1.0,
"offset": 0.0,
"linear_terms": [],
"quadratic_terms": [],
"metadata": {
"synthesis": {
"model": "Wishart",
"parameters": {
"n": "6",
"m": 3
}
}
}
}
""")

@test_throws QUBOTools.FormatError QUBOTools.read_model(temp_path)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This asserts only the error type, not that the failure comes from the synthesis block. The fixture is otherwise valid so it currently fails for the right reason, but an unrelated future change to this JSON could make it pass for the wrong reason. Consider asserting the error message references synthesis. Question/nit.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in 548379c. The negative Wishart test now catches the FormatError and asserts the rendered error includes [metadata][synthesis].

end
end
end

return nothing
Expand Down
Loading