Source code for qarp.blocks._primitives.synthesized_unitary_block

from typing import List, Optional

import numpy as np

from qarp.blocks._block import SimpleBlock


[docs] class SynthesizedUnitaryBlock(SimpleBlock): """Pattern A leaf: synthesize an arbitrary `2^n × 2^n` unitary into a circuit. Delegates to ``self.unitary_synthesis(...)`` (qarpx C++ Quantum Shannon Decomposition) in ``build_vanilla()``. """ def __init__( self, unitary_matrix: np.ndarray, target_qubits: Optional[List[int]] = None, name: str = "SynthUnitaryBlock", ): """ Args: unitary_matrix: A square unitary matrix of dimension `2^n × 2^n`. Validated for unitarity (`U @ U† ≈ I`) within `1e-10`. target_qubits, name: standard Block kwargs. """ size = unitary_matrix.shape[0] if size == 0 or size & (size - 1) != 0: raise ValueError("Unitary matrix size must be a power of 2") n_qubits = int(np.log2(size)) super().__init__( n_qubits, target_qubits=target_qubits, name=name, ) # Named `target_unitary`, not `unitary_matrix`: an attribute of that # name would shadow the inherited `Block.unitary_matrix()` method. self.target_unitary = np.asarray(unitary_matrix, dtype=complex) self._validate_inputs() def _validate_inputs(self) -> None: if self.target_unitary.shape[0] != self.target_unitary.shape[1]: raise ValueError("Unitary matrix must be square") size = self.target_unitary.shape[0] if size == 0 or size & (size - 1) != 0: raise ValueError("Unitary matrix size must be a power of 2") identity = np.eye(size) product = self.target_unitary @ self.target_unitary.conj().T tol = 1e-10 if not np.allclose(product, identity, atol=tol): max_err = float(np.max(np.abs(product - identity))) raise ValueError( f"Input matrix is not unitary: max|U U† - I| = {max_err:.2e} > {tol:.0e}. " f"Fix the precision where the matrix is produced: prefer " f"scipy.linalg.svd(M, lapack_driver='gesvd') over numpy.linalg.svd " f"and reunitarize via V @ Wh." )
[docs] def build_vanilla(self) -> None: self.unitary_synthesis(self.target_unitary)