def is_hermitian(matrix: np.array) -> bool: """ Whether matrix is Hermitian Args: matrix (np.ndarray): matrix to verify Return: bool: If matrix is Hermitian """ return np.allclose(matrix, matrix.conj().T)
def add_operator( self, op: np.array, qubits: typing.Optional[typing.Sequence[int]] = None) -> None: """ Apply an operator to the density matrix :param op: :param qubits: :return: """ if isinstance(qubits, int): qubits = [qubits] num_qubits, ndim = _get_dimensions(op) if qubits is None: qubits = np.arange(num_qubits) assert len(qubits) == num_qubits sum_indices_1 = list(qubits) new_indices_1 = list( range(2 * self.num_qubits, 2 * self.num_qubits + len(qubits))) sum_indices_2 = list(self.num_qubits + i for i in qubits) new_indices_2 = list( range(2 * self.num_qubits + len(qubits), 2 * self.num_qubits + 2 * len(qubits))) old_indices = list(range(2 * self.num_qubits)) out_indices = list(range(2 * self.num_qubits)) for i, ind in enumerate(sum_indices_1): out_indices[sum_indices_1[i]] = new_indices_1[i] for i, ind in enumerate(sum_indices_2): out_indices[sum_indices_2[i]] = new_indices_2[i] m = np.reshape(self.density_matrix, 2 * self.num_qubits * [2]) op = np.reshape(op, 2 * len(qubits) * [2]) m = np.einsum( op.conj(), sum_indices_1 + new_indices_1, m, old_indices, op, sum_indices_2 + new_indices_2, out_indices, ) self._density_matrix = m.reshape(2**self.num_qubits, -1)
def is_hermitian(matrix: np.array) -> bool: r""" Whether matrix is Hermitian A square matrix :math:`U` is Hermitian if .. math:: U = U^\dagger where :math:`U^\dagger` is the conjugate transpose of :math:`U`. Args: matrix (np.ndarray): matrix to verify Return: bool: If matrix is Hermitian """ return np.allclose(matrix, matrix.conj().T)
def correlations_from_samples(beamformed_samples_1: np.array, beamformed_samples_2: np.array, record: OrderedDict) -> np.array: """ Correlate two sets of beamformed samples together. Correlation matrices are used and indices corresponding to lag pulse pairs are extracted. Parameters ---------- beamformed_samples_1: ndarray [num_slices, num_beams, num_samples] The first beamformed samples. beamformed_samples_2: ndarray [num_slices, num_beams, num_samples] The second beamformed samples. record: OrderedDict hdf5 record containing bfiq data and metadata Returns ------- values: np.array Array of correlations for each beam, range, and lag """ # beamformed_samples_1: [num_beams, num_samples] # beamformed_samples_2: [num_beams, num_samples] # correlated: [num_beams, num_samples, num_samples] correlated = xp.einsum('jk,jl->jkl', beamformed_samples_1, beamformed_samples_2.conj()) if cupy_available: correlated = xp.asnumpy(correlated) values = [] if record['lags'].size == 0: values.append(xp.array([])) return values # First range offset in samples sample_off = record['first_range_rtt'] * 1e-6 * record['rx_sample_rate'] sample_off = xp.int32(sample_off) # Helpful values converted to units of samples range_off = xp.arange(record['num_ranges'], dtype=xp.int32) + sample_off tau_in_samples = record['tau_spacing'] * 1e-6 * record['rx_sample_rate'] lag_pulses_as_samples = xp.array(record['lags'], xp.int32) * xp.int32(tau_in_samples) # [num_range_gates, 1, 1] # [1, num_lags, 2] samples_for_all_range_lags = (range_off[..., xp.newaxis, xp.newaxis] + lag_pulses_as_samples[xp.newaxis, :, :]) # [num_range_gates, num_lags, 2] row = samples_for_all_range_lags[..., 1].astype(xp.int32) # [num_range_gates, num_lags, 2] column = samples_for_all_range_lags[..., 0].astype(xp.int32) # [num_beams, num_range_gates, num_lags] values = correlated[:, row, column] # Find the sample that corresponds to the second pulse transmitting second_pulse_sample_num = xp.int32( tau_in_samples) * record['pulses'][1] - sample_off - 1 # Replace all ranges which are contaminated by the second pulse for lag 0 # with the data from those ranges after the final pulse. values[:, second_pulse_sample_num:, 0] = values[:, second_pulse_sample_num:, -1] return values