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