zena-sdk 0.1.4__cp38-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qsys/__init__.py +31 -0
- qsys/__pycache__/__init__.cpython-313.pyc +0 -0
- qsys/backends/base.py +39 -0
- qsys/backends/local5q.py +8 -0
- qsys/circuit/__init__.py +20 -0
- qsys/circuit/__pycache__/__init__.cpython-313.pyc +0 -0
- qsys/circuit/__pycache__/quantum_circuit.cpython-313.pyc +0 -0
- qsys/circuit/quantum_circuit.py +103 -0
- qsys/cli/__init__.py +2 -0
- qsys/cli/io_cli.py +45 -0
- qsys/errors/__init__.py +32 -0
- qsys/errors/__pycache__/__init__.cpython-313.pyc +0 -0
- qsys/io/__init__.py +21 -0
- qsys/io/json_io.py +84 -0
- qsys/io/text_io.py +58 -0
- qsys/ir/__init__.py +1 -0
- qsys/ir/__pycache__/__init__.cpython-313.pyc +0 -0
- qsys/ir/__pycache__/types.cpython-313.pyc +0 -0
- qsys/ir/from_payload.py +27 -0
- qsys/ir/types.py +9 -0
- qsys/logging.py +14 -0
- qsys/runtime/__init__.py +4 -0
- qsys/runtime/__pycache__/__init__.cpython-313.pyc +0 -0
- qsys/runtime/__pycache__/execute.cpython-313.pyc +0 -0
- qsys/runtime/execute.py +155 -0
- qsys/target.py +44 -0
- qsys/targets.py +63 -0
- qsys/transpiler/__init__.py +6 -0
- qsys/transpiler/basis.py +39 -0
- qsys/transpiler/opt1q.py +101 -0
- qsys/transpiler/passes.py +57 -0
- qsys/transpiler/routing.py +136 -0
- qsys/transpiler/validate.py +132 -0
- qsys/viz/__init__.py +1 -0
- qsys/viz/text_drawer.py +89 -0
- simulator_statevector/__init__.py +5 -0
- simulator_statevector/__pycache__/__init__.cpython-313.pyc +0 -0
- simulator_statevector/simulator_statevector.pyd +0 -0
- zena/__init__.py +7 -0
- zena/__pycache__/__init__.cpython-312.pyc +0 -0
- zena/__pycache__/__init__.cpython-313.pyc +0 -0
- zena/__pycache__/execute.cpython-312.pyc +0 -0
- zena/__pycache__/execute.cpython-313.pyc +0 -0
- zena/circuit/__init__.py +2 -0
- zena/circuit/__pycache__/__init__.cpython-312.pyc +0 -0
- zena/circuit/__pycache__/__init__.cpython-313.pyc +0 -0
- zena/circuit/__pycache__/quantum_circuit.cpython-312.pyc +0 -0
- zena/circuit/__pycache__/quantum_circuit.cpython-313.pyc +0 -0
- zena/circuit/__pycache__/register.cpython-312.pyc +0 -0
- zena/circuit/__pycache__/register.cpython-313.pyc +0 -0
- zena/circuit/quantum_circuit.py +218 -0
- zena/circuit/register.py +28 -0
- zena/compiler/__init__.py +72 -0
- zena/compiler/__pycache__/__init__.cpython-312.pyc +0 -0
- zena/compiler/__pycache__/__init__.cpython-313.pyc +0 -0
- zena/dist/zena_sdk-0.1.0-py3-none-any.whl +0 -0
- zena/dist/zena_sdk-0.1.0.tar.gz +0 -0
- zena/execute.py +35 -0
- zena/providers/__init__.py +5 -0
- zena/providers/__pycache__/__init__.cpython-312.pyc +0 -0
- zena/providers/__pycache__/__init__.cpython-313.pyc +0 -0
- zena/providers/__pycache__/aer.cpython-312.pyc +0 -0
- zena/providers/__pycache__/aer.cpython-313.pyc +0 -0
- zena/providers/__pycache__/backend.cpython-312.pyc +0 -0
- zena/providers/__pycache__/backend.cpython-313.pyc +0 -0
- zena/providers/__pycache__/job.cpython-312.pyc +0 -0
- zena/providers/__pycache__/job.cpython-313.pyc +0 -0
- zena/providers/aer.py +71 -0
- zena/providers/backend.py +18 -0
- zena/providers/job.py +24 -0
- zena/visualization/__init__.py +28 -0
- zena/visualization/__pycache__/__init__.cpython-312.pyc +0 -0
- zena/visualization/__pycache__/__init__.cpython-313.pyc +0 -0
- zena_sdk-0.1.4.dist-info/METADATA +70 -0
- zena_sdk-0.1.4.dist-info/RECORD +76 -0
- zena_sdk-0.1.4.dist-info/WHEEL +4 -0
qsys/__init__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .circuit.quantum_circuit import QuantumCircuit
|
|
4
|
+
from .errors import ValidationError
|
|
5
|
+
from .runtime.execute import execute
|
|
6
|
+
|
|
7
|
+
# Phase 6 I/O
|
|
8
|
+
from .io import export_text, import_text, export_json, import_json
|
|
9
|
+
from .ir.from_payload import circuit_from_payload
|
|
10
|
+
|
|
11
|
+
# Phase 7 Targets & Backends
|
|
12
|
+
from .target import Target
|
|
13
|
+
from .targets import BUILTIN_TARGETS
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"QuantumCircuit",
|
|
17
|
+
"ValidationError",
|
|
18
|
+
"execute",
|
|
19
|
+
"export_text",
|
|
20
|
+
"import_text",
|
|
21
|
+
"export_json",
|
|
22
|
+
"import_json",
|
|
23
|
+
"circuit_from_payload",
|
|
24
|
+
"Target",
|
|
25
|
+
"BUILTIN_TARGETS",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def engine_version() -> str:
|
|
30
|
+
"""Return the current engine version."""
|
|
31
|
+
return "0.1.0"
|
|
Binary file
|
qsys/backends/base.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class Target:
|
|
6
|
+
n_qubits: int
|
|
7
|
+
basis_gates: set[str]
|
|
8
|
+
coupling_map: list[tuple[int, int]] # undirected edges
|
|
9
|
+
gate_durations: dict[str, float] = None # ns
|
|
10
|
+
error_rates: dict[str, float] = None # per gate
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Backend:
|
|
14
|
+
def __init__(self, target: Target, backend_id: str):
|
|
15
|
+
self._target = target
|
|
16
|
+
self._id = backend_id
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def id(self) -> str:
|
|
20
|
+
return self._id
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def target(self) -> Target:
|
|
24
|
+
return self._target
|
|
25
|
+
|
|
26
|
+
def supports(self, op: str, qubits: tuple[int, ...]) -> bool:
|
|
27
|
+
if (
|
|
28
|
+
op in ("x", "sx", "rz")
|
|
29
|
+
and len(qubits) == 1
|
|
30
|
+
and qubits[0] < self._target.n_qubits
|
|
31
|
+
):
|
|
32
|
+
return True
|
|
33
|
+
if op == "cx" and len(qubits) == 2:
|
|
34
|
+
a, b = qubits
|
|
35
|
+
return (a, b) in self._target.coupling_map or (
|
|
36
|
+
b,
|
|
37
|
+
a,
|
|
38
|
+
) in self._target.coupling_map
|
|
39
|
+
return op == "measure"
|
qsys/backends/local5q.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from .base import Backend, Target
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def local_5q_line() -> Backend:
|
|
5
|
+
edges = [(0, 1), (1, 2), (2, 3), (3, 4)]
|
|
6
|
+
basis = {"x", "sx", "rz", "cx", "measure"}
|
|
7
|
+
tgt = Target(n_qubits=5, basis_gates=basis, coupling_map=edges)
|
|
8
|
+
return Backend(tgt, backend_id="local_sim_5q_line")
|
qsys/circuit/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# circuit package
|
|
2
|
+
# qsys/circuit/__init__.py
|
|
3
|
+
"""
|
|
4
|
+
qsys.circuit package exports.
|
|
5
|
+
|
|
6
|
+
Expose the main public symbols here so callers can do:
|
|
7
|
+
from qsys.circuit import QuantumCircuit
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Primary public class
|
|
11
|
+
from .quantum_circuit import QuantumCircuit
|
|
12
|
+
|
|
13
|
+
# Optionally expose other useful names if they exist in quantum_circuit.py
|
|
14
|
+
# from .quantum_circuit import QuantumInstruction, QuantumError # <- uncomment if present
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"QuantumCircuit",
|
|
18
|
+
# "QuantumInstruction",
|
|
19
|
+
# "QuantumError",
|
|
20
|
+
]
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# qsys/circuit/quantum_circuit.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import List
|
|
4
|
+
|
|
5
|
+
from ..ir.types import Instruction
|
|
6
|
+
from ..errors import ValidationError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class QuantumCircuit:
|
|
10
|
+
"""
|
|
11
|
+
Minimal QuantumCircuit IR with lightweight builder-time validation.
|
|
12
|
+
|
|
13
|
+
Builder-time checks are intentionally *lightweight*:
|
|
14
|
+
- only validate argument types and non-negativity here
|
|
15
|
+
- upper-bound checks (index < n_qubits / n_clbits) are deferred to the runtime
|
|
16
|
+
(execute/transpiler) so passes and tooling can construct/transform circuits
|
|
17
|
+
without immediate failure.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, n_qubits: int, n_clbits: int = 0):
|
|
21
|
+
# constructor argument validation (strict)
|
|
22
|
+
if not isinstance(n_qubits, int) or n_qubits <= 0:
|
|
23
|
+
raise ValidationError("n_qubits must be a positive integer")
|
|
24
|
+
if not isinstance(n_clbits, int) or n_clbits < 0:
|
|
25
|
+
raise ValidationError("n_clbits must be a non-negative integer")
|
|
26
|
+
|
|
27
|
+
self.n_qubits: int = n_qubits
|
|
28
|
+
self.n_clbits: int = n_clbits
|
|
29
|
+
self._instructions: List[Instruction] = []
|
|
30
|
+
|
|
31
|
+
def _check_qubits(self, *qs: int) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Builder-time lightweight checks:
|
|
34
|
+
- qubit indices must be ints
|
|
35
|
+
- qubit indices must be >= 0
|
|
36
|
+
Upper-bound checks (q < self.n_qubits) are intentionally deferred to execute().
|
|
37
|
+
"""
|
|
38
|
+
for q in qs:
|
|
39
|
+
if not isinstance(q, int):
|
|
40
|
+
raise ValidationError(f"qubit index must be int, got {type(q)}")
|
|
41
|
+
if q < 0:
|
|
42
|
+
raise ValidationError(f"qubit index must be non-negative, got {q}")
|
|
43
|
+
|
|
44
|
+
def draw(self, mode: str = "text") -> str:
|
|
45
|
+
"""Render the circuit as ASCII. Usage: print(qc.draw())."""
|
|
46
|
+
if mode != "text":
|
|
47
|
+
raise ValueError("Only 'text' mode is supported for now.")
|
|
48
|
+
from ..viz.text_drawer import draw_ascii
|
|
49
|
+
|
|
50
|
+
return draw_ascii(self.n_qubits, self._instructions)
|
|
51
|
+
|
|
52
|
+
# ----- gate builders (chainable) -----
|
|
53
|
+
def x(self, q: int) -> "QuantumCircuit":
|
|
54
|
+
self._check_qubits(q)
|
|
55
|
+
self._instructions.append(Instruction(name="x", qubits=(q,)))
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
def sx(self, q: int) -> "QuantumCircuit":
|
|
59
|
+
self._check_qubits(q)
|
|
60
|
+
self._instructions.append(Instruction(name="sx", qubits=(q,)))
|
|
61
|
+
return self
|
|
62
|
+
|
|
63
|
+
def rz(self, q: int, theta: float) -> "QuantumCircuit":
|
|
64
|
+
self._check_qubits(q)
|
|
65
|
+
self._instructions.append(
|
|
66
|
+
Instruction(name="rz", qubits=(q,), params=(float(theta),))
|
|
67
|
+
)
|
|
68
|
+
return self
|
|
69
|
+
|
|
70
|
+
def cx(self, control: int, target: int) -> "QuantumCircuit":
|
|
71
|
+
if control == target:
|
|
72
|
+
raise ValidationError("control and target must differ")
|
|
73
|
+
self._check_qubits(control, target)
|
|
74
|
+
self._instructions.append(Instruction(name="cx", qubits=(control, target)))
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
def measure(self, q: int, c: int) -> "QuantumCircuit":
|
|
78
|
+
"""
|
|
79
|
+
Keep classical mapping in IR but validate types/non-negativity only here.
|
|
80
|
+
Upper-bound checks for classical bit index are deferred to execute().
|
|
81
|
+
"""
|
|
82
|
+
# validate qubit type and non-negativity (no upper-bound check here)
|
|
83
|
+
self._check_qubits(q)
|
|
84
|
+
|
|
85
|
+
# classical index: type and non-negative only (defer upper-bound)
|
|
86
|
+
if not isinstance(c, int):
|
|
87
|
+
raise ValidationError(f"classical bit index must be int, got {type(c)}")
|
|
88
|
+
if c < 0:
|
|
89
|
+
raise ValidationError(f"classical bit index must be non-negative, got {c}")
|
|
90
|
+
|
|
91
|
+
self._instructions.append(Instruction(name="measure", qubits=(q,), clbits=(c,)))
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
# convenience non-native gate (will be decomposed by transpiler)
|
|
95
|
+
def h(self, q: int) -> "QuantumCircuit":
|
|
96
|
+
self._check_qubits(q)
|
|
97
|
+
self._instructions.append(Instruction("h", (q,), ()))
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def instructions(self) -> List[Instruction]:
|
|
102
|
+
# return a copy to protect internal list
|
|
103
|
+
return list(self._instructions)
|
qsys/cli/__init__.py
ADDED
qsys/cli/io_cli.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# qsys/cli/io_cli.py
|
|
2
|
+
"""
|
|
3
|
+
Quick CLI: python -m qsys.cli.io_cli export-text myfile.ir
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _usage():
|
|
11
|
+
print("Usage:")
|
|
12
|
+
print(" python -m qsys.cli.io_cli export-text <circuit_pyfile> <out.ir>")
|
|
13
|
+
print(" python -m qsys.cli.io_cli export-json <circuit_pyfile> <out.json>")
|
|
14
|
+
print(
|
|
15
|
+
"circuit_pyfile should define a variable `CIRCUIT` referencing a circuit object."
|
|
16
|
+
)
|
|
17
|
+
sys.exit(1)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main(argv=None):
|
|
21
|
+
import importlib.util
|
|
22
|
+
from qsys.io import export_text, export_json
|
|
23
|
+
|
|
24
|
+
argv = argv or sys.argv[1:]
|
|
25
|
+
if len(argv) != 3:
|
|
26
|
+
_usage()
|
|
27
|
+
cmd, pyfile, out = argv
|
|
28
|
+
pyfile = Path(pyfile).absolute()
|
|
29
|
+
spec = importlib.util.spec_from_file_location("tmp_module", str(pyfile))
|
|
30
|
+
mod = importlib.util.module_from_spec(spec)
|
|
31
|
+
spec.loader.exec_module(mod)
|
|
32
|
+
if not hasattr(mod, "CIRCUIT"):
|
|
33
|
+
print("pyfile must define CIRCUIT variable")
|
|
34
|
+
sys.exit(2)
|
|
35
|
+
circuit = mod.CIRCUIT
|
|
36
|
+
if cmd == "export-text":
|
|
37
|
+
export_text(circuit, out)
|
|
38
|
+
elif cmd == "export-json":
|
|
39
|
+
export_json(circuit, out)
|
|
40
|
+
else:
|
|
41
|
+
_usage()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
main()
|
qsys/errors/__init__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# qsys/errors/__init__.py
|
|
2
|
+
"""Project-specific exception types."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class QSysError(Exception):
|
|
6
|
+
"""Base class for qsys errors."""
|
|
7
|
+
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ValidationError(QSysError):
|
|
12
|
+
"""Raised for invalid user input (indices, arity, params)."""
|
|
13
|
+
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TranspileError(QSysError):
|
|
18
|
+
"""Raised by transpiler passes."""
|
|
19
|
+
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BackendError(QSysError):
|
|
24
|
+
"""Raised by backends when they fail."""
|
|
25
|
+
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ExecutionError(QSysError):
|
|
30
|
+
"""Raised by the runtime/engine during execution."""
|
|
31
|
+
|
|
32
|
+
pass
|
|
Binary file
|
qsys/io/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# qsys/io/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
qsys.io - Export / Import helpers for the IR.
|
|
4
|
+
|
|
5
|
+
Public functions:
|
|
6
|
+
- export_text(circuit, path)
|
|
7
|
+
- import_text(path) -> circuit-like object
|
|
8
|
+
- export_json(circuit, path)
|
|
9
|
+
- import_json(path) -> circuit-like object
|
|
10
|
+
|
|
11
|
+
These functions accept your existing circuit object if it implements:
|
|
12
|
+
- .instructions (iterable) where each instruction is dict-like with:
|
|
13
|
+
{ "op": str, "targets": list[int], "params": dict|list|None }
|
|
14
|
+
- .n_qubits (optional; used for header metadata)
|
|
15
|
+
If your real circuit type differs, adapt the small adapter in export/import functions.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .text_io import export_text, import_text
|
|
19
|
+
from .json_io import export_json, import_json
|
|
20
|
+
|
|
21
|
+
__all__ = ["export_text", "import_text", "export_json", "import_json"]
|
qsys/io/json_io.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# qsys/io/json_io.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import Any, Dict, Iterable, Optional
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
# Schema (lightweight) for the JSON format:
|
|
8
|
+
# {
|
|
9
|
+
# "qsys_ir_version": 1,
|
|
10
|
+
# "n_qubits": N,
|
|
11
|
+
# "instructions": [
|
|
12
|
+
# {"op": "RZ", "targets": [0], "params": {"theta": 1.23}},
|
|
13
|
+
# {"op": "CX", "targets": [0,1], "params": null}
|
|
14
|
+
# ],
|
|
15
|
+
# "metadata": { ... } # optional
|
|
16
|
+
# }
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _normalize_instructions(circuit_or_instrs: Any) -> Iterable[Dict]:
|
|
20
|
+
"""
|
|
21
|
+
Similar to text_io._iter_instructions_from_circuit
|
|
22
|
+
"""
|
|
23
|
+
if hasattr(circuit_or_instrs, "instructions"):
|
|
24
|
+
instrs = circuit_or_instrs.instructions
|
|
25
|
+
else:
|
|
26
|
+
instrs = circuit_or_instrs
|
|
27
|
+
|
|
28
|
+
for ins in instrs:
|
|
29
|
+
if isinstance(ins, dict):
|
|
30
|
+
op = ins.get("op")
|
|
31
|
+
targets = list(ins.get("targets", []))
|
|
32
|
+
params = ins.get("params", None)
|
|
33
|
+
else:
|
|
34
|
+
op = getattr(ins, "op", getattr(ins, "name", None))
|
|
35
|
+
targets = list(getattr(ins, "targets", getattr(ins, "qubits", [])))
|
|
36
|
+
params = getattr(ins, "params", None)
|
|
37
|
+
yield {"op": str(op), "targets": [int(t) for t in targets], "params": params}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def export_json(
|
|
41
|
+
circuit: Any,
|
|
42
|
+
path: str | Path,
|
|
43
|
+
version: int = 1,
|
|
44
|
+
metadata: Optional[Dict] = None,
|
|
45
|
+
n_qubits: Optional[int] = None,
|
|
46
|
+
) -> None:
|
|
47
|
+
p = Path(path)
|
|
48
|
+
if n_qubits is None:
|
|
49
|
+
n_qubits = getattr(circuit, "n_qubits", None)
|
|
50
|
+
if n_qubits is None:
|
|
51
|
+
# determine
|
|
52
|
+
targets = []
|
|
53
|
+
for ins in _normalize_instructions(circuit):
|
|
54
|
+
targets.extend(ins["targets"])
|
|
55
|
+
n_qubits = (max(targets) + 1) if targets else 0
|
|
56
|
+
|
|
57
|
+
payload = {
|
|
58
|
+
"qsys_ir_version": version,
|
|
59
|
+
"n_qubits": n_qubits,
|
|
60
|
+
"instructions": list(_normalize_instructions(circuit)),
|
|
61
|
+
}
|
|
62
|
+
if metadata:
|
|
63
|
+
payload["metadata"] = metadata
|
|
64
|
+
|
|
65
|
+
p.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _validate_json_payload(payload: Dict) -> None:
|
|
69
|
+
if "qsys_ir_version" not in payload:
|
|
70
|
+
raise ValueError("Missing qsys_ir_version")
|
|
71
|
+
if "n_qubits" not in payload or not isinstance(payload["n_qubits"], int):
|
|
72
|
+
raise ValueError("Missing/invalid n_qubits")
|
|
73
|
+
if "instructions" not in payload or not isinstance(payload["instructions"], list):
|
|
74
|
+
raise ValueError("Missing/invalid instructions")
|
|
75
|
+
for i, ins in enumerate(payload["instructions"], start=1):
|
|
76
|
+
if not isinstance(ins, dict) or "op" not in ins or "targets" not in ins:
|
|
77
|
+
raise ValueError(f"Invalid instruction at index {i}: {ins!r}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def import_json(path: str | Path) -> Dict[str, Any]:
|
|
81
|
+
p = Path(path)
|
|
82
|
+
payload = json.loads(p.read_text(encoding="utf-8"))
|
|
83
|
+
_validate_json_payload(payload)
|
|
84
|
+
return payload
|
qsys/io/text_io.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# qsys/io/text_io.py - FINAL 100% WORKING VERSION (real + test compatible)
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import Any, Iterable
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
HEADER_RE = re.compile(r"^#\s*QSYS-IR\s+v(\d+)\s*\|\s*n_qubits:\s*(\d+)\s*$")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _iter_instructions_from_circuit(circuit: Any) -> Iterable[dict]:
|
|
12
|
+
for ins in getattr(circuit, "instructions", []):
|
|
13
|
+
if hasattr(ins, "name"): # real Instruction object
|
|
14
|
+
op = ins.name.lower()
|
|
15
|
+
targets = getattr(ins, "qubits", [])
|
|
16
|
+
params = getattr(ins, "params", [])
|
|
17
|
+
else: # dict used in tests
|
|
18
|
+
op = ins.get("op", "").lower()
|
|
19
|
+
targets = ins.get("targets", ins.get("qubits", []))
|
|
20
|
+
params = ins.get("params", [])
|
|
21
|
+
if op == "rz" and isinstance(params, dict):
|
|
22
|
+
params = [params["theta"]]
|
|
23
|
+
yield {"op": op, "targets": targets, "params": params or []}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def export_text(circuit: Any, path: str | Path) -> None:
|
|
27
|
+
p = Path(path)
|
|
28
|
+
n_qubits = getattr(circuit, "n_qubits", 0)
|
|
29
|
+
with p.open("w") as f:
|
|
30
|
+
f.write(f"# QSYS-IR v1 | n_qubits: {n_qubits}\n")
|
|
31
|
+
for ins in _iter_instructions_from_circuit(circuit):
|
|
32
|
+
targets_str = ",".join(map(str, ins["targets"]))
|
|
33
|
+
params_str = json.dumps(ins["params"])
|
|
34
|
+
f.write(f"{ins['op']} targets={targets_str} params={params_str}\n")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def import_text(path: str | Path) -> dict:
|
|
38
|
+
p = Path(path)
|
|
39
|
+
lines = p.read_text().splitlines()
|
|
40
|
+
m = HEADER_RE.match(lines[0])
|
|
41
|
+
if not m:
|
|
42
|
+
raise ValueError("Bad header")
|
|
43
|
+
n_qubits = int(m.group(2))
|
|
44
|
+
instructions = []
|
|
45
|
+
for line in lines[1:]:
|
|
46
|
+
line = line.strip()
|
|
47
|
+
if not line or line.startswith("#"):
|
|
48
|
+
continue
|
|
49
|
+
op = line.split()[0].upper()
|
|
50
|
+
rest = line[len(op.lower()) :].strip()
|
|
51
|
+
targets_str = rest.split("params=")[0].replace("targets=", "").strip()
|
|
52
|
+
params_str = rest.split("params=", 1)[1] if "params=" in rest else "[]"
|
|
53
|
+
targets = [int(x) for x in targets_str.split(",") if x]
|
|
54
|
+
params = json.loads(params_str)
|
|
55
|
+
if op == "RZ" and params:
|
|
56
|
+
params = {"theta": params[0]}
|
|
57
|
+
instructions.append({"op": op, "targets": targets, "params": params})
|
|
58
|
+
return {"n_qubits": n_qubits, "instructions": instructions}
|
qsys/ir/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# ir package
|
|
Binary file
|
|
Binary file
|
qsys/ir/from_payload.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# qsys/ir/from_payload.py - FINAL VERSION COMPATIBLE WITH YOUR CODE
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import Any
|
|
4
|
+
from qsys.circuit.quantum_circuit import QuantumCircuit
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def circuit_from_payload(payload: dict[str, Any]) -> QuantumCircuit:
|
|
8
|
+
circ = QuantumCircuit(n_qubits=payload["n_qubits"])
|
|
9
|
+
|
|
10
|
+
for ins in payload["instructions"]:
|
|
11
|
+
op = ins["op"]
|
|
12
|
+
targets = [int(t) for t in ins["targets"]]
|
|
13
|
+
params = ins["params"] or {}
|
|
14
|
+
|
|
15
|
+
if op == "RZ":
|
|
16
|
+
circ.rz(theta=params.get("theta", 0.0), q=targets[0])
|
|
17
|
+
elif op == "SX":
|
|
18
|
+
circ.sx(q=targets[0])
|
|
19
|
+
elif op == "X":
|
|
20
|
+
circ.x(q=targets[0])
|
|
21
|
+
elif op == "CX":
|
|
22
|
+
circ.cx(control=targets[0], target=targets[1])
|
|
23
|
+
elif op == "MEASURE":
|
|
24
|
+
for q in targets:
|
|
25
|
+
circ.measure(q, q)
|
|
26
|
+
|
|
27
|
+
return circ
|
qsys/ir/types.py
ADDED
qsys/logging.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
LOGGER_NAME = "qsys"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_logger(level: int = logging.INFO) -> logging.Logger:
|
|
7
|
+
logger = logging.getLogger(LOGGER_NAME)
|
|
8
|
+
if not logger.handlers:
|
|
9
|
+
handler = logging.StreamHandler()
|
|
10
|
+
fmt = logging.Formatter("[%(levelname)s] %(name)s: %(message)s")
|
|
11
|
+
handler.setFormatter(fmt)
|
|
12
|
+
logger.addHandler(handler)
|
|
13
|
+
logger.setLevel(level)
|
|
14
|
+
return logger
|
qsys/runtime/__init__.py
ADDED
|
Binary file
|
|
Binary file
|