Source code for tensorcircuit.compiler.symbolic_compiler
"""
Compiler passes for symbolic circuits.
"""
from typing import Any, Dict, Sequence, Tuple
from ..symbolcircuit import SymbolCircuit
[docs]
def lightcone_compile(
circuit: SymbolCircuit, observable_qubits: Sequence[int]
) -> Tuple[SymbolCircuit, Dict[str, Any]]:
"""
Compile a SymbolCircuit to the causal cone induced by a final observable's
qubit support.
The compiled circuit retains the original SymPy symbols, so callers may
pass the complete symbol-to-value binding dictionary to ``to_circuit``;
bindings for removed gates are ignored.
:param circuit: Symbolic circuit to compile.
:type circuit: SymbolCircuit
:param observable_qubits: Qubit support of the final observable.
:type observable_qubits: Sequence[int]
:return: Compiled circuit and mapping information. ``observable_qubits``
contains the remapped observable support, and
``logical_physical_mapping`` maps retained input qubits to compiled
qubits.
:rtype: Tuple[SymbolCircuit, Dict[str, Any]]
:raises ValueError: If the observable indices are empty, duplicated, or out
of range.
:raises NotImplementedError: If the circuit uses non-default inputs,
channels, or extra QIR instructions.
"""
if not observable_qubits:
raise ValueError("observable_qubits must contain at least one qubit")
normalized_observable_qubits = [
q if q >= 0 else circuit._nqubits + q for q in observable_qubits
]
if any(q < 0 or q >= circuit._nqubits for q in normalized_observable_qubits):
raise ValueError("observable_qubits contains an index outside the circuit")
if len(set(normalized_observable_qubits)) != len(normalized_observable_qubits):
raise ValueError("observable_qubits must not contain duplicates")
if circuit.inputs is not None:
raise NotImplementedError(
"lightcone_compile requires the default product input state"
)
if circuit._extra_qir:
raise NotImplementedError(
"lightcone_compile does not support extra QIR instructions"
)
qir = circuit.to_qir()
if any(instruction.get("is_channel", False) for instruction in qir):
raise NotImplementedError(
"lightcone_compile does not support channel instructions"
)
normalized_qir = []
for instruction in qir:
normalized_instruction = dict(instruction)
normalized_instruction["index"] = tuple(
q if q >= 0 else circuit._nqubits + q for q in instruction["index"]
)
normalized_qir.append(normalized_instruction)
active = set(normalized_observable_qubits)
kept = []
for instruction in reversed(normalized_qir):
support = set(instruction["index"])
if active & support:
kept.append(instruction)
active |= support
kept.reverse()
active_qubits = sorted(active)
qubit_mapping = {old: new for new, old in enumerate(active_qubits)}
reduced_qir = []
for instruction in kept:
reduced_instruction = dict(instruction)
reduced_instruction["index"] = tuple(
qubit_mapping[index] for index in instruction["index"]
)
if "parameters" in reduced_instruction:
reduced_instruction["parameters"] = dict(reduced_instruction["parameters"])
reduced_qir.append(reduced_instruction)
compiled = SymbolCircuit(len(active_qubits))
compiled.append_from_qir(reduced_qir)
info = {
"logical_physical_mapping": qubit_mapping,
"observable_qubits": [qubit_mapping[q] for q in normalized_observable_qubits],
}
return compiled, info