def commutator(leftInput, rightInput, contract=True, combine=True):

    # Convert inputs that are terms into lists of terms
    if isinstance(leftInput, term):
        leftTerms = [leftInput]
    else:
        leftTerms = leftInput
    if isinstance(rightInput, term):
        rightTerms = [rightInput]
    else:
        rightTerms = rightInput

    # Check input integrity
    TypeErrorMessage = "commutator inputs must be terms or lists of terms"
    if type(leftTerms) != type([]) or type(rightTerms) != type([]):
        raise TypeError, TypeErrorMessage
    for t in rightTerms:
        if not isinstance(t, term):
            raise TypeError, TypeErrorMessage
    for t in leftTerms:
        if not isinstance(t, term):
            raise TypeError, TypeErrorMessage

    # Construct terms resulting from the commutator
    preNOTerms = []  #terms before normal ordering
    for lterm in leftTerms:
        for rterm in rightTerms:
            preNOTerms.append(multiplyTerms(lterm, rterm))
            preNOTerms.append(multiplyTerms(rterm, lterm))
            preNOTerms[-1].scale(-1)

    # For each term, apply Wick's theorem to convert it to normal order
    noTerms = []  #terms after normal ordering
    for t in preNOTerms:
        noTerms.extend(normalOrder(t))
    del (preNOTerms)

    # Contract any delta functions resulting from the normal ordering,
    # unless told not to
    if contract:
        for t in noTerms:
            t.contractDeltaFuncs()

    # Remove any terms that are zero
    termChop(noTerms)

    # Combine any like terms unless told not to
    if combine:
        combineTerms(noTerms)

    # Return result
    return noTerms
def commutator(leftInput, rightInput, contract = True, combine = True):

  # Convert inputs that are terms into lists of terms
  if isinstance(leftInput,term):
    leftTerms = [leftInput]
  else:
    leftTerms = leftInput
  if isinstance(rightInput,term):
    rightTerms = [rightInput]
  else:
    rightTerms = rightInput

  # Check input integrity
  TypeErrorMessage = "commutator inputs must be terms or lists of terms"
  if type(leftTerms) != type([]) or type(rightTerms) != type([]):
      raise TypeError, TypeErrorMessage
  for t in rightTerms:
    if not isinstance(t, term):
      raise TypeError, TypeErrorMessage
  for t in leftTerms:
    if not isinstance(t, term):
      raise TypeError, TypeErrorMessage

  # Construct terms resulting from the commutator
  preNOTerms = [] #terms before normal ordering
  for lterm in leftTerms:
    for rterm in rightTerms:
      preNOTerms.append( multiplyTerms(lterm,rterm) )
      preNOTerms.append( multiplyTerms(rterm,lterm) )
      preNOTerms[-1].scale(-1)

  # For each term, apply Wick's theorem to convert it to normal order
  noTerms = [] #terms after normal ordering
  for t in preNOTerms:
    noTerms.extend(normalOrder(t))
  del(preNOTerms)

  # Contract any delta functions resulting from the normal ordering,
  # unless told not to
  if contract:
    for t in noTerms:
      t.contractDeltaFuncs()

  # Remove any terms that are zero 
  termChop(noTerms)

  # Combine any like terms unless told not to
  if combine:
    combineTerms(noTerms)

  # Return result
  return noTerms
