• No results found

6.3 Lazy matrix library lazyten

6.3.1 Examples

To finish off this section, we demonstrate the high-level syntax of lazyten in two example cases. First consider a general linear problem Ax = b with known right-hand side b and unknown x. The system matrix A shall be represented by the lazyten matrix object A and the right-hand side b by the object b, which is taken to be a simple stored vector of type SmallVector<double>. In lazyten there are two absolutely equivalent ways to solve this problem. First

1 SmallVector <double> x ( b . size () ) ; 2 solve (A , x , b ) ;

or equivalently

1 auto invA = inverse ( A ) ; 2 auto x = invA * b ;

i.e. quite literally coding the application of the inverse. In both cases the last line will cause the problem to be passed to a linear solver algorithm in order to solve it iteratively or by direct methods. The user may provide extra parameters to the calls of solve or

inversein order to influence the selected eigensolver algorithm or provide some means

of preconditioning the problem matrix.

The second example is more relevant to the scope of this work and brings us back to the end of section 5.5 on page 140, where we discussed the possibility of an SCF routine, which is independent from the type of basis function used. Figure 6.4 on the next page shows a code fragment from a very simple Roothaan repeated diagonalisation SCF routine (see section 5.4.1 on page 128) for closed-shell systems coded in the syntax of lazyten.

150 CHAPTER 6. CONTRACTION-BASED ALGORITHMS & LAZY MATRICES 1 // Obtain a core H a m i l t o n i a n guess

2 const auto hcorebb = Tbb + Vbb ;

3 const auto e i g e n s o l u t i o n = e i g e n s y s t e m _ h e r m i t i a n ( hcorebb , Sbb ,

4 n_orbs ) ;

5

6 // View current o c c u p i e d c o e f f i c i e n t s in c o n v e n i e n t data s t r u c t u r e 7 const auto cocc = e i g e n s o l u t i o n . e v e c t o r s () . subview ({0 , n_alpha }) ; 8

9 // I n i t i a l i s e two - e l e c t r o n terms with guess c o e f f i c i e n t s 10 Jbb . update ({{" c o e f f i c i e n t s _ o c c u p i e d ", cocc }}) ;

11 Kbb . update ({{" c o e f f i c i e n t s _ o c c u p i e d ", cocc }}) ; 12

13 // Start R o o t h a a n r e p e a t e d d i a g o n a l i s a t i o n 14 double oldene = 0;

15 std :: cout << " Iter etot echange " << std :: endl ; 16 for ( size_t i = 0; i < m a x _ i t e r ; ++ i ) {

17 // Obtain new e i g e n p a i r s ...

18 const auto Fbb = hcorebb + (2 * Jbb - Kbb ) ; 19 const auto e i g e n s o l u t i o n = e i g e n s y s t e m _ h e r m i t i a n ( Fbb , Sbb ,

20 n_orbs ) ;

21

22 // ... and a new view to the o c c u p i e d c o e f f i c i e n t s

23 const auto cocc = e i g e n s o l u t i o n . e v e c t o r s () . subview ({0 , n_alpha }) ; 24

25 // Compute HF e n e r g i e s :

26 // Coulomb energy is 2 * tr ( C ^ T J C ) ,

27 // where 2 appears , since we only c o n s i d e r alpha block , 28 // but both alpha and beta c o e f f i c i e n t s would c o n t r i b u t e . 29 double e n e _ c o u l o m b = 2 * trace ( o u t e r _ p r o d _ s u m ( cocc ,

30 Jbb * cocc ) ) ;

31 double e n e _ e x c h g e = - trace ( o u t e r _ p r o d _ s u m ( cocc ,

32 Kbb * cocc ) ) ;

33 double e n e _ o n e _ e l e c = trace ( o u t e r _ p r o d _ s u m ( cocc ,

34 hcorebb * cocc ) ) ;

35 double energy = 2 * ( e n e _ o n e _ e l e c + 0.5 * e n e _ c o u l o m b +

36 0.5 * e n e _ e x c h g e ) ;

37

38 // Display current i t e r a t i o n

39 double e n e r g y _ d i f f = energy - oldene ;

