def graph_embed(A, mean_photon_per_mode=1.0, make_traceless=False, rtol=1e-05, atol=1e-08): r"""Embed a graph into a Gaussian state. Given a graph in terms of a symmetric adjacency matrix (in general with arbitrary complex entries), returns the squeezing parameters and interferometer necessary for creating the Gaussian state whose off-diagonal parts are proportional to that matrix. Uses :func:`~.takagi`. Args: A (array[complex]): square, symmetric (weighted) adjacency matrix of the graph mean_photon_per_mode (float): guarantees that the mean photon number in the pure Gaussian state representing the graph satisfies :math:`\frac{1}{N}\sum_{i=1}^N sinh(r_{i})^2 ==` :code:``mean_photon`` make_traceless (bool): Removes the trace of the input matrix, by performing the transformation :math:`\tilde{A} = A-\mathrm{tr}(A) \I/n`. This may reduce the amount of squeezing needed to encode the graph but will lead to different photon number statistics for events with more than one photon in any mode. rtol (float): relative tolerance used when checking if the input matrix is symmetric atol (float): absolute tolerance used when checking if the input matrix is symmetric Returns: tuple[array, array]: squeezing parameters of the input state to the interferometer, and the unitary matrix representing the interferometer """ (m, n) = A.shape if m != n: raise ValueError("The matrix is not square.") if not np.allclose(A, np.transpose(A), rtol=rtol, atol=atol): raise ValueError("The matrix is not symmetric.") if make_traceless: A = A - np.trace(A) * np.identity(n) / n scale = adj_scaling(A, n * mean_photon_per_mode) A = scale * A s, U = takagi(A, tol=atol) vals = -np.arctanh(s) return vals, U
def bipartite_graph_embed(A, mean_photon_per_mode=1.0, rtol=1e-05, atol=1e-08): r"""Embed a bipartite graph into a Gaussian state. Given a bipartite graph in terms of an adjacency matrix (in general with arbitrary complex entries), returns the two-mode squeezing parameters and interferometers necessary for creating the Gaussian state that encodes such adjacency matrix Uses :func:`~.takagi`. Args: A (array[complex]): square, (weighted) adjacency matrix of the bipartite graph mean_photon_per_mode (float): guarantees that the mean photon number in the pure Gaussian state representing the graph satisfies :math:`\frac{1}{N}\sum_{i=1}^N sinh(r_{i})^2 ==` :code:``mean_photon`` rtol (float): relative tolerance used when checking if the input matrix is symmetric atol (float): absolute tolerance used when checking if the input matrix is symmetric Returns: tuple[array, array, array]: squeezing parameters of the input state to the interferometer, and the unitaries matrix representing the interferometer """ (m, n) = A.shape if m != n: raise ValueError("The matrix is not square.") B = np.block([[0 * A, A], [A.T, 0 * A]]) scale = adj_scaling(B, 2 * n * mean_photon_per_mode) A = scale * A if np.allclose(A, A.T, rtol=rtol, atol=atol): s, u = takagi(A, tol=atol) v = u else: u, s, v = np.linalg.svd(A) v = v.T vals = -np.arctanh(s) return vals, u, v