def irl(A: LinearOperator, start_vector=None, eig_n: int = 5, which: str = "LM", max_size: int = 20,
        max_iters: int = 100, tol: float = 1e-7, pbar: bool = False):
    """
    Runs the Implicitly Restarted Lanczos (IRL), which basically
    finds a factorization A V = V H using constant memory.

    Args:
        A (LinearOperator): A linear operator of size (n, n).
        start_vector (Array, optional): An initial vector to start Lanczos of size (n, ).
         Defaults to a random probe.
        eig_n (int): The number of eigenvalues to estimate.
        max_size (int): The maximum number of inner Lanczos iterations to run.
        max_iters (int): The maximum number of outer iterations to run.
        tol (float, optional): Stopping criteria.
        pbar (bool, optional): Show a progress bar.

    Returns:
        tuple:
            - V (Array): Unitary matrix of size (n, max_iters) or (b, n, max_iters)
            - H (Array): The Triangular  matrix of size (max_iters, max_iters) or (b, max_iters, max_iters)
            - idx (Array): The number of iterations ran
            - info (dict): General information about the iterative procedure.
    """
    xnp = A.xnp
    init_val = init_lanczos(xnp=xnp, rhs=start_vector, max_iters=max_size, dtype=A.dtype)
    init_val = init_val + (
        0,
        xnp.array([1.], dtype=A.dtype, device=A.device),
    )

    def cond_fun(state):
        *_, counter, norm = state
        is_not_max = counter < max_iters
        is_large = (norm > tol)
        return is_not_max & xnp.any(is_large)

    def body_fun(state):
        V, diag, subdiag, idx, counter, _ = state
        V, diag, subdiag, *_ = lanczos_fact(A, (V, diag, subdiag, idx), max_iters=max_size, tol=tol, pbar=pbar)
        T = Tridiagonal(subdiag[0, 1:-1], diag[0], subdiag[0, 1:-1]).to_dense()
        V = V[0]
        eigvals, _ = xnp.eigh(T.to_dense())
        eig_slice = get_deflation_eig_slice(eigvals, which=which, eig_n=eig_n, xnp=xnp)
        eigvals = xnp.cast(eigvals[eig_slice], dtype=A.dtype)
        # vec = subdiag[0, -1] * V[:, [-1]]
        vec = V[:, [-1]]
        T, Q = run_shift(T, eigvals, xnp)

        beta = T[eig_n, eig_n - 1]
        sigma = Q[-1, eig_n - 1]
        new_vec = beta * V[:, [eig_n]] + sigma * vec
        V0 = V[:, 1:-1] @ Q[:, :eig_n]
        T0 = T[:eig_n, :eig_n]
        norm = xnp.norm(A @ V0 - V0 @ T0)

        init_val = init_lanczos_from_vec(T0, V0, xnp, new_vec.T, rest=eig_n, max_iters=max_size)
        V, diag, subdiag, idx, _ = init_val
        return V, diag, subdiag, idx + 1, counter + 1, norm[None]

    while_fn, info = xnp.while_loop_winfo(lambda s: s[-1][0], tol, max_iters, pbar=pbar)
    state = while_fn(cond_fun, body_fun, init_val)
    V, diag, subdiag, idx, *_ = state
    return V, diag, subdiag, idx, info

    Runs the Implicitly Restarted Lanczos (IRL), which basically
    finds a factorization A V = V H using constant memory.

    Args:
        A (LinearOperator): A linear operator of size (n, n).
        start_vector (Array, optional): An initial vector to start Lanczos of size (n, ).
         Defaults to a random probe.
        eig_n (int): The number of eigenvalues to estimate.
        max_size (int): The maximum number of inner Lanczos iterations to run.
        max_iters (int): The maximum number of outer iterations to run.
        tol (float, optional): Stopping criteria.
        pbar (bool, optional): Show a progress bar.

    Returns:
        tuple:
            - V (Array): Unitary matrix of size (n, max_iters) or (b, n, max_iters)
            - H (Array): The Triangular  matrix of size (max_iters, max_iters) or (b, max_iters, max_iters)
            - idx (Array): The number of iterations ran
            - info (dict): General information about the iterative procedure.
    
cola/linalg/decompositions/lanczos.py