40 std :: cout << i << " " << energy << " " << e n e r g y _ d i f f << ւ ֒→std :: endl ; 41 oldene = energy ; 42 43 // Check for c o n v e r g e n c e 44 if ( fabs ( e n e r g y _ d i f f ) < 1e -6) break; 45

46 // Update the two - e l e c t r o n integrals , 47 // before c o e f f i c i e n t s go out of scope

48 Jbb . update ({{" c o e f f i c i e n t s _ o c c u p i e d ", cocc }}) ;

Figure 6.4: Code fragment of a simple basis-type independent Hartree-Fock procedure im- plemented with lazyten. The procedure follows the Roothaan repeated diagonalisation algorithm in the specialisation for closed-shell system (see section 5.4.1).

6.3. LAZY MATRIX LIBRARY LAZYTEN 151 Before the depicted code segment is executed, the integral library is given information about the chemical system and the desired discretisation and returns the objects Tbb, Vbb,

Jbb, Kbb and Sbb, which represent the matrices T, V0, J, K and S as they are defined

in (4.60) to (4.64). Additionally parameters appearing in the code include n_alpha, the number of alpha electrons and n_orb, the number of SCF orbitals to compute in each step.

Alongside the comments the code should largely be self-explanatory. In lines 1 to

6 a core Hamiltonian guess is obtained by diagonalising T + V0 (see 5.2.1). Then the

Coulomb and exchange lazy matrices are updated to the guess coefficients in lines 9 and 10. Depending on the implementation of these lazy matrices, this might already involve the computation of the matrices (4.62) and (4.63), but for others this might just update an internal reference to the current set of coefficients and apart from that do nothing. From what we discussed in the previous chapter it should be clear that the former is better-suited for a cGTO discretisation and the latter from a FE-based discretisation for example.

Afterwards the main loop starts, where first the Fock matrix expression is built in line 17 and then diagonalised in line 21. Then the current energies are computed in lines 27 to 30 following (4.59) making vivid use of the outer_prod_sum routine. Right now this routine is required in such a case originating from the unfortunate decision to represent a matrix and a set of vectors as two inherently different data structures. Effectively it

computes products such as CT(JC) from the matrices C and JC represented as a list

of vectors. The remaining lines 32 to 43 print the current iteration to the user, check for convergence and update the state of J and K for the next iteration.

Despite its simplicity the depicted code is independent of the type of basis function used to discretise the problem as lazyten automatically adapts the executed eigensolver routines for the calls in lines 6 and 18 to the structure of the Fock matrix. Indirectly it is

thus the structure of the matrices Tbb, Vbb, Jbb, Kbb and Sbb and usually3 not the code

depicted in figure 6.4 which decides, which eigensolver algorithms are chosen. Given that the basic heuristics currently implemented, the depicted code would for example perform a contraction-based SCF for a CS-based discretisation and use direct eigensolves for a cGTO-based discretisation. In the light of this lazyten becomes a very effective abstraction layer between the details of the lazy matrix implementation, i.e. the integral evaluation, and the SCF algorithm.

In the SCF depicted in figure 6.4 many expressions like lines 17 and 18 or the energy computation are designed to resemble the equivalent equations one would derive on paper up to a very large extent. Nevertheless the matrices like Tbb, Vbb, Jbb, Kbb and

Sbbcould be stored or lazy for the code to work. Adding an extra term to the Fock

matrix expression in line 17 can be done by a simple addition of another matrix object irrespective whether the added object is lazy or stored. In either case its structure would be taken into account during the following diagonalisation without explicit user interaction. Still the user could influence the behaviour of the called solver by providing appropriate parameters explicitly. For this reason we believe lazyten to be very suitable for teaching or experimentation with novel methods, since many details are abstracted and one may at first concentrate on the algorithm and not on numerics.

Overall lazyten therefore amounts to yield a very intuitive syntax for contraction-

3

Since the automatic selection methods are not yet extremely advanced, it is necessary to overwrite the automatic choice from user code from time to time.

152 CHAPTER 6. CONTRACTION-BASED ALGORITHMS & LAZY MATRICES based methods in the form of lazy matrices, where algorithms can be written at a high level. By means of changing the implementation behind the employed lazy matrix objects the code can be fixed but still flexible to changes in the available hardware or if novel types of basis functions with unusual matrix structures become available.

