Example #1
0
    def test_optimization_exact_input(self):
        """
        Test the optimization step accuracy.

        This test creates the exact input power-spectrum and tri-spectrum and then performs
        the optimization step of the algorithm. Since the optimizer is not perfect, the error
        is at most 5e-7, instead of the expected perfect-fit score 0. This test is performed for both
        real data and complex data.
        """
        seed: int = 1995
        optimization_error_upper_bound: float = 5e-7
        signal_length: int = 5
        approximation_rank: int = 2
        rng = Generator(PCG64(seed))

        for data_type in [np.complex128, np.float64]:
            exact_cov: Matrix = generate_covariance(signal_length,
                                                    approximation_rank,
                                                    data_type, rng)[0]
            exact_cov_fourier_basis: Matrix = change_to_fourier_basis(
                exact_cov)
            exact_power_spectrum: Vector = np.ascontiguousarray(
                np.real(np.diag(exact_cov_fourier_basis)))
            exact_tri_spectrum: ThreeDMatrix = calc_exact_tri_spectrum(
                exact_cov_fourier_basis, data_type)

            g, min_fit_score = perform_optimization(exact_tri_spectrum,
                                                    exact_power_spectrum,
                                                    signal_length, data_type)
            min_fit_score = abs(min_fit_score)
            self.assertLess(min_fit_score, optimization_error_upper_bound)
            print(
                f'Optimization error for exact data of type {data_type} is smaller than '
                f'{optimization_error_upper_bound}')
Example #2
0
    def test_power_spectrum_and_tri_spectrum_consistency(self):
        """
        Test the consistency of the power-spectrum estimation.

        This test validates that the estimation over a large number of observations (without noise)
        is "very close" to the exact power-spectrum

        """
        rng = Generator(PCG64(596))
        signal_length: int = rng.integers(low=10, high=20)
        observations_num: int = rng.integers(low=10000, high=50000)
        approximation_rank: int = rng.integers(low=2, high=signal_length)
        data_type = np.complex128
        tol = 1e-3
        exact_covariance, eigenvectors, eigenvalues = generate_covariance(
            signal_length, approximation_rank, data_type, rng)
        observations = generate_observations(eigenvectors, eigenvalues,
                                             approximation_rank,
                                             observations_num, data_type, rng)
        observations_fourier: Matrix = np.fft.fft(observations,
                                                  norm="ortho",
                                                  axis=1)
        exact_cov_fourier_basis: Matrix = change_to_fourier_basis(
            exact_covariance)
        exact_diagonal: Vector = np.real(np.diag(exact_cov_fourier_basis))
        power_spectrum_estimation: Vector = estimate_power_spectrum(
            observations_fourier)
        exact_tri_spectrum: ThreeDMatrix = calc_exact_tri_spectrum(
            exact_cov_fourier_basis, data_type)
        estimate_tri_spectrum: ThreeDMatrix = estimate_tri_spectrum_v2(
            observations_fourier)

        print(f'Observations number: {observations_num}')
        print(
            f'Complex Tri-spectrum estimation error: {np.max(np.abs(estimate_tri_spectrum - exact_tri_spectrum))}'
        )

        # Validate both tri-spectrum and power-spectrum estimations are consistent.
        self.assertTrue(
            np.allclose(exact_diagonal,
                        power_spectrum_estimation,
                        atol=tol,
                        rtol=0),
            msg=f'Power-spectrum estimation is inconsistent!, error=' +
            f'{np.max(np.abs(power_spectrum_estimation - exact_diagonal))}')
        self.assertTrue(
            np.allclose(estimate_tri_spectrum,
                        exact_tri_spectrum,
                        atol=tol,
                        rtol=0),
            msg=f'Tri-spectrum estimation is inconsistent!, error=' +
            f'{np.max(np.abs(estimate_tri_spectrum - exact_tri_spectrum))}')
Example #3
0
    def test_real_data_tri_spectrum_consistency(self):
        """
        Test the consistency of the tri-spectrum estimation in the case of REAL data (since the exact
        tri-spectrum becomes more complicated in this case).

        This test validates that the estimation over a large number of observations (without noise)
        is "very close" to the exact tri-spectrum

        """
        rng = Generator(PCG64(596))
        signal_length: int = rng.integers(low=10, high=20)
        observations_num: int = rng.integers(low=10000, high=40000)
        approximation_rank: int = rng.integers(low=2, high=signal_length)
        data_type = np.float64
        tol = 1e-3
        exact_covariance, eigenvectors, eigenvalues = generate_covariance(
            signal_length, approximation_rank, data_type, rng)
        observations = generate_observations(eigenvectors, eigenvalues,
                                             approximation_rank,
                                             observations_num, data_type, rng)
        observations_fourier: Matrix = np.fft.fft(observations, norm="ortho")
        exact_cov_fourier_basis: Matrix = change_to_fourier_basis(
            exact_covariance)
        exact_tri_spectrum: ThreeDMatrix = calc_exact_tri_spectrum(
            exact_cov_fourier_basis, data_type)
        estimate_tri_spectrum: ThreeDMatrix = estimate_tri_spectrum_v2(
            observations_fourier)

        print(f'Observations number: {observations_num}')
        print(
            f'Real Tri-spectrum estimation error: {np.max(np.abs(estimate_tri_spectrum - exact_tri_spectrum))}'
        )

        # Validate the tri-spectrum estimation in the real case is consistent.
        self.assertTrue(
            np.allclose(estimate_tri_spectrum,
                        exact_tri_spectrum,
                        atol=tol,
                        rtol=0),
            msg=f'Tri-spectrum estimation is inconsistent!, error=' +
            f'{np.max(np.abs(estimate_tri_spectrum - exact_tri_spectrum))}')
