LIBINT 2.7.2
deriv_map.h
1/*
2 * Copyright (C) 2004-2021 Edward F. Valeev
3 *
4 * This file is part of Libint.
5 *
6 * Libint is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Libint is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with Libint. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21
22#ifndef _libint2_include_derivmap_h_
23#define _libint2_include_derivmap_h_
24
25#include <libint2/util/cxxstd.h>
26#if LIBINT2_CPLUSPLUS_STD < 2011
27# error "The simple Libint API requires C++11 support"
28#endif
29
30#include <cstdlib>
31#include <vector>
32#include <algorithm>
33
34#include <libint2/braket.h>
35#include <libint2/tensor.h>
36
37namespace libint2 {
49 public:
50 // Statically generate all mapDerivIndex arrays up to LIBINT2_MAX_DERIV_ORDER
51 // for both BraKet::xx_xx and BraKet::xs_xx and store in a vector
52 static void initialize() {
53 int max_deriv_order = 0;
54#if defined(LIBINT2_MAX_DERIV_ORDER)
55 max_deriv_order = LIBINT2_MAX_DERIV_ORDER;
56#else // defined(LIBINT2_MAX_DERIV_ORDER)
57# ifdef INCLUDE_ERI
58 if (INCLUDE_ERI > max_deriv_order) max_deriv_order = INCLUDE_ERI;
59# endif // INCLUDE_ERI
60# ifdef INCLUDE_ERI3
61 if (INCLUDE_ERI3 > max_deriv_order) max_deriv_order = INCLUDE_ERI3;
62# endif // INCLUDE_ERI3
63#endif // defined(LIBINT2_MAX_DERIV_ORDER)
64 for (int d = 1; d <= max_deriv_order; ++d) {
65 braket_xx_xx().push_back(DerivMapGenerator::generate_deriv_index_map(d, BraKet::xx_xx));
66 braket_xs_xx().push_back(DerivMapGenerator::generate_deriv_index_map(d, BraKet::xs_xx));
67 }
68 }
69
70 // Helper functions for declaring/building/calling the maps in both initialize() and instance()
71 static std::vector<Tensor<size_t>>& braket_xx_xx() {
72 static std::vector<Tensor<size_t>> braket_xx_xx_maps;
73 return braket_xx_xx_maps;
74 }
75
76 static std::vector<Tensor<size_t>>& braket_xs_xx() {
77 static std::vector<Tensor<size_t>> braket_xs_xx_maps;
78 return braket_xs_xx_maps;
79 }
80
81 static Tensor<size_t>& instance(int deriv_order_, BraKet braket_) {
82 // access and return the one that is needed
83 switch(braket_) {
84 case BraKet::xx_xx: {
85 return braket_xx_xx()[deriv_order_ - 1];
86 }
87 case BraKet::xs_xx: {
88 return braket_xs_xx()[deriv_order_ - 1];
89 }
90 default:
91 assert(false && "Derivative index permutation maps for this braket type not yet supported.");
92 }
93 std::abort();
94 }
95
96 private:
97 // Functions required for generating the maps.
98 // Combinations with repetition.
99 // Combinations of size 'k' from 'n' elements stored in vector 'inp'.
100 // Requires instantiating vector 'out' and vector of vectors 'result', which stores every combination.
101 static void cwr_recursion(std::vector<size_t> inp,
102 std::vector<size_t> &out,
103 std::vector<std::vector<size_t>> &result,
104 size_t k, size_t i, size_t n)
105 {
106 // base case: if combination size is k, add to result
107 if (out.size() == k){
108 result.push_back(out);
109 return;
110 }
111 for (size_t j = i; j < n; j++){
112 out.push_back(inp[j]);
113 cwr_recursion(inp, out, result, k, j, n);
114 // backtrack - remove current element from solution
115 out.pop_back();
116 }
117 }
118
119 // How many shell set derivatives
120 // k is number of centers, n is deriv order.
121 // Assumes coordinate-independent operator, 3 coordinates per center
122 static size_t how_many_derivs(size_t k, size_t n) {
123 size_t val = 1;
124 size_t factorial = 1;
125 for (size_t i=0; i < n; i++) {
126 val *= (3 * k + i);
127 factorial *= i + 1;
128 }
129 val /= factorial;
130 return val;
131 }
132
133 // Create array which is of size (nderivs, deriv_order)
134 // which when indexed with a 1d buffer index (the flattened generalized upper triangle index)
135 // it maps it to the corresponding to multidimensional index tuple (i,j,k,...) where i<=j<=k<=...
136 // The multidimensional indices each correspond to a partial derivative operator,
137 // and the value of each index indicates which parameter is being differentiated.
138 // For example, if there are 6 differentiable parameters, and deriv_order = 2,
139 // calling generate_multi_index_lookup(6,2) yields 'combos' such that combos[13] yields the pair [2,4]
140 // which matches the value and index pair in the below array:
141 // 0 1 2 3 4 5
142 // 6 7 8 9 10
143 // 11 12 13 14
144 // 15 16 17
145 // 18 19
146 // 20
147 static std::vector<std::vector<size_t>> generate_multi_index_lookup(size_t nparams, size_t deriv_order) {
148 using namespace std;
149 // Generate vector of indices 0 through nparams-1
150 vector<size_t> inp;
151 for (size_t i = 0; i < nparams; i++) {
152 inp.push_back(i);
153 }
154 // Generate all possible combinations with repitition.
155 // These are upper triangle indices, and the length of them is the total number of derivatives
156 vector<size_t> out;
157 vector<vector<size_t>> combos;
158 cwr_recursion(inp, out, combos, deriv_order, 0, nparams);
159 return combos;
160 }
161
162 // Function which computes mapDerivIndex array
163 static Tensor<size_t> generate_deriv_index_map(size_t deriv_order, BraKet braket_)
164 {
165 using namespace std;
166 // Check BraKet type to determine permutations, number of centers
167 vector<size_t> swap_braket_perm;
168 vector<size_t> swap_bra_perm;
169 vector<size_t> swap_ket_perm;
170 vector<vector<size_t>> swap_combos;
171 size_t ncenters;
172 switch(braket_) {
173 case BraKet::xx_xx: {
174 swap_braket_perm = {6,7,8,9,10,11,0,1,2,3,4,5};
175 swap_bra_perm = {3,4,5,0,1,2,6,7,8,9,10,11};
176 swap_ket_perm = {0,1,2,3,4,5,9,10,11,6,7,8};
177 // All possible on/off combinations of swap_braket, swap_bra, and swap_ket
178 swap_combos = {{0, 0, 0},
179 {0, 0, 1},
180 {0, 1, 0},
181 {0, 1, 1},
182 {1, 0, 0},
183 {1, 0, 1},
184 {1, 1, 0},
185 {1, 1, 1}};
186 ncenters = 4;
187 }
188 break;
189 case BraKet::xs_xx: {
190 swap_braket_perm = {0,1,2,3,4,5,6,7,8};
191 swap_bra_perm = {0,1,2,3,4,5,6,7,8};
192 swap_ket_perm = {0,1,2,6,7,8,3,4,5};
193 swap_combos = {{0,0,0}, {0,0,1}};
194 ncenters = 3;
195 }
196 break;
197
198 default:
199 assert(false && "Derivative index maps for this BraKet type not implemented.");
200 }
201
202 // Number of possible derivatives
203 auto nderivs = how_many_derivs(ncenters, deriv_order); // e.g. for 4 center: 12,78,364,1365
204 // Number of differentiable parameters in a shell set (assumes 3 cartesian components each center)
205 auto nparams = ncenters * 3;
206
207 // Initialize mapDerivIndex. First three dimensions are either 0 or 1
208 // acting as a bool for whether to swap braket, swap bra, or swap ket.
209 // Last index is the integer map.
210 // Note that BraKet::xx_xx uses the whole thing, BraKet::xs_xx, only need [0][0][:][:] slice
211 // and BraKet::xx_sx, only need [0][:][0][:] slice
212 size_t swap_dim = 2;
213 Tensor<size_t> mapDerivIndex{swap_dim,swap_dim,swap_dim,nderivs};
214
215 // Get lookup which maps flattened upper triangle index to the multidimensional index
216 // in terms of full array axes. Each axis of this multidimensional array represents
217 // a different partial derivative of the shell set
218 vector<vector<size_t>> lookup = generate_multi_index_lookup(nparams, deriv_order);
219
220 // Now loop over every combination of swap_*
221 for (size_t swap = 0; swap < swap_combos.size(); swap++){
222 auto swap_braket = swap_combos[swap][0];
223 auto swap_bra = swap_combos[swap][1];
224 auto swap_ket = swap_combos[swap][2];
225 // For every single derivative index, lookup its multidimensional index
226 // and apply the permutation rules for this Braket type
227 for (size_t i = 0; i < nderivs; i++){
228 vector<size_t> multi_idx = lookup[i];
229 vector<size_t> permuted_indices;
230 for (size_t j=0; j < multi_idx.size(); j++){
231 size_t idx = multi_idx[j];
232 if (swap_braket == 1) idx = swap_braket_perm[idx];
233 if (swap_bra == 1) idx = swap_bra_perm[idx];
234 if (swap_ket == 1) idx = swap_ket_perm[idx];
235 permuted_indices.push_back(idx);
236 }
237 // Sort permuted indices into order of upper triangle indices, i <= j <= k...
238 sort(permuted_indices.begin(), permuted_indices.end());
239
240 // Since the vector of vectors 'lookup' generated by generate_multi_index_lookup is sorted such that
241 // each vector is elementwise <= the next vector, we can use binary search to find the new 1d index
242 // from the permuted multidimensional index
243 size_t new_idx = 0;
244 auto it = lower_bound(lookup.begin(), lookup.end(), permuted_indices);
245 if (it != lookup.end()) new_idx = it - lookup.begin();
246 (*mapDerivIndex.data(swap_braket,swap_bra,swap_ket,i)) = new_idx;
247 }
248 }
249 return mapDerivIndex;
250 }
251
252 }; // class definition
253
254} // namespace libint2
255
256#endif /* _libint2_include_derivmap_h_*/
This class statically initializes all index permutation maps for each BraKet type which requires them...
Definition: deriv_map.h:48
Defaults definitions for various parameters assumed by Libint.
Definition: algebra.cc:24
BraKet
types of shell sets supported by Engine, in chemist notation (i.e.
Definition: include/libint2/braket.h:36
Definition: tensor.h:37