Chapter 7

The molsturm

method-development

framework

[C has] the power of assembly language and the convenience of . . . assembly language.

— Dennis Ritchie (1941–2011)

Keep it simple, make it general, and make it intelligible.

— Doug McIlroy (1932–present)

When the molsturm project [40] was initiated a couple of years back, the original idea was to support molecular calculations with Sturmian-type basis functions in a formula- tion similar to the contraction-based ansatz mentioned in section 5.3.6, which explains the name. Following my unsuccessful attempts on a finite-element-based Hartree-Fock scheme, where I mostly used the approaches known to me from a cGTO setting, we expanded the goals of molsturm. Now, the primary project goal has become to yield a quantum-chemistry method development framework, which supports the implementa- tion and evaluation of discretisations based on novel types of basis functions. We have achieved this by building largely on the conclusions about the common structure of SCF algorithms and the generality of the lazy matrix syntax of lazyten, which were discussed in the previous chapters. Additionally molsturm has been equipped with a powerful python interface, where it is easy to obtain, archive and analyse results. Even implementing completely new features on top of molsturm’s SCF procedure often takes comparatively little development time as will be demonstrated. The arguments and examples of this chapter follow closely our publication [233].

All components of the molsturm program package are open-source software, licensed under the GNU General Public License. To obtain a copy of the code go to https: //molsturm.org.

154 CHAPTER 7. THE MOLSTURM METHOD-DEVELOPMENT FRAMEWORK

7.1

Related quantum-chemical software packages

Apart from molsturm I am unaware of another quantum-chemistry package which has achieved a similar flexibility with respect to the type of basis functions in its SCF procedure. Many packages still have related goals towards flexibility or generality of their codes and should therefore not go completely unmentioned here.

When it comes to flexibility of a program package a key ingredient is a versatile interface. This allows to invoke or extend the methods already available elsewhere. Recently the scripting language python has become very popular for achieving this. Even even meta-projects like ASE [234], which aim at extending existing packages by a common python front end, have emerged. Other packages like HORTON [235], pyscf [236],

pyQuante [237] and GPAW [38, 39] are written almost exclusively in python and only

employ low-level C or C++ code for the computational hot spots to various extents. Starting from the opposite direction Psi4 [238] has gradually introduced a more and more powerful python interface on top of their existing C++ core over the years.

The popularity of the combination of FORTRAN or a C-like language in the core and

pythonas the high-level interface language can be understood by considering the recent

publication of Sun et al. [236] about the pyscf package. They rationalise the choice of

pythonas follows:

• There is no need to learn a particular domain-specific input format.

• All language elements from python are immediately available to e.g. automatise repetitive calculations with loops or similar.

• The code is easily extensible beyond what is available inside pyscf, for example to facilitate plotting or other kinds of analysis.

• Computations can be done interactively, which is helpful for testing or debugging. Additionally one should mention, that python as a high-productivity, multi-paradigm language often permits to achieve even complicated tasks with few lines of code, whilst still staying surprisingly easy to read. In the context of quantum chemistry this has the pleasant side effect that a python script used for performing calculations and subsequent analysis is typically not overly lengthy, but still documents the exact procedure which is followed in a readable manner. All this comes at pretty much no downside if python is combined with carefully optimised low-level C or FORTRAN code in the computational hot spots. Sun et al. [236] for example claim that pyscf is as fast as any other existing quantum chemistry packages written solely in C or FORTRAN.

Another common feature between pyscf and Psi4 is their modular design inside the package. They vividly facilitate well-established open standards like HDF5 [239] or

numpyarrays [240] for data exchange, such that linking their codes to external projects is

easily feasible. Psi4 for example managed to integrate more than 15 external packages into their framework. This includes three completely different back ends for computing integrals. In the case of pyscf it only took us about a day to link our molsturm to the FCI algorithms of pyscf via an interface based on numpy. Nevertheless the numerical requirements of Gaussian-type orbitals are currently hard-coded inside the optimised C or C++ parts of both these projects, such that extending them by other types of basis functions could still be involved.

7.2. DESIGN OF THE MOLSTURM PACKAGE 155