Example #4
0
def _test_optimization_template(data_type, signal_length, approximation_rank,
                                seed):
    rng = Generator(PCG64(seed))
    exact_covariance, eigenvectors, eigenvalues = generate_covariance(
        signal_length, approximation_rank, data_type, rng)
    exact_cov_fourier_basis: Matrix = change_to_fourier_basis(exact_covariance)
    exact_power_spectrum: Vector = np.real(np.diag(exact_cov_fourier_basis))
    exact_tri_spectrum: ThreeDMatrix = calc_exact_tri_spectrum(
        exact_cov_fourier_basis, data_type)
    diagonals = find_diagonals(exact_cov_fourier_basis)
    g_mats = np.array([
        diagonal.reshape(-1, 1).dot(np.conj(diagonal.reshape(-1, 1).T))
        for diagonal in diagonals
    ])
    g_mats = np.vstack((np.outer(exact_power_spectrum,
                                 exact_power_spectrum).reshape(1, 3,
                                                               3), g_mats))
    optimization_object, _, _ = create_optimization_objective(
        exact_tri_spectrum, exact_power_spectrum, data_type, use_cp=False)
    return optimization_object(
        g_mats
    ), exact_tri_spectrum, exact_power_spectrum, g_mats, exact_cov_fourier_basis
Example #5
0
    def test_coefficient_matrix_construction(self):
        """
        Test coefficient matrix properties

        This test validates that the coefficients matrix in the phase retrieval algorithm follows the
        theoretical properties.

        """
        rng = Generator(PCG64(1995))
        n: int = rng.integers(low=9, high=20)
        r: int = rng.integers(low=2, high=np.floor(np.sqrt(n)))
        mat: Matrix = generate_covariance(n, r, np.complex128, rng)[0]
        mat = change_to_fourier_basis(mat)

        coeff_mat: Matrix = coefficient_matrix_construction(
            mat, signal_length=n, approximation_rank=r)
        self.assertEqual(coeff_mat.shape[0], n**3)
        self.assertEqual(coeff_mat.shape[1], (1 + r**4) * n)

        other_mat: Matrix = matrix_construction_alternative(
            mat, signal_length=n, approximation_rank=r)
        self.assertTrue(np.allclose(coeff_mat, other_mat),
                        msg=f'{np.max(np.abs(other_mat - coeff_mat))}')
Example #6
0
    def test_phase_retrieval_on_exact_data(self):
        """
        Test phase-retrieval performance on exact data.

        This test performs the phase-retrieval on the exact covariance, up to some random integer shift.
        The output estimated covariance should be equal to the exact covariance, up to some shift of both its axes,
        i.e the estimation error should be very close to zero (about 10^-20).
        This test is performed for both real and complex data.
        """
        rng = Generator(PCG64(1995))
        signal_length: int = rng.integers(low=9, high=20)
        approximation_rank: int = rng.integers(low=2,
                                               high=np.floor(
                                                   np.sqrt(signal_length)))
        tol: float = 1e-20

        for data_type in [np.complex128, np.float64]:
            exact_cov: Matrix = generate_covariance(signal_length,
                                                    approximation_rank,
                                                    data_type, rng)[0]
            random_shift: int = rng.integers(low=0, high=signal_length)
            rotated_cov: Matrix = np.roll(exact_cov,
                                          shift=[random_shift, random_shift],
                                          axis=[0, 1])
            exact_cov_fourier_basis: Matrix = change_to_fourier_basis(
                rotated_cov)

            random_phases: Vector = 1j * rng.standard_normal(signal_length - 1)
            input_cov = np.multiply(
                exact_cov_fourier_basis,
                circulant([1] + np.exp(random_phases).tolist()))
            estimated_cov: Matrix = phase_retrieval(input_cov, signal_length,
                                                    approximation_rank)
            estimation_error: float = calc_estimation_error(
                exact_cov, estimated_cov)

            self.assertTrue(np.allclose(estimation_error, 0, atol=tol, rtol=0))
