diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index be631e9..e58ca07 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: - black==23.10.1 - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.14 + rev: v0.1.15 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/phir/model.py b/phir/model.py index b94d447..aa63645 100644 --- a/phir/model.py +++ b/phir/model.py @@ -79,6 +79,24 @@ class ExportVar(Data): DataMgmt: TypeAlias = CVarDefine | QVarDefine | ExportVar +# Meta Instructions + + +class Meta(BaseModel, abc.ABC): + """Meta instructions base class.""" + + model_config = ConfigDict(extra="forbid") + + meta: str + + +class Barrier(Meta): + """Barrier instruction.""" + + meta: Literal["barrier"] + args: list[Bit] + + # Operations @@ -243,14 +261,14 @@ class MOp(Op): QOp: TypeAlias = MeasOp | SQOp | TQOp -OpType: TypeAlias = FFCall | COp | QOp | MOp +OpType: TypeAlias = FFCall | COp | QOp | MOp | Barrier # Blocks class Block(BaseModel, abc.ABC): - """General block type.""" + """Base class for block type.""" model_config = ConfigDict(extra="forbid") @@ -277,8 +295,8 @@ class IfBlock(Block): block: Literal["if"] condition: COp - true_branch: list[OpType] - false_branch: list[OpType] | None = None + true_branch: list[OpType | BlockType] + false_branch: list[OpType | BlockType] | None = None BlockType: TypeAlias = SeqBlock | QParBlock | IfBlock diff --git a/requirements.txt b/requirements.txt index 4415045..7ef1703 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,11 +2,11 @@ autodoc-pydantic==2.0.1 build==1.0.3 mypy==1.8.0 pre-commit==3.6.0 -pydantic==2.5.3 +pydantic==2.6.0 pydata_sphinx_theme==0.15.2 pytest==8.0.0 rich==13.7.0 -ruff==0.1.14 +ruff==0.1.15 setuptools-scm==8.0.4 sphinx==7.2.6 wheel==0.42.0 diff --git a/ruff.toml b/ruff.toml index 9637fbb..d538e10 100644 --- a/ruff.toml +++ b/ruff.toml @@ -61,6 +61,8 @@ select = [ "YTT", # flake8-2020 ] +ignore = ["COM812", "ISC001"] # conflicting with the formatter + [per-file-ignores] "__init__.py" = ["F401", "CPY001"] "docs/*" = [ @@ -71,8 +73,6 @@ select = [ "INP001", ] -ignore = ["COM812", "ISC001"] # conflicting with the formatter - [pydocstyle] convention = "google" diff --git a/schema.json b/schema.json index 20d17ca..afd720e 100644 --- a/schema.json +++ b/schema.json @@ -1,5 +1,39 @@ { "$defs": { + "Barrier": { + "additionalProperties": false, + "description": "Barrier instruction.", + "properties": { + "meta": { + "const": "barrier", + "title": "Meta" + }, + "args": { + "items": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "type": "string" + }, + { + "minimum": 0, + "type": "integer" + } + ], + "type": "array" + }, + "title": "Args", + "type": "array" + } + }, + "required": [ + "meta", + "args" + ], + "title": "Barrier", + "type": "object" + }, "COp": { "additionalProperties": false, "description": "Classical operation.", @@ -374,6 +408,18 @@ }, { "$ref": "#/$defs/MOp" + }, + { + "$ref": "#/$defs/Barrier" + }, + { + "$ref": "#/$defs/SeqBlock" + }, + { + "$ref": "#/$defs/QParBlock" + }, + { + "$ref": "#/$defs/IfBlock" } ] }, @@ -402,6 +448,18 @@ }, { "$ref": "#/$defs/MOp" + }, + { + "$ref": "#/$defs/Barrier" + }, + { + "$ref": "#/$defs/SeqBlock" + }, + { + "$ref": "#/$defs/QParBlock" + }, + { + "$ref": "#/$defs/IfBlock" } ] }, @@ -776,6 +834,9 @@ { "$ref": "#/$defs/MOp" }, + { + "$ref": "#/$defs/Barrier" + }, { "$ref": "#/$defs/SeqBlock" }, @@ -966,6 +1027,9 @@ { "$ref": "#/$defs/MOp" }, + { + "$ref": "#/$defs/Barrier" + }, { "$ref": "#/$defs/SeqBlock" }, diff --git a/spec.md b/spec.md index 031ff65..51a28a9 100644 --- a/spec.md +++ b/spec.md @@ -547,6 +547,24 @@ bitwise/logical operations, or comparisons between two variables and/or bits. In *Note:* While PHIR/PECOS can effectively manage nested if/else statements, extended OpenQASM 2.0 strictly permits only non-nested if statements. Consequently, such nesting should be sidestepped when converting from OpenQASM 2.0 to PHIR. +## Meta Instructions + +Instructions that communicate information such as a compiler hints and debugging commands that have influence beyond +a quantum program. + +### Barrier + +A barrier instruction provides a hint to the compiler/emulator that qubits involved in barrier may not be optimized or +parallelized across the barrier. Effectively, it enforces an ordering in time for how quantum state is manipulated by +the machine. + +```json5 +{ + "meta": "barrier", + "args": [qubit_id, ...] // list of qubit IDs +} +``` + ## Overall PHIR Example with Quantinuum's Extended OpenQASM 2.0 A simple quantum program might look like: diff --git a/tests/cond_barrier_qparallel.json b/tests/cond_barrier_qparallel.json new file mode 100644 index 0000000..a85705a --- /dev/null +++ b/tests/cond_barrier_qparallel.json @@ -0,0 +1,54 @@ +{ + "format": "PHIR/JSON", + "version": "0.1.0", + "metadata": { "strict_parallelism": "true" }, + "ops": [ + { + "data": "qvar_define", + "data_type": "qubits", + "variable": "q", + "size": 2 + }, + { "data": "cvar_define", "data_type": "u32", "variable": "m", "size": 2 }, + { + "block": "sequence", + "ops": [ + { + "block": "if", + "condition": { "cop": "==", "args": ["m", 0] }, + "true_branch": [ + { + "meta": "barrier", + "args": [ + ["q", 0], + ["q", 1] + ] + } + ] + }, + { + "block": "if", + "condition": { "cop": "==", "args": ["m", 1] }, + "true_branch": [ + { + "block": "qparallel", + "ops": [ + { "qop": "SZdg", "args": [["q", 0]] }, + { "qop": "SZ", "args": [["q", 1]] } + ] + } + ], + "false_branch": [ + { + "block": "qparallel", + "ops": [ + { "qop": "SZdg", "args": [["q", 0]] }, + { "qop": "SZ", "args": [["q", 1]] } + ] + } + ] + } + ] + } + ] +} diff --git a/tests/test_model.py b/tests/test_model.py index 583a379..06801e8 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -6,6 +6,8 @@ # ############################################################################## +# mypy: disable-error-code="misc" + """Basic validation tests.""" import json @@ -14,9 +16,20 @@ from phir.model import PHIRModel -def test_spec_example() -> None: # noqa: D103 - # From https://github.com/CQCL/phir/blob/main/spec.md#overall-phir-example-with-quantinuums-extended-openqasm-20 +def test_spec_example() -> None: + """From https://github.com/CQCL/phir/blob/main/spec.md . + + Specifically "Overall PHIR Example with Quantinuum's Extended OpenQASM 2.0" + """ with Path("tests/example.json").open() as f: - data = json.load(f) # type: ignore [misc] + data = json.load(f) + + PHIRModel.model_validate(data) + + +def test_conditional_barrier() -> None: + """Checks for barriers and qparallel blocks inside conditionals.""" + with Path("tests/cond_barrier_qparallel.json").open() as f: + data = json.load(f) - PHIRModel.model_validate(data) # type: ignore [misc] + PHIRModel.model_validate(data)