Source code for toqito.matrix_ops.vectors_to_gram_matrix

"""Calculates the Gram matrix from a list of vectors."""

import numpy as np


[docs] def vectors_to_gram_matrix(vectors: list[np.ndarray]) -> np.ndarray: r"""Construct the Gram matrix from a list of vectors or density matrices [@WikiGram]. The Gram matrix is a matrix of inner products. This function automatically detects whether the inputs are vectors (pure states) or density matrices (mixed states) and computes the appropriate Gram matrix. For vectors |ψᵢ⟩: G[i, j] = ⟨ψᵢ|ψⱼ⟩ For density matrices ρᵢ: G[i, j] = Tr(ρᵢ ρⱼ) Examples: Example with real vectors: ```python exec="1" source="above" import numpy as np from toqito.matrix_ops import vectors_to_gram_matrix vectors = [np.array([1, 2]), np.array([3, 4])] gram_matrix = vectors_to_gram_matrix(vectors) print(gram_matrix) ``` Example with complex vectors: ```python exec="1" source="above" import numpy as np from toqito.matrix_ops import vectors_to_gram_matrix vectors = [np.array([1+1j, 2+2j]), np.array([3+3j, 4+4j])] gram_matrix = vectors_to_gram_matrix(vectors) print(gram_matrix) ``` Example with density matrices (mixed states): ```python exec="1" source="above" import numpy as np from toqito.matrix_ops import vectors_to_gram_matrix # Two mixed states rho1 = 0.7 * np.array([[1., 0.], [0., 0.]]) + 0.3 * np.eye(2) / 2 rho2 = 0.7 * np.array([[0., 0.], [0., 1.]]) + 0.3 * np.eye(2) / 2 states = [rho1, rho2] gram_matrix = vectors_to_gram_matrix(states) print(gram_matrix) ``` Raises: ValueError: If the vectors are not all of the same shape. Args: vectors: A list of vectors (1D/column arrays for pure states) or density matrices (2D arrays for mixed states). Returns: The Gram matrix with entries G[i,j] = ⟨vᵢ|vⱼ⟩ for vectors or Tr(ρᵢρⱼ) for density matrices. """ # Check that all vectors are of the same shape if not all(v.shape == vectors[0].shape for v in vectors): raise ValueError("All vectors must be of the same shape.") first_input = vectors[0] # Check if inputs are vectors (1D or column vectors) or density matrices (2D with d > 1) if first_input.ndim == 1 or (first_input.ndim == 2 and first_input.shape[1] == 1): # Pure states: use standard Gram matrix construction # Stack vectors into a matrix stacked_vectors = np.column_stack(vectors) # Compute Gram matrix using vectorized operations return np.dot(stacked_vectors.conj().T, stacked_vectors) else: # Mixed states: compute Tr(ρᵢ ρⱼ) n = len(vectors) gram = np.zeros((n, n), dtype=complex) for i in range(n): for j in range(n): gram[i, j] = np.trace(vectors[i] @ vectors[j]) return gram