def energy_gradient( local_energies: np.ndarray, logarithmic_derivatives: np.ndarray, weights: np.ndarray = None, ) -> np.ndarray: r"""Calculates the gradient of energy with respect to variational parameters: ``∂⟨ψ|H|ψ⟩/∂W``. :param local_energies: local energy estimators ``⟨σ|H|ψ⟩/⟨σ|ψ⟩``. :param logarithmic_derivatives: **centered** logarithmic derivatives of the wavefunction with respect to variational parameters. :param weights: if specified, it is assumed that {σ} span the whole Hilbert space basis. Then ``weights`` are **normalized** probabilities ``|⟨σ|ψ⟩|²/‖ψ‖₂``. If ``weights`` is ``None``, then it is assumed that {σ} come from Monte Carlo sampling and are distributed according to ``|⟨σ|ψ⟩|²/‖ψ‖₂``. """ if weights is not None: assert np.isclose(np.sum(weights), 1.0) local_energies = weights * local_energies local_energies = local_energies.conj() local_energies = local_energies.reshape(1, -1) gradient = local_energies @ logarithmic_derivatives else: local_energies = local_energies.conj() local_energies = local_energies.reshape(1, -1) gradient = local_energies @ logarithmic_derivatives gradient /= logarithmic_derivatives.shape[0] return np.ascontiguousarray(2.0 * gradient.real.squeeze())
def primal_problem(q_a: np.ndarray, pperm: np.ndarray, num_reps: int) -> float: """ Primal problem for counterfeit attack. As the primal problem takes longer to solve than the dual problem (as the variables are of larger dimension), the primal problem is only here for reference. :return: The optimal value of performing a counterfeit attack. """ num_spaces = 3 sys = list(range(1, num_spaces * num_reps)) sys = [elem for elem in sys if elem % num_spaces != 0] # The dimension of each subsystem is assumed to be of dimension 2. dim = 2 * np.ones((1, num_spaces * num_reps)).astype(int).flatten() dim = dim.tolist() x_var = cvxpy.Variable((8**num_reps, 8**num_reps), hermitian=True) if num_reps == 1: objective = cvxpy.Maximize( cvxpy.trace(cvxpy.real(q_a.conj().T @ x_var))) else: objective = cvxpy.Maximize( cvxpy.trace( cvxpy.real(pperm @ q_a.conj().T @ pperm.conj().T @ x_var))) constraints = [ partial_trace(x_var, sys, dim) == np.identity(2**num_reps), x_var >> 0, ] problem = cvxpy.Problem(objective, constraints) return problem.solve()
def expand_to_hermitian( matrix: np.ndarray, vector: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: """ Expand a non-hermitian matrix A to a hermitian matrix by [[0, A.H], [A, 0]] and expand vector b to [b.conj, b]. Args: matrix: the input matrix vector: the input vector Returns: the expanded matrix, the expanded vector """ # half_dim = matrix.shape[0] full_dim = 2 * half_dim new_matrix = np.zeros([full_dim, full_dim]) new_matrix = np.array(new_matrix, dtype=complex) new_matrix[0:half_dim, half_dim:full_dim] = matrix[:, :] new_matrix[half_dim:full_dim, 0:half_dim] = matrix.conj().T[:, :] matrix = new_matrix new_vector = np.zeros((1, full_dim)) new_vector = np.array(new_vector, dtype=complex) new_vector[0, :vector.shape[0]] = vector.conj() new_vector[0, vector.shape[0]:] = vector vector = new_vector.reshape(np.shape(new_vector)[1]) return matrix, vector
def correlation(data1: np.ndarray, data2: np.ndarray) -> float: """ 相关系数计算 :param data1: 第一个序列 :param data2: 第二个序列 :return: 相关系数值 """ return np.abs(data1.dot(data2.conj())) / np.sqrt( (np.abs(data1.dot(data1.conj())))) / np.sqrt( (np.abs(data2.dot(data2.conj()))))
def calculate_pulse_correlation_filter_function(R: ndarray, which: str = 'fidelity' ) -> ndarray: r"""Compute pulse correlation filter function from the control matrix. Parameters ---------- R: array_like, shape (n_pulses, n_nops, d**2, n_omega) The control matrix. Returns ------- F_pc: ndarray, shape (n_pulses, n_pulses, n_nops, n_nops, [d**2, d**2], n_omega) # noqa The pulse correlation filter functions for each pulse and noise operator correlations. The first two axes hold the pulse correlations, the second two the noise correlations. which : str, optional Which filter function to return. Either 'fidelity' (default) or 'generalized' (see :ref:`Notes <notes>`). Notes ----- The generalized pulse correlation filter function is given by .. math:: F_{\alpha\beta,kl}^{(gg')}(\omega) = \bigl[ \mathcal{Q}^{(g'-1)\dagger}\mathcal{R}^{(g')\dagger}(\omega) \bigr]_{k\alpha} \bigl[ \mathcal{R}^{(g)}(\omega)\mathcal{Q}^{(g-1)} \bigr]_{\beta l} e^{i\omega(t_{g-1} - t_{g'-1})}, with :math:`\mathcal{R}^{(g)}` the control matrix of the :math:`g`-th pulse. The fidelity pulse correlation function is obtained by tracing out the basis indices, .. math:: F_{\alpha\beta}^{(gg')}(\omega) = \sum_{k} F_{\alpha\beta,kk}^{(gg')}(\omega) See Also -------- calculate_control_matrix_from_scratch: Control matrix from scratch. calculate_control_matrix_from_atomic: Control matrix from concatenation. calculate_filter_function: Regular filter function. """ if R.ndim != 4: raise ValueError('Expected R.ndim == 4.') if which == 'fidelity': return np.einsum('gako,hbko->ghabo', R.conj(), R) elif which == 'generalized': return np.einsum('gako,hblo->ghabklo', R.conj(), R)
def liouville_representation(U: ndarray, basis: _b.Basis) -> ndarray: r""" Get the Liouville representaion of the unitary U with respect to the basis. Parameters ---------- U: ndarray, shape (..., d, d) The unitary. basis: Basis, shape (d**2, d, d) The basis used for the representation, e.g. a Pauli basis. Returns ------- R: ndarray, shape (..., d**2, d**2) The Liouville representation of U. Notes ----- The Liouville representation of a unitary quantum operation :math:`\mathcal{U}:\rho\rightarrow U\rho U^\dagger` is given by .. math:: \mathcal{U}_{ij} = \mathrm{tr}(C_i U C_j U^\dagger) with :math:`C_i` elements of the basis spanning :math:`\mathbb{C}^{d\times d}` with :math:`d` the dimension of the Hilbert space. """ U = np.asanyarray(U) if basis.btype == 'GGM' and basis.d > 12: # Can do closed form expansion and overhead compensated path = ['einsum_path', (0, 1), (0, 1)] conjugated_basis = np.einsum('...ba,ibc,...cd->...iad', U.conj(), basis, U, optimize=path) # If the basis is hermitian, the result will be strictly real so we can # drop the imaginary part R = _b.ggm_expand(conjugated_basis).real else: path = ['einsum_path', (0, 1), (0, 1), (0, 1)] R = np.einsum('...ba,ibc,...cd,jda', U.conj(), basis, U, basis, optimize=path).real return R
def get_initial_state_set(gate: np.ndarray) -> Iterator[np.ndarray]: """Yields all initial states in order depending on gate shape""" # First tensor product then pauli transfer matrix if gate.shape[0] == gate.shape[ 1] == 2: # Pauli Transfer Matrix (single qubit) for observable in BasisUnitarySet.get_observable_set(dim=2): yield .5 * (gate @ (observable @ gate.conj().transpose())) elif gate.shape[0] == gate.shape[ 1] == 4: # Pauli Transfer Matrix (double qubits) for observable in BasisUnitarySet.get_observable_set(dim=4): yield .25 * (gate @ (observable @ gate.conj().transpose())) else: raise NotImplemented
def is_skew_hermitian(H: np.ndarray, tol: float = 1e-8) -> bool: """Checks if H is a skew hermitian matrix.""" if not is_square_matrix(H): return False if not np.allclose(-H, H.conj().T, rtol=0, atol=tol): if _logger.isEnabledFor(logging.DEBUG): norm = np.linalg.norm(-H - H.conj().T) _logger.debug( 'Failed skew hermitian condition, ||H - H^d|| = %e' % norm, ) return False return True
def compute_mismatch_statistics(waveforms: np.ndarray, reconstructions: np.ndarray) -> Dict[str, float]: """Compute statistics comparing the mismatch of values between two arrays. We compare the ratio of values for each length element (i.e. frequency bin) between the two arrays, following similar procedure to Green and Gair (2020). """ norm1 = np.mean(np.abs(waveforms)**2, axis=1) norm2 = np.mean(np.abs(reconstructions)**2, axis=1) inner = np.mean(waveforms.conj()*reconstructions, axis=1).real # if ratio of values are similar matches should tend to 1. matches = inner / np.sqrt(norm1 * norm2) mismatches = 1 - matches statistics = { 'mean': np.mean(mismatches), 'std': np.std(mismatches), 'max': np.max(mismatches), 'median': np.median(mismatches), 'perc99': np.percentile(mismatches, 99.), 'perc99.9': np.percentile(mismatches, 99.9), 'perc99.99': np.percentile(mismatches, 99.99), } return statistics
def calculate_canonical_filter_function_derivative( R: ndarray, deriv_R: ndarray) -> ndarray: r""" Compute the filter function derivative from the control matrix. Parameters ---------- R : array_like, shape (n_nops, d**2, n_omega) The control matrix. deriv_R: array_like, shape (n_nops, d**2, n_t, n_ctrl, n_omega) The derivative of the control matrix. Returns ------- deriv_filter_function : ndarray, shape (n_nops, n_dt, n_ctrl, n_omega) The regular filter functions' derivatives for variation in each control contribution :math:`\frac{\partial F_\alpha(\omega)}{\partial u_h(t_{g'})}`. The array's indexing has shape :math:`(\alpha,g^\prime,h,\omega)`. Notes ----- The filter function derivative is calculated according to .. math :: \frac{\partial F_\alpha(\omega)}{\partial u_h(t_{g'})} = 2 \mathrm{Re} \left( \sum_k R_{\alpha k}^\ast(\omega) \frac{\partial R_{\alpha k}(\omega)} {\partial u_h(t_{g'})} \right) """ summe = np.einsum('ako,aktho->atho', R.conj(), deriv_R) return 2 * summe.real
def concurrence(rho: np.ndarray) -> float: r""" Calculate the concurrence of a bipartite state. The concurrence of a bipartite state :math:`\rho` is defined as .. math:: \max(0, \lambda_1 - \lambda_2 - \lambda_3 - \lambda_4), where :math:`\lambda_1, \ldots, \lambda_4` are the eigenvalues in decreasing order of the matrix. References: [1] Wikipedia page for concurrence (quantum computing) https://en.wikipedia.org/wiki/Concurrence_(quantum_computing) :param rho: The bipartite system specified as a matrix. :return: The concurrence of the bipartite state :math:`\rho`. """ if rho.shape != (4, 4): raise ValueError( "InvalidDim: Concurrence is only defined for bipartite" " systems.") sigma_y = pauli("Y", False) sigma_y_y = np.kron(sigma_y, sigma_y) rho_hat = np.matmul(np.matmul(sigma_y_y, rho.conj().T), sigma_y_y) eig_vals = np.linalg.eigvalsh(np.matmul(rho, rho_hat)) eig_vals = np.sort(np.sqrt(eig_vals))[::-1] return max(0, eig_vals[0] - eig_vals[1] - eig_vals[2] - eig_vals[3])
def _expectation_from_wavefunction_no_validation( self, state: np.ndarray, qubit_map: Mapping[raw_types.Qid, int]) -> float: """Evaluate the expectation of this PauliString given a wavefunction. This method does not provide input validation. See `PauliString.expectation_from_wavefunction` for function description. Args: state: An array representing a valid wavefunction. qubit_map: A map from all qubits used in this PauliString to the indices of the qubits that `state` is defined over. Returns: The expectation value of the input state. """ if len(state.shape) == 1: num_qubits = state.shape[0].bit_length() - 1 state = np.reshape(state, (2, ) * num_qubits) ket = np.copy(state) for qubit, pauli in self.items(): buffer = np.empty(ket.shape, dtype=state.dtype) args = protocols.ApplyUnitaryArgs(target_tensor=ket, available_buffer=buffer, axes=(qubit_map[qubit], )) ket = protocols.apply_unitary(pauli, args) return self.coefficient * (np.tensordot( state.conj(), ket, axes=len(ket.shape)).item())
def pauli_decompose_hermitian(matrix: np.ndarray, qubits: Qubits = None) -> Pauli: """Decompose a Hermitian matrix into an element of the Pauli algebra. This works because tensor products of Pauli matrices form an orthonormal basis in the linear space of all 2^N×2^N matrices under Hilbert-Schmidt inner product. """ # TODO: This should work for any matrix, not just Hermitian? # Generalize: remove hermitian check and coercing coefficients to real. if not np.ndim(matrix) == 2: raise ValueError("Must be square matrix") # TODO: Wait is this true? if not np.allclose(matrix.conj().T, matrix): raise ValueError("Matrix must be Hermitian") N = int(np.log2(np.size(matrix))) // 2 if not 2**(2 * N) == np.size(matrix): raise ValueError("Matrix dimensions must be power of 2") if qubits is None: qubits = list(range(N)) else: assert len(qubits) == N terms = [] for ops in product("IXYZ", repeat=N): P = Pauli.term(qubits, "".join(ops)).asoperator(qubits=qubits) coeff = np.real(np.trace(P @ matrix) / (2**N)) term = Pauli.term(qubits, "".join(ops), coeff) terms.append(term) return pauli_sum(*terms)
def from_rotation_matrix(matrix: np.ndarray): # This method assumes row-vector and postmultiplication of that vector m = matrix.conj().transpose() if m[2, 2] < 0: if m[0, 0] > m[1, 1]: t = 1 + m[0, 0] - m[1, 1] - m[2, 2] q = [ m[1, 2] - m[2, 1], t, m[0, 1] + m[1, 0], m[2, 0] + m[0, 2] ] else: t = 1 - m[0, 0] + m[1, 1] - m[2, 2] q = [ m[2, 0] - m[0, 2], m[0, 1] + m[1, 0], t, m[1, 2] + m[2, 1] ] else: if m[0, 0] < -m[1, 1]: t = 1 - m[0, 0] - m[1, 1] + m[2, 2] q = [ m[0, 1] - m[1, 0], m[2, 0] + m[0, 2], m[1, 2] + m[2, 1], t ] else: t = 1 + m[0, 0] + m[1, 1] + m[2, 2] q = [ t, m[1, 2] - m[2, 1], m[2, 0] - m[0, 2], m[0, 1] - m[1, 0] ] q = np.array(q).astype('float64') q *= 0.5 / np.sqrt(t) return Quaternion(*q)
def a_sat(input_signal: np.ndarray, IBO_dB: int = 0) -> float: ibo = 10**(IBO_dB / 10) # IBOをもとにアンプの飽和電力を決める P_in = np.sum( (input_signal * input_signal.conj()).real) / input_signal.shape[0] # nで割るべき? A = np.sqrt(P_in * ibo) return A
def __init__(self, mat: np.ndarray): """ Initiliaze object of class. :param mat: np.ndarray[L,N] Matrix representation of the linear operator. """ # Check mat try: mat = np.asarray(mat) except ValueError: print("Input matrix must be a numpy array.") # Init from super class super(LinearOperatorFromMatrix, self).__init__(shape=mat.shape, dtype=mat.dtype) # Matrix corresponding to the linear operator self.mat = mat # Adjoint self.adjoint = mat.conj().transpose() # Corresponding Gram matrix self.gram = self.adjoint @ mat # Spectral norm, Lipschitz constant self.norm = self.lipschitz_cst = np.sqrt( spsparse.eigs(self.gram, k=1, which='LM', return_eigenvectors=False, maxiter=int(5e4)))
def negative_log_likelihood_gradient(choi_predictor: np.ndarray, observation_vec: np.ndarray, choi: np.ndarray) -> np.ndarray: r"""Calculate the derivative of the log-likelihood :math:`\mathcal{L}(C_\mathcal{E})` around the given Choi matrix :math:`C_\mathcal{E}`. See :func:`negative_log_likelihood` for computing the value at a given point. :return: A matrix giving the gradient :math:`\nabla \mathcal{L}(C_\mathcal{E}) = \frac{\partial\mathcal{L}(C_\mathcal{E})}{\partial C_\mathcal{E}}`, in the usual element-wise matrix calculus sense. """ probability_vec = np.real(choi_predictor @ mat2vec(choi)) # Fudge predictions away from 0 to avoid stalling as suggested in # [KBLG18] appendix D. mask_small = probability_vec < 1e-16 if np.any(mask_small): warnings.warn("{} very small probabilities encountered".format( np.sum(mask_small))) probability_vec[mask_small] = 1e-16 probability_vec /= np.sum(probability_vec) # See e.g. [AL12] eq. 13 and [KBLG18] eq. 6. # [KBLG18] suggests the equivalent of `-choi_predictor.conj().T @ vec2mat(...)` in # appendix A but that doesn't work out in terms of dimensions. return -vec2mat(choi_predictor.conj().T @ (observation_vec / probability_vec))
def is_unitary(matrix: np.ndarray) -> bool: """ Check if the passed in matrix of size (n times n) is unitary. Arguments: matrix: the matrix which is checked Return: unitary: True if the matrix is unitary """ unitary = True # TODO: check that F is unitary, if not return false n = matrix.shape[0] identity_matrix = np.identity(n) conjugate_transpose = matrix.conj().T mult = np.dot(matrix, conjugate_transpose) if np.allclose(mult, identity_matrix): unitary = True else: unitary = False return unitary
def stacked_fit_linear_background(data: numpy.ndarray, signal_index: int, rcond=1e-10) -> numpy.ndarray: """Return the linear background using least squares for an ndarray with signal in last index. The outline for this implementation comes from: http://stackoverflow.com/questions/30442377/how-to-solve-many-overdetermined-systems-of-linear-equations-using-vectorized-co """ assert signal_index == -1 signal_length = data.shape[signal_index] # using equation y = Ap where A = [[x 1]] and p = [[m], [c]], solve for p. linear = numpy.arange(signal_length) ones = numpy.ones((signal_length, )) A = numpy.vstack([linear, ones]).T # solve for p using svd. p will have the shape (n, 2) where n is the dimensions of the non-signal indexes of the data. u, s, v = numpy.linalg.svd(A, full_matrices=False) s_max = numpy.amax(s, axis=-1, keepdims=True) s_min = rcond * s_max inv_s = numpy.zeros_like(s) inv_s[s >= s_min] = 1 / s[s >= s_min] x = numpy.einsum('...ji,...j->...i', v, inv_s * numpy.einsum('...ji,...j->...i', u, data.conj())) return numpy.conj(x, x)
def solve_pure(self, y1: ndarray, ngrid: int = 11, debug: bool = False) -> PureSolutionInterface: """Solves time depepdent Schrödinger equation for pure inital state """ start = self.offset["normalized_time"][0] end = self.offset["normalized_time"][1] interval = np.linspace(start, end, ngrid) sol = PureSolutionInterface(y1) for jj in range(ngrid - 1): y1 = y1 / (np.sqrt(np.absolute(y1.conj().T @ y1))) tempsol = solve_ivp( fun=self._apply_H, t_span=[interval[jj], interval[jj + 1]], y0=y1, t_eval=np.linspace(*self.offset["normalized_time"], num=100), **self.solver, ) y1 = tempsol.y[:, tempsol.t.size - 1] sol.t = np.hstack((sol.t, tempsol.t)) sol.y = np.hstack((sol.y, tempsol.y)) if debug: print( "final total prob", (np.absolute(sol.y[:, -1].conj().dot(sol.y[:, -1])))**2, ) return sol
def gradient_descent(X: np.ndarray, y: np.ndarray, theta: float, alpha: float, iters: int): ''' Implements gradient descent to the update given theta Parameters ---------- X : Numpy ndarray Training data y : Numpy ndarray Target data theta : float The Parameter to update alpha : float Learning rate iters : int Number of iterations to run ''' m = len(y) history = [] for _ in range(iters): h_x = np.dot(theta, X) errors = h_x - y # Update the parameter theta_ch = (alpha * np.dot(X.conj().T, errors)) / m theta = theta - theta_ch # Capture history for plotting history.append((cf.calculate_cost(X, y, theta), theta)) return history, theta
def unitary_entanglement_fidelity(U_actual: np.ndarray, U_ideal: np.ndarray) -> np.ndarray: r"""Entanglement fidelity between two unitaries. For unitary matrices, this is related to the average unitary fidelity F as :math:`F = \frac{F_e d + 1}{d + 1}` where d is the matrix dimension. Args: U_actual : Matrix whose fidelity to U_ideal will be computed. This may be a non-unitary matrix, i.e. the projection of a larger unitary matrix into the computational subspace. U_ideal : Unitary matrix to which U_actual will be compared. Both arguments may be vectorized, in that their shapes may be of the form (...,M,M) (as long as both shapes can be broadcast together). Returns: The entanglement fidelity between the two unitaries. For inputs with shape (...,M,M), the output has shape (...). """ U_actual = np.asarray(U_actual) U_ideal = np.asarray(U_ideal) assert (U_actual.shape[-1] == U_actual.shape[-2] ), "Inputs' trailing dimensions must be equal (square)." dim = U_ideal.shape[-1] prod_trace = np.einsum('...ba,...ba->...', U_actual.conj(), U_ideal) return np.real((np.abs(prod_trace)) / dim)**2
def music(x: np.ndarray, pos, f, c, aziang, eleang, nsignals): # a = bf.covariance(x) R = np.dot(x, x.conj().T) / 1000 D, V = np.linalg.eigh(R) idx = D.argsort()[::-1] eigenvals = D[idx] # Sorted vector of eigenvalues eigenvects = V[:, idx] # Eigenvectors rearranged accordingly noise_eigenvects = eigenvects[:, nsignals:len( eigenvects)] # Noise eigenvectors # 计算迭代次数,最小化计算开销 len_az = len(aziang) len_el = len(eleang) pattern_shape = (len_el, len_az) scan_az, scan_el = np.meshgrid(aziang, eleang) scan_angles = np.vstack( (scan_az.reshape(-1, order='F'), scan_el.reshape(-1, order='F'))) # shape=(2, len_el*len_az) num_iter = min(len_el, len_az) scan_angle_block_size = max(len_el, len_az) scan_angle_block_index = np.arange(scan_angle_block_size) # scan pattern = np.zeros(len_az * len_el) for i in range(num_iter): cur_idx = scan_angle_block_index + i * scan_angle_block_size cur_angles = scan_angles[:, cur_idx] time_delay = bf.steering_plane_wave(pos, c, np.deg2rad(cur_angles.T)).T a = np.exp(-1j * 2 * np.pi * f * time_delay) # 两种方法一样 # A = a.conj().T.dot(noise_eigenvects).dot(noise_eigenvects.conj().T).dot(a) # D = np.diag(A) D = np.sum(np.abs((a.T.dot(noise_eigenvects)))**2, 1) pattern[cur_idx] = 1 / D scan_pattern = np.sqrt(pattern).reshape(pattern_shape, order='F') return scan_pattern
def weak_coin_flipping(rho: np.ndarray) -> float: """ Weak coin flipping protocol [MS]_. Examples ========== References ========== .. [MS] Ganz, Maor and Sattath, Or Quantum coin hedging, and a counter measure https://arxiv.org/pdf/1703.03887.pdf """ dims = rho.shape id_dim = int(np.sqrt(dims[0])) sdp_var = cvxpy.Variable(dims, PSD=True) objective = cvxpy.Maximize(cvxpy.trace(rho.conj().T @ sdp_var)) constraints = [ partial_trace_cvx(sdp_var) == 1 / id_dim * np.identity(id_dim) ] problem = cvxpy.Problem(objective, constraints) sol_default = problem.solve() return sol_default
def tlphcoh(wt1: ndarray, wt2: ndarray, freq: ndarray, fs: float, wsize: int = 10) -> ndarray: """ Time-localized phase coherence. Parameters ---------- wt1 : ndarray [2D array, complex] Wavelet transform of the first signal. wt2 : ndarray [2D array, complex] Wavelet transform of the second signal. Must be the same shape as `wt1`. freq : ndarray [1D array] Frequencies at which the wavelet transforms wt1 and wt2 were calculated. fs : float Sampling frequency of the signals. wsize : int, optional (Default value = 10) The window size. Returns ------- tpc : ndarray [2D array] The time-localized wavelet phase coherence. """ NF, L = wt1.shape ipc = np.exp(1j * np.angle(wt1 * wt2.conj())) zpc = ipc.copy() zpc[np.isnan(zpc)] = 0 zeros = np.zeros((NF, 1), np.complex64) csum = np.cumsum(zpc, axis=1) cum_pc = np.hstack((zeros, csum)) tpc = np.zeros((NF, L), np.complex64) * np.NaN for fn in range(NF): cs = ipc[fn, :] cumcs = cum_pc[fn, :] f = np.nonzero(~np.isnan(cs))[0] window = np.round(wsize / freq[fn] * fs) window += 1 - np.mod(window, 2) hw = np.floor(window / 2) if len(f) >= 2: tn1 = f[0] tn2 = f[-1] if window <= tn2 - tn1: window = np.int(window) hw = np.int(hw) locpc = (np.abs(cumcs[tn1 + window:tn2 + 1 + 1] - cumcs[tn1:tn2 - window + 1 + 1]) / window) tpc[fn, tn1 + hw:tn2 - hw + 1] = locpc return tpc # TODO: implement 'under_sample' from matlab version?
def fft_flip( fftarray: np.ndarray, axes: Sequence[int] = (-1, ), ) -> np.ndarray: """ Swap positive/negative frequencies and complex conjugate Fourier transform. Result is the Fourier transform of the complex conjugate of the inverse transform of the input array. If the input array is from a real Fourier transform the final axis should be excluded, as it doesn't contain negative frequencies. Parameters ---------- fftarray : np.ndarray Fourier transform. axes : Sequence[int], optional Which axes were Fourier transformed? Default = (-1,). Exclude final axis for a real Fourier transform. Returns ------- rfftarray : np.ndarray Fourier transform with frequencies flipped, complex conjugated. """ fftconj = fftarray.conj() return np.roll(np.flip(fftconj, axes), 1, axes)
def proj_choi_to_unitary(choi: np.ndarray, check_finite: bool = True) -> np.ndarray: """ Compute the unitary closest to a quantum process specified by a Choi matrix. This function enforces Hermiticity of the Choi matrix. See [IntQC]_ for more details: .. [IntQC] Interference of Quantum Channels. Daniel K. L. Oi. Phys. Rev. Lett. 91, 067902 (2003). https://doi.org/10.1103/PhysRevLett.91.067902 https://arxiv.org/abs/quant-ph/0303178 :param choi: the Choi representation of a quantum process. :param check_finite: check that the input matrices contain only finite numbers. :return: choi matrix corresponding to the closest unitary """ dim = int(np.sqrt(choi.shape[0])) hermitian_choi = (choi + choi.conj().T) / 2 # enforce Hermiticity vals, vs = linalg.eigh(hermitian_choi, check_finite=check_finite) # vec corresponding to largest eval = Kraus OP with largest norm large_eigen_vec = vs[:, np.argmax(vals)].reshape((dim * dim, 1)) kraus = unvec(large_eigen_vec) U, s, V = linalg.svd(kraus) unitary = U @ V # pick a global phase convention, we choose the first element phase = np.angle(unitary[0, 0]) return kraus2choi(np.exp(-1j * phase) * unitary)
def ssq(x: np.ndarray, axis=None): """ sum-of-squares this method is ~10% faster than (abs(x)**2).sum() """ x = np.asarray(x) return (x * x.conj()).real.sum(axis)
def __init__( self, matrix: np.ndarray, vector: np.ndarray, truncate_powerdim: bool = False, truncate_hermitian: bool = False, eigs: Optional[Eigenvalues] = None, init_state: Optional[InitialState] = None, reciprocal: Optional[Reciprocal] = None, num_q: int = 0, num_a: int = 0, orig_size: Optional[int] = None ) -> None: """ Constructor. Args: matrix: the input matrix of linear system of equations vector: the input vector of linear system of equations truncate_powerdim: flag indicating expansion to 2**n matrix to be truncated truncate_hermitian: flag indicating expansion to hermitian matrix to be truncated eigs: the eigenvalue estimation instance init_state: the initial quantum state preparation reciprocal: the eigenvalue reciprocal and controlled rotation instance num_q: number of qubits required for the matrix Operator instance num_a: number of ancillary qubits for Eigenvalues instance orig_size: The original dimension of the problem (if truncate_powerdim) Raises: ValueError: invalid input """ super().__init__() if matrix.shape[0] != matrix.shape[1]: raise ValueError("Input matrix must be square!") if matrix.shape[0] != len(vector): raise ValueError("Input vector dimension does not match input " "matrix dimension!") if not np.allclose(matrix, matrix.conj().T): raise ValueError("Input matrix must be hermitian!") if np.log2(matrix.shape[0]) % 1 != 0: raise ValueError("Input matrix dimension must be 2**n!") if truncate_powerdim and orig_size is None: raise ValueError("Truncation to {} dimensions is not " "possible!".format(orig_size)) self._matrix = matrix self._vector = vector self._truncate_powerdim = truncate_powerdim self._truncate_hermitian = truncate_hermitian self._eigs = eigs self._init_state = init_state self._reciprocal = reciprocal self._num_q = num_q self._num_a = num_a self._circuit = None self._io_register = None self._eigenvalue_register = None self._ancilla_register = None self._success_bit = None self._original_dimension = orig_size self._ret = {}
def is_normal(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-08) -> bool: r""" Determine if a matrix is normal. A matrix is normal if it commutes with its adjoint .. math:: \begin{equation} [X, X^*] = 0, \end{equation} or, equivalently if .. math:: \begin{equation} X^* X = X X^*. \end{equation} References: [1] Wikipedia: Normal matrix. https://en.wikipedia.org/wiki/Normal_matrix :param mat: The matrix to check. :param rtol: The relative tolerance parameter (default 1e-05). :param atol: The absolute tolerance parameter (default 1e-08). :return: Returns True if the matrix is normal and False otherwise. """ return np.allclose(np.matmul(mat, mat.conj().T), np.matmul(mat.conj().T, mat), rtol=rtol, atol=atol)