def decomp_4rdm_to_3rdm_sf(d4, d1, d2, d2_hom, d2_het, d3):
  """
  Returns the list of terms composing the decomposition of the 4 body spin free rdm d4
  in terms of one, two, and three body rdms"
  """

  # This decomposition assumes the density matrices correspond to a spin averaged ensemble state.

  # d4 -- Spin free 4rdm to be decomposed
  # d1 -- One body spin free rdm
  # d2 -- Two body spin free rdm
  # d2_hom -- Sum of all alpha and all beta two body rdms.  The "homogeneous spin" 2rdm.
  # d2_het -- Sum of abab and baba two body rdms.  The "heterogeneous spin" 2rdm.
  # d3 -- Three body spin free rdm

  # Note that d2 = d2_hom + d2_het

  # Check input
  if ( not isinstance(d4, tensor) ) or (len(d4.indices) != 8):
    raise TypeError, "d4 must be a tensor with 8 indices"
  if ( not isinstance(d1, tensor) ) or (len(d1.indices) != 2):
    raise TypeError, "d1 must be a tensor with 2 indices"
  if ( not isinstance(d2, tensor) ) or (len(d2.indices) != 4):
    raise TypeError, "d2 must be a tensor with 4 indices"
  if ( not isinstance(d2_hom, tensor) ) or (len(d2_hom.indices) != 4):
    raise TypeError, "d2_hom must be a tensor with 4 indices"
  if ( not isinstance(d2_het, tensor) ) or (len(d2_het.indices) != 4):
    raise TypeError, "d2_het must be a tensor with 4 indices"
  if ( not isinstance(d3, tensor) ) or (len(d3.indices) != 6):
    raise TypeError, "d3 must be a tensor with 6 indices"

  # Initialize the return value
  decomp = []

  # Compute terms from the sum involving the 3-body cumulant
  for p in range(4):
    ti = range(4)
    del(ti[p])
    ti.insert(0,p)
    for q in range(4):
      bi = range(4)
      del(bi[q])
      bi.insert(0,q)
      for i in range(1,4):
        for j in range(1,4):
          if i != j and ti[i] == bi[j]:
            temp = bi[i]
            bi[i] = bi[j]
            bi[j] = temp

      # Determine the number of index permutations
      n_perm = get_num_perms(ti,bi)

      # Determine the sign
      sign = (-1)**n_perm

      # Create the 1RDM 3RDM term
      bj = [i+4 for i in bi]
      coeff = sign * (0.5)**n_perm
      d1_copy_0 = d1.copy()
      d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
      d3_copy_0 = d3.copy()
      d3_copy_0.indices = [d4.indices[i] for i in (ti[1:4] + bj[1:4])]
      decomp.append(term(coeff, [], [d1_copy_0, d3_copy_0]))

      # Create terms not involving the 3RDM
      d3_decomp = decomp_3rdm_to_2rdm_sf(d3_copy_0, d1, d2)
      for t in d3_decomp:
        coeff = (-1) * sign * (0.5)**n_perm * t.numConstant
        decomp.append(term(coeff, [], [d1_copy_0] + t.tensors))
      
  # Compute terms from the sum involving two 2-body cumulants
  for p in range(1):
    for q in range(p+1,4):
      ti = [p,q]
      for i in range(4):
        if not (i in ti):
          ti.append(i)
      for r in range(4):
        for s in range(r+1,4):
          if s == p or r == q:
            bi = [s,r]
          else:
            bi = [r,s]
          temp = range(4)
          del temp[s]
          del temp[r]
          if temp[0] == ti[3] or temp[1] == ti[2]:
            temp = [temp[1], temp[0]]
          bi += temp
          
          # Determine the number of index permutations
          n_perm = get_num_perms(ti,bi)

          # Determine the sign
          sign = (-1)**n_perm

          # Create 2RDM 2RDM term
          bj = [i+4 for i in bi]
          if n_perm < 2:
            coeff = sign * (0.5)**n_perm
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d2_copy_1 = d2.copy()
            d2_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            decomp.append(term(coeff, [], [d2_copy_0, d2_copy_1]))
          elif n_perm == 2:
            coeff = sign * 0.5
            d2_hom_copy_0 = d2_hom.copy()
            d2_hom_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d2_hom_copy_1 = d2_hom.copy()
            d2_hom_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            decomp.append(term(coeff, [], [d2_hom_copy_0, d2_hom_copy_1]))
            d2_het_copy_0 = d2_het.copy()
            d2_het_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d2_het_copy_1 = d2_het.copy()
            d2_het_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            decomp.append(term(coeff, [], [d2_het_copy_0, d2_het_copy_1]))
            d2_het_copy_0 = d2_het.copy()
            d2_het_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + [bj[1], bj[0]])]
            d2_het_copy_1 = d2_het.copy()
            d2_het_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + [bj[3], bj[2]])]
            decomp.append(term(coeff, [], [d2_het_copy_0, d2_het_copy_1]))
          else:
            raise RuntimeError, "Did not expect a term with %i permutations" %n_perm

          # Create 'un-permuted' 2RDM 1RDM 1RDM terms
          coeff = sign * (-1) * (0.5)**n_perm 
          d2_copy_0 = d2.copy()
          d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
          d1_copy_0 = d1.copy()
          d1_copy_0.indices = [d4.indices[i] for i in (ti[2:3] + bj[2:3])]
          d1_copy_1 = d1.copy()
          d1_copy_1.indices = [d4.indices[i] for i in (ti[3:4] + bj[3:4])]
          decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
          d2_copy_0 = d2.copy()
          d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
          d1_copy_0 = d1.copy()
          d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
          d1_copy_1 = d1.copy()
          d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])]
          decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))

          # Create 'permuted' 2RDM 1RDM 1RDM terms
          if n_perm < 2:
            bj = bi[0:2] + bi[3:4] + bi[2:3]
            n_perm = get_num_perms(ti,bj)
            bj = [i+4 for i in bj]
            coeff = sign * (0.5)**n_perm
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d1_copy_0 = d1.copy()
            d1_copy_0.indices = [d4.indices[i] for i in (ti[2:3] + bj[2:3])]
            d1_copy_1 = d1.copy()
            d1_copy_1.indices = [d4.indices[i] for i in (ti[3:4] + bj[3:4])]
            decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
            bj = bi[1:2] + bi[0:1] + bi[2:4]
            n_perm = get_num_perms(ti,bj)
            bj = [i+4 for i in bj]
            coeff = sign * (0.5)**n_perm
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            d1_copy_0 = d1.copy()
            d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
            d1_copy_1 = d1.copy()
            d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])]
            decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
          elif n_perm == 2:
            bj = bi[1:2] + bi[0:1] + bi[3:4] + bi[2:3]
            n_perm = get_num_perms(ti,bj)
            bj = [i+4 for i in bj]
            coeff = sign * (-1) * (0.5)**n_perm
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])]
            d1_copy_0 = d1.copy()
            d1_copy_0.indices = [d4.indices[i] for i in (ti[2:3] + bj[2:3])]
            d1_copy_1 = d1.copy()
            d1_copy_1.indices = [d4.indices[i] for i in (ti[3:4] + bj[3:4])]
            decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
            d2_copy_0 = d2.copy()
            d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
            d1_copy_0 = d1.copy()
            d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
            d1_copy_1 = d1.copy()
            d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])]
            decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1]))
          else:
            raise RuntimeError, "Did not expect a term with %i permutations" %n_perm

          # Create 1RDM 1RDM 1RDM 1RDM terms
          bj = [i for i in bi]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))
          bj = bi[0:2] + bi[3:4] + bi[2:3]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (-1) * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))
          bj = bi[1:2] + bi[0:1] + bi[2:4]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (-1) * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))
          bj = bi[1:2] + bi[0:1] + bi[3:4] + bi[2:3]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))

  # Compute terms from the sum involving one 2-body cumulant
  for p in range(4):
    for q in range(p+1,4):
      ti = [p,q]
      for i in range(4):
        if not (i in ti):
          ti.append(i)
      for r in range(4):
        for s in range(4):
          if r == s:
            continue
          bi = [r,s]
          temp = range(4)
          del temp[max(r,s)]
          del temp[min(r,s)]
          if temp[0] == ti[3] or temp[1] == ti[2]:
            temp = [temp[1], temp[0]]
          bi += temp

          # Determine the number of index permutations
          n_perm = get_num_perms(ti,bi)

          # Determine the sign
          sign = (-1)**n_perm

          # Compute 1RDM 1RDM 2RDM term
          coeff = 1
          bj = [i for i in bi]
          pairs_rdm1 = [ [ti[i],bj[i]] for i in range(2)]
          pairs_rdm2 = [ [bj[i],ti[i]] for i in range(2,4)]
          for pair in pairs_rdm2:
            if pair[0] == pair[1]:
              pairs_rdm2 = []
              break
          for pair in pairs_rdm2:
            if not (pair in pairs_rdm1):
              #print "swapping bj[2] and bj[3]"
              (bj[3],bj[2]) = (bj[2],bj[3])
              coeff *= -1
              break
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff *= sign * (0.5)**n_perm
          d1_copy_0 = d1.copy()
          d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])]
          d1_copy_1 = d1.copy()
          d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])]
          d2_copy_0 = d2.copy()
          d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])]
          decomp.append(term(coeff, [], [d1_copy_0, d1_copy_1, d2_copy_0]))

          # Compute 'un-permuted' 1RDM 1RDM 1RDM 1RDM term
          bj = [i for i in bi]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (-1) * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))

          # Compute 'permuted' 1RDM 1RDM 1RDM 1RDM term
          bj = bi[0:2] + bi[3:4] + bi[2:3]
          n_perm = get_num_perms(ti,bj)
          bj = [i+4 for i in bj]
          coeff = sign * (0.5)**n_perm
          d1_copies = []
          for j in range(4):
            d1_copies.append(d1.copy())
            d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
          decomp.append(term(coeff, [], d1_copies))

  # Compute terms from the sum involving only one particle density matrices
  ti = range(4)
  for bi in makePermutations(4):

    # Determine the number of permutations
    n_perm = get_num_perms(ti,bi)

    # Determine the sign
    sign = (-1)**n_perm

    # Compute 1RDM 1RDM 1RDM 1RDM term
    bj = [i+4 for i in bi]
    coeff = sign * (0.5)**n_perm
    d1_copies = []
    for j in range(4):
      d1_copies.append(d1.copy())
      d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
    decomp.append(term(coeff, [], d1_copies))

  # Combine like terms
  for t in decomp:
    t.tensors.sort()
  decomp.sort()
  i = 0
  while i < len(decomp)-1:
    if decomp[i].tensors == decomp[i+1].tensors:
      decomp[i+1].numConstant += decomp[i].numConstant
      del(decomp[i])
    else:
      i += 1
  termChop(decomp)

  # Return the decomposition
  return decomp
