def test_vector(self, alpha: np.ndarray) -> None: paulis = PauliMatrices(2) H = paulis.dot_product(alpha) dFs0 = [] for p in paulis: _, dF = dexpm_exact(H, p) dFs0.append(dF) dFs0_np = np.array(dFs0) _, dFs1 = dexpmv(H, paulis.get_numpy()) assert np.allclose(dFs0_np, dFs1)
class PauliGate(QubitGate, DifferentiableUnitary, LocallyOptimizableUnitary): """A gate representing an arbitrary rotation.""" def __init__(self, size: int) -> None: """Create a PauliGate acting on `size` qubits.""" if size <= 0: raise ValueError('Expected positive integer, got %d' % size) self.size = size self.paulis = PauliMatrices(self.size) self.num_params = len(self.paulis) self.sigmav = (-1j / self.get_dim()) * self.paulis.get_numpy() def get_unitary(self, params: Sequence[float] = []) -> UnitaryMatrix: """Returns the unitary for this gate, see Unitary for more info.""" self.check_parameters(params) H = dot_product(params, self.sigmav) eiH = sp.linalg.expm(H) return UnitaryMatrix(eiH, check_arguments=False) def get_grad(self, params: Sequence[float] = []) -> np.ndarray: """Returns the gradient for this gate, see Gate for more info.""" self.check_parameters(params) H = dot_product(params, self.sigmav) _, dU = dexpmv(H, self.sigmav) return dU def get_unitary_and_grad( self, params: Sequence[float] = [], ) -> tuple[UnitaryMatrix, np.ndarray]: """Returns the unitary and gradient, see Gate for more info.""" self.check_parameters(params) H = dot_product(params, self.sigmav) U, dU = dexpmv(H, self.sigmav) return UnitaryMatrix(U, check_arguments=False), dU def optimize(self, env_matrix: np.ndarray) -> list[float]: """Returns optimal parameters with respect to an environment matrix.""" self.check_env_matrix(env_matrix) U, _, Vh = sp.linalg.svd(env_matrix) return list(pauli_expansion(unitary_log_no_i( Vh.conj().T @ U.conj().T)))