GQCP
Loading...
Searching...
No Matches
Simple2DM.hpp
Go to the documentation of this file.
1// This file is part of GQCG-GQCP.
2//
3// Copyright (C) 2017-2020 the GQCG developers
4//
5// GQCG-GQCP is free software: you can redistribute it and/or modify
6// it under the terms of the GNU Lesser General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// GQCG-GQCP is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public License
16// along with GQCG-GQCP. If not, see <http://www.gnu.org/licenses/>.
17
18#pragma once
19
20
26
27
28namespace GQCP {
29
30
31/*
32 * MARK: `Simple2DM` implementation
33 */
34
43template <typename _Scalar, typename _DerivedDM>
45 public BasisTransformable<_DerivedDM>,
46 public JacobiRotatable<_DerivedDM>,
47 public VectorSpaceArithmetic<_DerivedDM, _Scalar> {
48public:
49 // The scalar type used for a density matrix element: real or complex.
50 using Scalar = _Scalar;
51
52 // The type of the density matrix that derives from this class, enabling CRTP and compile-time polymorphism.
53 using DerivedDM = _DerivedDM;
54
55 // The type of 'this'.
57
58 // The type of the one-electron density matrix that is naturally related to the derived 2-DM.
60
61 // The type of transformation that is naturally associated to the derived 2-DM.
63
64
65private:
66 // The matrix representation of this two-electron density matrix.
68
69
70public:
71 /*
72 * MARK: Constructors
73 */
74
81 d {d} {}
82
83
89
90
91 /*
92 * MARK: Access
93 */
94
98 const SquareRankFourTensor<Scalar>& tensor() const { return this->d; }
99
104
105
106 /*
107 * MARK: General information
108 */
109
113 size_t numberOfOrbitals() const { return this->tensor().dimension(); }
114
115
116 /*
117 * MARK: Contractions
118 */
119
123 OneDM reduce() const {
124 // TODO: when Eigen3 releases tensor.trace(), use it to implement the reduction.
125
126 const auto K = this->numberOfOrbitals();
127
129 for (size_t p = 0; p < K; p++) {
130 for (size_t q = 0; q < K; q++) {
131
132 for (size_t r = 0; r < K; r++) {
133 D(p, q) += this->tensor()(p, q, r, r);
134 }
135 }
136 }
137
138 return OneDM(D);
139 }
140
141
145 Scalar trace() const {
146 // TODO: when Eigen3 releases tensor.trace(), use it to implement the trace
147
148 const auto K = this->numberOfOrbitals();
149
150 Scalar trace {};
151 for (size_t p = 0; p < K; p++) {
152 for (size_t q = 0; q < K; q++) {
153 trace += this->tensor()(p, p, q, q);
154 }
155 }
156
157 return trace;
158 }
159
160
161 /*
162 * MARK: Conforming to `VectorSpaceArithmetic`
163 */
164
168 DerivedDM& operator+=(const DerivedDM& rhs) override {
169 this->tensor().Eigen() += rhs.tensor().Eigen();
170 return static_cast<DerivedDM&>(*this);
171 }
172
173
177 DerivedDM& operator*=(const Scalar& a) override {
178 this->tensor() = this->tensor().Eigen() * a; // A compiler error occurs when writing "this->tensor().Eigen() *= a".
179 return static_cast<DerivedDM&>(*this);
180 }
181
182
183 /*
184 * MARK: Conforming to `BasisTransformable`
185 */
186
194 DerivedDM transformed(const Transformation& T) const override {
195
196 // The transformation formulas for two-electron operators and 2-DMs are similar, but not quite the same. Instead of using T, the transformation formula for the 2-DM uses T_inverse_transpose. See also (https://gqcg-res.github.io/knowdes/spinor-transformations.html).
197
198 // Since we're only getting T as a matrix, we should convert it to an appropriate tensor to perform contractions.
199 // Although not a necessity for the einsum implementation, it makes it a lot easier to follow the formulas.
200 const GQCP::SquareMatrix<Scalar> T_related = T.matrix().transpose().inverse();
201 const GQCP::Tensor<Scalar, 2> T_related_tensor = Eigen::TensorMap<Eigen::Tensor<const Scalar, 2>>(T_related.data(), T_related.rows(), T_related.cols());
202
203 // We calculate the conjugate as a tensor as well.
204 const GQCP::Tensor<Scalar, 2> T_related_conjugate = T_related_tensor.conjugate();
205
206 // We will have to do four single contractions
207 // d(T U V W) T^*(V R) -> a(T U R W)
208 // a(T U R W) T(W S) -> b(T U R S)
209 const auto temp_1 = this->tensor().template einsum<1>("TUVW,VR->TURW", T_related_conjugate).template einsum<1>("TURW,WS->TURS", T_related_tensor);
210
211 // T(U Q) b(T U R S) -> c(T Q R S)
212 const auto temp_2 = T_related_tensor.template einsum<1>("UQ,TURS->TQRS", temp_1);
213
214 // T^*(T P) c(T Q R S) -> d'(P Q R S)
215 const auto transformed = T_related_conjugate.template einsum<1>("TP,TQRS->PQRS", temp_2);
216
217 return DerivedDM {transformed};
218 }
219
220
221 // Allow the `rotate` method from `BasisTransformable`, since there's also a `rotate` from `JacobiRotatable`.
223
224 // Allow the `rotated` method from `BasisTransformable`, since there's also a `rotated` from `JacobiRotatable`.
226
227
228 /*
229 * MARK: Conforming to `JacobiRotatable`
230 */
231
239 DerivedDM rotated(const JacobiRotation& jacobi_rotation) const override {
240
241 // While waiting for an analogous Eigen::Tensor Jacobi module, we implement this rotation by constructing a Jacobi rotation matrix and then simply doing a rotation with it.
242 const auto J = Transformation::FromJacobi(jacobi_rotation, this->numberOfOrbitals());
243 return this->rotated(J);
244 }
245
246 // Allow the `rotate` method from `JacobiRotatable`, since there's also a `rotate` from `BasisTransformable`.
248};
249
250
251} // namespace GQCP
Definition: BasisTransformable.hpp:50
void rotate(const Transformation &U)
Definition: BasisTransformable.hpp:107
Definition: JacobiRotatable.hpp:50
Definition: JacobiRotation.hpp:33
Definition: Simple2DM.hpp:47
SquareRankFourTensor< Scalar > & tensor()
Definition: Simple2DM.hpp:103
Simple2DM(const SquareRankFourTensor< Scalar > &d)
Definition: Simple2DM.hpp:80
DerivedDM & operator*=(const Scalar &a) override
Definition: Simple2DM.hpp:177
Simple2DM()
Definition: Simple2DM.hpp:87
_Scalar Scalar
Definition: Simple2DM.hpp:50
size_t numberOfOrbitals() const
Definition: Simple2DM.hpp:113
_DerivedDM DerivedDM
Definition: Simple2DM.hpp:53
Scalar trace() const
Definition: Simple2DM.hpp:145
OneDM reduce() const
Definition: Simple2DM.hpp:123
DerivedDM & operator+=(const DerivedDM &rhs) override
Definition: Simple2DM.hpp:168
typename DensityMatrixTraits< DerivedDM >::Transformation Transformation
Definition: Simple2DM.hpp:62
DerivedDM transformed(const Transformation &T) const override
Definition: Simple2DM.hpp:194
const SquareRankFourTensor< Scalar > & tensor() const
Definition: Simple2DM.hpp:98
typename DensityMatrixTraits< DerivedDM >::OneDM OneDM
Definition: Simple2DM.hpp:59
DerivedDM rotated(const JacobiRotation &jacobi_rotation) const override
Definition: Simple2DM.hpp:239
Definition: SquareMatrix.hpp:39
static Self Zero(const size_t dim)
Definition: SquareMatrix.hpp:289
Definition: SquareRankFourTensor.hpp:36
size_t dimension() const
Definition: SquareRankFourTensor.hpp:209
Definition: Tensor.hpp:46
const Base & Eigen() const
Definition: Tensor.hpp:116
Definition: VectorSpaceArithmetic.hpp:35
Definition: BaseOneElectronIntegralBuffer.hpp:25
Definition: DensityMatrixTraits.hpp:28