def decomp_3rdm_to_2rdm_sf(d3, d1, d2, indexOrder = '1212'):
  """
  Returns the list of terms composing the decomposition of the 3 body spin free rdm d3
  in terms of one and two body rdms"
  """

  if ( not isinstance(d1, tensor) ) or (len(d1.indices) != 2):
    raise TypeError, "d1 must be a tensor with 2 indices"
  if ( not isinstance(d2, tensor) ) or (len(d2.indices) != 4):
    raise TypeError, "d2 must be a tensor with 4 indices"
  if ( not isinstance(d3, tensor) ) or (len(d3.indices) != 6):
    raise TypeError, "d3 must be a tensor with 6 indices"

  # sort the 3-body RDM's indices into 1212 ordering
  d3_indices = []
  if   indexOrder == '1212':
    d3_indices.extend(d3.indices)
  elif indexOrder == '1122':
    for i in range(3):
      d3_indices.append(d3.indices[2*i])
    for i in range(3):
      d3_indices.append(d3.indices[2*i+1])
  else:
    raise ValueError, "unexpected indexOrder parameter:  %s" %(indexOrder)

  decomp = []

  # Prepare terms from    sum (-1)^p %gamma^{i_1}_{i_4} %lambda^{i_2`i_3}_{i_5`i_6}
  for p0 in range(3):
    temp = range(3)
    del temp[p0]
    p1 = temp[0]
    p2 = temp[1]
    ti = [p0, p1, p2]
    for q0 in range(3):
      temp = range(3)
      del temp[q0]
      q1 = temp[0]
      q2 = temp[1]
      if q1 == p2 or q2 == p1:
        bi = [q0, q2, q1]
      else:
        bi = [q0, q1, q2]

      # determine the sign of the term based on the number of index permutations
      n_perm = get_num_perms(ti,bi)
      if n_perm > 1:
        raise ValueError, "n_perm = %i which is > 1.  This was unexpected." %n_perm
      sign = (-1)**n_perm

      # create the 1RDM 2RDM term
      bj = [i for i in bi]
      n_perm = get_num_perms(ti,bj)
      coeff = sign * (0.5)**n_perm
      bj = [i+3 for i in bj]
      d1_copy_0 = d1.copy()
      d1_copy_0.indices = [d3_indices[i] for i in (ti[0:1] + bj[0:1])]
      d2_copy_0 = d2.copy()
      d2_copy_0.indices = [d3_indices[i] for i in (ti[1:3] + bj[1:3])]
      decomp.append(term(coeff, [], [d1_copy_0, d2_copy_0]))

      # create the 1RDM 1RDM 1RDM terms
      bj = [i for i in bi]
      n_perm = get_num_perms(ti,bj)
      coeff = sign * (-1) * (0.5)**n_perm
      bj = [i+3 for i in bj]
      d1_copies = []
      for j in range(3):
        d1_copies.append(d1.copy())
        d1_copies[-1].indices = [d3_indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
      decomp.append(term(coeff, [], d1_copies))
      bj = bi[0:1] + bi[2:3] + bi[1:2]
      n_perm = get_num_perms(ti,bj)
      coeff = sign * (0.5)**n_perm
      bj = [i+3 for i in bj]
      d1_copies = []
      for j in range(3):
        d1_copies.append(d1.copy())
        d1_copies[-1].indices = [d3_indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
      decomp.append(term(coeff, [], d1_copies))

  # Compute terms from the sum involving only one particle density matrices
  ti = range(3)
  for bi in makePermutations(3):
    
    # Determine the number of permutations
    n_perm = get_num_perms(ti,bi)

    # Determine the sign
    sign = (-1)**n_perm

    # Compute 1RDM 1RDM 1RDM 1RDM term
    bj = [i+3 for i in bi]
    coeff = sign * (0.5)**n_perm
    d1_copies = []
    for j in range(3):
      d1_copies.append(d1.copy())
      d1_copies[-1].indices = [d3_indices[i] for i in (ti[j:j+1] + bj[j:j+1])]
    decomp.append(term(coeff, [], d1_copies))

  # Combine like terms
  for t in decomp:
    t.tensors.sort()
  decomp.sort()
  i = 0
  while i < len(decomp)-1:
    if decomp[i].tensors == decomp[i+1].tensors:
      decomp[i+1].numConstant += decomp[i].numConstant
      del(decomp[i])
    else:
      i += 1
  termChop(decomp)

  # if original ordering was 1122, convert back to it
  if indexOrder == '1122':
    for t in decomp:
      for ten in t.tensors:
        if ten.name == d2.name:
          ten.indices = [ten.indices[0], ten.indices[2], ten.indices[1], ten.indices[3]]

  # Return the decomposition
  return decomp
def assign_rdm_types(inTerms, rdm_name, rdms):
  """
  Assigns the rdm type (aa, bb, aaaa, bbbb, abab, etc.) to each tensor in the list
  of terms inTerms with name rdm_name.  rdms is a list of tensors representing the
  rdm types available for assignment.
  """

  # Prepare lists for the number of top and bottom alpha and beta indices in the supplied density matrices
  rdm_topACounts = [0]*len(rdms)
  rdm_topBCounts = [0]*len(rdms)
  rdm_bottomACounts = [0]*len(rdms)
  rdm_bottomBCounts = [0]*len(rdms)

  # Process the input rdms
  for i in range(len(rdms)):

    # If the number of indices is not even, raise an error
    if len(rdms[i].indices) % 2 != 0:
      raise ValueError, "Input rdms must have an even number of indices. rdm '%s' does not." %(str(rdms[i]))

    # Compute the rdm's order
    order = len(rdms[i].indices) / 2

    # Determine the numbers of alpha and beta indices in the top rdm indices
    (rdm_topACounts[i], rdm_topBCounts[i]) = getABCounts(rdms[i].indices[:order])

    # Determine the numbers of alpha and beta indices in the bottom rdm indices
    (rdm_bottomACounts[i], rdm_bottomBCounts[i]) = getABCounts(rdms[i].indices[order:])

#  for i in range(len(rdms)):
#    print "rdm: %20s   counts: %2i %2i %2i %2i" %(str(rdms[i]), rdm_topACounts[i], rdm_topBCounts[i], rdm_bottomACounts[i], rdm_bottomBCounts[i])

  # For each input term, loop through the tensors to assign rdm types
  for t in inTerms:
    for i in range(len(t.tensors)):

      # Assign a short name to the current tensor
      ten = t.tensors[i]

#      # If the tensor is a creOp or desOp, skip it
#      if isinstance(ten, creOp) or isinstance(ten, desOp):
#        continue

      # If the tensor does not have the specified name, skip it
      if ten.name != rdm_name:
        continue

      # If the number of indices is not even, raise an error
      if len(ten.indices) % 2 != 0:
        raise ValueError, "Density matrices must have an even number of indices. Tensor '%s' does not." %(str(ten))

      # Compute the tensor's order
      order = len(ten.indices) / 2

      # Determine the numbers of alpha and beta indices in the top tensor indices
      (topACount, topBCount) = getABCounts(ten.indices[:order])

      # Determine the numbers of alpha and beta indices in the bottom tensor indices
      (bottomACount, bottomBCount) = getABCounts(ten.indices[order:])

      # If the number of alpha and beta indices in the top and bottom do not match, the density matrix is zero
      if topACount != bottomACount or topBCount != bottomBCount:
        t.numConstant = 0.0
        break

      # Find the rdm with the matching number of top and bottom alpha and beta indices
      for j in range(len(rdms)):
        if rdm_topACounts[j] == topACount and \
           rdm_topBCounts[j] == topBCount and \
           rdm_bottomACounts[j] == bottomACount and \
           rdm_bottomBCounts[j] == bottomBCount:
          matching_rdm = rdms[j]
          break

      # If no rdm matches the top and bottom alpha and beta pattern, leave the tensor as is
      else:
        continue
#        raise RuntimeError, "No rdm with the correct number of alpha and beta top and bottom indices"

      # Create an index list for the assigned rdm
      indexList = ten.indices #[ind for ind in ten.indices]

      # Sort the top indices to match the rdm's alpha/beta order
      for j in range(order-1):
        if alpha_type in matching_rdm.indices[j].indType:
          target_type = alpha_type
        else:
          target_type = beta_type
        if target_type not in indexList[j].indType:
          k = order-1
          while k > j:
            if target_type in indexList[k].indType:
              temp = indexList[k]
              indexList[k] = indexList[j]
              indexList[j] = temp
              t.numConstant *= -1
              break
            k -= 1
          if k == j:
#            print "rdm = '%s'" %(str(matching_rdm))
#            print "ten = '%s'" %(str(ten))
#            print "tensor indices:"
#            for ind in indexList:
#              print ind.name, ", ", ind.indType
#            print "counts: %2i %2i %2i %2i" %(topACount, topBCount, bottomACount, bottomBCount)
            raise RuntimeError, "Could not sort the top indices of '%s' to match the rdm's alpha/beta ordering." %(str(ten))

      # Sort the bottom indices to match the rdm's alpha/beta order
      for j in range(order, len(indexList)-1):
        if alpha_type in matching_rdm.indices[j].indType:
          target_type = alpha_type
        else:
          target_type = beta_type
        if target_type not in indexList[j].indType:
          k = len(indexList)-1
          while k > j:
            if target_type in indexList[k].indType:
              temp = indexList[k]
              indexList[k] = indexList[j]
              indexList[j] = temp
              t.numConstant *= -1
              break
            k -= 1
          if k == j:
            raise RuntimeError, "Could not sort the bottom indices of '%s' to match the rdm's alpha/beta ordering." %(str(ten))

      # Replace the tensor with the assigned rdm
      t.tensors[i] = tensor(matching_rdm.name, indexList, matching_rdm.symmetries)

      # Mark the term as not being in canonical form
      t.isInCanonicalForm = False

  # Remove any zero terms
  termChop(inTerms)