docs: def _factorize_for_rhs(self, rhs):
          """Factorize a channel permutation circuit according the block
          structure of the upstream circuit.  This allows to move as much of the
          permutation as possible *around* a reducible circuit upstream.  It
          basically decomposes
  
              ``permutation << rhs --> permutation' << rhs' << residual'``
  
          where rhs' is just a block permutated version of rhs and residual'
          is the maximal part of the permutation that one may move around rhs.
  
          Args:
              rhs (Circuit): An upstream circuit object
  
          Returns:
              tuple: new_lhs_circuit, permuted_rhs_circuit, new_rhs_circuit
  
          Raises:
              .BadPermutationError
          """
          block_structure = rhs.block_structure
  
          block_perm, perms_within_blocks \
              = block_perm_and_perms_within_blocks(self.permutation,
                                                   block_structure)
          fblockp = full_block_perm(block_perm, block_structure)
  
          if not sorted(fblockp) == list(range(self.cdim)):
              raise BadPermutationError()
  
          new_rhs_circuit = CPermutation.create(fblockp)
          within_blocks = [CPermutation.create(within_block)
                           for within_block in perms_within_blocks]
          within_perm_circuit = Concatenation.create(*within_blocks)
          rhs_blocks = rhs.get_blocks(block_structure)
  
          summands = [SeriesProduct.create(within_blocks[p], rhs_blocks[p])
                      for p in invert_permutation(block_perm)]
          permuted_rhs_circuit = Concatenation.create(*summands)
  
          new_lhs_circuit = (self << within_perm_circuit.series_inverse() <<
                             new_rhs_circuit.series_inverse())
  
          return new_lhs_circuit, permuted_rhs_circuit, new_rhs_circuit