def decomp_3rdms_to_2rdms_sf(inTerms, d3name, d1, d2, indexOrder = '1212'):
  """
  Decomposes any 3-particle spin free RDMs in inTerms into products of 1 and 2
  particle reduced density mattrices.
  """

  # Prepare error message
  TypeErrorMessage = "inTerms must be a list of term objects"

  # Check input
  if type(inTerms) != type([]):
    raise TypeError, TypeErrorMessage
  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"

  # Initialize index
  i = 0
  opCount = 0

  # One by one, decompose 3RDMs and append the result
  # to the list of terms
  while i < len(inTerms):

    # Assign a short name for the ith term
    t = inTerms[i]

    # Check input
    if not isinstance(t, term):
      raise TypeError, TypeErrorMessage

    # Initialize the rdm variable
    rdm = False

    # Search for 3-particle RDMs in the term
    for j in range(len(t.tensors)-1, -1, -1):
      if t.tensors[j].name == d3name:
        rdm = t.tensors[j]
        pre_term = term(t.numConstant, t.constants, t.tensors[0:j])
        post_term = term(1.0, [], t.tensors[j+1:])
        break

    # If the term has no 3RDMs, incrment the index
    if rdm is False:
      i += 1

    # If the term has a 3RDM, decompose it
    else:
      opCount += 1
      decomp = decomp_3rdm_to_2rdm_sf(rdm, d1, d2, indexOrder)
      for s in decomp:
        inTerms.append(pre_term.copy())
        inTerms[-1] = multiplyTerms(inTerms[-1], s)
        inTerms[-1] = multiplyTerms(inTerms[-1], post_term)
      del(inTerms[i])

  if options.verbose:
    print 'decomposed %i 3-body RDMs' %(opCount)
def decomp_4ops_to_2ops_2rdms_sf(inTerms, d1, d2):
  """
  Decomposes any 4-particle sfExOps in inTerms into products of 1 and 2
  particle sfExOps and reduced density mattrices.
  """

  # Prepare error message
  TypeErrorMessage = "inTerms must be a list of term objects"

  # Check input
  if type(inTerms) != type([]):
    raise TypeError, TypeErrorMessage
  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"

  # Initialize index
  i = 0
  opCount = 0

  # One by one, decompose four particle operators and append the result
  # to the list of terms
  while i < len(inTerms):

    # Assign a short name for the ith term
    t = inTerms[i]

    # Check input
    if not isinstance(t, term):
      raise TypeError, TypeErrorMessage

    # Initialize the operator variable
    op = False

    # Search for a 4-particle operator in the term
    for j in range(len(t.tensors)-1, -1, -1):
      if isinstance(t.tensors[j], sfExOp) and t.tensors[j].order == 3:
        op = t.tensors[j]
        pre_term = term(t.numConstant, t.constants, t.tensors[0:j])
        post_term = term(1.0, [], t.tensors[j+1:])
        break

    # If the term has no four particle operators, incrment the index
    if op is False:
      i += 1

    # If the term has a four particle operator, decompose it
    else:
      opCount += 1
      decomp = decomp_4op_to_2op_2rdm_sf(op, d1, d2)
      for s in decomp:
        inTerms.append(pre_term.copy())
        inTerms[-1] = multiplyTerms(inTerms[-1], s)
        inTerms[-1] = multiplyTerms(inTerms[-1], post_term)
      del(inTerms[i])

  if options.verbose:
    print 'decomposed %i 4-body operators' %(opCount)
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