arb/acb_mat/eig_simple_vdhoeven_mourrain.c

252 lines
5.8 KiB
C

/*
Copyright (C) 2018 Fredrik Johansson
This file is part of Arb.
Arb is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See <http://www.gnu.org/licenses/>.
*/
#include "acb_mat.h"
/* todo: move out */
void
acb_mat_inf_norm(arb_t res, const acb_mat_t A, slong prec)
{
slong i, j, m, n;
arb_t s, t;
m = acb_mat_nrows(A);
n = acb_mat_nrows(A);
if (m == 0 || n == 0)
{
arb_zero(res);
return;
}
arb_init(s);
arb_init(t);
arb_zero(res);
for (i = 0; i < m; i++)
{
acb_abs(s, acb_mat_entry(A, i, 0), prec);
for (j = 1; j < n; j++)
{
acb_abs(t, acb_mat_entry(A, i, j), prec);
arb_add(s, s, t, prec);
}
arb_max(res, res, s, prec);
}
arb_clear(s);
arb_clear(t);
}
static void
diagonal_certify(arb_t epsilon, arb_t eta1, arb_t eta2, const acb_mat_t D, const acb_mat_t H, slong prec)
{
arb_t mu, sigma, alpha, t, u, v;
acb_t d;
slong i, j, n;
arb_init(mu);
arb_init(sigma);
arb_init(alpha);
arb_init(t);
arb_init(u);
arb_init(v);
acb_init(d);
n = acb_mat_nrows(D);
/* D = diagonal matrix; H = off diagonal matrix */
/* mu = ||D|| */
acb_mat_inf_norm(mu, D, prec);
/* sigma = sigma(D) = separation number */
arb_pos_inf(sigma);
for (i = 0; i < n; i++)
{
for (j = i + 1; j < n; j++)
{
acb_sub(d, acb_mat_entry(D, i, i), acb_mat_entry(D, j, j), prec);
acb_abs(t, d, prec);
arb_min(sigma, sigma, t, prec);
}
}
/* eta1 = ||Delta(H)|| Delta = diagonal projection */
arb_zero(eta1);
/* eta2 = ||Omega(H)|| Omega = off-diagonal projection */
acb_mat_inf_norm(eta2, H, prec);
/* alpha = min(sigma / (6 mu), 1/4) */
arb_div(t, sigma, mu, prec);
arb_div_ui(t, t, 6, prec);
arb_set_d(u, 0.25);
arb_min(alpha, t, u, prec);
arb_add(t, eta1, eta2, prec);
arb_mul(u, alpha, mu, prec);
arb_mul_2exp_si(u, u, -3);
arb_mul(v, alpha, sigma, prec);
arb_mul_2exp_si(v, v, -3);
if (arb_le(t, u) && arb_le(eta2, v))
{
arb_div(epsilon, eta2, sigma, prec);
arb_mul_ui(epsilon, epsilon, 3, prec);
}
else
{
arb_indeterminate(epsilon);
}
arb_clear(mu);
arb_clear(sigma);
arb_clear(alpha);
arb_clear(t);
arb_clear(u);
arb_clear(v);
acb_clear(d);
}
int
acb_mat_eig_simple_vdhoeven_mourrain(acb_ptr E,
acb_mat_t L, acb_mat_t R, const acb_mat_t A,
acb_srcptr E_approx, const acb_mat_t R_approx, slong prec)
{
acb_mat_t D, T, AT;
int result;
slong i, j, n;
result = 0;
n = acb_mat_nrows(A);
if (n == 0)
return 1;
if (n == 1)
{
acb_set_round(E, acb_mat_entry(A, 0, 0), prec);
if (L != NULL)
acb_one(acb_mat_entry(L, 0, 0));
if (R != NULL)
acb_one(acb_mat_entry(R, 0, 0));
return 1;
}
acb_mat_init(D, n, n);
acb_mat_init(T, n, n);
acb_mat_init(AT, n, n);
/* T D = A T */
acb_mat_get_mid(T, R_approx);
acb_mat_mul(AT, A, T, prec);
if (acb_mat_solve(D, T, AT, prec))
{
acb_mat_t DD, DH;
arb_t epsilon, eta1, eta2;
arb_init(epsilon);
arb_init(eta1);
arb_init(eta2);
acb_mat_init(DD, n, n);
acb_mat_init(DH, n, n);
for (i = 0; i < n; i++)
acb_set(acb_mat_entry(DD, i, i), acb_mat_entry(D, i, i));
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
if (i != j)
acb_set(acb_mat_entry(DH, i, j), acb_mat_entry(D, i, j));
diagonal_certify(epsilon, eta1, eta2, DD, DH, 2 * MAG_BITS);
if (arb_is_finite(epsilon))
{
for (i = 0; i < n; i++)
{
/* note: paper actually uses D_c which would be better? */
acb_set(E + i, acb_mat_entry(D, i, i));
arb_add_error(acb_realref(E + i), eta2);
arb_add_error(acb_imagref(E + i), eta2);
}
result = 1;
/* unlikely */
for (i = 0; i < n; i++)
for (j = i + 1; j < n; j++)
if (acb_overlaps(E + i, E + j))
result = 0;
if (result && (R != NULL || L != NULL))
{
mag_t ep, em;
mag_init(ep);
mag_init(em);
arb_get_mag(ep, epsilon);
acb_mat_zero(D);
acb_mat_add_error_mag(D, ep);
acb_mat_mul(D, T, D, MAG_BITS);
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
acb_get_mag(ep, acb_mat_entry(D, i, j));
acb_add_error_mag(acb_mat_entry(T, i, j), ep);
}
}
if (R != NULL)
acb_mat_set(R, T);
if (L != NULL)
{
if (!acb_mat_inv(L, T, prec))
acb_mat_indeterminate(L);
}
mag_clear(ep);
mag_clear(em);
}
}
acb_mat_clear(DD);
acb_mat_clear(DH);
arb_clear(epsilon);
arb_clear(eta1);
arb_clear(eta2);
}
if (!result)
{
for (i = 0; i < n; i++)
acb_indeterminate(E + i);
if (L != NULL)
acb_mat_indeterminate(L);
if (R != NULL)
acb_mat_indeterminate(R);
}
acb_mat_clear(D);
acb_mat_clear(T);
acb_mat_clear(AT);
return result;
}