code: def partial_trace(self, qubits: Qubits) -> 'QubitVector':
          
          N = self.qubit_nb
          R = self.rank
          if R == 1:
              raise ValueError('Cannot take trace of vector')
  
          new_qubits: List[Qubit] = list(self.qubits)
          for q in qubits:
              new_qubits.remove(q)
  
          if not new_qubits:
              raise ValueError('Cannot remove all qubits with partial_trace.')
  
          indices = [self.qubits.index(qubit) for qubit in qubits]
          subscripts = list(EINSUM_SUBSCRIPTS)[0:N*R]
          for idx in indices:
              for r in range(1, R):
                  subscripts[r * N + idx] = subscripts[idx]
          subscript_str = ''.join(subscripts)
  
          # Only numpy's einsum works with repeated subscripts
          tensor = self.asarray()
          tensor = np.einsum(subscript_str, tensor)
  
          return QubitVector(tensor, new_qubits)
docs: Return the partial trace over some subset of qubits