GQCP
Loading...
Searching...
No Matches
SimpleSQTwoElectronOperator.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#include <string>
28
29
30namespace GQCP {
31
32
42template <typename _Scalar, typename _Vectorizer, typename _DerivedOperator>
44 public SQOperatorStorage<SquareRankFourTensor<_Scalar>, _Vectorizer, SimpleSQTwoElectronOperator<_Scalar, _Vectorizer, _DerivedOperator>>,
45 public BasisTransformable<_DerivedOperator>,
46 public JacobiRotatable<_DerivedOperator> {
47public:
48 // The scalar type used for a single parameter: real or complex.
49 using Scalar = _Scalar;
50
51 // The type of the vectorizer that relates a one-dimensional storage of matrices to the tensor structure of one-electron operators. This allows for a distinction between scalar operators (such as the kinetic energy operator), vector operators (such as the spin operator) and matrix/tensor operators (such as quadrupole and multipole operators).
52 using Vectorizer = _Vectorizer;
53
54 // The type of the operator that derives from this class, enabling CRTP and compile-time polymorphism.
55 using DerivedOperator = _DerivedOperator;
56
57 // The type of 'this'.
59
60 // The matrix representation of the parameters of (one of the components of) the two-electron operator.
62
63 // The type of one-electron operator that is naturally related to the derived two-electron operator.
65
66 // The type of the one-particle density matrix that is naturally associated to the derived two-electron operator.
68
69 // The type of the one-particle density matrix that is naturally associated to the derived two-electron operator.
71
72 // The type of transformation that is naturally associated to the derived two-electron operator.
74
75
76private:
77 // If this two-electron operator contains matrix elements that are modified to obey antisymmetry w.r.t. creation and annihilation indices.
78 bool is_antisymmetrized = false;
79
80 // If this two-electron operator contains matrix elements that are expressed as g_PQRS or (PQ|RS).
81 bool is_expressed_using_chemists_notation = true;
82
83
84public:
85 /*
86 * MARK: Constructors
87 */
88
89 // Inherit `SQOperatorStorage`'s constructors.
91
92
93 /*
94 * MARK: Calculations
95 */
96
105
106 if (this->numberOfOrbitals() != d.numberOfOrbitals()) {
107 throw std::invalid_argument("SimpleSQTwoElectronOperator::calculateExpectationValue(const Derived2DM&): The given 2-DM's dimension is not compatible with the two-electron operator.");
108 }
109
110
111 // Calculate the expectation value for every component of the operator.
112 const auto& parameters = this->allParameters();
113 std::vector<Scalar> expectation_values(this->numberOfComponents()); // Zero-initialize the vector with a number of elements.
114
115 for (size_t i = 0; i < this->numberOfComponents(); i++) {
116
117 // Perform the actual contraction (with prefactor 0.5):
118 // 0.5 g(p q r s) d(p q r s)
119 Eigen::Tensor<Scalar, 0> contraction = 0.5 * parameters[i].template einsum<4>("pqrs, pqrs->", d.tensor());
120
121 // As the contraction is a scalar (a tensor of rank 0), we should access using `operator(0)`.
122 expectation_values[i] = contraction(0);
123 }
124
125 return StorageArray<Scalar, Vectorizer> {expectation_values, this->array.vectorizer()}; // convert std::array to Vector
126 }
127
128
139 template <typename Z = Scalar>
141
142 if (DM.numberOfOrbitals() != this->numberOfOrbitals()) {
143 throw std::invalid_argument("SimpleSQTwoElectronOperator::calculateFockianMatrix(const Derived1DM&, const Derived2DM&): The 1-DM's dimensions are not compatible with this two-electron operator.");
144 }
145
146 if (dm.numberOfOrbitals() != this->numberOfOrbitals()) {
147 throw std::invalid_argument("SimpleSQTwoElectronOperator::calculateFockianMatrix(const Derived1DM&, const Derived2DM&): The 2-DM's dimensions are not compatible with this two-electron operator.");
148 }
149
150 const auto& parameters = this->allParameters(); // The parameters of this two-electron operator, as a vector.
151 std::vector<SquareMatrix<double>> F_vector {this->numberOfComponents()}; // The resulting vector of Fockian matrices.
152
153 // A KISS implementation of the calculation of the Fockian matrix.
154 const auto& d = dm.tensor();
155 for (size_t i = 0; i < this->numberOfComponents(); i++) {
156
157 const auto& g_i = parameters[i]; // The matrix representation of the parameters of the i-th component.
158
159 // Calculate the Fockian matrix for every component and add it to the vector.
161 for (size_t p = 0; p < this->numberOfOrbitals(); p++) {
162 for (size_t q = 0; q < this->numberOfOrbitals(); q++) {
163
164 for (size_t r = 0; r < this->numberOfOrbitals(); r++) {
165 for (size_t s = 0; s < this->numberOfOrbitals(); s++) {
166 for (size_t t = 0; t < this->numberOfOrbitals(); t++) {
167 F_i(p, q) += 0.5 * g_i(q, r, s, t) * (d(p, r, s, t) + d(r, p, s, t)); // Include a factor 1/2 to accommodate for response density matrices.
168 }
169 }
170 }
171 }
172 } // F_i elements loop
173 F_vector[i] = F_i;
174 }
175
177 }
178
179
190 template <typename Z = Scalar>
192
193 if (DM.numberOfOrbitals() != this->numberOfOrbitals()) {
194 throw std::invalid_argument("SimpleSQTwoElectronOperator::calculateFockianMatrix(const Derived1DM&, const Derived2DM&): The given 1-DM's dimensions are not compatible with this two-electron operator.");
195 }
196
197 if (dm.numberOfOrbitals() != this->numberOfOrbitals()) {
198 throw std::invalid_argument("SimpleSQTwoElectronOperator::calculateFockianMatrix(const Derived1DM&, const Derived2DM&): The given 2-DM's dimensions are not compatible with this two-electron operator.");
199 }
200
201
202 const auto& parameters = this->allParameters(); // The parameters of this two-electron operator, as a vector.
203 std::vector<SquareRankFourTensor<double>> G_vector {this->numberOfComponents()}; // The resulting vector of super-Fockian matrices.
204 const auto F_vector = this->calculateFockianMatrix(DM, dm).elements(); // The Fockian matrices are necessary in the calculation of the super-Fockian matrices.
205
206
207 // A KISS implementation of the calculation of the super-Fockian matrix.
208 const auto& d = dm.tensor();
209 for (size_t i = 0; i < this->numberOfComponents(); i++) {
210
211 const auto& g_i = parameters[i]; // The matrix representation of the parameters of the i-th component.
212 const auto& F_i = F_vector[i]; // The Fockian matrix of the i-th component.
213
214 // Calculate the super-Fockian matrix for every component and add it to the array. Add factors 1/2 to accommodate for response density matrices.
216 G_i.setZero();
217 for (size_t p = 0; p < this->numberOfOrbitals(); p++) {
218 for (size_t q = 0; q < this->numberOfOrbitals(); q++) {
219 for (size_t r = 0; r < this->numberOfOrbitals(); r++) {
220 for (size_t s = 0; s < this->numberOfOrbitals(); s++) {
221
222 if (q == r) {
223 G_i(p, q, r, s) += F_i(p, s);
224 }
225
226 for (size_t t = 0; t < this->numberOfOrbitals(); t++) {
227 for (size_t u = 0; u < this->numberOfOrbitals(); u++) {
228 double value = g_i(s, t, q, u) * (d(r, t, p, u) + d(t, r, u, p));
229 value -= g_i(s, t, u, p) * (d(r, t, u, q) + d(t, r, q, u));
230 value -= g_i(s, p, t, u) * (d(r, q, t, u) + d(q, r, u, t));
231
232 G_i(p, q, r, s) += 0.5 * value;
233 }
234 }
235 }
236 }
237 }
238 } // G_i elements loop
239 G_vector[i] = G_i;
240 }
241
243 }
244
245
250
251 // Prepare some variables.
252 const auto& g = this->allParameters(); // The parameters of this two-electron operator, as a vector.
253
254 const auto K = this->numberOfOrbitals();
255 DerivedSQOneElectronOperator k_op = DerivedSQOneElectronOperator::Zero(K); // 'op' for operator.
256 auto& k = k_op.allParameters();
257
258 // A KISS-implementation of the elements of the one-electron partition.
259 for (size_t i = 0; i < this->numberOfComponents(); i++) {
260
261 for (size_t p = 0; p < K; p++) {
262 for (size_t q = 0; q < K; q++) {
263 for (size_t r = 0; r < K; r++) {
264 k[i](p, q) -= 0.5 * g[i](p, r, r, q);
265 }
266 }
267 }
268 }
269
270 return k_op;
271 }
272
273
274 /*
275 * MARK: Conforming to `BasisTransformable`
276 */
277
285 DerivedOperator transformed(const Transformation& T) const override {
286
287 // Since we're only getting T as a matrix, we should convert it to an appropriate tensor to perform contractions.
288 // Although not a necessity for the einsum implementation, it makes it a lot easier to follow the formulas.
289 const GQCP::Tensor<Scalar, 2> T_tensor = Eigen::TensorMap<Eigen::Tensor<const Scalar, 2>>(T.matrix().data(), T.matrix().rows(), T.matrix().cols());
290
291 // We calculate the conjugate as a tensor as well.
292 const GQCP::Tensor<Scalar, 2> T_conjugate = T_tensor.conjugate();
293
294 // Calculate the basis transformation for every component of the operator.
295 const auto& parameters = this->allParameters();
296 auto result = this->allParameters();
297
298 for (size_t i = 0; i < this->numberOfComponents(); i++) {
299
300 // We will have to do four single contractions
301 // g(T U V W) T^*(V R) -> a(T U R W)
302 // a(T U R W) T(W S) -> b(T U R S)
303 const auto temp_1 = parameters[i].template einsum<1>("TUVW,VR->TURW", T_conjugate).template einsum<1>("TURW,WS->TURS", T_tensor);
304
305 // T(U Q) b(T U R S) -> c(T Q R S)
306 const auto temp_2 = T_tensor.template einsum<1>("UQ,TURS->TQRS", temp_1);
307
308 // T^*(T P) c(T Q R S) -> g'(P Q R S)
309 const auto transformed_component = T_conjugate.template einsum<1>("TP,TQRS->PQRS", temp_2);
310
311 result[i] = transformed_component;
312 }
313
315 }
316
317
318 // Allow the `rotate` method from `BasisTransformable`, since there's also a `rotate` from `JacobiRotatable`.
320
321 // Allow the `rotated` method from `BasisTransformable`, since there's also a `rotated` from `JacobiRotatable`.
323
324
325 /*
326 * MARK: Conforming to `JacobiRotatable`
327 */
328
336 DerivedOperator rotated(const JacobiRotation& jacobi_rotation) const override {
337
338 // 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.
339 const auto J = Transformation::FromJacobi(jacobi_rotation, this->numberOfOrbitals());
340 return this->rotated(J);
341 }
342
343 // Allow the `rotate` method from `JacobiRotatable`, since there's also a `rotate` from `BasisTransformable`.
345
346
347 /*
348 * MARK: Antisymmetrizing
349 */
350
358 bool isAntisymmetrized() const { return this->is_antisymmetrized; }
359
360
369
370 // Attempt to modify a copy of these integrals if they haven't been antisymmetrized already.
371 const auto& parameters = this->allParameters();
372 auto copy = *this;
373 auto& copy_parameters = copy.allParameters();
374
375 if (!(this->isAntisymmetrized())) {
376
377 // Determine the shuffle-indices for the matrix elements that will be subtracted.
378 Eigen::array<int, 4> shuffle_indices;
380 shuffle_indices = Eigen::array<int, 4> {0, 3, 2, 1};
381 } else { // Expressed using physicist's notation.
382 shuffle_indices = Eigen::array<int, 4> {0, 1, 3, 2};
383 }
384
385 auto& copy_parameters = copy.allParameters();
386 for (size_t i = 0; i < this->numberOfComponents(); i++) {
387 copy_parameters[i] -= parameters[i].shuffle(shuffle_indices);
388 }
389
390 copy.is_antisymmetrized = true;
391 }
392
393 return copy;
394 }
395
396
404 void antisymmetrize() { *this = this->antisymmetrized(); }
405
406
407 /*
408 * MARK: Chemist's or physicist's notation
409 */
410
414 bool isExpressedUsingChemistsNotation() const { return this->is_expressed_using_chemists_notation; }
415
420
425
426 // Attempt to modify a copy if these integrals are expressed in physicist's notation.
427 const auto& parameters = this->allParameters();
428 auto copy = *this;
429 auto& copy_parameters = copy.allParameters();
430
432
433 Eigen::array<int, 4> shuffle_indices {0, 2, 1, 3};
434
435 for (size_t i = 0; i < this->numberOfComponents(); i++) {
436 copy_parameters[i] = SquareRankFourTensor<Scalar>(parameters[i].shuffle(shuffle_indices));
437 }
438
439 copy.is_expressed_using_chemists_notation = true;
440 }
441 return copy;
442 }
443
444
449
450 // Attempt to modify a copy if these integrals are expressed in chemist's notation.
451 const auto& parameters = this->allParameters();
452 auto copy = *this;
453 auto& copy_parameters = copy.allParameters();
454
456
457 Eigen::array<int, 4> shuffle_indices {0, 2, 1, 3};
458
459 for (size_t i = 0; i < this->numberOfComponents(); i++) {
460 copy_parameters[i] = SquareRankFourTensor<Scalar>(parameters[i].shuffle(shuffle_indices));
461 }
462
463 copy.is_expressed_using_chemists_notation = false;
464 }
465 return copy;
466 }
467
468
473
478};
479
480
481/*
482 * MARK: Operator traits
483 */
484
488template <typename _Scalar, typename _Vectorizer, typename _DerivedOperator>
489struct OperatorTraits<SimpleSQTwoElectronOperator<_Scalar, _Vectorizer, _DerivedOperator>> {
490
491 // The scalar type used for a single parameter/matrix element/integral: real or complex.
492 using Scalar = _Scalar;
493
494 // The type of the vectorizer that relates a one-dimensional storage of tensors to the tensor structure of two-electron operators. This distinction is carried over from SimpleSQOneElectronOperator.
495 using Vectorizer = _Vectorizer;
496
497 // The type of the operator that derives from `SimpleSQTwoElectronOperator`, enabling CRTP and compile-time polymorphism.
498 using DerivedOperator = _DerivedOperator;
499};
500
501
502/*
503 * MARK: BasisTransformableTraits
504 */
505
509template <typename _Scalar, typename _Vectorizer, typename _DerivedOperator>
510struct BasisTransformableTraits<SimpleSQTwoElectronOperator<_Scalar, _Vectorizer, _DerivedOperator>> {
511
512 // The type of the transformation for which the basis transformation should be defined.
514};
515
516
517/*
518 * MARK: JacobiRotatableTraits
519 */
520
524template <typename _Scalar, typename _Vectorizer, typename _DerivedOperator>
525struct JacobiRotatableTraits<SimpleSQTwoElectronOperator<_Scalar, _Vectorizer, _DerivedOperator>> {
526
527 // The type of Jacobi rotation for which the Jacobi rotation should be defined.
529};
530
531
532} // namespace GQCP
Definition: BasisTransformable.hpp:50
void rotate(const Transformation &U)
Definition: BasisTransformable.hpp:107
Definition: JacobiRotatable.hpp:50
Definition: JacobiRotation.hpp:33
const MatrixRepresentation & parameters(const Indices &... indices) const
Definition: SQOperatorStorageBase.hpp:236
size_t numberOfOrbitals() const
Definition: SQOperatorStorageBase.hpp:277
const std::vector< MatrixRepresentation > & allParameters() const
Definition: SQOperatorStorageBase.hpp:221
StorageArray< MatrixRepresentation, Vectorizer > array
Definition: SQOperatorStorageBase.hpp:68
size_t numberOfComponents() const
Definition: SQOperatorStorageBase.hpp:272
Definition: SQOperatorStorage.hpp:44
Definition: SimpleSQTwoElectronOperator.hpp:46
typename OperatorTraits< DerivedOperator >::Transformation Transformation
Definition: SimpleSQTwoElectronOperator.hpp:73
typename OperatorTraits< DerivedOperator >::TwoDM Derived2DM
Definition: SimpleSQTwoElectronOperator.hpp:70
_Vectorizer Vectorizer
Definition: SimpleSQTwoElectronOperator.hpp:52
enable_if_t< std::is_same< Z, double >::value, StorageArray< SquareRankFourTensor< double >, Vectorizer > > calculateSuperFockianMatrix(const Derived1DM &DM, const Derived2DM &dm) const
Definition: SimpleSQTwoElectronOperator.hpp:191
_DerivedOperator DerivedOperator
Definition: SimpleSQTwoElectronOperator.hpp:55
typename OperatorTraits< DerivedOperator >::SQOneElectronOperator DerivedSQOneElectronOperator
Definition: SimpleSQTwoElectronOperator.hpp:64
DerivedOperator rotated(const JacobiRotation &jacobi_rotation) const override
Definition: SimpleSQTwoElectronOperator.hpp:336
void convertToChemistsNotation()
Definition: SimpleSQTwoElectronOperator.hpp:472
Self convertedToPhysicistsNotation() const
Definition: SimpleSQTwoElectronOperator.hpp:448
enable_if_t< std::is_same< Z, double >::value, StorageArray< SquareMatrix< double >, Vectorizer > > calculateFockianMatrix(const Derived1DM &DM, const Derived2DM &dm) const
Definition: SimpleSQTwoElectronOperator.hpp:140
_Scalar Scalar
Definition: SimpleSQTwoElectronOperator.hpp:49
void antisymmetrize()
Definition: SimpleSQTwoElectronOperator.hpp:404
Self antisymmetrized() const
Definition: SimpleSQTwoElectronOperator.hpp:368
bool isExpressedUsingPhysicistsNotation() const
Definition: SimpleSQTwoElectronOperator.hpp:419
StorageArray< Scalar, Vectorizer > calculateExpectationValue(const Derived2DM &d) const
Definition: SimpleSQTwoElectronOperator.hpp:104
void convertToPhysicistsNotation()
Definition: SimpleSQTwoElectronOperator.hpp:477
bool isExpressedUsingChemistsNotation() const
Definition: SimpleSQTwoElectronOperator.hpp:414
Self convertedToChemistsNotation() const
Definition: SimpleSQTwoElectronOperator.hpp:424
DerivedOperator transformed(const Transformation &T) const override
Definition: SimpleSQTwoElectronOperator.hpp:285
DerivedSQOneElectronOperator effectiveOneElectronPartition() const
Definition: SimpleSQTwoElectronOperator.hpp:249
typename OperatorTraits< DerivedOperator >::OneDM Derived1DM
Definition: SimpleSQTwoElectronOperator.hpp:67
bool isAntisymmetrized() const
Definition: SimpleSQTwoElectronOperator.hpp:358
Definition: SquareMatrix.hpp:39
static Self Zero(const size_t dim)
Definition: SquareMatrix.hpp:289
Definition: StorageArray.hpp:38
const Vectorizer & vectorizer() const
Definition: StorageArray.hpp:177
Definition: Tensor.hpp:46
Definition: BaseOneElectronIntegralBuffer.hpp:25
typename std::enable_if< B, T >::type enable_if_t
Definition: type_traits.hpp:37
typename OperatorTraits< _DerivedOperator >::Transformation Transformation
Definition: SimpleSQTwoElectronOperator.hpp:513
Definition: BasisTransformable.hpp:37
Definition: JacobiRotatable.hpp:37
_DerivedOperator DerivedOperator
Definition: SimpleSQTwoElectronOperator.hpp:498
Definition: OperatorTraits.hpp:28