/src/openfermion/ops/representations/interaction_rdm.py

https://github.com/quantumlib/OpenFermion
Python | 147 lines | 70 code | 16 blank | 61 comment | 13 complexity | 474eb68034119eda9ccd7889a399564e MD5 | raw file
  1. # Licensed under the Apache License, Version 2.0 (the "License");
  2. # you may not use this file except in compliance with the License.
  3. # You may obtain a copy of the License at
  4. #
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. #
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS,
  9. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. # See the License for the specific language governing permissions and
  11. # limitations under the License.
  12. """Class and functions to store reduced density matrices."""
  13. import copy
  14. import numpy
  15. from openfermion.ops.operators import FermionOperator, QubitOperator
  16. from openfermion.ops.representations import (InteractionOperator,
  17. PolynomialTensor)
  18. class InteractionRDMError(Exception):
  19. pass
  20. class InteractionRDM(PolynomialTensor):
  21. r"""Class for storing 1- and 2-body reduced density matrices.
  22. Attributes:
  23. one_body_tensor: The expectation values <a^\dagger_p a_q>.
  24. two_body_tensor: The expectation values
  25. <a^\dagger_p a^\dagger_q a_r a_s>.
  26. """
  27. def __init__(self, one_body_tensor, two_body_tensor):
  28. r"""Initialize the InteractionRDM class.
  29. Args:
  30. one_body_tensor: Expectation values <a^\dagger_p a_q>.
  31. two_body_tensor: Expectation values
  32. <a^\dagger_p a^\dagger_q a_r a_s>.
  33. """
  34. super(InteractionRDM, self).__init__({
  35. (1, 0): one_body_tensor,
  36. (1, 1, 0, 0): two_body_tensor
  37. })
  38. @property
  39. def one_body_tensor(self):
  40. """The value of the one-body tensor."""
  41. return self.n_body_tensors[1, 0]
  42. @one_body_tensor.setter
  43. def one_body_tensor(self, value):
  44. """Set the value of the one-body tensor."""
  45. self.n_body_tensors[1, 0] = value
  46. @property
  47. def two_body_tensor(self):
  48. """The value of the two-body tensor."""
  49. return self.n_body_tensors[1, 1, 0, 0]
  50. @two_body_tensor.setter
  51. def two_body_tensor(self, value):
  52. """Set the value of the two-body tensor."""
  53. self.n_body_tensors[1, 1, 0, 0] = value
  54. def expectation(self, operator):
  55. """Return expectation value of an InteractionRDM with an operator.
  56. Args:
  57. operator: A QubitOperator or InteractionOperator.
  58. Returns:
  59. float: Expectation value
  60. Raises:
  61. InteractionRDMError: Invalid operator provided.
  62. """
  63. if isinstance(operator, QubitOperator):
  64. expectation_op = self.get_qubit_expectations(operator)
  65. expectation = 0.0
  66. for qubit_term in operator.terms:
  67. expectation += (operator.terms[qubit_term] *
  68. expectation_op.terms[qubit_term])
  69. elif isinstance(operator, InteractionOperator):
  70. expectation = operator.constant
  71. expectation += numpy.sum(self.one_body_tensor *
  72. operator.one_body_tensor)
  73. expectation += numpy.sum(self.two_body_tensor *
  74. operator.two_body_tensor)
  75. else:
  76. raise InteractionRDMError('Invalid operator type provided.')
  77. return expectation
  78. def get_qubit_expectations(self, qubit_operator):
  79. """Return expectations of QubitOperator in new QubitOperator.
  80. Args:
  81. qubit_operator: QubitOperator instance to be evaluated on
  82. this InteractionRDM.
  83. Returns:
  84. QubitOperator: QubitOperator with coefficients
  85. corresponding to expectation values of those operators.
  86. Raises:
  87. InteractionRDMError: Observable not contained in 1-RDM or 2-RDM.
  88. """
  89. # Importing here instead of head of file to prevent circulars
  90. from openfermion.transforms.opconversions import (reverse_jordan_wigner,
  91. normal_ordered)
  92. qubit_operator_expectations = copy.deepcopy(qubit_operator)
  93. for qubit_term in qubit_operator_expectations.terms:
  94. expectation = 0.
  95. # Map qubits back to fermions.
  96. reversed_fermion_operators = reverse_jordan_wigner(
  97. QubitOperator(qubit_term))
  98. reversed_fermion_operators = normal_ordered(
  99. reversed_fermion_operators)
  100. # Loop through fermion terms.
  101. for fermion_term in reversed_fermion_operators.terms:
  102. coefficient = reversed_fermion_operators.terms[fermion_term]
  103. # Handle molecular term.
  104. if FermionOperator(
  105. fermion_term).is_two_body_number_conserving():
  106. if not fermion_term:
  107. expectation += coefficient
  108. else:
  109. indices = [operator[0] for operator in fermion_term]
  110. if len(indices) == 2:
  111. # One-body term
  112. indices = tuple(zip(indices, (1, 0)))
  113. else:
  114. # Two-body term
  115. indices = tuple(zip(indices, (1, 1, 0, 0)))
  116. rdm_element = self[indices]
  117. expectation += rdm_element * coefficient
  118. # Handle non-molecular terms.
  119. elif len(fermion_term) > 4:
  120. raise InteractionRDMError('Observable not contained '
  121. 'in 1-RDM or 2-RDM.')
  122. qubit_operator_expectations.terms[qubit_term] = expectation
  123. return qubit_operator_expectations