//  [python]
def _split_next(successor: Successor,
                node: AbstractNode,
                node1_axes: Sequence[Ax],
                node2_axes: Sequence[Ax],
                mode: Text = 'svd',
                side: Optional[Text] = 'left',
                rank: Optional[int] = None,
                cum_percentage: Optional[float] = None,
                cutoff: Optional[float] = None) -> Tuple[Node, Node]:
    batch_axes = successor.hints['batch_axes']
    node1_axes = successor.hints['node1_axes']
    node2_axes = successor.hints['node2_axes']
    permutation_dims = successor.hints['permutation_dims']

    batch_shape = torch.tensor(node._shape)[batch_axes].tolist()
    node1_shape = torch.tensor(node._shape)[node1_axes]
    node2_shape = torch.tensor(node._shape)[node2_axes]
    
    node_tensor = node._direct_get_tensor(successor.node_ref,
                                          successor.index)
    if permutation_dims:
        node_tensor = node_tensor \
            .permute(*(batch_axes + node1_axes + node2_axes)) \
            .reshape(*(batch_shape +
                       [node1_shape.prod().item()] +
                       [node2_shape.prod().item()]))
    else:
        node_tensor = node_tensor \
            .reshape(*(batch_shape +
                       [node1_shape.prod().item()] +
                       [node2_shape.prod().item()]))

    if (mode == 'svd') or (mode == 'svdr'):
        u, s, vh = torch.linalg.svd(node_tensor, full_matrices=False)
        
        lst_ranks = []
        
        if rank is None:
            rank = s.shape[-1]
            lst_ranks.append(rank)
        else:
            lst_ranks.append(min(max(1, rank), s.shape[-1]))
            
        if cum_percentage is not None:
            s_percentages = s.cumsum(-1) / \
                (s.sum(-1, keepdim=True).expand(s.shape) + 1e-10) # To avoid having all 0's
            cum_percentage_tensor = cum_percentage * torch.ones_like(s)
            cp_rank = torch.lt(
                s_percentages,
                cum_percentage_tensor
                ).view(-1, s.shape[-1]).any(dim=0).sum()
            lst_ranks.append(max(1, cp_rank.item() + 1))
            
        if cutoff is not None:
            cutoff_tensor = cutoff * torch.ones_like(s)
            co_rank = torch.ge(
                s,
                cutoff_tensor
                ).view(-1, s.shape[-1]).any(dim=0).sum()
            lst_ranks.append(max(1, co_rank.item()))
        
        # Select rank from specified restrictions
        rank = min(lst_ranks)
        
        u = u[..., :rank]
        s = s[..., :rank]
        vh = vh[..., :rank, :]

        if mode == 'svdr':
            phase = torch.sign(torch.randn(s.shape))
            phase = torch.diag_embed(phase)
            u = u @ phase
            vh = phase @ vh

        if side == 'left':
            u = u @ torch.diag_embed(s)
        elif side == 'right':
            vh = torch.diag_embed(s) @ vh
        else:
            raise ValueError('`side` can only be "left" or "right"')

        node1_tensor = u
        node2_tensor = vh

    elif mode == 'qr':
        q, r = torch.linalg.qr(node_tensor)
        rank = q.shape[-1]

        node1_tensor = q
        node2_tensor = r

    elif mode == 'rq':
        q, r = torch.linalg.qr(node_tensor.transpose(-1, -2))
        q = q.transpose(-1, -2)
        r = r.transpose(-1, -2)
        rank = r.shape[-1]

        node1_tensor = r
        node2_tensor = q

    else:
        raise ValueError('`mode` can only be "svd", "svdr", "qr" or "rq"')

    node1_tensor = node1_tensor.reshape(
        *(batch_shape + node1_shape.tolist() + [rank]))
    node2_tensor = node2_tensor.reshape(
        *(batch_shape + [rank] + node2_shape.tolist()))

    children = successor.child
    children[0]._direct_set_tensor(node1_tensor)
    children[1]._direct_set_tensor(node2_tensor)

    # Record in inverse_memory while contracting, if network is traced
    # (to delete memory if possible)
    if node._network._traced:
        node._check_inverse_memory(successor.node_ref)

    return children[0], children[1]