diff --git a/docs/articles.bib b/docs/articles.bib index 5e7f03724..8c7805257 100644 --- a/docs/articles.bib +++ b/docs/articles.bib @@ -658,6 +658,13 @@ @misc{SO_43884189 } + +@misc{StanfordNormDFT, + author = "CCRMA, Stanford University", + title = "Normalized DFT", + howpublished = {https://ccrma.stanford.edu/~jos/st/Normalized_DFT.html} +} + @article{Sych_2009_AComplete, doi = {10.1088/1367-2630/11/1/013006}, url = {https://dx.doi.org/10.1088/1367-2630/11/1/013006}, @@ -788,6 +795,14 @@ @misc{WikiCirc } + +@misc{WikiCirculantMat, + author = "Wikipedia", + title = "Circulant matrix", + howpublished = {https://en.wikipedia.org/wiki/Circulant_matrix} + +} + @misc{WikiCircLaw, author = "Wikipedia", title = "Circular law", diff --git a/docs/rand.rst b/docs/rand.rst index b8e34696f..ded152d32 100644 --- a/docs/rand.rst +++ b/docs/rand.rst @@ -10,7 +10,8 @@ Random .. autosummary:: :toctree: _autosummary - + + toqito.rand.random_circulant_gram toqito.rand.random_density_matrix toqito.rand.random_ginibre toqito.rand.random_povm diff --git a/toqito/rand/__init__.py b/toqito/rand/__init__.py index 646c528df..682a2a491 100644 --- a/toqito/rand/__init__.py +++ b/toqito/rand/__init__.py @@ -5,3 +5,4 @@ from toqito.rand.random_povm import random_povm from toqito.rand.random_state_vector import random_state_vector from toqito.rand.random_states import random_states +from toqito.rand.random_circulant_gram import random_circulant_gram diff --git a/toqito/rand/random_circulant_gram.py b/toqito/rand/random_circulant_gram.py new file mode 100644 index 000000000..965307173 --- /dev/null +++ b/toqito/rand/random_circulant_gram.py @@ -0,0 +1,53 @@ +"""Generate random circulant Gram matrix.""" +import numpy as np + + +def random_circulant_gram(dim: int) -> np.ndarray: + r"""Generate a random circulant Gram matrix of specified dimension. + + A circulant matrix is a square matrix where the elements of each row are identical to the elements of the + previous row such that the elements in one row are relocated by 1 position (in a cyclic manner) compared + to the previous row. The eigenvalues and eigenvectors of this matrix are derived from the Discrete + Fourier Transform (DFT). + + For more information on circulant matrices, see :cite:`WikiCirculantMat`. This function utilizes the + normalized DFT, a variation of DFT with normalized basis vectors. + + For additional information, see :cite:`StanfordNormDFT`. + + The function creates a circulant matrix from a random diagonal matrix and the normalized DFT matrix. + First, it generates a diagonal matrix with random non-negative entries. Next, it constructs the + normalized DFT matrix. Finally, it computes the circulant matrix, which is real due to its origin + from the DFT of a real diagonal matrix. + + Examples + ========= + Generate a random circulant Gram matrix of dimension 4. + + >>> from this_module import random_circulant_gram + >>> circulant_matrix = random_circulant_gram(4) + >>> circulant_matrix.shape + (4, 4) + >>> np.allclose(circulant_matrix, circulant_matrix.T) + True + + References + ========== + .. bibliography:: + :filter: docname in docnames + + :param dim: int + The dimension of the circulant matrix to generate. + + :return: numpy.ndarray + A `dim` x `dim` real, symmetric, circulant matrix. + """ + # Step 1: Generate a random diagonal matrix with non-negative entries + diag_mat = np.diag(np.random.rand(dim)) + + # Step 2: Construct the normalized DFT matrix + dft_mat = np.fft.fft(np.eye(dim)) / np.sqrt(dim) + + # Step 3: Compute the circulant matrix. Since circ_mat is formed from the DFT of a real + # diagonal matrix, it should be real + return np.real(np.conj(dft_mat.T) @ diag_mat @ dft_mat) diff --git a/toqito/rand/tests/test_random_circulant_gram.py b/toqito/rand/tests/test_random_circulant_gram.py new file mode 100644 index 000000000..98c05da2e --- /dev/null +++ b/toqito/rand/tests/test_random_circulant_gram.py @@ -0,0 +1,41 @@ +"""Test random_circulant_gram.""" +import numpy as np +import pytest +from numpy.testing import assert_array_almost_equal, assert_equal + +from toqito.rand.random_circulant_gram import random_circulant_gram + + +@pytest.mark.parametrize( + "dim", + [ + # Test with a matrix of dimension 2. + 2, + # Test with a matrix of higher dimension. + 4, + # Test with another higher dimension. + 5, + # Test with yet another higher dimension. + 10, + ], +) + + +def test_random_circulant_gram(dim): + """Test for random_circulant_gram function.""" + # Generate a random circulant Gram matrix. + circulant_matrix = random_circulant_gram(dim) + + # Ensure the matrix has the correct shape. + assert_equal(circulant_matrix.shape, (dim, dim)) + + # Check that the matrix is symmetric. + assert_array_almost_equal(circulant_matrix, circulant_matrix.T) + + # Check that the matrix is real. + assert_equal(np.isreal(circulant_matrix).all(), True) + + # Check that the matrix is positive semi-definite by verifying + # all eigenvalues are non-negative. + eigenvalues = np.linalg.eigvalsh(circulant_matrix) + assert_array_almost_equal((eigenvalues >= 0), True)