1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: gradeval_base.cc
4 // Copyright (C) 2012 Toru Shiozaki
5 //
6 // Author: Toru Shiozaki <shiozaki@northwestern.edu>
7 // Maintainer: Shiozaki group
8 //
9 // This file is part of the BAGEL package.
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 //
24
25
26 #include <src/grad/gradeval_base.h>
27 #include <src/grad/dkhgrad.h>
28 #include <src/util/taskqueue.h>
29 #include <src/util/parallel/resources.h>
30 #include <src/util/parallel/mpi_interface.h>
31 #include <array>
32
33 using namespace std;
34 using namespace bagel;
35
contract_gradient(const shared_ptr<const Matrix> d,const shared_ptr<const Matrix> w,const shared_ptr<const DFDist> o,const shared_ptr<const Matrix> o2,const shared_ptr<const Matrix> v,const bool numerical,const shared_ptr<const Geometry> g2,const shared_ptr<const DFDist> g2o,const shared_ptr<const Matrix> g2o2)36 shared_ptr<GradFile> GradEval_base::contract_gradient(const shared_ptr<const Matrix> d, const shared_ptr<const Matrix> w,
37 const shared_ptr<const DFDist> o, const shared_ptr<const Matrix> o2,
38 const shared_ptr<const Matrix> v, const bool numerical,
39 const shared_ptr<const Geometry> g2, const shared_ptr<const DFDist> g2o, const shared_ptr<const Matrix> g2o2) {
40 grad_->zero();
41
42 if (!numerical) {
43 vector<shared_ptr<GradTask>> task = contract_grad2e(o);
44
45 vector<shared_ptr<GradTask>> task2;
46 if (geom_->hcoreinfo()->dkh()) {
47 auto dkh = make_shared<DKHgrad>(geom_);
48 task2 = contract_graddkh1e(dkh->compute(d, w));
49 } else {
50 task2 = contract_grad1e<GradTask1>(d, w);
51 }
52
53 vector<shared_ptr<GradTask>> task3 = contract_grad2e_2index(o2);
54 task.insert(task.end(), task2.begin(), task2.end());
55 task.insert(task.end(), task3.begin(), task3.end());
56 if (v) {
57 vector<shared_ptr<GradTask>> task4 = contract_grad1e<GradTask1s>(v, v);
58 task.insert(task.end(), task4.begin(), task4.end());
59 }
60
61 if (g2 && g2o) {
62 vector<shared_ptr<GradTask>> task0 = contract_grad2e(g2o, g2);
63 task.insert(task.end(), task0.begin(), task0.end());
64 }
65 if (g2 && g2o2) {
66 vector<shared_ptr<GradTask>> task0 = contract_grad2e_2index(g2o2, g2);
67 task.insert(task.end(), task0.begin(), task0.end());
68 }
69
70 TaskQueue<shared_ptr<GradTask>> tq(move(task));
71 tq.compute();
72 } else {
73 vector<shared_ptr<GradTask>> task = contract_grad1e<GradTask1s>(v, v);
74
75 TaskQueue<shared_ptr<GradTask>> tq(move(task));
76 tq.compute();
77 }
78
79 if (!v)
80 *grad_ += *geom_->compute_grad_vnuc();
81
82 grad_->allreduce();
83
84 return grad_;
85 }
86
87
88 template<typename TaskType>
contract_grad1e(const shared_ptr<const Matrix> d,const shared_ptr<const Matrix> w)89 vector<shared_ptr<GradTask>> GradEval_base::contract_grad1e(const shared_ptr<const Matrix> d, const shared_ptr<const Matrix> w) {
90 return contract_grad1e<TaskType>(d, d, w);
91 }
92
93
94 template<typename TaskType>
contract_grad1e(const shared_ptr<const Matrix> nmat,const shared_ptr<const Matrix> kmat,const shared_ptr<const Matrix> omat)95 vector<shared_ptr<GradTask>> GradEval_base::contract_grad1e(const shared_ptr<const Matrix> nmat, const shared_ptr<const Matrix> kmat, const shared_ptr<const Matrix> omat) {
96 vector<shared_ptr<GradTask>> out;
97 const size_t nshell = std::accumulate(geom_->atoms().begin(), geom_->atoms().end(), 0,
98 [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
99 out.reserve(nshell*nshell);
100
101 // TODO perhaps we could reduce operation by a factor of 2
102 int cnt = 0;
103 int iatom0 = 0;
104 auto oa0 = geom_->offsets().begin();
105 for (auto a0 = geom_->atoms().begin(); a0 != geom_->atoms().end(); ++a0, ++oa0, ++iatom0) {
106 int iatom1 = 0;
107 auto oa1 = geom_->offsets().begin();
108 for (auto a1 = geom_->atoms().begin(); a1 != geom_->atoms().end(); ++a1, ++oa1, ++iatom1) {
109
110 auto o0 = oa0->begin();
111 for (auto b0 = (*a0)->shells().begin(); b0 != (*a0)->shells().end(); ++b0, ++o0) {
112 auto o1 = oa1->begin();
113 for (auto b1 = (*a1)->shells().begin(); b1 != (*a1)->shells().end(); ++b1, ++o1) {
114
115 // static distribution since this is cheap
116 if (cnt++ % mpi__->size() != mpi__->rank()) continue;
117
118 array<shared_ptr<const Shell>,2> input = {{*b1, *b0}};
119 vector<int> atom = {iatom0, iatom1};
120 vector<int> offset_ = {*o0, *o1};
121
122 out.push_back(make_shared<TaskType>(input, atom, offset_, nmat, kmat, omat, this));
123 }
124 }
125 }
126 }
127
128 // if finite nucleus are used, we need to insert additional tasks that are omitted from the 1e part
129 if (geom_->has_finite_nucleus()) {
130 vector<shared_ptr<GradTask>> task0 = contract_grad1e_fnai(nmat);
131 out.insert(out.end(), task0.begin(), task0.end());
132 }
133
134 return out;
135 }
136
137
contract_graddkh1e(array<shared_ptr<const Matrix>,4> den)138 vector<shared_ptr<GradTask>> GradEval_base::contract_graddkh1e(array<shared_ptr<const Matrix>, 4> den) {
139 auto geom = make_shared<Molecule>(*geom_);
140 geom = geom->uncontract();
141 vector<shared_ptr<GradTask>> out;
142 const size_t nshell = std::accumulate(geom->atoms().begin(), geom->atoms().end(), 0,
143 [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
144 out.reserve(nshell*nshell);
145
146 // TODO perhaps we could reduce operation by a factor of 2
147 int cnt = 0;
148 int iatom0 = 0;
149 auto oa0 = geom->offsets().begin();
150 for (auto a0 = geom->atoms().begin(); a0 != geom->atoms().end(); ++a0, ++oa0, ++iatom0) {
151 int iatom1 = 0;
152 auto oa1 = geom->offsets().begin();
153 for (auto a1 = geom->atoms().begin(); a1 != geom->atoms().end(); ++a1, ++oa1, ++iatom1) {
154
155 auto o0 = oa0->begin();
156 for (auto b0 = (*a0)->shells().begin(); b0 != (*a0)->shells().end(); ++b0, ++o0) {
157 auto o1 = oa1->begin();
158 for (auto b1 = (*a1)->shells().begin(); b1 != (*a1)->shells().end(); ++b1, ++o1) {
159
160 // static distribution since this is cheap
161 if (cnt++ % mpi__->size() != mpi__->rank()) continue;
162
163 array<shared_ptr<const Shell>,2> input = {{*b1, *b0}};
164 vector<int> atom = {iatom0, iatom1};
165 vector<int> offset_ = {*o0, *o1};
166
167 out.push_back(make_shared<GradTask1d>(input, atom, offset_, den, this));
168 }
169 }
170 }
171 }
172
173 return out;
174 }
175
176
177 // TODO make a generic code to merge with grad1e (variadic templete? vector?)
contract_gradsmall1e(array<shared_ptr<const Matrix>,6> rmat)178 vector<shared_ptr<GradTask>> GradEval_base::contract_gradsmall1e(array<shared_ptr<const Matrix>,6> rmat) {
179 vector<shared_ptr<GradTask>> out;
180 const size_t nshell = std::accumulate(geom_->atoms().begin(), geom_->atoms().end(), 0,
181 [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
182 out.reserve(nshell*nshell);
183
184 // TODO perhaps we could reduce operation by a factor of 2
185 int cnt = 0;
186 int iatom0 = 0;
187 auto oa0 = geom_->offsets().begin();
188 for (auto a0 = geom_->atoms().begin(); a0 != geom_->atoms().end(); ++a0, ++oa0, ++iatom0) {
189 int iatom1 = 0;
190 auto oa1 = geom_->offsets().begin();
191 for (auto a1 = geom_->atoms().begin(); a1 != geom_->atoms().end(); ++a1, ++oa1, ++iatom1) {
192
193 auto o0 = oa0->begin();
194 for (auto b0 = (*a0)->shells().begin(); b0 != (*a0)->shells().end(); ++b0, ++o0) {
195 auto o1 = oa1->begin();
196 for (auto b1 = (*a1)->shells().begin(); b1 != (*a1)->shells().end(); ++b1, ++o1) {
197
198 // static distribution since this is cheap
199 if (cnt++ % mpi__->size() != mpi__->rank()) continue;
200
201 array<shared_ptr<const Shell>,2> input = {{*b1, *b0}};
202 vector<int> atom = {iatom0, iatom1};
203 vector<int> offset_ = {*o0, *o1};
204
205 out.push_back(make_shared<GradTask1r>(input, atom, offset_, rmat, this));
206 }
207 }
208 }
209 }
210 return out;
211 }
212
213
contract_grad2e(const array<shared_ptr<const DFDist>,6> o,const shared_ptr<const Geometry> geom)214 vector<shared_ptr<GradTask>> GradEval_base::contract_grad2e(const array<shared_ptr<const DFDist>,6> o, const shared_ptr<const Geometry> geom) {
215 shared_ptr<const Geometry> cgeom = geom ? geom : geom_;
216 vector<shared_ptr<GradTask>> out;
217 const size_t nshell = std::accumulate(cgeom->atoms().begin(), cgeom->atoms().end(), 0,
218 [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
219 const size_t nshell2 = std::accumulate(cgeom->aux_atoms().begin(), cgeom->aux_atoms().end(), 0,
220 [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
221
222 out.reserve(nshell*nshell*nshell2);
223
224 int iatom0 = 0;
225 auto oa0 = cgeom->offsets().begin();
226 for (auto a0 = cgeom->atoms().begin(); a0 != cgeom->atoms().end(); ++a0, ++oa0, ++iatom0) {
227 int iatom1 = 0;
228 auto oa1 = cgeom->offsets().begin();
229 for (auto a1 = cgeom->atoms().begin(); a1 != cgeom->atoms().end(); ++a1, ++oa1, ++iatom1) {
230 int iatom2 = 0;
231 auto oa2 = cgeom->aux_offsets().begin();
232 for (auto a2 = cgeom->aux_atoms().begin(); a2 != cgeom->aux_atoms().end(); ++a2, ++oa2, ++iatom2) {
233 if ((*a2)->dummy()) continue;
234 // dummy shell
235 auto b3 = make_shared<const Shell>((*a2)->shells().front()->spherical());
236
237 auto o0 = oa0->begin();
238 for (auto b0 = (*a0)->shells().begin(); b0 != (*a0)->shells().end(); ++b0, ++o0) {
239 auto o1 = oa1->begin();
240 for (auto b1 = (*a1)->shells().begin(); b1 != (*a1)->shells().end(); ++b1, ++o1) {
241 auto o2 = oa2->begin();
242 for (auto b2 = (*a2)->shells().begin(); b2 != (*a2)->shells().end(); ++b2, ++o2) {
243 tuple<size_t, size_t> info = o[0]->adist_now()->locate(*o2);
244 if (get<0>(info) != mpi__->rank()) continue;
245
246 array<shared_ptr<const Shell>,4> input = {{b3, *b2, *b1, *b0}};
247 vector<int> atoms = {iatom0, iatom1, iatom2};
248 vector<int> offs = {*o0, *o1, *o2};
249
250 out.push_back(make_shared<GradTask3r>(input, atoms, offs, o, this));
251 }
252 }
253 }
254
255 }
256 }
257 }
258 return out;
259 }
260
261
contract_grad1e_fnai(const array<shared_ptr<const Matrix>,6> o,const shared_ptr<const Geometry> geom)262 vector<shared_ptr<GradTask>> GradEval_base::contract_grad1e_fnai(const array<shared_ptr<const Matrix>,6> o, const shared_ptr<const Geometry> geom) {
263 shared_ptr<const Geometry> cgeom = geom ? geom : geom_;
264 vector<shared_ptr<GradTask>> out;
265 const size_t nshell = std::accumulate(cgeom->atoms().begin(), cgeom->atoms().end(), 0, [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
266 const size_t nfatom = std::accumulate(cgeom->atoms().begin(), cgeom->atoms().end(), 0, [](const int& i, const shared_ptr<const Atom>& o) { return i+(o->finite_nucleus() ? 1 : 0); });
267 out.reserve(nshell*nshell*nfatom);
268
269 // loop over atoms
270 int iatomf = -1;
271 int cnt = 0;
272 for (auto& af : cgeom->atoms()) {
273 ++iatomf;
274 if (!af->finite_nucleus()) continue;
275 // construct nuclear shell
276 const double fac = - af->atom_charge()*pow(af->atom_exponent()/pi__, 1.5);
277 auto b2 = make_shared<Shell>(af->spherical(), af->position(), 0, vector<double>{af->atom_exponent()}, vector<vector<double>>{{fac}}, vector<pair<int,int>>{make_pair(0,1)});
278
279 // dummy shell
280 auto b3 = make_shared<Shell>(af->spherical());
281
282 int iatom0 = 0;
283 auto oa0 = cgeom->offsets().begin();
284 for (auto a0 = cgeom->atoms().begin(); a0 != cgeom->atoms().end(); ++a0, ++oa0, ++iatom0) {
285 int iatom1 = 0;
286 auto oa1 = cgeom->offsets().begin();
287 for (auto a1 = cgeom->atoms().begin(); a1 != cgeom->atoms().end(); ++a1, ++oa1, ++iatom1) {
288 auto o0 = oa0->begin();
289 for (auto b0 = (*a0)->shells().begin(); b0 != (*a0)->shells().end(); ++b0, ++o0) {
290 auto o1 = oa1->begin();
291 for (auto b1 = (*a1)->shells().begin(); b1 != (*a1)->shells().end(); ++b1, ++o1) {
292
293 // static distribution since this is cheap
294 if (cnt++ % mpi__->size() != mpi__->rank()) continue;
295
296 array<shared_ptr<const Shell>,4> input = {{b3, b2, *b1, *b0}};
297 vector<int> atoms = {iatom0, iatom1, iatomf};
298 vector<int> offs = {*o0, *o1, 0};
299
300 out.push_back(make_shared<GradTask1rf>(input, atoms, offs, o, this));
301 }
302
303 }
304 }
305 }
306 }
307 return out;
308 }
309
310
contract_grad2e(const shared_ptr<const DFDist> o,const shared_ptr<const Geometry> geom)311 vector<shared_ptr<GradTask>> GradEval_base::contract_grad2e(const shared_ptr<const DFDist> o, const shared_ptr<const Geometry> geom) {
312 shared_ptr<const Geometry> cgeom = geom ? geom : geom_;
313 vector<shared_ptr<GradTask>> out;
314 const size_t nshell = std::accumulate(cgeom->atoms().begin(), cgeom->atoms().end(), 0,
315 [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
316 const size_t nshell2 = std::accumulate(cgeom->aux_atoms().begin(), cgeom->aux_atoms().end(), 0,
317 [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
318
319 out.reserve(nshell*(nshell+1)*nshell2/2);
320
321 // loop over atoms (using symmetry b0 <-> b1)
322 int iatom0 = 0;
323 auto oa0 = cgeom->offsets().begin();
324 for (auto a0 = cgeom->atoms().begin(); a0 != cgeom->atoms().end(); ++a0, ++oa0, ++iatom0) {
325 int iatom1 = iatom0;
326 auto oa1 = oa0;
327 for (auto a1 = a0; a1 != cgeom->atoms().end(); ++a1, ++oa1, ++iatom1) {
328 int iatom2 = 0;
329 auto oa2 = cgeom->aux_offsets().begin();
330 for (auto a2 = cgeom->aux_atoms().begin(); a2 != cgeom->aux_atoms().end(); ++a2, ++oa2, ++iatom2) {
331 if ((*a2)->dummy()) continue;
332 // dummy shell
333 auto b3 = make_shared<const Shell>((*a2)->shells().front()->spherical());
334
335 auto o0 = oa0->begin();
336 for (auto b0 = (*a0)->shells().begin(); b0 != (*a0)->shells().end(); ++b0, ++o0) {
337 auto o1 = a0!=a1 ? oa1->begin() : o0;
338 for (auto b1 = (a0!=a1 ? (*a1)->shells().begin() : b0); b1 != (*a1)->shells().end(); ++b1, ++o1) {
339 auto o2 = oa2->begin();
340 for (auto b2 = (*a2)->shells().begin(); b2 != (*a2)->shells().end(); ++b2, ++o2) {
341 tuple<size_t, size_t> info = o->adist_now()->locate(*o2);
342 if (get<0>(info) != mpi__->rank()) continue;
343
344 array<shared_ptr<const Shell>,4> input = {{b3, *b2, *b1, *b0}};
345 vector<int> atoms = {iatom0, iatom1, iatom2};
346 vector<int> offs = {*o0, *o1, *o2};
347
348 out.push_back(make_shared<GradTask3>(input, atoms, offs, o, this));
349 }
350 }
351 }
352
353 }
354 }
355 }
356 return out;
357 }
358
359
contract_grad2e_2index(const shared_ptr<const Matrix> den,const shared_ptr<const Geometry> geom)360 vector<shared_ptr<GradTask>> GradEval_base::contract_grad2e_2index(const shared_ptr<const Matrix> den, const shared_ptr<const Geometry> geom) {
361 shared_ptr<const Geometry> cgeom = geom ? geom : geom_;
362 vector<shared_ptr<GradTask>> out;
363 const size_t nshell2 = std::accumulate(cgeom->aux_atoms().begin(), cgeom->aux_atoms().end(), 0,
364 [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
365 out.reserve(nshell2*(nshell2+1)/2);
366
367 // using symmetry (b0 <-> b1)
368 int cnt = 0;
369 int iatom0 = 0;
370 auto oa0 = cgeom->aux_offsets().begin();
371 for (auto a0 = cgeom->aux_atoms().begin(); a0 != cgeom->aux_atoms().end(); ++a0, ++oa0, ++iatom0) {
372 int iatom1 = iatom0;
373 auto oa1 = oa0;
374 for (auto a1 = a0; a1 != cgeom->aux_atoms().end(); ++a1, ++oa1, ++iatom1) {
375 if ((*a0)->dummy() || (*a1)->dummy()) continue;
376
377 // dummy shell
378 auto b3 = make_shared<const Shell>((*a0)->shells().front()->spherical());
379
380 auto o0 = oa0->begin();
381 for (auto b0 = (*a0)->shells().begin(); b0 != (*a0)->shells().end(); ++b0, ++o0) {
382 auto o1 = a0!=a1 ? oa1->begin() : o0;
383 for (auto b1 = (a0!=a1 ? (*a1)->shells().begin() : b0); b1 != (*a1)->shells().end(); ++b1, ++o1) {
384
385 // static distribution since this is cheap
386 if (cnt++ % mpi__->size() != mpi__->rank()) continue;
387
388 array<shared_ptr<const Shell>,4> input = {{*b1, b3, *b0, b3}};
389 vector<int> atoms = {iatom0, iatom1};
390 vector<int> offs = {*o0, *o1};
391
392 out.push_back(make_shared<GradTask2>(input, atoms, offs, den, this));
393 }
394 }
395 }
396 }
397 return out;
398 }
399
400
contract_grad1e_fnai(const shared_ptr<const Matrix> nmat)401 vector<shared_ptr<GradTask>> GradEval_base::contract_grad1e_fnai(const shared_ptr<const Matrix> nmat) {
402 vector<shared_ptr<GradTask>> out;
403 const size_t nshell = std::accumulate(geom_->atoms().begin(), geom_->atoms().end(), 0, [](const int& i, const shared_ptr<const Atom>& o) { return i+o->shells().size(); });
404 const size_t nfatom = std::accumulate(geom_->atoms().begin(), geom_->atoms().end(), 0, [](const int& i, const shared_ptr<const Atom>& o) { return i+(o->finite_nucleus() ? 1 : 0); });
405 out.reserve(nshell*nshell*nfatom);
406
407 int cnt = 0;
408
409 // loop over finite nucleus
410 int iatomf = -1;
411 for (auto& af : geom_->atoms()) {
412 ++iatomf;
413 // skip if this atom is not finite nucleus
414 if (!af->finite_nucleus()) continue;
415
416 // construct nuclear shell
417 const double fac = - af->atom_charge()*pow(af->atom_exponent()/pi__, 1.5);
418 auto nshell = make_shared<Shell>(af->spherical(), af->position(), 0, vector<double>{af->atom_exponent()}, vector<vector<double>>{{fac}}, vector<pair<int,int>>{make_pair(0,1)});
419
420 // construct dummy shell
421 auto dummy = make_shared<const Shell>(af->spherical());
422
423 int iatom0 = 0;
424 auto oa0 = geom_->offsets().begin();
425 for (auto a0 = geom_->atoms().begin(); a0 != geom_->atoms().end(); ++a0, ++oa0, ++iatom0) {
426 int iatom1 = 0;
427 auto oa1 = geom_->offsets().begin();
428 for (auto a1 = geom_->atoms().begin(); a1 != geom_->atoms().end(); ++a1, ++oa1, ++iatom1) {
429
430 auto o0 = oa0->begin();
431 for (auto b0 = (*a0)->shells().begin(); b0 != (*a0)->shells().end(); ++b0, ++o0) {
432 auto o1 = oa1->begin();
433 for (auto b1 = (*a1)->shells().begin(); b1 != (*a1)->shells().end(); ++b1, ++o1) {
434
435 // static distribution since this is cheap
436 if (cnt++ % mpi__->size() != mpi__->rank()) continue;
437
438 array<shared_ptr<const Shell>,4> input = {{dummy, nshell, *b1, *b0}};
439 vector<int> atom = {iatom0, iatom1, iatomf};
440 vector<int> offset_ = {*o0, *o1, 0};
441
442 out.push_back(make_shared<GradTask1f>(input, atom, offset_, nmat, this));
443 }
444 }
445 }
446 }
447 }
448 return out;
449 }
450
451 // explicit instantiation of the template functions
452 template
453 std::vector<std::shared_ptr<GradTask>> GradEval_base::contract_grad1e<GradTask1>(const std::shared_ptr<const Matrix> d, const std::shared_ptr<const Matrix> w);
454 template
455 std::vector<std::shared_ptr<GradTask>> GradEval_base::contract_grad1e<GradTask1s>(const std::shared_ptr<const Matrix> d, const std::shared_ptr<const Matrix> w);
456 template
457 std::vector<std::shared_ptr<GradTask>> GradEval_base::contract_grad1e<GradTask1>(const std::shared_ptr<const Matrix> n, const std::shared_ptr<const Matrix> k,
458 const std::shared_ptr<const Matrix> o);
459 template
460 std::vector<std::shared_ptr<GradTask>> GradEval_base::contract_grad1e<GradTask1s>(const std::shared_ptr<const Matrix> n, const std::shared_ptr<const Matrix> k,
461 const std::shared_ptr<const Matrix> o);
462