qaiji.core.semantics.summary 源代码

# Copyright 2026 Qaiji-IR core developer: Ye Jun <yjmaxpayne@hotmail.com>
# SPDX-License-Identifier: Apache-2.0
"""电路语义内容的规范化 JSON 摘要及其内容哈希。

维护规则:凡是改动哈希输入域、14 个稳定键的集合,或 ``types.py`` 中会流入摘要键的
任一字面量(morphism/equiv-level/cartan 字符串、``GATE_COVERAGE_TOTAL``、
``operations`` 的编码形状),都必须递增 ``SUMMARY_SCHEMA_VERSION``。
"""

from __future__ import annotations

import dataclasses
import hashlib
import json
from collections import Counter
from collections.abc import Mapping, Sequence
from typing import TYPE_CHECKING, Any

from qaiji.core.circuit import Gate
from qaiji.core.classical import Conditional, Measure
from qaiji.core.semantics.dataflow import build_dataflow_summary

if TYPE_CHECKING:
    from qaiji.core.circuit import Circuit, GateType
    from qaiji.core.semantics.annotation import SemanticAnnotation

__all__ = [
    "GATE_COVERAGE_TOTAL",
    "SUMMARY_SCHEMA_VERSION",
    "build_semantic_summary",
    "canonical_summary_hash",
]

SUMMARY_SCHEMA_VERSION = "qaiji.semantic_summary.v0"
GATE_COVERAGE_TOTAL = 19


def _encode_gate_triple(gate: Gate) -> list[Any]:
    """编码一个门的结构性身份:类型、量子比特与参数。

    参数以 repr(float) 字符串形式传递 —— 这是 CPython 最短的往返表示 —— 因此哈希
    不受 JSON 数字编码差异的影响。显式的 float() 强制转换很关键:
    Gate.__post_init__ 只把 params 元组化而不强制转 float,于是手工构造的
    Gate(RZ, (0,), (0,)) 与 Gate(RZ, (0,), (0.0,)) 在 dataclass 意义上相等,但
    repr(0) != repr(0.0) —— 不做强转的话,这两个逐位相同的电路会哈希到不同值。
    """
    return [
        gate.gate_type.value,
        list(gate.qubits),
        [repr(float(param)) for param in gate.params],
    ]


def _encode_operation(operation: Gate | Measure | Conditional) -> list[Any]:
    """将一个顶层节点编码进 ``operations`` schema(ARCH §2.2)。"""
    if isinstance(operation, Gate):
        return ["gate", *_encode_gate_triple(operation)]
    if isinstance(operation, Measure):
        return ["measure", operation.qubit, operation.target.register.name, operation.target.index]
    if isinstance(operation, Conditional):
        return [
            "conditional",
            operation.register.name,
            operation.value,
            [_encode_gate_triple(gate) for gate in operation.body],
        ]
    raise TypeError(f"Cannot encode {type(operation).__name__} into operations")


def _gate_coverage(circuit: Circuit) -> dict[str, int]:
    """统计电路中出现过的不同 GateType 数量,含 Conditional 体内的门。"""
    covered: set[GateType] = set()
    for operation in circuit.gates:
        if isinstance(operation, Gate):
            covered.add(operation.gate_type)
        elif isinstance(operation, Conditional):
            covered.update(gate.gate_type for gate in operation.body)
    return {"covered": len(covered), "total_gate_types": GATE_COVERAGE_TOTAL}


[文档] def build_semantic_summary( *, circuit: Circuit, annotations: Sequence[SemanticAnnotation], status_label: str = "available", source_language: str | None = None, preservation: Mapping[str, Any] | None = None, ) -> dict[str, Any]: """构建一个电路的规范化、JSON 安全的语义摘要。 全部 14 个稳定键都无条件输出,包括空容器和 ``None``:一个时有时无的键会让键的 漂移只能表现为哈希差异,而不是显式的键名差异,从而变得不可见。 Args: circuit: 判别性键(num_qubits、cregs、operations)与 gate_coverage 的来源。 annotations: 该电路的语义标注,按 ``annotate_circuit`` 的顺序排列。 status_label: 取自 ``HandleStatus`` 的值(本函数不做校验,按原样写入的 普通 str)。 source_language: 可选的溯源标签。生产调用方保持为 None;只有测试会传入 具体值。 preservation: 可选的保持性裁决载荷,给定时原样并入。 Returns: 一个 JSON 安全的 dict,含 14 个稳定键,外加所有被赋值的可选键。 """ dataflow = build_dataflow_summary(circuit) summary: dict[str, Any] = { "summary_schema_version": SUMMARY_SCHEMA_VERSION, "status_label": status_label, "num_qubits": circuit.num_qubits, "cregs": [[register.name, register.size] for register in circuit.cregs], "operations": [_encode_operation(operation) for operation in circuit.gates], "annotation_count": len(annotations), "gate_coverage": _gate_coverage(circuit), "morphism_counts": dict(Counter(annotation.morphism.value for annotation in annotations)), "equiv_level_counts": dict( Counter( annotation.equiv_level.value for annotation in annotations if annotation.equiv_level is not None ) ), "cartan_counts": dict(Counter(annotation.cartan.value for annotation in annotations)), "measurement_handles": [ dataclasses.asdict(handle) for handle in dataflow["measurement_handles"] ], "classical_edges": [dataclasses.asdict(edge) for edge in dataflow["classical_edges"]], "condition_model": dataflow["condition_model"], "unitary_segment_count": dataflow["unitary_segment_count"], } if source_language is not None: summary["source_language"] = source_language if preservation is not None: summary["preservation"] = preservation return summary
def _jsonify(value: Any) -> Any: """``json.dumps`` 的 ``default=`` 钩子:只处理 Mapping -> dict。 其余所有类型一律保持 TypeError(R-C01):除了 ``freeze_summary`` 产出的冻结树 Mapping 这一种情况,没有任何证据支持为别的类型开特例。 """ if isinstance(value, Mapping): return dict(value) raise TypeError(f"Object of type {type(value).__name__} is not JSON serializable")
[文档] def canonical_summary_hash(summary: Mapping[str, Any]) -> str: """将语义摘要哈希为它的规范化内容身份。 Args: summary: 一份语义摘要,原始的或已冻结的(``freeze_summary`` 的输出)—— 二者经由 ``default=`` 钩子哈希出完全相同的结果。 Returns: 规范化 JSON 编码的 ``"sha256:" + 十六进制摘要``。 Raises: ValueError: 摘要中含有 NaN 或 Infinity。 TypeError: 摘要中含有既非 JSON 原始类型、也非 Mapping 的值。 """ encoded = json.dumps( summary, sort_keys=True, separators=(",", ":"), ensure_ascii=False, allow_nan=False, default=_jsonify, ) return "sha256:" + hashlib.sha256(encoded.encode("utf-8")).hexdigest()