diff --git a/.agents/skills/pyzx-optimization/mcp/bridge.py b/.agents/skills/pyzx-optimization/mcp/bridge.py index 29e64530..450c9f19 100644 --- a/.agents/skills/pyzx-optimization/mcp/bridge.py +++ b/.agents/skills/pyzx-optimization/mcp/bridge.py @@ -10,7 +10,7 @@ def compute(v): import numpy as np import pyzx as zx - if version("pyzx") != "0.10.6": + if version("pyzx") != "0.10.8": raise ValueError("Unexpected PyZX version; restore the frozen environment") circuit = zx.Circuit(v["numQubits"]) names = {"H": "HAD", "X": "NOT", "CX": "CNOT"} diff --git a/.agents/skills/pyzx-optimization/mcp/contracts.mjs b/.agents/skills/pyzx-optimization/mcp/contracts.mjs index c4b7ad9f..fd16b94d 100644 --- a/.agents/skills/pyzx-optimization/mcp/contracts.mjs +++ b/.agents/skills/pyzx-optimization/mcp/contracts.mjs @@ -5,7 +5,7 @@ const metrics = obj({ gates: int(0), twoQubitGates: int(0), tCount: int(0) }); export const definition = defineScienceTool({ name: "optimize_pyzx_circuit", description: "Optimize Clifford+T circuits through PyZX ZX rewriting and extraction. Return input/output OpenQASM 2 and gate counts. Independent full-unitary comparison is optional; circuit size is determined by the user's resources. No measurements, hardware routing or guaranteed gate-count improvement.", - source: { name: "pyzx", version: "0.10.6", repository: "https://github.com/zxcalc/pyzx" }, + source: { name: "pyzx", version: "0.10.8", repository: "https://github.com/zxcalc/pyzx" }, inputSchema: obj({ ...circuitSchema(), referenceMode: referenceModeSchema }), checkInput: checkCircuit, resultSchema: referenceAwareResultSchema({ inputQasm: { type: "string", minLength: 1 }, optimizedQasm: { type: "string", minLength: 1 }, before: metrics, after: metrics, unitaryMaxError: nullable(num(0, 2)), equivalenceTolerance: nullable({ const: 1e-8 }), equivalentUpToGlobalPhase: nullable({ type: "boolean" }), bitOrder: { const: "left-to-right q0,q1,..." } }, ["unitaryMaxError", "equivalenceTolerance", "equivalentUpToGlobalPhase"]), diff --git a/.agents/skills/pyzx-optimization/pyproject.toml b/.agents/skills/pyzx-optimization/pyproject.toml index 3de78c6c..ce004957 100644 --- a/.agents/skills/pyzx-optimization/pyproject.toml +++ b/.agents/skills/pyzx-optimization/pyproject.toml @@ -2,7 +2,7 @@ name = "openquantum-pyzx-optimization-bridge" version = "0.1.0" requires-python = ">=3.12,<3.13" -dependencies = ["pyzx==0.10.6"] +dependencies = ["pyzx==0.10.8"] [tool.uv] package = false diff --git a/.agents/skills/pyzx-optimization/uv.lock b/.agents/skills/pyzx-optimization/uv.lock index 2cb75951..45a6614f 100644 --- a/.agents/skills/pyzx-optimization/uv.lock +++ b/.agents/skills/pyzx-optimization/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 3 +revision = 5 requires-python = "==3.12.*" [[package]] @@ -157,7 +157,7 @@ dependencies = [ ] [package.metadata] -requires-dist = [{ name = "pyzx", specifier = "==0.10.6" }] +requires-dist = [{ name = "pyzx", specifier = "==0.10.8" }] [[package]] name = "parso" @@ -246,7 +246,7 @@ wheels = [ [[package]] name = "pyzx" -version = "0.10.6" +version = "0.10.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ipywidgets" }, @@ -254,11 +254,10 @@ dependencies = [ { name = "numpy" }, { name = "pyperclip" }, { name = "tqdm" }, - { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/79/eb5d252a73408b894fc7a34c88cd403baeaf91f5957ee8d27eefa3fd718e/pyzx-0.10.6.tar.gz", hash = "sha256:ba770fb71ba84f8a9d8604246b21f87bf4dfafdcc4c01d480424e566fc1f4c6a", size = 548797, upload-time = "2026-09-01T13:50:46.17Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/e4/d1f2be2de0d2358f509749e17848476c617c69c1743b096763cfaa885178/pyzx-0.10.8.tar.gz", hash = "sha256:d44218be9d648be1a3e30a45c1b22432a1624d88f513e65f3729d0cfcb385b77", size = 562421, upload-time = "2026-11-01T15:38:58.74Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/c8/1284142d46cd8f1ae9418aae5494050d74eadc2891b0b81e73cca4b214ed/pyzx-0.10.6-py3-none-any.whl", hash = "sha256:3e98a54b5da2385e5dea5754c0d1b57c4a5dc1c9bc5eb1a1eaee4115deb324d9", size = 576349, upload-time = "2026-09-01T13:50:44.171Z" }, + { url = "https://files.pythonhosted.org/packages/61/c7/d09347195ddad63b50c23d0cef77f1483476daa2454ac7c5cbff663d9477/pyzx-0.10.8-py3-none-any.whl", hash = "sha256:a618610c2f96db506685da6bd067e6e0cb504847ba9f63d02b1e9bd4fe5dfcb5", size = 576263, upload-time = "2026-11-01T15:38:56.844Z" }, ] [[package]] @@ -296,15 +295,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl", hash = "sha256:f775618166caa0396c8e337099240f2bd3e5e917d203b2e6fbe21a58d3cb1f6b", size = 86211, upload-time = "2026-08-03T08:32:34.48Z" }, ] -[[package]] -name = "typing-extensions" -version = "4.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, -] - [[package]] name = "wcwidth" version = "0.8.3" diff --git a/.github/workflows/pyzx-sdk.yml b/.github/workflows/pyzx-sdk.yml new file mode 100644 index 00000000..b786d515 --- /dev/null +++ b/.github/workflows/pyzx-sdk.yml @@ -0,0 +1,50 @@ +name: PyZX correctness + +permissions: + contents: read + +on: + pull_request: + paths: + - '.agents/skills/pyzx-optimization/**' + - 'benchmarks/upstream-updates/pyzx_regressions.py' + - 'tests/unitary-next-live.test.mjs' + - '.github/workflows/pyzx-sdk.yml' + push: + branches: [main] + paths: + - '.agents/skills/pyzx-optimization/**' + - 'benchmarks/upstream-updates/pyzx_regressions.py' + - 'tests/unitary-next-live.test.mjs' + - '.github/workflows/pyzx-sdk.yml' + +jobs: + numerical-regressions: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + steps: + - uses: actions/checkout@v7 + - uses: actions/setup-node@v6 + with: + node-version: '24' + cache: npm + - run: npm ci --ignore-scripts + - uses: astral-sh/setup-uv@v7 + - name: Prepare the frozen production SDK + run: node scripts/setup-paper-tools.mjs pyzx-optimization + - name: Real MCP, independent references and error boundaries + env: + OPENQUANTUM_REAL_UNITARY_NEXT: '1' + run: node --test --test-name-pattern='real PyZX' tests/unitary-next-live.test.mjs + - name: Save numerical evidence + if: always() + uses: actions/upload-artifact@v7 + with: + name: pyzx-correctness-${{ matrix.os }} + path: .openquantum/unitary-next-evidence + include-hidden-files: true + if-no-files-found: error diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 7b13eb2e..ce67cfeb 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -17,7 +17,7 @@ Pasqal agent-marketplace 的实验方法与三个研究示例固定于 `2b0353d8 | qBraid | Apache-2.0 | 固定 0.12.2,独立 Python 环境调用 Qiskit/QASM2/Cirq 双向转换;不使用云 Provider,不复制上游源码 | [qBraid/qBraid](https://github.com/qBraid/qBraid/tree/v0.12.2) | | QDMI | Apache-2.0 WITH LLVM-exception | 固定 v1.3.3 / `18cfb67fd9042761d3005c2f8655751c1758f9c5`;显式安装器在忽略目录构建官方示例 client driver/device 并保留 LICENSE,Python ctypes 只读查询 client ABI;不重新分发上游源码或二进制 | [Munich-Quantum-Software-Stack/QDMI](https://github.com/Munich-Quantum-Software-Stack/QDMI/tree/v1.3.3) | | Cirq / Qiskit SDK | Apache-2.0 | qBraid 环境固定 Cirq 1.6.1 / Qiskit 2.5.2,用于电路表示、转换和独立矩阵对照;由包管理器安装并保留各自许可证 | [Cirq](https://github.com/quantumlib/Cirq) / [Qiskit](https://github.com/Qiskit/qiskit) | -| PyZX | Apache-2.0 | 固定 0.10.6,隔离环境调用ZX 重写、Clifford+T 门数比较与完整酉矩阵对照;上游源码由包管理器安装,保留原始许可证,不随本仓库重新分发 | [zxcalc/pyzx](https://github.com/zxcalc/pyzx) | +| PyZX | Apache-2.0 | 固定 0.10.8,隔离环境调用ZX 重写、Clifford+T 门数比较与完整酉矩阵对照;上游源码由包管理器安装,保留原始许可证,不随本仓库重新分发 | [zxcalc/pyzx](https://github.com/zxcalc/pyzx) | | Graphix | Apache-2.0 | 固定 0.4,隔离环境调用电路到 MBQC 模式、资源图、自适应测量和纠正输出;上游源码由包管理器安装,保留原始许可证,不随本仓库重新分发 | [TeamGraphix/graphix](https://github.com/TeamGraphix/graphix) | | Symmer | MIT | 固定 0.0.13@a4ba56e3,隔离环境调用指定 Pauli 对称性扇区的降比特与同扇区保谱检查;上游源码由包管理器安装,保留原始许可证,不随本仓库重新分发 | [qmatter-labs/symmer](https://github.com/qmatter-labs/symmer) | | PauLie | MIT | 固定 0.2.3,隔离环境调用Pauli 生成元的 Lie 闭包、分类维数与独立矩阵参照;上游源码由包管理器安装,保留原始许可证,不随本仓库重新分发 | [QPauLie/PauLie](https://github.com/QPauLie/PauLie) | diff --git a/benchmarks/upstream-updates/probes/pyzx/pyproject.toml b/benchmarks/upstream-updates/probes/pyzx/pyproject.toml new file mode 100644 index 00000000..faa99505 --- /dev/null +++ b/benchmarks/upstream-updates/probes/pyzx/pyproject.toml @@ -0,0 +1,8 @@ +[project] +name = "openquantum-pyzx-upstream-probe" +version = "0.1.0" +requires-python = ">=3.12,<3.13" +dependencies = ["pyzx==0.10.8"] + +[tool.uv] +package = false diff --git a/benchmarks/upstream-updates/probes/pyzx/uv.lock b/benchmarks/upstream-updates/probes/pyzx/uv.lock new file mode 100644 index 00000000..457dd91a --- /dev/null +++ b/benchmarks/upstream-updates/probes/pyzx/uv.lock @@ -0,0 +1,314 @@ +version = 1 +revision = 3 +requires-python = "==3.12.*" + +[[package]] +name = "asttokens" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/25/1e/faf0f247f6f881b98fc4d6d07e14085cb89d13665084e6d6ac1dc2c03d0b/asttokens-3.0.2.tar.gz", hash = "sha256:3ecdbd8f2cc195f53ccada3a613538bb5f9ef6f6869129f13e03c30a677b8fe2", size = 63136, upload-time = "2026-07-12T03:31:49.084Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/2b/04b8a15f3a1c77bc79ddf5c73875327f34b4fa75982df2b76e45e402d364/asttokens-3.0.2-py3-none-any.whl", hash = "sha256:9da13157f5b28becde0bd374fc677dcd3c290614264eff096f167c469cd9f933", size = 28702, upload-time = "2026-07-12T03:31:47.542Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "comm" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/13/7d740c5849255756bc17888787313b61fd38a0a8304fc4f073dfc46122aa/comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971", size = 6319, upload-time = "2025-07-25T14:02:04.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, +] + +[[package]] +name = "ipython" +version = "9.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ipython-pygments-lexers" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit" }, + { name = "psutil", marker = "sys_platform != 'cygwin' and sys_platform != 'emscripten'" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/32/99451b1283ec5d92ad77073f12e1c667dc10775384d8f15c2914207149dd/ipython-9.17.1.tar.gz", hash = "sha256:8919be8c27f20a6f4423145028063f6637b42a03ce57665bb12015ee1f073529", size = 4539289, upload-time = "2026-09-01T08:29:32.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/1e/65b59cf518c106aa755e7f7da3099027738687a862ec785060702a481320/ipython-9.17.1-py3-none-any.whl", hash = "sha256:6d1645743cfd1a07eb695d85aa2b5fa66721f8cbae9431d4049f7084bbf06509", size = 639038, upload-time = "2026-09-01T08:29:30.673Z" }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, +] + +[[package]] +name = "ipywidgets" +version = "8.1.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "comm" }, + { name = "ipython" }, + { name = "jupyterlab-widgets" }, + { name = "traitlets" }, + { name = "widgetsnbextension" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/7c/6db60eddf38547353b06d57941f5eee22a990640ce30479fd71a810507f2/ipywidgets-8.1.9.tar.gz", hash = "sha256:bcccba38a6ec3253f7a39c943cea5b9ad01999ce071396171adbc51c6a6a8613", size = 117252, upload-time = "2026-08-18T08:54:24.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/55/298e9b3b864a198234997e87a1471c1b17d7f3546ace6d18fb5cf1ce24b2/ipywidgets-8.1.9-py3-none-any.whl", hash = "sha256:f2b8cbcaae10252b809fbe4d7470db75c09b769a32cbf816d20e5ca6d3c5a79d", size = 140101, upload-time = "2026-08-18T08:54:22.339Z" }, +] + +[[package]] +name = "jedi" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/46/b7/a3635f6a2d7cf5b5dd98064fc1d5fbbafcb25477bcea204a3a92145d158b/jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011", size = 3119416, upload-time = "2026-05-01T23:38:47.814Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/93/242e2eab5fe682ffcb8b0084bde703a41d51e17ee0f3a31ff0d9d813620a/jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67", size = 4884812, upload-time = "2026-05-01T23:38:43.919Z" }, +] + +[[package]] +name = "jupyterlab-widgets" +version = "3.0.17" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/8b/e739cf9066ad5037a2d4b0a403f06da374fdccb9748221661c8b492d3dbc/jupyterlab_widgets-3.0.17.tar.gz", hash = "sha256:6e61fe21ca8a66039180a5cc52a433e07279d2fee79c8be963e00d55193f17a8", size = 213919, upload-time = "2026-08-18T08:52:17.511Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/ef/6d27fc118f58cb24886da413545a7efb0853d405fddbfd8b2d9ac09fbed4/jupyterlab_widgets-3.0.17-py3-none-any.whl", hash = "sha256:40ac1e9955acf116c4d995d9bfa082d86ad9ec6d91c4f134827cf5e0a5eb75e0", size = 217292, upload-time = "2026-08-18T08:52:15.47Z" }, +] + +[[package]] +name = "lark" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/c0/9f7c9a46090390368a4d7bcb76bb87a4a36c421e4c0792cdb53486ffac7a/matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79", size = 8150, upload-time = "2026-05-08T17:33:33.49Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/09/5b161152e2d90f7b87f781c2e1267494aef9c32498df793f73ad0a0a494a/matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6", size = 9534, upload-time = "2026-05-08T17:33:32.055Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/01/11703282db468b85f6f7b8c7f22d058de5970d5c7e60a3a8aaa313c3de36/numpy-2.5.3.tar.gz", hash = "sha256:df2d5874ff183595a4ba404edd04f6bd9b5505c1d7708573f6a6c17489a67563", size = 20791231, upload-time = "2026-09-06T16:27:47.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/50/8fdbb16af64895706a45f06a4068e29db732ec180f3c1375f14123359138/numpy-2.5.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cb189f09db39283b26bfd061ec16189e14f71c6755207f72a0f7540867afe5b9", size = 16994982, upload-time = "2026-09-06T16:24:29.244Z" }, + { url = "https://files.pythonhosted.org/packages/60/39/789131c1188c078dcb3a1692e72e1e050c68b88ffe72c9ccaac9bcd7a9cd/numpy-2.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f59a878c33d6b88122d80d239bb3b845d58708750b0cb06a09aebb9b18ec696c", size = 12009327, upload-time = "2026-09-06T16:24:32.491Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/a312e95696e5f601914dd8b6dd844692ba61670807417e24b68e337b5c70/numpy-2.5.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a72f874bc9e10e4b8f80426fb49716d5141f64442a0c8418065093ec8017fbb0", size = 5445405, upload-time = "2026-09-06T16:24:35.071Z" }, + { url = "https://files.pythonhosted.org/packages/30/d0/5623a1707ed4fe16e3909fe3cf5ee3da004ae677ad23d83bbf3adf1a6faf/numpy-2.5.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fc36dc566135b5eceec4cf89758fcb719266a019ef07dae1754ae7c9f617ef3e", size = 6783213, upload-time = "2026-09-06T16:24:37.253Z" }, + { url = "https://files.pythonhosted.org/packages/f1/32/84146fc020ad3c25f805f70ab60da46fe3c540a21369754a7e4369754b6f/numpy-2.5.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76c2c1e6bfa5c84adc6434dfbf013aa92096a7985221762c8f11fedfd20fff58", size = 15687872, upload-time = "2026-09-06T16:24:39.751Z" }, + { url = "https://files.pythonhosted.org/packages/65/af/aa78d1a88805456e212b65461354cd943197fb9acecc4c90fd12295123a3/numpy-2.5.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7e18c623bb5c95acb3b3328861272816ba199fb531921c5d6d0b675f1fde9e3", size = 16717410, upload-time = "2026-09-06T16:24:42.745Z" }, + { url = "https://files.pythonhosted.org/packages/3b/24/faa79d865e69a97ba17473b23a1b74094b2259c03e820c70297293b9ea49/numpy-2.5.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4f8929ee6c96bfbd7b4ed2032e0c03af86fe1826740ab61ddabf9072d06e57ff", size = 17040975, upload-time = "2026-09-06T16:24:45.961Z" }, + { url = "https://files.pythonhosted.org/packages/62/4a/8877e629445a7176297dffcaf9c485faa96a95d81728a62521ad55bd4c0f/numpy-2.5.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b5d93cf48f687479941d12b69c873ad2cc76bbd487f0091c2200636497f34034", size = 18476479, upload-time = "2026-09-06T16:24:49.35Z" }, + { url = "https://files.pythonhosted.org/packages/c8/db/35e1c2d38b04cbd5b731f9d71495e055e813197669d22b612f11748d2ff9/numpy-2.5.3-cp312-cp312-win32.whl", hash = "sha256:bf63afbe037eb5d2fe87fbcc7778e61da53ebaf21d938a4515aa73b62532a5d4", size = 6133378, upload-time = "2026-09-06T16:24:51.915Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/accf6d4f0c80c5d9ba9735d6b1550e444180599f34dec69ca01360f717ad/numpy-2.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0a59a421a32580a009e8a1751345bf829631b990dc1794b80514ab722b435def", size = 12567828, upload-time = "2026-09-06T16:24:54.255Z" }, + { url = "https://files.pythonhosted.org/packages/22/43/1764aff32e4652526ae2f71fa8b3efd8d25c8a3d6926914454e47138ed1e/numpy-2.5.3-cp312-cp312-win_arm64.whl", hash = "sha256:ccb32e0525d29e8b0572eb84c9a57af0e7a4e615726927506f55063c62414034", size = 10485432, upload-time = "2026-09-06T16:24:57.278Z" }, +] + +[[package]] +name = "openquantum-pyzx-upstream-probe" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "pyzx" }, +] + +[package.metadata] +requires-dist = [{ name = "pyzx", specifier = "==0.10.8" }] + +[[package]] +name = "parso" +version = "0.8.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/4b/90c937815137d43ce71ba043cd3566221e9df6b9c805f24b5d138c9d40a7/parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1", size = 401824, upload-time = "2026-05-01T23:13:02.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/5d/8268b644392ee874ee82a635cd0df1773de230bde356c38de28e298392cc/parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c", size = 107025, upload-time = "2026-05-01T23:12:58.867Z" }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.53" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/ea/39b988c938f75cb75d7045b5c69f8bfed47ee2152c8837fb403de29d6fb8/prompt_toolkit-3.0.53.tar.gz", hash = "sha256:9ec8a0ad96d5c56148b3f914aa79c1564c3fde5d2e6b876e7bc327e353cf8fa6", size = 435492, upload-time = "2026-07-26T20:56:14.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/6f/84908cad2d6aa5144abcf7b42709fe4fdb459bc640ec7ac5786e7693dabc/prompt_toolkit-3.0.53-py3-none-any.whl", hash = "sha256:01c0891d7f9237d5e339f7d3e42cdae80b7534abb1c7c0e3352efba6231492f2", size = 392288, upload-time = "2026-07-26T20:56:12.512Z" }, +] + +[[package]] +name = "psutil" +version = "7.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" }, + { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" }, + { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" }, + { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" }, + { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" }, + { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" }, + { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" }, + { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, +] + +[[package]] +name = "pure-eval" +version = "0.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/9f/abfd2959e9261dd5217ca8551d4de211ca6ab26fe9b72cf44731ff6c4442/pure_eval-0.2.4.tar.gz", hash = "sha256:260c2774686e651b79f8b8e7fc9d80b3599ea6a66334b47d5f4abb69fc2c0ea1", size = 20563, upload-time = "2026-09-10T21:41:22.836Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/18/83376915176eb058cb86470eb7396388a13350b09a8f233a79303bbcbc5b/pure_eval-0.2.4-py3-none-any.whl", hash = "sha256:96cae060a313cfaad51bb761278bfb0e62dc0248d9315a81173752dc546cd37a", size = 11893, upload-time = "2026-09-10T21:41:21.532Z" }, +] + +[[package]] +name = "pygments" +version = "2.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" }, +] + +[[package]] +name = "pyperclip" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, +] + +[[package]] +name = "pyzx" +version = "0.10.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipywidgets" }, + { name = "lark" }, + { name = "numpy" }, + { name = "pyperclip" }, + { name = "tqdm" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8d/e4/d1f2be2de0d2358f509749e17848476c617c69c1743b096763cfaa885178/pyzx-0.10.8.tar.gz", hash = "sha256:d44218be9d648be1a3e30a45c1b22432a1624d88f513e65f3729d0cfcb385b77", size = 562421, upload-time = "2026-11-01T15:38:58.74Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/c7/d09347195ddad63b50c23d0cef77f1483476daa2454ac7c5cbff663d9477/pyzx-0.10.8-py3-none-any.whl", hash = "sha256:a618610c2f96db506685da6bd067e6e0cb504847ba9f63d02b1e9bd4fe5dfcb5", size = 576263, upload-time = "2026-11-01T15:38:56.844Z" }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, +] + +[[package]] +name = "tqdm" +version = "4.70.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/ea/b2a5bd54b28a324dae8211928b2d730b6547500342c7e6c6dea08bd0a485/tqdm-4.70.1.tar.gz", hash = "sha256:cefd0eca11b2a37a3aee776544d4f4ae913f02688135b5556b8788dfa474afc4", size = 171846, upload-time = "2026-09-11T07:25:16.601Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/03/921a3d3c75785aca9ebfbfcabfbc3a1be12e2ab5265deb026d55a5a3f83e/tqdm-4.70.1-py3-none-any.whl", hash = "sha256:c293e525e6fef9c20e8728fd4612df02a0aa31bb5fe91ecd93e123b1b7bffa73", size = 80199, upload-time = "2026-09-11T07:25:14.599Z" }, +] + +[[package]] +name = "traitlets" +version = "5.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/2e/a7fbfe268c8a3b32546930c0297c101d65a4a14c304ad5790a9f478f0e4e/traitlets-5.16.1.tar.gz", hash = "sha256:ed900c2b631aa3a112811139fa97b8d2c3bad5e989656bba4b7e52c7852c18c1", size = 166137, upload-time = "2026-08-03T08:32:36.848Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/66/0d785f0bc5e4315a96c989bb476d0fc07ea4f85132550c7b156ca2035d52/traitlets-5.16.1-py3-none-any.whl", hash = "sha256:f775618166caa0396c8e337099240f2bd3e5e917d203b2e6fbe21a58d3cb1f6b", size = 86211, upload-time = "2026-08-03T08:32:34.48Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/36/57/ed58088fafdf4c55a0ad6bde846502567645424d7ebf325230b9237f4085/wcwidth-0.8.3.tar.gz", hash = "sha256:d128512515fbf4612e0ff21fd6380399210318b7b54a9af59dff8454cf9730eb", size = 1458450, upload-time = "2026-08-28T18:10:06.875Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c4/0e/57f6bb3024a597b2e8ec4aee710ffe62ddc95af2e2bb1ee7a7abdc22c68c/wcwidth-0.8.3-py3-none-any.whl", hash = "sha256:d5b73dba6158a595ec9370350e7f2637bcac8d6c5e4fde34f30fcffb6103a5e4", size = 331669, upload-time = "2026-08-28T18:10:04.909Z" }, +] + +[[package]] +name = "widgetsnbextension" +version = "4.0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bf/60/bc7a980fc78837d6ef8f5940cca4cadc433364503a4c4d42e2a7a0de3231/widgetsnbextension-4.0.16.tar.gz", hash = "sha256:adeea0ae78f0856ee4945f413299801b82a0a01416303301f39a704282a37b73", size = 1111094, upload-time = "2026-08-18T08:52:55.859Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/95/40e17e20046b7bc820d29d09ae84ec157ec8dd6e6f6cd722626292c31b2e/widgetsnbextension-4.0.16-py3-none-any.whl", hash = "sha256:a31a8774885b96fe825462f5d6496166f0c7cae111195b6465c801d230eb5a4e", size = 2225148, upload-time = "2026-08-18T08:52:53.736Z" }, +] diff --git a/benchmarks/upstream-updates/pyzx_regressions.py b/benchmarks/upstream-updates/pyzx_regressions.py new file mode 100644 index 00000000..7fc88b87 --- /dev/null +++ b/benchmarks/upstream-updates/pyzx_regressions.py @@ -0,0 +1,62 @@ +"""Read-only SDK upgrade diagnostics; no product pins or scientific acceptance changes.""" +import json +import sys +import platform +from fractions import Fraction +from importlib.metadata import version +from pathlib import Path +import numpy as np +import pyzx as zx + +repo = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).resolve().parents[2] +sys.path.insert(0, str(repo / "src/lib")) +from quantum_reference import circuit_unitary + +TOLERANCE = 1e-8 # Existing PyZX bridge engineering reference tolerance. +def error(actual, expected): + overlap = np.vdot(actual, expected) + phase = overlap / abs(overlap) if abs(overlap) > 1e-14 else 1 + return float(np.max(np.abs(expected - phase * actual))) + +records = [] +def record(name, observed, expected, scope): + delta = error(observed, expected) + records.append(dict(name=name,unitaryMaxError=delta,passed=delta <= TOLERANCE,scope=scope)) + +# Independent dense reference, q0 is the left tensor factor. +expected = circuit_unitary(2, [ + {"gate":"CX","targets":[1,0]}, {"gate":"CX","targets":[0,1]}, + {"gate":"RX","targets":[1],"angle":np.pi/2}, +]) +original = zx.Circuit(2) +original.add_gate("CNOT",1,0) +original.add_gate("CNOT",0,1) +original.add_gate("XPhase",1,phase=Fraction(1,2)) +record("optimizer-input-calibration",original.to_matrix(),expected,"SDK input against independent circuit_unitary") +optimized = zx.optimize.basic_optimization(original.copy()) +record("swap-remapping-XPhase",optimized.to_matrix(),expected,"public pyzx.optimize.basic_optimization") +# U3 in the QASM convention; derive its matrix without PyZX. +theta,phi,lam = np.pi/3,np.pi/5,np.pi/7 +u3 = np.array([ + [np.cos(theta/2),-np.exp(1j*lam)*np.sin(theta/2)], + [np.exp(1j*phi)*np.sin(theta/2),np.exp(1j*(phi+lam))*np.cos(theta/2)], +], dtype=complex) +c = zx.Circuit(1) +c.add_gate("U3",0,Fraction(1,3),Fraction(1,5),Fraction(1,7)) +record("U3-input-calibration",c.to_matrix(),u3,"analytic U3 matrix, up to global phase") +record("U3-adjoint",c.adjoint().to_matrix(),u3.conj().T,"public Circuit.adjoint") +# Same authorized gate vocabulary and public pipeline as the current bridge; +# this is SDK-only, not a claim that the version-guarded bridge accepts the new pin. +tool = zx.Circuit(2) +for gate,args in [("CNOT",(1,0)),("CNOT",(0,1)),("HAD",(1,)),("S",(1,)),("HAD",(1,))]: + tool.add_gate(gate,*args) +graph=tool.to_graph() +zx.simplify.full_reduce(graph) +out=zx.optimize.basic_optimization(zx.extract.extract_circuit(graph).to_basic_gates()).to_basic_gates() +record("existing-bridge-public-pipeline",out.to_matrix(),expected,"SDK pipeline only; MCP/version guard not exercised") +payload=dict(pyzx=version("pyzx"),numpy=version("numpy"),python=platform.python_version(), + platform=platform.platform(),reference="src/lib/quantum_reference.py plus analytic 2x2 U3", + tolerance=TOLERANCE,records=records,scientificAcceptance="not_evaluated", + productPinsChanged=False,upstreamUrl="https://github.com/zxcalc/pyzx/releases/tag/v0.10.8") +print(json.dumps(payload,indent=2)) +sys.exit(0 if all(r["passed"] for r in records) else 1) diff --git a/docs/integrations/SDK_INVENTORY.md b/docs/integrations/SDK_INVENTORY.md index 7b170b53..9e32b46b 100644 --- a/docs/integrations/SDK_INVENTORY.md +++ b/docs/integrations/SDK_INVENTORY.md @@ -71,7 +71,7 @@ | `pygsti-characterization` | pyGSTi | python | pygsti[reports]==0.10.2 | Circuit、Model、DataSet、GST/RB/RPE 与拟合报告 | 保留原始计数、SPAM、gauge、样本数、拟合诊断与不确定度。合成数据明确标记;拟合质量与实际硬件性能分开。 | | `pyquil-simulation` | pyQuil | python | pyquil==4.21.0 | Program、自定义 gates、Quil 控制流、编译器与模拟/设备接口 | 按固定版本、已安装可选组件及目标平台核实 | | `pytket-compilation` | pytket | python | pytket==2.18.4 | 自定义 Circuit、Architecture、pass、predicate、路由与后端 | 按固定版本、已安装可选组件及目标平台核实 | -| `pyzx-optimization` | PyZX | python | pyzx==0.10.6 | Circuit、Graph、重写策略、提取、优化与等价性流程 | 按固定版本、已安装可选组件及目标平台核实 | +| `pyzx-optimization` | PyZX | python | pyzx==0.10.8 | Circuit、Graph、重写策略、提取、优化与等价性流程 | 按固定版本、已安装可选组件及目标平台核实 | | `qblox-scheduling` | Qblox Scheduler | python | qblox-scheduler==1.0.0b8 | 自定义 Schedule、operation、pulse、资源、硬件配置与编译 | 本地程序构建与外部调用分开;账户、凭据、费用、设备操作和数据外发沿用宿主及用户授权。原生 SDK 访问不自动启用服务。 | | `qbraid-conversion` | qBraid / Cirq | python | qbraid==0.12.2, qiskit==2.5.2, cirq-core==1.6.1, ply==3.11 | 程序类型、转换图、转译路径、序列化与供应商接口 | 按固定版本、已安装可选组件及目标平台核实 | | `qcarchive-query` | QCArchive Portal | python | qcportal==0.70 | PortalClient、分子、记录、dataset、查询与任务提交接口 | 本地程序构建与外部调用分开;账户、凭据、费用、设备操作和数据外发沿用宿主及用户授权。原生 SDK 访问不自动启用服务。 | diff --git a/docs/integrations/UNITARY_NEXT_TOOLS.md b/docs/integrations/UNITARY_NEXT_TOOLS.md index 2168d65b..8a20e434 100644 --- a/docs/integrations/UNITARY_NEXT_TOOLS.md +++ b/docs/integrations/UNITARY_NEXT_TOOLS.md @@ -4,7 +4,7 @@ OpenQuantum 对四个上游项目做了计算适配,分别解决电路优化 | 项目与固定版本 | 连接 / Tool | 当前范围 | | --- | --- | --- | -| [PyZX 0.10.6](https://github.com/zxcalc/pyzx/tree/v0.10.6),Apache-2.0 | `pyzx_local` / `optimize_pyzx_circuit` | Clifford+T 电路的 ZX 重写、提取、QASM 和门数;可选完整酉矩阵对照 | +| [PyZX 0.10.8](https://github.com/zxcalc/pyzx/tree/v0.10.8),Apache-2.0 | `pyzx_local` / `optimize_pyzx_circuit` | Clifford+T 电路的 ZX 重写、提取、QASM 和门数;可选完整酉矩阵对照 | | [Graphix 0.4.1](https://github.com/TeamGraphix/graphix/tree/v0.4.1),Apache-2.0 | `graphix_local` / `simulate_graphix_pattern` | 电路转 MBQC 模式与资源图;可选测量过程模拟、输出纠正及独立态矢对照 | | [Symmer 0.0.13 / a4ba56e3](https://github.com/qmatter-labs/symmer/tree/a4ba56e3332424a65d4a2c088fd6363aca0c68e6),MIT | `symmer_local` / `taper_symmer_hamiltonian` | 实 Pauli Hamiltonian 按指定独立对称性和扇区降维;允许零量子位标量 | | [PauLie 0.2.3](https://github.com/QPauLie/PauLie/tree/v0.2.3),MIT | `paulie_local` / `analyze_paulie_algebra` | 非恒等 Pauli 生成元的分类与精确维数;可选闭包及独立实 Lie 空间对照 | diff --git a/docs/releases/2026-11-08-pyzx-upstream-update.md b/docs/releases/2026-11-08-pyzx-upstream-update.md new file mode 100644 index 00000000..9dce0645 --- /dev/null +++ b/docs/releases/2026-11-08-pyzx-upstream-update.md @@ -0,0 +1,85 @@ +# PyZX 0.10.8:升级前正确性对照 + +调研与验证日期:2026-11-08(Asia/Shanghai)。基线: +`0da563fa87c6592a8923d2eb98eea80f484d2151`。 + +**状态:2026-11-10 已在本 PR 接入生产依赖 0.10.8;尚未合并或部署。** +后续实施与本机验证见 [升级记录](2026-11-10-pyzx-production-upgrade.md)。 +下文保留 11 月 8 日隔离诊断的历史事实;#79 不再阻塞这个独立正确性修复。 + +## 为什么值得升级 + +[官方 v0.10.8 发布](https://github.com/zxcalc/pyzx/releases/tag/v0.10.8)修复了 +`basic_optimization` 在 CNOT 对重映射后把 XPhase 作用于错误量子位,以及 +`Circuit.adjoint` 对 U3 等门返回错误伴随的缺陷。两者属于已公开 SDK 能力, +会影响优化结果或等价性检查,不是单纯的新功能。 + +目标稳定 tag 的完整提交为 +[`d9691d0b2aaaab92f11ed0b2cabe2bcc36f8cc14`](https://github.com/zxcalc/pyzx/tree/d9691d0b2aaaab92f11ed0b2cabe2bcc36f8cc14); +[发行包](https://pypi.org/project/pyzx/0.10.8/)已在独立环境安装,使用 Apache-2.0。 +Python 最低版本升为 3.11,仓库现有 PyZX 环境使用 3.12。 + +## 实测结果与边界 + +两版均使用 Python 3.12.13、NumPy 2.5.3、macOS arm64;仅 PyZX 及其必要依赖锁差异。 +独立参考复用仓库的 `circuit_unitary`;U3 使用解析 2×2 矩阵的共轭转置。 +比较忽略整体相位,沿用现有 bridge 的 1e-8 工程容差,未修改科学阈值。 + +| 检查 | 0.10.6 最大矩阵偏差 | 0.10.8 最大矩阵偏差 | +| --- | ---: | ---: | +| CNOT(1,0)、CNOT(0,1)、XPhase(1,π/2) 优化 | 0.70710678,失败 | 5.56e-16,通过 | +| U3(π/3,π/5,π/7) 的 adjoint | 1.0,失败 | 4.00e-16,通过 | +| 现有 bridge 的公开计算管线,同输入 H-S-H 表达 | 5.56e-16,通过 | 5.56e-16,通过 | + +输入矩阵本身的两个独立校准在两版均通过。旧版探针退出码 1,新版退出码 0。 +完整观测见[旧版 JSON](evidence/2026-11-08-pyzx-old.json)与 +[新版 JSON](evidence/2026-11-08-pyzx-new.json);包含锁、探针及参考文件摘要。 + +这是两个明确 SDK 反例及一个公开管线小例。尚未执行新版 MCP 握手、版本守卫、 +全部 Tool 输入、宿主注册、全部 SDK API、其他平台或科学 Acceptance。 +`scientificAcceptance=not_evaluated`。不能据此宣称所有旧版便捷 Tool 调用失败, +或宣称新版已经接入、发布、部署。 + +## 复跑 + +[探针](../../benchmarks/upstream-updates/pyzx_regressions.py)使用公开 API; +[开发环境](../../benchmarks/upstream-updates/probes/pyzx/pyproject.toml)及 +[冻结锁](../../benchmarks/upstream-updates/probes/pyzx/uv.lock)不由产品安装器加载。 +先选择独立外置盘目录作为 `OQ_CHECK_DIR`,保持用户使用中的环境不变: + +```bash +export OQ_CHECK_DIR="/path/to/isolated/external-disk-directory" +mkdir -p "$OQ_CHECK_DIR/tmp" +export UV_CACHE_DIR="$OQ_CHECK_DIR/uv-cache" +export UV_PYTHON_INSTALL_DIR="$OQ_CHECK_DIR/python-install" +export TMPDIR="$OQ_CHECK_DIR/tmp" +export UV_LINK_MODE=copy + +# 旧版必须从旧提交的冻结 manifest/lock 准备,当前产品已为 0.10.8。 +git worktree add --detach "$OQ_CHECK_DIR/old-source" 7a23b15fbc37d21321ea2b7486cba5c1f533e62f +UV_PROJECT_ENVIRONMENT="$OQ_CHECK_DIR/old/pyzx-optimization" uv sync --frozen --no-dev --project "$OQ_CHECK_DIR/old-source/.agents/skills/pyzx-optimization" --python 3.12 +"$OQ_CHECK_DIR/old/pyzx-optimization/bin/python" benchmarks/upstream-updates/pyzx_regressions.py +# 历史生产 pin 为 0.10.6:上行预期退出 1,JSON 中两个 SDK 反例失败。 + +UV_PROJECT_ENVIRONMENT="$OQ_CHECK_DIR/new" uv sync --frozen --no-dev --project benchmarks/upstream-updates/probes/pyzx --python 3.12 +"$OQ_CHECK_DIR/new/bin/python" benchmarks/upstream-updates/pyzx_regressions.py +# 目标 0.10.8:预期退出 0。 +``` + +## 11 月 8 日迁移计划(历史,实施见后续记录) + +PR 正文是本次需求规格;本文件保存版本化诊断证据。最小迁移继续使用原有 SDK、 +`optimize_pyzx_circuit` 和两宿主适配,不增加公开接口、转发层或运行时。 + +1. 等 #79 完成共享清单修改,重新从最新 main 核对其他在办 PR。 +2. 同步原 PyZX manifest/lock、bridge 版本守卫、Tool source.version 与 SDK 生成清单; + 保持输入 schema、位序、可选参考和错误语义。 +3. 核对 0.10.7/0.10.8 移除的旧提取、Quantomatic JSON、d3 和 gadget API,以及新的 + flow 默认值。仓库本身本次定向搜索未使用这些接口;自定义 SDK 程序仍可能受影响。 +4. 运行现有 `tests/unitary-next-contracts.test.mjs`、 + `tests/harness-unitary-next.test.mjs` 与启用真实环境的 + `tests/unitary-next-live.test.mjs` 中 `real PyZX` 入口; + 补齐 Codex 清单、失败输入、required/skip 参考及生成清单检查。上述检查本轮未运行。 +5. 对最终提交重新完成 Standards/Spec 自审和适用 CI,方可把 Draft 转为待审。 + 生产升级前没有需要回退的 pin;升级后以同一 PR 的完整 pin/守卫/清单变更为回退单元, + 回退会重新带回本报告记录的旧版 SDK 缺陷。 diff --git a/docs/releases/2026-11-10-pyzx-production-upgrade.md b/docs/releases/2026-11-10-pyzx-production-upgrade.md new file mode 100644 index 00000000..b669637d --- /dev/null +++ b/docs/releases/2026-11-10-pyzx-production-upgrade.md @@ -0,0 +1,55 @@ +# PyZX 0.10.8:生产接入修复 + +日期:2026-11-10 UTC。基于 main `508ed5cd05eb44276fd5009641efa7e82a3b70c0`, +延续 PR #82;未合并、未部署,也没有改动用户已经安装的环境。 + +## 范围与依赖 + +同步原有 PyZX manifest、冻结锁、bridge 版本守卫、Tool source.version、SDK 设置目录、 +第三方说明与生成清单为 0.10.8。没有新增公开接口、宿主或运行时,未调整输入 schema、 +位序、1e-8 工程容差与科学 Acceptance。#77/#79 的 README/新 SDK 工作不属于这个正确性修复; +在最新 main 上只重生成 PyZX 清单行,不等待那些功能合并。 + +核对官方 v0.10.8 peeled commit `d9691d0b2aaaab92f11ed0b2cabe2bcc36f8cc14`。 +0.10.7/0.10.8 移除 PyQuil compiler、graph_tool/igraph、streaming_extract、旧 Quantomatic JSON、 +d3.draw、gadget_extract,并有 flow/symbolic 行为调整。本 bridge 使用的 Circuit、full_reduce、 +extract_circuit、basic_optimization 均仍支持,定向搜索未发现调用这些已移除入口。 +Python 最低 3.11;现有项目固定 3.12.x。自定义 SDK 程序可能需要自行迁移这些上游 API。 +锁只升级 PyZX 并移除不再需要的 typing-extensions,保留其他现有版本。 + +## 已完成的工程验证 + +在 Linux x86_64、Python 3.12.14、NumPy 2.5.3、Node 24.22.1 的独立工作区: + +- 旧 0.10.6 冻结环境复现 XPhase 重映射约 0.707 与 U3 adjoint 约 1 的错误。 +- 产品安装器按新冻结锁安装 0.10.8,五项独立矩阵检查全部通过;最大误差 <7e-16。 + [旧观测](evidence/2026-11-10-pyzx-old.json)、[新观测](evidence/2026-11-10-pyzx-new.json)。 + 探针中的 productPinsChanged=false 仅说明探针本身不修改 pin,不代表本 PR 未更新 pin。 +- 新版本守卫拒绝旧 0.10.6 环境,退出 1 并给出恢复冻结环境提示。 +- 真实 MCP 三项测试通过:原数值案例、生产 SDK 两个反例、required/skip/65-qubit auto 与无效输入。 +- 契约、SDK 访问与插件静态目录测试 16 项通过。插件目录测试不调用 Codex。 +- 实际 Harness 本地协议 fixture 完成 PyZX 工具发现、真实调用、成功/失败及结果持久化,一项通过。 + 修复原测试的会话标题请求抢先消耗一次性工具响应的竞态;默认四 SDK 测试范围不变, + 新增可选 OPENQUANTUM_UNITARY_NEXT_IDS=pyzx-optimization 以独立验证单项升级。 +- release/lint/locales/capability conformance/SDK guides/generated inventory 检查通过。 +- 新增 Ubuntu/macOS 真实 PyZX MCP 和独立矩阵 CI;远端结果需在当前提交后实际检查。 + +初次 npm --ignore-scripts 导致 node-pty 缺少原生模块,使用隔离缓存的正常 npm rebuild 后 +实际 Harness 定向测试通过。整仓 npm run check 在重建前已经启动,共 1249 项(878 通过、9 失败、362 跳过),包含其他 Harness 测试失败, +不将其记为整仓绿色;本修复仍保持 Draft,等当前 head 的 CI 和完整审查。 + +## 复跑与回退 + +```bash +npm ci +node scripts/setup-paper-tools.mjs pyzx-optimization +node scripts/build-chatgpt.mjs +node --test tests/unitary-next-contracts.test.mjs tests/sdk-access.test.mjs tests/codex-plugin-catalog.test.mjs +OPENQUANTUM_REAL_UNITARY_NEXT=1 node --test --test-name-pattern='real PyZX' tests/unitary-next-live.test.mjs +OPENQUANTUM_REAL_UNITARY_NEXT=1 OPENQUANTUM_UNITARY_NEXT_IDS=pyzx-optimization node --test tests/harness-unitary-next.test.mjs +npm run check +``` + +回退须整体回退 manifest/lock、守卫与版本元数据,不能只改 pin;回退会重引入上面的 SDK 缺陷。 +这些是限定案例的工程回归,不是全部 PyZX API、所有平台或科学 Acceptance; +scientificAcceptance/scientificValidation 继续为 not_evaluated,没有外部模型调用。 diff --git a/docs/releases/evidence/2026-11-08-pyzx-new.json b/docs/releases/evidence/2026-11-08-pyzx-new.json new file mode 100644 index 00000000..a4e2f242 --- /dev/null +++ b/docs/releases/evidence/2026-11-08-pyzx-new.json @@ -0,0 +1,52 @@ +{ + "pyzx": "0.10.8", + "numpy": "2.5.3", + "python": "3.12.13", + "platform": "macOS-27.2-arm64-arm-64bit", + "reference": "src/lib/quantum_reference.py plus analytic 2x2 U3", + "tolerance": 1e-08, + "records": [ + { + "name": "optimizer-input-calibration", + "unitaryMaxError": 3.3333914541515734e-16, + "passed": true, + "scope": "SDK input against independent circuit_unitary" + }, + { + "name": "swap-remapping-XPhase", + "unitaryMaxError": 5.558049685633556e-16, + "passed": true, + "scope": "public pyzx.optimize.basic_optimization" + }, + { + "name": "U3-input-calibration", + "unitaryMaxError": 6.684427777288334e-16, + "passed": true, + "scope": "analytic U3 matrix, up to global phase" + }, + { + "name": "U3-adjoint", + "unitaryMaxError": 4.0029660424867215e-16, + "passed": true, + "scope": "public Circuit.adjoint" + }, + { + "name": "existing-bridge-public-pipeline", + "unitaryMaxError": 5.558049685633556e-16, + "passed": true, + "scope": "SDK pipeline only; MCP/version guard not exercised" + } + ], + "scientificAcceptance": "not_evaluated", + "productPinsChanged": false, + "upstreamUrl": "https://github.com/zxcalc/pyzx/releases/tag/v0.10.8", + "checkedAt": "2026-11-08", + "baseRevision": "0da563fa87c6592a8923d2eb98eea80f484d2151", + "environment": "development-probe", + "lockPath": "benchmarks/upstream-updates/probes/pyzx/uv.lock", + "lockSha256": "1875c2468475f170c635a1dc5c6554b48a98d3091d0b67d346f7bf7c5733b906", + "probePath": "benchmarks/upstream-updates/pyzx_regressions.py", + "probeSha256": "0b5417e1c1d36559ad13fffaaeb574b5f4a774e66b3e6051aff8fc1b5a95d95f", + "referenceSha256": "1a45bd14f97019f4c11b8bfa58820f51d2d98d9f0e01e95a347b142918f2c1fd", + "exitCode": 0 +} diff --git a/docs/releases/evidence/2026-11-08-pyzx-old.json b/docs/releases/evidence/2026-11-08-pyzx-old.json new file mode 100644 index 00000000..236a2ef6 --- /dev/null +++ b/docs/releases/evidence/2026-11-08-pyzx-old.json @@ -0,0 +1,52 @@ +{ + "pyzx": "0.10.6", + "numpy": "2.5.3", + "python": "3.12.13", + "platform": "macOS-27.2-arm64-arm-64bit", + "reference": "src/lib/quantum_reference.py plus analytic 2x2 U3", + "tolerance": 1e-08, + "records": [ + { + "name": "optimizer-input-calibration", + "unitaryMaxError": 3.3333914541515734e-16, + "passed": true, + "scope": "SDK input against independent circuit_unitary" + }, + { + "name": "swap-remapping-XPhase", + "unitaryMaxError": 0.7071067811865475, + "passed": false, + "scope": "public pyzx.optimize.basic_optimization" + }, + { + "name": "U3-input-calibration", + "unitaryMaxError": 6.684427777288334e-16, + "passed": true, + "scope": "analytic U3 matrix, up to global phase" + }, + { + "name": "U3-adjoint", + "unitaryMaxError": 0.9999999999999997, + "passed": false, + "scope": "public Circuit.adjoint" + }, + { + "name": "existing-bridge-public-pipeline", + "unitaryMaxError": 5.558049685633556e-16, + "passed": true, + "scope": "SDK pipeline only; MCP/version guard not exercised" + } + ], + "scientificAcceptance": "not_evaluated", + "productPinsChanged": false, + "upstreamUrl": "https://github.com/zxcalc/pyzx/releases/tag/v0.10.8", + "checkedAt": "2026-11-08", + "baseRevision": "0da563fa87c6592a8923d2eb98eea80f484d2151", + "environment": "production-frozen", + "lockPath": ".agents/skills/pyzx-optimization/uv.lock", + "lockSha256": "cc0e9a97de0fa0439ea4add6b7f13b8bb0ba3b18954b94ebcfba94595676b458", + "probePath": "benchmarks/upstream-updates/pyzx_regressions.py", + "probeSha256": "0b5417e1c1d36559ad13fffaaeb574b5f4a774e66b3e6051aff8fc1b5a95d95f", + "referenceSha256": "1a45bd14f97019f4c11b8bfa58820f51d2d98d9f0e01e95a347b142918f2c1fd", + "exitCode": 1 +} diff --git a/docs/releases/evidence/2026-11-10-pyzx-new.json b/docs/releases/evidence/2026-11-10-pyzx-new.json new file mode 100644 index 00000000..fe5e66a7 --- /dev/null +++ b/docs/releases/evidence/2026-11-10-pyzx-new.json @@ -0,0 +1,43 @@ +{ + "pyzx": "0.10.8", + "numpy": "2.5.3", + "python": "3.12.14", + "platform": "Linux-6.18.57-x86_64-with-glibc2.41", + "reference": "src/lib/quantum_reference.py plus analytic 2x2 U3", + "tolerance": 1e-08, + "records": [ + { + "name": "optimizer-input-calibration", + "unitaryMaxError": 5.552748978401242e-16, + "passed": true, + "scope": "SDK input against independent circuit_unitary" + }, + { + "name": "swap-remapping-XPhase", + "unitaryMaxError": 5.558049685633556e-16, + "passed": true, + "scope": "public pyzx.optimize.basic_optimization" + }, + { + "name": "U3-input-calibration", + "unitaryMaxError": 6.684427777288334e-16, + "passed": true, + "scope": "analytic U3 matrix, up to global phase" + }, + { + "name": "U3-adjoint", + "unitaryMaxError": 4.710277376051326e-16, + "passed": true, + "scope": "public Circuit.adjoint" + }, + { + "name": "existing-bridge-public-pipeline", + "unitaryMaxError": 5.558049685633556e-16, + "passed": true, + "scope": "SDK pipeline only; MCP/version guard not exercised" + } + ], + "scientificAcceptance": "not_evaluated", + "productPinsChanged": false, + "upstreamUrl": "https://github.com/zxcalc/pyzx/releases/tag/v0.10.8" +} diff --git a/docs/releases/evidence/2026-11-10-pyzx-old.json b/docs/releases/evidence/2026-11-10-pyzx-old.json new file mode 100644 index 00000000..4f536ece --- /dev/null +++ b/docs/releases/evidence/2026-11-10-pyzx-old.json @@ -0,0 +1,43 @@ +{ + "pyzx": "0.10.6", + "numpy": "2.5.3", + "python": "3.12.14", + "platform": "Linux-6.18.57-x86_64-with-glibc2.41", + "reference": "src/lib/quantum_reference.py plus analytic 2x2 U3", + "tolerance": 1e-08, + "records": [ + { + "name": "optimizer-input-calibration", + "unitaryMaxError": 5.552748978401242e-16, + "passed": true, + "scope": "SDK input against independent circuit_unitary" + }, + { + "name": "swap-remapping-XPhase", + "unitaryMaxError": 0.7071067811865475, + "passed": false, + "scope": "public pyzx.optimize.basic_optimization" + }, + { + "name": "U3-input-calibration", + "unitaryMaxError": 6.684427777288334e-16, + "passed": true, + "scope": "analytic U3 matrix, up to global phase" + }, + { + "name": "U3-adjoint", + "unitaryMaxError": 0.9999999999999997, + "passed": false, + "scope": "public Circuit.adjoint" + }, + { + "name": "existing-bridge-public-pipeline", + "unitaryMaxError": 5.558049685633556e-16, + "passed": true, + "scope": "SDK pipeline only; MCP/version guard not exercised" + } + ], + "scientificAcceptance": "not_evaluated", + "productPinsChanged": false, + "upstreamUrl": "https://github.com/zxcalc/pyzx/releases/tag/v0.10.8" +} diff --git a/src/settings/server/project-settings-catalog.mjs b/src/settings/server/project-settings-catalog.mjs index 0be23e6a..cf989925 100644 --- a/src/settings/server/project-settings-catalog.mjs +++ b/src/settings/server/project-settings-catalog.mjs @@ -506,7 +506,7 @@ const MCP_CATALOG = Object.freeze({ provider: "pyzx / OpenQuantum", sourceUrl: "https://github.com/zxcalc/pyzx", packageName: "pyzx", - packageVersion: "0.10.6", + packageVersion: "0.10.8", setup: null, }), graphix_local: Object.freeze({ diff --git a/tests/harness-unitary-next.test.mjs b/tests/harness-unitary-next.test.mjs index 3ce6b7c8..5706c1b1 100644 --- a/tests/harness-unitary-next.test.mjs +++ b/tests/harness-unitary-next.test.mjs @@ -18,7 +18,12 @@ const scaledInputs = { "symmer-tapering": { numQubits: 65, terms: [{ pauli: "I".repeat(64) + "Z", coefficient: 0.5 }], symmetries: [{ pauli: "Z" + "I".repeat(64), sector: -1 }], referenceMode: "skip" }, "paulie-algebra": { numQubits: 65, generators: ["I".repeat(64) + "X", "I".repeat(64) + "Z"], closureMode: "skip", referenceMode: "skip" }, }; -const CASES = [...UNITARY_NEXT_TOOLS.map(c => scaled ? { ...c, input: scaledInputs[c.id] } : c), +// Upgrade checks may prepare one SDK without installing unrelated environments. +// The default remains the complete four-SDK suite. +const selectedIds = process.env.OPENQUANTUM_UNITARY_NEXT_IDS?.split(",").filter(Boolean); +if (selectedIds && (!selectedIds.length || selectedIds.some(id => !UNITARY_NEXT_TOOLS.some(c => c.id === id)))) throw new Error("Unknown unitary-next SDK selection"); +const selectedTools = selectedIds ? UNITARY_NEXT_TOOLS.filter(c => selectedIds.includes(c.id)) : UNITARY_NEXT_TOOLS; +const CASES = [...selectedTools.map(c => scaled ? { ...c, input: scaledInputs[c.id] } : c), { id: "pyzx-invalid", server: "pyzx_local", tool: "optimize_pyzx_circuit", input: { gates: [] }, expectError: true }, ]; const toolNames = CASES.map(c => c.server ? `mcp__${c.server}__${c.tool}` : c.tool); @@ -35,7 +40,7 @@ async function waitFor(probe, description, timeoutMs = 45000) { throw new Error(`${description}: ${last ?? "timed out"}`); } -test("Harness discovers four ecosystem Skills, runs four real Tools and persists success and failure results", { skip: !enabled, timeout: 600000 }, async (t) => { +test("Harness discovers ecosystem Skills, runs selected real Tools and persists success and failure results", { skip: !enabled, timeout: 600000 }, async (t) => { const root = process.cwd(); const sandbox = await mkdtemp(path.join(tmpdir(), "oq-unitary-harness-")); const harnessHome = path.join(sandbox, "dsh"); @@ -45,9 +50,12 @@ test("Harness discovers four ecosystem Skills, runs four real Tools and persists const chunks = []; for await (const chunk of request) chunks.push(chunk); const body = JSON.parse(Buffer.concat(chunks).toString()); sawModelToolResult ||= body.messages?.some((message) => message.role === "tool"); - const initial = !dispatched; - dispatched = true; - const delta = initial ? { role: "assistant", tool_calls: CASES.map((c, index) => ({ index, id: `unitary-fixture-${index}`, type: "function", function: { name: toolNames[index], arguments: JSON.stringify(c.input) } })) } : { role: "assistant", content: "Four local ecosystem actions completed and one invalid circuit failed as expected; scientificValidation=not_evaluated." }; + // Session-title requests can race the tool-bearing conversation request. + // They must not consume the one-shot tool dispatch fixture. + const hasRequestedTools = toolNames.every(name => body.tools?.some(tool => tool.function?.name === name)); + const initial = hasRequestedTools && !dispatched; + if (initial) dispatched = true; + const delta = initial ? { role: "assistant", tool_calls: CASES.map((c, index) => ({ index, id: `unitary-fixture-${index}`, type: "function", function: { name: toolNames[index], arguments: JSON.stringify(c.input) } })) } : { role: "assistant", content: "Selected local ecosystem actions completed and one invalid circuit failed as expected; scientificValidation=not_evaluated." }; response.writeHead(200, { "content-type": "text/event-stream" }); for (const item of [ { choices: [{ index: 0, delta, finish_reason: null }] }, @@ -85,7 +93,8 @@ test("Harness discovers four ecosystem Skills, runs four real Tools and persists assert.equal(result.ok, true, `${JSON.stringify(result)}\n${redactHarnessLaunchTokens(logs)}`); return result.value; } - await waitFor(() => rpc("llm.models"), "Harness startup"); + try { await waitFor(() => rpc("llm.models"), "Harness startup"); } + catch (error) { throw new Error(`${error.message}\n${redactHarnessLaunchTokens(logs)}`, { cause: error }); } const sessionId = `session-unitary-${crypto.randomUUID()}`; await rpc("session.create", { sessionId, cwd: workspace, agentPreset: "openquantum" }); const skillList = await rpc("skill.list", { sessionId }); diff --git a/tests/unitary-next-live.test.mjs b/tests/unitary-next-live.test.mjs index 90058940..5e4537cc 100644 --- a/tests/unitary-next-live.test.mjs +++ b/tests/unitary-next-live.test.mjs @@ -158,3 +158,40 @@ test("real PauLie: real Lie dimension, abelian central generators, local sum and } }); }); + + +test("real PyZX: production SDK fixes XPhase remapping and U3 adjoint", { skip: !enabled, timeout: 120000 }, async () => { + const sdk = await describeSdk(process.cwd(), "pyzx-optimization"); + assert.equal(sdk.environmentState, "prepared"); + const result = JSON.parse(execFileSync(sdk.run.command, ["-B", "benchmarks/upstream-updates/pyzx_regressions.py"], { encoding: "utf8", timeout: 110000 })); + assert.equal(result.pyzx, "0.10.8"); + assert.equal(result.records.length, 5); + for (const record of result.records) assert.equal(record.passed, true, record.name); + assert.equal(result.scientificAcceptance, "not_evaluated"); + await mkdir(evidence, { recursive: true }); + await writeFile(path.join(evidence, "pyzx-sdk-correctness.json"), JSON.stringify(result, null, 2) + "\n"); +}); + +test("real PyZX: required, skipped, large automatic references and invalid input", { skip: !enabled, timeout: 240000 }, async t => { + await withTool(t, "pyzx-optimization", async run => { + const input = { numQubits: 2, gates: [ + { gate: "CX", targets: [1, 0] }, { gate: "CX", targets: [0, 1] }, + { gate: "H", targets: [1] }, { gate: "S", targets: [1] }, { gate: "H", targets: [1] }, + ] }; + const required = await run({ ...input, referenceMode: "required" }, "required-remapping"); + assert.equal(required.reference.status, "computed"); + assert.ok(required.unitaryMaxError < 1e-8); + const skipped = await run({ ...input, referenceMode: "skip" }, "skip-remapping"); + const automatic = await run({ numQubits: 65, gates: [{ gate: "H", targets: [64] }], referenceMode: "auto" }, "auto-65-qubits"); + for (const result of [skipped, automatic]) { + assert.equal(result.reference.status, "not_run"); + assert.equal(result.unitaryMaxError, null); + assert.equal(result.equivalentUpToGlobalPhase, null); + } + }); + const client = new Client({ name: "pyzx-invalid-regression", version: "1" }, { capabilities: {} }); + t.after(() => client.close()); + await client.connect(new StdioClientTransport({ command: process.execPath, args: [".agents/skills/pyzx-optimization/mcp/server.mjs"] })); + const response = await client.callTool({ name: "optimize_pyzx_circuit", arguments: { gates: [] } }); + assert.equal(response.isError, true); +});