code: def test_parafac2(monkeypatch, normalize_factors, init, linesearch):
      rng = tl.check_random_state(1234)
      tol_norm_2 = 10e-2
      rank = 3
  
      random_parafac2_tensor = random_parafac2(
          shapes=[(15 + rng.randint(5), 30) for _ in range(25)],
          rank=rank,
          random_state=rng,
      )
      # It is difficult to correctly identify B[i, :, r] if A[i, r] is small.
      # This is sensible, since then B[i, :, r] contributes little to the total value of X.
      # To test the PARAFAC2 decomposition in the precence of roundoff errors, we therefore add
      # 0.01 to the A factor matrix.
      random_parafac2_tensor.factors[0] = (
          random_parafac2_tensor.factors[0] + 0.01
      )
  
      tensor = parafac2_to_tensor(random_parafac2_tensor)
      slices = parafac2_to_slices(random_parafac2_tensor)
  
      rec, err = parafac2(
          slices,
          rank,
          random_state=rng,
          init=init,
          n_iter_parafac=2,  # Otherwise, the SVD init will converge too quickly
          normalize_factors=normalize_factors,
          return_errors=True,
          n_iter_max=100,
          linesearch=linesearch,
      )
      rec_tensor = parafac2_to_tensor(rec)
  
      error = T.norm(rec_tensor - tensor, 2)
      error /= T.norm(tensor, 2)
      assert_(
          error < tol_norm_2, "norm 2 of reconstruction higher than tol"
      )
  
      # Test factor correlation
      A_sign = T.sign(random_parafac2_tensor.factors[0])
      rec_A_sign = T.sign(rec.factors[0])
      A_corr = congruence_coefficient(
          A_sign * random_parafac2_tensor.factors[0],
          rec_A_sign * rec.factors[0],
      )[0]
      assert_(A_corr > 0.98)
  
      C_corr = congruence_coefficient(
          random_parafac2_tensor.factors[2], rec.factors[2]
      )[0]
      assert_(C_corr > 0.98)
  
      for i, (true_proj, rec_proj) in enumerate(
          zip(random_parafac2_tensor.projections, rec.projections)
      ):
          true_Bi = (
              T.dot(true_proj, random_parafac2_tensor.factors[1])
              * A_sign[i]
          )
          rec_Bi = T.dot(rec_proj, rec.factors[1]) * rec_A_sign[i]
          Bi_corr = congruence_coefficient(true_Bi, rec_Bi)[0]
          assert_(Bi_corr > 0.98)
  
      # Test convergence criterion
      rec, err = parafac2(
          slices,
          rank,
          random_state=rng,
          init=init,
          normalize_factors=normalize_factors,
          tol=1e-10,
          absolute_tol=1e-3,
          return_errors=True,
          linesearch=linesearch,
      )
      assert len(err) > 2  # Check that we didn't just immediately exit
      assert err[-1] ** 2 < 1e-3
      assert (
          err[-2] ** 2 > 1e-3
      )  # Check that the previous iteration didn't meet the criteria
  
      noisy_slices = [
          slice_
          + tl.tensor(0.001 * rng.standard_normal(T.shape(slice_)))
          for slice_ in slices
      ]
      rec, err = parafac2(
          noisy_slices,
          rank,
          random_state=rng,
          init=init,
          normalize_factors=normalize_factors,
          tol=1e-1,
          absolute_tol=-1,
          return_errors=True,
          linesearch=linesearch,
      )
      assert len(err) > 2  # Check that we didn't just immediately exit
      assert abs(err[-2] ** 2 - err[-1] ** 2) < (1e-1 * err[-2] ** 2)
      assert abs(err[-3] ** 2 - err[-2] ** 2) > (
          1e-1 * err[-3] ** 2
      )  # Check that the previous iteration didn't meet the criteria
  
      assert_class_wrapper_correctly_passes_arguments(
          monkeypatch,
          parafac2,
          Parafac2,
          ignore_args={"return_errors"},
          rank=3,
      )
  
file_path: tensorly/tensorly/decomposition/tests/test_parafac2.py
imports: import itertools
  import numpy
  import pytest
  import tensorly
  from ...random import random_parafac2
  from ...None import backend
  from ...testing import assert_, assert_class_wrapper_correctly_passes_arguments, assert_array_almost_equal, assert_allclose
  from .._parafac2 import Parafac2, parafac2, initialize_decomposition, _BroThesisLineSearch
  from ...parafac2_tensor import Parafac2Tensor, parafac2_to_tensor, parafac2_to_slices
  from ...metrics.factors import congruence_coefficient
label_desc: Use image node_id 1 for calling a global function with example usage: test_parafac2(monkeypatch, normalize_factors, init, linesearch) without return types