Source code for quantum_mind.embodied.qiskit_slot
"""The quantum belief slot as a Qiskit circuit (cross-check of :class:`~quantum_mind.embodied.belief.QuantumSlot`).
Each answer is a non-unitary measurement ``M = U diag(sqrt(L)) U^dagger`` with ``||M|| <= 1``. A circuit can only apply
unitaries, so ``M`` is embedded in the unitary Halmos dilation ``W = [[M, sqrt(I - M M^dagger)], [sqrt(I - M^dagger M), -M^dagger]]``
acting on the objects (two qubits hold three objects and a spare level) plus one ancilla qubit per answer; the answer is accepted
when every ancilla reads 0. This reproduces the NumPy slot exactly on a simulator; it adds no speed or accuracy, and it is not
run on hardware. It needs the optional ``qiskit`` package.
"""
from __future__ import annotations
import numpy as np
from .belief import _ctx_matrices, _floor
def _sqrtm_psd(a):
w, v = np.linalg.eigh((a + a.conj().T) / 2)
return (v * np.sqrt(np.clip(w, 0, None))) @ v.conj().T
[docs]
def qiskit_posterior(slot, g0, L, ctx, mask):
"""Posterior over the objects of one sequence from a Qiskit circuit; ``g0, L, ctx, mask`` as for ``slot.posterior`` with one row."""
from qiskit import QuantumCircuit
from qiskit.circuit.library import UnitaryGate
from qiskit.quantum_info import Statevector
U = _ctx_matrices(slot.x)
Lf = _floor(L, mask)[0]
n_ans = L.shape[1]
psi0 = np.zeros(4, complex)
psi0[:3] = np.sqrt(g0[0])
qc = QuantumCircuit(2 + n_ans)
qc.initialize(psi0, [0, 1])
for j in range(n_ans):
Uj = U[ctx[0, j]]
M3 = Uj @ np.diag(np.sqrt(Lf[j])) @ Uj.conj().T
M = np.eye(4, dtype=complex)
M[:3, :3] = M3
D1, D2 = _sqrtm_psd(np.eye(4) - M @ M.conj().T), _sqrtm_psd(np.eye(4) - M.conj().T @ M)
W = np.block([[M, D1], [D2, -M.conj().T]])
qc.append(UnitaryGate(W), [0, 1, 2 + j])
amp = Statevector.from_instruction(qc).data
sys_amp = amp[:4] # all ancillas read 0: the highest qubits are 0
p = np.abs(sys_amp[:3]) ** 2 * mask[0]
return p / p.sum(), float(np.sum(np.abs(sys_amp) ** 2))