Example #7
0
    def test_equivalence_to_naive_method(self):
        """
        Test methods equivalence.

        This test validates that both the naive method and the improved method return identical
        output (up to numerical inaccuracies) for some random signal.

        """
        rng = Generator(PCG64(596))
        signal_length: int = rng.integers(low=10, high=40)
        observations_num: int = rng.integers(low=5, high=20)
        approximation_rank: int = rng.integers(low=5, high=20)
        data_type = np.complex128
        covariance, eigenvectors, eigenvalues = generate_covariance(
            signal_length, approximation_rank, data_type, rng)
        observations = generate_observations(eigenvectors, eigenvalues,
                                             approximation_rank,
                                             observations_num, data_type, rng)
        observations_fourier: Matrix = np.fft.fft(observations,
                                                  axis=1,
                                                  norm="ortho")
        tri_spectrum_naive: ThreeDMatrix = estimate_tri_spectrum_naive(
            observations_fourier)
        tri_spectrum_improved: ThreeDMatrix = estimate_tri_spectrum_v2(
            observations_fourier)
        # Validate both tri-spectra are equal
        self.assertTrue(
            np.allclose(np.abs(tri_spectrum_naive),
                        np.abs(tri_spectrum_improved)),
            msg=f'{np.max(np.abs(tri_spectrum_improved - tri_spectrum_naive))}'
        )
        self.assertTrue(
            np.allclose(np.angle(tri_spectrum_naive),
                        np.angle(tri_spectrum_improved)),
            msg=
            f'{np.max(np.angle(tri_spectrum_improved) - np.angle(tri_spectrum_naive))}'
        )
def main(signal_lengths: List[int], observations_numbers: List[int],
         approximation_ranks: List[int], noise_powers: List[float],
         shifts_distribution_type: str, trials_num: int, data_type,
         results_path: str, experiment_name: str, first_seed: int,
         distribution_params: Dict) -> None:
    """ The main function of this project

    This functions performs the desired experiment according to the given configuration.
    The function runs the random_svd and random_id for every combination of data_size, approximation rank and increment
    given in the config and saves all the results to a csv file in the results folder (given in the configuration).
    """
    results_log = DataLog(LogFields)  # Initializing an empty results log.

    for signal_length, noise_power, approximation_rank, observations_num in product(
            signal_lengths, noise_powers, approximation_ranks,
            observations_numbers):
        if approximation_rank >= np.sqrt(signal_length):
            warnings.warn(
                f'Approximation rank {approximation_rank} is at least the square-root of the '
                +
                f'signal length {signal_length}, consistency is NOT guaranteed!',
                Warning)
        shifts_distribution: Vector = create_discrete_distribution(
            shifts_distribution_type, signal_length, distribution_params)
        mean_error: float = 0
        max_error: float = 0.0
        trials_seeds: Vector = np.arange(first_seed,
                                         first_seed + trials_num).tolist()

        for trial_index, trial_seed in enumerate(trials_seeds):
            print(f'Started trial no. {trial_index}')
            rng = Generator(PCG64(trial_seed))  # Set trial's random generator.
            exact_covariance, eigenvectors, eigenvalues = generate_covariance(
                signal_length, approximation_rank, data_type, rng)
            observations = generate_observations(eigenvectors, eigenvalues,
                                                 approximation_rank,
                                                 observations_num, data_type,
                                                 rng)
            observations_shifts: List[int] = rng.choice(signal_length,
                                                        size=observations_num,
                                                        p=shifts_distribution)
            observations = generate_shifts_and_noise(observations,
                                                     observations_shifts,
                                                     noise_power, data_type,
                                                     rng)
            observations_fourier: Matrix = fft(observations,
                                               norm="ortho",
                                               axis=1)

            exact_cov_fourier_basis: Matrix = change_to_fourier_basis(
                exact_covariance)
            if np.any(np.abs(exact_cov_fourier_basis)) < 1e-15:
                warnings.warn(
                    "The covariance matrix in Fourier basis has some 0 entries, "
                    + "consistency is NOT guaranteed!", Warning)

            estimated_covariance: Matrix = low_rank_multi_reference_factor_analysis(
                observations_fourier, signal_length, approximation_rank,
                noise_power, data_type)
            current_error: float = calc_estimation_error(
                exact_covariance, estimated_covariance)
            mean_error += current_error
            print(f'Current error: {current_error}')
            max_error = max(max_error, current_error)

        mean_error /= trials_num
        print(
            f'Finished experiment of signal length L={signal_length}, n={observations_num}, '
            f'r={approximation_rank}, noise={noise_power} with mean error {mean_error} and max error {max_error}'
        )

        # Appending all the experiment results to the log.
        results_log.append(LogFields.DataSize, signal_length)
        results_log.append(LogFields.DataType, data_type.__name__)
        results_log.append(LogFields.ApproximationRank, approximation_rank)
        results_log.append(LogFields.NoisePower, noise_power)
        results_log.append(LogFields.ObservationsNumber, observations_num)
        results_log.append(LogFields.TrialsNum, trials_num)
        results_log.append(LogFields.ShiftsDistribution,
                           shifts_distribution_type)
        results_log.append(LogFields.MeanError, mean_error)
        results_log.append(LogFields.MaxError, max_error)

    results_log.save_log(f'{experiment_name} results',
                         results_folder_path=results_path)