def von_neumann_entropy( state: 'cirq.QUANTUM_STATE_LIKE', qid_shape: Optional[Tuple[int, ...]] = None, validate: bool = True, atol: float = 1e-7, ) -> float: """Calculates the von Neumann entropy of a quantum state in bits. If `state` is a square matrix, it is assumed to be a density matrix rather than a (pure) state tensor. Args: state: The quantum state. qid_shape: The qid shape of the given state. validate: Whether to check if the given state is a valid quantum state. atol: Absolute numerical tolerance to use for validation. Returns: The calculated von Neumann entropy. Raises: ValueError: Invalid quantum state. """ if isinstance(state, QuantumState) and state._is_density_matrix(): state = state.data if isinstance(state, np.ndarray) and state.ndim == 2 and state.shape[0] == state.shape[1]: if validate: if qid_shape is None: qid_shape = (state.shape[0],) validate_density_matrix(state, qid_shape=qid_shape, dtype=state.dtype, atol=atol) eigenvalues = np.linalg.eigvalsh(state) return stats.entropy(np.abs(eigenvalues), base=2) if validate: _ = quantum_state(state, qid_shape=qid_shape, copy=False, validate=True, atol=atol) return 0.0
def plot_density_matrix( matrix: np.ndarray, ax: Optional[plt.Axes] = None, *, show_text: bool = False, title: Optional[str] = None, ) -> plt.Axes: """Generates a plot for a given density matrix. 1. Each entry of the density matrix, a complex number, is plotted as an Argand Diagram where the partially filled red circle represents the magnitude and the line represents the phase angle, going anti-clockwise from positive x - axis. 2. The blue rectangles on the diagonal elements represent the probability of measuring the system in state $|i\rangle$. Rendering scheme is inspired from https://algassert.com/quirk Args: matrix: The density matrix to visualize show_text: If true, the density matrix values are also shown as text labels ax: The axes to plot on title: Title of the plot """ plt.style.use('ggplot') _padding_around_plot = 0.001 matrix = matrix.astype(np.complex128) num_qubits = int(np.log2(matrix.shape[0])) validate_density_matrix(matrix, qid_shape=(2**num_qubits, )) if ax is None: _, ax = plt.subplots(figsize=(10, 10)) ax.set_xlim(0 - _padding_around_plot, 2**num_qubits + _padding_around_plot) ax.set_ylim(0 - _padding_around_plot, 2**num_qubits + _padding_around_plot) for i in range(matrix.shape[0]): for j in range(matrix.shape[1]): _plot_element_of_density_matrix( ax, i, j, np.abs(matrix[i][-j - 1]), np.angle(matrix[i][-j - 1]), show_rect=(i == matrix.shape[1] - j - 1), show_text=show_text, ) ticks, labels = np.arange(0.5, matrix.shape[0]), [ f"{'0'*(num_qubits - len(f'{i:b}'))}{i:b}" for i in range(matrix.shape[0]) ] ax.set_xticks(ticks) ax.set_xticklabels(labels, rotation=90) ax.set_yticks(ticks) ax.set_yticklabels(reversed(labels)) ax.set_facecolor('#eeeeee') if title is not None: ax.set_title(title) return ax
def _numpy_arrays_to_state_vectors_or_density_matrices( state1: np.ndarray, state2: np.ndarray, qid_shape: Optional[Tuple[int, ...]], validate: bool, atol: float, ) -> Tuple[np.ndarray, np.ndarray]: if state1.ndim > 2 or (state1.ndim == 2 and state1.shape[0] != state1.shape[1]): # State tensor, convert to state vector state1 = np.reshape(state1, (np.prod(state1.shape, dtype=np.int64).item(),)) if state2.ndim > 2 or (state2.ndim == 2 and state2.shape[0] != state2.shape[1]): # State tensor, convert to state vector state2 = np.reshape(state2, (np.prod(state2.shape, dtype=np.int64).item(),)) if state1.ndim == 2 and state2.ndim == 2: # Must be square matrices if state1.shape == state2.shape: if qid_shape is None: # Ambiguous whether state tensor or density matrix raise ValueError( 'The qid shape of the given states is ambiguous. ' 'Try specifying the qid shape explicitly or ' 'using a wrapper function like cirq.density_matrix.' ) if state1.shape == qid_shape: # State tensors, convert to state vectors state1 = np.reshape(state1, (np.prod(qid_shape, dtype=np.int64).item(),)) state2 = np.reshape(state2, (np.prod(qid_shape, dtype=np.int64).item(),)) elif state1.shape[0] < state2.shape[0]: # state1 is state tensor and state2 is density matrix. # Convert state1 to state vector state1 = np.reshape(state1, (np.prod(state1.shape, dtype=np.int64).item(),)) else: # state1.shape[0] > state2.shape[0] # state2 is state tensor and state1 is density matrix. # Convert state2 to state vector state2 = np.reshape(state2, (np.prod(state2.shape, dtype=np.int64).item(),)) elif ( state1.ndim == 2 and state2.ndim < 2 and np.prod(state1.shape, dtype=np.int64) == np.prod(state2.shape, dtype=np.int64) ): # state1 is state tensor, convert to state vector state1 = np.reshape(state1, (np.prod(state1.shape, dtype=np.int64).item(),)) elif ( state1.ndim < 2 and state2.ndim == 2 and np.prod(state1.shape, dtype=np.int64) == np.prod(state2.shape, dtype=np.int64) ): # state2 is state tensor, convert to state vector state2 = np.reshape(state2, (np.prod(state2.shape, dtype=np.int64).item(),)) if validate: dim1: int = ( state1.shape[0] if state1.ndim == 2 else np.prod(state1.shape, dtype=np.int64).item() ) dim2: int = ( state2.shape[0] if state2.ndim == 2 else np.prod(state2.shape, dtype=np.int64).item() ) if dim1 != dim2: raise ValueError('Mismatched dimensions in given states: ' f'{dim1} and {dim2}.') if qid_shape is None: qid_shape = (dim1,) else: expected_dim = np.prod(qid_shape, dtype=np.int64) if dim1 != expected_dim: raise ValueError( 'Invalid state dimension for given qid shape: ' f'Expected dimension {expected_dim} but ' f'got dimension {dim1}.' ) for state in (state1, state2): if state.ndim == 2: validate_density_matrix(state, qid_shape=qid_shape, atol=atol) else: validate_normalized_state_vector(state, qid_shape=qid_shape, atol=atol) return state1, state2