Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

amen_mv exceeds sweep limit when working with complex numbers #98

Open
levdik opened this issue Oct 19, 2023 · 2 comments
Open

amen_mv exceeds sweep limit when working with complex numbers #98

levdik opened this issue Oct 19, 2023 · 2 comments
Labels

Comments

@levdik
Copy link

levdik commented Oct 19, 2023

amen_mv weirdly exceeds number of sweeps limit when working with some complex-valued matrices and vectors.

Here is the simplest example I could find:

d = 3

x = tt.rand(2, d)
x += 0.1j * x

a = tt.matrix(tt.rand(4, d))
y = amen_mv(a, x, 1e-12, nswp=1000)[0]

error = np.linalg.norm(y.full(asvector=True) - np.dot(a.full(), x.full(asvector=True)))
print(f'Error: {error}')

This results in:

amen-mv: swp=1{1}, max_dx=1.564e+00, max_r=4
amen-mv: swp=1{0}, max_dx=3.960e-01, max_r=2
amen-mv: swp=2{1}, max_dx=2.027e-01, max_r=4
 
 .
 .
 .

amen-mv: swp=999{0}, max_dx=3.928e-01, max_r=2
amen-mv: swp=1000{1}, max_dx=2.005e-01, max_r=4
amen-mv: swp=1000{0}, max_dx=3.921e-01, max_r=2
Error: 5.999547960677571e-14

The method converges quickly, though, as the error is negligible even if nswp=1:

amen-mv: swp=1{1}, max_dx=3.305e+00, max_r=4
amen-mv: swp=1{0}, max_dx=3.960e-01, max_r=2
Error: 1.1873159181672063e-14
@levdik levdik changed the title amen_mv exceeds sweep limit when working with complex numbers amen_mv exceeds sweep limit when working with complex numbers Oct 19, 2023
@daskol
Copy link
Collaborator

daskol commented Oct 19, 2023

First of all, it worth to note that tolerance tol is not an error of matvec approximation. It characterizes an L₂-norm of change in core during iterations.

We can see this if we run amen_mv() with verb=1 to see norm of residuals during iterations. Or we can see change it with your slightly modified code.

import numpy as np
from numpy.testing import assert_array_almost_equal

import tt
from tt.amen import amen_mv


def test_amen_mv(nosweeps, d=3):
    x = tt.rand(2, d)
    x += 0.1j * x

    a = tt.matrix(tt.rand(4, d))
    y = amen_mv(a, x, 1e-12, nswp=nosweeps, verb=0)[0]

    y_exact = np.dot(a.full(), x.full(asvector=True))
    error = np.linalg.norm(y.full(asvector=True) - y_exact)
    print(f'error: {error:e}')
    return y

Then we set manually random seed and compute matvecs for 10 sweeps and 11 sweeps.

np.random.seed(42)
y_curr = test_amen_mv(10)

np.random.seed(42)
y_next = test_amen_mv(11)

We do not see difference in dense output. Resulting vector are the same.

assert_array_almost_equal(y_curr.full(), y_next.full())  # Ok

However, cores could change significant.

def get_cores(self: tt.vector) -> np.ndarray:
    """Tuple of views on TT-cores. Each element in the list is a view on
    underlying buffer.
    """
    offset = 0
    cores = []
    ranks = tuple(self.r.tolist())
    for core_shape in zip(ranks[:-1], self.n, ranks[1:]):
        core_size = np.prod(core_shape)
        core = self.core[offset:offset + core_size]
        cores.append(core.reshape(core_shape, order='F'))
        offset += core_size
    return cores


cores_curr = get_cores(y_curr)[::-1]
cores_next = get_cores(y_next)[::-1]
for i, (core_curr, core_next) in enumerate(zip(cores_curr, cores_next)):
    print(f'core_curr[{i}] =\n')
    print(core_curr.squeeze())
    print()
    print(f'core_next[{i}] =\n')
    print(core_next.squeeze())
    print()
    assert_array_almost_equal(core_curr, core_next)

The absolute difference in this example is about 1.4 and relative is about 1.5, i.e. elements are significantly differ. This is what are the first TT-cores.

core_curr[0] =

[[-2.49107418-2.74883544e-17j  0.92247455+1.80844437e-18j]
 [ 0.33472097-4.18401217e-02j  0.90388919-1.12986149e-01j]]

core_next[0] =

[[-2.49107418e+00+4.96719386e-17j  9.22474549e-01-5.99197900e-17j]
[ 8.43940704e-19+3.37325845e-01j  5.00939089e-17+9.10923456e-01j]]

This is quite usual situation with ALS-like method in complex arithmetics, I guess. We probably can add another stopping criterion.

UPD May be @kharyuk has some thoughts.

@levdik
Copy link
Author

levdik commented Oct 27, 2023

@daskol thank you for the quick response! Now it's clear why such behavior occurs.

Another stopping criterion would be a great improvement, in my opinion. Otherwise, the method's behavior isn't quite intuitive. And, in my case, nswp has to be manually changed constantly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants