by
Wyndham Bolling Blanton
B.S. Chemistry (Carnegie Mellon University) 1998
B.S. Physics (Carnegie Mellon University) 1998
A dissertation submitted in partial satisfaction of the
requirements for the degree of
Doctor of Philosophy
in
Chemistry
in the
GRADUATE DIVISION
of the
UNIVERSITY OF CALIFORNIA, BERKELEY
Committee in charge:
Professor Alexander Pines, Chair
Professor Jeﬀrey A. Reimer
Professor Raymond Y. Chiao
David E. Wemmer
Fall 2002
The dissertation of Wyndham Bolling Blanton is approved:
Chair Date
Date
Date
Date
University of California, Berkeley
Fall 2002
High Performance Computations in NMR
Copyright c _ 2002
by
Wyndham Bolling Blanton
1
Abstract
High Performance Computations in NMR
by
Wyndham Bolling Blanton
Doctor of Philosophy in Chemistry
University of California, Berkeley
Professor Alexander Pines, Chair
As an analytic noninvasive technique to study molecules in their natural envi
ronment, NMR has little equal. The advancement of the technique is beginning to enter
a new phase, where many body dynamics, complex control, and precise measurements of
many body spin properties preclude any exact theoretical treatment. Approximation meth
ods and other reductions in the set of parameter spaces are currently used to obtain some
form of intuition about a simpliﬁed NMR system; however, to exactly proﬁle a real system,
numerical simulation is required.
The scope of most NMR simulations is chieﬂy regulated to small spin systems,
where the dynamics are simpliﬁed enough to simulate eﬃciently. The cause is typically
based on a poor understanding of how to simulate an NMR situation eﬀectively and eﬃ
ciently. This seems consistent with the fact that most NMR spectroscopists are not com
puter scientists as well. The introduction of novel programming paradigms and numerical
techniques seems to have eluded the ﬁeld. A complete simulation environment for NMR is
2
presented here marrying three fundamental aspects of simulations 1) numerical speed and
eﬃciency, 2) simplicity in implementation, and 3) NMR speciﬁc algorithmic developments.
The majority of numerical NMR is reduced to a simple simulation framework. The
framework allows for more complex simulations for explorations of both many body spin
dynamics and control. A speciﬁc large scale simulation is applied to recoupling sequences in
solid–state NMR. Using simple permutations on base pulse sequences can result in control
enhancements on both the simple system and the many body system beyond a theoretical
approach. The sheer number of permutations required to solve the problem would have
certainly been impossible without the aid of this framework. This new framework now
opens other unexplored possibilities of using simulation as a development tool for the larger
problems of many body dynamics and control.
Professor Alexander Pines
Dissertation Committee Chair
i
To my Grandmother and Grandfather, Lucy and Wyndham Jr.
ii
Contents
List of Figures v
List of Tables viii
1 Introduction 1
2 Computer Mechanics 4
2.1 Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 The Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.2.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 Expression Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.1 Motivations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.2 Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3.3 An Array Object and Stacks . . . . . . . . . . . . . . . . . . . . . . 15
2.3.4 Expression Template Implementation . . . . . . . . . . . . . . . . . 19
2.4 Optimizing For Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.4.1 Basic Computer Architecture . . . . . . . . . . . . . . . . . . . . . . 30
2.4.2 A Faster Matrix Multiplication . . . . . . . . . . . . . . . . . . . . . 37
3 NMR Forms 42
3.1 Classical Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.2 Bloch Equation Magnetic Fields . . . . . . . . . . . . . . . . . . . . . . . . 43
3.3 Quantum Mechanics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.3.1 Rotations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.3.2 Rotational Frames . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
3.3.3 The Hamiltonians . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
3.4 NMR Initial Conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.4.1 Quantum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.4.2 Classical . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4 NMR Algorithms 76
4.1 Classical Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.1.1 Eigenvalue Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.1.2 ODE solvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.2 Quantum Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
iii
4.2.1 The Direct Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.2.2 Periodicity and Propagator Reduction . . . . . . . . . . . . . . . . . 83
4.2.3 Eigenspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
4.2.4 Periodicity and Eigen–Space methods . . . . . . . . . . . . . . . . . 95
4.2.5 Nonperiodic Hamiltonians . . . . . . . . . . . . . . . . . . . . . . . 100
4.2.6 Powder Average Integration . . . . . . . . . . . . . . . . . . . . . . . 100
4.3 Conclusions and Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
5 BlochLib 105
5.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
5.2 The Abstract NMR Simulation . . . . . . . . . . . . . . . . . . . . . . . . . 106
5.2.1 Experimental Evolutions (EE) . . . . . . . . . . . . . . . . . . . . . 106
5.2.2 Theoretical Evolutions (TE) . . . . . . . . . . . . . . . . . . . . . . . 106
5.2.3 Existing NMR Tool Kits . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.2.4 Why Create a new Tool Kit? . . . . . . . . . . . . . . . . . . . . . . 109
5.3 BlochLib Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
5.3.1 Existing Numerical Tool Kits . . . . . . . . . . . . . . . . . . . . . . 110
5.3.2 Experimental and Theoretical Evolutions for NMR simulations . . . 111
5.3.3 BlochLib Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
5.3.4 Drawbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
5.4 Various Implementations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
5.4.1 Solid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
5.4.2 Classical Program: Magnetic Field Calculators . . . . . . . . . . . . 129
5.4.3 Classical Programs: Bloch Simulations . . . . . . . . . . . . . . . . . 131
5.5 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
6 Massive Permutations of Rotor Synchronized Pulse Sequences 141
6.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
6.1.1 Rotor Synchronization . . . . . . . . . . . . . . . . . . . . . . . . . . 142
6.2 Background Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
6.2.1 Average Hamiltonian . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
6.2.2 Recoupling RSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
6.2.3 C7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
6.2.4 Removable of Higher Order Terms . . . . . . . . . . . . . . . . . . . 151
6.3 Permutations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
6.3.1 The Sub–Units . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
6.3.2 The Measure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
6.3.3 Algorithmic Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
6.4 Data and Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.4.1 Sequence Measures . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.4.2 Transfer Eﬃciencies . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
6.5 Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
7 Future Expansions 201
7.1 Evolutionary Algorithms (EA) . . . . . . . . . . . . . . . . . . . . . . . . . 202
7.2 Neural Networks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
7.3 Final Remarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
iv
Bibliography 213
A Auxillary code 225
A.1 General C++ code and examples . . . . . . . . . . . . . . . . . . . . . . . . 225
A.1.1 C++ Template code used to generate prime number at compilation 225
A.1.2 C++ Template metaprogram to unroll a ﬁxed length vector at com
pilation time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
A.1.3 C++ code for performing a matrix multiplication with L2 cache block
ing and partial loop unrolling. . . . . . . . . . . . . . . . . . . . . . . 228
A.1.4 An MPI master/slave implimentation framework . . . . . . . . . . . 230
A.1.5 C++ class for a 1 hidden layer Fully
connected back–propagation Neural Network . . . . . . . . . . . . . 232
A.2 NMR algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
A.2.1 Mathematica Package to generate Wigner Rotation matrices and Spin
operators. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
A.2.2 Rational Reduction C++ Class . . . . . . . . . . . . . . . . . . . . . 244
A.2.3 Optimized static Hamiltonian FID propogation . . . . . . . . . . . . 252
A.2.4 γ −COMPUTE C++ Class . . . . . . . . . . . . . . . . . . . . . . 253
A.3 BlochLib Conﬁgurations and Sources . . . . . . . . . . . . . . . . . . . . . . 263
A.3.1 Solid conﬁguration ﬁles . . . . . . . . . . . . . . . . . . . . . . . . . 263
A.3.2 Magnetic Field Calculator input ﬁle . . . . . . . . . . . . . . . . . . 266
A.3.3 Quantum Mechanical Single Pulse Simulations . . . . . . . . . . . . 267
A.3.4 Example Classical Simulation of the Bulk Susceptibility . . . . . . . 267
A.3.5 Example Classical Simulation of the Modulated Demagnetizing Field 274
v
List of Figures
2.1 A two state Turing machine . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2 A simple stack tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.3 How the compiler unrolls an expression template set of operations. . . . . . 25
2.4 DAXPY speed tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.5 A pictorial representation for the matrix–matrix tensor multiplication . . . 28
2.6 Speed in MFLOPS of a matrix–matrix multiplication . . . . . . . . . . . . . 29
2.7 A generic computer data path. . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.8 Pipe lines and loop unrolling . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.9 A 128 bit SIMD registers made of 4–32 bit data values . . . . . . . . . . . . 35
2.10 Cache levels in modern Processors . . . . . . . . . . . . . . . . . . . . . . . 36
2.11 Speed comparison in MFLOPS of loop unrolling . . . . . . . . . . . . . . . 39
2.12 Speed comparison in MFLOPS of L2 cache blocking and loop unrolling . . 40
3.1 The magnitude of the dipole ﬁeld . . . . . . . . . . . . . . . . . . . . . . . . 52
3.2 The magnetization of a sample inside a magneti ﬁeld. . . . . . . . . . . . . 55
3.3 Magnetization in iso–surfaces versus the applied magnetic ﬁeld, B
o
, the tem
perature T, and number of moles. . . . . . . . . . . . . . . . . . . . . . . . . 75
4.1 Various propagators needed for an arbitrary rational reduction. . . . . . . . 84
4.2 Eﬀectiveness of the rational propagator reduction method. . . . . . . . . . . 89
4.3 Diagram of one Hamiltonian period and the propagator labels used for the
COMPUTE algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
4.4 Octants of equal volume of a sphere. . . . . . . . . . . . . . . . . . . . . . . 102
5.1 Experimental Evolutions and Theoretical Evolutions . . . . . . . . . . . . . 107
5.2 The basic design layout of the BlochLib NMR tool kit. . . . . . . . . . . . 113
5.3 C=A*B*adjoint(A) speed of BlochLib . . . . . . . . . . . . . . . . . . . . . 115
5.4 Solid vs. Simpson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
5.5 The design of the EE program Solid derived from the input syntax. . . . . 127
5.6 1D static and spinning 2 spin simulation . . . . . . . . . . . . . . . . . . . . 128
5.7 1D and 2D postC7 simulation . . . . . . . . . . . . . . . . . . . . . . . . . 128
5.8 The basic design for the Field Calculator program. . . . . . . . . . . . . . 130
5.9 Magnetic ﬁeld of a D–circle . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
5.10 A rough design for a classical Bloch simulation over various interactions. . 133
vi
5.11 Bulk susceptibility HETCOR . . . . . . . . . . . . . . . . . . . . . . . . . . 135
5.12 Simulation of radiation damping and the modulated local ﬁeld . . . . . . . 136
5.13 Magnetic ﬁeld of a split solenoid . . . . . . . . . . . . . . . . . . . . . . . . 138
5.14 Magnetic ﬁeld of a solenoid . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
6.1 A general rotor synchronized pulse sequence a) using pulses and delays, and
b) using a quasi continuous RF pulse. . . . . . . . . . . . . . . . . . . . . . 142
6.2 The two RSS classes C (a) and R (b). . . . . . . . . . . . . . . . . . . . . . 147
6.3 Compensated C (a), R (b) and posted C (c), R(d) RSS sequences. . . . . . 149
6.4 PostC7 transfer eﬃciencies on a two spin system with ω
r
= 5kHz for various
dipolar coupling frequencies . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
6.5 Diﬀerent base permutations on the postC7 seqeunce . . . . . . . . . . . . . 153
6.6 Spin system SS
1
with 4 total number of C7s applied. . . . . . . . . . . . . . 164
6.7 Spin system SS
1
with 8 total number of C7s applied. . . . . . . . . . . . . . 165
6.8 Spin system SS
1
with 12 total number of C7s applied. . . . . . . . . . . . . 166
6.9 Spin system SS
1
with 16 total number of C7s applied. . . . . . . . . . . . . 167
6.10 Spin system SS
1
with 20 total number of C7s applied. . . . . . . . . . . . . 168
6.11 Spin system SS
1
with 24 total number of C7s applied. . . . . . . . . . . . . 169
6.12 Spin system SS
1
with 32 total number of C7s applied. . . . . . . . . . . . . 170
6.13 Spin system SS
1
with 40 total number of C7s applied. . . . . . . . . . . . . 171
6.14 Spin system SS
1
with 48 total number of C7s applied. . . . . . . . . . . . . 172
6.15 Spin system SS
2
with 4 total number of C7s applied. . . . . . . . . . . . . . 173
6.16 Spin system SS
2
with 8 total number of C7s applied. . . . . . . . . . . . . . 174
6.17 Spin system SS
2
with 12 total number of C7s applied. . . . . . . . . . . . . 175
6.18 Spin system SS
2
with 16 total number of C7s applied. . . . . . . . . . . . . 176
6.19 Spin system SS
2
with 24 total number of C7s applied. . . . . . . . . . . . . 177
6.20 Spin system SS
2
with 32 total number of C7s applied. . . . . . . . . . . . . 178
6.21 Spin system SS
3
with 4 total number of C7s applied. . . . . . . . . . . . . . 179
6.22 Spin system SS
3
with 8 total number of C7s applied. . . . . . . . . . . . . . 180
6.23 Spin system SS
3
with 12 total number of C7s applied. . . . . . . . . . . . . 181
6.24 Spin system SS
3
with 16 total number of C7s applied. . . . . . . . . . . . . 182
6.25 Spin system SS
3
with 24 total number of C7s applied. . . . . . . . . . . . . 183
6.26 Spin system SS
3
with 32 total number of C7s applied. . . . . . . . . . . . . 184
6.27 Pulse sequence, initial density matrices and detection for a transfer eﬃciency
measurement. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
6.28 Transfer eﬃciencies for a 4 fold application of the basic C7 and the postC7
for the SS
1
system as a function of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz. . 188
6.29 3D transfer eﬃciencies plots for a 4,8,12,16 fold application of the postC7
and the best permutation cycles for the SS
1
system as a function of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz. . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
6.30 Contour–gradient transfer eﬃciencies plots for a 4,8,12,16 fold application of
the postC7 and the best permutation cycles for the SS
1
system as a function
of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz. . . . . . . . . . . . . . . . . . . . . 191
6.31 3D transfer eﬃciencies plots for a 4,8,12,16 fold application of the postC7
and the best permutation cycles for the SS
2
system as a function of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz. . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
vii
6.32 Contour–gradient transfer eﬃciencies plots for a 4,8,12,16 fold application of
the postC7 and the best permutation cycles for the SS
2
system as a function
of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz. . . . . . . . . . . . . . . . . . . . . 193
6.33 3D transfer eﬃciencies plots for a 4,8,12,16 fold application of the postC7
and the best permutation cycles for the SS
3
system as a function of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz. . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
6.34 Contour–gradient transfer eﬃciencies plots for a 4,8,12,16 fold application of
the postC7 and the best permutation cycles for the SS
3
system as a function
of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz. . . . . . . . . . . . . . . . . . . . . 195
6.35 Transfer Eﬃciencies using the postC7 and the best permutated cycles across
over diﬀerent cycles for the SS
1
spin system. . . . . . . . . . . . . . . . . . 197
6.36 Transfer eﬃciencies using the postC7 and the best permutated cycles across
over diﬀerent cycles for the SS
2
spin system. . . . . . . . . . . . . . . . . . 198
6.37 Transfer eﬃciencies using the postC7 and the best permutated cycles across
over diﬀerent cycles for the SS
3
spin system. . . . . . . . . . . . . . . . . . 199
7.1 The standard evolutionary strategy methods and controls. . . . . . . . . . . 204
7.2 An arbitrary permutation cycle parent genes and resulting child. . . . . . . 205
7.3 Evolution Programming (EP) generation step for an ES
(2,1)
strategy. . . . 206
7.4 Genetic Algorithm (GA) generation step for an ES
(3,2)
strategy. . . . . . . 207
7.5 Diﬀerential Evolution (DE) generation step for an ES
(3,1)
strategy. . . . . 208
7.6 Basic 1 and 2 layer feed–forward neural networks. . . . . . . . . . . . . . . 209
viii
List of Tables
2.1 Basic High Level Language Data Types . . . . . . . . . . . . . . . . . . . . 8
2.2 SIMD registers available of common CPUs . . . . . . . . . . . . . . . . . . 34
3.1 Wigner rank 1 rotation elements, D
1
m,m
. . . . . . . . . . . . . . . . . . . . 62
3.2 Reduced Wigner rank 2 rotation elements, d
2
m,m
. . . . . . . . . . . . . . . 63
3.3 Spherical tensor basis as related to the Cartesian basis for spin i and spin j 67
4.1 Time propagation using individual propagators via the Direct Method . . . 86
4.2 A reduced set of individual propagators for m = 9 and n = 7 . . . . . . . . 86
4.3 Matrix Multiplication (MM) reduction use rational reduction . . . . . . . . 88
4.4 For m = 1 and n = 5 we have this series of propagators necessary to calculate
the total evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.1 Available Matlab visualization functions in BlochLib . . . . . . . . . . . . 121
5.2 Key examples and implementation programs inside BlochLib . . . . . . . . 124
6.1 A list of some sub–units for a C7 permutation cycle. . . . . . . . . . . . . 156
6.2 Sequence Permutation set for the eﬀective Hamiltonian calculations of the
postC7 sequence. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
6.3 Spin operators and tensors generated to probe the eﬀective Hamiltonians . 161
6.4 Spin System parameters for the three sets of permutations. All units are in
Hz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.5 Relevant weighting factors for Eq. 6.17 . . . . . . . . . . . . . . . . . . . . 162
6.6 Best C7 permutation sequences for each spin system and C7 cycle length. 186
ix
Acknowledgments
Ack
None of this thesis would have even existed without the aid of an SUV knocking
me oﬀ my motor cycle at the beginning of my years in the Pines group. It left my arm in a
state of mushy goo for 6 months. With only my left (not my ‘good’ arm) functioning I had
to leave the experimental track I had started and venture into the only thing I could do,
type. From that point on, the CPU was inevitable. So to this yokel, I give my estranged
thanks.
Nowledge
To say that one ﬁnished anything here without any help would be a nasty lie.
Those many years staring at a computer screen have made me appreciate the comments
and discussions from those who do not. Their constant volley of questions and ‘requests’
give me the impetuous to push my own skills higher. To all those Pine Nuts I have run
into, I give my thanks.
There is always something new spewing forth from the voice boxes of the pines
folk. In particular Jamie Walls and Bob Havlin seem to always have something new to try.
In essence the mathematical background was brought to bare by Jamie as Bob enlightened
the experimental side of NMR. From many years of discussion with these two, I have learned
most everything I claim to know.
From this point I thank Dr. Andreas Trabesinger for calling to my attention the
classical/quantum crossover opening up totally new CPU problems and solutions. John
Logan and Dr. Dimitris Sakellariou pushed the development of speed. John’s constant
testing and back and forth has helped me improve almost every aspect of my coding life.
x
Ment
Sadly, I was not able to work with many others in the lab, as it seemed my
instrument of choice was not a common NMR tool. It has been a privilege to have had
the ability to explore the capabilities of the CPU even if it was not on the main research
track of the group. For this I thank Alex Pines. Were it not for him, this exploration
and assembly would not have been possible. Alex seems to have an uncanny foresight into
peoples capabilities and personalities creating an interesting blend of skills, ideas, and brain
power that seem to fuel the everyday life in the lab as well as pushing new thoughts to the
end. I only hope to leave something behind for this group to take to the next stage.
S
We must not forget those folks that have constantly dealt with the emotional
sideshow that is grad school. During my stay here, my family has suﬀered many losses,
yet still has the strength to support my own endeavors; however crazy and obnoxious they
made me act towards them. One cannot forget the friends as well; Dr. P, Sir Wright, Prof.
Brown and ma’am Shirl have been around for many ages and are always a breath of clean,
cool air and patience. Were it not for all friend and family, I certainly would not be at this
point
•
So I thank all y’all.
1
Chapter 1
Introduction
Before the arrival of the computer, analytic mathematical techniques were the
only methods to gain insight into physical systems (aside from experiment of course). This
limited the scale of the problems that could be solved. For instance, there are few analytic
solutions to Ordinary Diﬀerential Equations (ODEs) in comparison to the massive number
that can be generated from simple physical systems. Nonlinearities in ODEs are extraordi
narily hard to treat analytically. Now, computers and simulations have increased the scale,
complexity, and knowledge about many systems from nuclear reactions and global weather
patterns to describing bacteria populations and protein folding.
The basic function of numerical simulations is to provide insight into theoretical
structures, physical systems, and to aid in experimental design. Its use in science comes
from the necessity to extend understanding where analytic techniques fail to produce any
insight. Numerical techniques are as much an art form as experimental techniques. There
are typically hundreds of ways to tackle numerical problems based on the available computer
architecture, algorithms, coding language, and especially development cost. Though many
2
numerical solutions to problems exist, some execute too slowly, others are too complicated
for anybody but the creator to use, and still others are not easily extendable.
The basic scientiﬁc simulation begins with a theory. The theory usually produces
the equations of motion for the system and the simulations task is to evolve a particular sys
tem in time. The theory of Nuclear Magnetic Resonance (NMR) is over 50 years[1, 2, 3, 4]
strong. The theory is so well developed that simulations have become the corner stone to
which all experimental results are measured[5, 6]. This is the perfect setting for numerical
simulations. The equations of motion are well established, approximation methods and
other simpliﬁcation techniques are prevalent, and the techniques for experimental veriﬁca
tion are very powerful.
Much of the advancement in NMR today comes from the aid provided by numeri
cal investigations (to list single references would be futile, as virtually all NMR publications
include a simulation of some kind). Even though there is this wide spread usage of simula
tion, there is surprisingly little available to assist in the task. This leaves the majority of
the numerical formulation to the scientist, when an appropriate tool kit can simplify the
procedure a hundred fold. Numerical tool kits are a collection of numerical routines that
make the users life easy (or at least easier).
The two largest and most popular toolkits available today are Matlab
1
and Math
ematica
2
. These two packages provide a huge number of tools for development of almost
any numerical situation. However, they are both costly, slow, and have no tools for NMR
applications. Of course it is possible to use these two to create almost any other tool kit,
but then the users will have to get the basic programs. Including other toolkits at this level
1
The MathWorks, Inc., 3 Apple Hill Drive, Natick, MA 017602098, Matheworks,http://mathworks.com
2
Wolfram Research, Inc., 100 Trade Center Drive, Champaign, IL 61820, Wolfram, http://wolfram.com
3
is next to impossible as is creating parallel or distributed programs.
This thesis attempts to collapse the majority of NMR research into a fast numerical
tool kit, but because there are over 50 years of mathematics to include, not everything can
be covered in a single thesis. However, the presented tool kit here can easily provide a basis
to include the rest. After we describe the tool kit, we will show how much easier it is to
create NMR simulations from the tiny to the large, and more importantly, how it can be
used to aid the ever toiling researcher to develop more and more interesting techniques.
Six chapters will follow this introduction. The second chapter describes the com
putational knowledge required to create algorithms and code that achieve both simplicity
in usage and, more importantly, speed. The third chapter then goes through the various
equations of motion for an NMR system in detail. It is these interactions that we need to
calculate eﬃciently and provide the abstract interface. The forth chapter describes most
all the possible algorithmic techniques used to solve NMR problems. The ﬁfth chapter will
demonstrate the basic algorithms, data structures, and design issues and how to contain
them all into one tool kit called BlochLib. The next chapter includes a demonstration of a
class of simulations now possible using the techniques developed in previous chapters. Here
I investigate the eﬀect of massive permutations on simple pulse sequences, and ﬁnally close
with several possible future applications and techniques.
4
Chapter 2
Computer Mechanics
Contrary to almost every other Pines’ Lab thesis, this discussion will begin with
the fundamentals of computation, rather then the fundamentals of NMR. This discussion is
best begun with the bad deﬁnition of a Turing Machine from MerriamWebster Dictionary.
“A hypothetical computing machine that has an unlimited amount of informa
tion storage.”
This basically says that a Turing machine is a computational machine, which does
not help us at all. What Turing really said is something like the following[7]. Imagine a
machine that can both read and write along one spot on a one dimensional tape divided
into sections (this tape can be of inﬁnite length). This machine can move to any section
on the tape. The machine has a ﬁnite number of allowed states, and the tape has a ﬁnite
number of allowed values. The machine can read the current spot on the tape, erase that
spot and write a new one. What the machine writes and does afterwards is determined by
three factors: the state of the machine, the value on the tape, and a table of instructions.
The table of instructions is the more important aspect of the machine. They specify for
any given state of the machine and value on the tape, what the machine should write on
5
the tape and where the machine should move to on the tape. This very general principle
deﬁnes all computations. There is no distinction made between hardware (a physical device
that performs computations) or software (a set of instructions to be run by a computing
device). Both can be made to perform the same task, however, hardware is typically much
faster when optimally designed then software, but in comparison hardware is very hard to
make. Software allows the massive generalizations of particular ideas and algorithms, where
as hardware suﬀers the opposite extreme. Our discussions will be limited to software, only
introducing hardware where necessary.
A simple example of a two state Turing machine is shown in Figure 2.1. In this
very simple Turing machine example, the machine performs no writing, and the instructions
change the state of the machine and move the machine. The lack of an instruction for a
possible combination of machine state (B) and tape value (0), causes the machine to stop.
This particular example does not do much of anything except demonstrate the
basic principles of a Turing machine. To demonstrate a Turing machines instruction set
for even simple operations (like multiplication or addition) would take a few pages, and
is beyond the scope here
1
. Once a useful set of instructions is given, we can collapse the
instructions into a single reference for another Turing machine to use. A function is now
born. To be a bit more concrete, a function is a reference to a set of independent instructions.
Of course, writing complex programs using just a Turing machine instruction set is
very hard and tedious. When computers ﬁrst were born, the Turing machine approach was
how computer programming was actually performed. One can easily see that we should be
able represent a function by a simple name (i.e. multiply), if we had some translator take
1
A good place to ﬁnd more Turing machine information, including a Turing machine multiplication
instruction set is at this web address http://www.ams.org/newinmath/cover/turing.html.
6
0 1 1 1 1 0 0
the tape
Our machine
Machine States: A, B
Instructions Set
machine state tape value action
A
A
B
B
0
1
0
1
move Right, go into state A
move Right, go into state B
move Right, go into state B
not defined
0 1 1 1 1 0 0
Start Machine State: A
0 1 1 1 1 0 0
Machine State: A
0 1 1 1 1 0 0
Machine State: B
0 1 1 1 1 0 0
Machine State: B
Halted...End
b)
a)
Figure 2.1: A two state Turing machine. The current machines position is represented by
the gray box, the tape inputs values can be 0 or 1, and the machine states can be A or
B. The instruction set is designed to stop because one of the four possible combinations of
states and inputs is undeﬁned.
2.1. DATA TYPES 7
our function name and write out the Turing machine equivalent, we could spend much less
time and eﬀort to get our computer to calculate something for us. A compiler is such an
entity. It uses a known language (at least known to the compiler, and learned by the user),
that when the compiler is run, translates the names into working machine instructions.
Compilers and their associated languages are called High Level Languages, because there is
no need for a user to write in the low level machine instruction set.
Programming languages can then be created from a set of translation functions.
Until the development of programming languages like C++, many of the older languages
(Fortran, Algol, Cobal) were only “words” to “machine–code” translators. The next level
of language would the function of functions. These would translate a set of functions into a
series of functions then to a machine code level. Such a set of functions and actions are now
referred to as a class or an object, and the languages C++ and Java are such languages.
The next level, we may think, would be an object of objects, but this is simple a generality
of an object already handled by C++ and Java. For an in depth history of the various
languages see Ref. [8]. For a history of C++ look to Ref. [9].
2.1 Data Types
Besides simple functions, high level languages also provide basic data types. A
data type is a collection of more basic data types, where the most basic data type for a
computer is a binary value (0 or 1), or a bit. Every other data type is some combination and
construction of the bit. For instance a byte is simple the next smallest data type consisting
of eight bits. Table 2.1 shows the data available to almost all modern high level languages.
2.2. THE OBJECT 8
Table 2.1: Basic High Level Language Data Types
Name Composition
bit None, the basic block
byte 8 bits
character 1 byte
integer 2 to 4 bytes
ﬂoat 4 bytes
double 8 bytes
The languages also deﬁne the basic interactions between the basic data types. For example,
most compilers will know how to add an integer and a ﬂoat. Beyond these basic types, the
compiler knows only how to make functions and to manipulate these data types.
In current versions of Fortran, C and most other modern languages, the language
also gives one the ability to create their own data types from the basic built in ones. For
example we can create a complex data type composed of two floats or two doubles, then
we must create the functions that manipulate this data type (i.e. addition, multiplication,
etc.).
Suppose we wish to have the ability to mix data types and functions: creation
of a data type immediately deﬁnes the functions and operations available to it, as well as
conversion between diﬀerent data types. These are what we referred to as objects and are
the subject of the next section.
2.2 The Object
Scientiﬁc computation has seen much of its life stranded in the abyss of Fortran.
Although Fortran has come a long way since its creation in the early 1950s, the basic
syntax and language is the same. Only the basic data types (plus a few more) shown in
Table 2.1 are allowed to be used, and creation of more complex types are not allowed.
2.2. THE OBJECT 9
The functions and function usage are typically long and hard to read and understand
2
. Its
saving grace is that it performs almost ideal machine translation, meaning it is fast (few
unnecessary instructions are used during the translation). Given the scientiﬁc need for
speed in computation, Fortran is still the choice today for many applications. However, this
all may change soon due to fairly recent developments in C++ programming paradigms.
2.2.1 Syntax
Before we can go any further, it is necessary to introduce some syntax. Throughout
this document, I will try to present actual code for algorithms when possible. As is turns
out, much of the algorithmic literature uses “pseudocode” to deﬁne the working procedures
for algorithms. Although this usually makes the algorithm easier to understand, it leaves
out the details that are crucial upon implementation of an algorithm. The implementation
determines the speed of the algorithms execution, and thus its overall usefulness. Where
appropriate, both the algorithmic steps and actual code will be presented.
The next several paragraphs will attempt to introduce the syntax of C++ as it
will be the implementation language of choice for the remainder of this document. It will
be short and the reader is encouraged to look towards an introductory text for more detail
(Ref. [10] is a good example of many). Another topic to grasp when using C++ is the
idea of inheritance. This is not discussed here, but the reader should look to Ref. [11] as
inheritance is an important programming paradigm. It will be assumed that the reader has
had some minor experience a very high level language like Matlab.
• The ﬁrst necessary fact of C++ (and C) is declaration of data types. Code Example
2.1 declares an integer data type, that can be used by the name myInt later on.
2
Look to the Netlib repository, http://netlib.org for many examples of what is claimed here.
2.2. THE OBJECT 10
Code Example 2.1 Integer declaration
int myInt;
• The deﬁnition of functions requires a return type, a name, and arguments where both
the return type and the arguments must be valid data types as shown in Code Example
2.2. In code example 2.3 the Return T is the return data type, Arg T1 through Arg TN
Code Example 2.2 Function declarations: general syntax
Return_T functionname(Arg_T1 myArg1, ..., Arg_TN myArgN)
are the argument data types. For example, in Code Example 2.3 is a function that
adds two integers.
Code Example 2.3 Function declarations: speciﬁc example
int addInt(int a, int b)
{ return a+b; }
• Pointers (via the character ‘*’ ) and references (via the character ‘&’) claim to
be what they say: Pointers point to the address (in memory) of the data type, and
references are aliases to an address in memory. The diﬀerence between them illustrated
in the example in Code Example 2.4.
• Creating diﬀerent data types can be performed using a class or struct. A complex
number data type is shown in Code Example 2.5. The above example shows the
syntax for both creation of the a data type and how to access its sub elements.
• Templates allow the programmer to create generic data types. For instance in the
class complex example in Code Example 2.5, we assigned the two sub elements to
a double. Suppose we wanted to create one using a float or an int. We do not
2.2. THE OBJECT 11
Code Example 2.4 Pointers and References
//declare a pointer
int *myPoinerToInt;
//assign it a value
//the ‘*’ now acts to extract the memory
// not the address
*myPoinerToInt=8;
//declare an integer
int myInt=4;
//this will print ‘‘4 8’’
cout<<myInt<<" "<<*myPoinerToInt<<endl;
//make out pointer above, point to this new integer
// using the reference
myPoinerToInt=&myInt;
//now when we change ’myInt’ BOTH objects will change
myInt=10;
//this will print ‘‘10 10’’
cout<<myInt<<" "<<*myPoinerToInt<<endl;
Code Example 2.5 Object declaration Syntax
class complex{
public:
//a complex number contains has two real numbers
double real;
double imag;
//The constructor defines how to create a complex number
complex():
real(0), image(0) {}
//The constructor defines how to create a complex number
//with input values
complex(double r, double i):
real(r), image(i) {}
};
//here we use the new data type
complex myCmx(7,4);
//this will print ‘‘7+i4’’
cout<<myCmx.real<<"+i"<<myCmx.imag<<endl;
2.2. THE OBJECT 12
wish to create a new class for each type, instead we can template the class as in Code
Example 2.6. In C++ we can template both classes and the arguments of functions.
Code Example 2.6 Template Objects
template<class Type_T>
class complex{
public:
//a complex number contains has two real numbers
Type_T real;
Type_T imag;
//The constructor defines how to create a complex number
complex():
real(0), image(0) {}
//The constructor defines how to create a complex number
//with input values
complex(Type_T r, Type_T i):
real(r), image(i) {}
};
//here we use the new data type
// use a double as the sub element
complex<double> myCmx(7,4);
//this will print ‘‘7+i4’’
cout<<myCmx.real<<"+i"<<myCmx.imag<<endl;
// use a int as the sub element
complex<int> myCmxInt(7,4);
//this will print ‘‘7+i4’’
cout<<myCmxInt.real<<"+i"<<myCmxInt.imag<<endl;
This template procedure allows the creation of a wide range of generic data types
and function that operate over a large range of data types without having to code a
diﬀerent function or object for each diﬀerent combination of data types. In Fortran,
one must code a diﬀerent function for each diﬀerent data type making the creation of
general algorithms tedious[12]. Given M data types, and N functions, using templates
can in principle reduce the O(MN) number of procedures in a Fortran environment
to O(N +M) procedures.
2.3. EXPRESSION TEMPLATES 13
Given those simple syntax rules, we can move forward to explain the object and
the power that resides in a templated object.
2.3 Expression Templates
2.3.1 Motivations
Until recently[13], C++ has been avoided for scientiﬁc computation because of an
issue with speed. We have shown how to create an object, but we can also create speciﬁc
functions, or operators, that deﬁne the mathematics of the object. Let us revisit the class
complex example and deﬁne the addition operator. We also must deﬁne the assignment
(‘=’) operator before we can deﬁne an addition operator as shown in Code Example 2.7.
Now we can use our addition operator to add two complex numbers. The addition operator
Code Example 2.7 Deﬁning operators
template<class Type_T>
class complex{
public:
//define the sub elements
....
//define the assignment operator
//an INTERNAL CLASS FUNCTION
complex operator=(complex a){ real=a.real; imag=a.imag;}
};
template<class Type_T>
complex<Type_T> operator+(complex<Type_T> a, complex<Type_T> b)
{
return complex<Type_T>(a.real+b.real, a.imag+b.imag);
}
(and any others we deﬁne) can be nested into a long sequence as shown in Code Example
2.9.
2.3. EXPRESSION TEMPLATES 14
Code Example 2.8 simple addition
complex<double> A(4,5), B(2,3), C;
C=A+B;
//this will print ‘‘6+i8’’
cout<<C.real<<"+i"<<C.imag<<endl;
Code Example 2.9 Single operations
complex<double> A(4,5), B(2,3), C;
C=A+BB+A;
//this will print ‘‘8+i10’’
cout<<C.real<<"+i"<<C.imag<<endl;
2.3.2 Stacks
We should take note as to what the compiler and the computer are doing when it
sees an expression like the one in Code Example 2.9. Initially the compiler will attempt to
translate our mathematical expression into a stack. A stack is a list with a last–in–ﬁrst–out
property. The order of the list is determined by the syntax, using standard mathematical
rules (e.g. items inside parentheses are treated ﬁrst, multiplication is performed before
addition, etc.). The expression will be parsed from the last element to the ﬁrst in the
sequence, B+A, then B(result of (B+A)), then A+(result of (B(result of (B+A)), ﬁnally
C=result of (A+(result of (B(result of (B+A))). Each step represents a stack step, and
can be best represented as a stack tree shown in Figure 2.2. After this stack is created, the
compiler writes the appropriate instruction set to complete the operation once the program
is run. When the program is run, the machine must go to the bottom of the stack and
perform each operation as it works its way up the stack tree. Another way to perform the
same operations shown in Code Example 2.9 is to follow the exact stack tree, in the code
itself as shown in Code Example 2.10.
It is then easy to see that in the process of using the operators we necessitate the
2.3. EXPRESSION TEMPLATES 15
A B
add
C=A+BB+A
B
subtract A
assign
C
Figure 2.2: A simple stack tree
Code Example 2.10 Code representation of a stack tree
complex A(4,5), B(2,3), C;
complex tmp1=B+A;
complex tmp2=Btmp1;
C=A+tmp2;
use of temporary objects. For individual data types (doubles, ﬂoats, ints, and our complex
example), there is no way around this fact
3
. But for arrays of values, we can potentially
create a much more optimal situation.
2.3.3 An Array Object and Stacks
First we shall deﬁne a templated Vector class so that we can continue our discus
sion. The vector class shown in Code Example 2.112.12 maintains a list of numbers and
deﬁnes appropriate operators for addition, multiplication, subtraction, and division of two
Vectors.
The code examples in Code Example 2.11 also gives the deﬁnitions for element
3
There is no easy way to see how such a stack tree can be simpliﬁed. However, the ever increasing
complexity of microchip architectures are actually creating new instruction sets that give the compiler the
ability to, for example, add and multiply two numbers under the same instruction as in a PowerPC chip. The
complex functions like sin and cos are now included on the microchips instruction set which then increase
the speed of the produced code by reducing the stack tree length.
2.3. EXPRESSION TEMPLATES 16
Code Example 2.11 a simple Template Vector class
template<class T>
class Vector{
private:
T *data_;
int len_;
public:
Vector():data_(NULL), len_(0){}
Vector(int len, T fillval=0)
{
data_=new T[len];
len_=len;
for(int i=0;i<len;++i)
{ data_[i]=fillval; }
}
//this is the ‘destructor’ or how we free the memory
// after we are done with the Vector
~Vector()
{ if(data_!=NULL) delete [] data_; }
Vector &operator=(Vector rhs)
{
if(data_!=NULL) delete [] data_;
data_=new T[rhs.size()];
len_=rhs.size();
for(int i=0;i<len;++i)
{ data_[i]=rhs(i); }
return *this;
}
T &operator()(int i){ return data_[i]; }
T &operator[](int i){ return data_[i]; }
int size(){ return len_; }
};
2.3. EXPRESSION TEMPLATES 17
Code Example 2.12 a simple Template Vector operations
template<class T>
Vector<T> operator+(Vector<T> a, Vector<T> b)
{
Vector c(a.size());
for(int i=0;i<len;++i)
{ c[i]=a[i]+b[i]; }
}
template<class T>
Vector<T> operator(Vector<T> a, Vector<T> b)
{
Vector c(a.size());
for(int i=0;i<len;++i)
{ c[i]=a[i]b[i]; }
}
template<class T>
Vector<T> operator/(Vector<T> a, Vector<T> b)
{
Vector c(a.size());
for(int i=0;i<len;++i)
{ c[i]=a[i]/b[i]; }
}
template<class T>
Vector<T> operator*(Vector<T> a, Vector<T> b)
{
Vector c(a.size());
for(int i=0;i<len;++i)
{ c[i]=a[i]*b[i]; }
}
2.3. EXPRESSION TEMPLATES 18
access (the operator()(int) and operator[](int) ) as wells as a way to determine how
long the Vector is (the int size() function). The destruction (the ~Vector()) function
is also important as it frees the memory used by the vector. Also note that in the examples
there are no error checking on the sizes of the vectors when we perform an operation. Such
checks are easy to implement, but add clutter to the code, so they will be left out here.
A simple expression using our new object is shown in Code Example 2.13. Using
Code Example 2.13 a simple vector expression
Vector<double> a(5,7), b(5,8), c(5,9), d(5,3);
d=c+b+ba;
our stack representation, we can also write the example in Code Example 2.13 as the stack
produced code as shown in Code Example 2.14. In the example in Code Example 2.14 we
Code Example 2.14 a simple vector expression as it would be represented on the stack.
Vector<double> a(5,7), b(5,8), c(5,9), d(5,3);
Vector<double> t1(5), t2(5), t3(5);
int i=0;
for(i=0;i<d.size();++i)
{ t1[i]=b[i]a[i]; }
for(i=0;i<d.size();++i)
{ t2[i]=b[i]+t1[i]; }
for(i=0;i<d.size();++i)
{ t3[i]=c[i]+t2[i]; }
for(i=0;i<d.size();++i)
{ d[i]=t3[i]; }
could have both saved the temporary vectors (t1, t2, and t3), as well as the ﬁnal assign
ment loop. In general, however, this optimization is not possible for the compiler to see, and
this example is an accurate representation of the expression d=c+b+ba. An experienced
programmer could easily reduce everything to a single loop requiring no temporary vectors
as shown in Code Example 2.15. This case is at least a factor of 3 faster then the previous
2.3. EXPRESSION TEMPLATES 19
Code Example 2.15 a simple vector expression in an optimal form.
Vector<double> a(5,7), b(5,8), c(5,9), d(5,3);
for(int i=0;i<d.size();++i)
{ d[i]=c[i]+b[i]+b[i]a[i]; }
case in Code Example 2.14 (it is a even faster the three because we did not have to create
the temporaries). It is for this reason that C++ has been avoided for scientiﬁc or other
numerically intensive computations. One may as well write a single function that performs
the speciﬁc optimal operations of vectors (or any other array type). In fact the Netlib
4
is
full of such speciﬁc functions.
2.3.4 Expression Template Implementation
A few years ago Todd Veldhuizen developed a technique that uses templates to
trick the compiler into creating the optimized case shown in Code Example 2.15 from a
simple expression like the one shown in Code Example 2.13[14]. This technique is called
expression templates. Because the technique is a template technique, it is applicable to
many data types without much alteration.
This trickery with templates began with Erwin Unruh when he made the compiler
itself calculate prime numbers[15]. He could do this because for templated objects to be
compiled into machine code, they must be expressed, or they must have a real data type
replace the template argument (as in our examples of using the Vector class with the
double replacing the class T argument). The code that generated the prime numbers can
be found in Appendix A. In fact Erwin showed that the compiler itself could be used as
Turing machine (albeit a very slow one).
Now we can describe the technique in painful detail. It uses fact that any template
4
See http://netlib.org
2.3. EXPRESSION TEMPLATES 20
augment must be expressed before it can be used. To allow a bit of ease in the discussion
we will assume that only one data type, the double, is inside the array object
5
.
We will restrict ourselves to the Vector, as most other data types are simply
extensions to a vector type. Second, in our discussions, we will restrict the code to the
addition operation, as other operations are easily implemented in exactly the same way. A
better deﬁnition of what we wish to accomplish is given below.
Given an arbitrary righthandside (rhs) of a given expression, a single element
on the lefthandside (lhs) should be able to be assignable by only one index
reference on the rhs.
This statement simply means that the entire rhs should be collapsible into one
loop. But the key is in the realization that we require the index for both the lhs and the
rhs. The beginning is already given, namely the operator()(int i) function shown in
Code Example 2.11. The remaining task is to ﬁgure out how to take an arbitrary rhs and
make it indexable by this operator.
We can analyze the inside of the operators in Code Example 2.12. Notice that
they are binary operations using a single index, meaning they require two elements to
perform correctly (the a[i] and b[i] with the index i). A new object can be created
that performs the binary operation of the two values a[i] and b[i] as shown in Code
Example 2.16. The addition operation has been eﬀectively reduced to a class, which means
the operation can be templated into another class. The reason why the apply function is
static
6
will be come apparent in the Code Example 2.17. The class, VecBinOp, in Code
Example 2.16 does not give us the single index desired. The class shown in Code Example
2.17 does. VecBinOp stands for a VectorVector binary operation. Note that the object is
5
We can perform more generic procedures if we use the typedef. A typedef is essentially a short cut to
naming data types. For instance if we had a data type that was templated like Vector<Vector<double> >
we could create a short hand name to stand for that object like typedef Vector<Vector<double> > VDmat;
6
A static function or variable is one that never changes from any declaration of the object.
2.3. EXPRESSION TEMPLATES 21
Code Example 2.16 A Binary operator addition class
class ApAdd {
public:
ApAdd() { }
static double apply(double a, double b)
{ return a + b; }
};
created by creating pointers to the input vectors not copying the vectors. This object takes
three template arguments, the two vector types and the operation class. One may wonder
Code Example 2.17 A Binary operator class
template<class V1, class V2, class Op>
class VecBinOp{
private:
V1 *vec1;
V2 *vec2;
public:
VecBinOp(V1 &a, V2 &b):
vec1(&a), vec2(&b){}
~VecBinOp(){ vec1=NULL; vec2=NULL; }
//requires ’Op::apply’ to be static
// to be used in this way
double operator()(int i)
{ return Op::apply(vec1(i), vec2(i)); }
};
why we templated the two vector class V1 and V2 as we know we are dealing with only
Vector<double> objects, the reasons for this will be clear below. Our object creates the
desired single index operator; however, we are far from ﬁnished. We could use the VecBinOp
alone, to create our new addition operator as shown in Code Example 2.18. This addition
operator did nothing more then make that code more complex, and actually slowed down
the addition operation because of the creation of the new VecBinOp object, and it does
not allow us to nest multiple operations (e.g. d=a+b+c) with any improvement. But we
are a step closer to realizing our goal and we wish to nest the template arguments and
2.3. EXPRESSION TEMPLATES 22
Code Example 2.18 A bad expression addition operator
template<class V1, class V2>
Vector &operator+(V1 &a, V2 &b)
{
Vector<double> out(a.size());
VecBinOp<V1, V2, ApAdd> addObj(a,b,ApAdd());
for(int i=0;i<a.size();++i)
{ out(i)=addObj(i); }
return out;
}
not the operations themselves. In order to nest the template operations, we need to create
another object that can maintain the binary operation in name only (e.g. VecBinOp<V1,
V2, ApAdd>), then use this name to pass to the next operation. Such an object is shown in
Code Example 2.19. This new object gives use the ability to pass an arbitrary expression
Code Example 2.19 A simple Vector Expression Object
template<class TheExpr>
class VecExpr{
private:
TheExpr *expr;
public:
VecExpr(TheExpr &a):
expr(&a){}
double operator()(int i)
{ return expr(i)); }
};
around as an object, but not evaluating the expression. The expression is only evaluated
when the operator()(int) is called. Thus we can delay the evaluation until have an
assignment. This object can then be passed back to the VecBinOp object as a template
argument (the reason why we left the ‘Vector’ template input for VecBinOp as a template
argument and not directly assigned it to the Vector). Now we can rewrite out addition
operator to simply pass back the VecExpr object as shown in Code Example 2.20. Now the
2.3. EXPRESSION TEMPLATES 23
Code Example 2.20 A good expression addition operator
template<class Expr_T1, Expr_T2>
VecExpr< VecBinOp<Expr_T1,Expr_T2, ApAdd> >
operator+(Expr_T1 &a, Expr_T2 &b)
{
return VecExpr<
VecBinOp<Expr_T1,Expr_T2, ApAdd>
>(a,b, ApAdd());
}
addition operation does not evaluate any arguments, it simply passes a staging expression
that we will need to ﬁnd another means to evaluate. This new addition operator can
be used for any combination of Vector or VecExpr objects. It can also be used for any
object as well, but it will more then likely give you many errors because of conﬂicts of data
types. For instance there is not operator()(int) deﬁned for a simple double number, thus
the compiler will give you an error. The best method around this problem is to create a
quadruple of operators using the more speciﬁc objects as shown in Code Example 2.21. Here,
we partially express the templates to show that they are only for Vector’s and VecExpr’s.
Now we have any rhs that will be condensed into a single expression. The ﬁnal step is the
evaluation/assignment. Since all the operators return a VecExpr object, we simply need to
deﬁne an assignment operator (operator=(VecExpr)). Assignments can only be written
internal to the class, so inside of our Vector class in Code Example 2.11 we must deﬁne this
operator as shown in Code Example 2.22. Besides the good practice checking the vector
sizes and generalization to types other then doubles, this completes the entire expression
template arithmetic for adding a series of vectors. It is easy to extend this same procedure
for the other operators (, /, *) and unary types (cos, sin, log, exp, etc.) where we would
create a VecUniOp object. Now that we have a working expression template structure, we
can now show in Figure 2.3 what the compiler actually performs upon compilation of an
2.3. EXPRESSION TEMPLATES 24
Code Example 2.21 A quadruple of addition operators to avoid compiler conﬂicts.
//Vector+Vector
template<class Expr_T2>
VecExpr< VecBinOp<Vector<double>,Vector<double>, ApAdd> >
operator+(Vector<double> &a, Vector<double> &b)
{
return VecExpr<
VecBinOp<Vector<double>,Vector<double>, ApAdd>
>(a,b, ApAdd());
}
//Vector+VecExpr
template<class Expr_T2>
VecExpr< VecBinOp<Vector<double>,VecExpr<Expr_T2>, ApAdd> >
operator+(Vector<double> &a, VecExpr<Expr_T2> &b)
{
return VecExpr<
VecBinOp<Vector<double>,VecExpr<Expr_T2>, ApAdd>
>(a,b, ApAdd());
}
//VecExpr+Vector
template<class Expr_T1>
VecExpr< VecBinOp<VecExpr<Expr_T1>,Vector<double>, ApAdd> >
operator+(VecExpr<Expr_T1> &a, Vector<double> &b)
{
return VecExpr<
VecBinOp<VecExpr<Expr_T1>, Vector<double>,ApAdd>
>(a,b, ApAdd());
}
//VecExpr+VecExpr
template<class Expr_T1, class Expr_T2>
VecExpr< VecBinOp<VecExpr<Expr_T1>,VecExpr<Expr_T2>, ApAdd> >
operator+(VecExpr<Expr_T1> &a, VecExpr<Expr_T2> &b)
{
return VecExpr<
VecBinOp<VecExpr<Expr_T1>,VecExpr<Expr_T2>,ApAdd>
>(a,b, ApAdd());
}
2.3. EXPRESSION TEMPLATES 25
Code Example 2.22 An internal VecExpr to Vector assignment operator
template<class Expr_T>
Vector &operator=(VecExpr< Expr_T > &rhs)
{
for(int i=0;i<size();++i)
{ this>operator(i)=rhs(i); }
return *this;
}
a b
add
d=c+b+b+a
b
add c
add
VecExpr<Vector<double>,
Vector<double>, ApAdd> >
VecExpr<Vector<double>,
VecExpr<Vector<double>,
Vector<double>, ApAdd> >,
ApAdd> >
VecExpr<Vector<double>,
VecExpr<Vector<double>,
VecExpr<Vector<double>,
Vector<double>, ApAdd> >,
ApAdd> >,
ApAdd> >
d(i)=expr3(i)
expr1=
expr2=
expr3=
expr3(i)=ApAdd::apply(c(i), ApAdd::apply(b(i), ApAdd::apply(b(i), a(i)) ) )
Figure 2.3: How the compiler unrolls an expression template set of operations.
expression such as d=c+b+b+a. This technique is not limited to vectors but also matrices
and any other indexable data type; all one has to do is change the operator() to the size
and the index type desired.
To show the actual beneﬁt of using the expression templates, Figure 2.4 shows a
benchmark for performing a DAXPY (Double precision A times X Plus Y) for a variety
of languages and programming techniques. You can see from the ﬁgure that the results
are comparable to a highly optimized Fortran version. The degree of matching depends
greatly on the compiler and the platform. The data in the ﬁgure is using gcc3.2.1 under
the Cygwin environment, under Linux (Red Hat 7.3) the results match even better. From
the ﬁgure it is apperent that if the size of the vector is known and ﬁxed before the code
2.3. EXPRESSION TEMPLATES 26
10
0
10
1
10
2
10
3
10
4
10
5
0
200
400
600
800
1000
1200
DAXPY(a+=const*b) 933 MHz Pentium III Xeon
Vector Length
M
F
L
O
P
S
fixed length Vector
expressionVector
F77 BLAS
nonexpression Vector
Figure 2.4: Double precision A times X Plus Y,DAXPY benchmarks in Millions of FLoating
Point operations per Second (MFLOPS) for a ﬁxed length expression template vector (‘*’),
the basic expression template vector (the box), the optimized Fortran 77 routine (‘o’) and
the normal non–expression template vector (‘x’). All code was compiled under the Cygwin
environment using gcc3.2.1.
2.4. OPTIMIZING FOR HARDWARE 27
is compiled, then we can perform even further optimizations using the template structures.
This technique is called meta–programming[16, 17, 18, 19] and exploits the compilers ability
to be a Turing machine as in the example in Appendix A.1.1. An example metaprogram
for unrolling ﬁxed length vectors is shown in Appendix A.1.2. More about template based
programming can be found in Ref. [20].
There are, however, situations where this simple expression unrolling does not
improve the speed. Such operations typically require the use of a workspace; they require
the use of temporary data structures. This type of optimization is the topic of the next
section.
2.4 Optimizing For Hardware
Expression templates provide a nice technique for reducing complex expressions
into a single expression allowing similare speed of a hand produced reduction, but still
maintain the powerful ease and readability of the produced code.
Consider the matrix multiplication
7
. Figure 2.5 depicts a representation of a ma
trix multiplication. To compute each element in the resulting matrix, an entire row of the
ﬁrst matrix and an entire column of the second matrix is needed. We can implement a
simple matrix multiplication via the Code Example 2.23. Assume that we have deﬁned a
matrix<T> class already, so we can perform some speed tests using our simple algorithm.
We will stick to square matrices (the most common case, and basically the only case in
NMR) for our speed test. The results on a 933 MHz Pentium III using gcc3.2.1 is shown
in Figure 2.6. A Basic matrix multiplication takes N
3
operations where the matrix is of
7
A tensor multiplication, not the element–by–element multiplication. The element–by–element case is
handled well by the expression templates.
2.4. OPTIMIZING FOR HARDWARE 28
C=A*B
C
M
A
N
M
K
B
N
*
=
K
C
2,4
A
2,1
A
2,2
....
B
1,4
B
2,4
....
Figure 2.5: A pictorial representation for the matrix–matrix tensor multiplication, C=A*B.
The sub box indicates the required elements from each matrix to compute one element in
the resulting matrix C.
Code Example 2.23 Simple tensor matrix multiplication
template<class T>
matrix<T> operator*(matrix<T> &a, matrix<T> b)
{
matrix<T> c(a.rows(), b.cols());
int i,j,k;
for(i=0;i<a.rows();++i){
for(j=0;j<b.cols();++j){
c(i,j)=a(i,0) * b(0,j);
for(k=1; k<b.cols();++k){
c(i,j)+=a(i,k) * b(k,j);
}
}
}
return c;
}
2.4. OPTIMIZING FOR HARDWARE 29
0 100 200 300 400 500 600
0
100
200
300
400
500
600
700
800
Double
NxN
M
F
L
O
P
S Basic
Matlab 5.3
ATLAS 3.4
0 100 200 300 400 500 600
0
100
200
300
400
500
600
700
800
complex<float>
NxN
M
F
L
O
P
S
Basic
ATLAS 3.4
0 100 200 300 400 500 600
0
100
200
300
400
500
600
700
800
complex<double>
NxN
M
F
L
O
P
S
Basic
Matlab 5.3
ATLAS 3.4
a) b)
c)
C=A*B Matrix Multiplication
933 MHz Pentium III
Figure 2.6: Speed in MFLOPS of a double(a), complex <ﬂoat> (b) and complex < double
> (c) matrix–matrix multiplication (C=A*B).
this size N N. A complex matrix multiplication is actually 4 separate noncomplex mul
tiplications (C
r
= (A
r
∗ B
r
), C
r
+ = (A
i
∗ B
i
), C
i
= (A
r
∗ B
i
), C
i
+ = (A
r
∗ B
i
)). Also
shown in Figure 2.6 is the matrix multiplication from another library called ATLAS[21] and
the algorithm inside Matlab version 5.3. The ATLAS library is enormously faster and ap
proaches the theoretical maximum for the 933 MHz processor of 933 MFLOPS. Matlab does
not have a float as a precision value, so those speed tests are not performed. In all cases
the ATLAS algorithm performs an order of magnitude better. How does ATLAS actually
perform the multiplication this much faster?
2.4. OPTIMIZING FOR HARDWARE 30
Registers
Memory
Arithmetic
Logical
Unit
Instruction
Memory
Data
Memory
Program
Counter
Figure 2.7: A generic computer data path.
The answer is buried deep in the computer architecture. So before we can continue
with the explanation, we must ﬁrst describe a generic computer. The discussion in the
following sections are not thorough by any means, they simply are designed to show how
one can manipulate programs to use the full potential of speciﬁc computer architectures. A
good place to learn more nasty details is from Ref. [22].
2.4.1 Basic Computer Architecture
The Data Path
To most programmers, the computer architecture is a secondary concern with
algorithms and designs taking precedent. However, Figure 2.6 demonstrates clearly that for
even simple algorithms, ignoring the architecture can reduce overall performance by orders
of magnitude. For numerically intense programs, this can be the diﬀerence in waiting days
as apposed to weeks for simulations to ﬁnish. To get the most optimum performance from a
computer architecture, we must know how the computer functions on a relatively basic level.
Figure 2.7 shows a simple generic layout of a Central Processing Unit’s (CPU) data path.
The data path is the ﬂow of a single instruction, where an instruction tells the computer
what to do with selected data stored in memory (things like add, multiply, save, load, etc.).
The data path shown in Figure 2.7 is based on the ﬁgures and discussion in Ref. [22].
Each element in the data path shown in Figure 2.7 can be implemented in a
2.4. OPTIMIZING FOR HARDWARE 31
variety of diﬀerent ways giving rise to the production of many diﬀerent brands (Intel, RISC,
PowerPC, etc.). The data path for each of the various CPU’s can be described in much the
same way based on the simple fact that both data and instructions can be represented as
numbers.
• Program Counter–This element controls which instruction should be executed and
takes care of jumps (function calls) or branches (things like if/else statements).
• Instruction Memory–This element holds the number representations of the various
instructions the program wishes to perform. The Program Counter then gives the
correct address inside the Instruction memory of the instruction to execute.
• Regsiter Memory–This element holds ‘immediate’ data. The immediate data is the
data closest to the Arithmetic Logical Unit (ALU) and is the only data that can have
any operation performed on it. Thus if a data element is stored in the Data Memory,
it must be placed into the Register Memory before an operation on it can occur.
• Arithmetic Logical Unit (ALU)–This element is the basic number cruncher of
the CPU. It typically takes in two data elements and performs a bit wise operation
on them (like add or multiply).
• Data Memory–The main data memory of a computer. This can be the RAM (Ran
dom Access Memory), a Hard disk, a network connection, etc.
Given a speciﬁc architecture, each of the elements in the data path above and the instruction
set are ﬁxed entities. A programmer cannot divide two numbers any faster then the data
path allows. The most important element of control for the programmer is in what order
speciﬁc instructions are given.
2.4. OPTIMIZING FOR HARDWARE 32
Programmer Control
There are a number of enhancements to the basic data path described above. In
almost every modern processor today there are numerous other hardware additions.
• pipelines–This enhancement allows the next instruction to be executed before the
previous one has ﬁnished. For instance while one instruction is in the ALU, another
can be accessing the Register Memory.
• caches–The closest memory to the ALU is the fastest memory, caches provide various
levels inside the Data Memory that are closer to the ALU, the fastest being closer to
the ALU, the slowest farthest away.
• Single Instruction Multiple Data (SIMD)–This is called more generically vector
processing where more then two data elements can be operated on in one ALU oper
ation. Thus we can add 4 ﬂoating point number to 4 another in a single instruction
rather then the usual method of 4 instructions for each addition of two ﬂoats.
The above list is only partial, but they are the three major features available to a program
mer to enhance the speed of a calculation.
Pipelining is easily described in the context of loop unrolling. Many of you may
have noticed that in certain codes that there is typically a 4fold unrolling of for/do/while
loops (see Code Example 2.24). This 4fold unrolling may look simply like more typing and
added confusion about the algorithm, but this is in fact taking advantage of pipe lining
on the processor. In the not unrolled case, the for condition (i<16) must be evaluated
each time before continuing, which is an action that is hard to pipeline because of the
dependence on a condition. For the 4fold unrolled case, not only can each of the four
2.4. OPTIMIZING FOR HARDWARE 33
Code Example 2.24 A simply loop unrolling to a 4 fold pipe line.
//length 16 vectors
Vector<double> A(16), B(16), C(16);
//a standard for loop
for(int i=0;i<16;++i){
C[i]=A[i]+B[i];
}
//a ‘loopunrolled’ loop
for(int i=0;i<16;i+=4){
int i2=i+1,i3=i+2,i4=i+3;
C[i]=A[i]+B[i];
C[i2]=A[i2]+B[i2];
C[i3]=A[i3]+B[i3];
C[i4]=A[i4]+B[i4];
}
operations be pipelined, but the condition testing is reduced by a factor of four. Figure
2.8 shows a pictorial representation of the data path as the loop shown in Code Example
2.24 is run. Some compilers (namely the GNU compiler) perform this sort of loop unrolling
automatically when called with optimizations, so writing the fully unrolled loop of the type
shown here are becoming a thing of the past. However, if there are more complex data
types in the loop or even other branch conditions, the harder it becomes for the compiler
to unroll them eﬀectively, so having a good picture of pipelining is still necessary to achieve
optimal throughput.
SIMD optimizations are highly system speciﬁc and until recently were only avail
able in super computers like Cray system machines. In recent years, consumer CPUs now
have these instructions. These instructions act on vectors worth of data at a time, rather
then just two elements at a time. They require both special data types and special CPU
instructions. Figure 2.9 shows pictorially how a 128 bit SIMD register can be thought of
as 4, 32 bit data values. Table 2.2 lists a few of the basic CPUs and there available SIMD
2.4. OPTIMIZING FOR HARDWARE 34
Registers
Memory
Arithmetic
Logical
Unit
Instruction
Memory
Data
Memory
Program
Counter
Loop Unrolled
C[i]=A[i]+B[i]
{
A
{
B
{
C
{
D
{
E
Operation
time
C[i3]=A[i3]+B[i3]
A B C D E
A B C D E
A B C D E
A B C D
Loop Standard
C[i]=A[i]+B[i]
Test i<16
Operation
time
i=i+1
A B C D E
A B
A
wait until test is finished
.
.
.
.
.
.
C[i4]=A[i4]+B[i4]
C[i2]=A[i2]+B[i2]
Figure 2.8: Pipe lines and loop unrolling
data types.
Programming using the SIMD types is almost never portable to other CPUs. It
may be up to the compiler to attempt to use the SIMD where it can, but currently most
compilers are not able to optimize for these registers. As a result programming using SIMD
Table 2.2: SIMD registers available of common CPUs
Architecture SIMD size number of common data types
Intel Pentium II MMX 64 bit 4 ints (only int)
Intel Pentium III SSE1 64 bit 4 ints, 2 ﬂoats
Intel Pentium IV SSE2 128 bit 8 ints, 4 ﬂoats
AMD K5 3Dnow! 64 bit 4 ints, 2 ﬂoats
AMD K6 3Dnow2! 128 bit 8 ints, 4 ﬂoats
Motorola G4 128 bit 8 ints, 4 ﬂoats
Cray J90 64 bit 4 ints, 2 ﬂoats
Fujitsu VPP300 2048 bit 128 ints, 64 ﬂoats
2.4. OPTIMIZING FOR HARDWARE 35
Operation
32 bit data
32 bit data
32 bit data
32 bit data
32 bit data
32 bit data
32 bit data
32 bit data
32 bit data
32 bit data
32 bit data
32 bit data
Figure 2.9: A 128 bit SIMD registers made of 4–32 bit data values
tends to be limited to a speciﬁc CPU and up to the programmer.
The ﬁnal optimizing technique involves caching. Caching turns out to be one of
the more important aspect in optimizing for modern CPUs. The reason for this is based
on the ever growing speed diﬀerence between memory access and CPU clock speeds. For
instance a 2GHz Pentium IV processor can only access the main data memory (RAM) at
rate less then 400 MHz, meaning that while the CPU waits for the data element to arrive
from memory, over 5 CPU cycles were wasted doing no work. In actuality the number is
much higher because the data element must be found in RAM then sent back.
For large continuous data structure like vectors or matrices, if each element took
multiple cycles simply to retrieve and save, calculations would be exceedingly ineﬃcient.
Caches, however, provide a method to increase performance using the spatial and temporal
locality of a program. This simply means that data just accessed will probably be accessed
again soon, and more then likely, the data next to the one just accessed will also be accessed
soon. Thus caches tend to load blocks of memory at a time with the hope that the data
elements within the block will also be used. Figure 2.10 shows the various levels of caching
2.4. OPTIMIZING FOR HARDWARE 36
Register
Memory
L1 Cache
8128 bytes
832 kbytes
L2 Cache 324096 kbytes
RAM 0.012 Gbytes
Figure 2.10: Cache levels in modern Processors
available to most computers today. Level 1 (L1) cache is the smallest ranging is size from
8 kb64 kb but is the fastest with access times very close to the internal CPU Register
Memory. Level 2 (L2) caches range in size from 32 kb  4 Mb and is much slower then the
L1 cache with access time about a fact of 25 more then the L1 cache. Some computers
provide Level 3 cache, but these are few. The next level is the actual RAM with the slowest
access times but is the largest.
To make software as fast as possible, careful management of the caches must be
maintained. If a data element is not in the cache then we call this a miss, if it is in the
cache we call this a hit. Our desire is to minimize misses. A miss can cost diﬀerent amounts
depending on which cache level misses. If the data is not in the L1 cache, the L2 cache is
checked, then the RAM. Because the L2 cache is much larger we can place much more data
(i.e. the entire vector or matrix of interest) here initially, then place smaller data chunks
inside the L1 cache as needed. The key is to do optimal replacements of the block inside
the L1 cache. Simply meaning that when we ﬁll the L1 cache, we only want to operate on
those elements, then place the entire block back to the next level and retrieve a new block.
This avoids many as many misses as possible.
2.4. OPTIMIZING FOR HARDWARE 37
2.4.2 A Faster Matrix Multiplication
We can now develop a method to improve the matrix multiply. We will do this
in a sequential manner. The ﬁrst step is to look at the loops in Code Example 2.23. Here
we can simply rearrange the loop such that the most accessed element c(i,j) is in the
innermost loop as in Code Example 2.25. Here the indexes i,j,k have been ﬂipped. The
Code Example 2.25 Simple tensor matrix multiplication with loop indexes rearranged.
template<class T>
matrix<T> operator*(matrix<T> &a, matrix<T> b)
{
matrix<T> c(a.rows(), b.cols());
c=0; //fill with zeros
int i,j,k;
for(k=0;k<b.cols();++k){
for(j=0;j<b.cols();++j){
for(i=0; i<a.rows();++i){
c(i,j)+=a(i,k) * b(k,j);
}
}
}
return c;
}
GNU compiler will rearrange the loops automatically as shown, so we cannot show the
improvement in MFLOPS for this particular optimization.
The loop unrolling technique discussed above is also performed by the GNU com
plier and even better then by hand as it will unroll the higher level loops also. Here we
demonstrate its eﬀect for completeness sake. In Code Examples 2.26 we ﬁnd a partially
unrolled loop. I found that using ﬁve fold unrolling was a bit better then the four fold
unrolling on the 933 MHz Pentium III. The comparison with the Code Example 2.25 is
shown in Figure 2.11.
The next level of optimization would be to make sure the L2 cache is completely
2.4. OPTIMIZING FOR HARDWARE 38
Code Example 2.26 Partial loop unrolling for the matrix multiply.
matrix<T> mulmatLoopUnroll(matrix<T> &a, matrix<T> &b)
{
int i,j,k, leftover;
matrix<T> c(a.rows(), b.cols(), 0);
static int Unrolls=5;
//figure out how many do not fit in the Pipeline unrolling
leftover=c.cols() % (Unrolls);
for(k=0;k<c.rows();++k){
for(j=0;j<c.cols();++j){
i=0;
//do the elements that do not fit
//in the unrolling
for(;i<leftover;++i)
{ c(i,j)+=a(i,k) * b(k,j); }
//do the rest
for(;i<c.cols();i+=Unrolls){
//avoid calculating the indexes twice
int i1=i+1, i2=i+2, i3=i+3, i4=i+4;
//avoid reading the b(k,j) more then once
T tmpBkj=b(k,j);
//read the a(i,k)’s first into the registers
T tmpAij=a(i,k);
T tmpAi1j=a(i1,k);
T tmpAi2j=a(i2,k);
T tmpAi3j=a(i3,k);
T tmpAi4j=a(i4,k);
c(i,j)+=tmpAij * tmpBkj;
c(i1,j)+=tmpAi1j * tmpBkj;
c(i2,j)+=tmpAi2j * tmpBkj;
c(i3,j)+=tmpAi3j * tmpBkj;
c(i4,j)+=tmpAi4j * tmpBkj;
}
}
}
return c;
}
2.4. OPTIMIZING FOR HARDWARE 39
0 100 200 300 400 500 600
4
6
8
10
12
14
16
18
20
22
C=A*Bdouble*double 933 MHz Pentium III, Cygwin, GNU gcc
NxN
M
F
L
O
P
S
Basic
Partial Loop unrolling
Figure 2.11: MFLOPS of a matrix multiplication: comparison of Code Example 2.25 (solid
line) and Code Example 2.26 (‘*’).
full. For large matrices we would have to divide the matrix into sub matrices that ﬁt into
the L2 cache. For a L2 cache of 1 Mb, we can ﬁt approximately 125000 doubles. Of course
we have 3 matrices to consider so that would drop us to ~42000 doubles per matrix. This
assumes that we would have the total L2 cache, but we will need some space for the indexes
and other functional elements as well as operating system elements and other programs
running as well as the required instruction set. We will halve this number to 20000 doubles
as the L2 block size. The largest square matrix that will ﬁt into a 20000 data chunk is
~140x140. If one looks back to Figure 2.6a and Figure 2.11 you can see the unoptimized
multiply has a performance drop when the matrix size is over 160. This is a result of the
L2 cache being unoptimally used on the 1Mb L2 cache of the Pentium III.
In order to realize our blocking technique, we must copy sub–matrices of the larger
matrix into smaller matrices that ﬁt into the L2 cache. Each sub–matrix is then added to
2.4. OPTIMIZING FOR HARDWARE 40
50 100 150 200 250 300 350 400 450 500 550
0
20
40
60
80
100
120
C=A*Bdouble*double 933 MHz Pentium III, Cygwin, GNU gcc
NxN
M
F
L
O
P
S
Partial Loop unrolling
Partial Loop unrolling+L2 cache Blocking
Partial Loop unrolling+compiler Optimizations
Partial Loop unrolling+L2 cache Blocking+compiler Optimizations
Figure 2.12: MFLOPS of a matrix multiplication: comparison of simple loop unrolling Code
Example 2.26 (solid line) and the L2 cache blocking with unrolling (see Appendix A.1.3).
Compiler optimizations increase the total MFLOPS (line with dots) and the beneﬁt of L2
blocking (dashed line).
the output matrix. This is same as performing a normal matrix multiply as in Figure 2.5 if
we consider each box a sub matrix rather then one element.
The code for performing the L2 cache blocking is shown in Appendix A.1.3 because
it is a bit long. Figure 2.12 shows the improvement over not blocking the L2 cache. In general
the L2 cache is still relatively slow when compared to the L1 cache and thus the speed
enhancement is small (a few MFLOPs here). We can turn on the compiler optimizations to
see a much more dramatic eﬀect of L2 blocking and this is also shown in Figure 2.12.
The next level of optimization is L1 cache blocking. This turns out to be a very
2.4. OPTIMIZING FOR HARDWARE 41
hard problem to optimize for many reasons. Each hardware has a diﬀerent memory structure
for the L1 cache that needs to be matched exactly to improve performance. Because the
sub matrices need to be much smaller to ﬁt into the L1 cache size, there is a large penalty
for copying the sub matrix if the copying is not optimized. Being in the L1 cache does
not guarantee the highest performance, because the register memory must then be used
eﬀectively. Finally we would need to program in assembly to use the SIMD extensions.
All of these factors can then have varying degrees of pipe line optimizations. Finding an
optimal solution would take a single person much tinkering with all of them, or a computer
search algorithm to ﬁnd the best one. ATLAS[21] performs this search eﬀectively and this
is why its performance in Figure 2.6 is much higher then anything we have shown here.
Conclusions
In this chapter I have given you the tools and methods for constructing highly op
timal computer algorithms. There are two essential levels. The ﬁrst, expression templates,
involves an abstract software interface to reduce the number of operations on a stack while
still maintaining a highlevel of simplicity for the user. The second, hardware optimizations,
is an art unto itself and should be used where we cannot rely on the compiler to generate
the optimized code. Because each hardware is diﬀerent, optimizing for one architecture will
most deﬁnitely not work on another. For this reason, there is not generic abstraction like the
expression template technique. However, both code abstraction and hardware optimization
are necessary for fully optimal solutions.
42
Chapter 3
NMR Forms
Before we can lay down the foundation of a complete NMR tool kit, we need to
know what sort of mathematics we are dealing with. What kind of interactions must we
take into account to achieve a real physical model? All of NMR, like most physical systems,
can be reduced to two extremes, the Quantum and the Classical. Both are treated in fun
damentally diﬀerent ways both mathematically and numerically. Since classical mechanics
is usually a bit more intuitive, we shall start there.
3.1 Classical Mechanics
The basic tenant of the classical description of NMR is a magnetic dipole inter
acting with some external magnetic ﬁeld. As we will show later there are many ‘external
magnetic ﬁelds’. The basic interaction is easily described by a ﬁrst order diﬀerential equa
tion
dM
dt
= −γMB. (3.1)
Most of the world calls this the Bloch Equation [23]. Here M is the magnetic
3.2. BLOCH EQUATION MAGNETIC FIELDS 43
moment (sometimes called µ) and is a 3 dimensional vector (a coordinate). Various ortho–
normal representations can be given to the three components, here, we will stick to simple
Cartesian
M = (M
x
, M
y
, M
z
). (3.2)
Mis usually considered a bulk property: the entire macroscopic sample of spins add together
to produce M. An individual spin’s magnetic moment will be called µ, and a normalized
bulk magnetic moment will be called little m, m. B is also a 3vector and represents the
external magnetic ﬁeld. γ is a nuclear spin’s gyromagnetic ratio which converts the ‘Gauss’
or ‘Tesla’ of M B to a frequency. The gyromagnetic ratio is spin speciﬁc. The cross
product is representative of a torque which the magnetic moment feels from the external
ﬁeld.
3.2 Bloch Equation Magnetic Fields
We are interested in any and all magnetic ﬁelds of the most general form B(r, t),
a magnetic ﬁeld as a function of position and time.
Oﬀsets
The ﬁrst sets of ﬁelds of interest are those that we can apply to the system using
electro–magnets, superconducting magnets, or simple coils. In most NMR circumstances
we apply a very large static magnetic ﬁeld along the z–axis (actually we deﬁne our z–axis
about this large ﬁeld). Typically superconducting ﬁelds are of the order of Telsa or higher,
and our Bloch equation is
dM
dt
= −γMB
z
. (3.3)
3.2. BLOCH EQUATION MAGNETIC FIELDS 44
This simple equation gives us a three equations of motion
dM
x
dt
= −γB
z
M
y
(3.4)
dM
y
dt
= γB
z
M
x
(3.5)
dM
z
dt
= 0 (3.6)
If we specify an initial condition with M(0) = (M
o
x
, M
o
y
, M
o
z
), we get the analytic solution
of
M
x
(t) = M
o
x
cos(γB
z
t) −M
o
y
sin(γB
z
t) (3.7)
M
y
(t) = M
o
y
cos(γB
z
t) +M
o
x
sin(γB
z
t) (3.8)
M
z
(t) = M
o
z
(3.9)
The magnetization therefore spins around the z–axis. The γB
z
term is very large. For a
B
z
= 1 Telsa we get a oscillation frequency of 42.58 MHz for a proton. To solve such a fast
solution is akin to suicide when a typical NMR experiments can last on order of seconds. A
solver would be required to evaluate millions of functions, making it very ineﬃcient. Given
that the ﬁeld is static, we can go into the rotating frame of the ﬁeld. That is, spin ourselves
at the in the opposite direction, but at the same rate as the magnetization is spinning.
The physics should not change in this new frame but we need to add this new term to the
equations of motion
dM
dt
= MΩ
r
+
_
dM
dt
_
r
. (3.10)
Where Ω
r
is the rotational frequency of the rotating frame and
_
dM
dt
¸
r
is the term for how
M appears in the rotating frame. We wish to satisfy the condition,
_
dM
dt
_
r
= 0. (3.11)
3.2. BLOCH EQUATION MAGNETIC FIELDS 45
In other words, we want everything to be still in the rotating frame. Comparing Eq. 3.11
and Eq. 3.10 we see we need to rotate counter to the B
z
ﬁeld, which give us
Ω
r
= −γB
z
_
dM
dt
¸
r
= −γMB
z
−γM (−B
z
).
= 0
(3.12)
As you can see we have gone into a frame where nothing evolves, a rather boring
frame. So suppose that our magnetization feels a slightly diﬀerent ﬁeld. Call the diﬀerence
∆B then our applied ﬁeld, we then must add a term that describes this new oﬀset
dM
dt
= −γM (B
z
+∆B) (3.13)
Looking at the 3–vector of equations of motion,
dMx
dt
= −γ((B
z
+ ∆B
z
)M
y
− ∆B
y
M
z
)
dMy
dt
= γ((B
z
+ ∆B
z
)M
x
− ∆B
x
M
z
)
dMz
dt
= −γ(∆B
y
M
x
− ∆B
x
M
y
)
(3.14)
If we assume that B
z
>> ∆B
i
by orders of magnitude, then the only terms that contribute
to the observable evolution will be terms with B
z
, thus eliminating any solo ∆B
i
terms in
our equations of motion. This approximation is sometimes called ﬁrst order perturbation
theory, where the only terms that remain in a small perturbation are the ones parallel to
the axis of the main interaction. We will also call this truncation of an interaction because
we essentially drop some terms from the interaction. If we then apply to the rotating frame
to this reduced form we get
_
dMx
dt
¸
r
= −γ∆B
z
M
y
_
dMy
dt
_
r
= γ∆B
z
M
x
_
dMz
dt
¸
r
= 0.
(3.15)
3.2. BLOCH EQUATION MAGNETIC FIELDS 46
As you can see we are back to the form of the equation in Eq. 3.4 except now, the ∆B
z
terms are much smaller: anywhere from Hz to kHz. In the rotating frame a small oﬀset
from the main magnetic ﬁeld will appear to oscillate about the main magnetic ﬁeld axis.
If we cannot assume that B
z
>> ∆B
i
, then we must use the full form of the
equations of motion in Eq. 3.14. This does have an analytic solution but it is a bit messy
to write here. Furthermore if the main static applied ﬁeld is not along the z–axis then we
still can reduce the equations of motion to the form shown in Eq. 3.3 and we can use the
technique in section 4.1.1 to solve the problem.
Magnetic Pulses
Magnetic pulses are how one can manipulate the magnetization. I describe them
here as magnetic pulses, instead of the usual ‘Radio Frequency Pulses’ because the ‘Radio
Frequency’ applies only when there is a large Telsa external ﬁeld already applied on the
system. In general a magnetic pulse is similar to an oﬀset with the exception that it
is applied along an arbitrary direction and for some length of time. Oﬀsets are usually
independent of time.
If the sample is not under the inﬂuence of a large external ﬁeld, then any directed
DC (Direct Current) pulse will behave as the external ﬁeld and the spins will evolve under
that ﬁeld according to the same equations as section 3.2. If we are under the inﬂuence of a
large external ﬁeld, a relatively weak DC pulse (all one can muster experimentally) will do
nothing unless applied along the same axis as the main ﬁeld, as we showed in section 3.2.
All we would observe then is a larger oﬀset. So we need some other way to use an applied
ﬁeld to give us some control.
First lets assume that we can make our applied ﬁeld time dependant. We will call
3.2. BLOCH EQUATION MAGNETIC FIELDS 47
this ﬁeld B
1
by convention. Then our equation of motion in the nonrotating frame become
dM
dt
= −γM (B
z
+B
1
(t)). (3.16)
We wish to go into the rotating frame to make things numerically simple, but now we have
an added complication. We wish that the rotated B
1
(t) (calling it B
r
1
(t)) appear in the
rotating frame and not become truncated. We have already made the assumption that our
B
z
was much larger then anything else, so our rotating frame will still be −γB
z
giving our
rotating frame equation of motion as
_
dM
dt
_
r
= −γM (B
r
1
(t)). (3.17)
Now we can express the nonrotating frame B
1
ﬁeld as the rotating ﬁeld multiplied by the
reverse time dependant rotation around the z–axis.
B
1
(t) = RB
r
1
(t) (3.18)
where R is a rotation matrix is given by the solution to
dˆ r
dt
= ˆ r Ω
z
, the solution given in
Eq. 3.7 with M → r and B
z
→ Ω
z
.
R =
_
_
_
_
_
_
_
_
cos(Ω
z
t) sin(Ω
z
t) 0
−sin(Ω
z
t) cos(Ω
z
t) 0
0 0 1
_
_
_
_
_
_
_
_
(3.19)
thus
B
1
(t) =
_
_
_
_
_
_
_
_
B
r
1,x
(t) cos(Ω
z
t) +B
r
1,y
(t) sin(Ω
z
t)
B
r
1,y
(t) cos(Ω
z
t) −B
r
1,x
(t) sin(Ω
z
t)
B
r
1,z
(t)
_
_
_
_
_
_
_
_
(3.20)
So we see that in order for the external applied pulse to remain in the rotating frame, it
must be rotating at the same frequency as the rotating frame (i.e. a resonance condition).
3.2. BLOCH EQUATION MAGNETIC FIELDS 48
We could have intuitively guessed this result, but the result for oﬀ axis rotating frames and
other time dependant interactions, which give much more complicated expressions, can all
be derived the same way as this example.
This most typical magnetic pulse NMR uses is one that is constant in the rotating
frame, thus B
r
1
(t) = B
r
1
. The nonrotating frame from this point on will be called the lab
frame. In the lab frame, we still have a time dependence of Ω
z
t. In the rotating frame a
perfectly resonant pulse is typically applied perpendicular to the z–axis, and can reside any
where in the xy–plane. We represent this in the rotating frame as
B
1
= (B
1x
cos(φ), B
1y
sin(φ), 0) (3.21)
where φ is a phase factor. If we move oﬀ resonance, the B
1
vector points out of the xy–plane
thus introducing an extra z term
B
1
= (B
1x
cos(φ), B
1y
sin(φ), B
1z
) . (3.22)
Shaped pulses introduce time dependent amplitudes (B
1i
= B
1i
(t)) or phase factors (φ =
φ(t)) or both.
Gradients
Gradients can be thought of as a combination of magnetic pulses with a spatial
dependence. A gradient magnetic ﬁeld will be called B
g
.
B
g
= B
g
(r, t) (3.23)
Because they are spatially varying, the quantity of interest is its derivative with
respect to the spatial coordinates which have 9 components. The result is the gradient
3.2. BLOCH EQUATION MAGNETIC FIELDS 49
tensor
G =
_
_
_
_
_
_
_
_
δBgx
δx
δBgy
δx
δBgz
δx
δBgx
δy
δBgy
δy
δBgz
δy
δBgx
δz
δBgy
δz
δBgz
δz
_
_
_
_
_
_
_
_
=
_
_
_
_
_
_
_
_
G
xx
G
xy
G
xz
G
yx
G
yy
G
yz
G
zx
G
zy
G
zz
_
_
_
_
_
_
_
_
. (3.24)
In a high magnetic ﬁeld along the z–axis, the only components of the gradient tensor that
contribute to any observed eﬀect are the terms along the z direction
B
hf
g
= (0, 0, B
gz
). (3.25)
If we apply a linear gradient (the most common NMR situation) then
δB
gi
δj
= const = G
ij
(3.26)
and we can get the total ﬁeld along the z–axis via a sum of all the z derivatives times its
position
B
hf
gz
= G
iz
• r = G
xz
∗ x +G
yz
∗ y +G
zz
∗ z (3.27)
As you can see this simply acts like a spatial dependent oﬀset. If we are not in a high ﬁeld
the entire tensor must be used; furthermore, the simple formula in Eq. 3.27 is not valid for
nonlinear gradients.
Relaxation
Relaxation itself is not a ‘magnetic ﬁeld’ but more a heuristic addition to the
equations of motion. There are two fundamentally diﬀerent forms of relaxation. The ﬁrst
occurs from energy transfer from the system we are interested in to a system outside of
our control (usually called the lattice). This form is usually called longitudinal relaxation
or T
1
relaxation. This phenomenon occurs in almost every physical system where we must
separate control to the system of interest from the outside world. This basic relaxation is
3.2. BLOCH EQUATION MAGNETIC FIELDS 50
the driving force that drives the dynamics of the system back to it equilibrium condition at
some rate, 1/T
1
. If our equilibrium condition is M
o
= (M
o
x
, M
o
y
, M
o
z
), then at any given
time, relaxation will move the magnetization back towards this vector. Thus we have a new
term in the equation of motion deﬁned as
dM
dt
=
1
T
1
(M
o
−M(t)). (3.28)
In NMR there are many ways to calculate T
1
based on the system of study, the
reader is referred to Ref. [24] for more information and the various equations. For many
computational studies, all we need to know is this value. The most common case in NMR
is the high ﬁeld case, where M
o
= (0, 0, M
o
z
), so T
1
relaxation is only applicable to the z
part of our equations.
The second form of relaxation is usually an internal phase disturbance relaxation.
In a many body system there are slight diﬀerences in the local environments of each in
dividual spin cause slightly diﬀerent evolutions between them. In a bulk sample (where
we have an Avogadros number of spins) this eﬀect manifests itself as a dephasing of any
previously inphase magnetization. This type of relaxation is typically called transverse or
T
2
relaxation. Unlike T
1
relaxation, this interaction can be reversed because it is still within
our system (under our control). The reversibility or irreversibility of the relaxation mecha
nism is what deﬁnes T
2
from T
1
. It is called transverse relaxation because it acts in plane
perpendicular to the equilibrium condition. Because it acts on the plane perpendicular
to the equilibrium condition, we have to rotate from this axis to the perpendicular plane.
If we remain in our Cartesian basis, then we can get two angles from the z–axis to unit
3.2. BLOCH EQUATION MAGNETIC FIELDS 51
equilibrium vector (
ˆ
M
o
= M
o
/ M
o
), θ, and the rotation about the xy–plane, φ as
θ = arccos
_
ˆ
M
o
z
_
φ = arctan
_
M
o
x
M
o
y
_
(3.29)
T
2
does not ‘return’ magnetization to equilibrium, it simply removes magnetization, so our
equation of motion should look something like
dM
dt
=
−1
T
2
C M(t) (3.30)
where C is the rotation matrix to take us into the plane perpendicular to the equilibrium
axis.
C =
_
_
_
_
_
_
_
_
cos(φ) sin(φ)cos(θ) 0
cos(φ)cos(θ) sin(φ) 0
0 sin(θ) 0
_
_
_
_
_
_
_
_
. (3.31)
To get C we can ﬁrst assume that our plane of interest is the xy–plane, then rotate the
x and y axis to the
ˆ
M
o
axis. For those of you that are paying attention, to accurately
describe a three dimensional rotation, we need three angles, not just two. However, the
third angle here would describe the relative rotation of a vector in that plane perpendicular
to M
o
. Luckily for us, this third angle is irrelevant as it would place the T
2
relaxation along
a speciﬁc axis, where it actually is directionally independent in that plane. I say ‘luckily’,
because there is no way for us to get this third angle.
The standard high ﬁeld NMR case of M
o
= (0, 0, M
o
z
), leads us to the normal
form of the T
2
relaxation equations
dM
dt
=
−1
T
2
(M
x
(t), M
y
(t), 0) . (3.32)
3.2. BLOCH EQUATION MAGNETIC FIELDS 52
z
x
y
a) b)
Figure 3.1: The magnitude of the dipole ﬁeld in no static external ﬁeld (a), and in a high
magnetic ﬁeld along ˆ z (b). Each shell represents B
D
• B
D
at r = 0.7..1 in 0.1 steps and
where µ = (0, 0, 1/µ
o
).
Dipole Interaction
The dipole interactions is one of the most important to the ﬁeld of NMR, for in
this one interaction we have a method for determination of distances between atoms, for
simplifying (and complicating) spectra, and basically adding a second dimension on to our
normal 1D oﬀset interaction. It is also one of the chief mechanisms for T
2
type relaxation.
The interaction is a spinspin interaction: the dipolar ﬁeld on one spin is felt as a magnetic
ﬁeld by its neighbor. In its most general form the dipolar ﬁeld, B
D
, at position r from a
single spin is given by
B
D
(r) =
µ
o
4π
3(µ • ˆr) ˆr −µ
(r • r)
3/2
(3.33)
where µ
o
is the permeability of free space (12.566370614 ∗ 10
−7
T
2
m
3
/J). The dipolar ﬁeld
from a single magnetic moment µ at r is proportional to the cube of the distance away from
the spin. If we are not in a high ﬁeld, this give us the ‘dumbbell’ picture of the magnetic
ﬁeld as shown in Figure 3.1a. To get the images in Figure 3.1, we have to ﬁrst transform
3.2. BLOCH EQUATION MAGNETIC FIELDS 53
the spherical basis, and choose a direction for µ. Here we choose µ = (0, 0, µ
z
), to get the
following equations
B
D
x
(r) =
µoµz
4π
_
3ˆ xˆ z
2r−r
i

3
_
=
µoµz
4π
_
3 cos φsin θ cos θ
r−r
i

3
_
B
D
y
(r) =
µoµz
4π
_
3ˆ yˆ z
2r−r
i

3
_
=
µoµz
4π
_
3 sin φsin θ cos θ
r−r
i

3
_
B
D
z
(r) =
µoµz
4π
_
3ˆ z
2
−1
2r−r
i

3
_
=
µoµz
4π
_
3 cos
2
θ−1
2r−r
i

3
_
.
(3.34)
In the high ﬁeld case, we must remember that the only terms that survive from
the above equations are those that either contribute to the z–axis or those that are invariant
to any rotation (terms like µ • µ and µ • ˆ r). Then we get the following sets of equations
shown in Eq. 3.35 which has a ﬁeld shown in Figure 3.1b
B
D
x
(r) =
µoµx
4π
_
−1
2r−r
i

3
_
=
µoµx
4π
_
−1
r−r
i

3
_
B
D
y
(r) =
µoµy
4π
_
−1
2r−r
i

3
_
=
µoµz
4π
_
−1
r−r
i

3
_
B
D
z
(r) =
µoµz
4π
_
3ˆ z
2
−1
2r−r
i

3
_
=
µoµz
4π
_
3 cos
2
θ−1
2r−r
i

3
_
.
(3.35)
In more interesting simulation we are interested in many spins, and every spin has a magnetic
dipole moment, thus every spin, spin j, sees a ﬁeld generated by all of its neighbors, B
D
i
,
as
B
D
j
=
N
i=j
B
D
i
(r
i
−r
j
) (3.36)
where N is the total number of spins and r
i
− r
j
is the vector separating the two spins.
This sum is one of the computationally limiting steps as it requires the sum to be calculated
for every spin j at every integration step. All the previous interaction have been had N
fold scaling, where this one has N
2
scaling. There are other complications as well. In a
bulk sample, we are not concerned with a single spin magnetic moment, but a small volume
of spins, which has total magnetization M. Thus we need to calculate the dipole ﬁelds
due to the small volume. If M is not chosen properly the value of B
D
will grow out of
3.2. BLOCH EQUATION MAGNETIC FIELDS 54
control. These considerations dealing with what M really is will be treated at the end of
this chapter. Also in macroscopic samples, the sum in Eq. 3.36 becomes an integral, this
integral is the topic of another eﬀect, the local ﬁeld and will be discussed below.
Bulk Susceptibility
The next three and ﬁnal ﬁelds I will discuss are all high ﬁeld, bulk eﬀects simply
meaning they are inherently due to the high magnetic ﬁeld and the fact that we are dealing
with a macroscopic magnetized sample. The ﬁrst and easiest to understand is the bulk
susceptibility. All matter when exposed to a magnetic or electric ﬁeld ‘reacts’ to the ﬁeld’s
presence by either opposing the ﬁeld or aligning with the ﬁeld. A sample of nuclear spins
is a slightly polarized by an applied ﬁeld so it creates a magnetic moment in the sample.
Inside the sample the total ﬁeld, B, is simply a sum of the two ﬁelds
B = µ
o
(H+DM) (3.37)
where H =
B
applied
µo
is the applied ﬁeld intensity, M is the sample magnetization. The
constant in front of M, D depends on the sample shape. For a perfect sphere this constant
is 0, for a ﬂat disk it is 1/3, and for a long cylinder (i.e. a liquid NMR sample tube) it is
1 (its maximum value). A pictorial representation of this eﬀect is shown in Figure 3.2. We
can further simply this equation by realizing that the applied ﬁeld H is responsible for the
magnetization M
M = µ
o
χH (3.38)
where χ, called the magnetic susceptibility, is related to the sample. We have made the
assumption that the material is isotropic and linear in H. For paramagnetic material this
constant is large, for diamagnetic materials (like water, and most NMR samples), χ is quite
3.2. BLOCH EQUATION MAGNETIC FIELDS 55
H
M
o o
D D = − ( ) = − ( ) B H M H µ µ χ 1
Figure 3.2: The magnetization of a sample inside a magneti ﬁeld.
small (of order 10
−6
) and negative. We will discuss this constant more later. Thus our ﬁeld
equation becomes, for diamagnetic samples,
B
bs
= µ
o
(1 −Dχ)H. (3.39)
In a high H intensity (by high we mean [H[ >> [M[), our rotating frame transfor
mation indicates that only the components of χH that are parallel with H will contribute
to any observed eﬀect. Again, this eﬀect behaves simply like an oﬀset. However, we can
control M using the magnetic pulses, as a consequence, in a high ﬁeld along ˆ z we can turn
on and oﬀ this eﬀect (i.e. turn oﬀ and on the oﬀset). If you place our magnetization on
the xy–plane, then there is no contributing eﬀect, on the other hand, if we align our mag
netization along the ﬁeld, we would see a slight oﬀset. The equations of motion are only
eﬀected by the magnetic ﬁeld along z and we get these terms in our equations of motion:
dMx
dt
= χγM
T
z
(t)M
y
dMy
dt
= −χγM
T
z
(t)M
x
dMz
dt
= 0
(3.40)
3.2. BLOCH EQUATION MAGNETIC FIELDS 56
M
T
z
(t) is the TOTAL magnetization along the z axis at any given time.
Radation Damping
Another high ﬁeld eﬀect comes from the actual hardware used in an NMR exper
iment. Typically, a solenoid is placed around the sample that acts both as the ‘magnetic
pulse’ and the detection apparatus. It detects signal using Faraday’s Law, which states any
moving magnetic ﬁeld creates a reaction electric current inside a conductor. The changing
current/voltage (or electromotiveforce (emf)) is related to the changing magnetic ﬂux, Φ
as
dΦ
dt
= emf (3.41)
In a simple solenoid Φ is simply the magnetic ﬁeld B times a constant area, thus we can
relate this emf to our Bloch equations
dM
dt
∗ Area = emf. (3.42)
This new emf, which is time dependant, then creates another magnetic ﬁeld (Lenz’s Law),
which apposes our magnetization. This is in essence applying another magnetic pulse as
its time dependence is the same as the magnetization’s time dependence (i.e. still on
resonance).
d(emf)
dt
= −
d(B
rd
)
dt
= −α
dM
dt
. (3.43)
The constant, α, simply represents the strength of the back reaction ﬁeld. We
now have a nonlinear equation as this eﬀect depends directly on how much magnetization
is present (but in the opposite rotating sense). The amount of back reaction depends on a
number of physical parameters which can be reduced to two constants called the Quality
factor or Q and the sample ﬁlling factor, η. Q is dependant on the coil size, inductance,
3.2. BLOCH EQUATION MAGNETIC FIELDS 57
and a slew of other details. η is simply how much of the sample ﬁlls the space in the coil.
Obviously the eﬀect is also driven by how much total magnetization exists in the sample.
This magnetization is only appreciable enough to create any eﬀect in high magnetic ﬁelds,
so our rotating frame approximation still holds. Another key bit of information is that the
coils are aligned perpendicular to the main ﬁeld, so the reaction ﬁeld is then only applied
in the xy–plane. We can ﬁnally write down the radiation damping reaction ﬁeld as
B
rd
x
(t) = −1/τ
r
M
T
y
(t)/γ
B
rd
y
(t) = 1/τ
r
M
T
x
(t)/γ
(3.44)
where the 1/τ
r
is related to the coils parameters (1/τ
r
= Qη[M
o
[γ), and M
T
i
(t), is the total
magnetization at a given time t. We have made the assumption that the back reaction ﬁeld
is the same at any point in the sample. If this is not the case (which it is not in a real
experiment due to coil edge eﬀects) then Eq. 3.44 becomes an integral where B
rd
i
(t) →
B
rd
i
(r, t), τ
r
→ τ
r
(r), and M
T
i
(t) → M
T
i
(r, t). Assuming a uniform interaction, the new
terms in our equations of motion are
dMx
dt
= −
1
τr
M
T
x
(t)M
z
dMy
dt
= −
1
τr
M
T
y
(t)M
z
dMz
dt
= −
1
τr
_
M
T
x
(t)M
x
+M
T
y
(t)M
y
_
.
(3.45)
Local Field
The ﬁnal ﬁeld I will discuss involves extending the bulk susceptibility and the
dipole ﬁelds to a more closed form. In macroscopic samples, the dipole ﬁeld at a position r
would be the sum over all the various dipoles in the sample. The trouble with performing
this sum is that there is an Avogadros number of them, making this sum impossible to
3.2. BLOCH EQUATION MAGNETIC FIELDS 58
perform, but with such a large number, we can reduce the sum to an integral
B
LF
(r) =
µ
o
4π
_
1 − 3 cos(θ
r−r
)
2 [r −r
[
3
_
3M
z
(r
)ˆ z −M(r
)
¸
dr
3
. (3.46)
where θ
r−r
is the angle between the r −r
vector and the magnetic ﬁeld. Eq. 3.46 assumes
that we are in a high ﬁeld. B
LF
still exists in low ﬁeld situations, however, the bulk property
of the magnetization is so small, that it all but eliminates this eﬀect. This integral can only
be integrated analytically for a few special cases. If we assume uniform magnetization
(M(r) = M), then the integral reduces to
B
LF
(r) =
µ
o
4π
[3M
z
ˆ z −M]
_
1 − 3 cos(θ
r−r
)
2 [r −r
[
3
dr
3
. (3.47)
The remaining term in the integral will give a simple constant which is dependant on the
shape of the sample, and we are left with something that looks very similar to the bulk
susceptibility. If the sample shape is an ellipsoid (a sphere, disk, cylinder) then the integral
in Eq. 3.47 is soluble[25, 26]. The dipolar local ﬁeld looks like
B
LF
(r) =
µ
o
6
(3n
z
− 1) [3M
z
ˆ z −M] (3.48)
where n
z
is called the demagnetizing factor (n
z
= 0 for a long rod, n
z
= 1/3 for a sphere,
n
z
= 1 for a thin disk).
If the magnetization is not uniform, then in general we would have to evaluate
this integral. In this case the best we can do numerically is break the integral into a
sum over little ∆r
3
volume elements. The problem with this technique is that it require
many volume cells for the integral to converge properly, which can result in very long
function evaluations. There do exist techniques using Fourier transforms to simply this
integral[27, 28]. Those techniques however are for the most general case and the algorithmic
complexity becomes daunting. There is another special case that is of greater interest
3.3. QUANTUM MECHANICS 59
because it allow manipulation of this ﬁeld[29, 27]. Upon an applied external gradient which
completely ‘crushes’ (the magnetization in the total volume sums to 0) the magnetization
along a single direction, we can write the ﬁeld as
B
MLF
(r) =
3(ˆ s ˆ z)
2
− 1
2τ
D
_
[M
z
(ˆ s) − ¸M
z
)] −
1
3
[M(ˆ s) − ¸M)]
_
. (3.49)
We will call this the Modulated local ﬁeld. In Eq. 3.49, ˆ s is the direction of the
modulation, and M(ˆs) is the magnetization along the direction of the modulation. The ¸...)
indicate the mean of the magnetization. Finally the time constant τ
d
is 1/(µ
o
γM
o
) where
M
o
is the total equilibrium magnetization. This form of the B
MLF
does not require any
explicit sums, so this interaction scales a N as well.
3.3 Quantum Mechanics
The fundamental ﬁelds discussed in section 3.1 also form the basis of the quantum
mechanical description, with two fundamental diﬀerences: instead of ﬁelds, we are interested
in the Hamiltonian, H, of the system which evolve a density operator, ρ, rather then a
magnetization.
dρ
dt
= −i[H, ρ] (3.50)
where [...] is a commutator operator ([A, B] = AB − BA). This equation is called the
LiouvillVon Neumann equation. NMR speciﬁcally treats atomic spin states. There are a
variety of properties associated with the spin states based on the spins quantum number I.
A spin of quantum number I has 2I − 1 possible states. The most common NMR spin of
I = 1/2 has two states. NMR is a measure of bulk phenomena so simple states are not an
accurate description. Like the classical case we must pick a basis in which to describe our
3.3. QUANTUM MECHANICS 60
spin(s), here we choose the spin operator basis in a Cartesian frame
I
x
=
1
2
_
_
_
_
0 1
1 0
_
_
_
_
I
y
=
1
2
_
_
_
_
0 i
−i 0
_
_
_
_
I
z
=
1
2
_
_
_
_
1 0
0 −1
_
_
_
_
I
e
=
_
_
_
_
1 0
0 1
_
_
_
_
.
(3.51)
The density operator has four possible states or linear combinations of any of the
above 4 operators.
ρ = aI
e
+bI
y
+cI
x
+dI
z
+higher order terms (3.52)
Usually, we work in a reduced basis, where we factor out an I
e
term which describe a non
polarized, and non–manipulable set of states (the identity operator, I
e
never eﬀects any
evolution nor is aﬀected by any interactions). From this point on we will mention ρ as the
reduced density operator. The higher order terms (terms proportional to I
i
to some power
n > 1) are typically ignored in NMR and will be discussed further in Section 3.4.
3.3.1 Rotations
Almost all of the mathematics of NMR can be reduced to rotations on quantum
operators. There are two equally important views in treating quantum mechanical rota
tions: Cartesian based rotations and spherical tensor rotations. Computationally, Cartesian
rotations should be a slight bit faster in execution in the general case, as rotation matrices
3.3. QUANTUM MECHANICS 61
are all 3x3. Spherical tensor rotations are typically used for theoretical/symmetry based
rotational considerations because of their nice symmetry properties. Both, however, may
be used to treat NMR computationally or theoretically.
Cartesian Based Rotations
All 3 dimensional rotations can be reduced to three angles Ω = (φ, θ, γ). The angle
φ rotates the xy–plane around the z–axis into new axes x
and y
, then θ around the old
y–axis to rotate z–axis creating three new rotated basis x
, y
, and z
. Finally, γ rotates x
and y
about the z
–axis into the ﬁnal new rotated state (x
, y
, and z
). Mathematically,
this can be represented by a 3 3 matrix
R(Ω) =
_
_
_
_
_
_
_
_
cos γ cos φ − cos θ sin γ sin φ cos γ sin φ − cos θ sin γ cos φ sin γ sin θ
−sin γ cos φ − cos θ cos γ sin φ −sin γ sin φ − cos θ cos γ cos φ cos γ sin θ
sin θ sin φ −cos φsin θ cos θ
_
_
_
_
_
_
_
_
.
(3.53)
This can easily be generates by taking into account the separate three rotations
R(Ω) = R
z
(γ)R
y
(θ)R
z
(φ). (3.54)
Each R
i
has the form shown in Eq. 3.7.
Spherical Tensor Rotations
The spherical tensor rotation representation actually comes about by treating sym
metry and invariants of angular momentum in quantum mechanics. The most common form
for rotation of angular momentum are the Wigner matrix elements [30]. Given the total
angular momentum L, there are (2L−1)
2
elements to rotate each of the 2L−1 eigenvectors
3.3. QUANTUM MECHANICS 62
Table 3.1: Wigner rank 1 rotation elements, D
1
m,m
.
.
.
. m
m
.
.
.
1 0 1
1 e
−i(γ+φ)
cos
_
θ
2
_
2
−e
−iφ
sin(θ)
√
2
e
−i(φ−γ)
sin
_
θ
2
_
2
0 e
−iγ
sin(θ)
√
2
cos(θ) −e
−iγ
sin(θ)
√
2
1 e
−i(γ−φ)
sin
_
θ
2
_
2
e
iφ
sin(θ)
√
2
e
i(γ+φ)
cos
_
θ
2
_
2
(m). For L = 1, we have 9 matrix elements as shown in Table 3.1 using the same three
angles as in the Cartesian case.
These matrix elements are usually called D
l
m,m
where l is the rank of the matrix,
and the m, m
correspond to a particular matrix element. There is a reduced notation given
as
e
i(mγ+m
φ)
d
l
m,m
(θ) (3.55)
where d
l
m,m
(θ) is called the ‘reduce Wigner element’ because the two z–rotation angles φ
and γ are easily factored out of the total matrix element. The reduced Wigner elements for
l = 2 are shown in Table 3.2. Almost all NMR interactions are some form of the rank 0, 1,
and 2 matrix elements. Like the Cartesian these rotations matrices can be generated from
the individual rotations
R(Ω) =
R
z
(γ)R
y
(θ)R
z
(φ) =
e
iIzγ
e
iIyθ
e
iIzφ
.
(3.56)
We can decompose our Hamiltonian into a spherical tensor basis. Hamiltonians
are scalar/energy operators and invariant under a total system rotation, we end up with a
3.3. QUANTUM MECHANICS 63
Table 3.2: Reduced Wigner rank 2 rotation elements, d
2
m,m
.
.
.
. m
m
.
.
.
2 1 0
2 cos(
θ
2
)
4
2 cos(
θ
2
)
3
sin(
θ
2
) 2
_
3
2
sin(θ)
2
1 2 cos(
θ
2
)
3
sin(
θ
2
) cos(
θ
2
)
2
(−1 + 2 cos(θ)) −
_
3
2
cos(θ) sin(θ)
0
_
3
2
sin(θ)
2
_
3
2
cos(θ) sin(θ)
1+3 cos(2θ)
4
1 2 cos(
θ
2
)sin(
θ
2
)
3
(1 + 2 cos(θ)) sin(
θ
2
)
2
_
3
2
cos(θ) sin(θ)
2 sin(
θ
2
)
4
2 cos(
θ
2
)sin(
θ
2
)
3
_
3
2
sin(θ)
2
2
.
.
. m
m
.
.
.
1 2
2 −2 cos(
θ
2
)sin(
θ
2
)
3
sin(
θ
2
)
4
1 (1 + 2 cos(θ)) sin(
θ
2
)
2
−2 cos(
θ
2
) sin(
θ
2
)
3
0 −
__
3
2
cos(θ) sin(θ)
_
_
3
2
sin(θ)
2
2
1 cos(
θ
2
)
2
(−1 + 2 cos(θ)) −2cos(
θ
2
)
3
sin(
θ
2
)
2 2cos(
θ
2
)
3
sin(
θ
2
) cos(
θ
2
)
4
Hamiltonian of the form
H =
l
α
l
·
l
. (3.57)
where each ·
l
is a spherical tensor basis element and each α
l
is a complex constant. It is
implied that ·
l
contains all the m subcomponents. An important aspect in NMR is that
the Hamiltonians can be separated into a spatial tensor component, A
l
and a spin tensor
component T
l
. So we can rewrite · as a tensor product of the two
·
l
= A
l
T
l
. (3.58)
Using the explicit form of the product we get
·
l
=
l
m=−l
(−1)
m
A
l,m
T
l,−m
=
l
m=−l
(−1)
m
A
l,−m
T
l,m
(3.59)
3.3. QUANTUM MECHANICS 64
Each tensor component can be rotated by using our Wigner rotation matrix ele
ments
A
l,m
=
l
m=−l
D
l
m
,m
(Ω)A
l,m
(3.60)
3.3.2 Rotational Frames
PAS
The Hamiltonians are typically created with an initial reference frame centered on
the atom. We can think of the atomic frame as being the diagonal representation of the
interaction. As soon as we move from this frame via some rotation then elements become
mixed combinations of the atomic frame. This atomic frame is given the name Principle
Axis System (PAS). In the PAS frame the arbitrary interaction in NMR can be reduced
to 3 components. In the Cartesian frame these are typically given the labels δ
x
, δ
y
, and
δ
z
in the spherical frame they are given the labels δ
iso
(isotropic), δ
ani
(anisotropic) and η
(asymmetry), and are related via
δ
iso
= 1/3(δ
x
+δ
y
+δ
z
)
δ
ani
= δ
z
η =
δx+δy
δz
(3.61)
The Cartesian interaction frame is a 3 3 matrix, and in the PAS it is given as
A
PAS
cart
=
_
_
_
_
_
_
_
_
δ
x
0 0
0 δ
y
0
0 0 δ
z
_
_
_
_
_
_
_
_
. (3.62)
3.3. QUANTUM MECHANICS 65
The spherical basis reduced to a sum over the various rank l components as
A
PAS
sph
= A
0
+A
1
+A
2
A
0,0
= −
√
3δ
iso
A
1,±1
= A
1,0
= 0
A
2,±1
= 0, A
2,±2
=
1
2
δ
ani
η, A
2,0
=
_
3
2
δ
ani
.
(3.63)
Molecule Frame
The next frame is the molecular frame, where we have gone past the atomic frame
and now look at the various atomic frames relationship to each other on a molecule where
we assume the atoms are ﬁxed in space. To create this transformation, one needs to deﬁne
another axis system in the molecular frame, then rotate each of the atomic interactions
to this new frame, either by a Cartesian Euler rotation (Eq. 3.53) or a spherical Wigner
rotations (Eq. 3.60). The Euler angles used to perform this rotation will be called Ω
mol
.
A
mol
cart
= R(Ω
mol
).A
PAS
cart
.R(Ω
mol
)
−1
A
mol
sph
= A
PAS
0
+
2
m
=−2
D
2
m,m
(Ω
mol
)A
PAS
2,m
(3.64)
Rotor Frame
This particular rotation takes the molecule frame into the frame of the physical
sample. Again we need to pick a reference axis by which all molecules are to be rotated.
If the sample is a liquid, then this particular rotation would be time dependant as all the
molecules are rotating in various ways in time inside the liquid. In a solid powder sample,
then there are many diﬀerent orientations relative to the chosen reference axis and they are
‘ﬁxed’ in time. In a liquid this rotation is unnecessary as usually the time dependence of
this rotation (on the order of micro seconds) is much faster then the observable on the NMR
3.3. QUANTUM MECHANICS 66
times scale (on the order of seconds/millisconds). So the eﬀect of this rotation in a liquid
averages away. This assumption is not true for large molecules like proteins or bicelles that
have a very slow rotational rate, then the rotational average is only partial and must be
included to achieve a proper model.
In solids, however, NMR experiments are performed in a ‘rotor’ (the sample holder)
which is aligned in some arbitrary direction. So we call the Euler angles to rotate into this
frame Ω
rot
and this rotation is given by
A
rot
cart
= R(Ω
rot
).R(Ω
mol
).A
PAS
cart
.R(Ω
mol
)
−1
.R(Ω
rot
)
−1
A
rot
sph
= A
PAS
0
+
2
m
=−2
D
2
m,m
(Ω
rot
)
2
m
=−2
D
2
m
,m
(Ω
mol
)A
PAS
2,m
(3.65)
Lab Frame
The ﬁnal rotational frame relates the rotor frame back a chosen lab frame. The lab
frame is the ﬁnal resting point for all interactions and is static (like the superconducting
magnet is static). This frame needs to be included only when the rotor frame moves,
otherwise, we could simply choose that static frame as the rotor frame and there is then
no need to perform this rotation. However, many solid–state techniques use the fact that
a rotating rotor provides another method of control over the interactions. We will call the
Euler angles that rotate the rotor into the lab frame Ω
lab
. The ﬁnal set of rotations is then
given by
A
lab
cart
= R(Ω
lab
).R(Ω
rot
).R(Ω
mol
).A
PAS
cart
.R(Ω
mol
)
−1
.R(Ω
rot
)
−1
.R(Ω
lab
)
−1
A
lab
sph
= A
PAS
0
+
2
m
=−2
D
2
m,m
(Ω
lab
)
2
m
=−2
D
2
m
,m
(Ω
rot
)
2
m
=−2
D
2
m
,m
(Ω
mol
)A
PAS
2,m
(3.66)
3.3. QUANTUM MECHANICS 67
Table 3.3: Spherical tensor basis as related to the Cartesian basis for spin i and spin j
Spherical Tensor T
spin
l,m
Cartesian Representation
T
i
0,0
I I
T
i
1,0
I
i
z
T
i
1,±1
1
√
2
I
i
±
=
1
2
√
2
_
I
i
x
±iI
i
y
_
T
(i,j)
2,0
1
√
6
_
3I
i
z
I
j
z
−I
i
I
j
_
T
(i,j)
2,±1
∓1
2
_
I
i
±
I
j
z
+I
i
z
I
j
±
_
T
(i,j)
2,±2
1
2
_
I
i
±
I
j
±
_
3.3.3 The Hamiltonians
Now that we know how to move our interaction into any frame we desire, we can
describe the system Hamiltonians in the PAS frame. Before we will discuss the speciﬁc
interactions, we again must address the rotating frame/truncation in the new basis. In
the last section, there was no mention of any spin system or an NMR system (except of
some small enlightenments). The above discussion is general for any Hamiltonian, so in the
absence of any ‘rotating frame’ transformation, the ﬁnal Hamiltonian will be of the form of
Eq. 3.57. If the spatial components can be separated from the other components, then the
rotation discussion and Eq. 3.59 holds. However, the rotations DO NOT eﬀect the ﬁnal
energy spectrum of the Hamiltonian. Applying a large magnetic ﬁeld removes the spherical
symmetry of the Hamiltonian in Eq. 3.57, so that now the spectrum of the Hamiltonian
has a directional dependence. To show this we need to look at the spherical spin tensors
basis (T
l
) shown in Table 3.3 and how they relate to the Cartesian basis in Eq. 3.51.
3.3. QUANTUM MECHANICS 68
Zeeman
The Zeeman interaction is the one responsible for the symmetry breaking. Like
Eq. 3.3 except that we desire an energy term, not a torque, the Zeeman Hamiltonian is
H
zee
= γI B (3.67)
Again, if Bis large and static, then all the remain interactions will be truncated with respect
to this axis. For simplicity B = B
z
, thus the main Hamiltonian has a term proportional
to I
z
. Using the fact that the ﬁrst order perturbation theory only keeps those terms that
commute with the main Hamiltonian, in this case I
z
. Looking at Table 3.3 only T
0,0
and
T
2,0
survive this truncation.
Chemical Shift Anisotropy
The Chemical Shift Anisotropy (CSA) Hamiltonian is caused by the electronic
shielding around the nucleus. The electron cloud slightly deforms in a ﬁeld causing shifts
in the oﬀset in 3 directions in the PAS. In Cartesian space we still perform the standard
I B, but with respect to the PAS system.
H
CSA
= I
i
C
i
B (3.68)
where C
i
is the chemical shielding tensor on spin i
C
i,PAS
cart
=
_
_
_
_
_
_
_
_
δ
x
0 0
0 δ
y
0
0 0 δ
z
_
_
_
_
_
_
_
_
(3.69)
In the spherical basis this interaction reduces to
H
CSA
= δ
iso
(I
i
B) +A
CSA,i
2,0
T
i
2,0
(3.70)
3.3. QUANTUM MECHANICS 69
Even though the η term does not explicitly appear in the original Hamiltonian, upon a
rotation (where all m components become mixed), it will. Excluding the molecular rotation,
and the lab frame rotations, we can get a rotor frame angular dependence of the frequencies
(rad/sec) to be
ω
csa
= 2πδ
iso
+πδ
ani
_
3 cos
2
θ − 1 +η sin
2
θ cos(2φ)
¸
. (3.71)
Scalar Coupling
Scalar coupling (or J) comes as a 2 atom, through bond interaction. There is
no equivalent of this interaction in the classical sense because it is a result of the anti
symmetry of the electron (a purely quantum mechanical eﬀect). The atoms must be in
equivalent for one to observe this eﬀect, so atoms with the same chemical shift do not have
a J. Furthermore, if the two atoms have huge chemical shift diﬀerences when compared to
the J coupling, then the Jcoupling is truncated again with respect to the isotropic part of
the chemical shifts. This is called ‘weak’ coupling, the other case being ‘strong’. For most
NMR the Jcoupling is considered solely isotropic, but there can easily be an electron cloud
distortion like the CSA , so there is an anisotropic component as well. Below in Eq. 3.72
is the weak coupling limit where we have assumed the high magnetic ﬁeld (and thus the
Chemical shifts) are along the z–axis. Eq. 3.73 shows the strong case.
H
J
weak
= δ
i,j
iso
I
i
z
I
j
z
+A
J
2,0
_
2I
i
z
I
j
z
¸
(3.72)
H
J
strong
= δ
i,j
iso
I
i
I
j
+A
J
2,0
_
3I
i
z
I
j
z
−I
i
I
j
¸
(3.73)
3.3. QUANTUM MECHANICS 70
Dipole Coupling
The dipole interaction in a high ﬁeld looks much like Eq. 3.33, except that we
are interested in the relative orientation of the two nuclei spin degree of freedom (I
i
, I
j
).
So the µ ˆ r terms switch to I
i
I
j
terms. Much like J couplings, there are two extremes.
For two homonuclear spins the scale of the dipolar interaction is usually the same as the
chemical shift, so no chemical shift truncation will occur (Eq. 3.74). For heteronuclear
dipole systems, the chemical shift diﬀerence is in the MHz, where as dipoledipoles are on
the order kHz. The heteronuclear coupling is truncated with respect to the chemical shift
diﬀerence on the two hetero nuclear spins (Eq. 3.75). The dipolar coupling is symmetric
about the z–axis, therefore there will be no η terms. Also, there is no part of the total
dipolar Hamiltonian invariant under rotations, therefore there is no isotropic component.
H
D
hom
= A
D
2,0
T
i,j
2,0
=
ω
i,j
D
_
1 − 3 cos
2
θ
_
2
_
3I
i
z
I
j
z
−I
i
I
j
¸
(3.74)
H
D
het
= A
D
2,0
T
i,j
2,0
= ω
i,j
D
_
1 − 3 cos
2
θ
_ _
I
i
z
I
j
z
¸
(3.75)
where ω
i,j
D
is
ω
i,j
D
=
γ
i
γ
j
µ
o
4π [r
i
−r
j
[
3
(3.76)
Quadrupole
The quadrupolar coupling is due to electric ﬁeld gradients around a single nucleus,
and it only eﬀect nuclei with spin > 1/2. If the gradient is 0 (spherical) then the interaction
is also 0, therefore there is no isotropic component, but there can be an asymmetry (η) to
the gradient. The anisotropic component (the gradient along the z–axis) is
δ
Q
z
= e
2
qQ =
2
3
2I(2I − 1)ω
Q
(3.77)
3.3. QUANTUM MECHANICS 71
where e is the charge of an electron, qQ is the actual gradient value, and I is the spin of the
nucleus, and ω
Q
is the coupling constant. Simulations need only ω
Q
which can be expressed
as
ω
Q
=
3δ
Q
z
2I(2I − 1)
. (3.78)
The ﬁrst order truncated Hamiltonian is then
H
Q
1
= A
Q
2,0
T
2,0
= ω
Q
_
1 − 3 cos
2
θ
_ _
3I
2
z
−I
e
(I(I − 1))
¸
. (3.79)
The quadrupole interaction tends to be very large, on the order of MHz, the same order
as our magnetic ﬁeld. Our truncation approximation breaks down, so the second order
quadrupole is needed for an accurate description. The second order eﬀect is proportional to
ω
2
Q
γBz
and includes contributions of all the commutors of the basic spin tensors that commute
with the Zeeman interaction (terms proportional to T
2,1
T
2,−1
and T
2,2
T
2,−2
). The functional
form of this interaction can be broken down into a total rank 2 component and a total rank
4 component. Note that the rank 4 component is obtained from the rank 2 components via
rules for tensor multiplication [30]. The second rank quadrupolar Hamiltonian is
H
Q
2
=
ω
2
Q
γBz
__
A
Q
2,−1
A
Q
2,1
_
_
T
2,−1
T
2,1
¸
+
_
A
Q
2,−2
A
Q
2,2
_
_
T
2,−2
T
2,2
¸
_
=
ω
2
Q
Iz
γBz
_
A
Q
2,−1
A
Q
2,1
_
4I
e
I(I + 1) − 8I
2
z
−I
e
_
+A
Q
2,−2
A
Q
2,2
_
2I
e
I(I + 1) − 2I
2
z
−I
e
_
_
(3.80)
Computationally, the second order quadrupole under the many rotations is best
done in the Cartesian basis where we only need to multiply 3 3 matrices rather then the
many 5 5 rotations. Let Q be the quadrupolar Cartesian tensor in the PAS frame as
Q =
_
_
_
_
_
_
_
_
η−1
2
−
η+1
2
1
_
_
_
_
_
_
_
_
. (3.81)
3.3. QUANTUM MECHANICS 72
After our series of rotations all the elements will be mixed and non–zero, the ﬁrst and
second order quadrupole can be reduced to
H
Q
1
= ω
Q
Q(2, 2)T
2,0
H
Q
2
=
ω
2
Q
γBz
c
4
_
(Q(0,0)−Q(1,1))
2
4
+Q(0, 1)
2
_
−
ω
2
Q
γBz
c
2
__
Q(0, 2)
2
−Q(1, 2)
2
_¸
(3.82)
where Q(i, j) indicate the matrix elements within Q and c
4
and c
2
are
c
2
= 2I
z
_
4I(I + 1) − 8I
2
z
−I
e
_
c
4
= 2I
z
_
2I(I + 1) − 2I
2
z
−I
e
_
(3.83)
Pulses
Much like the classical case in the rotating frame, our magnetic pulse can be
described simply by giving each direction (
ˆ
i) the corresponding spin tensor. The pulse
Hamiltonian on spin j is then given as
H
j
RF
= ω
1
(cos φI
j
x
+ sin φI
j
y
) + ∆ω
1
I
j
z
(3.84)
where ω
1
= γ
j
B
1
, φ a the phase factor, and ∆ω
1
is the oﬀset.
There are typically two extremes of pulses when we treat the problem computa
tionally. The ﬁrst are called ‘hard’ pulses where the pulse is very strong (much larger then
any other Hamiltonian) and of short duration such that the eﬀective Hamiltonian for this
small time is only the pulse. The second case, a called ‘soft’ pulse, is the opposite extreme
where the pulse is either applied for long times and/or is relatively weak. In this case, the
total Hamiltonian is the system Hamiltonian plus the pulse.
3.4. NMR INITIAL CONDITIONS 73
Other Second Order Eﬀects
If the main ﬁeld is weak enough (or the interactions strong enough), then all the
interactions will have second order aﬀects much like the quadrupole. Not only will they
need to be treated individually to second order, but the total Hamiltonian will have to be
treated to second order. This results in very messy expressions for the Hamiltonians that
require much care to evalutate. A good reference how to treat the second order components
is by Sungsool and Frydman[31].
3.4 NMR Initial Conditions
3.4.1 Quantum
NMR measures bulk magnetic properties of nuclei, not a single nucleus. Much of
our quantum discussion above appeared as if we were treating one or two nuclei, when in
fact the Hamiltonians apply to the bulk sample of identical particles. The density matrix
ρ is the quantum mechanical way to treat many quantum states in terms populations of
states rather then explicit eigen–states. The density matrix at equilibrium is given by a
simple Boltmann distribution
ρ
o
=
exp
_
−H
k
B
T
_
Tr
_
−H
k
B
T
_
=
exp
_
−γBz
k
B
T
i
I
i
z
_
Tr
_
−H
k
B
T
_
ρ
o
≈
Ie−
_
γBz
k
B
T
i
I
i
z
_
+
1
2
_
_
γBz
k
B
T
_
2
j,i
I
j
z
I
i
z
_
−
1
6
_
_
γBz
k
B
T
_
3
j,i,k
I
k
z
I
j
z
I
i
z
_
+...
Tr
_
−H
k
B
T
_
(3.85)
where k
B
is Boltzamann constant and T is the temperature. We have also assumed the
Hamiltonian is simply the Zeeman Hamiltonian as it is the largest of all the other interac
tions. The indexes i, j, k sum over the entire number of spins. At this point most of NMR
makes a fundamental approximation, the ‘high temperature limit’ where k
B
T >> γB
z
.
3.4. NMR INITIAL CONDITIONS 74
Thus the only term that contributes any component to the density matrix is the ﬁrst term,
which for a B
z
of 7 Telsa, T = 300K, and one mole of nuclei is about 4.3 ∗ 10
−5
. The
deviation from an equal distribution is only 4.3∗10
−5
, so ignoring any spins that we cannot
manipulate or measure (the Identity (I
e
) term), we only have about 1 in every 10
5
spins
that we can control. The reduced density matrix is then simply
ρ
o
≈
−γB
z
k
B
T
i
I
i
z
/2
_
γB
z
k
B
T
_
≈ −
1
2
i
I
i
z
(3.86)
Since we are dealing with identical particles, the sum over I
z
is easily reduced to
a single I
z
matrix with the implication that when we eﬀect this term we eﬀect every spin.
So the Hamiltonians discussed above are valid to this reduced spin matrix as well as the
individual nuclei.
Eq. 3.86 is usually the initial condition in most NMR situations. However, certain
experimental observations lead Warren[32] to include higher order terms of the density
matrix. This results in an explanation for certain cross peaks in 2D NMR[33, 34, 35, 36]
and new imaging techniques[37, 38, 39]
3.4.2 Classical
In the classical case we are concerned with the total magnetization of a volume, or
a single spin. The magnitude of a single spin’s magnetic moment is simply a nuclear Bohr
magneton. The quantum density matrix picture describes a polarization diﬀerence. This
polarization diﬀerence manifests itself as a bulk magnetization of the form[3, 40]
M
o
=
Nγ
2
2
I(I + 1)B
o
3µ
o
k
B
T
= χ
B
o
µ
o
= χH (3.87)
where N is the number of spins and I is the nuclear spin. This value, like the polarization is
very small. Figure 3.3 shows this magnetization as a function of temperature, concentration,
3.4. NMR INITIAL CONDITIONS 75
Figure 3.3: Magnetization in iso–surfaces versus the applied magnetic ﬁeld, B
o
, the tem
perature T, and number of moles.
and the applied magnetic ﬁeld.
76
Chapter 4
NMR Algorithms
4.1 Classical Algorithms
4.1.1 Eigenvalue Problem
The most general form for Eq. 3.1 can be given as a matrix equation
dM
dt
= −γ
_
_
_
_
_
_
_
_
B
xx
B
xy
B
xz
B
yx
B
yy
B
yz
B
zx
B
zy
B
zz
_
_
_
_
_
_
_
_
.
_
_
_
_
_
_
_
_
M
x
M
y
M
z
_
_
_
_
_
_
_
_
. (4.1)
This is a standard tensor equation
dM
dt
= B M (4.2)
and due to the properties of the cross product we know that B is an antisymmetric matrix
(i.e. B
ij
= −B
ji
). We also know that B is always real as it represents a real physical
quantity. We can easily solve this equation using standard the eigensystem, where we know
that there is some transformation matrix Λ such that
B = Λ Ω Λ
−1
(4.3)
4.1. CLASSICAL ALGORITHMS 77
where Ω is a diagonal matrix. Ω are the eigen–frequencies and the matrix Λ are the eigen–
vectors of B. Because of the antisymmetry of B, Λ forms a set of orthonormal basis states
equivalent to our Cartesian set. If we transform our Cartesian set into this eigen–basis via
˜
M = Λ M, (4.4)
the equations of motion become
_
_
_
_
_
_
_
_
˙
˜
M
1
˙
˜
M
2
˙
˜
M
3
_
_
_
_
_
_
_
_
=
_
_
_
_
_
_
_
_
ω
1
0 0
0 ω
2
0
0 0 ω
3
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
˜
M
1
˜
M
2
˜
M
3
_
_
_
_
_
_
_
_
. (4.5)
This has the trivial solution given at t = 0,
˜
M = (
˜
M
o
1
,
˜
M
o
2
,
˜
M
o
3
) of
_
_
_
_
_
_
_
_
˜
M
1
(t)
˜
M
2
(t)
˜
M
3
(t)
_
_
_
_
_
_
_
_
=
_
_
_
_
_
_
_
_
˜
M
o
1
e
ω
1
t
˜
M
o
2
e
ω
2
t
˜
M
o
3
e
ω
3
t
_
_
_
_
_
_
_
_
. (4.6)
The ﬁnal step is then to transform back to our Cartesian basis to get the solutions in a
space we can visualize
M(t) = Λ
−1
˜
M(t), (4.7)
Another equally valid, and algorithmically simple, is the solution directly in the
Cartesian basis of
M(t) = e
Bt
M
o
(4.8)
This is a general solution to the Bloch equations. It requires, numerically, to perform a
matrix diagonalization (or matrix exponentiation) which for one spin is very simple, however
for many spins, the matrix becomes huge, rendering this method unusable especially if B
is a function of time, as we then must perform a numerical matrix integration of the form
M(t) = e
t
_
0
B(t
)∂t
M
o
(4.9)
4.1. CLASSICAL ALGORITHMS 78
which requires many matrix diagonalizations and is prohibitively numerically expensive for
large (i.e. hundred to thousands) of spins.
Looking at the form of the these equations, you may think that this very large
matrix is simply many 3 3 sub matrices along the diagonal. This is true so long as there
are no spinspin interactions, and spinspin interactions are what make NMR interesting in
the ﬁrst place. So it seems we need another method to solve our systems of equations.
4.1.2 ODE solvers
Becuase the classical time evolution we wish to probe is a ﬁrst order ordinary
diﬀerential equation (ODE), to evolve the system we simply need a diﬀerential equation
solver. The basic property of an ODE solver is that it marches through time using the
approximation that step size is small enough such that the integrating function is constant.
ODE solvers come in many varieties, here we are concerned only with the ‘Initial
Value Problem’ where we have an initial condition and that is all we know at the beginning.
Other algorithms treat more then one point of knowledge (a npoint boundary value prob
lem). There are an abundance of such solvers all of them with certain accuracy, eﬃciency
and usefulness.
For our initial value problem there are a few subclasses of solvers
• Implicit–Requires a guess of the k point in order to evaluate a correct point at
k, where k is some step in the integration (for us k is always t, time). These are
sometimes called predictor–corrector methods because they must ‘guess’ the k value
at least initially (although the guess can be quite educated), then correct the ‘guess.’
• Explicit–An explicit need only the previous, k−1, point(s) to calculate the next one,
4.1. CLASSICAL ALGORITHMS 79
k.
• SemiImplicit–Uses a tad of both integration techniques.
The simplest Initial Value Problem ODE solver is the Euler solver. It is essentially
worthless in real life applications because it is much too ineﬃcient, but is the basic model
for all others that follow it. Given an ODE like
dy
dt
= f(y, t) (4.10)
and an initial value
y(t
o
) = x (4.11)
then we can approximate the next point, ∆t, from t
o
in an explicit fashion as
y(t
o
+ ∆t) = y(t
o
) + ∆t ∗ f(y(t
o
), t
o
) (4.12)
or in an implicit fashion
y(t
o
+ ∆t) = y(t
o
) + ∆t ∗ f(y(t
o
+ ∆t), t
o
+ ∆t). (4.13)
The Euler solver is a linear solver. You can see the implicit formula Eq. 4.13 requires a
guess at the starting value for y(t
o
+ ∆t) to be useful. Both forms demonstrate how most
ODE solvers function. The typical diﬀerence from one ODE solver to the next is how many
other f(y
n
, t
n
) in between t
o
and t
o
+ ∆t are sampled. Each sampled point would have a
series of coeﬃcients associated with it. To extend the Euler solver, we could simply split ∆t
in half, and use two function evaluations, thus the new coeﬃcients would be
1
2
rather then
1. The number of sampled points determines the order of the algorithm. Taking 4 function
evaluations between t
o
and t
o
+ ∆t is a fourth order solver.
4.1. CLASSICAL ALGORITHMS 80
The three classes of solvers have their beneﬁts and hardships. Implicit methods
can usually handle stiﬀ ODEs. A stiﬀ ODE is one that has two or more solutions that diﬀer
tremendously in there rates of evolution (one is very slow, the other very fast). Explicit
methods cannot treat these sorts of equations eﬃciently, because the time step must be
very small. The small time step is required for explicit solvers because one must be certain
not to ‘jump’ over the fast solution. This problem is reminiscent of the Nyquist Frequency
problem encountered in experimental spectra in NMR where if the time step is too large,
higher frequencies appear as much lower frequencies in the resulting spectrum. Implicit
methods perform better because we initially must guess the solution, then correct it and
continue this prediction–correction scheme until the solution stabilizes.
Implicit methods, however, tend to be very hard to start accurately (because they
need an initial guess and there is no previous points to get a educated guess from). This
results a large algorithmic complexity that can slow down the solvers and other accuracy
diﬃculties. Explicit methods can operate very eﬃciently and without the starting problems
so long as the system is not stiﬀ.
It turns out that the NMR classical ﬁelds do not lead to stiﬀ equations, so we can
freely use any explicit method we desire. The two basic solvers are the RungeKutta and
Richardson Extrapolation. Before we describe these two solver, lets review a few algorithmic
points/sections that one should be aware of when treating ODE solvers.
• Errors–To know our accuracy in the results, we must have some way to measure
errors. This is typically done by using the information of the next order. For example
if we had a 4th order algorithm, then we could monitor the error by the diﬀerence
between the 4th and 5th order results.
4.1. CLASSICAL ALGORITHMS 81
• Time step adjustment–In order for an ODE algorithm to be eﬃcient, it should be
able to take large time steps when the solution is slowly evolving and shrink the time
step when the solution is evolving faster. We can simply adjust the step size based
on the ratio of a desired accuracy and our error monitor[41].
• Kernel–The kernel should operate independently of any errors or time step adjusters.
It should simply produce the next point.
The 5th order embedded Runge–Kutta[42] algorithm uses a total of 6 function
evaluations and has the error estimate ‘embedded’ into the formula. By embedded we
mean it uses all 6 function evaluations to produce a 5th order result and a 6th order error
estimate. This kernel is a standard work horse for most any problem. It is very robust,
but can be slow as it requires 6 function evaluations. A much better kernel is the Bulirsh–
Stoer–Richardson–extrapolation method[43, 44]. This kernel evaluates n points between t
and t + ∆t (usually equally spaced), storing the ﬁnal value, y(t + ∆t). It then performs
n + 2 points, stores another point at y(t + ∆t), and so on up to n + m where m can be
arbitrary (i.e. m = 2, 4, 6, 8, ..., for hard problems m should be 12, for easier problems m
can be 6). Given these m point at y(t +∆t) we can ﬁt an m–order polynomial and use our
ﬁtted function to extrapolate to the case where m → ∞ (or as ∆t
m
→ 0) . The beneﬁt here
over the Runge–Kutta kernel is ∆t can be quite large due to the extrapolation, thus we can
minimize many function calls. The only problem with this kernel is that it is not nearly as
robust as the Runge–Kutta, and even slightly stiﬀ problems cause this method to fail.
That is all the algorithmic complexity we really need to solve the classical form
of Bloch equation, for a review of even more ODE solvers one should look to these two
references [45] and [46].
4.2. QUANTUM ALGORITHMS 82
4.2 Quantum Algorithms
Unlike the classical case where everything can be placed in to one ODE solver and
the trajectories of N spins can be easily calculated, the Quantum Mechanical nature pro
hibits the calculation of arbitrary N. For 10 spin 1/2 nuclei the matrix sizes are 10241024
equivalent to about 35000 classical spins. As shown in chapter 2, the matrix multiplication
can be quite time consuming. Unless other techniques are used to address the problem,
even a 10 spin system may prove prohibitively long. In this section, we do not wish to use
any theoretical approximations to simplify the problem because we are more interested in
the exact solution numerically, not the approximation.
4.2.1 The Direct Method
The solution to our Eq. 3.50 is given by
ρ(t) = Uρ
o
U
−1
. (4.14)
where U is called the propagator and is deﬁned as
U(t
o
, t
o
+ ∆t) = T exp
_
_
−i
to+∆t
_
to
H(t
)dt
_
_
. (4.15)
Here, T is the Dyson Time Ordering operator, and maintains that a propagator is multiplied
in correct time order. By time order it is easiest to look at the approximation to the integral
solution.
U(t
o
, t
o
+ ∆t) =
kδt=∆t
k=1
exp
_
−iδtH
_
t
o
+kδt
2
__
(4.16)
where δt is much smaller then any time dependence in H. The product cannot be performed
in any other order then in the series given. This product requires two time consuming
operations to calculate. The ﬁrst is the matrix exponential which takes about N
3
operations
4.2. QUANTUM ALGORITHMS 83
to complete, the second is the matrix product, another N
3
operations. Numerically we can
solve any NMR problem in this fashion, calling it the ‘Direct Method.’
For many cases of NMR simulation, this method is not as bad as it sounds. If the
Hamiltonian is not time dependant, then the integral vanishes only to produce a constant,
∆t, multiplication factor. This reduces the problem to a single matrix exponentiation. Most
liquid state or static solid state NMR simulations can be calculated very quickly using the
direct method. We run into computational trouble when time dependence is introduced
into the system.
4.2.2 Periodicity and Propagator Reduction
Periodic Hamiltonians appear over and over in NMR. In this section we will go over
the few algorithmic tricks we can play with periodic Hamiltonians and their propagators.
By periodic we mean that
H(t) = H(t +τ
p
) (4.17)
where τ
p
is the period. There are three cases where using periodicity reduces the total
number of calculated propagators necessary to have a complete description of the dynamics.
The typical NMR experiment requires observation of the system at speciﬁc inter
vals of time, ∆t. Given that every observation is the same distance in time away from the
last observation and that the time dependence of the Hamiltonian is periodic, we can have
3 possible situations. Figure 4.1 shows the three possible situations given those conditions
if we wish to observe the Hamiltonian at some rational fraction of the this periodicity time
(
m
n
τ
p
). In general the m factor, called the periodicity factor, represents the number of peri
ods of length τ
p
that must occur before an observation is synchronous with τ
p
. The n factor,
4.2. QUANTUM ALGORITHMS 84
t
m%n=0
t
observe (m=n=1)
m>n
m=1, n>1
observe
U
0
t=0
U
0
U
1
...
U
carry
U
carry U
m1
U
m2
...
...
t=τ
p
t
observe
U
0
U
1
Uτ
p/n1
...
observe
Figure 4.1: Various propagators needed for an arbitrary rational reduction.
4.2. QUANTUM ALGORITHMS 85
called the observation factor, represents the number of the m sub–propagators necessary to
advance one observation time step. Each sub propagator (U
i
in Figure 4.1) is assumed to
been calculated via the direct method.
Point–to–Point, mod (m, n) = 0
The simplest way to use the periodicity is to realize that at each interval n of τ
p
(for a total time of nτ
p
) the propagator equation reduces to
U(nτ
p
) = (U(τ
p
))
n
. (4.18)
This means we only have to evaluate the computationally expensive integral in Eq. 4.16
n times, and we can take much larger steps in multiples of τ
p
. This method I will call
‘Point–to–Point’ (PtoP) as we can only get the dynamics at the speciﬁc point of t = nτ
p
,
not arbitrary points. The method is well suited for any rotor synchronized pulse sequences
where the pulses we apply and the rotor spinning frequency are synchronized. Here we only
have to store and calculate n propagators.
Rational Reduction, m > n (also n > m)
Many times one wants to get dynamics inbetween the period and we may be re
duced back to using the ‘Direct Method’ for such tasks. Consider still that our Hamiltonian
is periodic with a cycle of τ
p
. The condition is where n > m is also the case where n < m,
all we have to do here is switch the indices and perform an extra observation step at the
begining of the sequence. In this case there will be a propogator (U
carry
in Figure 4.1) that
will span over a τ
p
. To see how the reduction works ﬁrst consider a normal propagation
series in time shown in Table 4.1.
4.2. QUANTUM ALGORITHMS 86
Table 4.1: Time propagation using individual propagators via the Direct Method
time step Propagator series
1 U
0
2 U
1
U
0
3 U
2
U
1
U
0
4 U
3
U
2
U
1
U
0
5 U
4
U
3
U
2
U
1
U
0
6 U
5
U
4
U
3
U
2
U
1
U
0
7 U
6
U
5
U
4
U
3
U
2
U
1
U
0
8 U
7
U
6
U
5
U
4
U
3
U
2
U
1
U
0
9 U
8
U
7
U
6
U
5
U
4
U
3
U
2
U
1
U
0
10 U
9
U
8
U
7
U
6
U
5
U
4
U
3
U
2
U
1
U
0
11 U
10
U
9
U
8
U
7
U
6
U
5
U
4
U
3
U
2
U
1
U
0
Table 4.2: A reduced set of individual propagators for m = 9 and n = 7
observation time step Propagator series
1 U
6
U
5
U
4
U
3
U
2
U
1
U
0
=U
T
0
2 U
4
U
3
U
2
U
1
U
0
U
8
U
7
∗ U
T
0
=U
T
1
3 U
2
U
1
U
0
U
8
U
7
U
6
U
5
∗ U
T
1
=U
T
2
4 U
0
U
8
U
7
U
6
U
5
U
4
U
3
∗ U
T
2
=U
T
3
5 U
7
U
6
U
5
U
4
U
3
U
2
U
1
∗ U
T
3
=U
T
4
6 U
5
U
4
U
3
U
2
U
1
U
0
U
8
∗ U
T
4
=U
T
5
7 U
3
U
2
U
1
U
0
U
8
U
7
U
6
∗ U
T
5
=U
T
6
8 U
1
U
0
U
8
U
7
U
6
U
5
U
4
∗ U
T
6
=U
T
7
9 U
8
U
7
U
6
U
5
U
4
U
3
U
2
∗ U
T
7
=U
T
8
10 U
6
U
5
U
4
U
3
U
2
U
1
U
0
∗ U
T
8
=U
T
0
∗ U
T
8
Consider now the case where m = 9, and n = 7. Here we require 7 time steps for
each observation, and we know that every 9 observations the propagator sequence repeats.
Table 4.2 shows a clearer picture of this situation.
From this table, we can see that based on the repetition number m we only need
to calculate m sub–propagators, U
i
, each with a relatively small ∆t of τ
p
/m using the direct
method. Then calculate 9 more larger time step propagators (that span a ∆t of n/mτ
p
) of
propagators, U
T
i
using simple matrix multiplication. The hardest calculation is the using
4.2. QUANTUM ALGORITHMS 87
the direct method to calculate the initial 9 propagators, and this step cannot be avoided.
However the remaining 9 larger propagators require 63 extra matrix multiplications to
calculate. If the matrices are large, this can be an expensive operation. To further reduce
the number of matrix multiplications, we can use that fact that one typically calculates the
U
i
in sequence (i.e. i = 0, then i = 1, ..., i = m − 1) and that no matter what other tricks
we play, we know that we will have to calculate U
6
U
5
U
4
U
3
U
2
U
1
U
0
= U
T
0
. Along the way to
calculating U
T
0
we can easily store each sub sequence (U
0
, U
1
U
0
= U
1,0
, U
2
∗U
1,0
= U
2,1,0
, ...).
Call these sequences the ‘forward’ sequences. Calculating these results in at least m matrix
multiplications. Calculating the ‘backwards’ sequences (U
8
, U
8
U
7
= U
8,7
, U
8,7
∗U
6
∗ = U
8,7,6
,
...) is also relatively simple, because we have stored all the U
i
’s and we can use the same
procedure as in calculating the ‘forward’ sequences resulting in another m multiplications.
Calculating the forward propagators seems to at least make intuitive sense, so why
did we calculate the seemingly unnecessary backwards propagators? The next step is to
realize that inside each U
T
i
are sequences of U
i
that repeat many times. For instance the
sub sequence U
8
U
7
(a backwards propagator) appears 6 times in Table 4.2, so we could
save at least 5 matrix multiplications by simply storing the U
8
U
7
result the ﬁrst time we
calculate it. One can look over this entire set of propagators U
T
i
looking at the ones we
have already saved from the above operations to see that there will be some optimal set of
backwards and forward propagators that reduce the total number of matrix multiplications
needed.
Of course to ﬁgure out this rational reduction of propagators, no propagators are
actually necessary, simply the indexes i, the ‘forward’ labels, (i, j, k...) and the ‘backward’
labels (k, j, i, ...). Given m and n these can all be automatically generated as simple integers,
4.2. QUANTUM ALGORITHMS 88
Table 4.3: Matrix Multiplication (MM) reduction use rational reduction
m n Original MMs Reduced MMs % reduction
4 3 12 8 33%
6 5 30 14 53%
11 5 55 39 29 %
21 5 105 89 15%
9 7 54 21 61%
11 7 77 41 47%
13 7 91 55 40%
104 7 728 692 5%
104 103 10712 308 97%
as can each U
T
i
sequence. The problem is then reduced to ﬁnding each forward and backward
set of indexes inside each U
T
set of indices. Comparing the number of multiplications
required by each set and pick the minimum. After the minimum is found, it is up to the
user to provide the set of U
i
, U
i,j,k...
and U
k,j,i,...
, and the algorithm can then generate the
basic m propagators of observation. A C++ class is given in Appendix A.2.2 that performs
this task.
Table 4.3 shows the eﬀective reduction in the matrix multiplications using this
technique for a range of m and n. From the Table it is easy to see that if the periodicity
factor m is much diﬀerent then the observation factor n then the rational reduction does
not produce much improvement because we still need to calculate the m sub–propagators.
Figure 4.2 shows better picture as to the eﬀectiveness of the rational reduction method. In
the Figure, the spikes are where m becomes a multiple of n and we get a PtoP method.
Sub–Point–to–Point, m = 1, n > 1,
This case is another special case closely related to the PtoP method because it
essentially means that n is a factor of τ
p
, so each n sub–propagators calculated leads us
4.2. QUANTUM ALGORITHMS 89
0 20 40 60 80 100 120 140
0
10
20
30
40
50
60
70
80
90
Periodicity factor, m
r
e
d
u
c
t
i
o
n
(
%
)
percent reduction for observation factor n
n=3
n=5
n=7
n=20
Figure 4.2: Eﬀectiveness of the rational propagator reduction method.
back to the τ
p
. This special conditions leaves us only to calculate n sub–propagators and
the various combinations for an optimal minimization of the matrix multiplications. For
instance if n = 5 then we need to calculate the n sub–propagators (U
0
...U
4
) each spanning a
time of ∆t = τ
p
/n = τ
p
/5. While we complete one τ
p
cycle, we collect the sub multiplications
shown in Table 4.4 using them in later observation points. This particular case is a typical
NMR situation, and will be used later.
4.2.3 Eigenspace
The past methods have only been treating time dependence explicitly. We can
easily do a transformation to treat the frequency domain. This is a natural transformation
because our Hamiltonians all have the units of Hz and NMR typically collects data in equal
spaced time steps. Because Hamiltonians are Hermetian, we know that all the eigenvalues
4.2. QUANTUM ALGORITHMS 90
Table 4.4: For m = 1 and n = 5 we have this series of propagators necessary to calculate
the total evolution
observe Point Propogators
1 U
0
= U
T
0
2 U
1
U
0
= U
T
1
3 U
2
U
1
U
0
= U
T
2
4 U
3
U
2
U
1
U
0
= U
T
3
5 U
4
U
3
U
2
U
1
U
0
= U
T
4
6 U
T
0
U
T
4
7 U
T
1
U
T
4
8 U
T
2
U
T
4
... ...
are real and the eigenvectors form an orthonormal basis. The next few methods use the
eigenp–basis to potentially remove the explicit time dependence to avoid performing many
matrix multiplications, when only a matrix digitalization is necessary.
Eigenvalue propagation
One special case involves non time dependant Hamiltonians, or inhomogeneous
Hamiltonians. In this case the eigenvectors to not change in time, and we can easily write
our Hamiltonian in terms of the eigenvectors and eigenvalues.
H = ∆ Ω ∆
†
. (4.19)
where ∆ is a matrix of the eigenvectors, and Ω is a diagonal matrix of the eigenvalues and
the † is the adjoint (complex transpose) of a matrix, which for unitary matrices is also the
inverse. To propagate forward in time, we can use this property of unitary matrices and
matrix exponentials
exp[H] = ∆exp[Ω]∆
−1
. (4.20)
Placing this solution back into Eq. 4.15, we get
ρ(t) = ∆exp[−itΩ]∆
†
ρ∆exp[itΩ]∆
†
(4.21)
4.2. QUANTUM ALGORITHMS 91
In NMR we can only detect certain components of the density matrix at a given
time, those two components are the two axis where our coils sit, ˆ x and ˆ y. So we can only
detect I
x
and I
y
. We then need to project out the component of our detection operator
(which from now on will be called I
det
) in the density matrix via the trace operator Tr.
I
det
(t) = Tr[ρ(t)I
†
det
]I
det
(4.22)
Using Eq. 4.21, Eq. 4.22, and the cyclic permutation properties of the trace we ﬁnd that
I
det
(t) = Tr[
˜
ρ(t)
˜
I
†
det
]I
det
. (4.23)
where the
˜
A = ∆A∆
†
, a similarity transform. Our recorded signal, S is then just the Tr
constant
S(t) = Tr[exp[−itΩ] ˜ ρ
o
exp[itΩ]
˜
I
†
det
]. (4.24)
This results in a sum over all over the diagonal of the multiplication inside the result.
The multiplied result contains all the diﬀerences in frequencies in Ω with coeﬃcients given
by the ρ(t) and I
det
. Thus when we calculate this signal, we only need to be concerned
with the multiplied quantities along the diagonal, turns a previously N
3
operation into an
approximately N
2
operation.
Tr[AB] =
N
i
N
j
A
ij
B
ji
(4.25)
If we assume that we are sampling n steps the evolution in equal times of ∆t, our signal in
terms of explicit elements is
S(n∆t) =
N
k
N
j
I
det
kj
ρ
o
jk
(Φ
kj
)
n
(4.26)
where Φ
kj
is the transition frequency matrix, Φ
kj
= exp(−i(ω
k
−ω
j
)∆t). An algorithm for
this static case is shown in Appendix A.2.3.
4.2. QUANTUM ALGORITHMS 92
Eﬀective Hamiltonians
This method is an extension to the PtoP method. In the PtoP method, we have
only one propagator necessary to calculate our evolution. To move in to observation point n
of τ
p
it is then necessary to multiply the propagator n times, resulting in n∗N
3
operations.
We can in principle invert the propagator to ﬁnd the eﬀective Hamiltonian, H
eﬀ
for this
one period using the matrix logarithm.
H
eﬀ
=
−i
τ
p
log(U) (4.27)
This eﬀective form is now time independent on the period. Once we have the eﬀective
Hamiltonian, we can easily use this as the Hamiltonian in the static eigenvalue propagation
discussed in section 4.2.3. Thus we avoid performing many matrix multiplications. There is
a problem using this technique in general in that it has a very narrow frequency bandwidth.
The largest frequency it can accurately obtain from the propagator is one that falls within
the ±1/τ
p
/2 range. If the real Hamiltonian has frequencies outside this range (which it usu
ally does) then they will be folded into the resulting spectrum and will result in a cluttering
of spectral features. This problem leads to amplitude discrepancies in any frequencies of
integer multiples of the period, as higher order multiples that are not in the range will be
folded on top of ones that are in range.
This technique works very well when τ
p
is very short when compared to the real
Hamiltonian time dependence because our spectral window is quite large. If one can accu
rately claim this condition, no other technique can match this one for its speed in calculating
the evolution.
4.2. QUANTUM ALGORITHMS 93
Fourier components and the Floquet approximation
The Floquet method corrects the folding and amplitude problems of the eﬀective
Hamiltonian by working totally in frequency space. But before we can venture further into
using frequency domain methods, we need to decompose our Hamiltonian into its Fourier
components. Mind you, this is not necessarily where the Hamiltonian is diagonal, but where
the Hamiltonian can be broken into a form
H =
m
H
m
e
−imφ
. (4.28)
where φ is some phase factor, and H
m
is a Fourier component of H. Given our entire
sequence of possible rotations in Eq. 3.66 we have these Fourier components already calcu
lated as well as the phase factor. So the Fourier components of the Hamiltonian are nothing
more the rotated l, m components.
Assuming that the time dependence is in the φ phase factor, and can be factored
(φ = φ(t) = ωt), we can write a Floquet Hamiltonian H
F
[47, 48, 49, 50, 51] of the form
¸pn[ H
F
[qm) = nωδ
nm
δ
pq
+ ¸p[ H
n−m
[q) (4.29)
where p, q are spin states and n, m and Fourier indices. Both n and m have the range
(−∞, ∞) in integer multiples. Thus the Floquet Hamiltonian is an inﬁnity sized matrix
4.2. QUANTUM ALGORITHMS 94
which looks like
H
F
=
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
.
.
.
.
.
.
.
.
. H
n
.
.
. H
0
+ 2ω H
1
H
2
.
.
.
.
.
. H
−1
H
0
+ω H
1
H
2
H
−2
H
−1
H
0
H
1
H
2
H
−2
H
−1
H
0
−ω H
1
.
.
.
.
.
.
H
−2
H
−1
H
0
− 2ω
.
.
.
H
−n
.
.
.
.
.
.
.
.
.
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
_
(4.30)
To evolve this matrix we must diagononalize it, but this time we get the raw
frequencies and amplitudes that are time independent, so we only have to diagonalize it
once. However the matrix is inﬁnitely sized, so computationally we must set an arbitrary
size of NN. We then get N frequencies and amplitudes. If N is contains all the necessary
frequencies to describe our system Hamiltonian then this matrix truncation is valid, if not,
then we must go to higher and higher N. Eventually we will hit a computational limit
as digonalization become prohibitively hard. We do have another simpliﬁcation for this
matrix, in that the only H
n−m
terms that appear in NMR are usually n−m = ±2 for most
interactions and n − m = ±4 for second order interactions (the second order quadrupole
for instance). So the Floquet matrix is a banded matrix. It turns out that the size of H
F
necessary to handled most normal NMR spin systems is much to large to be used eﬃciently.
So this technique is not used much in computational NMR. It is still a valuable tool for
theoretical studies[52, 53, 54] and simuation of small (12 spins) systems.
It can be used rather powerfully when there are only a few frequencies that describe
the Hamiltonian. Cases like rotational resonance [53, 55, 56, 57] and multi–photon eﬀects
[3, 58].
4.2. QUANTUM ALGORITHMS 95
4.2.4 Periodicity and Eigen–Space methods
This section will cover the blending of both aspects of periodicity in the Hamilto
nians and the fact that they are easily decomposed Fourier components. In essence we wish
to combine the aspects of both the propagator reductions discussed in section 4.2.2 and the
Fourier methods discussed in 4.2.3.
COMPUTE
The COMPUTE algorithm was ﬁrst proposed by Eden et. al in 1996[59]. First,
let’s assume that we are observing in the regime in our periodic picture where m = 1 and
n > 1. Eden shows we can use the sub–propagators along with the total period propagator
to remove the frequencies wrapping and amplitude problems of the eﬀective Hamiltonian
method, as well as observe our system at times inbetween periods.
To describe the algorithm in more detail, I will use the notation as in Table 4.4,
and elucidated a bit more in Figure 4.3. First we note that we can separate the total period
propagator U
T
n−1
can be factored into its diagonal form via
U
T
n−1
= Γe
−iτpΩ
Γ
†
(4.31)
Our signal at each k∆t, where ∆t = τ
p
/n, is then following the same discussion as in section
4.2. QUANTUM ALGORITHMS 96
t=0
U
0
t=τ
p
/n
U
1
U
n1
U
n2
U
0
T
U
1
T
t=2τ
p
/n
U
n2
T
U
n1
T
t=(n1)τ
p
/n
Figure 4.3: Diagram of one Hamiltonian period and the propagator labels used for the
COMPUTE algorithm
4.2.3, and using the fact that ΓΓ
†
= I
e
S(k∆t) = Tr
_
ρ
T
(k∆t)I
det
¸
= Tr
_
U
T
k
ρ
o
_
U
T
k
_
†
I
det
_
= Tr
_
I
e
ρ
o
_
U
T
k
_
†
I
e
I
det
U
T
k
_
= Tr
_
ΓΓ
†
ρ
o
_
U
T
k
_
†
ΓΓ
†
I
det
U
T
k
_
= Tr
_
Γ
†
ρ
o
ΓΓ
†
_
U
T
k
_
†
I
det
U
T
k
Γ
_
= Tr
_
ρ
T
o
I
T
det
¸
(4.32)
where we have deﬁned these two important matrices
ρ
T
o
= Γ
†
ρ
o
Γ
I
T
det,k
= Γ
†
_
U
T
k
_
†
I
det
U
T
k
Γ.
(4.33)
The next part comes from the realization that the NMR signal is simply of sum of frequencies
and amplitudes. There are no explicit frequencies in the form of the signal in Eq. 4.32,
4.2. QUANTUM ALGORITHMS 97
but we are always free to multiply the signal by 1=exp[−i(ω
rs
)t] exp[i(ω
rs
)t], where ω
rs
=
(ω
r
−ω
s
). When we do this we get
S(k∆t) =
N
r,s=1
f
k
rs
exp[iω
rs
k∆t] (4.34)
where
f
k
rs
=
_
I
T
det,k
_
rs
_
ρ
T
o
_
sr
exp[−iω
rs
k∆t]. (4.35)
Eden showed in Ref. [59] that this function is periodic with k → k + n, and thus
can be expanded as a discrete Fourier series as
f
k
rs
=
n/2
j=−n/2+1
a
j
rs
exp[i2πkj/n] (4.36)
This form can be easily inverted to give us the complex amplitudes, a
j
rs
a
j
rs
= 1/n
n/2
j=−n/2+1
f
k
rs
exp[−i2πkj/n]. (4.37)
We now have the the complex amplitudes and the system frequencies exactly for this speciﬁc
∆t. In essence what we have done is 1) use the Eﬀective Hamiltonian method to get the
frequencies of within the period and 2) used the sub–propagators to correct these frequencies
and amplitudes due to the wrapping problems of the eﬀective approach. We did all this
only calculating n propagators. We still must calculated the list of transformed detection
matrices I
T
det,k
and amplitude functions f
k
rs
resulting in a few more multiplications.
The algorithm proceeds as follows
• Choose an n such that n∆t = τ
p
.
• Calculate all the U
T
k
from the system Hamiltonian.
• Invert U
T
n−1
using the matrix logarithm.
4.2. QUANTUM ALGORITHMS 98
• Calculate frequency diﬀerences, ω
rs
from the eigenvalues of the eﬀective Hamiltonian
and use eigenvectors to calculate ρ
T
o
.
• Using the U
T
k
propagators calculate and store all the I
T
det,k
matrices.
• Using Eq. 4.35 calculate and store all the f
k
rs
.
• To generate the observed spectrum simply apply Eq. 4.34 using the f
k
rs
and ω
rs
realizing that at k > n that f
k
rs
= f
k+n
rs
.
A code example of this is not given, because of a more recent extension to this
algorithm which we will discuss in the next section.
γCOMPUTE
Up until now, we have made minor assumptions about the Hamiltonians we are
dealing with: 1) they are periodic, and 2) the time dependence can usually be easily factored
out of the rest of the Hamiltonian. In order to describe the γCOMPUTE algorithm, I will
need to expand the Hamiltonian more then I have thus far. In the section 4.2.6 I describe
methods of integrating over space using various powder averages. The powder average is
necessary to properly simulate all the various orientations of a single crystal in a real powder
solid sample. This corresponds the molecule–to–rotor rotation described in section 3.3.2.
Below we write out explicitly all the rotational sums in the spherical tensor basis if we only
rotate the spatial degree of freedom
H
m
=
l
_
¸
¸
¸
¸
¸
¸
¸
¸
¸
¸
_
¸
¸
¸
¸
¸
¸
¸
¸
¸
¸
_
l
m
=−l
l
m
=−l
l
m
=−l
_
e
−imγ
lab
d
l
m,m
(θ
lab
) e
−im
φ
lab
_
_
e
−im
γrot
d
l
m
,m
(θ
rot
) e
−im
φrot
_
_
e
−im
γ
mol
d
l
m
,m
(θ
mol
) e
−im
φ
mol
_
A
l
m
_
¸
¸
¸
¸
¸
¸
¸
¸
¸
¸
_
¸
¸
¸
¸
¸
¸
¸
¸
¸
¸
_
T
l
m
(4.38)
4.2. QUANTUM ALGORITHMS 99
The typical φ
lab
is the rotor spinning rate ω
r
t. Because the high magnetic ﬁeld is
assumed cylindrically symmetric, the γ
lab
is arbitrary and constant, so we can easily choose
0. Condensing the molecule rotation into new
ˆ
A
m
terms, we get a more compact form of
the full Hamiltonian.
H
m
=
l
_
l
m
=−l
l
m
=−l
_
d
l
m,m
(θ
lab
) e
−im
ωrt
_ _
e
−im
γrot
d
l
m
,m
(θ
rot
) e
−im
φrot
_
ˆ
A
l
m
_
T
l
m
(4.39)
Collecting terms we get
H
m
=
l
_
l
m
=−l
l
m
=−l
_
d
l
m,m
(θ
lab
) e
−im
(ωrt+γrot)
_ _
d
l
m
,m
(θ
rot
) e
−im
φrot
_
ˆ
A
l
m
_
T
l
m
.
(4.40)
Now we notice that the rotor spinning rate and the γ
rot
powder angle are in the same
exponent of the sum, in essence the γ
rot
powder angles acts like a shift in in time. γ
rot
is typically considered constant through the evolution. We can factor out an ω
r
from the
expression ω
r
+ γ
rot
to get ω
r
(t + γ
rot
/ω
r
). In most circumstances ω
r
= 2π/τ
p
. We can
pick γ
rot
to be some multiple of our periodicity, say γ
rot
= c2πτ
p
/n, where c is some integer
index, and n is exactly the same n as discussed in the COMPUTE algorithm. We see the
eﬀect of performing a γ
rot
powder average in the COMPUTE framework is simply reordering
the sub–propagators U
k
. Rather then recalculating the U
k
for each diﬀerent γ
rot
angle, we
simply reuse the ones we have previously calculated saving us from have to perform a direct
method integration step for these angles.
To be a bit more explicit, we can write the propagator at k
th
or t = k/ω
r
/n division
relating to the previous k − 1 propagator as
U(t
1
, t
2
, γ) = U(t
1
+
γ
ω
r
, t
2
+
γ
ω
r
, 0) = U(
k − 1
nω
r
,
k
nω
r
,
2πc
n
) = U
k,c
(4.41)
Because of the relation of the time shift and the γ angle to previously calculated propagators,
4.2. QUANTUM ALGORITHMS 100
we can remove the c dependence so that
U
k,c
= U
(c+k−1 mod n),(0)
. (4.42)
Our total sub–period propagators, U
T
, then become
U
T
k.c
= U
(c+k−1 mod n),(0)
_
U
(n−1),(0)
_
m
_
U
(p mod n),(0)
_
†
: m = int
_
k +c
n
_
−
_
c
n
_
. (4.43)
We then use these propagators in the same analysis as in the COMPUTE method to get
an improved algorithm which shortens the total simulation time if we need to include γ
rot
angles.
This method was ﬁrst elucidated by Hohwy et. al. in Ref. [60]. However, the γ
rot
as a time shift was realized by both Charpentier[61] and Levitt[62]. An implementation of
the γ–COMPUTE algorithm is given in Appendix A.2.4.
4.2.5 Nonperiodic Hamiltonians
Non–periodic time dependant Hamiltonians are the hardest problems computa
tionally. There is almost no other technique to perform the simulations other then the
direct method. The direct method is very slow for problems in general, but it is the only
one. Pulse shaping[63], slow molecular motion[64, 65, 66], and chemical exchange[67, 68] are
the largest classes of non–periodic Hamiltonians. Molecular motion and chemical exchange
are sometimes treated in the fast regime where the time dependence can be averaged away.
4.2.6 Powder Average Integration
There are two extremes in solid–sate NMR. The ﬁrst is a single crystal experiment
where each molecule is aligned in the same direction. In this extreme, there is only one
angle that describes a molecular frame to the rotor frame. In this picture the γ–COMPUTE
4.2. QUANTUM ALGORITHMS 101
algorithm is invalid as there is only one γ angle. It also only requires us to compute the
observed signal once, making it similar in speed to a liquid NMR simulation.
The second extreme is much more common in solid–state NMR, where we have
many diﬀerent crystallites oriented randomly throughout the sample. It is typically assumed
that over the billions of crystallites, every possible orientation is present. To get our total
signal then requires a sum over all these crystallites over the entire volume.
S(t) =
_
S(t, Ω
rot
)dΩ
rot
(4.44)
In most cases we do not know the analytical form of S(t, Ω
rot
), so we are forced to perform
the integral as a discrete sum
S(t) =
i,j,k
S(t, φ
i
, θ
j
, γ
k
). (4.45)
Calculation of each S(t, φ
i
, θ
j
, γ
k
) can be time consuming itself, and to get the proper S(t)
we may have to perform many evaluations. Sampling the volume as best as possible using
as fewest possible angles is desired to achieve computational eﬃciency. The γ–COMPUTE
algorithm does aid us by attempting to eliminate the γ
k
part of the sum, however, both θ
j
and φ
i
remain. Instead of sampling the entire sphere represented by θ and φ we can look
at the angular dependences of the Hamiltonians. We can divide our spherical distribution
into equal volume octants as shown in Figure 4.4. The valid range of θ = [0, π] and the
valid range of φ = [0, 2π), and each octant has the range as shown in Figure 4.4. Using our
high ﬁeld expressed Hamiltonian form in Eq 4.40 where m = 0 (the only part that survives
the truncation)
H = T
2
0
2
m
=−2
2
m
=−2
_
d
2
0,m
(θ
lab
) e
−im
(ωrt+γrot)
_ _
d
2
m
,m
(θ
rot
) e
−im
φrot
_
ˆ
A
2
m
(4.46)
4.2. QUANTUM ALGORITHMS 102
θ=0
θ=π
φ=0
φ=π
φ=π/2
φ=3π/2
Figure 4.4: Octants of equal volume of a sphere.
Speciﬁc Hamiltonians can lead to a simpliﬁcation of this form and diﬀerent symmetry sets.
There are essentially 3 diﬀerent symmetry groups that exist in NMR[69].
• No Symmetry–requires all 3 angles to be fully integrated over the entire range.
0 ≤ θ ≤ π
0 ≤ φ < 2π
0 ≤ γ < 2π
(4.47)
• C
i
–Inversion symmetric therefore no γ dependence and inversion symmetric therefore
requiring only half a sphere. Such cases exist when the eigen–states of the Hamiltonian
do not change as the rotor rotates.
0 ≤ θ ≤
π
2
0 ≤ φ < 2π
(4.48)
4.3. CONCLUSIONS AND COMMENTS 103
• D
2h
–Cylindrically symmetric and inversion symmetric therefore no γ dependence.
Inversion symmetric requires only half a sphere, and cylindrically symmetric requires
two octants (where 0 ≤ θ ≤ π). The two together implies one octant.
0 ≤ θ ≤
π
2
0 ≤ φ ≤
π
2
(4.49)
This case is most prevalent when there is no η term in the Hamiltonians, and the
molecule frame is the same for all atoms under static conditions[6].
For each three symmetry groups there are a variety of ways of generating points
with in the required ranges[70, 71, 72, 73, 74, 75, 76, 77, 78]. Since we cannot sample an
inﬁnite number of angles, we wish to optimally choose the angles. From my own experi
ence the best powder averaging schemes for no symmetry are the 3DZCW schemes[79, 80]
using Gaussian quadrature[69]. For the C
i
symmetry the 2DZCW schemes seem to work
the best. Static NMR problems do not require that much time (by comparison to spin
ning simulations). The choice for a D
2h
average should be handled best by the Lebedev
schemes[81, 82].
Powder averaging is the easiest point at which to parallelize a NMR simulation.
Each new processor should allow for linear scaling of the problem (i.e. n processors reduces
the running time by n). To help create such parallel programs, an master/slave based
MPI[83] implementation backbone is given in Appendix A.1.4.
4.3 Conclusions and Comments
Much of the numerical aspects of NMR are treated in the framework of the algo
rithms presented in this chapter using the speciﬁc forms of the equations of motion found
4.3. CONCLUSIONS AND COMMENTS 104
in chapter 3. The algorithms presented here are the sum total of the ones available to the
NMR spectroscopist. Given each diﬀerent algorithm, powder averaging type, rotational
evolution, and Hamiltonians there are hundreds of diﬀerent simulations one can perform.
The basic reason to review all the available algorithms is to be able to choose the proper
algorithm, interaction set, and other parameters that will perform the simulation at the
greatest speed and eﬃciency. There is no ‘one’ algorithm that will provide the best answer
every time, as a result constructing a master program that performs every possible simula
tion will not prove fruitful. Instead a package that includes the algorithms, data structures,
and sub–routines needed to construct the correct program is more useful in general. The
next chapter will treat the development and implementation of such a toolkit.
105
Chapter 5
BlochLib
5.1 Introduction
In the chapter 2, I laid down a foundation for a set of highly optimized data
structures needed for NMR, basically the Complex number, the Vector and the Matrix.
Everything else in computational NMR is based on these simple data types. Having very
fast Vectors and Matrix operations will then determine the speed and functionality of the
rest of the code that uses them. For this point on, I will take for granted the fact that
we have these fast data structures. C++ and objects allow such creation of nice ‘block–
boxes’ that performs speciﬁc tasks without any input from the user. This ability to create
foundation and foundations upon foundations provide a simple way to construct our total
simulation with ease. I will now present a tool kit speciﬁc to both classes of NMR, the
quantum mechanical and classical.
5.2. THE ABSTRACT NMR SIMULATION 106
5.2 The Abstract NMR Simulation
Numerical simulations in NMR (and most other physical systems) can be divided
into 2 basic classes: Experimental Evolutions (EEs) and Theoretical Evolutions (TEs). The
main object of both is some sort of generated data and both typically require some input
of parameters.
5.2.1 Experimental Evolutions (EE)
In Figure 5.1a, we ﬁnd a pictorial representation of an EE. These types of simula
tions are some of the simplest to construct and generalize. A basic EE simulation/program
is one designed to mimic some experimental condition. The basic experimental condition
is an RF coil that applies RF pulses and provides a detection mechanism within chemical
sample. The main function of an experiment is to apply diﬀerent sets of pulse sequences to
retrieve diﬀerent sets of information.
Because of the wide variety of diﬀerent pulse sequences, an EE must ﬁrst act as
a Parameter Parser. The Parameter Parser takes in some set of conditions and sets the
various mathematical structures (the Objects) such that a Kernel can perform the proper
calculation(s) which produces some sort of data we wish to see, thus Output.
5.2.2 Theoretical Evolutions (TE)
The other class of simulation, Theoretical Evolutions (TEs) (see Figure 5.1b), are
used to explore theoretical frameworks and theoretical modeling. Of course there can be
much overlap between the EEs and TEs, but the basic tenet of a TE simulation is they
are a designed to explore the physical properties of the system, even those not assessable
5.2. THE ABSTRACT NMR SIMULATION 107
a) Experimental Evolutions
b) Theoretical Evolutions
Kernel
Obj1 ObjN
P
a
r
a
m
e
t
e
r
P
a
r
s
e
r
Output
Parameters
Obj1 ObjN
Output
Parameters
Kernel1 KernelN
Figure 5.1: Many basic simulations can be divided into two main subgroups, a) Experimen
tal Evolutions (EE), and b) Theoretical Evolutions (TE). EEs tend to use a solid Kernel
driver, whereas the TEs can use many diﬀerent Kernels, feedback upon itself; use the gen
erated data in other kernel, and so forth. For this reason EEs can be developed to a large
degree of generality on the input parameters (e.g. various diﬀerent types of pulse sequences).
Their main function is a parameter parser where much attention is given to the interface.
TEs, on the other hand, are usually complex in the kernels and transparent interfaces are
not necessarily their primary goal.
5.2. THE ABSTRACT NMR SIMULATION 108
to experiments, to develop an understanding and intuition about the systems involved.
Simulations of singular interactions (e.g. including only radiation damping in a spin system)
to see their eﬀect is one such example. Development of a master TE program proves near
impossible simply because of the magnitude of diﬀerent methods and ideas used to explore
an arbitrary model. The best one can do today is to create a tool kit that provides the
most common algorithms, structures, and ideas used for the theoretical modeling. These
tool kits should be a simple starting places for more complex ideas (see Figure 5.1). A good
overview of the methods desired in NMR can be found in Ref. [84].
5.2.3 Existing NMR Tool Kits
Programs such as Simpson[85] have generalized the EE form of NMR simulation
into a simple working structure not unlike programming a spectrometer itself. EEs typically
require only a few algorithms to solve the dynamics of the systems, the rest of the program
is simply a user interface to input experimental parameters (e.g. pulse sequences, rotor
angles, etc.). EEs are essential to understand or discover any anomalies in experimentally
observed data. Another common usage of EEs is to give the experimenter a working picture
of ‘what to expect’ from the experiment. Surprisingly, there are very few complete NMR
EE packages. In fact, up until this tool kit, Simpson seems to be the only EE publicly
available.
Currently there is only one TE tool kit available to the NMR spectroscopists,
Gamma[86]. The main focus of Gamma is liquid state NMR (the solid state practicalities
are becoming developed in later versions). However, NMR experimentation is evolving past
the basic high ﬁeld liquid experiment.
5.3. BLOCHLIB DESIGN 109
5.2.4 Why Create a new Tool Kit?
Complex interactions like the demagnetizing ﬁeld and radiation damping are be
coming important and are best treated classically (see Ref. [87] and references there in).
Solid state NMR (SSNMR) is being used more frequently and with better and better reso
lution and techniques. Exsitu NMR is a new branch currently under exploration[88, 89, 90]
requiring detailed knowledge of magnetic ﬁelds in the sample. Low ﬁeld experiments(see
[91] and references there in) are also becoming more common. Pulse shaping[92] and mul
tiple rotor angle liquid crystal experiments[93] are also becoming more frequent. Gamma
and Simpson are illequipped to handle these new developments.
To treat all these newer developments (and to use the fast data structures described
in chapter 2) I have created BlochLib to be the next generation NMR simulation tool kit.
The tool kit is quite large and the documentation that describes all of its functionality is
well over 1000 pages, I will try in thesis chapter to give a general overview of library itself.
The following section will discuss some generic classes of NMR simulations that drive the
basic design of the BlochLib. Following the design overview, several example programs will
be discussed. They will attempt to demonstrate both the generality of the library as well
as how to set up a basic program ﬂow from parameter inputs to data output.
5.3 BlochLib Design
The design of a given tool kit relies heavily on the objectives one wishes to accom
plish. These objectives then determine the implementation (code language, code structure,
etc). The key objectives for BlochLib are, in order of importance, speed, ease of use, the
incorporation of existing numerical techniques and implementations, and the ability to eas
5.3. BLOCHLIB DESIGN 110
ily create both TEs and EEs for NMR in almost any circumstance. Below, several issues
are addressed before the major design of BlochLib is discussed.
5.3.1 Existing Numerical Tool Kits
For the quantum mechanical aspects of NMR, the basic operation is matrix mul
tiplication. The same expression template methodology can also by applied to matrices.
However, there are certain matrix operations that will always require the use of a temporary
matrix. Matrix multiplication is one such operation. One cannot unroll these operations
because an evaluated point depends on more then one element in the input. So the task
becomes one of optimizing a matrixmatrix multiplication. This task is not simple; in fact
it is probably one of the more complex operations to optimize because it depends dramat
ically on the systems architecture. A tool kit called ATLAS (Automatically Tuned Linear
Algebra Software)[21] performs these optimizations.
The introduction of the fast Fourier transform made possible another class of
simulations. Since that time several fast algorithms have been developed an implemented
in a very eﬃcient way. The Fastest Fourier Transform in the West (FFTW)[94] is one of
the best libraries for the FFT.
Another relatively recent development in scientiﬁc simulations is the movement
away from supercomputers to workstation clusters. To use both of them eﬀectively one
needs to know how to program in parallel. The Message Passing Interface (MPI)[95, 83]
provides a generic interface for parallel programming.
Most any scientiﬁc endeavor eventually will have to perform data ﬁtting of exper
imental data to theoretical models. Data ﬁtting is usually a minimization process (usually
minimizing a χ
2
function). There are many diﬀerent types of minimization routines and im
5.3. BLOCHLIB DESIGN 111
plementations. One used fairly frequently for its power, speed, multiple types of algorithms
is the CERN package MINUIT[96].
5.3.2 Experimental and Theoretical Evolutions for NMR simulations
As stated above TEs tend to require more possible conﬁgurations then an EE
program. EEs tend to be heavily parameter based using a main driver kernel, while a
TEs are basically open ended in both parameters and kernels (a better assumption about
a TE simulation is that one cannot really make any assumptions). Figure 5.1 shows a
rough diagram of an NMR simulation for both types (of course it can be applied to many
simulation types).
EEs are easily parsed into four basic sections: Parameters, Parameter parser,
Main Kernel, and Data Output. The Parameters deﬁne a program’s input, the Parameter
parser decided what to do with the parameters, the Main Kernel performs the desired
computation, and the Data Output decides what to do with any generated data. BlochLib
is designed to make the Parameters, Main Kernel and Data Output relatively simple for
any NMR simulation. The Parameter Parser tends to be the majority of programming
an EE. BlochLib also has several helper objects to aid in the creation of the parser. The
objects Parameters, Parser and ScriptParse are designed to be extended. They serve as
a base for EE design. With these extendable objects almost any complex input state can
be treated with minimal programming eﬀort.
The Main Kernel drivers need to be able to handle the two distinct classes of NMR
simulation the quantum mechanical and the classical as described in Chapter 4.
With these basic ideas of a TE and EE, the basic design of BlochLib will be
described in the next section.
5.3. BLOCHLIB DESIGN 112
5.3.3 BlochLib Layout
BlochLib is written entirely in C++. Figure 5.2 shows the basic layout of the tool
kit. The Utilities, Parameters, Aux Libs, Containers, and Kernels sections comprise the
basic beginning of the tool kit and have little to do with NMR. They form a basic generic
data structure framework to perform almost any high performance scientiﬁc simulations.
The Quantum Mechanic and Bloch Equation sections assemble objects that comprise the
backbone for the NMR simulations. Finally the Programs section assembles the NMR pieces
into functional programs which perform general NMR simulations (like Solid), calculate
arbitrary ﬁelds from coil geometries, and a wide range of investigative programs on NMR
systems (see Table 5.2). It is designed to be as modular as possible with each main section
shown in Figure 5.2 treated as separate levels of sophistication. The ﬁrst levels are the
main numerical and string kernels, the second levels utilize the kernels to create valid
mathematical objects, the third levels uses these objects to perform complex manipulations,
and the fourth levels creates a series of modules speciﬁc to NMR for both the classical and
quantum sense.
It uses C++ wrappers to interface with MPI, ATLAS, FFTW, and MINUIT.
BlochLib uses MPI to allow for programming in parallel and to pass the parallel objects to
various classes to achieve a seamless implementation in either parallel or serial modes. It also
allows the user to put and get the libraries basic data types (vectors of any type, matrices of
any type, strings, coords of any type, vectors of coords of any type) with simple commands
to any processor. The ATLAS library provides the backbone of the matrix multiplication for
BlochLib. Figure 5.3 shows you some speed tests for the basic quantum mechanical NMR
propagation operations. Each code sample (except for Matlab) was compiled using the GNU
5.3. BLOCHLIB DESIGN 113
Utilities
Global functions
Constants
Matlab I/O
VNMR I/O
Parameters
Parameter sets
Math parser
Script parser
Aux Libs
FFTW
MPI
ATLAS
MINUIT
Containers
Coords
Vectors
Matrices
Grids
Kernels
Shapes
ODE solvers
Stencils
Quantum Mechanics
Isotopes
Tensors, spin operators
Spin systems
Interactions, rotations
Solid System
FID algorithms
Bloch Equations
Bloch Parameters
Gradients, Pulses
Field calculators
Interactions
Bloch
Solvers
Programs
Solid
Fields
'Others'
Figure 5.2: The basic design layout of the BlochLib NMR tool kit.
5.3. BLOCHLIB DESIGN 114
compiler (g++) using the same optimizations (O3 ﬁnlinefunctions funroolloops). Both
a, b, and c are full, square, complex matrices. ATLAS shows the fastest speed, but BlochLib
using ATLAS as a base is not far behind. An existing C++ library, Gamma, shows normal
nonoptimized performance. Matlab’s algorithm is slowed appreciably by this expression
because the overhead on its use of temporaries is very high. It may be interesting to note
that the speed of Matlab’s single matrix multiply (c = a ∗ b) is much better (and close to
that of Gamma’s) then the performance shown for (c = a∗b ∗a
†
) because of this temporary
problem. The matrix sizes are incremented in typical numbers of spin 1/2 particles. A ‘1
spin 1/2’ matrix is a 2 2, a ‘5 spin 1/2’ matrix is 32 32, and a ‘9 spin 1/2’ matrix is
512 512.
You may notice that BlochLib’s speed is slower then ATLAS’s even though the
same code is used. The reason for this discrepancy is discussed in section 5.3.4. BlochLib
uses FFTW to perform FFTs on its vectors and matrices, and allows the usage of the
MINUIT algorithms with little or no other conﬁguration.
The containers are the basic building blocks. It is critical that the operations on
these objects are as fast as possible. The optimizations of vector operations are critical
to performance of classical simulations as the solving of diﬀerential equations take place
on the vector level. Matrix operations are critical for quantum mechanical evolutions and
integration. For this reason the coord, Vector, and matrix classes are all written using
expression templates, with the exception of the matrix multiplication and matrix division
which use the ATLAS and LU decompositions algorithms respectively. The coord<> ob
ject is exceptionally fast and should be used for smaller vector operations. The coord<>
object is speciﬁcally made for 3space representations, with speciﬁc functions like rotations
5.3. BLOCHLIB DESIGN 115
c=a*b*adjoint(a)
log(N)
M
F
L
O
P
S
/
s
e
c
o
n
d
NxN Matrices
1 spin 1/2
9 spin 1/2
5 spin 1/2
10
0
10
1
10
2
10
3
0
100
200
300
400
500
600
ATLAS
BlochLib1.0w/ ATLAS
BlochLib1.0NO ATLAS
Gamma 4.0.5
Matlab 6.0.88
Figure 5.3: This ﬁgure shows a speed test for the common NMR propagation expression
c = a ∗ b ∗ a
†
in Millions of Floating point operations (MFLOPS) per second performed on
a 700 MHz Pentium III Xeon processor running Linux (Redhat 7.2).
5.3. BLOCHLIB DESIGN 116
and coordinate transformations which only function on a 3space. However, any length
is allowed, but as Figure 2.4 shows, the Vector speed approaches the coord<> for large
N, and with much less compilation times. The matrix class has several structural types
available: Full (all elements in the matrix are stored), Hermitian (only the upper triangle
of the matrix are stored), Symmetric (same as Hermitian), Tridiagonal (only the diagonal,
the super–diagonal, and the sub–diagonal elements are stored), Diagonal (only the diago
nal is stored), and Identity (assumed ones along the diagonal). Each of these structures
has speciﬁc optimized operations, however, the ATLAS matrix multiplication is only used
for Full matrices. There are also a wide range of other matrix operations: LU decompo
sitions, matrix inverses, QR decompositions, GramSchmidt orthonormalization, matrix
exponentials and matrix logarithms. The Tridiagonal structure has an exceptionally fast
LU decomposition. The Grid class consists of a basic grid objects and allows for creation
of rectangular Cartesian grid sets.
The utilities/IO objects include several global functions that are useful string
manipulation functions. These string functions power the parameter parsing capabilities of
BlochLib. Several basic objects designed to manipulate parameters are given. The Parser
object can be used to evaluate string input expressions. For instance if “3 ∗ 4/ sin(4)” was
entered, the Parser can evaluate this expression to be −15.85. The object can also use
variables either deﬁned globally (visible by every instance of Parser) or local (visible only
be the speciﬁc instance of Parser). For examples, if a program registers a variable x = 6, the
Parser object can use that variable in an expression, like “sin(x)∗3”, and return the correct
value, −0.83. The Parameters object comprises the basic parameter input capabilities.
Large parameter sets can be easily grouped into sections and passed between other objects
5.3. BLOCHLIB DESIGN 117
in the tool kit using this object. The parameter sets can be nested (parameters sets within
parameters sets) and separated. Creation of simple custom scripts can be performed using
the ScriptParse object in conjunction with Parser. The ScriptParse object is used to
deﬁne speciﬁc commands to be used in conjunction with any mathematical kernels.
Data output can be as complicated as the data input. The Parameters object
can output and update speciﬁc parameters. Any large amount of data (like matrices and
vectors) can be written to either Matlab (5 or greater) format. One can write matrices,
vectors, and coords, of any type to the Matlab ﬁle, as well as read these data elements from
a Matlab binary ﬁle. Several visualization techniques are best handled in the native format
of NMR spectrometer software. A VNMR (Varian) reader and writer of 1D and 2D data
is available as well as a XWinNMR (Bruker) and SpinSight (Chemagnetics) 1D and 2D
readers are also included. Any other text or binary formats can be constructed as needed
using the basic containers.
The next level comprises the function objects, meaning they require some other
object to function properly. The XYZshape objects require the Grid objects. These combine
a set of rules that allow speciﬁc Cartesian points to be included in a set. It basically
allows the construction of nonrectangular shapes within a Cartesian grid. For instance
the XYZcylinder object will remove all the points not included in the cylinder dimensions.
Similar shapes exist for slice planes and rectangles, as well as the capability to construct
other shapes. The shapes themselves can be used in combination (e.g. you can easily specify
a grid to contain all the points within a cylinder and a rectangle, using normal operators
and (&& ) and or (), “XYZcylinder && XYZrect”).
The ODE solvers require function generation objects . Available ODE solvers are
5.3. BLOCHLIB DESIGN 118
listed in section 4.1.2. The solvers are created as generically as possible, allowing for vari
ous data types (double, float, complex) and containers (Vectors, coords, matrices, and
vectors of coords). The ODE solver requires another object that deﬁnes a function. All
the algorithms require the same template arguments, template<class Engine T, class
ElementType T, class Container T> . Engine T is another class which deﬁnes the func
tion(s) required by the solver. ElementType T is the precision desired or another container
type (it can be things like double, float, coord<>, Vector<>, etc.). The ElementType T
is the type inside the container, Container T. For instance if ElementType T=double, then
Container T will usually be Vector<double> or coord<double, N>. The CashKarp
RungeKutta 5th order method (the ckrk class) is a basic work horse medium accuracy. It
is a good ﬁrst attempt for attempting to solve ODEs[42, 45]. The BulirschStoer extrap
olation method (the bs class) is of relatively high accuracy and very eﬃcient (minimizes
function calls). However, stiﬀ equations are not handled well and it is highly sensitive
to impulse type functions. The BlochSolver object uses the bs class as its default ODE
solver [43, 41, 44, 45]. The semiimplicit BulirschStoer extrapolation method is base on the
BulirschStoer extrapolation method for solving stiﬀ sets of equations. It uses the jacobian
of the system to handle the stiﬀ equations by using a combination of LU decompositions
and extrapolation methods[97, 45]. All the methods use adaptive steps size controls for
optimal performance.
Finally, the stencils perform the basic ﬁnite diﬀerence algorithms over vectors
and grids. Because there is no array greater then two dimensional in BlochLib yet, the
stencils over grid spaces are treated much diﬀerently then they would be over a standard
three dimensional array. They are included in this version of BlochLib for completeness,
5.3. BLOCHLIB DESIGN 119
however, the Ndimensional array and tools should be included in later versions.
At this point the tool kit is split into a classical section and a quantum section.
Both sections begin with the basic isotropic information (spin, quantum numbers, gamma
factors, labels, mass, momentum).
The quantum mechanical structures begin with the basic building blocks of spin
dynamics: the spin and spatial tensors, spin operators, and spin systems. Spatial tensors
are explicitly written out for optimal performance. The spin operators are also generated
to minimize any computational demand. There is a Rotations object to aid in optimal
generation of rotation matrices and factors given either spherical or Cartesian spaces. After
the basic tensor components are developed, BlochLib provides the common Hamiltonians
objects: Chemical Shift Anisotropy (CSA), Dipoles, Scalar couplings, and Quadrupoles as
described in section 3.3.
These objects use the Rotations object in the Cartesian representation to generate
rotated Hamiltonians. The HamiltonianGen object allows for string input of Hamiltonians
to make arbitrary Hamiltonians or matrix forms more powerful. For example, the input
strings “45 ∗ pi ∗ (Ix 1 +Iz 0)” (Ix 1 +Iz 0 are the x and z spin operators for spin 1 and
0 respectively), and “T21 0, 1 ∗ 56” (T21 0, 1 is the second rank, m=1 spin tensor between
spin 0 and spin 1), can be parsed by the HamiltonianGen much like the Parser object.
The SolidSys object combines the basic Hamiltonians, rotations, and spin operators into a
combined object which generates entire system Hamiltonians and provides easy methods for
performing powder averages and rotor rotations to the system Hamiltonian. This class can
be extended to any generic Hamiltonian function. In fact, using the inheritance properties of
SolidSys is imperative for further operation of the algorithm classes oneFID and compute.
5.3. BLOCHLIB DESIGN 120
The Hamiltonian functions from the SolidSys object, or another derivative, act as the
basis for the oneFID object that will choose the valid FID collection method based on rotor
spinning or static Hamiltonians. It uses normal eignvalue propagation for static samples
and the γCOMPUTE[60] algorithm for spinning samples. If the FID is desired over a
powder, the algorithm is parallelized using a powder object. The powder object allows for
easy input of powder orientation ﬁles and contains several built–in powder angle generators.
For classical simulations the relevant interactions are oﬀsets (magnetic ﬁelds), T
2
and T
1
relaxation, radiation damping, dipole–dipole interactions, bulk susceptibility, the
demagnetizing ﬁeld, and diﬀusion
1
as described in section 3.1.
These interactions comprise the basis for the classical simulations. Each interaction
is treated separately from the rest, and can be either extended or used in any combination
to solve the system. The grids and shapes interact directly with the Bloch parameters
to creates large sets of conﬁgured spins either in gradients or rotating environment. New
interactions can be added using the framework given in the library. The interactions are
optimally collected using the Interactions object, which is a crucial part of the Bloch
object. The Bloch object is the master container for the spin parameters, pulses, and
interactions. This object is then used as the main function driver for the BlochSolver
object (a useful interface to the ODE solvers).
As magnetic ﬁelds are the main interactions of classical spins, there is an entire
set of objects devoted to calculating magnetic ﬁelds for a variety of coil geometries. The
basic shapes of coils, circles, helices, helmholtz, lines, and spirals, are built–in. These
particular objects are heavily parameter based, requiring positions, turns, start and end
points, rotations, centering, lengths, etc. One can also create other coil geometries and add
1
In the current version of BlochLib, diﬀusion is not treated.
5.3. BLOCHLIB DESIGN 121
Table 5.1: Available Matlab visualization functions in BlochLib
Matlab Function Desicrption
Solidplotter A GUI that plots many of the NMR ﬁle formats
plotter2D A function that performs generic data plotting
plotmag Visualization functions for the magnetic ﬁeld calculators
plottrag Magnetization trajectories classical evolutions visualizations
them to the basic coil set (examples are provided in the tool kit). The magnetic ﬁelds can
be added to the oﬀset interaction object to automatically create a range of ﬁelds over a
grid structure, as well as into other objects to create rotating or other time dependant ﬁeld
objects.
No toolkit would be complete without examples and useful programs. Many pro
grams come included with BlochLib (see Table 5.2). Also included are several Matlab
visualization functions (see Table 5.1) that interact directly with the data output from
the magnetic ﬁeld generators plotmag, the trajectories from solving the Bloch equations,
plottraj, and generic FID and data visualization, plotter2D and Solidplotter.
5.3.4 Drawbacks
As discussed before, the power of C++ lies within the object and templates that
allow for the creation of generic objects, generic algorithms, and optimization. There are
several problems inherent to C++ that can be debilitating to the developer if they are not
understood properly. The ﬁrst three problems revolve around the templates.
Because templated objects and algorithms are generic, they cannot be compiled
until used in a speciﬁc manner (the template is expressed). For example to add two vectors,
the compiler must know what data types are inside the vector. Most of the main mathe
matical kernels in BlochLib cannot be compiled until expressed (matrices, vectors, grids,
5.3. BLOCHLIB DESIGN 122
shapes, coords, and the ODE solvers). This can leave a tremendous amount of overhead for
the compiler to unravel when a program is actually written and compiled.
The other template problem arises from the expression template algorithms. Each
time a new operation is performed on an expression template data type (like the vectors),
the compiler must ﬁrst unravel the expression, then create the actual machine code. This
can require a large amount of time to perform, especially if the operations are complex.
The two template problems combined require large amounts of memory and CPU time
to perform, however, the numerical beneﬁts usually overshadow these constraints. For
example the bulksus example in BlochLib takes approximately 170 Mb of memory and
around 90 seconds (using gcc 2.95.3) to optimally compile one source ﬁle, but the speed
increase is approximately a factor of 10 or greater. Compiler’s themselves are getting better
at handling the template problems. For the same bulksus example, the gcc 3.1.1 compiler
took approximately 100 Mb of memory and around 45 second of compilation time.
The ﬁnal template problem arises from expression template arithmetic, which
require a memory copy upon assignment (i.e. A=B). Nonexpression template data types
can pass pointers to memory rather than the entire memory chunk. For smaller array sizes,
the cost of this copying can be signiﬁcant with respect to the operation cost. The eﬀect
is best seen in Figure 5.3 where the pointer copying used for the ATLAS test saves a few
MFLOPS as opposed to the BlochLib version. However, as the matrices get larger the
relative cost becomes much smaller.
The last problem for C++ is one of standardization. The C++ standard is not
well adhered to by every compiler vendor. For instance Microsoft’s Visual C++ will not
even compile the most basic template code. Other compliers cannot handle the memory
5.4. VARIOUS IMPLEMENTATIONS 123
requirements for expression template unraveling (CodeWarrier (Metrowerks) crashes con
stantly because of memory problems from the expression templates). The saving grace for
these problems is the GNU compiler, which is a good optimizing compiler for almost every
platform. GNU g++ 3.2.1 adheres to almost every standard and performs optimization of
templates eﬃciently.
5.4 Various Implementations
This section will describe a basic design template to create programs from BlochLib
using the speciﬁc example of the program Solid. Solid is a generic NMR simulator. Several
other programs are brieﬂy described within the design template. The emphasis will not be
on the simulations themselves, but more on their creation and the modular nature the tool
kit.
There is potentially an inﬁnite number of programs that can be derived from
BlochLib, however, the tool kit comes with many of the basic NMR simulations programs
already written and optimized. These programs serve as a good starting place for many
more complex programs. In Table 5.2 is a list of the programs included and their basic
function. Some of them are quite complicated while others are very simple. Describing
each one will show a large amount of redundancy in how they are created. A few of the
programs which represent the core ideologies used in BlochLib will be explicitly considered
in the following sections.
5.4. VARIOUS IMPLEMENTATIONS 124
Table 5.2: Key examples and implementation programs inside BlochLib
Catagory Folder Description
Classical
bulksus Bulk susceptibility interaction
dipole Dipole–dipole interaction over a cube
echo A gradient echo
EPI an EPI experiment[98]
magfields Magnetic ﬁeld calculators
rotating field Using ﬁeld calculators and oﬀset interactions
splitsol Using ﬁeld calculators for coil design
mas Simple spinning grid simulation
raddamp Radiation damping interaction
relaxcoord T
1
and T
2
oﬀ the zaxis
simple90 Simple 90
◦
pulse on an interaction set
yylin Modulated demagnetizing ﬁeld example[87]
Quantum
MMMQMAS A complex MQMAS program
nonsec Nonsecular quadrupolar terms exploration
perms Permutations on pulse sequences
shapes A shaped pulse reader and simulator
Solid2.0 General Solid State NMR simulator
Other
classes Several ‘HowTo’ class examples
data readers Data reader and conversion programs
diffusion 1D diﬀusion example
mpiplay Basic MPI examples
5.4.1 Solid
The program Solid represents the basic EE quantum mechanical simulation pro
gram. Solids basic function is to simulate most 1D and 2D NMR experiments. It behaves
much like Simpson but is faster for large spin sets as shown in Figure 5.4. Solid tends to be
slower for small spin sets as explained in section 5.3.4. All simulations were performed on a
700 MHz Pentium III Xeon (Redhat 7.3), compiled with gcc 2.95.3 with donditions the same
as those shown in Figure 5 of Ref. [85] for Figure 5.4a. Figure 5.4b shows the speed of the
simulation of a C7 with simulations conditions the same as those shown in Figure 6e of Ref.
[85]. In both cases the extra spins are protons with random CSAs that have no interactions
between with the detected
13
C nuclei. Solid is essentially a parameter parser which then
sends the obtained parameters to the main kernel for evaluation. The EE diagram (Figure
5.4. VARIOUS IMPLEMENTATIONS 125
2 3 4 5 6
10
0
10
1
10
2
10
3
10
4
number of spins
l
o
g
(
t
i
m
e
)
s
e
c
o
n
d
s
10
2
10
3
10
4
10
5
10
6
10
7
l
o
g
(
t
i
m
e
)
s
e
c
o
n
d
s
a)
b)
Figure 5.4: Time for simulations of Solid (solid line) and Simpson (dashed–dotted line) as
a function of the number of spins. a) shows the simulation of a rotary resonance experiment
on set pair of spins, and b) shows the speed of the simulation of a C7.
5.4. VARIOUS IMPLEMENTATIONS 126
5.1) can be extended to more speciﬁc object usage used in Solid (Figure 5.5). Three basic
sections are needed. Deﬁnitions of a solid system (spins), deﬁnition of powder average
types, other basic variables and parameters (parameters and the subsection powder), and
ﬁnally the deﬁnition of a pulse section where spin propagation and ﬁd collection is deﬁned
(pulses). The pulses section contains the majority of Solid’s functionality. Based on this
input syntax, a simple object trail can be constructed. MultiSolidSys contains at least
one (or more) SolidSys objects. This combined with the powder section/object deﬁnes the
Propagation object where the basic propagation algorithms are deﬁned. Using the extend
able ScriptParse object, the SolidRunner object deﬁnes the new functions available to
the user. SolidRunner then combines the basic FID algorithms (in the oneFID object), the
Propagation object, and the output classes to perform the NMR experimental simulation.
Solid has three stages, parameter input, main kernel composition, and output
structures. The EE normal section, parameter parser, was written to be the main interface
to the kernel and output sections. It extends the ScriptParse object to add more simulation
speciﬁc commands (spin evolution, FID calculation, and output).
There are three basic acquisition types Solid can perform: a standard 1D, a stan
dard 2D, and a pointtopoint (obtains the indirect dimension of a 2D experiment without
performing the entire 2D experiment). Simple 1D simulations are shown in Figure 5.6.
The results of a 2D and pointtopoint simulation of the postC7 sequence[99]
are shown in Figure 5.7. Appendix A.3.1 shows the input conﬁguration scripts for the
generation of this data.
5.4. VARIOUS IMPLEMENTATIONS 127
spins{
numspin 2
T 1H 0
T 13C 1
C 1000 2000 0 0
D 231 0 1
}
S
p
i
n
p
a
r
a
m
e
t
e
r
s
parameters{
powder{
aveType zcw
thetaStep 233
phiStep 144
}
wr=3000
npts1D=512
ro=Iz
detect=Ip_0
}
S
i
m
u
l
a
t
i
o
n
p
a
r
a
m
e
t
e
r
s
pulses{
amp=15000
amplitude(amp)
1H:pulse(1/amp/4,0)
fid()
savefidtext(data)
}
P
u
l
s
e
s
e
q
u
e
n
c
e
Object Trail
SolidSys
Parameters
Input Syntax
MultiSolidSys
Parameters
Parameters
powder
ScriptParse
Parser
SolidRunner
Propagation
Defined Commands
pulse, delay,
amplitude, offset
ro, detect, use,
alterSys, reuse,
fid, 2D, show,
ptop, savefid
oneFID
matstream
Figure 5.5: The design of the EE program Solid derived from the input syntax.
5.4. VARIOUS IMPLEMENTATIONS 128
2 1.5 1 0.5 0 0.5 1 1.5 2
x 10
4
ω
r
=2000
(Hz)
ω
r
=0 a)
b)
Figure 5.6: A two spin system as simulated by Solid where a) is with no spinning, and b)
is with a rotor speed of 2000 Hz at the magic angle (54.7 deg). The spins system included 2
CSA’s with the ﬁrst spins parameters as ω
iso
= 5000 ∗ 2π, ω
ani
= 4200 ∗ 2π, and η = 0, the
second spin’s parameters as ω
iso
= 5000 ∗ 2π, ω
ani
= 6012 ∗ 2π, and η = 0.5, with a scalar
J coupling of 400 Hz between the two spins. For a) and b) 3722 and 2000 powder average
points were used respectively. See Appendix A.3.1 for input conﬁguration ﬁle.
2500 2000 1500 1000 500 0 500 1000 1500 2000 2500
Hz
a) pointtopoint
b) 2D
H
z
Figure 5.7: A two spin system as simulated by Solid of the postC7 sequence where a) is
collected using a pointtopoint acquisition, and b) is a full 2D collection. The spins system
includes a dipole coupling of 1500 Hz. For both a) and b) 233 powder average points were
used. See Appendix A.3.1 for input conﬁguration ﬁle.
5.4. VARIOUS IMPLEMENTATIONS 129
5.4.2 Classical Program: Magnetic Field Calculators
Included in BlochLib is the ability to calculate magnetic ﬁelds over arbitrary coil
geometries. The main ﬁeld algorithm calculates a discrete integral of Ampere’s equation
for the magnetic ﬁeld.
B(r) =
µ
o
4π
_
I(r
) dl(r
)
[r −r
[
2
(5.1)
where the magnetic ﬁeld at the point r, B(r), is the volume integral of the current at
r
, I(r
), crossed into a observation direction, dl(r
), divided by the square of the distance
between the observation point, r, and the current point, r
. One way to evaluate this integral
numerically, the integral is broken into a sum over little lines of current (the BiotSavart
Law). For this to function properly numerically, the coil must be divided into small line
segments.
There are numerous coil geometries, but most of the more complex designs can
be broken into a set of primitive objects. The geometric primitives included in BlochLib
are lines, circles, spirals, helices, an ideal helmholtz pair (basically 2 circles separated by
a distance), a true helmholtz pair (two sets of overlapping helices), input ﬁles of points,
and constant ﬁelds. BlochLib also allows the user to create their own coil primitives and
combine them along with the rest of the basic primitives. Figure 5.8 shows the basic design
of the ﬁeld calculator using the MultiBiot object.
There are two basic parameters sections needed. The ﬁrst describes the coil geom
etry using the basic elements (see text) and any user written coils geometries. The second
describes the Cartesian grids where the ﬁeld will actually be calculated. Again once the
parameter sets are known a simple object trail can be developed. Initially the user must
register their own geometries into the BiotCoil list. The parameters then feed into the
5.4. VARIOUS IMPLEMENTATIONS 130
grid{
min 1,1,1
max 1,1,1
dim 5,5,5
}
G
r
i
d
P
a
r
a
m
e
t
e
r
s
coils{
subcoil1{
type circle
.....
}
subcoil2{
type line
.....
}
} C
o
i
l
P
a
r
a
m
e
t
e
r
s
parameters
section coils
matout fname.mat
...
}
A
u
x
P
a
r
a
m
e
t
e
r
s
Object Trail
Grid
Parameters
Input Syntax
XYZshape
Parameters
Parameters
MultiBiot
BiotCoil
Biot
Register Coils
Register MPI object
Calc field
Write data
Figure 5.8: The basic design for the Field Calculator program.
XYZshape and MultiBiot objects. Parallelization can be implemented simply by deﬁn
ing the MPIworld object and passing it to the MultiBiot object. The data is written in
2 formats; into one readable by Matlab for visualization and into a ﬁle readable by the
MultiBiot object.
This program is included in BlochLib under the magfields directory (see Table
5.2). Figure 5.9 shows the data generated by the program. The input ﬁle for this program
can be seen in Appendix A.3.2. It should be noted that the convergence of the integral in
5.4. VARIOUS IMPLEMENTATIONS 131
Eq. 5.1 is simply a function of the number of line segments you choose for the coils.
5.4.3 Classical Programs: Bloch Simulations
Programs of this type are designed to function on large spin sets optimally based
on the interactions present. The basic layout for these simulation can be see in Figure
5.10. These programs typically need as much optimization as possible in order to function
optimally over large spin sets. As a result, the parameter input is expected to be minimal,
with the bulk of the design to aid in optimization of the interaction sets and pulse sequences
used. Items in gray are optional objects, that can be simply added in the speciﬁc object
chain to be used.
The Grid serves as the basis for much of the rest of the Bloch interactions and
Bloch parameters. Grids also serve as the basis for gradients and physical rotations. The
interactions are also a key part of the simulation and can rely on the grid structures as well
as any magnetic ﬁelds calculated. A pulse on a Bloch system represents a type of impulse
function to the system. A pulse should be treated as a separate numerical integral step due
to this impulse nature (such impulses can play havoc with ODE solvers). The pulses, Bloch
parameters, and interactions are ﬁnally wrapped into a master object, Bloch, which is then
fed into the BlochSolver object which performs the integration.
Bulk Susceptibility
One such implementation attempts to simulate the result obtained in Ref. [100]
Figure 2. This is a HETCOR (Heteronuclear Correlation) experiment between a proton
and a phosphorous. The delay in the HETCOR sequence (see Figure 5.11a) allows the
oﬀset of the
1
H to evolve. Next the
1
H magnetization is placed back on the zaxis. The z
5.4. VARIOUS IMPLEMENTATIONS 132
2
1
0
1
2
3
2
1
0
1
2
5
0
5
y axis (cm)
x axis (cm)
z
a
x
is
(
c
m
)
G
a
u
s
s
G
a
u
s
s
G
a
u
s
s
Bx
By
Bz
a)
b)
c)
d)
z
(
c
m
)
x(cm)
y
(c
m
)
Figure 5.9: The magnetic ﬁeld calculated by the program shown in Figure 5.8. The conﬁgu
ration ﬁle is shown in Appendix A.3.2 The Matlab function, plotmag, was used to generate
these ﬁgures (see Table 5.1). The coil and the sampled grid are shown in a), the ﬁelds along
the x, y, z directions are shown in b) –d) respectively.
5.4. VARIOUS IMPLEMENTATIONS 133
Basic Parameters Input
Grid
XYZshape
Parameters
Gradient Grids,
Rotating Grids
ListBlochParameter Pulses
Interactions
Field Calculators
Bloch
TimeTrains
BlochSolver
Data Ouput
Figure 5.10: A rough design for a classical Bloch simulation over various interactions.
5.4. VARIOUS IMPLEMENTATIONS 134
magnetization of the proton will oscillate (its magnitude eﬀected by the time evolved under
the delay). If one then places the
31
P magnetization in the xyplane and collects an FID,
the
31
P will feel a slight oﬀset shift due to the varying
1
H zmagnetization (eﬀect of the
bulk susceptibility). Thus in the indirect dimension an oscillation of the
31
P magnetization
due to the protons will be observed. The results is shown in Figure 5.11b and matches the
result obtained in [100]. In order to correctly replicate the ﬁgure, the
1
H oﬀset had to be
changed to 722 Hz (the reference quotes 115 Hz as the oﬀset, but it seems a factor of 2π
was omitted (722 = 2π ∗ 115). The T2 relaxation of the
1
H also had to be altered to 0.002
seconds (the reference quotes 0.015 seconds, however the diagram shows a much faster decay
then this time). The code for this diagram is in the bulksus folder of the distribution.
Radiation Damping
Another interesting implementation attempts to emulate the result obtained by
Y.Y. Lin, et.al[87]. In this simulation, the interplay between radiation damping and the
demagnetizing ﬁeld resurrect a completely crushed magnetization (a complete helical wind
ing). Radiation damping is responsible for the resurrection as the demagnetizing ﬁeld alone
does not resurrect the crushed magnetization. The simulated data (Figure 5.12b) matches
Figure 2 in reference [87]. The result is a nonlinear partial resurrection of magnetization.
The input parameters are those in the reference [87]. The data was plotted using plottrag
in the distribution. The code for this diagram is in the yylin folder of the distribution and
can be seen in Appendix A.3.5.
5.4. VARIOUS IMPLEMENTATIONS 135
1
H
t
1
90y
31
P
90
y
90
y
4 4.2 4.4 4.6 4.8 5
1
2
3
4
5
6
7
8
5.2 5.4 5.6 5.8 6
t
1
(
m
s
)
f
2
(Hz)
t
2
a)
b)
Figure 5.11: Simulated data from a HETCOR (Heteronuclear Correlation) experiment
showing the eﬀect of bulk susceptibility on the oﬀset of the
31
P. a) shows the simulated
pulse sequence and b) shows the simulated data.
5.4. VARIOUS IMPLEMENTATIONS 136
1H
Gz
90y
a)
b)
0 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2
10
5
0
5
10
15
20
25
30
35
<
M
>
%
time(s)
<Mx>
<My>
<Mz>
1
0.5
0
0.5
1
My
0.4
0.8
1.2
1.4
2
time(s)
0.5
0
0.5
1
Mx
Figure 5.12: Simulated resurrection of magnetization after a crusher gradient pulse in the
sequence shown in a). b) shows the eﬀect of radiation damping and the modulated local
ﬁeld.
5.4. VARIOUS IMPLEMENTATIONS 137
Probe Coils
The ﬁnal example involves analyzing an NMR probe coil design. Dynamic Angle
Spinning (DAS)[101] experiments require the probe stator to move during the experiment. A
solenoid coil moves with the stator, however, as the stator angle approaches 0 degrees (with
respect to the high ﬁeld), there would be little detected signal (or pulse power transmission)
because the high static magnetic ﬁeld and coils ﬁeld are parallel (resulting in a 0 cross
product). One can remove this shortcoming by removing the coil from the stator. But this
represents its own problem if the coil is a solenoid, because the stator is large compared to
the sample, and thus the solenoid would also have to be large thus reducing the ﬁlling and
quality factor too much to detect any signal. A suitable alteration to the solenoid would be
to split it. The entire probe design is the subject of a forth coming paper[102]. To optimize
the split solenoid design one needs to see factors like inhomogeneities and eﬀective power
within the sample area. Figure 5.13 shows a split solenoid design as well the inhomogeneity
proﬁle along the xyplane (the high ﬁeld removes any need for concern about the zaxis).
Compared with a normal solenoid, Figure 5.14, the ﬁeld proﬁle is much more
distorted, also given the same current in the two coils, the solenoid has 6 times more ﬁeld
in the region of interest then the splitcoil design. The ﬁgure also shows us a weak spot
in the splitcoil design. The wire that connects the two helices creates the majority of
the asymmetric ﬁeld proﬁle, and is the major contributor to the inhomogeneity across the
sample. Correcting this by a U shape (or equivalent) should aid in correcting the proﬁle.
5.4. VARIOUS IMPLEMENTATIONS 138
1 0.5 0 0.5 1
0.5
0
0.5
1.4
1.2
1
0.8
0.6
0.4
0.2
0
0.2
0.4
y axis (cm)
x axis (cm)
z
a
x
i
s
(
c
m
)
Bx
G
a
u
s
s
b)
a)
c)
4 3 2 1 0 1 2 3 4
kHz
SplitSolenoid
y (cm)
x (cm)
z (cm)
Figure 5.13: The magnetic ﬁeld proﬁle of a split solenoid (3 turns/cm with a radius of 0.3175
cm and a splitting of 0.6 cm), a practical coil to work around the Dynamic Angle Spinning
problem of the solenoid coil (see text). a) shows the coil as well as the region of interest
for the magnetic ﬁeld (black points). b) shows the ﬁeld proﬁle along the xdirection given
3 amps of current. c) shows the eﬀective inhomogeneity of such a coil for a proton. The
majority of the inhomogeneity is due to the small line connecting the two helical segments.
The average ﬁeld of the coil was subtracted from the result in c).
5.4. VARIOUS IMPLEMENTATIONS 139
1
0.5
0
0.5
1
0.4
0.2
0
0.2
0.4 0.4
0.2
0
0.2
0.4
y axis (cm)
x axis (cm)
z
a
x
i
s
(
c
m
)
Normal Solenoid
Bx
G
a
u
s
s
b)
a)
c)
4 3 2 1 0 1 2 3 4
kHz
y (cm)
x (cm)
z (cm)
Figure 5.14: The magnetic ﬁeld of a standard Solid State NMR probe detection coil (the
solenoid, 10 turns/cm with a radius of 0.3175 cm ). a) shows the coil as well as the region of
interest for the magnetic ﬁeld (black points). b) shows the ﬁeld proﬁle along the x direction
given 0.5 amps of current. c) shows the eﬀective inhomogeneity of such a coil for a proton.
The small peak to the right of the main peak is the edges of the sampled rectangle close to
the coil. The average ﬁeld of the coil was subtracted from the result in c).
5.5. CONCLUSIONS 140
5.5 Conclusions
Throughout this chapter, emphasis on the generic physical simulation design is
discussed for the speciﬁc case of NMR. The created tool kit, BlochLib, adheres to these
basic design ideas: speed using expressiontemplates and ease of use using C++ and ob
jects/operators. BlochLib is designed to be the next generation of simulation tool kits for
NMR. It is highly optimized and generalized for almost any NMR simulation situation. It
has been shown that utilizing relatively modern numerical techniques and algorithms allows
a study of more complicated spin dynamics under various interactions and experimental de
signs then previous NMR tool kits. The input of complex parameters, coding, and creation
of programs should be easy and highly optimized for both the classical and quantum me
chanical aspects of NMR. Diﬀusion and other partial diﬀerential equation entities (like ﬂuid
ﬂow) are currently being designed for inclusion into the tool kit. Relaxation using normal
Louville space operators and Redﬁeld approximations should also be included. The total
tool kit and documentation can be found at http://waugh.cchem.berkeley.edu/blochlib/, or
so I hope it remains after I leave. If it not there, I hope to maintain a copy and updates at
http://theaddedones.com/.
141
Chapter 6
Massive Permutations of Rotor
Synchronized Pulse Sequences
6.1 Introduction
Given that we have a large set of fast computational tools, I will now go into
an application beyond the typical scope of the ‘usual’ NMR simulation. The usual NMR
simulation consists of a pulse sequence that either needs to be simulated to validate an
experiment or add subtle corrections. Such simulations abound in the NMR literature to
the point that even including a reference list for such types of simulation, I would probably
have to reference 80% of the NMR publications around. In this chapter I wish to develop
a general frame work to optimize any rotor synchronized pulse sequence over long time
evolutions with an explicit example applied to the postC7[99] sequence.
6.1. INTRODUCTION 142
ϕ
θ
θ'
=
ϕ'
nτ
r
τ
r
τ
r
Rotor:
RF:
delay a) Pulse Delay:
ϕ
θ θ
i
=
ϕi
b) Continous Pulse:
θ'
ϕ'
Figure 6.1: A general rotor synchronized pulse sequence a) using pulses and delays, and b)
using a quasi continuous RF pulse.
6.1.1 Rotor Synchronization
Rotor synchronized sequences (RSS) are designed to create a speciﬁc average
Hamiltonian when observed in multiples of the synchronized parameter, n. n is the amount
of rotor periods the sequence takes to complete. Within the n cycle, there are typically two
diﬀerent types of sequences. The ﬁrst contains a series of pulses and delays as in Figure
6.1a. The second applies continuous RF radiation during the rotor cycles as in Figure 6.1b.
These RF sequences are designed to manipulate the spin space degree of freedom (the T
l,m
tensors) in conjunction with the spatial rotation the rotor performs (the A
l,m
tensors).
The RF pulses can be of arbitrary amplitude, phases and duration; however, the
rotor can only be spun in a constant direction and is not adjustable in terms of duration
6.2. BACKGROUND THEORY 143
or phase during the course of an experiment. The speed of its rotation is manipulable,
however, doing so during the course of an experiment is unstable and unrealizable due to
the physical aspects of the rotation. For this reason, we typically say the rotor has a ﬁxed
spinning rate, ω
r
and a corresponding period of τ
r
= 2π/ω
r
. The synchronized parameter
then refers to n multiples of τ
r
.
Almost all RSS sequences use continuous pulses. Each continuous pulse version
has a pulse–delay–pulse version counterpart. The pulse–delay–pulse versions have better
properties theoretically, but experimentally, they are extremely hard to implement. The
limitation is the assumption of hard pulses, for many experimental parameters and real spin
systems, a hard pulse is extremely hard to implement correctly.
6.2 Background Theory
6.2.1 Average Hamiltonian
Before we can really to describe speciﬁc RSSs, we need Average Hamiltonian The
ory (AHT)[103] to attempt to understand the dynamics of such sequences. AHT uses the
periodicity of the Hamiltonian to render a time dependant problem into an approximate time
independent form. Given a periodic Hamiltonian of period τ
r
, we can write the integrated
Hamiltonian at time τ
r
as series of time independent Hamiltonians
τr
_
0
H(t)∂t =
¯
H
0
+
¯
H
1
+
¯
H
2
+... (6.1)
6.2. BACKGROUND THEORY 144
where
¯
H
0
=
1
τr
τr
_
0
H(t)∂t
¯
H
1
= −
i
2τr
τr
_
0
t
_
0
[H(t), H(t
)]∂t∂t
¯
H
2
=
−1
6τr
τr
_
0
t
_
0
t
_
0
[H(t), [H(t
), H(t
)]]
+[H(t
), [H(t
), H(t)]] ∂t∂t
∂t
.
(6.2)
As an example, I will show the eﬀect of the average Hamiltonian of a simple dipole
under a rotor rotation of ω
r
spinning at the rotor axis θ
r
. The physical rotation only eﬀects
the spatial tensors. The Hamiltonian for a disordered sample in a high ﬁeld reduces to
H =
_
m
exp [−imω
r
t] d
2
0,m
(θ
r
)
m
D
2
m,m
(Ω
rot
)A
2
m
_
T
2
0
(6.3)
where Ω
rot
= (φ, θ, γ) is the powder rotation. Given that there is no molecular orientation
to worry about, in a disordered solid, we easily expand the sum to get
H =
δz
16
_
3
2
(1 + 3 cos(2θ)) (1 + 3 cos(2θ
r
)) T
2
0
+
3δz
2
_
3
2
e
−i(φ+ωrt)
cos θ cos θ
r
sin θ sin θ
r
T
2
0
+
3δz
2
_
3
2
e
i(−φ+ωrt)
cos θ cos θ
r
sin θ sin θ
r
T
2
0
+
3δz
8
_
3
2
e
−i(2φ+2ωrt)
sin
2
θ sin
2
θ
r
T
2
0
+
3δz
8
_
3
2
e
i(2φ+2ωrt)
sin
2
θ sin
2
θ
r
T
2
0
.
(6.4)
When integrated over a period τ
r
, we get
¯
H
0
=
δ
z
16
_
3
2
(1 + 3 cos(2θ)) (1 + 3 cos(2θ
r
)) T
2
0
. (6.5)
The rest of the average Hamiltonian orders,
¯
H
n
, are all zero because this Hamiltonian
commutes with itself at diﬀerent times
¯
H
1
= −
i
2τr
τr
_
0
t
_
0
[A(t)T
2
0
, A(t
)T
2
0
]∂t∂t
= −
i
2τr
τr
_
0
t
_
0
A(t)A(t
)T
2
0
T
2
0
−A(t)A(t
)T
2
0
T
2
0
∂t∂t
= 0.
(6.6)
6.2. BACKGROUND THEORY 145
Using Eq. 6.5 it is then easily to see we can pick θ
r
such that
¯
H
0
= 0. This
angle, called the magic angle, is arccos(1/
√
3) = 54.7
◦
. Magic angle spinning therefore
removes any isolated second order spatial tensors. For dipoles and 1
st
order quadrupoles,
this completely removes the interaction, for CSAs and J couplings it removes the anisotropic
shift. By isolated we mean there there is NO other interaction in the main Hamiltonian
that does not commute with the rest of the interactions. In real systems, we typically have
CSAs and dipoles and J couplings to consider over multiple spins. Then our commutation
rule breaks down and we must consider higher order AHT terms to get the proper answer.
It also should be noted that the solution here is only valid for the time t = τ
r
, at any
other time, the other components of Eq. 6.4 do not integrate to zero. The typical NMR
experiment does not observe every t = τ
r
but something less, the extra terms the introduce
‘sidebands’ in the resulting eigen–spectrum.
6.2.2 Recoupling RSS
RSSs have two main uses in NMR 1) to remove unwanted terms from the AHT
(decoupling) [104, 105, 106, 107, 108, 109] and 2) to selectively reintroduce certain terms
into an AHT (recoupling) [110, 111, 112, 113, 114, 115, 116, 99, 117, 118, 119, 120, 121,
122, 123, 124, 116]. Here we will focus on recoupling, but the methods introduced here are
easily extendable to decoupling.
Most RSS recoupling methods have been categorized very nicely by their symme
tries. The reader is encouraged to look to Carravetta’s [110], Eden’s [105], and Brinkmann’s
[112] papers for more information, here we will simply give the results. RSSs are broken
into two diﬀerent main classes, the R and C classes. Figure 6.2 show the main diﬀerence
between the two. Thus an entire RSS is made up from C or R subcomponents. The char
6.2. BACKGROUND THEORY 146
acteristics of the C sub element is that they perform a total rotation of 2π. The R subunit
rotates the spin states by π. To classify them even further Eden and Carravetta introduce
2 more symmetry elements along with n. N represents the time duration of a single sub
element as nτ
r
/N = π for the R class and nτ
r
/N = 2π for the C class. The next factor ν
along with N represent the phase iteration from one sub element to the next as shown in
Figure 6.2.
Carravetta and Eden showed that given proper selection of N, n, and ν that one
can select almost arbitrary elements to comprise the
¯
H
0
element of the AHT given a certain
set of interactions (dipoles, CSAs, etc). To correct for higher order AHT terms one can
apply further symmetry if we know which tenser elements we desire.
Given a real experimental system, the basic C7 as shown in Figure 6.2 will fail
to produce desired results because of higher order commutators. Another aspect of a real
experimental system is that RF pulses are not perfect and resonant on only one frequency.
To make a C or R cycle robust towards inhomogeneity and oﬀset of the RF pulse itself,
each C or R cycle must be internally compensated, in eﬀect reversing the damage done by
a bad RF pulse. For the C cycles, this is easily performed by applying the same amplitude,
but reversing the RF phase by π, which reverses any problems, to ﬁrst order, caused by
oﬀsets and inhomogeneity problems. For the R cycles, we apply a second R with the phase
equal to −φ from the last (as we are treating a π pulse). We have assumed that over the
course of a single C/R cycle that the real system Hamiltonian can be considered constant,
which is why this only works to ﬁrst order. Each C/R cycle gets an extra C/R attached as
shown in Figure 6.3a and b. This technique is called compensation. For the C cycles, we
have a total rotation of 4π, which can be divided into (π/2)
φ
− (2π)
φ+π
− (3π/2)
φ
which
6.2. BACKGROUND THEORY 147
nτ
r
τ
r
τ
r
Rotor:
RF:
a) C class:
b)R class:
ϕ
o
+
j*ν*2π
Ν
j=0..N1
CN
ν
n
n/ω
r
θ=2 π
j= 0 4 1 2 3
N2 N1 N3
= j
ϕ
o
+
ν*π
Ν
j=0..N1
RN
ν
n
n/ω
r
θ=π
= j
ϕ
j
=
ϕ=
ϕ
Figure 6.2: The two RSS classes C (a) and R (b).
6.2. BACKGROUND THEORY 148
has better oﬀset and inhomogeneity properties as demonstrated in Ref. [99]. This simple
reordering of the 4π is called ‘posting’ hence the name postC7. The R sequences have now
a total rotation of 2π which can be split arbitrarily into many forms like a (θ
1
)
φ
− (2π −
θ
1
)
φ+π
−(θ
1
)
−φ
−(2π −θ
1
)
−φ−π
sequence. Figure 6.3c and d show this posting explicitly.
The RSS sequences produce desired tensor elements from a system Hamiltonian.
of the form
¯
H
0
=
l
m,m
g
m,m
A
l,m
T
l,m
+c.c. (6.7)
where c.c. means the complex conjugate and g
m,m
is a scaling factor. The scaling factors
is also an important aspect of these sequences as they determine the apparent spectral
distribution, and are needed when using these RSS in larger pulse sequences designs[113].
Calculation of the scaling factors for the R or a C type is a simple application of AHT.
A general rotation through the Euler angles (ψ, θ, φ), on an arbitrary tensor can
be represented using the notation for tensor rotation
F
l,m
=
l
m=−l
e
−im
ψ
d
l
m
,m
(θ)e
−imφ
F
l,m
, (6.8)
where F
l,m
is the rotated tensor and F
l,m
is the original tensor. A given dipolar or CSA
interaction contains two unique tensor elements, the spatial part, A
2,0
, and a spin part, T
2,0
.
The RSS sequences rotate the spatial part by (0, θ
r
, ω
r
t) where θ
r
the angle of the rotor,
and ω
r
is the spinning speed (ω
r
t then represents the total phase). They also rotate the
spin part through the Euler angles (0, ω
rf
t, φ) where ω
rf
is the pulse amplitude (thus ω
rf
t
represents the total rotation angle) and φ
j
is the pulse phase. Our dipolar Hamiltonian
under such a rotation becomes
A
2,0
T
2,0
→
2
m=−2
d
2
m,0
(θ
r
)e
−im2πωrt
A
2,m
2
m=−2
d
2
m,0
(2πω
rf
t)e
−imφ
j
T
2,m
(6.9)
6.2. BACKGROUND THEORY 149
a) C class compensated:
b)R class compensated:
ϕ
o
+
j*ν*2π
Ν
j=0..N1
CN
n
n
2n/ω
r
θ=2 π
ϕ
o
+
ν*π
Ν
j=0..N1
RN
ν
n
2 n/ω
r
θ=π
ϕ
j
=
ϕ=
ϕ
θ=2 π
ϕ
j
ϕ
j
+π
θ=π
−ϕ
c) C class posted:
ϕ
o
+
j*ν*2π
Ν
j=0..N1
CN
ν
n
2n/ω
r
θ=π/2
ϕ
j
=
θ=2 π
ϕ
j ϕ
j
+π
θ=3π/2
ϕ
j
d)R class posted:
ϕ
o
+
ν*π
Ν
j=0..N1
RN
ν
n
4 n/ω
r
θ
1
ϕ=
ϕ
2 π−θ
1
ϕ+π
θ
1
−ϕ
2 π−θ
1
−ϕ−π
Figure 6.3: Compensated C (a), R (b) and posted C (c), R(d) RSS sequences.
6.2. BACKGROUND THEORY 150
Assuming that the symmetry pulse phases, φ
j
, selects only terms with l = 2, m, m
then
our rotated Hamiltonian becomes
A
2,0
T
2,0
→ (d
2
2,0
(θ
r
)e
−i4πωrt
A
2,m
)(d
2
2,0
(2πω
rf
t)e
−i2φ
T
2,m
) +c.c. (6.10)
In order to calculate what the scaling factor in front of the A
2,±m
T
2,±m
terms we
need to make some assumptions about the applied sequence. First, the entire sequence is
applied over a multiple of a rotor cycle such that the sequence is periodic and AHT can be
used to continue the calculation. Second, the rotor cycle is divided into N subsections such
that during the j
th
subsection only the pulse phase, φ
i
is varied and all subsections are the
same length in time, τ =
n
ωrN
. The zeroth order average Hamiltonian is then
¯
H
0
=
N−1
j=0
1
τ
_
(j+1)τ
jτ
d
2
m,0
(θ
r
)e
−imπωrt
d
2
m
,0
(2πω
rf
t)e
−im
φ
j
dtA
2,m
T
2,m
+c.c. (6.11)
The scaling factor, g, is given as the total constant that multiplies A
2,m
T
2,m
in
the above equation.
g
m,m
=
N
j=0
1
τ
_
(j+1)τ
jτ
d
2
m,0
(θ
r
)e
−imπωrt
d
2
m
,0
(2πω
rf
t)e
−im
φ
j
dt (6.12)
6.2.3 C7
From this point I will discuss the C7
1
2
, the C7[115] as it was originally named
before Eden’s paper. The post version was also tagged before Eden’s paper in Ref. [99].
This sequence produces a double quantum (DQ) AHT to zeroth order using the dipolar
Hamiltonian
¯
H
0
= g
−1,2
A
2,−1
T
2,2
+g
∗
1,−2
A
2,1
T
2,−2
. (6.13)
This Hamiltonian can then be used to determine approximate distance information
between atoms as it selectively evolves only terms from the dipolar Hamiltonian, creating
6.2. BACKGROUND THEORY 151
DQ coherences between them. The accuracy of the distances is related directly to how well
the C7 performs under all the various experimental and molecular considerations.
A good measure of the C7 ability to create DQ coherences, is to measure the
transfer between two spins. The transfer can be measured directly starting from an initial
polarization on spin 1 (ρ
o
= I
1
z
), application of the sequence, then a measurement of the
polarization on the second spin (I
det
= I
2
z
). Application of the sequence n times where
n >> 1 results in a steady state transfer of the coherence. Figure 6.4 shows the transfer
for diﬀerent dipole couplings between the two spins. Introduction of CSA terms reduce the
eﬀect of the transfer as also shown in Figure 6.4. The sequence used in the Figures is shown
in Figure 6.5a.
The dipole coupling determines the rate of transfer: a large rate means a closer
distance. One can easily see from Figure 6.4 that introduction of large CSA terms cause
the steady state transfer to fail. In multi–spin systems, this then leads to confusion of the
distance information. The eﬀect is most pronounced at small dipole couplings where we
would like to achieve the best data for longer range distance determination. It would be
beneﬁcial to remove as many higher order average Hamiltonians as possible in these RSS
type sequences.
6.2.4 Removable of Higher Order Terms
Usage of the RSS sequences is restricted to multiples of the rotor period. So long as
we create the zeroth order average Hamiltonian at the ﬁnal observation point, the number of
rotor periods is arbitrary. The higher order terms of the C7 sequence introduce a variety of
other unwanted tensor components due to the commutators between the CSA and dipolar
Hamiltonians during each cycle. For a simple two spin system, the only other terms that
6.2. BACKGROUND THEORY 152
0 50 100
0.2
0
0.2
0.4
0.6
0.8
ω
d
=100Hz
0 50 100
0.2
0
0.2
0.4
0.6
0.8
ω
d
=400Hz
0 50 100
0.2
0
0.2
0.4
0.6
0.8
ω
d
=700Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=1000Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=1300Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=1600Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=1900Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=2200Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=2500Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=2800Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=3100Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=3400Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=3700Hz
0 50 100
0
0.2
0.4
0.6
0.8
ω
d
=4000Hz
0 50 100
0.2
0
0.2
0.4
0.6
0.8
ω
d
=4300Hz
0 50 100
0.2
0
0.2
0.4
0.6
0.8
ω
d
=4600Hz
Figure 6.4: PostC7 transfer eﬃciencies on a two spin system with ω
r
= 5kHz for various
dipolar coupling frequencies (ω
d
). The dashed lines indicate a CSA on spin one of (δ
iso
=
12000Hz, δ
ani
= 8700Hz, η = 0) and on spin two of (δ
iso
= −5400Hz, δ
ani
= 12300Hz, η =
0).
6.2. BACKGROUND THEORY 153
a)
ϕ
n
θ=π/2 θ=3π/2
ϕ
n
+π
= n
ϕ
n
+π
2τr
C7
1
2
0 0 1 3 1 2 2 3 4 4 6 5 5 6
2τr 2τr
0 0 1 3 1 2 2 3 4 4 6 5 5 6 3 2 2 0 1 1 0 6 6 5 3 5 4 4
Permutation
2τr 2τr
0 0 1 3 1 2 2 3 4 4 6 5 5 6 0 0 1 3 1 2 2 3 4 4 6 5 5 6
Bar
b)
c)
d)
Figure 6.5: Diﬀerent base permutations on the postC7 seqeunce: a) the basic sequence, b)
the barred sequence, c)permuted sequence, and d) the composition of a sub element.
6.2. BACKGROUND THEORY 154
can appear are combinations of T
2,0
, T
2,±1
and T
2,±2
. Because we only wish to observe the
I
z
terms in the evolved density matrix any solo T
2,0
terms can be ignored as [T
2,0
, I
z
] = 0.
Thus the only tensor component we wish to remove are the T
2,±1
terms. These terms have
the property that they are odd under a parity transformation where as the T
2,±2
are not.
In the ﬁrst application of a RSS sequence generates any T
2,±1
terms, the same sequence
with a global π phase shift (called barring) from the last will ‘reverse’ any evolution from
the T
2,±1
while maintaining the evolution under T
2,±2
terms. We can then easily alter the
original C7 sequence to include two C7 sequences with one barred relative to the last as
shown in Figure 6.5b.
Even in the 2–cycle C7 sequence errors can still accumulate from even higher order
terms, thus we can even bar the next 2 C7 relative to the last 2 resulting in a sequence like
C7 −
¯
C7 −
¯
C7 −C7. We can continue this process, which is called super–cycling, until the
signal has decayed beyond observation. This super–cycling process was initially used most
dramatically in liquid state broadband decoupling[125, 126, 127, 128, 129, 130, 131, 132] but
is becoming more prevalent in solid–state NMR as the hardware and theory improve[112,
113].
Problems with super–cycling
The removal of higher order terms comes typically with a penalty. The sequence is
usually altered to include super cycles which can make the sequence very long. Most solid–
state samples have a relatively fast decay rate (T
2
is on the order of mille–seconds where as
in liquids it can be seconds) meaning that super cycles cannot get very long. Perhaps not
long enough to remove all the terms necessary for eﬃcient use of the sequence. Not only
do we have a time constraint, but even super–cycling for anisotropic samples can lead to
6.3. PERMUTATIONS 155
diminishing returns due to hardware imperfections[133, 134].
This leads us to ask weather or not we can improve on the basic RSS in a framework
that allows us to measure the eﬀectiveness of a particular super–cycle. We still would like
to use the basic symmetry principles that do provide useful removal of terms, but use them
in a time constrained way.
6.3 Permutations
The problem of determining the best set of RSS sub–cycles to use for a super–cycle
was one best handled using the symmetries of the underlying Hamiltonians. This technique
works very well for liquid samples where the Hamiltonians as isotropic and have nice sym
metry properties. Techniques like MLEV[131, 130], WALTZ [127, 126], SPARC[125], and
GARP[128] use the symmetries to decouple liquid state spins in a highly optimal way. The
anisotropic and spinning environment of a solid–state experiment makes such application of
super–cycles hard and less eﬀective. We now wish to see if there is a way for us to determine
the best set of cycles where the symmetry principles seem to fail. In order to investigate
the eﬀect of diﬀerent cycles, we will use the symmetry principles as our starting point, but
from there, the problem is open ended. We will use simple permutation to determine the
best sequence.
6.3.1 The Sub–Units
Any particular deﬁned ordering of RSS sequences is a subunit. We will use a
particular naming scheme: the basic RSS sequence is labeled ‘o’ (lowercase ‘o’) as in Figure
6.5a, a barred RSS sequence is labeled ‘O’ (capital ‘O’) as in Figure 6.5b, an internal
6.3. PERMUTATIONS 156
Table 6.1: A list of some sub–units for a C7 permutation cycle.
sub–units
o
O
w
W
oO
oOOo
wW
wWWw
oOWw
OooO
WwwW
permutation (a reordering internal to a single RSS cycle) of a RSS sequence is given the
label ‘w’ (lowercase ‘w’) as in Figure 6.5c, the bared version of the internal permutation
is given the label ‘W’ (captial ‘W’). A sub unit can be constructed from other subunits as
well so long as the subunit return the Average Hamiltonian to the desired result. Table 6.1
lists a few of the sub units for a C7.
6.3.2 The Measure
To determine the best sequence, we need some sort of measure that typiﬁes the
RSS sequence. For a C7 sequence we desire T
2,±2
terms above all other elements. So the
measure of a good sequence will have large T
2,±2
terms while minimizing any other terms.
Since we are performing exact numerical simulations of the propagator of the various C7
cycles, we need to use the eﬀective Hamiltonian
H
eff
=
−i
τ
log(U) =
l
α
l,m
T
l,m
+
l,l
β
l,m
[T
l,m
, T
l
,m
] +... (6.14)
where τ is the total time of the super–cycle, α and β are complex constants. Because of the
commutation relations of T
l,m
the higher order terms, when expanded, will reduce to terms
6.3. PERMUTATIONS 157
proportional to a single tensor, thus numerically the sum is reduced to a sum of only the
α
l,m
components. α
l,m
is easily extracted by the trace operation
[α
l,m
[ =
¸
¸
¸
¸
¸
¸
_
Tr
_
H
eff
(Ω) (T
l,m
)
†
_
Tr
_
T
l,m
(T
l,m
)
†
_ dΩ
¸
¸
¸
¸
¸
¸
. (6.15)
In Eq. 6.15 the integral over all the power angles as the eﬀective Hamiltonian is Ω dependent.
We are also only interested in the magnitude as any sign changes are easily phased away.
For the C7 we can now deﬁne two measures. Given n diﬀerent sequences, the ratio of the
magnitudes of the i
th
sequences α
i
2,±2
to the original ‘nonpermuted’ sequence α
0
2,±2
should
be large for good signal–to–noise in an experimental setting, M
i
mag
. The second, and better
theoretical measure is the ratio of the α
i
2,±2
terms to the rest the undesired tensor terms,
the maximum being the best sequence, M
i
R
.
M
i
mag
=
[α
i
2,±2
[
α
0
2,±2
M
i
R
=
[α
i
2,±2
[
l=2,m=±2
[α
i
l,m
[
M
mag
= max(M
i
mag
)
M
R
= max (M
i
R
)
(6.16)
The goal for any given master cycle is to maximize both of these measures, using M
R
as the
master measure. Some of the α
l,m
terms are not relevant to the M
R
measure. For instance,
because the T
2,0
terms do not eﬀect the evolution, they should not be counted in the sum.
Also other generated tensors are more harmful to the evolution then others. For the C7,
extra I
z
terms are worse then I
x,y
terms, so the M
R
should weight the z terms more. A
revised version of the M
i
R
is given as
M
i
R
=
¸
¸
α
i
2,±2
¸
¸
l=2,m=±2
b
l,m
¸
¸
¸α
i
l,m
¸
¸
¸
(6.17)
where b
l,m
are the relevant weighting factors.
6.3. PERMUTATIONS 158
6.3.3 Algorithmic Flow
There are many algorithmic sub components to optimally generate and measure
each master cycle. Because we have a time constrained problem, there will be a maximum
number of sub–units that we can apply, P. An arbitrary permutation can be constructed
from any subset of the available sub–units. The number of sub–units in a given subset is
called N. The task is to generate all the valid permutations of length P from subset of
length N. P is not necessarily a factor of N (i.e. P/N ,= 0), we then must select the
largest subset, N
, of the N sub–units that are factors of P. Then to generate all the valid
permutations we need to generate all the K–subsets of N of length N
, then generate all
the permutations of length P. Using the K–subsets can produce similar permutations from
another K–subset, so we need to remove any duplicates. The time symmetry all the RSS
sequences indicates that a master cycle will give exactly the same results as the reverse
of that master cycle, so when we generate the permutation list we must remove all the
duplicates and reverse duplicates (i.e. [1234] is the same as [4321]). The general method to
generate the permutations lists can be summarized below.
1. Determine the number of distinct sub–units, N.
2. Determine the sequence length (the total number of individual sub–units to apply),
P.
3. Generate all the possible Ksubsets given N sequences and the length P. For instance
if N = 2, and P = 3, the available Ksubsets are ¦1, 2¦, ¦1, 3¦, and ¦2, 3¦. Ksubsets
that are the reverse of another Ksubset are also removed from further calculation.
4. For each Ksubset, generate all the permutations. For instance a Ksubset of ¦1, 2, 3¦
6.3. PERMUTATIONS 159
and P = 3 has these permutations: ¦1, 2, 3¦, ¦1, 3, 2¦, ¦2, 1, 3¦, ¦2, 3, 1¦, ¦3, 1, 2¦, and
¦3, 2, 1¦.
5. Since there can be duplicate permutations within diﬀerent Ksubset permutation lists,
remove all duplicates.
6. Remove all the reverse permutations from the master list. For instance the permuta
tion ¦2, 3, 1¦ would be removed because ¦1, 3, 2¦ is already included.
To save much computational eﬀort, the removal of reverse permutations and du
plicates can occur at the time each permutation is generated. The removal of the reversed
permutations for large permutations sets (sets larger than 20 items) is a computational lim
itation because of the searching problem. On a 700 MHz Pentium III Xeon, the generation
of unique nonreversed, permutations for a length 20 system takes 2 hours. The generation
of a length 40 list proved too memory intensive as over 10
47
permutations would need to be
searched for duplicates and reversed duplicates. Given a list that large (even if it was cut in
half) would also prove prohibitively time demanding to calculate the eﬀective Hamiltonians
for all 10
47
/2 sequences. In practice, the simulations used either N = 2 or N = 4 distinct
sequences. The maximal length, P, for the sequences was found to be 20 for N = 2 and
12 for N = 4 that can be handled without huge memory and time requirements. We will
apply the label N P for each of the calculated permutation data segments. For a 2 20
segment there are 92504 unique non reversible permutations and for a 4 12 segment there
are 184800 unique non reversible permutations.
As an algorithmic point, only integers representing each sub–unit are necessary
to perform the permutation calculation. This saves much time when both generating the
permutations and comparing them for duplication.
6.3. PERMUTATIONS 160
Table 6.2: Sequence Permutation set for the eﬀective Hamiltonian calculations of the
postC7 sequence.
N P basic Units (length)
2 (2,4,8,12,16,20)
o, O (2,4,8,12,16,20)
w, W (2,4,8,12,16,20)
oO, wW (4,8,16,24,32,40)
wO, oW (4,8,16,24,32,40)
ow ,OW (4,8,16,24,32,40)
oOOo, wWWw (8,16,32,48)
oOWw, OowW (8,16,32,48)
oOOo, WwwW (8,16,32,48)
4 (4,8,12)
o, O,w,W (4,8,12)
oOOo, wWWw, Ww, Oo (12, 40)
Because the 2 20 and 4 12 was our computer limitation, and many of the
desired sequences are applied for many more cycles then 20 or 12, the third stage of the
program allows the ability to use any number of sub permutations for each index N. To
calculate all the eﬀective Hamiltonians and their spin operator components in a 2 20
system for 2 spins spinning at a rotor speed of 5000 Hz for 1154 powder average points
took 5 days on a single processor. The program is able to distribute the powder average
over multiple workstations to allow linear scaling of the calculation. Table 6.2 shows the
calculated sequences calculated for the postC7 permutations.
The next stage is generating all of the spin tensors desired to ﬁgure out the tensor
components. The tensors themselves can be generated using similar permutation techniques
as the N P by labeling each spin by an integer and each direction as an integer. Table
6.3 shows which tensors were used for this study.
6.4. DATA AND RESULTS 161
Table 6.3: Spin operators and tensors generated to probe the eﬀective Hamiltonians
Type Form
1
st
order Cartesian I
i
r
, (r = x, y, z)
2
nd
order Cartesian I
i
r
I
i
r
(r = r
= x, y, z)
1
st
order spherical T
(i,i
)
l,m
, (l = 1, 2, m = −l...l)
2
nd
order spherical T
(i,i
)
l,m
T
(i
,i
)
l
,m
, (l = l
= 1, 2, m = m
= −l...l)
Table 6.4: Spin System parameters for the three sets of permutations. All units are in Hz
System Label Spin parameters
SS
1
13
C
1
CSA (δ
iso
= 1254, δ
ani
= 12345, η = 0)
13
C
2
CSA (δ
iso
= −1544, δ
ani
= 8552, η = 0)
13
C
1
–
13
C
2
dipole (δ
ani
= 2146)
SS
2
13
C
1
CSA (δ
iso
= 1254, δ
ani
= 12345, η = 0)
13
C
2
CSA (δ
iso
= −1544, δ
ani
= 8552, η = 0)
13
C
1
–
13
C
2
dipole (δ
ani
= 2146)
1
H
3
–
13
C
1
dipole (δ
ani
= 4506)
1
H
3
–
13
C
2
dipole (δ
ani
= 7564)
SS
3
13
C
1
CSA (δ
iso
= 1254, δ
ani
= 12345, η = 0)
13
C
2
CSA (δ
iso
= −1544, δ
ani
= 8552, η = 0)
13
C
1
–
13
C
2
dipole (δ
ani
= 2146)
1
H
3
–
13
C
1
dipole (δ
ani
= 4506)
1
H
3
–
13
C
2
dipole (δ
ani
= 7564)
1
H
4
–
13
C
1
dipole (δ
ani
= 2150)
1
H
4
–
13
C
2
dipole (δ
ani
= 4562)
1
H
3
–
1
H
4
dipole (δ
ani
= 15546)
6.4 Data and Results
6.4.1 Sequence Measures
There were over 500000 diﬀerent C7 master cycles simulated and measured. The
next few ﬁgures will show the data, giving both the M
R
and M
max
for all the sequences of
a given number of C7 cycles as well as the best one showing you the tensor components.
There are three diﬀerent sets of data corresponding to 3 diﬀerent numbers of nuclei. Table
6.4 shows the spin system parameters for each set.
6.4. DATA AND RESULTS 162
Table 6.5: Relevant weighting factors for Eq. 6.17
Tensor weight(b
l,m
)
I
i
z
0.2381 (a factor of 5)
I
i
x,y
0.0952 (a factor of 2)
T
i,j
2,0
0 (a factor of 0)
T
i,j
2,±1
0.0476 (a factor of 1)
The spin parameters were chosen to avoid any rotational resonance conditions with
either the spinning rate or the RF amplitude. They were also chosen as a representative
organic molecule so dipole and CSA values are consistent with peptides and amino acids
(although no one amino acid was used). The couplings were chosen to be all the same order
of magnitude as the spinning frequency ω
r
= 5kHz as to be in the regime of truly nonideal
conditions where the beneﬁts of the permutation cycles would show more dramatically. If
the spinning rate (and consequently the RF power) are high, then the average Hamiltonian
series converges must faster as each order falls oﬀ like (1/ω
r
)
n
. As with most of the RSS
sequences, an experimental limit is usually reached with an RF power of 150kHz. For the
C7 this implies a maximum rotor speed of about 20 kHz. For other CN sequences ω
r
is
much less, so 5000 kHz is a good value to investigate the properties of the sequences.
To handled the data more eﬀectively, only the ﬁrst order tensors of Table 6.3 were
considered in the M
R
measure. The higher order tensors were recorded, but as stated before,
the commutation relations of 2 spin 1/2 nuclei reduce them all to ﬁrst order tensors. The
higher order tensors can give better insight as to the coherence pathways the error terms
follow, which could potentially be used to construct sequences and phase cycles that remove
these pathways. The relevant weighting factors for Eq. 6.17 are given in Table 6.5.
6.4. DATA AND RESULTS 163
Figures 6.6–6.14 show the data recorded for the SS
1
system for a total sequence
length of 4,8,12,16,20,24,32,40,48 respectively. Figures 6.15–6.20 show the data recorded for
the SS
2
system for a total sequence length of 4,8,12,16,24,32 respectively. Figures 6.21–6.26
show the data recorded for the SS
3
system for a total sequence length of 4,8,12,16,24,32
respectively.
6.4. DATA AND RESULTS 164
5 10 15 20 25
1
2
3
Sequence (i)
5 10 15 20 25
0.1
0.15
0.2
0.25
Sequence (i)
0.05 0.1 0.15 0.2 0.25 0.3
0
1
2
3
b
i
n
c
o
u
n
t
0 1 2 3 4
0
1
2
3
b
i
n
c
o
u
n
t
10
3
10
2
10
1
(o)4 postC7
(woOW)1
M
mag
(oOOo)1
M
R
M
mag
I
x
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I
y
1
I
y
0
I
z
1
I
z
0
Magnitude of Tensor
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
1
, Total number of postC7's=4
Figure 6.6: Spin system SS
1
with 4 total number of C7s applied.
6.4. DATA AND RESULTS 165
200 400 600 800 1000 1200
2
4
6
200 400 600 800 1000 1200
0.08
0.1
0.12
0.14
0.16
0.05 0.1 0.15 0.2 0.25
0
5
10
15
20
0 2 4 6 8
0
5
10
15
oooooooo
wWwwwWWW
oOOoOooO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
10
3
10
2
10
1
M
mag
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
Magnitude of Tensor
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
1
, Total number of postC7's=8
M
mag
M
R
postC7
Figure 6.7: Spin system SS
1
with 8 total number of C7s applied.
6.4. DATA AND RESULTS 166
200 400 600 800
2
4
6
200 400 600 800
0.08
0.1
0.12
0.14
0.16
0.05 0.1 0.15 0.2 0.25
0
5
10
0 2 4 6 8
0
2
4
6
8
oooooooooooo
oooooOoOOOOO
oOOooOoOOooO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
1
, Total number of postC7's=12
M
mag
M
R
postC7 10
3
10
2
10
1
Magnitude of Tensor
Figure 6.8: Spin system SS
1
with 12 total number of C7s applied.
6.4. DATA AND RESULTS 167
2000 4000 6000 8000 10000 12000
2
4
6
8
2000 4000 6000 8000 10000 12000
0.06
0.08
0.1
0.12
0.14
0.16
0.18
0.05 0.1 0.15 0.2 0.25
0
50
100
150
0 2 4 6 8 10
0
20
40
60
80
oooooooooooooooo
owowowOWowOWOWOW
oOoOOoooOOOooOoO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
1
, Total number of postC7's=16
M
mag
M
R
postC7 10
3
10
2
10
1
Magnitude of Tensor
Figure 6.9: Spin system SS
1
with 16 total number of C7s applied.
6.4. DATA AND RESULTS 168
5 10 15 x 10
4
2
4
6
8
10
12
14
5 10 15 x 10
4
0.06
0.08
0.1
0.12
0.04 0.06 0.08 0.1 0.12 0.14
0
500
1000
1500
0 5 10 15
0
500
1000
1500
oooooooooooooooooooo
oooOOOOOOOOoooooooOO
ooooOoooooOOOOoOOOOO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
1
, Total number of postC7's=20
10
3
10
2
10
1
Magnitude of Tensor
M
mag
M
R
postC7
Figure 6.10: Spin system SS
1
with 20 total number of C7s applied.
6.4. DATA AND RESULTS 169
200 400 600 800 1000 12001400
4
6
8
200 400 600 800 100012001400
0.06
0.08
0.1
0.12
0.04 0.06 0.08 0.1 0.12 0.14
0
5
10
15
2 4 6 8 10 12
0
5
oooooooooooooooooooooooo t
owOWOWowowowowOWOWOWOWow
owOWOWowowOWowOWOWowowOW
10
10
Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
1
, Total number of postC7's=24
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Sequence (i)
Figure 6.11: Spin system SS
1
with 24 total number of C7s applied.
6.4. DATA AND RESULTS 170
5000 10000 15000
5
10
15
5000 10000 15000
0.06
0.08
0.1
0.12
0.04 0.06 0.08 0.1 0.12 0.14
0
50
100
150
200
0 5 10 15 20
0
50
100
150
owOWOWowowowowOWowOWOWOWOWowowOW
owowOWowowOWOWOWowowowOWOWowOWOW
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
1
, Total number of postC7's=32
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
oooooooooooooooooooooooooooooo
Figure 6.12: Spin system SS
1
with 32 total number of C7s applied.
6.4. DATA AND RESULTS 171
0.5 1 1.5 2 2.5 x 10
5
5
10
15
0.5 1 1.5 2 2.5 x 10
5
0.06
0.08
0.1
0.04 0.06 0.08 0.1 0.12
0
1000
2000
3000
0 5 10 15 20
0
500
1000
1500
2000
owOWowOWowOWowowowowOWOWOWOWowOWowOWowOW
owowowowowowOWOWOWOWowowowowOWOWOWOWOWOW
oooooooooooooooooooooooooooooooooooooooo
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
1
, Total number of postC7's=40
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.13: Spin system SS
1
with 40 total number of C7s applied.
6.4. DATA AND RESULTS 172
500 1000 1500 2000 2500
2
4
6
8
10
500 1000 1500 2000 2500
0.08
0.09
0.1
0.11
0.07 0.08 0.09 0.1 0.11 0.12
0
5
10
15
20
0 5 10 15
0
10
20
30
oooooooooooooooooooooooooooooooooooooooooooo
OowWoOWwoOWwoOWwoOWwOowWOowWoOWwoOWwOowWOowWOowW
oOOooOOooOOooOOooOOooOOowWWwwWWwwWWwwWWwwWWwwWWw
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
1
, Total number of postC7's=48
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.14: Spin system SS
1
with 48 total number of C7s applied.
6.4. DATA AND RESULTS 173
5 10 15 20 25
0.5
1
1.5
2
5 10 15 20 25
0.1
0.11
0.12
0.13
0.14
0.08 0.1 0.12 0.14 0.16
0
1
2
3
0 0.5 1 1.5 2 2.5
0
1
2
3
oooo
woOW
OooO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
2
, Total number of postC7's=4
M
mag
M
R
postC7
10
3
10
2
Magnitude of Tensor
Figure 6.15: Spin system SS
2
with 4 total number of C7s applied.
6.4. DATA AND RESULTS 174
200 400 600 800 1000 1200
1
2
3
4
200 400 600 800 1000 1200
0.08
0.1
0.12
0.14
0.06 0.08 0.1 0.12 0.14 0.16
0
5
10
15
0 1 2 3 4 5
0
5
10
15
oooooooo
wWwoOoOW
oOOoOooO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
2
, Total number of postC7's=8
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.16: Spin system SS
2
with 8 total number of C7s applied.
6.4. DATA AND RESULTS 175
200 400 600 800
1
2
3
4
5
200 400 600 800
0.08
0.1
0.12
0.06 0.08 0.1 0.12 0.14 0.16
0
5
10
15
0 2 4 6
0
5
10
15
oooooooooooo
wWwWwwwWWWWw
oooOOOoooOOO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
2
, Total number of postC7's=12
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.17: Spin system SS
2
with 12 total number of C7s applied.
6.4. DATA AND RESULTS 176
2000 4000 6000 8000 10000 12000
1
2
3
4
5
2000 4000 6000 8000 10000 12000
0.06
0.08
0.1
0.12
0.04 0.06 0.08 0.1 0.12 0.14
0
50
100
0 2 4 6
0
50
100
oooooooooooooooo
wWWwwWwwwWWWwWwW
ooooOOOoOoooOOOO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
2
, Total number of postC7's=16
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.18: Spin system SS
2
with 16 total number of C7s applied.
6.4. DATA AND RESULTS 177
200 400 600 800 100012001400
2
4
6
200 400 600 800 100012001400
0.07
0.08
0.09
0.1
0.11
0.06 0.08 0.1 0.12
0
5
10
15
0 2 4 6 8
0
5
10
15
oooooooooooooooooooooooo
owowowOWowOWowOWowOWOWOW
owOWowowOWowOWowOWOWowOW
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
2
, Total number of postC7's=24
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.19: Spin system SS
2
with 24 total number of C7s applied.
6.4. DATA AND RESULTS 178
5000 10000 15000
2
4
6
8
5000 10000 15000
0.06
0.07
0.08
0.09
0.1
0.04 0.06 0.08 0.1 0.12
0
50
100
150
0 2 4 6 8 10
0
50
100
150
200
oooooooooooooooooooooooooooooooo
wOwOoWoWwOwOwOoWoWoWwOoWoWoWwOwO
oOOowWWwoOOowWWwoOOowWWwoOOowWWw
Sequence (i)
Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
2
, Total number of postC7's=32
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.20: Spin system SS
2
with 32 total number of C7s applied.
6.4. DATA AND RESULTS 179
5 10 15 20 25
0.5
1
1.5
2
5 10 15 20 25
0.1
0.12
0.14
0.08 0.1 0.12 0.14 0.16
0
1
2
3
0 0.5 1 1.5 2 2.5
0
1
2
3
oooo
woOW
ooOO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
I
x
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I
y
1
I
y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
3
, Total number of postC7's=4
M
mag
M
R
postC7
10
3
10
2
Magnitude of Tensor
Figure 6.21: Spin system SS
3
with 4 total number of C7s applied.
6.4. DATA AND RESULTS 180
200 400 600 800 1000 1200
1
2
3
200 400 600 800 1000 1200
0.08
0.1
0.12
0.06 0.08 0.1 0.12 0.14 0.16
0
5
10
15
20
0 1 2 3 4
0
5
10
15
20
oooooooo
woOWwoOW
woOwoWOW
Sequence(i) Sequence(i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
3
, Total number of postC7's=8
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.22: Spin system SS
3
with 8 total number of C7s applied.
6.4. DATA AND RESULTS 181
200 400 600 800
1
2
3
4
200 400 600 800
0.08
0.1
0.12
0.06 0.08 0.1 0.12 0.14 0.16
0
2
4
6
8
0 1 2 3 4 5
0
5
10
15
oooooooooooo
wWwWwwwWWWWw
oOooooOOOoOO
Sequence(i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
3
, Total number of postC7's=12
M
mag
M
R
postC7 10
3
10
2
10
1
Magnitude of Tensor
Figure 6.23: Spin system SS
3
with 12 total number of C7s applied.
6.4. DATA AND RESULTS 182
2000 4000 6000 8000 10000 12000
1
2
3
4
2000 4000 6000 8000 10000 12000
0.06
0.08
0.1
0.04 0.06 0.08 0.1 0.12
0
50
100
0 1 2 3 4 5
0
50
100
oooooooooooooooo
wWwWWWWWwwwwwWwW
oOoOooooOOOOoOoO
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
3
, Total number of postC7's=16
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.24: Spin system SS
3
with 16 total number of C7s applied.
6.4. DATA AND RESULTS 183
200 400 600 800 1000 1200 1400
2
3
4
5
200 400 600 800 1000 1200 1400
0.07
0.08
0.09
0.1
0.06 0.07 0.08 0.09 0.1 0.11
0
5
10
15
1 2 3 4 5 6
0
5
10
15
oooooooooooooooooooooooo t
oWwOwOwOoWoWwOwOoWoWwOoW
owOWOWowOWOWowowOWowowOW
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
Ix
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I y
1
I y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
3
, Total number of postC7's=24
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.25: Spin system SS
3
with 24 total number of C7s applied.
6.4. DATA AND RESULTS 184
5000 10000 15000
2
3
4
5
6
5000 10000 15000
0.07
0.08
0.09
0.1
0.06 0.07 0.08 0.09 0.1 0.11
0
50
100
150
0 2 4 6 8
0
50
100
150
200
oooooooooooooooooooooooooooooooo t
owOWowOWowOWowowowOWOWowOWOWOWow
wOoWoWwOwOwOoWwOwOoWoWoWwOoWwOoW
Sequence (i) Sequence (i)
b
i
n
c
o
u
n
t
b
i
n
c
o
u
n
t
I
x
1
1
T
2,
0,1
T
2, 0
0,1
T
2,1
0,1
T2,2
0,1
T2,2
0,1
I
x
0
I
y
1
I
y
0
I
z
1
I
z
0
M
mag
i
M
R
i
M
R
i
M
mag
i
Spin System, SS
3
, Total number of postC7's=32
M
mag
M
R
postC7
10
3
10
2
10
1
Magnitude of Tensor
Figure 6.26: Spin system SS
3
with 32 total number of C7s applied.
6.4. DATA AND RESULTS 185
6.4.2 Transfer Eﬃciencies
The amount of data is quite overwhelming however; the results can be consolidated
into a single sentence. Small permutation cycles (i.e. the total number of C7 was less then
8) give expected results from symmetry considerations, any master cycles where the total
number of C7s is greater then 8 give results seemingly uncorrelated results using only the
basic symmetry principles. This is in fact what we were looking for. It does not necessarily
mean that for the longer sequences, that symmetry considerations could not have produced
the desired sequences. In fact the generated sequences are the best conditions to cancel
higher order terms, which if the full average Hamiltonian sequence was generated could
have designed by hand. Of course calculating the average Hamiltonian sequence is a much
harder problem then simply probing the eﬀectiveness of a given sequence. Table 6.6 lists
the best permutation cycles found for each spin system and total number of C7 calculated.
In both the SS
2
and the SS
3
data sets, the protons were not decoupled from
the
13
C nuclei, and the generated sequences are thus diﬀerent for each spin system. Ideal
decoupling of the protons would give the same sequences as SS
1
. There is a problem using
decoupling in RSS sequences. As we are applying a continuous rotation to the carbons,
the RF power applied to the protons must be larger then the power applied to the carbons
for decoupling otherwise we would eﬀectively synchronize the motions of the two nuclei,
creating more recoupling then decoupling. The power ratio condition has been empirically
found to be ω
decoupling
rf
> 3 ∗ ω
13
C
rf
[108, 135, 136]. For large N or large spinning rates in RSS
sequences this is very hard to satisfy experimentally. Because ω
13
C
rf
can be large itself, we
should be able to use this as our decoupling ﬁeld. One can then use similar the symmetry
6.4. DATA AND RESULTS 186
Table 6.6: Best C7 permutation sequences for each spin system and C7 cycle length.
System permutations
SS
1
length best permutation
4 oOOo
8 oOOoOooO
12 oOOooOoOOooO
16 oOoOOoooOOOooOoO
20 ooooOoooooOOOOoOOOOO
24 owOWOWowowOWowOWOWowowOW
32 owowOWowowOWOWOWowowowOWOWowOWOW
40 owowowowowowOWOWOWOWowowowowOWOWOWOWOWOW
48 oOOooOOooOOooOOooOOooOOowWWwwWWwwWWwwWWwwWWwwWWw
SS
2
length best permutation
4 OooO
8 oOOoOooO
12 oooOOOoooOOO
16 ooooOOOoOoooOOOO
24 owOWowowOWowOWowOWOWowOW
32 oOOowWWwoOOowWWwoOOowWWwoOOowWWw
SS
3
length best permutation
4 ooOO
8 woOwoWOW
12 oOooooOOOoOO
16 oOoOooooOOOOoOoO
24 owOWOWowOWOWowowOWowowOW
32 wOoWoWwOwOwOoWwOwOoWoWoWwOoWwOow
6.4. DATA AND RESULTS 187
2τ
r
13
C
1
n
2τ
r
13
C
2
n
S(nt)=Tr[ ρ
f
,I
(z,2)
]
ρ
o
=I
(z,1)
C7
C7
ρ
o
=0
1
H
n
ρ
o
=I
(z,n)
Figure 6.27: Pulse sequence, initial density matrices and detection for a transfer eﬃciency
measurement.
considerations to also remove higher order
1
H −
13
C cross terms. For systems SS
2
and SS
3
the search found permutation sequences that minimized these as well simply because the
larger a T
2,±2
term the less the
1
H −
13
C cross terms as our polarization is conserved
1
.
To investigate the eﬀectiveness of the generated sequences, we looked at the trans
fer eﬃciencies over a range of oﬀset conditions. The applied pulse sequence is shown in
Figure 6.27. The eﬃciencies for the original C7 and the postC7 are shown in Figure 6.28
for the SS
1
system changing only the oﬀset parameters of
13
C
1
and
13
C
2
. The basic C7 is
only eﬀective when the diﬀerence between two oﬀsets is zero, with dramatic increases when
a rotational resonant condition is met ([δ
1
iso
− δ
2
iso
[ = nω
r
). The postC7 is eﬀective over
a much wider range of oﬀsets, with a sharp drop after a positive oﬀset diﬀerence over the
spinning rate.
The next few ﬁgures will show the transfer eﬃciencies for each of the best sequences
as determined from the total C7 length of 4, 8, 12, and 16, comparing them to the original
1
Unitary evolution cannot increase the polarization of the system.
6.4. DATA AND RESULTS 188
C7 after 4 applications
postC7 after 4 applications
20
10
0
10
20 20
10
0
10
20
1
0
1
Max: 0.48497
Min: 0.074039
Max: 0.63848
Min: 0.1754
Transfer Efficiencies for SS
1
spin System vs. Offsets
For the Basic C7 sequences
13
C
1
offset (kHz)
13C
2
offset (kHz)
e
f
f
i
c
i
e
n
c
y
20 15 10 5 0 5 10 15 20
20
0
20
0
.
1
0
0
0
0
0
0
0
0
0
0
0
0
0. 1
0.1
0. 2
0.
2
0.3
0.3
0.4
0. 4
0.5
20 15 10 5 0 5 10 15 20
20
0
20
0
0 0
0 0.1
0 1
0.2
0. 2
0.3
0.3
0.4
0.4
0.4
13
C
2
offset (kHz)
Figure 6.28: Transfer eﬃciencies for a 4 fold application of the basic C7 and the postC7
for the SS
1
system as a function of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz.
6.4. DATA AND RESULTS 189
postC7 sequence given a length of 4, 8, 12, and 16. There are two diﬀerent views for
each data set. The ﬁrst is the 3D proﬁle, which gives a better view of the form of transfer
function, the second is the gradient–contour plot for numerical representations. Data for
spin system SS
1
are shown in Figures 6.29 and 6.30. Data for spin system SS
2
are shown
in Figures 6.31 and 6.32, and data for spin system SS
3
are shown in Figures 6.33 and 6.34.
6.4. DATA AND RESULTS 190
Best 4 permutations
Min: 0.062431
Max: 0.7384
Best 8 permutation
Max: 0.48441
Min: 0.30411
postC7 after 8 applications
Max: 0.46827
Min: 0.18802
postC7 after 12 applications
Min: 0.29139
Max: 0.2434
Best 12 permutation
Min: 0.37582
Max: 0.52092
20
10
0
10
20 20
10
0
10
20
1
0
1
postC7 after 16 applications
Min: 0.2668
Max: 0.28484
20
10
0
10
20 20
10
0
10
20
Best 16 permutation
Min: 0.52742
Max: 0.58538
Max: 0.48497
Min: 0.074039
postC7 after 4 applications
13
C
1
offset (kHz) 13
C
2
offset (kHz)
13
C
1
offset (kHz) 13
C
2
offset (kHz)
e
f
f
i
c
i
e
n
c
y
Transfer Efficencies for SS
1
spin System vs. Offsets
Figure 6.29: 3D transfer eﬃciencies plots for a 4,8,12,16 fold application of the postC7 and
the best permutation cycles for the SS
1
system as a function of
13
C
1
and
13
C
2
oﬀsets at
ω
r
= 5kHz.
6.4. DATA AND RESULTS 191
0.1
0
0
0
0
0
0
0.1
0
.
1
0.2
0
.2
0.3
0
.3
0.4
0
0
0
0
0.1
0.2
0.3
0.3
0.4
0.4
0.5
0
.5
0. 6
0.6
0.7
0
.
7
0
.
1
0
0
0
0
0
0.1
0.1
0.2
0. 2
0.3
0. 3
0.4
0
.
1
0
0
0
0
0
0 1
0. 1
0.1
0. 1
0.2
0. 2
0.2
0. 1
0.1
0
0
0 0
0
0 .1
0.1
0
.
1
0.1
0
.2
0.2
0
.
2
0.3
0
.
3
0.4
0
.
4
20 15 10 5 0 5 10 15 20
20
0
20
0.1
0
.
1
0
0
0
0
0
0
0
0
0
0.1
0
.
1
0
.
1
0
.1
0
.
1
0.2
0
.
2
0.2
20 15 10 5 0 5 10 15 20
20
0
20
0
.
4
0
.
3
0.2
0
.2
0.1
0.1
0
0
0
0
0
0
0
0
0
0
0
0.1
0
.
1
0.1
0
.
1
0. 1
0
.
2
0.2
0.2
0 2
0.3
0. 3
0.4
0.
4
0. 5
0. 5
0
0
0 0.1
0 1
0.2
0. 2
0.3
0.3
0.4
0.4
0.4
postC7 after 4 applications
postC7 after 8 applications
postC7 after 12 applications
postC7 after 16 applications
Best 4 permutation
Best 8 permutation
Best 12 permutation
Best 16 permutation
Transfer Efficencies for SS
1
Spin System vs. Offsets
13
C
2
offset (kHz)
1
3
C
1

o
f
f
s
e
t
(
k
H
z
)
13
C
2
offset (kHz)
Figure 6.30: Contour–gradient transfer eﬃciencies plots for a 4,8,12,16 fold application of
the postC7 and the best permutation cycles for the SS
1
system as a function of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz.
6.4. DATA AND RESULTS 192
20
0
20 20
0
20
1
0
1
20
0
20
20
0
20
1
0
1 Min: 0.31563
Max: 0.32221
Max: 0.34481
Min: 0.17917
Min: 0.21751
Max: 0.32125
Min:0.17093
Max: 0.42129
Min: 0.24223
Max: 0.49721
Min: 0.038105
Max: 0.69145
Min: 0.13172
Max: 0.41516
Best 4 permutation
Best 8 permutation
postC7 after 8 applications
postC7 after 12 applications
Best 12 permutation
postC7 after 16 applications Best 16 permutation
postC7 after 4 applications
Transfer Efficiencies for SS
2
spin System vs. Offsets
Min: 0.042667
Max: 0.38451
13
C
1
offset (kHz)
13
C
2
offset (kH
z)
e
f
f
i
c
i
e
n
c
y
13
C
1
offset (kHz)
13
C
2
offset (kH
z)
Figure 6.31: 3D transfer eﬃciencies plots for a 4,8,12,16 fold application of the postC7 and
the best permutation cycles for the SS
2
system as a function of
13
C
1
and
13
C
2
oﬀsets at
ω
r
= 5kHz.
6.4. DATA AND RESULTS 193
0
0
0
0
0
0
.
1
0
.1
0.2
0
.
2
0.3
0
.
3
0
0
0
0
0. 1
0. 2
0.3
0 3
0. 4
0. 4
0. 5
0
.5
0.6
0
0
0
0
0
.
1
0
.
1
0.2
0
.
2
0
.
3
0. 3
0
0
0
0
0
0
.
1
0
.
1
0
.
1
0. 2
0.2
0
.2
0
.
3
0. 3
0
.3
0. 4
0
.
4
0
0
0
0
0
0
0. 1
0. 1
0.1
0
.1
0.2
0. 2
0
.
2
0
0
0
0
0
0
0
0
0
0
0
0
0
.
1
0
.
1
0
.
1
0.2
0
.2
0
.
3
0
.
3
20 10 0 10 20
20
0
20
0
.
1
0
0
0
0
0
0
0 1
0. 1
0.1
0
.
1
0.1
0.2
0
.
2
0
.2
0
.
2
20 10 0 10 20
20
0
20
0
0
0
0
0
0
0
0 1
0
.
1
0
.
1
0
.
1
0
.
1
0.2
0
.
2
postC7 after 4 applications
postC7 after 8 applications
postC7 after 12 applications
postC7 after 16 applications
Best 4 permutation
Best 8 permutation
Best 12 permutation
Best 16 permutation
Transfer Efficencies for SS
2
Spin System vs. Offsets
13
C
2
offset (kHz)
1
3
C
1

o
f
f
s
e
t
(
k
H
z
)
13
C
2
offset (kHz)
Figure 6.32: Contour–gradient transfer eﬃciencies plots for a 4,8,12,16 fold application of
the postC7 and the best permutation cycles for the SS
2
system as a function of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz.
6.4. DATA AND RESULTS 194
Max: 0.26525
Min: 0.13923
Max: 0.25827
Min 0.12492
20
0
20
20
0
20
1
0
1
Max: 0.25547
Min: 0.13129
20
0
20
20
0
20
1
0
1 Max: 0.26315
Min: 0.074689
Min: 0.054667 Max: 0.34514
Min: 0.019694
Max: 0.5872
Min: 0.086714
Max: 0.30833
Min: 0.12314
Max: 0.20661
Best 4 permutation
Best 8 permutation
postC7 after 8 applications
postC7 after 12 applications
Best 12 permutation
postC7 after 16 applications Best 16 permutation
postC7 after 4 applications
Transfer Efficiencies for SS
3
Spin System vs. Offsets
13
C
1
offset (kHz)
13
C
2
offset (kH
z)
e
f
f
i
c
i
e
n
c
y
13
C
1
offset (kHz)
13
C
2
offset (kH
z)
Figure 6.33: 3D transfer eﬃciencies plots for a 4,8,12,16 fold application of the postC7 and
the best permutation cycles for the SS
3
system as a function of
13
C
1
and
13
C
2
oﬀsets at
ω
r
= 5kHz.
6.4. DATA AND RESULTS 195
0
0
0
0
0
0
0
.1
0
.
1
0. 2
0.2
0
.3
0
0
0
0
0
0.1
0
.
2
0. 3
0
.
3
0.4
0
.
4
0
.5
0
0
0
0
0
.
1
0
.1
0.2
0
.
2
0.3
0
0
0
0 0
0
.1
0.1
0
.
2
0
0
0
0
0
0
0. 1
0
.
1
1
0
.
1
0. 2
0. 2
0
0
0
0
0
0 0
0.1
0.1
0.2
0
.
2
20 10 0 10 20
20
0
20
0
0
0
0
0. 1
0. 1
0
.
1
0
.
2
0.2
0
.
2
0.2
20 10 0 10 20
20
0
20
0
0
0
0
0
0
0
0
0
0
.
1
0.1
0.1
0. 1
0.1
0.2
0
.
2
postC7 after 4 applications
postC7 after 8 applications
postC7 after 12 applications
postC7 after 16 applications
Best 4 permutation
Best 8 permutation
Best 12 permutation
Best 16 permutation
Transfer Efficencies for SS
3
Spin System vs. Offsets
13
C
2
offset (kHz)
1
3
C
1

o
f
f
s
e
t
(
k
H
z
)
13
C
2
offset (kHz)
Figure 6.34: Contour–gradient transfer eﬃciencies plots for a 4,8,12,16 fold application of
the postC7 and the best permutation cycles for the SS
3
system as a function of
13
C
1
and
13
C
2
oﬀsets at ω
r
= 5kHz.
6.5. CONCLUSIONS 196
As the Figures clearly show, the permuted sequences are always better then the
standard postC7 sequences. We can generate similar views as in Figure 6.4 by taking slices
along each of the above ﬁgures using each the best permutation cycle. These complete
transfer diagrams are shown in Figures 6.356.37 for systems SS
1
, SS
2
and SS
3
respectively.
The resulting transfers are on average 50% better in eﬃciency transfer and 25% more stable
(the standard deviation across speciﬁc oﬀset value) then the original sequence.
6.5 Conclusions
RSS sequences represent a large class of the pulse sequences used in solidstate
NMR. They rely on generation of a speciﬁc zeroth order average Hamiltonian. However, in
real systems the desired eﬀect this zeroth order average Hamiltonian is destroyed by many
experimental and system speciﬁc parameters. To correct for these problem, the symmetry
of the system and the zeroth order average Hamiltonians are used to cancel other terms in
the expansion. The implementation of symmetry is usually broken into two parts. Internal
compensation and posting techniques as designed to act on a small part of the total sequence.
Super–cycling takes these internally compensated sequences through application of phase
shifts, attempts to compensate for errors in the total sequence. This process, for small
number of sequence applications tends to work very well to compensate for errors. As
the sequence becomes longer and longer, this simple approach breaks down as higher and
higher order terms and errors accumulate. Determination of the best super–cycle for these
longer sequences becomes a tedious task as many orders of the average Hamiltonian must
be calculated and analyzed for weaknesses, a task for the general spin system is nearly
impossible analytically. We can, however, use a permutation approach to the problem as
6.5. CONCLUSIONS 197
4 6 8 10 12 14 16
0. 4
0. 2
0
0.2
0.4
0.6
4 6 8 10 12 14 16
0.6
0.4
0.2
0
0.2
0.4
0.6
0.8
cycles
T
r
a
n
s
f
e
r
E
f
f
i
e
n
c
y
Original postC7
Best Permutation
12000 Hz  12000 Hz
12000 Hz 
0 Hz
12000 Hz  12000 Hz
0 Hz  12000 Hz
0 Hz  0 Hz
0 Hz  12000 Hz
12000 Hz  12000 Hz
12000 Hz  0 Hz
12000 Hz  12000 Hz
13
C
1
,δ
iso
13
C
2
,δ
iso
Figure 6.35: Transfer Eﬃciencies using the postC7 and the best permutated cycles across
over diﬀerent cycles for the SS
1
spin system.
6.5. CONCLUSIONS 198
4 6 8 10 12 14 16
0. 4
0. 3
0. 2
0. 1
0
0.1
0.2
0.3
0.4
4 6 8 10 12 14 16
0.2
0.1
0
0.1
0.2
0.3
0.4
0.5
cycles
T
r
a
n
s
f
e
r
E
f
f
i
e
n
c
y
Original postC7
Best Permutation
12000 Hz  12000 Hz
12000 Hz 
0 Hz
12000 Hz  12000 Hz
0 Hz  12000 Hz
0 Hz  0 Hz
0 Hz  12000 Hz
12000 Hz  12000 Hz
12000 Hz  0 Hz
12000 Hz  12000 Hz
13
C
1
,δ
iso
13
C
2
,δ
iso
Figure 6.36: Transfer eﬃciencies using the postC7 and the best permutated cycles across
over diﬀerent cycles for the SS
2
spin system.
6.5. CONCLUSIONS 199
4 6 8 10 12 14 16
0.15
0. 1
0.05
0
0.05
0.1
0.15
0.2
0.25
0.3
4 6 8 10 12 14 16
0.2
0.1
0
0.1
0.2
0.3
0.4
0.5
cycles
T
r
a
n
s
f
e
r
E
f
f
i
e
n
c
y
Original postC7
Best Permutation
12000 Hz  12000 Hz
12000 Hz 
0 Hz
12000 Hz  12000 Hz
0 Hz  12000 Hz
0 Hz  0 Hz
0 Hz  12000 Hz
12000 Hz  12000 Hz
12000 Hz  0 Hz
12000 Hz  12000 Hz
13
C
1
,δ
iso
13
C
2
,δ
iso
Figure 6.37: Transfer eﬃciencies using the postC7 and the best permutated cycles across
over diﬀerent cycles for the SS
3
spin system.
6.5. CONCLUSIONS 200
we have shown here to generate markedly improved sequences using only the well known
symmetry principles used for the shorter sequences.
201
Chapter 7
Future Expansions
The permutation technique described in chapter 6 is limited only by computer
power and memory. For the study of the postC7 the total simulation time for all of the
permutations in all of the spin systems took about 3 weeks running on 4 diﬀerent processors.
If we had not optimized each step as we have done in chapters 4 and 2 this problem would
have taken months to accomplish.
One may ask themselves why every permutation had to be calculated? Why
not perform a minimization (or maximization in this case)? There are two fundamental
problems associated with using normal minimization methods. The ﬁrst is the vast di
mensionality of the system. Using gradient or simulate annealing methods would almost
certainly ﬁnd local minima and not global minima unless it can sample most of the space.
The second problem is that there is little information about the functional form of this
‘minimization’ function. We cannot say it is continuous or single valued making gradient
searching techniques inaccurate and not robust. We are still not even sure if the function
at point a parameter point A has any method of getting us to A+ 1 without a look–up
7.1. EVOLUTIONARY ALGORITHMS (EA) 202
table approach. If it is a look–up table, then running all the permutations seems to be
the only way, unless we leave gradient/distance based minimization techniques. It is for
these reasons that these techniques failed to produce any reasonable answers, and why the
permutation approach seems to be the best alternative at the time.
There are techniques for both search very large dimensional spaces as well as the
‘look–up’ table problem and both are easily tackled using Evolutionary type Algorithms
(EA)[137, 138], or Neural Networks (NN), or even both. I will discuss both in turn a give
the basic structure for implementing both of these structures and the problems that should
be able to be tackled using them.
7.1 Evolutionary Algorithms (EA)
The basics of an EA begin with a ‘gene.’ A gene in the biological sense propagates
forward by creating children. These children contain some mixture of both parents due to
crossover/breeding and mutations. A gene is then only likely to survive if it has a suitable
mixture of the good qualities from the parents. It will die oﬀ if the child has inherited most
of the bad ones. The relevance of ‘good’ and ‘bad’ from an algorithmic point of view is
given as follows: a ‘good’ gene is one which has a ﬁtness better or close to the parents value,
a ‘bad’ gene has a worse ﬁtness then the parents. The ﬁtness can simply be an evaluation
of the function giving a χ
2
or distance value or in our case the M
R
value. We wish to ﬁnd
the best gene/phenotype we can.
There two diﬀerent classes of EA using the blending mechanisms. If the blending
mechanism includes both crossover and mutation it is called a Genetic Algorithm (GA)[139,
140, 141], if it only uses mutation (i.e. the children have no ‘parents’, just mutated versions
7.1. EVOLUTIONARY ALGORITHMS (EA) 203
of itself) then this is call Evolutionary Programming (EP)[142, 143]. GAs use both a type
of forced evolution from the parents and a pure evolution from mutation, where as EPs use
only the pure evolution. A sub class of GAs is the Diﬀerential Evolution (DE)[144] where
a child can have more then one parent. However the blending is performed, a strategy
is typically devised for moving one step in a generation. These are usually called p–c
Evolutionary strategies[145, 146], ES
(p,c)
. Here p is the number of parents, and c is the
number of children to be generated. These ESs are best shown pictorially as shown in
Figure 7.1 along with a sub class of ESs, the ‘plus’ and ‘comma’ methods.
Which ever strategy is used we need to make our problem and the parameter space
ﬁt into a gene. There are many diﬀerent ways of implementing such a gene using the RSS
structure based on the number of assumption we can/wish to make. Using the methodology
we discussed in chapter 6 our gene is of the length of the total number of cycles in we wish
to optimize. A ‘base–pair’ is then one of the 4 possible symmetry cycles (o, O, w, W). Two
arbitrary parent genes and a resulting child gene is shown in Figure 7.2. Initially we would
generate a random set of these genes for the parents and evaluate the ﬁtness for each of
them (here it would be M
R
) . In fact it would be better to pick genes that span the extremes
of the parameter space. This means including genes with all of one type of base–pair (i.e.
(o,o,o,o,o...), (W,W,W,W,W,W,...), (w,w,w,w,w,w,...), (O,O,O,O,O,...)). If one does not
span the entire range initially, it is likely that the minimization will remain in the local
range initially generated, or that it will take many iterations to get out of the local area.
The next step is where a GA or DE or EP algorithmic decision comes into play
as well as the rates of mutation and cross over points. A diagram showing the plus and
comma type of ES
(2,1)
for an EP algorithm is shown in Figure 7.3. A diagram showing the
7.1. EVOLUTIONARY ALGORITHMS (EA) 204
p Parents
c children
"Plus strategy"
p Parents
c children
Survive both
"Comma strategy"
c children
Survive children
Controls
p,c > Environment considerations
(steep decents require much
fewer children)
p/c > small=fast local convergence
large=slower 'global' convergence
method > plus keeps best solution always
commaallows local minima escape
Figure 7.1: The standard evolutionary strategy methods and controls.
7.1. EVOLUTIONARY ALGORITHMS (EA) 205
o o w O O w W o
p
1
p
2
O o o
c
1
O o o o w w O O
crossover point=3
O O w W o
no mutations
Figure 7.2: An arbitrary permutation cycle parent genes and resulting child.
plus and comma type of ES
(3,2)
for a GA algorithm is shown in Figure 7.4, and a diagram
showing the plus and comma type of ES
(3,1)
for a DE algorithm is shown in Figure 7.5.
DE methods are particular suited for continuous parameter space problems where the ‘add’
function in Figure 7.5 makes some sort of since. Both EP and GA methods are better suited
for the ‘4–switch’, (w,W,o,O), type of values we want to search for.
In the permutations examples, we have limited ourselves to the basic symmetry
4–switch, we should be able to use the DE types for more complex searches. For instance
we can arbitrarily change the phase of each RSS cycle and ﬁnd a minimum. This phase
changing should provide even better cycles then the permutation method as hinted by the
super–cycle of Ref. [120]. This leads us to another branch of searches were we may begin
to ﬁnd diﬀerent ‘post’ methods for an internal compensation of sequences. This type of
internal search has been performed before, however, using the gradient techniques and for
a much less general problem[147].
7.1. EVOLUTIONARY ALGORITHMS (EA) 206
o
o
w
O
O
O
W
o
p
1
p
2
M
R
=
0
.
2
3
M
R
=
0
.
1
4
o
o
w
O
O
w
W
o
O
o
o
o
w
w
O
O
mutate
c
1
PlusES
(2,1)
M
R
=
0
.
2
2
M
R
=
0
.
2
6
O
o
W
o
w
w
O
O
keep
keep
K
e
e
p
p
1
a
n
d
c
2
f
o
r
t
h
e
n
e
x
t
g
e
n
e
r
a
t
i
o
n
c
2
o
o
w
O
O
O
W
o
p
1
p
2
M
R
=
0
.
2
3
M
R
=
0
.
1
4
o
o
w
O
O
w
W
o
O
o
o
o
w
w
O
O
mutate
c
1
M
R
=
0
.
2
2
M
R
=
0
.
2
6
O
o
W
o
w
w
O
O
keep
keep
K
e
e
p
c
2
f
o
r
t
h
e
n
e
x
t
g
e
n
e
r
a
t
i
o
n
,
m
u
t
a
t
e
p
1
a
n
d
p
2
a
g
a
i
n
t
o
f
i
n
d
a
n
o
t
h
e
r
c
h
i
l
d
c
2
CommaES
(2,1)
Evolutionary Programming
Figure 7.3: Evolution Programming (EP) generation step for an ES
(2,1)
strategy.
7.1. EVOLUTIONARY ALGORITHMS (EA) 207
o
o
w
O
o
O
o
o
p
1
p
2
M
R
=0.23
M
R
=0.14
o
o
w
O
O
w
W
o
O
o
o
o
w
w
O
O
c
2
PlusES
(3,2)
MR=0.22 MR=0.23
O
o
W
o
O
w
W
o
k
e
e
p
c
1
K
e
e
p
c
2
&
c
1
f
o
r
t
h
e
n
e
x
t
g
e
n
e
r
a
t
i
o
n
,
p
e
r
f
o
r
m
b
r
e
e
d
i
n
g
a
g
a
i
n
t
o
f
i
n
d
a
n
o
t
h
e
r
c
h
i
l
d
CommaES
(3,2)
p
3
M
R
=0.04
O
o
o
o
o
o
o
o
random parent & breed & mutate
k
e
e
p
k
e
e
p
o
o
w
O
o
O
o
o
p
1
p
2
M
R
=0.23
M
R
=0.14
o
o
w
O
O
w
W
o
O
o
o
o
w
w
O
O
c
2
MR=0.22 MR=0.23
O
o
W
oo
O
w
W
o
c
1
p
3
M
R
=0.04
O
o
o
o
o
o
o
o
random parent & breed & mutate
k
e
e
p
k
e
e
p
Genetic Algorithm
Figure 7.4: Genetic Algorithm (GA) generation step for an ES
(3,2)
strategy.
7.1. EVOLUTIONARY ALGORITHMS (EA) 208
p
1
p
2
M
R
=0.23
M
R
=0.14
o
o
w
O
O
w
W
o
O
o
o
o
w
w
O
O
PlusES
(3,1)
MR=0.23
O
o
W
o
O
w
W
o
k
e
e
p
c
1
K
e
e
p
c
1
f
o
r
t
h
e
n
e
x
t
g
e
n
e
r
a
t
i
o
n
,
p
e
r
f
o
r
m
b
r
e
e
d
i
n
g
a
g
a
i
n
t
o
f
i
n
d
a
n
o
t
h
e
r
c
h
i
l
d
CommaES
(3,1)
p
3
M
R
=0.04
O
o
o
o
o
o
o
o
k
e
e
p
p
1
p
2
M
R
=0.23
M
R
=0.14
o
o
w
O
O
w
W
o
O
o
o
o
w
w
O
O
p
3
M
R
=0.04
O
o
o
o
o
o
o
o
Differential Evolution
'add' 3 parents & breed
k
e
e
p
MR=0.23
O
o
W
o
O
w
W
o
c
1
k
e
e
p
'add' 3 parents & breed
Figure 7.5: Diﬀerential Evolution (DE) generation step for an ES
(3,1)
strategy.
7.2. NEURAL NETWORKS 209
INPUT 0
INPUT 1
OUTPUT 0
i0 i1
b1
1 Level NN
INPUT 0
INPUT 1
OUTPUT 0
i0 i1
b1
w01 w11
hidden layer
w01 w11
w10 w00
w20 w02
input layer
output layer
2 Level NN
hidden layer
input layer
output layer
Figure 7.6: Basic 1 and 2 layer feed–forward neural networks.
7.2 Neural Networks
There is astounding amount of literature on neural networks, much of it organized
towards programmers[148, 149, 150]. To go through all but the most basic of neural networks
here, would be much too much, so I will only attempt to scratch the surface of their
capability. A neural network is simply a number of nodes connected by weights to other
nodes based on neurons in a brain. It is designed to recreate a function when the function
is unknown. Like a brain, in order to make predictions it must be trained. The training
process is the hardest part of designing a NN and is crucial to the prediction power of a
network[151]. For a network to begin to model a function, it must be trained with known
inputs and outputs, then it should be able to give the correct outputs based on arbitrary
inputs for a particular model. Figure 7.6 represents a subclasse of NNs used for predictive
purposes, a one and two level feed–forward (FF) NN (there are others, such as self organizing
7.2. NEURAL NETWORKS 210
networks). The ‘feedforward’ implies that the input layer, I, eﬀects the next layer(s) (called
the hidden layers) and these eﬀect the output layers. Each node, i, in a FFNN uses the
weights, w
ij
of the previous layer passed through a ‘relevance’ or threshold (R) function
which determines its value, N
i
.
N
i
= R
_
connected nodes
w
ij
I
j
+b
i
_
(7.1)
The relevance function is used to model a typical neuron electronic switch where
if the electric potential is high enough, it will pass on the signal, if it is not, the signal will
halt there. The relevance function is usually a sigmoid function (1/(1 + exp(−A))) or a
simple step function. The sigmoid allows for a small range of valid signals to pass, where
as the step function does not. There is also a bias value b
i
applied to each value.
To train a network one typically picks a ﬁxed number of hidden layers, and then
manipulates the weights w
ij
and the connectivity of the nodes. This is where the majority
of the NN literature is based as in essence we wish to choose only the relevant data[152]
and determine the connectivity and weights as a minimization process. One can even use
evolutionary techniques to perform the minimization[153]. The simplest technique is back
propagation. This minimization process does not try to determine any relevance or node
removal; it simply uses the diﬀerences in the desired values and the current output values
to adjust the weights and biases. Given a training data set, we can specify a ‘learning–rate’
(a value from 0..1) that determines the amount to adjust the weights given the distances.
If we choose a learning rate of 1 then the network will quickly adjust the weights to match
that one data set, if it is 0 then no adjustments occur with the diﬀerences. A simple back
propagation fully connected FFNN C++ class is given in Appendix A.1.5.
So what could a NN provide in our optimization of the generic RSS (and potentially
7.3. FINAL REMARKS 211
other) sequences? As you can see the amount of data generated from the permutation study
is quite large. We now have a huge training data set. In essence one could train a NN given
the permutation order as the output, and the tensor coeﬃcients as the input. As we generate
more and more data we could even output a phases of an RSS sub–cycle from the tensor
coeﬃcients. After the training, we should be able to see two things. The ﬁrst, and most
obvious, is attempting to input our maximum T
2,±2
condition and see what the network
produces as an answer. The second, and I think more interesting, is the information that
can be obtained from the relevant weights and connectivity’s. From these particular values,
it could be possible to ﬁnd the most relevant pathways, which we could then infer possible
symmetry classes to the general analytical problem. If nothing else, a properly trained NN
can at least give us the good answers from a sequence abstraction without having to go
through each permutation or phase shift, only a much smaller subset of them.
7.3 Final Remarks
The majority of this thesis is geared to the creation of fast numerical techniques
and algorithms to simulate NMR situations as fast as one can. We can now tackle problems
that were before next to impossible before this assembly. The genetic and neural networks
applications are some of the more interesting paths to follow as their implementation is now
some what possible given the processes shown here. There results, however, are unknown.
For all I know, these newer algorithmic techniques may not give much new insight to the
basic problems and control of NMR. However, simply looking at the statistical distributions
of one RSS sequence has demonstrated that the solution NMR seeks is usually the far outlier
(our evolutionary searches), while most of the data ﬁts into a nice distribution (the neural
7.3. FINAL REMARKS 212
networks). Optimal control of a given NMR system may soon be reduced to a trained neural
network producing ﬁrst order results (pulse sequences), while the evolutionary techniques
ﬁnd the optimum.
213
Bibliography
[1] E. M. Purcell, H. C. Torrey, and R. V. Pound, Phys. Rev. 69 (12), 37 (1946).
[2] F. Bloch, W. W. Hansen, and P. M., Phys. Rev. 70 (78), 474 (1946).
[3] A. Abragam, The Principles of Nuclear Magnetism: The International Series of
Monographs on Physics (Clarendon Press, Oxford, 1961).
[4] M. H. Levitt, Spin Dynamics: Basics of Nuclear Magnetic Resonance (John Wiley &
Sons, ltd., Chichester, 2000).
[5] R. R. Ernst, G. Bodenhausen, and A. Wokaun, Principles of Nuclear Magnetic Res
onance in One and Two Dimensions (Clarendon Press, Oxford, 1989).
[6] C. P. Slichter, Principles of Magnetic Resonance (Springer, Heidelberg, 1978).
[7] A. Turing, in Proceedings of the London Mathematical Society, Series 2 (Oxford Uni
versity Press, Oxford, 1936), Vol. 42.
[8] R. W. Sebesta, Concepts of Programming Languages 5/E (Addison Wesley Higher
Education, Boston, MA, 2001).
[9] B. Stroustrup, The design and evolution of C++ (AddisonWesley, Boston, MA,
1994).
BIBLIOGRAPHY 214
[10] B. Stroustrup, The C++ Programming Language. third ed. (AddisonWesley, Boston,
MA, 1997).
[11] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns: Elements of
Reusable ObjectOriented Software (AddisonWesley, Boston, MA, 1995).
[12] E. Anderson, Z. Bai, C. Bischof, S. Blackford, J. Demmel, J. Dongarra, J. D. Croz, A.
Greenbaum, S. Hammarling, A. McKenney, and D. Sorensen., LAPACK Users Guide,
third ed. (Society for Industrial and Applied Mathematics, Philadelphia, PA, 1999).
[13] T. L. Veldhuizen and M. E. Jernigan, in Proceedings of the 1st International Scientiﬁc
Computing in ObjectOriented Parallel Environments (ISCOPE’97), Lecture Notes in
Computer Science (SpringerVerlag, New York, 1997).
[14] T. Veldhuizen, C++ Report 7(5), 26 (1995).
[15] E. Unruh, 1994, aNSI X3J16940075/ISO WG21462.
[16] U. W. Eisenecker and K. Czarnecki, Generative Programming  Towards a New
Paradigm of Software Engineering (Addison Wesley, Boston, MA, 2001).
[17] C. Pescio, C++ Report 9(7), (1997).
[18] N. C. Myers, C++ Report 7(5), 42 (1995).
[19] T. Veldhuizen, C++ Report 7(4), 36 (1995).
[20] J. G. Siek, Master’s thesis, University of Notre Dame, 1994.
[21] W. Clint, automatically Tuned Linear Algebra Software (ATLAS).
BIBLIOGRAPHY 215
[22] J. L. Hennessy and D. A. Patterson, Computer Organization and Design (Mogan
Kaufmann Publishers Inc., San Francisco, Ca, 1998).
[23] F. Bloch, Phys. Rev. 70 (78), 460 (1946).
[24] M. Mehring and V. A. Weberruss, Object–Oriented Magnetic Resonance (Academic
Press, London, UK, 2001).
[25] A. Vlassenbroek, J. Jeener, and P. Broekaert, J. Mag. Reson. A 118, 234 (1996).
[26] J. Jeener, A. Vlassenbroek, and P. Broekaert, J. Chem. Phys. 103(9), 1309 (1995).
[27] G. Deville, M. Bernier, and J. M. Delrieux, Phys. Rev. B 19(11), 5666 (1979).
[28] T. Enss, S. Ahn, and W. S. Warren, Chem. Phys. Lett. 305, 101 (1999).
[29] W. S. Warren, S. Lee, W. Richter, and S. Vathyan, Chem. Phys. Lett. 247, 207 (1995).
[30] R. N. Zare, Angular Momentum: Understanding Spatial Aspects in Chemistry and
Physics (John Wiley & Sons, Inc., Chichester, 1988).
[31] S. Wi and L. Frydman, J. Chem. Phys. 112(7), 3248 (2000).
[32] W. Warren, W. Richter, A. Andreotti, and B. Farmer, Science 262 (5142), 2005
(1993).
[33] W. Richter and W. Warren, Conc. Mag. Reson. 12(6), 396 (2000).
[34] S. Lee, W. Richter, S. Vathyam, and W. S. Warren, J. Chem. Phys. 105(3), 874
(1996).
[35] W. S. Warren, S. Y. Huang, S. Ahn, and Y. Y. Lin, J. Chem. Phys. 116(5), 2075
(2002).
BIBLIOGRAPHY 216
[36] Q. H. He, W. Richter, S. Vathyam, and W. S. Warren, J. Chem. Phys. 98(9), 6779
(1993).
[37] R. R. Rizi, S. Ahn, D. C. Alsop, S. GarrettRoe, M. Mescher, W. Richter, M. D.
Schnall, J. S. Leigh, and W. S. Warren, Mag. Reson. Med. 18, 627 (2000).
[38] W. Richter, M. Richtera, W. S. Warren, H. Merkle, P. Andersen, G. Adriany, and K.
Ugurbil, Mag. Reson. Img. 18, 489 (2000).
[39] W. Richter, S. Lee, W. Warren, and Q. He, Science 267 (5198), 654 (1995).
[40] J. H. V. Vleck, Electric and Magnetic Susceptibilities (Oxford University Press, Great
Britan, 1932).
[41] P. Deuﬂhard, Numerische Mathematik 41, 399 (1983).
[42] J. R. Cash and A. H. Karp, ACM Transactions on Mathematical Software 16, 201
(1990).
[43] J. Stoer and R. Bulirsch, Introduction to Numerical Analysis (SpingerVerlag, New
York, 1980).
[44] P. Deuﬂhard, SIAM Rev. 27, 505 (1985).
[45] W. H. Press, S. A. Teukolsky, W. T. Vetterling, and B. P. Flannery, Numerical Recipes
in C, The Art of Scientiﬁc Computing (Cambridge University Press, Cambridge,
1997).
[46] C. W. Gear, Numerical Initial Value Problems in Ordinary Diﬀerential Equations
(Prentice–Hall, Englewood Cliﬀs, NJ, 1971).
BIBLIOGRAPHY 217
[47] J. H. Shirley, Phys. Rev. B. 138, 979 (1965).
[48] A. Schmidt and S. Vega, J. Chem. Phys. 96 (4), 2655 (1992).
[49] R. Challoner and M. CA, J. Mag. Reson. 98 (1), 123 (1992).
[50] O. Weintraub and S. Vega, J. Mag. Reson. Ser. A. 105 (3), 245 (1993).
[51] T. Levante, B. H. Baldus, M.and Meier, and R. Ernst, Mol. Phys. 86 (5), 1195 (1995).
[52] J. W. Logan, J. T. Urban, J. D. Walls, K. H. Lim, A. Jerschow, and A. Pines, Solid
State NMR 22, 97 (2002).
[53] J. Walls, K. Lim, J. Logan, J. Urban, A. Jerschow, and A. Pines, J. Chem. Phys 117,
518 (2002).
[54] J. Walls, K. Lim, and A. Pines, J. Chem. Phys. 116, 79 (2002).
[55] M. H. Levitt, D. P. Raleigh, F. Creuzet, and R. G. Griﬃn, J. Chem. Phys. 92(11),
6347 (1990).
[56] D. P. Raleigh, M. H. Levitt, and R. G. Griﬃn, Chem. Phys. Lett. 146, 71 (1988).
[57] M. G. Colombo, B. H. Meier, and R. R. Ernst, Chem. Phys. Lett. 146, 189 (1988).
[58] Y. Zur and M. H. Levitt, J. Chem. Phys. 78(9), 5293 (1983).
[59] M. Eden, Y. K. Lee, and M. H. Levitt, J. Magn. Reson. A. 120, 56 (1996).
[60] M. Hohwy, H. Bildse, and N. C. Nielsen, J. Magn. Reson. 136, 6 (1999).
[61] T. Charpentier, C. Fermon, and J. Virlet, J. Magn. Reson. 132, 181 (1998).
[62] M. H. Levitt and M. Eden, Mol. Phys. 95(5), 879 (1998).
BIBLIOGRAPHY 218
[63] H. Geen and r. Freeman, J. Mag. Reson. 93(1), 93 (1991).
[64] P. Bilski, N. A. Sergeev, and J. Wasicki, Solid State Nuc. Mag. Reson. 22(1), 1 (2002).
[65] A. Baram, J. Phys. Chem. 88(9), 1695 (1984).
[66] M. Mortimer, G. Oates, and T. B. Smith, Chem. Phys. Lett. 115(3), 299 (1985).
[67] A. Kumar and P. K. Madhu, Conc. Mag. Reson. 8(2), 139 (1996).
[68] P. Hazendonk, A. D. Bain, H. Grondey, P. H. M. Harrison, and R. S. Dumont, J.
Mag. Reson. 146, 33 (2000).
[69] M. Eden and M. H. Levitt, J. Mag. Reson. 132, 220 (1998).
[70] D. W. Alderman, M. S. Solum, and D. M. Grant, J. Chem. Phys. 84, 3717 (1986).
[71] M. J. Mombourquette and J. A. Weil, J. Mag. Reson. 99, 37 (1992).
[72] L. Andreozzi, M. Giordano, and D. Leporini, J. Mag. Reson. A 104, 166 (1993).
[73] D. Wang and G. R. Hanson, J. Mag. Reson. A 117, 1 (1995).
[74] S. J. Varner, R. L. Vold, and G. L. Hoatson, J. Mag. Reson. A 123, 72 (1996).
[75] M. Bak and N. C. Nielsen, J. Mag. Reson. 125, 132 (1997).
[76] S. K. Zaremba, Ann. Mat. Pura. Appl. 4:73, 293 (1966).
[77] J. M. Koons, E. Hughes, H. M. Cho, and P. D. Ellis, J. Mag. Reson. A 114, 12 (1995).
[78] L. GonzalezTovany and V. BeltranLopez, J. Mag. Reson. 89, 227 (1990).
[79] C. V. B., H. H. Suzukawa, and M. Wolfsberg, J. Chem. Phys. 59(8), 3992 (1973).
BIBLIOGRAPHY 219
[80] H. Conroy, J. Chem. Phys. 47(2), 5307 (1967).
[81] V. I. Lebedev, Zh. Vychisl. Mat. Fiz. 16, 293 (1976).
[82] V. I. Lebedev, Zh. Vychisl. Mat. Fiz. 15, 48 (1975).
[83] J. Dongarra, P. Kacsuk, and N. P. (eds.)., Recent advances in parallel virtual ma
chine and message passing interface: 7th European PVM/MPI users group meeting
(Springer, Berlin, 2000).
[84] P. Hodgkinson and L. Emsley, Prog. Nucl. Magn. Reson. Spectrosc. 36, 201 (2000).
[85] M. Bak, J. T. Rasmussen, and N. C. Nielsen, J. Magn. Reson. 147, 296 (2000).
[86] S. Smith, T. Levante, B. Meier, and R. Ernst, J. Mag. Reson. 106a, 75 (1994).
[87] Y. Y. Lin, N. Lisitza, S. D. Ahn, and W. S. Warren, Science 290 (5489), 118 (2000).
[88] C. A. Meriles, D. Sakellariou, H. Heise, A. J. Moule, and A. Pines, Science 293, 82
(2001).
[89] H. Heise, D. Sakellariou, C. A. Meriles, A. Moule, and A. Pines, J. Mag. Reson. 156,
146 (2002).
[90] T. M. Brill, S. Ryu, R. Gaylor, J. Jundt, D. D. Griﬃn, Y. Q. Song, P. N. Sen, and
M. D. Hurlimann, Science 297, 369 (2002).
[91] R. McDermott, A. H. Trabesinger, M. Muck, E. L. Hahn, A. Pines, and J. Clarke,
Science 295, 2247 (2002).
[92] J. D. Walls, M. Marjanska, D. Sakellariou, F. Castiglione, and A. Pines, Chem. Phys.
Lett. 357, 241 (2002).
BIBLIOGRAPHY 220
[93] R. H. Havlin, G. Park, and A. Pines, J. Mag. Reson. 157, 163 (2002).
[94] M. Frigo and S. G. Johnson, Technical report, Massachusetts Institute of Technology
(unpublished).
[95] E. Lusk, Technical report, University of Tennessee (unpublished).
[96] F. James, Technical report, Computing and Networks Division CERN Geneva,
Switzerland (unpublished).
[97] G. Bader and P. Deuﬂhard, Numerische Mathematik 41, 373 (1983).
[98] M. K. Stehling, R. Turner, and P. Mansﬁeld, Science 254 (5028), 43 (1991).
[99] M. Hohwy, H. J. Jakobsen, M. Eden, M. H. Levitt, and N. C. Nielsen, J. Chem. Phys.
108, 2686 (1998).
[100] M. P. Augustine and K. W. Zilm, J. Mag. Reson. Ser. A. 123, 145 (1996).
[101] M. K.T., B. Sun, G. Chinga, J. Zwanziger, T. Terao, and A. Pines, J. Mag. Reson.
86 (3), 470 (1990).
[102] R. H. Havlin, T. Mazur, W. B. Blanton, and A. Pines, (2002), in preparation.
[103] U. Haeberlen, High Resolution NMR in Solids: Selective Averaging (Academic Press,
New York, 1976).
[104] M. Mehring, Principles of High Resolution NMR in Solids (Springer, Berlin, 1983).
[105] M. Eden and M. H. Levitta, J. Chem. Phys. 111(4), 1511 (1999).
[106] P. Tekely, P. Palmas, and D. Canet, J. Mag. Reson. A 107(2), 129 (1994).
BIBLIOGRAPHY 221
[107] M. Ernst, S. Bush, A. Kolbert, and A. Pines, J. Chem. Phys. 105 (9), 3387 (1996).
[108] A. E. Bennett, C. M. Rienstra, M. Auger, K. V. Lakshmi, and R. G. Griﬃn, J. Chem.
Phys. 103 (16), 6951 (1995).
[109] A. Bielecki, A. C. Kolbert, and M. H. Levitt, Chem. Phys. Lett. 155(45), 341 (1989).
[110] M. Carravetta, M. Eden, X. Zhao, A. Brinkmann, and M. H. Levitt, Chem. Phys.
Lett. 321, 205 (2000).
[111] X. Zhao, M. Eden, and M. Levitt, Chem. Phys. Lett. 234, 353 (2001).
[112] A. Brinkmann, M. Eden, and M. H. Levitt, J. Chem. Phys. 112(19), 8539 (2000).
[113] J. Walls, W. B. Blanton, R. H. Havlin, and A. Pines, Chem. Phys. Lett. 363 (34),
372 (2002).
[114] R. Tycko and G. Dabbagh, Chem. Phys. Lett. 173, 461 (1990).
[115] Y. K. Lee, N. D. Kurur, M. Helmle, O. G. Johannessen, N. C. Nielsen, and M. H.
Levitt, Chem. Phys. Lett. 242(3), 304 (1995).
[116] W. Sommer, J. Gottwald, D. Demco, and H. Spiess, J. Magn. Reson. A 113(1), 131
(1995).
[117] C. M. Rienstra, M. E. Hatcher, L. J. Mueller, B. Q. Sun, S. W. Fesik, and R. G.
Griﬃn, J. Am. Chem. Soc. 120(41), 10602 (1998).
[118] M. Hohwy, C. M. Rienstra, and R. G. Griﬃn, J. Chem. Phys. 117(10), 4973 (2002).
[119] M. Hohwy, C. M. Rienstra, C. P. Jaroniec, and R. G. Griﬃn, J. Chem. Phys. 110(16),
7983 (1999).
BIBLIOGRAPHY 222
[120] A. Brinkmann and M. H. Levitt, J. Chem. Phys. 115(1), 357 (2001).
[121] A. Brinkmann, J. S. auf der Gnne, and M. H. Levitt, J. Mag. Reson. 156(1), 79
(2002).
[122] M. Hohwy, C. P. Jaroniec, B. Reif, C. M. Rienstra, and G. R. G., J. Am. Chem. Soc.
122(13), 3218 (2000).
[123] B. Reif, M. Hohwy, C. P. Jaroniec, C. M. Rienstra, and R. G. Griﬃn, J. Mag. Reson.
145, 132 (2000).
[124] M. H. Levitt, K. A. C., A. Bielecki, and D. J. Ruben, Solid State Nucl. Magn. Reson.
2(4), 151 (1993).
[125] Y. Yu and B. M. Fung, J. Mag. Reson. 130, 317 (1998).
[126] A. J. Shaka, J. Keeler, T. Frenkiel, and R. Freeman, J. Mag. Reson. 52(2), 335 (1983).
[127] A. J. Shaka, J. Keeler, and R. Freeman, J. Mag. Reson. 53, 313 (1983).
[128] A. J. Shaka and J. Keeler, Prog. NMR Spectrosc. 19, 47 (1987).
[129] M. H. Levitt, R. Freeman, and T. Frenkiel, Adv. Mag. Reson. 11, 47 (1983).
[130] M. H. Levitt, R. Freeman, and T. Frenkiel, J. Mag. Reson. 50(1), 157 (1982).
[131] M. H. Levitt, R. Freeman, and T. Frenkiel, J. Mag. Reson. 47(2), 328 (1982).
[132] M. H. Levitt and R. Freeman, J. Mag. Reson. 43(3), 502 (1981).
[133] W. S. Warren, J. B. Murdoch, and A. Pines, J. Mag. Reson. 60(2), 236 (1984).
[134] J. Murdoch, W. S. Warren, D. P. Weitekamp, and A. Pines, J. Mag. Reson. 60(2),
205 (1984).
BIBLIOGRAPHY 223
[135] A. Bennett, Ph.D. thesis, Massachusetts Institute of Technology, Massachusetts In
stitute of Technology, 1995.
[136] A. Bennett, C. Rienstra, J. Griﬃths, W. Zhen, P. Lansbury, and R. Griﬃn, J. Chem.
Phys. 108(22), 9463 (1998).
[137] D. B. Fogel, in Evolutionary Computation. The Fossil Record. Selected Readings on
the History of Evolutionary Computation (IEEE Press, Philadelphia, 1998), Chap. 16:
Classiﬁer Systems, this is a reprint of (Holland and Reitman, 1978), with an added
introduction by Fogel.
[138] W. M. Spears, K. A. D. Jong, T. B¨ack, D. B. Fogel, and H. de Garis, in Proceedings of
the European Conference on Machine Learning (ECML93), Vol. 667 of LNAI, edited
by P. B. Brazdil (Springer Verlag, Vienna, Austria, 1993), pp. 442–459.
[139] M. D. Vose, Evolutionary Computation 3, 453 (1996).
[140] M. D. Vose, in Foundations of Genetic Algorithms 2, edited by L. D. Whitley (Morgan
Kaufmann, San Mateo, CA, 1993), pp. 63–73.
[141] M. D. Vose, The simple genetic algorithm: foundations and theory (MIT Press, Cam
bridge, MA, 1999).
[142] in Evolutionary Programming – Proceedings of the Third International Conference,
edited by A. V. Sebald and L. J. Fogel (World Scientiﬁc Publishing, River Edge, NJ,
1994).
[143] in Proceedings of the 1995 IEEE International Conference on Evolutionary Compu
tation, edited by ???? (IEEE Press, Piscataway, 1995), Vol. 1.
BIBLIOGRAPHY 224
[144] R. Storn and K. Price, Technical report, International Computer Science Institute,UC
Berkeley (unpublished).
[145] D. B. Fogel, in Evolutionary Algorithms, edited by L. D. Davis, K. De Jong, M. D.
Vose, and L. D. Whitley (Springer, New York, 1999), pp. 89–109.
[146] D. Deugo and F. Oppacher, in Artiﬁcial Neural Nets and Genetic Algorithms, edited
by R. F. Albrecht, N. C. Steele, and C. R. Reeves (Springer Verlag, Wien, 1993), pp.
400–407.
[147] D. Sakellariou, A. Lesage, P. Hodgkinson, and L. Emsley, Chem. Phys. Lett. 319, 253
(200).
[148] C. M. Bishop, Neural Networks for Pattern Recognition (Clarendon Press, Oxford,
1995).
[149] A. Blum, Neural networks in C++ (Wiley & Sons, New York, 1994).
[150] T. Masters, Practical Neural Network Recipes in C++ (Academic Press,, Boston,
1996).
[151] E. Barnard, IEEE Transactions on Neural Networks 3(2), 232 (1992).
[152] A. L. Blum and P. Langley, Arti. Intel. 97, 245 (1997).
[153] X. Yao, International Journal of Intelligent Systems 8, 539 (1993).
225
Appendix A
Auxillary code
The code presented here are all dependant on the BlochLib library and tool kit.
As a result you will probably need it to compile this code. You can get it here
http://waugh.cchem.berkeley.edu/blochlib/ (and if it is not there I hope to maintain a copy
here http://theaddedones.com/ and perhaps http://sourceforge.net/). The code examples
here are relatively short and should be easily typed in by hand.
A.1 General C++ code and examples
A.1.1 C++ Template code used to generate prime number at compilation
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
// Program by Erwin Unruh
// Compil e wi t h : g++ −c prime . cc [ & grep conversi on
// The ’ grep ’ command pi c k s out onl y t he er r or s we want t o see
// namely t hos e wi t h t he prime numbers
// Cl ass t o cr eat e ” out put ” at compi l e ti me ( er r or messages )
// g i v e s er r or on D=i nt
template <i nt i , i nt prim> struct D ¦ ¦ ;
//no er r or on D=i nt
template <i nt i > struct D<i , 0 > ¦ D( i nt ) ; ¦ ;
// Cl ass t o compute prime condi t i on
template <i nt p , i nt i > struct i s pr i me ¦
enum ¦ prim = ( ( p%i ) && i s pr i me < ( i >2 ? p : 0 ) , i −1>:: prim ) ¦ ;
¦;
// s p e c i f i c i ns t anc e s t o s t op
template<> struct i s pr i me <0,1> ¦ enum ¦ prim = 1 ¦ ; ¦ ;
template<> struct i s pr i me <0,0> ¦ enum ¦ prim = 1 ¦ ; ¦ ;
A.1. GENERAL C++ CODE AND EXAMPLES 226
// Cl ass t o i t e r a t e t hrough a l l val ues : 2 . . i
template <i nt i > struct Pr i me pr i nt ¦
Pri me pri nt <i −1> a ; // cascade from i t o 2
enum ¦ prim = i s pr i me <i , i −1>:: prim ¦ ;
// wi l l produce an er r or i f ’ prim’==1
// ( i f we have a prime number )
void f ( ) ¦ a . f ( ) ; D<i , prim> d = prim ; ¦
¦;
// s p e c i f i c i ns t ance t o s t op at i =2
template<> struct Pri me pri nt <2> ¦
enum ¦ prim = 1¦;
void f ( ) ¦ D<2, prim> d = prim ; ¦
¦;
void f oo ( ) ¦
Pri me pri nt <25> a ;
a . f ( ) ;
¦
/∗∗∗∗∗∗ expect ed out put from ’ Pri me pri nt <25> a ; a . f ( ) ; ’
prime . cc : 3 0 : convers i on from ‘ Pri me pri nt <2>::¦anonymous enum¦ ’
t o non−s c al ar t ype ‘D<2,1>’ r eques t ed
prime . cc : 2 5 : convers i on from ‘ Pri me pri nt <3>::¦anonymous enum¦ ’
t o non−s c al ar t ype ‘D<3,1>’ r eques t ed
prime . cc : 2 5 : convers i on from ‘ Pri me pri nt <5>::¦anonymous enum¦ ’
t o non−s c al ar t ype ‘D<5,1>’ r eques t ed
prime . cc : 2 5 : convers i on from ‘ Pri me pri nt <7>::¦anonymous enum¦ ’
t o non−s c al ar t ype ‘D<7,1>’ r eques t ed
prime . cc : 2 5 : convers i on from ‘ Pri me pri nt <11>::¦anonymous enum¦ ’
t o non−s c al ar t ype ‘D<11,1>’ r eques t ed
prime . cc : 2 5 : convers i on from ‘ Pri me pri nt <13>::¦anonymous enum¦ ’
t o non−s c al ar t ype ‘D<13,1>’ r eques t ed
prime . cc : 2 5 : convers i on from ‘ Pri me pri nt <17>::¦anonymous enum¦ ’
t o non−s c al ar t ype ‘D<17,1>’ r eques t ed
prime . cc : 2 5 : convers i on from ‘ Pri me pri nt <19>::¦anonymous enum¦ ’
t o non−s c al ar t ype ‘D<19,1>’ r eques t ed
prime . cc : 2 5 : convers i on from ‘ Pri me pri nt <23>::¦anonymous enum¦ ’
t o non−s c al ar t ype ‘D<23,1>’ r eques t ed
∗/
A.1.2 C++ Template metaprogram to unroll a ﬁxed length vector at
compilation time
// Thi s meta program a p l i e s t o a f i x e d l e ng t h vect or
// where t he t empl at e arguments f or t h i s vect or
// woul d be T=t he dat a t ypes , and N t he vect or l e ng t h
// we wi l l c a l l t h i s vect or a ‘ coord<T, N>’ t o d i s t i ng u i s h
// bet ween t he gener al vect or case .
//
// t h i s i s onl y a code pi eces , i t wi l l not work unl e s s
A.1. GENERAL C++ CODE AND EXAMPLES 227
// one has def i ned a v a l i d coord , and t he coordExpr c l a s s e s
//Here i s t he = operat or t hat pas s es
// t he expr es s i on To t he ’ coordAssi gn ’ meta program
template<cl ass T, i nt N>
template<cl ass Expr T>
coord<T, N> &coord<T, N>: : operator=(const coordExpr<Expr T> &rhs )
¦
coordAssi gn<N, 0 >: : as s i gn (∗ this , rhs , ApAssign ) ;
return ∗ thi s ;
¦
// Thi s i s a ‘ ApAssign ’ c l a s s f or
// a dat a t ype ‘T’
template<cl ass T>
cl ass ApAssign¦
public :
ApAssign ( ) ¦¦
stati c i nl i ne void appl y (T &a , T &b)
¦ a=b ; ¦
¦;
// a ’ qui ck ’ meta program ( one t he compi l er perf orms )
// t o unr ol l l oops compl et el y . . . t h i s i s t he ’ ent ry ’ poi nt ,
// bel ow a s p e c i f i c i ns t ance (N=0, I =0) i s expr es s ed
// t o s t op t he t empl at e casscade
template<i nt N, i nt I>
cl ass coordAssi gn ¦
public :
// t h i s i s t e l l s us when t o s t op t he casscade
enum ¦ l oopFl ag = ( I < N−1 ) ? 1 : 0 ¦ ;
template<cl ass CoordType , cl ass Expr , cl ass Op>
stati c i nl i ne void as s i gn ( CoordType& vec , Expr expr , Op u)
¦
// as s i gn t he two el ement s
u . appl y ( vec [ I ] , expr ( I ) ) ;
//move on t he t he next i ns t ance ( I +1)
coordAssi gn<N ∗ l oopFl ag ,
( I +1) ∗ l oopFl ag >: : as s i gn ( vec , expr , u ) ;
¦
¦;
// t he c l a s s t o ’ k i l l ’ or s t op t he above one . .
// we get here we s t op t he t empl at e unr ol l i ng
template<>
cl ass coordAssi gn <0,0> ¦
public :
template<cl ass VecType , cl ass Expr , cl ass Op>
stati c i nl i ne void as s i gn ( VecType& vec , Expr expr , Op u)
A.1. GENERAL C++ CODE AND EXAMPLES 228
¦ ¦
¦;
A.1.3 C++ code for performing a matrix multiplication with L2 cache
blocking and partial loop unrolling.
template<cl ass T>
void mulmatUnrool ( matri x<T> &c , matri x<T> &a , matri x<T> &b)
¦
i nt i , j , k , l e f t o v e r ;
stati c i nt Unr ol l s =5;
// f i g ur e out how many do not f i t i n t he unr ol l i ng
l e f t o v e r=a . rows ( ) % ( Unr ol l s ) ;
for ( k=0; k<b . rows ();++k)¦
for ( j =0; j <b . c o l s ();++ j )¦
i =0;
//do t he el ement s t hat do not f i t i n t he unrol l ment
for ( ; i <l e f t o v e r ;++i )
¦ c ( i , j )+=a ( i , k ) ∗ b( k , j ) ; ¦
//do t he r e s t
for ( ; i <a . rows ( ) ; i+=Unr ol l s )¦
// avoi d c a l c u l a t i ng t he i ndexes t wi ce
i nt i 1=i +1 , i 2=i +2 , i 3=i +3 , i 4=i +4;
// avoi d readi ng t he b ( k , j ) more t hen once
typename matri x<T>: : numtype tmpBkj=b( k , j ) ;
// read t he a( i , k ) ’ s f i r s t i nt o t he r e g i s t e r s
typename matri x<T>: : numtype tmpAij=a ( i , k ) ;
typename matri x<T>: : numtype tmpAi1j=a ( i 1 , k ) ;
typename matri x<T>: : numtype tmpAi2j=a ( i 2 , k ) ;
typename matri x<T>: : numtype tmpAi3j=a ( i 3 , k ) ;
typename matri x<T>: : numtype tmpAi4j=a ( i 4 , k ) ;
c ( i , j )+=tmpAij ∗ tmpBkj ;
c ( i 1 , j )+=tmpAi1j ∗ tmpBkj ;
c ( i 2 , j )+=tmpAi2j ∗ tmpBkj ;
c ( i 3 , j )+=tmpAi3j ∗ tmpBkj ;
c ( i 4 , j )+=tmpAi4j ∗ tmpBkj ;
¦
¦
¦
¦
/∗ L2 b l oc k i ng ∗∗∗ ∗/
i nt L2rowMAX=140;
i nt L2colMAX=140;
A.1. GENERAL C++ CODE AND EXAMPLES 229
//makes t he sub mat ri x el ement s i nt o t he
// proper pl ace from t he o r i g i na l
template<cl ass T>
void makeSubMatrixFrom(
matri x<T> &out ,
matri x<T> &Orig ,
i nt beR , // begi ni ng row i ndex
i nt enR , // endi ng row i ndex
i nt beC , // begi ni ng column i ndex
i nt enC) // endi ng column i ndex
¦
out . r e s i z e ( enR−beR , enC−beC) ;
for ( i nt i=beR , ctR=0; i <enR;++i , ++ctR)¦
for ( i nt j=beC , ctC=0; j <enC;++j , ++ctC)¦
out ( ctR , ctC)=Ori g ( i , j ) ;
¦
¦
¦
// put s t he sub mat ri x el ement s i nt o t he
// proper pl ace i n t he o r i g i na l
template<cl ass T>
void putSubMatrixTo (
matri x<T> &i n ,
matri x<T> &Orig ,
i nt beR , // begi ni ng row i ndex
i nt enR , // endi ng row i ndex
i nt beC , // begi ni ng column i ndex
i nt enC) // endi ng column i ndex
¦
for ( i nt i=beR , ctR=0; i <enR;++i , ++ctR)¦
for ( i nt j=beC , ctC=0; j <enC;++j , ++ctC)¦
Ori g ( i , j )+=i n ( ctR , ctC ) ;
¦
¦
¦
template<cl ass T>
void L2BlockMatMul ( matri x<T> &C, matri x<T> &A, matri x<T>& B)
¦
// r e s i z e our ret urn mat ri x t o t he proper s i z e
C. r e s i z e (A. rows ( ) , B. c o l s ( ) ) ;
C=0;
//no need t o do t h i s i f mat ri x i s l e s s t hen L2 s i z e
i f (A. rows ()<L2rowMAX && B. c o l s () < L2colMAX)
¦ mulmatLUnrool (C, A, B) ; return ; ¦
// t he number of d i v i s i o ns al ong rows and c ol s
i nt rDi v=(i nt ) c e i l ( double(A. rows ( ) ) / double(L2rowMAX) ) ;
i nt cDiv=(i nt ) c e i l ( double(B. c o l s ( ) ) / double(L2colMAX) ) ;
i nt BDiv=(i nt ) c e i l ( double(B. rows ( ) ) / double(L2colMAX) ) ;
i nt i , j , k ;
A.1. GENERAL C++ CODE AND EXAMPLES 230
//now do C( i , j )=Sum k ( a( i , k )∗ b ( k , j ) )
for ( i =0; i <rDi v;++i )¦
// t he cur rent begi nni ng Row i ndex f or out mat ri x
i nt beCr=i ∗L2rowMAX;
// t he cur rent endi ng Row i ndex f or out mat ri x
i nt enCr=( i +1)∗L2rowMAX;
i f ( enCr>A. rows ( ) ) enCr=A. rows ( ) ;
for ( j =0; j <cDiv;++j )¦
// t he cur rent begi nni ng Column i ndex f or out mat ri x
i nt beCc=j ∗L2colMAX;
// t he cur rent endi ng Row i ndex f or out mat ri x
i nt enCc=( j +1)∗L2colMAX;
i f ( enCc>B. c o l s ( ) ) enCc=B. c o l s ( ) ;
// sub out put mat ri x f or out mat ri x
matri x<T> Ci j ( enCr−beCr , enCc−beCc ) ;
// zero out t he mat ri x
Ci j =0;
//now l oop t hrough t he B Row d i v i s i o ns
for ( k=0; k<BDiv;++k)¦
// t h i s val ue i s begi nni ng f or t he col umns
// of A and t he rows of B
i nt beAB=k∗L2colMAX;
// t h i s val ue i s f or end t he col umns
// of A and t he rows of B
i nt enAB=(k+1)∗L2colMAX;
i f (enAB>B. c o l s ( ) ) enAB=B. rows ( ) ;
// sub A and B mat ri ces
matri x<T> Aik ;
makeSubMatrixFrom( Aik , A, beCr , enCr , beAB, enAB) ;
matri x<T> Bkj ;
makeSubMatrixFrom( Bkj , B, beAB, enAB, beCc , enCc ) ;
//perf orm t he mul t i pl y on t he subs
// not ei ng t hat t he el ement s i n Ci j wi l l be
// added t o ( not ov er wr i t t e n )
mulmatLUnrool ( Ci j , Aik , Bkj ) ;
¦
// put t he sub C mat ri x back i nt o t he o r i g i na l
putSubMatrixTo ( Ci j , C, beCr , enCr , beCc , enCc ) ;
¦
¦
¦
A.1.4 An MPI master/slave implimentation framework
#include ” bl o c hl i b . h”
A.1. GENERAL C++ CODE AND EXAMPLES 231
//need t o use t he proper namespaces
using namespace Bl ochLi b ;
using namespace std ;
// de f i ne out f unct i on we wi sh t o run i n p a r a l l e l
void MyFunction( i nt kk)¦
cout<<endl <<” I was c a l l e d on : ”<<MPIworld . rank ( )
<<” wi th val ue : ”<<kk<<endl ;
s l e e p ( MPIworld . rank () −1);
¦
i nt main( i nt argc , char ∗ argv [ ] )
¦
// St ar t up t he Master c o nt r o l l e r
MPIworld . s t a r t ( argc , argv ) ;
//dump out i nf o about what and where we are
std : : cout<<MPIworld . name()<<” : : ”<<MPIworld . rank ( )
<<”/”<<MPIworld . s i z e ()<<std : : endl <<endl ;
// t h i s i nt g e t s s ent went t he Master has s ent
// e v e r yt hi ng ( t he k i l l s wi t ch )
i nt done =−1;
i nt cur =0; // t he cur rent val ue
// i f we are t he master , we need t o i n i t i a l i z e some t hi ng s
i f ( MPIworld . master ( ) ) ¦
// t he el ement s i n here wi l l be s ent t o t he s l av e procs
i nt Max=10; // onl y want t o send 10 t hi ng s
i nt CT=0 , r r =−1;
//we must perf orm an i n i t i a l send t o a l l t he proc
//from 1 . . s i z e , i f s i z e >Max we need t o send no more
for ( i nt qq=1; qq<MPIworld . s i z e ();++qq)¦
MPIworld . put (CT, qq); ++CT;
i f (CT>Max) break ;
¦
i nt get ;
//now we get an I nt e g e r from ANY pr oces s or t hat i s NOT
// t he master . . . and keep put t i ng val ues u nt i l we run out
while (CT<Max)¦
// get an i nt ( ’ get ’=t he proc i s came from)
get=MPIworld . getAny ( r r ) ;
MPIworld . put (CT, get ) ; // put t he next val ue
++CT; //advance
¦
// put t he ’We−Are−Done ’ f l a g t o a l l t he procs once we f i n i s h
for ( i nt qq=1; qq<MPIworld . s i z e ();++qq)
A.1. GENERAL C++ CODE AND EXAMPLES 232
MPIworld . put ( done , qq ) ;
¦ el se ¦ // s l av e procs
// keep l oopi ng u nt i l we t he master t e l l s us t o q ui t
while ( 1) ¦
MPIworld . get ( cur , 0 ) ;
i f ( cur==done ) break ; // i d we get t he k i l l s wi t ch get out
MyFunction( cur ) ; //run out f unct i on wi t h t he got t en val ue
MPIworld . put ( cur , 0 ) ; //send back a r e q ue s t f or more
¦
¦
// e x i t MPI and l e av e t he prog
MPIworld . end ( ) ;
return 0 ;
¦
A.1.5 C++ class for a 1 hidden layer Fully
connected back–propagation Neural Network
/∗
A si mpl e 1 hi dden l aye r Back propgat i on
f u l l y connect ed Feed Foward neural Net
∗/
#include ” bl o c hl i b . h”
using namespace Bl ochLi b ;
template<cl ass Num T>
cl ass si gmoi d¦
public :
Num T operator ( ) ( i nt i , Num T &i n )¦ return si gmoi d ( i n ) ; ¦
i nl i ne stati c Num T sigm(Num T num) // The si gmoi d f unct i on .
¦ return ( 1. /( 1. + exp(−num) ) ) ; ¦
¦;
template<cl ass Num T> //Num T i s t he out put / i nput dat a t ype
cl ass BackPropNN ¦
private :
// Weights f or t he neurons i nput −−hi dden
r mat r i xs I Hwei ghts ;
// Weights f or t he neurons hi dden−−>out put
r mat r i xs HOweights ;
Vector<f l oat > I Hbi as ; // t he in−−hi dden b i as e s
Vector<f l oat > HObias ; // t he hi dden−−out b i as e s
Vector<Num T> hl aye r ; // t he hi dden l aye r ’ val ues ’
Vector<Num T> outTry ; // t he at t empt ed out put s
Vector<Num T> out Er r or ; // t he ouput−−>hi dden er r or s
A.1. GENERAL C++ CODE AND EXAMPLES 233
Vector<Num T> hi ddenEr r or ; // t he hi dden−−>i nput er r or s
f l oat l r a t e ;
public :
BackPropNN( ) ;
BackPropNN( i nt numin , i nt numH, i nt numout ) ;
˜BackPropNN( ) ¦ ¦ ;
// r e s i z e t he i ns and out s
void r e s i z e ( i nt numin , i nt numH, i nt numout ) ;
// r e s e t t he wei ght s t o random
void r e s e t ( ) ;
i nl i ne f l oat l ear ni ngRat e ( )
¦ return l r a t e ; ¦ // g e t t he l e ar i ng r at e
void l ear ni ngRat e ( f l oat l r )
¦ l r a t e =l r ; ¦ // s e t t he l e ar i ng r at e
void f owardPass ( Vector<Num T> &i n ) ;
void backPass ( Vector<Num T> &i nput , Vector<Num T> &t ar ge t ) ;
Vector<Num T> run ( Vector<Num T> &i nput ) ;
Vector<Num T> t r ai n ( Vector<Num T> &i nput , Vector<Num T> &t ar ge t ) ;
r mat r i xs IHwei ghts ( ) ¦ return I Hwei ghts ; ¦
r mat r i xs HOweights ( ) ¦ return HOweights ; ¦
//dumps a mat l ab f i l e t hat
// p l o t s t he neurons wi t h l i ne s bet ween
// them based on t he wei ght
void pr i nt ( st d : : s t r i ng fname ) ;
f l oat e r r or ( Vector<Num T> &t ar ge t ) ;
¦;
template<cl ass Num T>
BackPropNN<Num T>: : BackPropNN( i nt numin , i nt numout , i nt numH=0 )
¦
l r a t e =0. 5;
r e s i z e ( numin , numout , numH) ;
¦
template<cl ass Num T>
void BackPropNN<Num T>: : r e s i z e ( i nt numin , i nt numout , i nt numH=0)
¦
RunTimeAssert ( numin>=1);
A.1. GENERAL C++ CODE AND EXAMPLES 234
RunTimeAssert ( numout>=1);
i f (numH==0) numH=numin ;
RunTimeAssert (numH>=1);
// t he wei ght s i z e i s ( numin+1)x ( numin+1)
// t he ’ +1 ’ f or t he b i as e nt r i e s
I Hwei ghts . r e s i z e ( numin , numH) ;
HOweights . r e s i z e (numH, numout ) ;
I Hbi as . r e s i z e (numH, 0 ) ;
HObias . r e s i z e ( numout , 0 ) ;
hl aye r . r e s i z e (numH, 0 ) ;
outTry . r e s i z e ( numout , 0 ) ;
out Er r or . r e s i z e ( numout , 0 ) ;
hi ddenEr r or . r e s i z e (numH, 0 ) ;
r e s e t ( ) ;
¦
template<cl ass Num T>
void BackPropNN<Num T>: : r e s e t ( )
¦
Random<UniformRandom<f l oat > > myR( −1 , 1) ;
HOweights . appl y (myR) ;
I Hwei ghts . appl y (myR) ;
I Hbi as . appl y (myR) ;
HObias . appl y (myR) ;
hl aye r . f i l l ( 0 . 0 ) ;
outTry . f i l l ( 0 . 0 ) ;
¦
// t h i s does t he f oward propogat i on . . .
template<cl ass Num T>
void BackPropNN<Num T>: : f owardPass ( Vector<Num T> &i n )
¦
regi ster i nt i , j ;
regi ster Num T tmp=0;
// i nput −−> hi dden
for ( i =0; i <I Hwei ghts . c o l s ();++ i )¦
for ( j =0; j <i n . s i z e ();++ j )¦
tmp+=i n ( j )∗ I Hwei ghts ( j , i ) ;
¦
hl aye r [ i ]=si gmoi d<Num T>: : sigm(tmp+I Hbi as ( i ) ) ;
tmp=0;
¦
// hi dden −−> out put
for ( i =0; i <outTry . s i z e ();++ i )¦
for ( j =0; j <HOweights . rows ();++ j )¦
tmp+=hl aye r ( j )∗ HOweights ( j , i ) ;
¦
outTry [ i ]=si gmoi d<Num T>: : sigm(tmp+HObias ( i ) ) ;
tmp=0;
¦
¦
A.1. GENERAL C++ CODE AND EXAMPLES 235
template<cl ass Num T>
f l oat BackPropNN<Num T>: : e r r or ( Vector<Num T> &t ar ge t )
¦
return norm( t ar get −outTry ) ;
¦
// t h i s does t he backwards propogat i on . . .
template<cl ass Num T>
void BackPropNN<Num T>: : backPass (
Vector<Num T> &i nput ,
Vector<Num T> &t ar ge t )
¦
regi ster i nt i , j ;
regi ster Num T tmp=0;
// er r or f or ouput s
out Er r or =t ar get −outTry ;
// er r or f or hi dden
for ( i =0; i <HOweights . c o l s ();++ i )¦
for ( j =0; j <outTry . s i z e ();++ j )¦
tmp+=out Er r or [ j ] ∗ HOweights ( i , j ) ;
¦
hi ddenEr r or ( i )=f l oat ( hl aye r ( i )∗(1. 0 − hl aye r ( i ) ) ∗tmp ) ;
tmp=0;
¦
// adj us t hi dden−−>out put wei ght s
Num T l en =0;
l en=sum( s qr ( hl aye r ) ) ; // t he mean l e ng t h of t he hi dden
i f ( l en <=0.1) l en =0. 1; //do not reduce t oo much . . .
for ( i =0; i <HOweights . rows ();++ i )¦
for ( j =0; j <outTry . s i z e ();++ j )¦
HOweights ( i , j )+=f l oat ( l r a t e ∗ out Er r or ( j )∗ hl aye r ( i )/ l en ) ;
¦
¦
// adj us t hi dden b i as l e v e l s
for ( i =0; i <HObias . s i z e ();++ i )¦
HObias ( i )+=f l oat ( l r a t e ∗ out Er r or ( i )/ l en ) ;
¦
// adj us t wei ght s from i nput t o hi dden
l en=sum( s qr ( i nput ) ) ;
i f ( l en <=0.1) l en =0. 1; //do not reduce t oo much . . .
for ( i =0; i <i nput . s i z e ();++ i )¦
for ( j =0; j <I Hwei ghts . c o l s ();++ j )¦
I Hwei ghts ( i , j )+=f l oat ( l r a t e ∗ hi ddenEr r or ( j )∗ i nput ( i )/ l en ) ;
¦
¦
// adj us t i nput b i as l e v e l s
for ( i =0; i <I Hwei ghts . c o l s ();++ i )¦
A.1. GENERAL C++ CODE AND EXAMPLES 236
I Hbi as ( i )+=f l oat ( l r a t e ∗ hi ddenEr r or ( i )/ l en ) ;
¦
¦
template<cl ass Num T>
Vector<Num T> BackPropNN<Num T>: :
t r ai n ( Vector<Num T> &i n , Vector<Num T> &out )
¦
f owardPass ( i n ) ;
backPass ( i n , out ) ;
return outTry ;
¦
template<cl ass Num T>
Vector<Num T> BackPropNN<Num T>: :
run ( Vector<Num T> &i n )
¦
f owardPass ( i n ) ;
return outTry ;
¦
// t h i s dumps t he i nf o t o a matab
// s c r i p t so t hat i t can be e a s i l y p l o t t e d
template<cl ass Num T>
void BackPropNN<Num T>: : pr i nt ( std : : s t r i ng fname )
¦
std : : of stream oo ( fname . c s t r ( ) ) ;
i f ( oo . f a i l ( ) ) ¦
std : : cer r <<std : : endl <<”BackPropNN. pr i nt ”<<std : : endl ;
std : : cer r <<”cannot open ouput f i l e ”<<std : : endl ;
return ;
¦
/∗we wi sh t he pi c t ur e t o l ook l i k e
O O
/ ¸ / ¸
O O O
¸ / ¸ /
O O
∗/
// t he ’ dot ’ f or a Neuron
oo<<” f i g ur e ( 153) ; ¸ n”
<<” c l f r e s e t ; ¸ n” ;
//we want each node t o be s pear at ed by
// 5 i n t he on t he x ’ ax i s ’ and 10 on t he yaxi s
// we need t o s c al e t he x ax i s based on t he maxNode
oo<<”i nNodes=”<<I Hwei ghts . rows()<<” ; ¸ n”
<<”hNodes=”<<I Hwei ghts . c o l s ()<<” ; ¸ n”
<<”outNodes=”<<outTry . s i z e ()<<” ; ¸ n”
A.1. GENERAL C++ CODE AND EXAMPLES 237
<<”maxNodes=max( inNodes , max( hNodes , outNodes ) ) ; ¸ n”
<<”ySep=10; ¸n”
<<”xSep=2∗ySep ; ¸ n”
<<”ybSep=2; ¸n”
<<” i nSep=(xSep /( i nNodes +2)); ¸n”
<<”hSep=(xSep /( hNodes +2)); ¸n”
<<”outSep=(xSep /( outNodes +1)); ¸n”
<<”xc =[ −1 , −1 , 1 , 1] ; ¸n”
<<”yc =[ −1 , 1 , 1 , −1] ; ¸n ”
<<” hol d on¸n” ;
// pr i nt out t he wei ght s and b i as e s
oo<<” IHwei ghts =[ ” ;
for ( i nt i =0; i <I Hwei ghts . rows ();++ i )¦
oo<<” [ ” ;
for ( i nt j =0; j <I Hwei ghts . c o l s ();++ j )¦
oo<<I Hwei ghts ( i , j )<<” ” ;
¦
oo<<” ] ¸ n” ;
¦
oo<<” ] ; ¸ n” ;
oo<<”HOweights=[ ” ;
for ( i nt i =0; i <HOweights . rows ();++ i )¦
oo<<” [ ” ;
for ( i nt j =0; j <HOweights . c o l s ();++ j )¦
oo<<HOweights ( i , j )<<” ” ;
¦
oo<<” ] ¸ n” ;
¦
oo<<” ] ; ¸ n”
<<” I Hbi as =[ ”<<I Hbi as <<” ] ; ¸ n”
<<”HObias=[ ”<<HObias <<” ] ; ¸ n”
// f i nd t he max of a l l of them
<<”maxW=max(max( abs ( IHwei ghts ) ) ) ; ¸ n”
<<”maxW=max(maxW, max(max( abs ( HOweights ) ) ) ) ; ¸ n”
<<”maxW=max(maxW, max(max( abs ( I Hbi as ) ) ) ) ; ¸ n”
<<”maxW=max(maxW, max(max( abs ( HObias ) ) ) ) ; ¸ n”
<<”maxWidth=5;¸n”
<<” al t Col or =[ 0 , 0 , 0 . 8 ] ; ¸ n”
<<” posCol or =[ 0 . 8 , 0 , 0] ; ¸ n”
// pr i nt a l i ne f or each one of them . . .
<<”% INput−−>HIdden l i n e s ¸n”
<<” f o r i =1: hNodes ¸n”
<<” f o r j =1: i nNodes ¸n”
<<” c ol or=posCol or ; ¸ n”
<<” i f IHwei ghts ( j , i ) <0 , c ol or=al t Col or ; , end ; ¸ n”
<<” l i =l i n e ( [ j ∗ i nSep i ∗hSep ] , [ 2 ∗ ySep ySep ] , ”
<<” ’ Col or ’ , col or , ’ LineWidth ’ , ”
A.1. GENERAL C++ CODE AND EXAMPLES 238
<<” maxWidth∗abs ( IHwei ghts ( j , i ) ) /maxW) ; ¸ n”
<<” end¸n”
<<”end¸n”
<<”% Hidden−−>out l i n e s ¸n”
<<” f o r i =1: hNodes ¸n”
<<” f o r j =1: outNodes ¸n”
<<” c ol or=posCol or ; ¸ n”
<<” i f HOweights ( i , j ) <0 , c ol or=al t Col or ; , end ; ¸ n”
<<” l i =l i n e ( [ i ∗hSep j ∗outSep ] , [ ySep 0 ] , ”
<<” ’ Col or ’ , col or , ’ LineWidth ’ , ”
<<” maxWidth∗abs ( HOweights ( i , j ) ) /maxW) ; ¸ n”
<<” end¸n”
<<”end¸n”
<<”% i nput Bi as−−>Hidden l i n e s ¸n”
<<” j=i nNodes +1;¸n”
<<” f o r i =1: hNodes ¸n”
<<” c ol or=posCol or ; ¸ n”
<<” i f I Hbi as ( i ) <0 , c ol or=al t Col or ; , end ; ¸ n”
<<” l i =l i n e ( [ j ∗ i nSep i ∗hSep ] , [ 2 ∗ ySep−ybSep ySep ] , ”
<<” ’ Col or ’ , col or , ’ LineWidth ’ , ”
<<” maxWidth∗abs ( I Hbi as ( i ) ) /maxW) ; ¸ n”
<<”end¸n”
<<”% Hidden Bi as−−>output l i n e s ¸n”
<<” j=hNodes +1;¸n”
<<” f o r i =1: outNodes ¸n”
<<” c ol or=posCol or ; ¸ n”
<<” i f HObias ( i ) <0 , c ol or=al t Col or ; , end ; ¸ n”
<<” l i =l i n e ( [ j ∗hSep i ∗outSep ] , [ ySep−ybSep 0 ] , ”
<<” ’ Col or ’ , col or , ’ LineWidth ’ , ”
<<” maxWidth∗abs ( HObias ( i ) ) /maxW) ; ¸ n”
<<”end¸n” ;
oo<<” f o r i =1: i nNodes ¸n”
<<” f i l l ( xc/i nNodes+i ∗i nSep , yc/i nNodes+2∗ySep , ’ r ’ ) ; ¸ n”
<<”end¸n”
<<”%bi as I−−>H node¸n”
<<” f i l l ( xc/i nNodes+(i nNodes +1)∗i nSep , ”
<<” yc/i nNodes+2∗ySep−ybSep , ’ g ’ ) ; ¸ n”
<<”¸n”
<<” f o r i =1: hNodes¸n”
<<” f i l l ( xc/hNodes+i ∗hSep , yc/hNodes+ySep , ’ k ’ ) ; ¸ n”
<<”end¸n”
<<”%bi as H−−>O node¸n”
<<” f i l l ( xc/hNodes+(hNodes+1)∗hSep , yc/hNodes+ySep−ybSep , ’ g ’ ) ; ¸ n”
<<”¸n”
<<” f o r i =1: outNodes ¸n”
<<” f i l l ( xc/outNodes+i ∗outSep , yc/outNodes , ’ b ’ ) ; ¸ n”
<<”end¸n”
<<” daspect ( [ 1 1 1 ] ) ; ¸ n”
<<” axi s t i ght ; ¸ n”
A.2. NMR ALGORITHMS 239
<<” hol d o f f ¸n”
<<”¸n” ;
¦
A.2 NMR algorithms
A.2.1 Mathematica Package to generate Wigner Rotation matrices and
Spin operators.
This small and simple Mathematica package (a .m ﬁle) allows the creation of the
basic Cartesian spin operators and Wigner rotation matrices of a given spin space of spin
I. To use the package, simple call MakeSpace[Spin] where Spin is the total spin (i.e. 1/2,
1, 3/2, etc). It will make Iz, Ix, Iy, Ipp and Imm as global matrices. To generate the
Wigner rotation matrix call Wigner[Spin] where Spin is the same as the MakeSpace value.
(∗ s pi nt en .m∗)
(∗ In t h i s package we t r y t o cr eat a l l t he nes s es ar y b i t s
f or gener at i ng e v e r yt hi ng we coul d p o s s i b l y want t o
do wi t h s pi n t ens or s and r ot at i ons ∗)
Unprotect [ Ix , Iy , Iz , Ipp , Imm, rank , created , WignerExpIy , D12 , d12 ]
Clear [ Ix , Iy , Iz , Ipp , Imm, rank , created , WignerExpIy , D12 , d12 ]
BeginPackage [ ” spi nten ‘ ” ]
Unprotect [ MakeSpace , MakeIz , MakeIplus , MakeIx , MakeIy , MakeImin ,
Wigner , Di rect , MultWig , MakeSpinSys , MakeExpIz ]
Clear [ MakeSpace , MakeIz , MakeIplus , MakeIx , MakeIy , MakeImin ,
Wigner , Di rect , MultWig , MakeSpinSys , MakeExpIz ]
(∗ Usages ∗)
Wigner : : usage=
”Wigner [ L, m, mp, ¦ al pha , beta , gamma¦] ge ne r at e s a wi gner
¸n r ot at i on el ement
¸n <mp, L[ Exp[−I I z al pha ] Exp[−I Iy beta ] ∗
Exp[−I I z gamma ] [ m, L>.
¸n Other po s s i bl e s i nc l ude :
¸n Wigner [ L] −−> For an e nt i r e matri x
¸n Wigner [ L, ¦ al pha , beta , gamma¦] −−> matri x us i ng de f aul t
’ al pha , beta , gamma’
¸n Wigner [ L, mp, m] −−> us i ng de f aul t
¸n ’ al pha , beta , gamma’ symbol s ” ;
Wigner : : errmb=”m’ s i n ’ Wigner ’ i s bi gger then L . . Bad , bad , person” ;
Wigner : : errms=”m’ s i n ’ Wigner ’ i s s mal l e r then −L . . Bad , bad , person” ;
A.2. NMR ALGORITHMS 240
MultWig : : usage=
”MultWig [ ¦ L1 , L2¦ , ¦J , M3p, M3¦] Wi l l gi ve the Dj (m3p, m3) wi gner
¸n el ements from two Other Wigner Mat ri ces ! ! ” ;
MultWig : : errm=”You J or M3p or M3 i s too bi g f o r L1+L2” ;
MakeSpace : : usage=
”MakeSpace [ L] t hi s f unc t i on ge ne r at e s a l l the mat r i ces f o r s pi n=L
¸n systems . The output si mpl y c r e at e s d e f i n i t i o n s f o r Iz , Ix ,
¸n and Iy Which can then be c a l l e d up as Ix , Iy , I z .
¸n I f you have def i ned them pr e vi ous l y t hi s wi l l r e de f i ne them” ;
MakeSpace : : e r r = ”you have ent er ed i n a val ue f o r L that i s not
¸n and i nt e r g e r or ha l f an i nt e r g e r ” ;
MakeIz : : usage =”MakeIz [ L] Generates I z i n space of rank L” ;
MakeIx : : usage=”MakeIx [ L] Generates Ix i n space of rank L” ;
MakeIy : : usage=”MakeIy [ L] Generates Iy i n space of rank L” ;
MakeIpl us : : usage =”MakeIpl us [ L] Generates I + i n space of rank L” ;
MakeImin : : usage =”MakeImin [ L] Generates I − i n space of rank L” ;
MakeExpIz : : usage=”MakeExpIz [ a , L] A f a s t e r way of doi ng exp [ a I z ] ” ;
Di r ect : : usage =
” Di r ect [ m, p ] the c r e at e s a di r e c t product of
two mat r i ces ¦m and p¦” ;
MakeSpinSys : : usage=
”MakeSpinSys [ ¦ L1 , L2 , L3 ¦] t hi s f unc t i on ge ne r at e s a l l the
¸n mat r i ces f o r s pi n=L systems .
¸n The output si mpl y c r e at e s A LIST f o r Iz , Ix ,
¸n and Iy Which can then be c a l l e d up as Ix , Iy , I z .
¸n I f you have def i ned them pr e vi ous l y t hi s wi l l r e de f i ne them” ;
Begin[ ” ‘ Pri vate ‘ ” ]
(∗ here we de f i ne t he ’ base / de f aul t ’ paul i mat ri ces
( t hey are f or s pi n 1/2 ) ∗)
Gl obal ‘ I z =1/2¦¦1 , 0¦ , ¦0 , −1¦¦
Gl obal ‘ Ix =1/2¦¦0 , 1¦ , ¦1 , 0¦¦
Gl obal ‘ Iy =1/2¦¦0, −I ¦ , ¦ I , 0¦¦
Gl obal ‘ Ipp =¦¦0 , 1¦ , ¦0 , 0¦¦
Gl obal ‘ Imm=¦¦0 , 0¦ , ¦1 , 0¦¦
Gl obal ‘ numspin=1;
Gl obal ‘ rank=1/2
Gl obal ‘ d12=MatrixExp[−I Gl obal ‘ ¸ [ Beta ] ¸
Gl obal ‘ Iy ] // ExpToTrig//Simplify
Gl obal ‘ D12=(MatrixExp[−I Gl obal ‘ ¸ [ Alpha ] Gl obal ‘ I z ] .
Gl obal ‘ d12 .
MatrixExp[−I Gl obal ‘ ¸ [Gamma] Gl obal ‘ I z ] ) // Simplify
A.2. NMR ALGORITHMS 241
(∗ t h i s f l a g t e l l s me t hat i have al r eady cr eat ed made t he mat ri x
Exp[−I bet a Iy ] . Thi s can be a l ar g e l ar ge ,
e s p e c i a l l y s y mb ol i c al l y and need onl y be done
once ( of course unl es s i change my L) ∗)
Gl obal ‘ cr eat edwi g =0;
(∗ here i s a f unct i on t hat does a d i r e c t
product bet ween two mat ri ces ∗)
Di r ect [ m , p ] : =
Module[ ¦dimM=Dimensions [m] [ [ 1 ] ] , dimP=Dimensions [ p ] [ [ 1 ] ] , f r oo ¦ ,
I f [ dimM==0[[dimP==0,
Print [ ”Bad person , you gave me a ’NULL’ f o r a matri x” ] ] ;
f r oo=Table [ 0 , ¦ i , 1 , dimP∗dimM¦ , ¦ j , 1 , dimP∗dimM¦ ] ;
Table [ Table [
f r oo [ [ i +( l ∗dimP) , j +(k∗dimP) ] ] =m[ [ l +1, k +1] ] ∗ p [ [ i , j ] ] , ¸
¦ i , 1 , dimP¦ , ¦ j , ¸
1 , dimP¦ , ¦ l , 0 , dimM−1¦,¦k , 0 , dimM−1 ¦ ] ] ] ;
(∗ here i s a f unct i on t hat c r e at e s t he Iz , I pl us ,
and Imin mat ri x f or a Rank L s pi n space by doi ng
usi ng t hes e si mpl e i d e n t i t i e s
I z [ L,m>=m[ L,m>
I +/−[L,m>=Sqrt [ L(L+1)−m(m+/ −1)] [L, m+/−1>
∗)
MakeIz [ L ] : =
Table [
Table [
I f [ mp==m, m, 0 ] ,
¦mp, L, −L, −1¦ , ¦m, L, −L, −1¦] ]
MakeIpl us [ L ] : =
Table [
Table [
I f [ mp==(m+1) , Sqrt [ L(L+1)−m(m+1) ] , 0] ,
¦mp, L, −L, −1¦ , ¦m, L, −L, −1¦] ]
MakeImin [ L ] : =
Table [
Table [
I f [ mp==m−1 , Sqrt [ L(L+1)−m(m−1) ] , 0] ,
¦mp, L,−L, −1¦ , ¦m, L, −L, −1¦] ]
MakeIx [ L ] : =1/2( MakeIpl us [ L]+MakeImin [ L] )
MakeIy [ L ] :=−I 1/2( MakeIpl us [ L]−MakeImin [ L] )
MakeSpace [ L ] : = Module[ ¦¦ ,
I f [Mod[ L, 0 . 5 ] ! =0 , Message [ MakeSpace : : e r r ] ; ,
I f [ Gl obal ‘ rank!=L,
A.2. NMR ALGORITHMS 242
Gl obal ‘ rank=L;
Gl obal ‘ I z=MakeIz [ L ] ;
Gl obal ‘ Ipp=MakeIpl us [ L ] ;
Gl obal ‘ Imm=MakeImin [ L ] ;
Gl obal ‘ Ix =1/2( Gl obal ‘ Ipp+Gl obal ‘ Imm) ;
Gl obal ‘ Iy=−I 1/2( Gl obal ‘ Ipp−Gl obal ‘ Imm) ;
Gl obal ‘ cr eat edwi g =0 ; ] ] ]
MakeExpIz [ a , L ] : =
Table [
Table [
I f [ mp==m, Exp[m a ] , 0 ] ,
¦mp, L, −L, −1¦ , ¦m, L, −L, −1¦] ]
Wigner [ L , mp , m , ¦ al pha , beta , gamma ¦] : =
Module[ ¦ l =1/2¦,
I f [ mp>L, Message [ Wigner : : errmb ] ,
I f [ mp<−L, Message [ Wigner : : errms ] ,
I f [ m>L, Message [ Wigner : : errmb ] ,
I f [ m<−L, Message [ Wigner : : errms ] ] ] ] ] ;
tmp=Gl obal ‘ d12 ;
While [ l <L−1/2,
tmp=MultWig [ ¦tmp , Gl obal ‘ d12 ¦ , l +1/2] ;
l=l +1/2;
] ;
I f [ L==0 , 1 ,
I f [ L==1/2, Gl obal ‘ D12 ,
Exp[−I mp Gl obal ‘ ¸ [ Alpha ] ] ∗Exp[−I m Gl obal ‘ ¸ [Gamma] ] ∗
MultWig [ ¦tmp , Gl obal ‘ d12 ¦ , ¦L, mp, m¦ ] ] ]
]
Wigner [ L , mp , m ] : = Wigner [ L, mp, m, ¦ al pha , beta , gamma¦]
Wigner [ L , ¦ al pha , beta , gamma ¦] : =
Module[ ¦ l , tmp¦ ,
l =1/2;
I f [ mp>L, Message [ Wigner : : errmb ] ,
I f [ mp<−L, Message [ Wigner : : errms ] ,
I f [ m>L, Message [ Wigner : : errmb ] ,
I f [ m<−L, Message [ Wigner : : errms ] ] ] ] ] ;
tmp=Gl obal ‘ d12 ;
While [ l <L−1/2,
tmp=MultWig [ ¦ Gl obal ‘ d12 , tmp¦ , l +1/2] ;
l=l +1/2;
] ;
MakeExpIz[−I Gl obal ‘ ¸ [ Alpha ] , L ] .
I f [ L==0 , 1 ,
I f [ L==1/2, Gl obal ‘ d12 ,
MultWig [ ¦ Gl obal ‘ d12 , tmp¦ , L ] ] ] .
MakeExpIz[−I Gl obal ‘ ¸ [Gamma] , L]
]
A.2. NMR ALGORITHMS 243
Wigner [ L ] : =Wigner [ L, ¦ al pha , beta , gamma¦]
MultWig [ ¦ L1 ?MatrixQ, L2 ?MatrixQ¦ , ¦ J , m3p , m3 ¦ , ¦ a , b , g ¦] : =
Module[ ¦
l 1 =(Dimensions [ L1 ] [ [ 1 ] ] −1 ) / 2 ,
l 2 =(Dimensions [ L2 ] [ [ 1 ] ] −1 ) / 2 ¦ ,
I f [ J>l 1+l 2 , Message [ MultWig : : errm ] ,
(Sum[
Sum[ I f [ m3−m1>l 2 [ [ m3−m1<−l 2 [ [ m3p−m1p>l 2 [ [ m3p−m1p<−l 2 , 0 ,
ClebschGordan[ ¦ l 1 , m1¦ , ¦ l 2 , m3−m1¦ , ¦J , m3¦] ∗
ClebschGordan[ ¦ l 1 , m1p¦ , ¦ l 2 , m3p−m1p¦ , ¦J , m3p¦] ∗
L1 [ [ l 1−m1p+1 ] ] [ [ l 1−m1+1] ] ∗
L2 [ [ l 2 −(m3p−m1p) +1 ] ] [ [ l 2 −(m3−m1) +1 ] ] ] ,
¦m1, l 1 , −l 1 , −1¦] ,
¦m1p, l 1 ,−l 1 , −1¦] ) // Simplify ]
]
MultWig [ ¦ L1 ?MatrixQ, L2 ?MatrixQ¦ , ¦ J , m3p , m3 ¦] : =
MultWig [ ¦ L1 , L2 ¦ , ¦ J , m3p, m3¦ , ¦ a , b , g ¦]
MultWig [ ¦ L1 , L2 ¦ , J ] : =
Table [ MultWig [ ¦ L1 , L2¦ , ¦J , i , j ¦ ] , ¦ i , J, −J , −1¦ , ¦ j , J, −J , −1¦]
MultWig [ ¦ L1 , L2 ¦ , J , ¦ a , b , g ¦] : =
Table [ MultWig [ ¦ L1 , L2¦ , ¦J , i , j ¦ , ¦ a , b , g ¦ ] , ¸
¦ i , J, −J , −1¦ , ¦ j , J, −J , −1¦]
MakeSpinSys [ s p i n s i z e s ] : =
Module[ ¦ i ¦ ,
I f [ Length[ s pi n s i z e s ]==1 , MakeSpace [ s p i ns i z e s [ [ 1 ] ] ] ,
I f [ Length[ s pi n s i z e s ]==0 , MakeSpace [ s p i ns i z e s ] ,
Gl obal ‘ Ix=Table [ MakeIx [ s p i n s i z e s [ [ i ] ] ] , ¸
¦ i , 1 , Length[ s pi n s i z e s ] ¦ ] ;
Gl obal ‘ Iy=Table [ MakeIy [ s p i n s i z e s [ [ i ] ] ] , ¸
¦ i , 1 , Length[ s pi n s i z e s ] ¦ ] ;
Gl obal ‘ I z=Table [ MakeIz [ s p i n s i z e s [ [ i ] ] ] , ¸
¦ i , 1 , Length[ s pi n s i z e s ] ¦ ] ;
Gl obal ‘ Ipp=Table [ MakeIpl us [ s p i n s i z e s [ [ i ] ] ] , ¸
¦ i , 1 , Length[ s pi n s i z e s ] ¦ ] ;
Gl obal ‘ Imm=Table [ MakeImin [ s p i n s i z e s [ [ i ] ] ] , ¸
¦ i , 1 , Length[ s pi n s i z e s ] ¦ ] ;
Gl obal ‘ numspin=Length[ s pi n s i z e s ] ;
] ] ; ]
(∗T=Functi on [ ¦L, m, s pi ns ys ¦ ,
Module [ ¦ tmpi x ¦ ,
MakeSpinSys [ s pi ns ys ] ;
∗)
End[ ]
Protect [ Wigner , MakeSpace , MakeIz , MakeIplus , MakeImin ,
A.2. NMR ALGORITHMS 244
MakeIx , MakeIy , Di rect , MultWig , MakeSpinSys , MakeExpIz ]
EndPackage [ ]
A.2.2 Rational Reduction C++ Class
This includes both the C++ header ﬁle, the C++ source ﬁle and an example usage
ﬁle.
The header ﬁle
#i f ndef Prop Reduce h
#define Prop Reduce h 1
#include ” bl o c hl i b . h”
/∗ ∗∗∗ Thi s c l a s s s houl d be used as f o l l o ws . . .
PropReduce myred( base , f act , l og ) ;
myred . reduce ( ) ;
f or ( i nt i =0; i <base;++i )¦
< generat e t he i nd and fow props . . . >
¦
f or ( i nt i =0; i <myred . maxBackReduce();++ i )¦
< generat e t he back props . . . >
¦
Vector<matri x > myred . generat eProps ( ind , fow , bac ) ;
∗∗∗/
/∗ ∗∗ The Rat i onal Reducti on of Propogat ors ∗∗ ∗/
using namespace Bl ochLi b ;
cl ass PropReduce¦
private :
Vector<Vector<int > > BackRed ;
Vector<int > BackName ;
Vector<Vector<int > > FowardRed ;
Vector<int > FowardName ;
Vector<Vector<int > > Speci al Red ;
Vector<int > Speci al Name ;
Vector<Vector<int > > dat ;
i nt Mults , UseMe , baseTag , speTag ;
A.2. NMR ALGORITHMS 245
bool i t e r a t i o n ( Vector<Vector<int > > &dat ,
Vector<Vector<int > > &propRed ,
Vector<Vector<int > > &subN,
Vector<int > &name ) ;
public :
i nt base , f a c t o r ;
std : : ostream ∗ l o g f ;
// c ons t r uc t or s
PropReduce ( ) ¦¦
PropReduce ( i nt bas , i nt f ac t or , std : : ostream ∗ oo =0);
void setParams ( i nt bas , i nt f act , std : : ostream ∗ oo =0);
void fowardReduce ( ) ;
void backReduce ( ) ;
void s peci al Reduce ( ) ;
void reduce ( ) ;
i nl i ne i nt be s t Mul t i pl i c a t i o ns ( ) ¦ return Mults ; ¦
i nl i ne i nt maxBackReduce ( ) const ¦ return UseMe+1; ¦
i nl i ne i nt maxFowardReduce ( ) const ¦ return FowardRed . s i z e ( ) ; ¦
// t hes e f unc t i ons wi l l cr eat e t he propogat ors
// from 3 i nput mat ri x l i s t s . . t he f i r s t are t he
// i nd i v i d ua l propogat ors ( ”0” , ”1” , ”2”. . . )
// t he second t he ’ Foward ’ props ( ”0∗1” , ”0∗1∗2” . . . )
// t he t hi r d the , ’ Back ’ props ( ”7∗8” , ”6∗7∗8”. . . )
// t he f or t h i s t he pl ace t o f i l l . . .
void generateProps ( Vector<matri x> &i ndi v ,
Vector<matri x> &Foward ,
Vector<matri x> &Back ,
Vector<matri x> &Fi l l Me ) ;
¦;
#endif
The source ﬁle
#include ” bl o c hl i b . h”
#include ” propreduce . h”
using namespace Bl ochLi b ;
using namespace std ;
PropReduce : : PropReduce ( i nt bas , i nt f ac , ostream ∗ oo )
¦ setParams ( bas , f ac , oo ) ; UseMe=0; Mults =0;¦
A.2. NMR ALGORITHMS 246
void PropReduce : : setParams ( i nt bas , i nt f ac , ostream ∗ oo )
¦
// f i nd t he g r e a t e s t common d i v i s o r . . .
i nt u = abs ( bas ) ;
i nt v = abs ( f ac ) ;
i nt q , t ;
while ( v)¦
q = i nt ( f l o o r ( double( u)/double( v ) ) ) ;
t = u − v∗q ;
u = v ;
v = t ;
¦
base=bas /u ;
f a c t o r=f ac /u ;
l o g f=oo ;
dat . r e s i z e ( base , Vector<int >( f a c t o r ) ) ;
FowardName . r e s i z e ( base −1);
FowardRed . r e s i z e ( base −1);
BackName . r e s i z e ( base −1);
BackRed . r e s i z e ( base −1);
i nt ct =0 , ct 2 =0;
for ( i nt i =0; i <base ∗ f a c t o r ;++i )¦
dat [ ct ] [ ct 2 ]=i%base ;
++ct 2 ;
i f ( ct2>=f a c t o r )¦ ++ct ; ct 2 =0; ¦
¦
baseTag=100∗Bl ochLi b : : max( base , f a c t o r ) ;
for ( i nt i =1; i <base;++i )¦
FowardName [ i −1]=i ∗baseTag ;
FowardRed [ i −1] . r e s i z e ( i +1);
for ( i nt j =0; j<=i ;++j ) FowardRed [ i −1] [ j ]=j ;
i f ( l o g f )
∗ l ogf <<”Foward r educt i on : ”<<FowardName [ i −1]<<”=”
<<FowardRed [ i −1]<<std : : endl ;
BackName [ i −1]=−i ∗baseTag ;
BackRed [ i −1] . r e s i z e ( i +1);
for ( i nt j=base−i −1 , k=0; j <base;++j ,++k)¦ BackRed [ i −1] [ k]=j ; ¦
i f ( l o g f )
∗ l ogf <<”Back r educt i on : ”<<BackName [ i −1]<<”=”
<<BackRed [ i −1]<<std : : endl ;
¦
// t h i s i s a s p e c i a l one whi ch si mpl y t ri ms t he t he 0 and base −1
A.2. NMR ALGORITHMS 247
// f ac t or and can be cal ced by U( 0) ’ ∗U( t r )∗U( base −1) ’
Speci al Red . r e s i z e ( 1 , Vector<int >(base −2));
speTag=20000∗Bl ochLi b : : max( base , f a c t o r ) ;
Speci al Name . r e s i z e ( 1 , speTag ) ;
for ( i nt i =1; i <base −1;++i )¦
Speci al Red [ 0 ] [ i −1]=i ;
¦
i f ( l o g f )
∗ l ogf <<” Spe c i al r educt i on : ”<<Speci al Red[0] <<”=”
<<Speci al Name [0] <<std : : endl <<std : : endl ;
¦
bool PropReduce : : i t e r a t i o n ( Vector<Vector<int > > &dat ,
Vector<Vector<int > > &propRed ,
Vector<Vector<int > > &subN,
Vector<int > &name)
¦
// l oops t o f i nd t he matches
bool gotanyTot=f al se ;
for ( i nt i =0; i <dat . s i z e ();++ i )¦
bool gotany=f al se ;
Vector<int > curU ;
for ( i nt M=0;M<dat [ i ] . s i z e ();++M)¦
bool got=f al se ;
i nt p=0;
for ( p=subN. s i z e () −1; p>=0;−−p)¦
i f ( subN[ p ] . s i z e ()+M<=dat [ i ] . s i z e ( ) ) ¦
i f ( subN[ p]==dat [ i ] ( Range (M,M+subN[ p ] . s i z e () −1)))¦
got=true ;
break ;
¦
¦
¦
i f ( got )¦
for ( i nt k=0; k<M;++k)¦
curU . push back ( dat [ i ] [ k ] ) ;
¦
curU . push back ( name [ p ] ) ;
for ( i nt k=subN[ p ] . s i z e ()+M; k<dat [ i ] . s i z e ();++k)¦
curU . push back ( dat [ i ] [ k ] ) ;
¦
propRed [ i ]=curU ;
gotany=true ;
break ;
¦
¦
i f ( ! gotany )¦
for ( i nt k=0; k<dat [ i ] . s i z e ();++k)¦
curU . push back ( dat [ i ] [ k ] ) ;
¦
A.2. NMR ALGORITHMS 248
propRed [ i ] =( curU ) ;
¦ el se ¦
gotanyTot=true ;
¦
¦
return gotanyTot ;
¦
/∗ ∗∗ f oward r educt i ons . . . ∗ ∗ ∗/
void PropReduce : : fowardReduce ( )
¦
Vector<Vector<int > > propRed( base , Vector<int >( 0) ) ;
while ( i t e r a t i o n ( dat , propRed , FowardRed , FowardName ) ) ¦
dat=propRed ;
¦
i nt mul ti =0;
for ( i nt i =0; i <dat . s i z e ();++ i )¦
i f ( l o g f ) ∗ l ogf <<”Sequence ”<<i <<” : ”<<dat [ i ]<<endl ;
mul ti+=dat [ i ] . s i z e ( ) ;
¦
i f ( l o g f )
∗ l ogf <<” Af t er Foward Reducti on . . . Number of mul t i pl i c a t i o ns : ”
<<mul ti <<endl <<endl ;
¦
/∗∗∗Back Reduct i ons ∗∗ ∗/
// t he back r educt i ons we do not e get f or f r e e
// ( l i k e t he f orward ones whci h we have t o c al c
// from t he exp (H) oper at i on ) , so t he number
// of back r educt i ons used depends on t he t o t a l mul t i p l i c a t i o n
// s avei ngs . . . so we need t o go t hrough t he e nt i r e l oops of
// back r educt i ons . . .
void PropReduce : : backReduce ( )
¦
Vector<Vector<int > > propRed( base , Vector<int >( 0) ) ;
Vector<Vector<int > > holdDat ( dat . s i z e ( ) ) ;
for ( i nt i =0; i <dat . s i z e ();++ i ) holdDat [ i ]=dat [ i ] ;
Mults =1000000;
i nt mul ti =0;
UseMe=0;
Vector<Vector<int > > curBack ;
Vector<int > curName ;
for ( i nt k=0; k<BackRed . s i z e ();++k)¦
i f ( l o g f ) ∗ l ogf <<” Number of ’ Back Reducti ons ’ : ”<<k<<endl ;
curBack=BackRed( Range ( 0 , k ) ) ;
curName=BackName( Range ( 0 , k ) ) ;
for ( i nt i =0; i <dat . s i z e ();++ i ) dat [ i ]=holdDat [ i ] ;
A.2. NMR ALGORITHMS 249
while ( i t e r a t i o n ( dat , propRed , curBack , curName ) ) ¦
dat=propRed ;
mul ti=curBack . s i z e ( ) ;
for ( i nt j =0; j <dat . s i z e ();++ j )¦
mul ti+=dat [ j ] . s i z e ( ) ;
¦
i f ( Mults>mul ti )¦
UseMe=k ;
Mults=mul ti ;
¦
¦
for ( i nt j =0; j <dat . s i z e ();++ j )¦
i f ( l o g f )
∗ l ogf <<”Sequence ”<<j <<” : ”<<dat [ j ]<<std : : endl ;
¦
i f ( l o g f )
∗ l ogf <<” Af t er Back Reducti on . . . Number of mul t i pl i c a t i o ns : ”
<<mul ti <<std : : endl <<std : : endl ;
¦
//need t o ’ regen ’ t he b e s t one f or di s pl ay i ng
i f ( l o g f ) ∗ l ogf <<” Number of ’ Back Reducti ons ’ : ”<<UseMe<<std : : endl ;
curBack=BackRed( Range ( 0 , UseMe ) ) ;
curName=BackName( Range ( 0 , UseMe ) ) ;
for ( i nt i =0; i <dat . s i z e ();++ i ) dat [ i ]=holdDat [ i ] ;
Vector<int > BackNeedToGen ;
while ( i t e r a t i o n ( dat , propRed , curBack , curName ) ) ¦
dat=propRed ;
¦
¦
/∗ ∗∗ Spe c i al Reduct i ons ∗∗ ∗/
void PropReduce : : s peci al Reduce ( )
¦
Vector<Vector<int > > propRed( base , Vector<int >( 0) ) ;
while ( i t e r a t i o n ( dat , propRed , Speci al Red , Speci al Name ) ) ¦
dat=propRed ;
¦
Vector<Vector<int > > curBack=BackRed( Range ( 0 , UseMe ) ) ;
i nt mul ti=curBack . s i z e ( ) ;
for ( i nt i =0; i <dat . s i z e ();++ i )¦
i f ( l o g f ) ∗ l ogf <<”Sequence ”<<i <<” : ”<<dat [ i ]<<std : : endl ;
mul ti+=dat [ i ] . s i z e ( ) ;
¦
i nt t t t=Mults−mul ti ; // s avi ngs f or ’ s p e c i a l s ’
Mults−=t t t ;
i f ( l o g f )
∗ l ogf <<” Af t er Spe c i al Reducti on . . . Number of mul t i pl i c a t i o ns : ”
A.2. NMR ALGORITHMS 250
<<Mults<<std : : endl <<std : : endl ;
¦
void PropReduce : : reduce ( )
¦
fowardReduce ( ) ;
backReduce ( ) ;
s peci al Reduce ( ) ;
i f ( l o g f )¦
∗ l ogf <<endl <<” The Best Reducti on i s f o r us i ng ”
<<UseMe+1<<” Back Reducti ons ”<<std : : endl ;
∗ l ogf <<” For a grand t o t a l of ”<<Mults
<<” mul t i pi c at i ons ”<<std : : endl ;
∗ l ogf <<” The t o t a l Sequence . . . . ”<<std : : endl ;
¦
for ( i nt j =0; j <dat . s i z e ();++ j )¦
i f ( l o g f ) ∗ l ogf <<”Sequence ”<<j <<” : ”<<dat [ j ]<<std : : endl ;
¦
¦
// t hes e f unc t i ons wi l l cr eat e t he propogat ors
// from 3 i nput mat ri x l i s t s . . t he f i r s t are t he
// i nd i v i d ua l propogat ors ( ”0” , ”1” , ”2”. . . )
// t he second t he ’ Foward ’ props ( ”0∗1” , ”0∗1∗2” . . . )
// t he t hi r d the , ’ Back ’ props ( ”7∗8” , ”6∗7∗8”. . . )
// t he f or t h i s t he pl ace t o f i l l . . .
void PropReduce : :
generateProps ( Vector<matri x> &i ndi v ,
Vector<matri x> &Foward ,
Vector<matri x> &Back ,
Vector<matri x> &Fi l l Me )
¦
i f ( i ndi v . s i z e ( ) ! = base )¦
std : : cer r <<”PropReduce : : generateProps ( ) ”<<endl ;
std : : cer r <<” I ndi vi dual Mat r i c i e s must have l engt h ’ base ’ ”<<endl ;
e xi t ( 1 ) ;
¦
i f ( Foward . s i z e ( ) ! = base −1)¦
std : : cer r <<”PropReduce : : generateProps ( ) ”<<endl ;
std : : cer r <<” Foward Mat r i c i e s must have l engt h ’ base −1’ ”<<endl ;
e xi t ( 1 ) ;
¦
i f ( Fi l l Me . s i z e ( ) ! = base )¦
std : : cer r <<”PropReduce : : generateProps ( ) ”<<endl ;
std : : cer r <<” Fi l l Me Mat r i c i e s must have l engt h ’ base ’ ”<<endl ;
e xi t ( 1 ) ;
¦
i f ( Back . s i z e ( ) ! = UseMe+1)¦
std : : cer r <<”PropReduce : : generateProps ( ) ”<<endl ;
A.2. NMR ALGORITHMS 251
std : : cer r <<” Back Mat r i c i e s must have the proper ”<<endl ;
std : : cer r <<” l engt h from ’ maxBackReduce ( ) ’ ”<<endl ;
e xi t ( 1 ) ;
¦
for ( i nt i =0; i <dat . s i z e ();++ i )¦
for ( i nt j =0; j <dat [ i ] . s i z e ( ) ; j ++)¦
i f ( j ==0)¦
i f ( dat [ i ] [ j ]>=baseTag && dat [ i ] [ j ] ! =speTag )¦
Fi l l Me [ i ]=Foward [ dat [ i ] [ j ] / baseTag −1] ;
¦ el se i f ( dat [ i ] [ j ] <0)¦
Fi l l Me [ i ]=Back[−dat [ i ] [ j ] / baseTag −1] ;
¦ el se i f ( dat [ i ] [ j ]==speTag )¦
Fi l l Me [ i ]=adj oi nt ( i ndi v [ base −1] )∗Foward [ base −2]∗
adj oi nt ( i ndi v [ 0 ] ) ;
¦ el se ¦
Fi l l Me [ i ]=i ndi v [ dat [ i ] [ j ] ] ;
¦
¦ el se ¦
i f ( dat [ i ] [ j ]>=baseTag && dat [ i ] [ j ] ! =speTag )¦
Fi l l Me [ i ]=Foward [ dat [ i ] [ j ] / baseTag −1] ∗ Fi l l Me [ i ] ;
¦ el se i f ( dat [ i ] [ j ] <0)¦
Fi l l Me [ i ]=Back[−dat [ i ] [ j ] / baseTag −1] ∗ Fi l l Me [ i ] ;
¦ el se i f ( dat [ i ] [ j ]==speTag )¦
Fi l l Me [ i ]=adj oi nt ( i ndi v [ base −1] )∗Foward [ base −2]∗
adj oi nt ( i ndi v [ 0 ] ) ∗ Fi l l Me [ i ] ;
¦ el se ¦
Fi l l Me [ i ]=i ndi v [ dat [ i ] [ j ] ] ∗ Fi l l Me [ i ] ;
¦
¦
¦
¦
¦
Example usage
/∗ ∗∗ Sample Usage of t he ’ PropReduce ’ c l a s s ∗∗ ∗/
#include ” bl o c hl i b . h”
#include ” propreduce . h”
using namespace Bl ochLi b ;
using namespace std ;
i nt main( i nt argc , char ∗ argv [ ] ) ¦
i nt base , f a c t o r ;
query parameter ( argc , argv , 1 , ”Enter Base : ” , base ) ;
query parameter ( argc , argv , 2 , ”Enter f a c t o r : ” , f a c t o r ) ;
std : : s t r i ng fname ;
query parameter ( argc , argv , 3 , ”Enter l og f i l e name : ” , fname ) ;
A.2. NMR ALGORITHMS 252
of st ream oo ( fname . c s t r ( ) ) ;
PropReduce myReduce ( base , f ac t or , &oo ) ;
myReduce . reduce ( ) ;
¦
A.2.3 Optimized static Hamiltonian FID propogation
Vector<complex > Stati cFID ( matri x &H, matri x &rho ,
matri x &det ect , i nt npts , double dt )
¦
Vector<complex > f i d ( npts , 0 ) ;
complex z (0. 0 , −dt ∗2. 0∗ Pi ) ; // i ∗2 pi dt
matri x evect ; // e i g e nv e c t or s of H
dmatri x eval ; // e i g e nv al ue s of H
// di ag onal i z e t he Hami l toni an
di ag (H, eval , evect ) ;
const double c ut o f f = 1. 0 e −10;
// put rho i nt o ei genbas e of H
matri x s i g0=adj prop ( evect , rho ) ; // adj oi nt ( evect )∗ ro ∗( evect ) ;
// Put de t e c t i on op . t o ei genbas e of H
matri x Do=adj prop ( evect , dect ec ) ; // adj oi nt ( evect )∗ de ∗( evect ) ;
i nt hs = hami l . rows ( ) ;
i nt l s = hs ∗hs ;
eval=exp ( z∗ eval ) ; //exp[−i 2 pi t Omega]
// s t or age f or t he c o e i f f i c e n t s
complex ∗A=new complex [ l s ] ;
// s t or age f or t he e i g e nval ue d i f f e r e nc e s
complex ∗B=new complex [ l s ] ;
i nt i , j , pos =0;
// c a l c u l a t e them , ommiting anyt hi ng t hat i s ’ 0 ’
for ( i =0; i <hs ; i ++)¦
for ( j =0; j <hs ; j ++)¦
// t he s hor t e r mat ri x t r ace and mat ri x mul t i pi c at i on
// i nt o an Nˆ2 l oop r at her t hen an Nˆ3 l oop
A[ pos ] = Do( i , j )∗ s i g0 ( j , i ) ;
// c a l c u l a t e t he e i g e nv al ue terms from
// t he mul t i p l i c a t i o n
B[ pos ] = eval ( i )∗ conj ( eval ( j ) ) ;
//do not care about t he val ue i f t he c o i e f
// i s bel ow our c u t o f f val ue
i f ( square norm(A[ pos ]) > c ut o f f )¦ pos++;¦
¦
¦
//move npt s ∗ dt i n ti me
for ( i nt k=0; k<npts ( ) ; k++)¦
A.2. NMR ALGORITHMS 253
z = 0 ; //temporary s i g na l
// t h i s i s our reduced
// mat ri x and t r ace mu l t i p l i c a t i o n
for ( i nt p=0; p<pos ; p++)¦
//add a l l t he c o i e f f ∗ f r e q ue nc i e s
z += A[ p ] ;
A[ p] ∗= B[ p ] ;
¦
// as s i gn temp s i g na l t o f i d
f i d ( k)=z ;
¦
delete [ ] A;
delete [ ] B;
return f i d ;
¦
A.2.4 γ −COMPUTE C++ Class
/∗compute . cc
∗ t h i s l i t t l e c l a s s devel ops s t r o p o s c o p i c a l l y obs erved
s pect r a usi ng t he ’COMPUTE’ method gi ven i n
@Art i cl e ¦Eden96 ,
aut hor=”Mat t i as Eden and Young K. Lee and Malcolm H. Le v i t t ” ,
t i t l e =”Ef f i c i e n t Si mul at i on of Per i odi c Probl ems i n NMR.
Appl i cat i on t o Decoupl i ng and Rot at i onal Resonance ” ,
j our nal =”J . Magn . Reson . A. ” ,
vol ume =”120”,
pages =”56−71”,
year =1996
¦
@Art i cl e ¦Hohwy99 ,
aut hor=”Hohwy , M. and Bi l dse , H. and Ni el sen , N. C. ” ,
t i t l e =”Ef f i c i e n t Spe c t r al Si mul at i ons i n ¦NMR¦ of Rot at i ng
Sol i ds . The $¸gamma$−COMPUTE Al gori t hm” ,
j our nal =”J . Magn . Reson . ” ,
vol ume =”136”,
pages =”6−14”,
year =1999
¦
∗ i t c a l c u a l t e s a s i ng l e propogat or f or some modul at i on
∗ peri od , T. I t uses a l l t he l i t t l e comput e st ep used t o c a l c u l a t e
∗ propogat or t o r e c ons t r uc t t he e nt i r e f r equecy range
∗ and t hus a f i d from t he f r e q ue nc i e s
∗
∗ i t al s o c a l c u l a t e s propogat ors vi a a d i r e c t method
∗ i . e . U( t )=Prod( exp(−i dt H( t ) ) )
t he ’ f unc t i o n t ’ c l a s s MUST have a f unct i on c a l l e d
’ hmat ri x Hami l toni an ( doubl e TIME1, doubl e TIME2, doubl e WR) ’
A.2. NMR ALGORITHMS 254
where ’ TIME1=t he begi ni ng of a d e l t a T s t ep
’TIME2=t he END of a d e l t a T s t ep
’WR’= t he Rotor Speed
The Hami l toni an f unct i on Must perf orm t he c or r e c t
r ot at i on under WR, i t i s al s o up t o t he user
t o s e t t he c or r e c t ROTOR ANGLE BEFORE t h i s i s c a l l e d
I t i s des gi ned t o be part of t he BLOCHLIB t o o l k i t
t hus t he ’BEGIN BL NAMESPACE ’ macro and t he
’ odd ’ i nc l ude s .
∗/
#i f ndef compute h
#define compute h 1
#include ” c ont ai ne r /matri x/matri x . h” // Bl oc hl i b f i l e
#include ” c ont ai ne r /Vector /Vector . h” // Bl oc hl i b f i l e
BEGIN BL NAMESPACE
template<cl ass f unc t i on t >
cl ass compute ¦
private :
// s t or age f or U k
stati c Vector<matri x > U k ;
//a poi nt er t o t he hami l t oni an f unct i on c l a s s
f unc t i o n t ∗mf ;
// 1 i f ro==1/2( det+adj oi nt ( det )) , 0= f al s e , 2=not c a l c ul a t e d YET
// c a l c ul a t e d vi a ’ isroSYM ’ bel ow
i nt rosym ;
i nt isroSYM( const matri x &ro , const matri x &det )
¦
i f ( rosym==2)¦
i f ( ro ==0.5∗( det+adj oi nt ( det ) ) ) return 1 ;
el se return 0 ;
¦ el se ¦
return rosym ;
¦
¦
// gi ven a peri od of ‘ 1/ wr ’
// and a de s i r e d sweep wi dt h ‘ sw ’
// t he number ‘ n ’ ( comput e st ep ) d i v i s i o ns of
// t he r ot or c y c l e i s f l o o r ( sw/wr+0.5)
// as i t must be an i nt e g e r
// t hus t he sweep wi dt h may need t o modi f i ed
// t o accomi dat e ‘ n ’
//
// Thi s al s o c a l c u l a t e s t he number of
// ‘ gamma ’ powder angl es we can c a l c u l a t e
A.2. NMR ALGORITHMS 255
// gi ven ‘ n ’ , s houl d we de s i r e and gamma angl es
// computed at a l l , we a l t e r t he ‘ gammaloop ’
// f ac t or t o > 1 t o perf orm t he r eor der i ng
// gamma step needs t o be a mut i pl e of comput e st ep
void CalcComputeStep ( )
¦
i f ( wr ==0.0) return ;
compute step=i nt ( f l o o r ( sw /wr +0. 5) ) ;
i f ( gamma step>=compute step )¦
gammaloop=gamma step/ compute step ;
¦
i f ( gammaloop<1) gammaloop=1;
gamma step=gammaloop∗ compute step ;
// compute ti me =1./( doubl e ( comput e st ep )∗ wr ) ;
sw =double( compute step∗wr ) ;
¦
public :
// t he TOTAL one peri od propogat or
matri x Uf ;
//number of r ot or d i v i s i o ns
i nt compute step ;
// t o t a l number of gamma angl es
// t o c a l c u l a t e
i nt gamma step ;
// t o t a l number of r eor der i ngs of propogat ors t o c a l c u l a t e
// more gamma steps ( gamma step=comput e st ep ∗gammaloop)
i nt gammaloop ;
//sweep wi dt h and r ot or speed i n Hz
double sw , wr ;
// s t a r t ti me and end ti me and s t ep ti me f or
// t he peri od
double tmin ;
double tmax ;
double tau ;
compute ( ) ;
compute ( f unc t i o n t &) ;
compute ( f unc t i o n t &i n , i nt compute stepIn )
compute ( f unc t i o n t & , double wr , double sw , double tmin ,
double tmax ) ;
compute ( f unc t i o n t & , i nt compute stepIn , double tmin ,
double tmax ) ;
˜compute ( ) ¦ mf=NULL; ¦
// f unc t i ons f or i nt e r na l v a r i a b l e s
i nl i ne double wr ( ) const
A.2. NMR ALGORITHMS 256
¦ return wr ; ¦
i nl i ne void setWr ( double i n )
¦ wr =i n ; CalcComputeStep ( ) ; ¦
i nl i ne double sweepWidth ( ) const
¦ return sw ; ¦
i nl i ne void setSweepWidth ( double i n )
¦ sw =i n ; CalcComputeStep ( ) ; ¦
i nl i ne i nt gammaStep ( ) const
¦ return gamma step ; ¦
i nl i ne void setGammaStep( i nt i n )
¦
RunTimeAssert ( i n >=1);
gamma step=i n ;
CalcComputeStep ( ) ;
¦
// c a l c u l a t e s t he U k propogat ors
// gi ven no addi t i onal r eor der i ng
// t o compute t he gamma angl es
void calcUFID( ) ¦ calcUFID ( 1 ) ; ¦
// c a l c u l a t e s t he U k propogat ors
// gi ven t he cur rent gamma angl e i ndex de s i r e d
void calcUFID( i nt gammaon ) ;
//computes t he FID gi ven i n i t i a l and de t e c t i on
// mat ri ces and t he number of propogat or
// poi nt s de s i r e d
Vector<complex > FID( matri x &ro , matri x &det , i nt npts ) ;
¦;
// t h j e s t a t i c l i s t of U k mat ri ces
template<cl ass f unc t i on t >
Vector<typename compute<f unct i on >: : matri x>
compute<f unc t i on t >: : U k ( 1 , matri x ( ) ) ;
// d e f a u l t cons t r uct or
template<cl ass f unc t i on t >
compute<f unc t i on t >: : compute ( )
¦
mf=NULL;
compute step=0;pmax=0;
tmin =0. ; tmax =0. ; tau =0. ;
rosym=2;
gammaloop=1;
gamma step=10;
wr =0;
sw =0;
¦
A.2. NMR ALGORITHMS 257
// c ons t c t or as s i g ns f unct i on poi nt er
template<cl ass f unc t i on t >
compute<f unc t i on t >: : compute ( f unc t i o n t &i n )
¦
mf=&i n ;
tmin =0. ; compute step=0;
tmax =0. ; tau =0. ; pmax=0;
rosym=2;
gammaloop=1;
gamma step=10;
wr =0;
sw =0;
¦
// c ons t c t or as s i g ns f unct i on poi nt er
// and comput e st ep
template<cl ass f unc t i on t >
compute<f unc t i on t >: : compute ( f unc t i o n t &i n , i nt compute stepIn )
¦
mf=&i n ;
compute step=compute stepIn ;
U k . r e s i z e ( compute step +1 , mf−>Fe ( ) ) ;
Uf=mf−>Fe ( ) ;
pmax=compute step+1;
tmin =0. ; tmax =0. ; tau =0. ;
rosym=2;
¦
template<cl ass f unc t i on t >
compute<f unc t i on t , MatrixType T >: : compute (
f unc t i o n t &i n , // f unct i on
double wr , // r ot or speed
double sw , //sweep wi dt h
double tmi ni n , // s t a r t ti me of a peri od
double tmaxin ) //end ti me of a peri od
¦
mf=&i n ;
wr =wr ;
sw =sw;
gammaloop=1;
gamma step=10;
CalcComputeStep ( ) ;
U k . r e s i z e ( compute step +1 , mf−>Fe ( ) ) ;
pmax=compute step+1;
Uf=mf−>Fe ( ) ;
tmin=tmi ni n ;
tmax=tmaxin ;
tau=(tmax−tmin )/ compute step ;
i f ( tau<=0)¦
std : : cer r <<std : : endl <<std : : endl
<<” Error : compute : : compute ( ) ”<<std : : endl ;
std : : cer r <<” your ti me f o r the propgator i s negat i ve ”<<std : : endl ;
std : : cer r <<” . . . an e v i l st ench f i l l s the room . . . ”<<std : : endl ;
A.2. NMR ALGORITHMS 258
BLEXCEPTION( FILE , LINE )
¦
rosym=2;
¦
template<cl ass f unc t i on t >
compute<f unc t i on t >: : compute ( f unc t i o n t &i n , // t he f unct i on
i nt compute stepi n , // i n i t i a l compute s t e ps
double tmi ni n , // begi ni ng ti me of t he peri od
double tmaxin ) // t he end ti me of t he peri od
¦
mf=&i n ;
compute step=compute stepi n ;
U k . r e s i z e ( compute step +1 , mf−>Fe ( ) ) ;
gammaloop=1;
gamma step=10;
Uf=mf−>Fe ( ) ;
tmin=tmi ni n ;
tmax=tmaxin ;
tau=(tmax−tmin )/ compute step ;
i f ( tau<=0)¦
std : : cer r <<std : : endl
<<std : : endl <<” Error : compute : : compute ( ) ”<<std : : endl ;
std : : cer r <<” your ti me f o r the propgator i s negat i ve ”<<std : : endl ;
std : : cer r <<” . . . an e v i l st ench f i l l s the room . . . ”<<std : : endl ;
BLEXCEPTION( FILE , LINE )
¦
rosym=2;
¦
// c a l c u l a t e t he U k propogat ors
template<cl ass f unc t i on t >
void compute<f unc t i on t >: : calcUFID( i nt gammaon)
¦
// t he e f f e c i t v e ’ gamma ’ angl e i s
// perf ormed by ’ s h i f t i n g ’ ti me . . . .
double tadd=PI2∗double(gammaon)/
double( gammaloop∗ compute step )/ wr ;
double t1=tmin+tadd ;
double t2=tmin+tadd+tau ;
stati c matri x hh ;
// l oop t hrough t he compute s t ep d i v i s i o ns
// usi ng t he ’ Hami l toni an ( t1 , t2 , wr ) f unct i on
// r equi r ed i n t he f unc t i o n t
for ( i nt i =0; i <compute step ; i ++)¦
hh=Mexp( mf−>Hami l toni an ( t1 , t2 , wr ) , −complex ( 0 , 1) ∗ tau∗PI2 ) ;
i f ( i ==0)¦ U k [ 0 ] . i de nt i t y ( hh . rows ( ) ) ; ¦
U k [ i +1]=hh∗U k [ i ] ;
t1+=tau ;
t2+=tau ;
A.2. NMR ALGORITHMS 259
¦
// t o t a l peri od propogat or i s t he l a s t s t ep
Uf=(U k [ compute step ] ) ;
¦
// f i d c a l c ul a t i o n
// needs 1) t o l oop t hrough a l l permut at i ons
// of t he gammaloop , t o s h i f t ti me pr oper l y
// 2) use t he propogat ors t o c a l c u l a t e t he FID
template<cl ass f unc t i on t >
Vector<complex>
compute<f unc t i on t >: : FID( matri x &ro , matri x &det , i nt npts )
¦
Vector<complex > f i d ( npts , 0 ) ;
for ( i nt q=0; q<gammaloop ; q++)¦
calcUFID( q ) ;
f i d+=cal cFID ( ro , det , npts ) ;
¦
return f i d ;
¦
template<cl ass f unc t i on t >
Vector<complex>
compute<f unc t i on t >: : cal cFID( matri x &ro , matri x &det , i nt npts )
¦
// zero out a new FID vect or
Vector<complex > f i d ( npts , 0 ) ;
// i s ro and det symmetric?
rosym=isroSYM( ro , det ) ;
matri x evect ; // e i g ne v e c t or s
dmatri x eval ; // e i g e nv al ue s
// c a l c u l a t e t he e f f e c t i v e Hami l toni an
// from t he t o t a l peri od propogat or
di ag ( Uf , eval , evect ) ;
i nt N=Uin . rows ( ) ;
i nt i =0 , j =0 , p=0 , r =0 , s =0;
// vect or of l og ( e i g e nv al ue s ) i n H ef f
Vector<complex > ev (N, 0 ) ;
// t he mat ri x of f r e q ue nc i e s d i f f e r e nc e s
// w rs
matri x wrs (N, N) ;
double tau2PI=tau ;
// c a l c u l a t e t he t r a ns i t i o n mat ri x
complex t ot t=complex ( 0 . , double( compute step )∗ tau2PI ) ;
for ( i =0; i <N; i ++)¦
ev [ i ]=l og ( eval ( i , i ) ) ;
¦
for ( i =0; i <N; i ++)¦
A.2. NMR ALGORITHMS 260
for ( j =0; j <N; j ++)¦
wrs ( i , j )=chop ( ( ev [ i ]−ev [ j ] ) / t ot t , 1 e −10);
¦
¦
// t he gamma−compute al gor i t hm use t he symetry r e l a t i o n
// bet ween t he gamma powder angl es DIVIDED i nt o 2 Pi / comput e st ep
// s e c t i ons . . . t he de t e c t i on operat or
// so f or each gamma angl e we woul d t hi nk t hat we woul d need
// ( comput e st ep ) propogat ors f or each r ot or c y c l e d i v i s i o n
// ( which we s e t t o be equal t o ( comput e st ep ) al s o ) t o
// cori spond t o each d i f f e r e n t gamma angl e . . . we l l not so ,
// becuase we have some ni ce ti me symetry bet ween t he gamma
// angl e and t he ti me eveol ved . So we onl y need t o c a l c u l a t e t he
// propogat ors f or gamma=0. . . from t h i s one i n s e l e c t combi nat i ons
// we can gener at e a l l t he ( comput e st ep ) propogat ors from
// t he gamma=0 ones we s t i l l need t o di v i de our r ot or c y c l e up
// however , al s o i nt o ( comput e st ep ) propogat ors
// f or t he remai ni ng not es i wi l l use t h i s l a b a l i ng convent i on
//
// pQs k −−> t he t ransf ormed de t e c t i on operat or f or t he kt h
// r ot or d i v i s i o n f or a gamma angl e of
// ’ p ’ ∗2 Pi /( comput e st ep )
//
// pRoT −−> t he t ransf ormed de ns i t y mat ri x f or t he f or a gamma
// angl e of ’ p ’ ∗2 Pi /( comput e st ep )
// NOTE: : t he r ot or d i v i s i o n i nf o i s cont ai ned i n t he Qs
//
// pU k −−> t he uni t ar y t r as f or mat i on f or t he i t h r ot or d i v i s i o n
// f or a gamma angl e of ’ p ’ ∗2 Pi /( comput e st ep ) . . .
// t he oper at or s 0 U k were c a l c ul a t e d i n t he f unct i on
// ’ calcUFID( i nt ) ’
//
// t he ’ pt h ’ one of a l l of t hes e i s r e al at e d back t o t he ’ 0 t h ’
// operat or by some s e r i e s of i nt e r na l mu l t i p l i c a t i o ns
//
complex tmp1 ;
stati c Vector<matri x > Qs ;
Qs . r e s i z e ( compute step ) ;
stati c Vector<matri x > RoT;
RoT. r e s i z e ( compute step ) ;
// c a l c u l a t i ng
// t he kt h de ns i t y mat ri x
// (0RoT)ˆd=( evect )ˆd∗(0U k )ˆd∗ro ∗(0U k )∗ evect
// t he kt h de t e c t i on op
// (0 Qs k )ˆd=( evect )ˆd∗(0U k )ˆd∗ro ∗(0U k )∗ evect
//
//IF ro=1/2( det+adj oi nt ( det ) ) t hen
// t he i t h de ns i t y mat ri x i s ( 0RoT)ˆd=0Qs k+(0Qs k )ˆd
//
A.2. NMR ALGORITHMS 261
// t he ’ ˆ d ’ i s a adj oi nt oper at i on
for ( i =0; i <compute step ; i ++)¦
Qs [ i ]=adj prop ( evect , adj prop ( U k [ i +1] , det ) ) ;
i f ( rosym==0) RoT[ i ]=adj prop ( evect , adj prop ( U k [ i +1] , ro ) ) ;
el se RoT[ i ]=Qs [ i ]+adj oi nt (Qs [ i ] ) ;
¦
//The s i g na l i s t hen a ni ce sum over t he t r a ns i t i o n mat ri x
// and t he pQs ’ s and pRos Of course t h i s i s where we
// mani pul at e t he ’ 0 t h ’ oper at or s t o cr eat e t he ’ pt h ’
// and combine them a l l i nt o a ’ f ’ mat ri x whi ch
// cont ai ns t he ampl i t udes
//
// pF k ( r , s )= means t he ’ pt h ’ gamma angl e f or t he
// ’ kt h ’ r ot or d i v i s i o n el ement ( r , s )
//
// pF k ( r , s )= exp [ i m wrs ( r , s ) t o t t ] ∗ Ro[ p%comput e st ep ] ( s , r )
// ∗ Qs [ k+p%comput e st ep ] ( r , s ) exp[−i wrs ( r , s ) j t o t t ]
//
// here m=i nt ( ( k+p)/ comput e st ep )) − i nt ( p/ comput e st ep )
// of course we have many ’ p ’ s e c t i ons ( or gamma angel s )
// t hat c ont r i b ut e t o t he ampl i t ude f ac t or s , and becuase
// t hey are s t r i c t l y ampl i t udes f or s epar at e gamma anl ges , we can
// e a s i l y sum them i nt o a t o t a l ampl i t ude
//
// Fave k ( r , s )=1/ comput e st ep ∗ Sum ( p=0)ˆ(p=n−1) ¦ pF k ( r , s ) ¦
// = 1/( comput e st ep )∗Sum( . . . ) ¦ [ ( p%comput e st ep )R] ( s , r )
// exp ( i [ p−i nt ( p/ comput e st ep )∗
// comput e st ep ] wrs ( r , s ) t au )¦
// ∗0Qs ( k+p%n) ( r , s ) ] exp(−i
// [ j+p−i nt ( ( j+p)/ comput e st ep ))∗
// comput e st ep ] wrs ( r , s ) t au )
stati c Vector<matri x > Fave ;
Fave . r e s i z e ( compute step , mf−>Fz ( ) ) ;
// ampl i t ude c a l c u l a t i ng
i nt i nd1 , i nd2 ;
for ( i =0; i <compute step ; i ++)¦
for ( p=0;p<compute step ; p++)¦
// proper ‘ p ‘ f or Q s e l e c t i o n i ndex
i nd1=( i+p)%( compute step ) ;
i f ( ( i+p)>=compute step )¦
i nd2=p−compute step ;
¦ el se ¦
i nd2=p ;
¦
tmp1=complex (0. , −double( i nd2 )∗ tau2PI ) ;
matri x tmm(N, N) ;
for ( r =0; r<N; r++)¦
A.2. NMR ALGORITHMS 262
for ( s =0; s<N; s++)¦
Fave [ p ] ( r , s)+=Qs [ i nd1 ] ( r , s )∗
RoT[ i ] ( s , r )∗
exp ( tmp1∗wrs ( r , s ) ) ;
¦
¦
¦
¦
complex tmp=complex ( 0 . , ( tau2PI ) ) ; // i ∗ t au
//a l i t t l e comput at i on ti me save . . .
// c a l c u l a t e t he exp ( i ∗ t au∗wrs ) once
wrs=exp (tmp∗wrs ) ;
matri x tmpmm( wrs ) ; //copy w rs
// t he FID at i nt e r v a l s of i ∗ t au i s t hen gi ven by
//
// s ( i ∗ t au)=Sum ( r , s ) ¦ Fave i ( r , s )∗ exp [ i wrs ( r , s ) i ∗ t au ] ¦
// here i s t he j =0 poi nt . . . s aves us a exp c a l c ul a t i o n
for ( i =0; i <N; i ++)¦
for ( j =0; j <N; j ++)¦
f i d [0]+=Fave [ 0 ] ( i , j ) ;
¦
¦
for ( i =1; i <npts ; i ++)¦
// t o s e l e c t t he proper ’ p ’ f or Fave p
p=i%compute step ;
for ( r =0; r<N;++r )¦
for ( s =0; s<N;++s )¦
f i d [ i ]+=Fave [ p ] ( r , s )∗tmpmm( r , s ) ;
//advance ’ ti me ’ exp ( dt ∗ wi j )
tmpmm( r , s )∗=wrs ( r , s ) ;
¦
¦
¦
//need t o normal i ze t he f i d as we have
// added t og e t he r many ‘ sub f i d s ’
// but t he t o t a l s houl d s t i l l be 1
i nt f f = rosym==1?2:1;
f i d∗=double ( 1. / double( compute step/ f f ) ) ;
return f i d ;
¦
/∗ ∗∗ END compute CLASS ∗∗ ∗/
END BL NAMESPACE
#endif
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 263
A.3 BlochLib Conﬁgurations and Sources
A.3.1 Solid conﬁguration ﬁles
1D static and spinning experiments shown in Figure 5.6
# a si mpl e MAS and St a t i c FID c o l l e c t i o n
s pi ns ¦
#t he g l o b a l opt i ons ¦
numspin 2
T 1H 0
T 1H 1
#csa <i so > <del > <et a > <spi n >¦
C 5000 4200 0 0
C −5000 6012 0. 5 1
#j coupl i ng <i so > <spi n1 > <spi n2 >¦
J 400 0 1
¦
parameters ¦
#use a f i l e f ound wi t h t he $Bl ochLi b$ d i s t r i b u t i o n ¦
powder¦
#powder f i l e used f or t he s t a t i c FID
aveType ZCW 3 3722
#powder f i l e used f or t he s pi nni ng FID
#aveType rep2000
¦
#number of 1D f i d poi nt s
npts1D=512
#sweep wi dt h ¦
sw=40000
¦
pul s e s ¦
#s e t t he s pi nni ng
wr=0 #s e t f or NON−s pi nni ng FID
#wr=2000 #s e t f or SPINNING FID
#s e t t he r ot or ¦
r ot or =0 #s e t f or NON−s pi nni ng f i d s
#r ot or=acos (1/ s q r t ( 3) ) ∗ rad2deg #s e t f or SPINNING FID
#s e t t he de t e c t i on mat ri x
de t e ct ( Ip )
#s e t t he i n i t i a l mat ri x
ro ( Ix )
#no pul s e s neces s ary f or ro=I x
#c o l l e c t t he f i d
f i d ( )
s a ve f i dt e xt ( si mpStat ) #save as a t e x t f i l e
¦
postC7 input ﬁle for the pointtopoint FID in Figure 5.7a
#perf orms a poi nt −to−poi nt C7 ( a 1D FID)
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 264
s pi ns ¦
#t he g l o b a l opt i ons
numspin 2
T 1H 0
T 1H 1
D 1500 0 1
¦
parameters ¦
powder¦
aveType zcw
t het aSt ep 233
phi Step 144
¦
#t he i nt e g r at or s t ep s i z e
maxtstep=1e−6
#number of 1D f i d poi nt s
npts1D=512
roeq= I z
de t e ct=I z
¦
pul s e s ¦
#our post −C7 sub pul s e s e c t i on
sub1¦
#pos t C7 pul s e ampl i t ude
amp=7∗wr
ampl i tude (amp)
#phase s t e ppe r s
stph=0
phst =360/7
#pul s e t i mes
t90=1/amp/4
t270=3/amp/4
t360=1/amp
#pos t C7 l oop
l oop ( k=1: 7)
1H: pul s e ( t90 , stph )
1H: pul s e ( t360 , stph+180)
1H: pul s e ( t270 , stph )
stph=stph+phst
end
¦
#a s i ng l e f i d i s cons i der ed poi nt t o poi nt ¦
ptop ( )
#s e t t he s pi nni ng
wr=5000
r ot or=rad2deg∗ acos (1/ sqrt ( 3) )
#can use ’ reuse ’ as t he v a r i a b l e s
# are s e t once i n our s ub s e c t i on
r eus e ( sub1 )
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 265
#c o l l e c t t he f i d ¦
f i d ( )
s a ve f i dt e xt ( simpC7 ) #save t as a t e x t f i l e
¦
postC7 input ﬁle for the 2D FID in Figure 5.7b
# perf orms a ’ r e al ’ experi ment
# f or t he post −C7 ( a s e r i e s of 2D f i d s are c o l l e c t e d )
s pi ns ¦
#t he g l o b a l opt i ons
numspin 2
T 1H 0
T 1H 1
D 1500 0 1
¦
parameters ¦
powder¦
aveType zcw
t het aSt ep 233
phi Step 144
¦
#number of 1D f i d poi nt s
npts1D=512
¦
pul s e s ¦
#our post −C7 sub pul s e s e c t i on
sub1¦
#pos t C7 pul s e ampl i t ude
amp=7∗wr
ampl i tude (amp)
#phase s t e ppe r s
stph=0
phst =360/7
#pul s e t i mes
t90=1/amp/4
t270=3/amp/4
t360=1/amp
#pos t C7 l oop ¦
l oop ( k=1: 7)
1H: pul s e ( t90 , stph )
1H: pul s e ( t360 , stph+180)
1H: pul s e ( t270 , stph )
stph=stph+phst
end
¦
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 266
#number of 2D poi nt s
f i dpt =128
#c o l l e c t i o n a mat ri x of dat a
2D( )
#s e t t he s pi nni ng
wr=5000
#t he b as i c r ot or angl e
r ot or=rad2deg∗ acos (1/ sqrt ( 3 ) ) )
#s e t t he de t e c t i on mat ri x
de t e ct ( Ip )
#r e s e t t he ro back t o t he eq
ro ( I z )
#90 ti me ampl i t udes
amp=150000
t90=1/amp/4
#l oop over t he r ot or s t e ps
l oop (m=0: f i dpt −1)
#may use ’ reuse ’ a l l v a r i a b l e s are s t a t i c i n sub1
# must be r epeat m t i mes t o advance t he de ns i t y mat ri x
# f or each f i d ( t he f i r s t f i d g e t s no c7 )
r eus e ( sub1 , m)
#pul s e t he IZ down t o t he xy pl ane f or de t e c t i on
1H: pul s e ( t90 , 2 7 0 , amp)
#c o l l e c t t he f i d at t he ’ mth ’ pos i t i on
f i d (m)
#r e s e t t he ro back t o t he eq
ro ( I z )
end
s avef i dmat l ab (2 dc7 ) #save t he mat l ab f i l e
¦
¦
A.3.2 Magnetic Field Calculator input ﬁle
The input coil type ‘Dcircle’ is a user registered function, and not part of the
normal please view the source code in the distribution for details. par
MyCoil ¦
s ubc oi l 1 ¦
type hel mhol tz
l oops 25
amps −4
numpts 4000
R 2
length 3
axi s z
¦
s ubc oi l 2 ¦
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 267
type Dc i r c l e
l oops 1
amps 2
numpts 2000
R 2
t het a1 0
t het a2 180
axi s z
ce nt er 0 , −. 6 , 5
¦
s ubc oi l 3 ¦
type Dc i r c l e
l oops 1
amps 2
numpts 2000
R 2
t het a1 0
t het a2 180
axi s z
ce nt er 0 , −. 6 , −5
¦
¦
gr i d ¦
min −1, −1, −1
max 1 , 1 , 1
dim 10 , 10 , 10
¦
params¦
#which magneti c f i e l d s e c t i on t o use
s e c t i on MyCoil
#out put t e x t f i l e name
t ext out shape . bi ot
#out put mat l ab f i l e name
matout f i e l d . mat
¦
A.3.3 Quantum Mechanical Single Pulse Simulations
A.3.4 Example Classical Simulation of the Bulk Susceptibility
This simulation is a replication of the simulation performed by M. Augustine in
Figure 2 of Ref. [100]. It demonstrates the slight oﬀset eﬀect imposed by the magnetization
of one spin on another. Both the C++ source using the BlochLib framework and the
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 268
conﬁguration ﬁle is given. Results from this simulation can be found in Figure 5.11.
C++ source
#include ” bl o c hl i b . h”
// t he r equi r ed 2 namespaces
using namespace Bl ochLi b ;
using namespace std ;
/∗
THis s i mul at es t he e f f e c t of t he Bul k S u s e p t i b i l i t y on
a HETCOR experi ement . . . h o p e f ul l y we s h a l l see s e v e r al echos
i n t he i nd i r e c t di mensi on
a HETOCR i s a 2D experi ement
spi n1 :: 90−−t −−90−−−−−
spi n2:: −−−−−−−90−FID
∗/
ti mer stopwatch ;
void pri ntTi me ( i nt nrounds =1)¦
std : : cout <<std : : endl << ”Time taken : ”
<< (stopwatch ( ) / nrounds ) << ” seconds ” ;
¦
void I nf o ( std : : s t r i ng mess )
¦
cout<<mess<<endl ;
cout . f l us h ( ) ;
¦
i nt main( i nt argc , char ∗ argv [ ] )
¦
std : : s t r i ng f n ;
// t he parameter f i l e
query parameter ( argc , argv , 1 , ”Enter f i l e to par s e : ” , f n ) ;
Parameters ps et ( f n ) ;
// get t he b as i c paramet ers
i nt ns t eps=ps et . getParamI ( ” npts ” ) ;
double t f=ps et . getParamD( ” t f ” ) ;
double inTemp=ps et . getParamD( ” temperature ” ) ;
s t r i ng s pi nt ype1=ps et . getParamS( ” s pi nt ype1 ” ) ;
s t r i ng s pi nt ype2=ps et . getParamS( ” s pi nt ype2 ” ) ;
s t r i ng detsp=ps et . getParamS( ” de t e ct ” ) ; ;
double mol es=ps et . getParamD( ”mol es ” ) ;
std : : s t r i ng f out=ps et . getParamS ( ” f i dout ” ) ;
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 269
coord<int > dims ( ps et . getParamCoordI ( ”dim” ) ) ;
coord<> mins ( ps et . getParamCoordD( ”min” ) ) ;
coord<> maxs ( ps et . getParamCoordD( ”max” ) ) ;
std : : s t r i ng dataou=ps et . getParamS ( ” t r a j e c t o r i e s ” , ”” , f al se ) ;
// Grid Set up
typedef XYZful l TheShape ;
typedef XYZshape<TheShape> TheGrid ;
I nf o ( ” Creati ng gr i d . . . . ” ) ;
Grid<UniformGrid > gg ( mins , maxs , dims ) ;
I nf o ( ” Creati ng i n i t a l shape . . . . ” ) ;
TheShape t e s t e r ;
I nf o ( ” Creati ng t o t a l shape−gr i d . . . . ” ) ;
TheGrid j j ( gg , t e s t e r ) ;
// Li s t Bl ochParameters
typedef Li stBl ochParams<
TheGrid ,
BPopti ons : : Densi ty [ BPopti ons : : Hi ghFi el d ,
double > MyPars ;
i nt nsp=j j . s i z e ( ) ;
I nf o ( ” Creati ng e nt i r e s pi n parameter l i s t f o r ”
+i t o s t ( nsp)+
” s pi ns . . . . ” ) ;
MyPars mypars ( nsp , ”1H” , j j ) ;
nsp=mypars . s i z e ( ) ;
//The pul s e l i s t f or a r e al pul s e on prot ons . .
I nf o ( ” Creati ng r e a l pul s e l i s t s . . . ” ) ;
// get t he i nf o from t he ps et
coord<> pang1=ps et . getParamCoordD( ” pul s e1 ” ) ;
coord<> pang2=ps et . getParamCoordD( ” pul s e2 ” ) ;
double del ays t ep=ps et . getParamD( ” del ay ” ) ;
// ( spi n , ampl i t ude , phase , o f f s e t )
Pul se PP1( spi ntype1 , pang1 [ 2 ] ∗ PI2 , pang1 [ 1 ] ∗DEG2RAD) ;
Pul se PP2( spi ntype1 , pang2 [ 2 ] ∗ PI2 , pang2 [ 1 ] ∗DEG2RAD) ;
PP2+=Pul se ( spi ntype2 , pang2 [ 2 ] ∗ PI2 , pang2 [ 1 ] ∗DEG2RAD) ;
// get t he Bo
double inBo=ps et . getParamD( ”Bo” ) ;
I nf o ( ” Se t t i ng s pi n parameter o f f s e t s . . . . ” ) ;
for ( i nt j =0; j <nsp ; j ++)¦
i f ( j %2==0)¦ mypars ( j )=s pi nt ype1 ; ¦
el se ¦ mypars ( j )=s pi nt ype2 ; ¦
mypars ( j ) . mol es ( mol es /nsp ) ;
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 270
mypars ( j ) . Bo( inBo ) ;
mypars . temperature ( inTemp ) ;
¦
mypars . cal cTotal Mo ( ) ;
mypars . pr i nt ( cout ) ;
PP1. pr i nt ( cout ) ;
PP2. pr i nt ( cout ) ;
// Extra i nt e r a c t i o ns
typedef I nt e r ac t i ons <
Of f s et <>,
Relax<>,
BulkSus > MyI nt er act i ons ;
I nf o ( ” Se t t i ng I nt e r a c t i o ns . . . . ” ) ;
// t he o f f s e t s
// get t he f i r s t o f f s e t
double o f f s e t 1=ps et . getParamD( ” o f f s e t 1 ” )∗ PI2 ;
double o f f s e t 2=ps et . getParamD( ” o f f s e t 2 ” )∗ PI2 ;
Of f s et <> myOffs ( mypars , o f f s e t 1 ) ;
// Rel axat i on
double t 2s 1=ps et . getParamD( ”T2 1” ) ;
double t 1s 1=ps et . getParamD( ”T1 1” ) ;
double t 2s 2=ps et . getParamD( ”T2 2” ) ;
double t 1s 2=ps et . getParamD( ”T1 2” ) ;
Relax<> myRels ( mypars ,
( ! t 2s 1 ) ? 0 . 0 : 1 . 0 / t2s1 ,
( ! t 1s 1 ) ? 0 . 0 : 1 . 0 / t 1s 1 ) ;
for ( i nt i =0; i <nsp;++i )¦
// s e t t he o f f s e t s and r e l a x t i o n v al s
i f ( i %2==0)¦
myOffs . o f f s e t ( i )=o f f s e t 1 ;
myRels . T1( i )=(! t 1s 1 ) ? 0 . 0 : 1 . 0 / t 1s 1 ;
myRels . T2( i )=(! t 2s 1 ) ? 0 . 0 : 1 . 0 / t 2s 1 ;
¦ el se ¦
myOffs . o f f s e t ( i )=o f f s e t 2 ;
myRels . T1( i )=(! t 1s 2 ) ? 0 . 0 : 1 . 0 / t 1s 2 ;
myRels . T2( i )=(! t 2s 2 ) ? 0 . 0 : 1 . 0 / t 2s 2 ;
¦
¦
//Bul k s u s e p t i b i l i t y
double D=ps et . getParamD( ”D” ) ;
BulkSus myBs(D) ;
// t o t a l i nt e r ac t i on obect
MyI nt er act i ons MyInts ( myOffs , myRels , myBs ) ;
// t y pe de f s f or Bl och parameter s e t s
typedef Bloch < MyPars , Pul se , MyI nteracti ons > Pul seBl och ;
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 271
typedef Bloch < MyPars , NoPulse , MyI nteracti ons > NoPul seBl och ;
// second di mensi on poi nt s
i nt npts2D=ps et . getParamI ( ”npts2D” ) ;
//our dat a mat ri x
matri x FIDs ( npts2D , ns t eps ) ;
// get t he ti me f or t he 2 90 pul s e
double t pul s e 1=PP1. ti meForAngl e ( pang1 [ 0 ] ∗ Pi /180. , s pi nt ype1 ) ;
double t pul s e 2=PP2. ti meForAngl e ( pang2 [ 0 ] ∗ Pi /180. , s pi nt ype1 ) ;
// t he ti me t r ai ns t h i s one wi l l al ways be t he same
I nf o ( ” I n i t i a l i z i n g Time t r ai n f o r f i r s t Pul se . . . . ” ) ;
TimeTrain<UniformTimeEngine >
P1( UniformTimeEngine ( 0 . , t pul s e1 , 1 0 , 1 0 ) ) ;
// l oop over a l l our D val ues
for ( i nt kk=0; kk<npts2D;++kk)
¦
double curDel ay=double( kk)∗ del ays t ep ;
cout<<”On del ay : ”<<curDel ay<<” ”<<kk<<”/”<<npts2D
<<” ¸ r ” ; cout . f l us h ( ) ;
// t he ti me t r ai ns f or t he deal y
TimeTrain<UniformTimeEngine >
D1( UniformTimeEngine ( t pul s e1 , t pul s e 1+curDel ay , 1 0 , 5 ) ) ;
// t he ti me t r ai ns f or t he deal y
TimeTrain<UniformTimeEngine >
P2( UniformTimeEngine (
t pul s e 1+curDel ay ,
t pul s e 2+t pul s e 1+curDel ay ,
10 ,
1 0 ) ) ;
TimeTrain<UniformTimeEngine >
F1( UniformTimeEngine (
t pul s e 2+t pul s e 1+curDel ay ,
t pul s e 2+t pul s e 1+curDel ay+t f ,
nsteps ,
5 ) ) ;
//Thi s i s t he ‘ Bl och ’ t o perf orm a pul s e
Pul seBl och myparspul se ( mypars , PP1 , MyInts ) ;
//Thi s i s t he Bl och s o l v e r t o Col l e c t t he FID
// ( i . e . has no pus l e s . . . FASTER)
NoPul seBl och me ;
me=(myparspul se ) ;
// out i n i t i a l condi t i on
Vector<coord <> > tm=me . currentMag ( ) ;
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 272
stopwatch . r e s e t ( ) ;
Bl ochSol ver <Pul seBl och > dri vP( myparspul se , tm, ”out ” ) ;
dri vP . s et Pr ogr es s Bar ( Sol verOps : : Of f ) ;
// i nt e g r a t e t he Pul se
dri vP . s et Wr i t ePol i cy ( Sol verOps : : Hold ) ;
i f ( ! dri vP . s ol ve (P1) ) ¦
I nf o ( ” ERROR! ! . . coul d not i nt e g r a t e pul s e P1 . . . . ” ) ;
return −1;
¦
// t he f i d s i n i t i a l condi t i on i s j u s t t he pr evi ous
// i nt e g r a t i o ns l a s t poi nt
Bl ochSol ver <NoPul seBl och > dr i v (me , dri vP . l as t Poi nt ( ) ) ;
dr i v . s et Pr ogr es s Bar ( Sol verOps : : Of f ) ;
// i nt e g r a t e t he Del ay
dr i v . s et Wr i t ePol i cy ( Sol verOps : : Hold ) ;
i f ( ! dr i v . s ol ve (D1) ) ¦
I nf o ( ” ERROR! ! . . coul d not i nt e g r a t e del ay D1 . . . . ” ) ;
return −1;
¦
// i nt e g r a t e second t he Pul se
dri vP . s et Wr i t ePol i cy ( Sol verOps : : Hold ) ;
// s e t t he new pul s e s e t
myparspul se . s e t Pul s e s (PP2 ) ;
dri vP . s e t I ni t i a l Co ndi t i o n ( dr i v . l as t Poi nt ( ) ) ;
i f ( ! dri vP . s ol ve (P2) ) ¦
I nf o ( ” ERROR! ! . . coul d not i nt e g r a t e pul s e P2 . . . . ” ) ;
return −1;
¦
// s e t t he de t e c t i on s pi n
dr i v . s et Det ect ( detsp ) ;
// s e t var i ous dat a c o l l e c t i o n p o l i c i e s
dr i v . s e t I ni t i a l Co ndi t i o n ( dri vP . l as t Poi nt ( ) ) ;
dr i v . s e t Co l l e c t i o nPo l i c y ( Sol verOps : : MagAndFID) ;
dr i v . s et Wr i t ePol i cy ( Sol verOps : : Hold ) ;
// i nt e g r a t e t he FID
i f ( dr i v . s ol ve ( F1) ) ¦
FIDs . putRow( kk , dr i v . FID( ) ) ;
¦
¦
matstream matout ( f out , i o s : : bi nary [ i o s : : out ) ;
matout . put ( ”vdat ” , FIDs ) ;
matout . c l o s e ( ) ;
pri ntTi me ( ) ;
¦
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 273
Input Conﬁg File
#parameter f i l e f or l oopi ng t hrough
# s e v e r al Bul kSus paramet ers
dim 1 , 1 , 2
min −0. 5 , −0. 5 , −0. 5
max 0 . 5 , 0 . 5 , 0 . 5
#f i d pi e c e s
npts 512
t f 8
#t he pul s e b i t s
#angl e , phase , ampl i t ude
pul s e1 90 , 90 , 80000
pul s e2 90 , −90 , 80000
#t he t 2 del ay
del ay 0. 000125
npts2D 64
#b as i c s pi n paramet ers
s pi nt ype1 1H
s pi nt ype2 31P
de t e ct 31P
Bo 4 . 7
temperature 300
mol es . 104
#o f f s e t s f or each s pi n
o f f s e t 1 −722
o f f s e t 2 −4. 9
#r e l ax at i on params f or each s pi n
T2 1 0. 002
T1 1 0
T2 2 0 . 5
T1 2 0
#f or t he Bul k S u s e p t i b i l i t y
D 1
#f i l e out put names f or t he dat a
f i dout data
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 274
A.3.5 Example Classical Simulation of the Modulated Demagnetizing
Field
This simulation is a replication of the simulation performed by Y.Y Lin in Sci
ence [87]. It demonstrates the nonlinear properties of including both Radiation Damping
and the Modulated Demagnetizing ﬁeld resulting in a resurrection of a completely crushed
magnetization. Both the C++ source using the BlochLib framework and the conﬁguration
ﬁle is given. Results of this simulation can be seen in Figure 5.12.
C++ source
#include ” bl o c hl i b . h”
/∗
t h i s i s an at t empt t o i mi t at e t he r e s u l t from YY Lin i n
6 OCTOBER 2000 VOL 290 SCIENCE
The s i mul at ed e f f e c t i v e pul s e sequence
RF −−−90x−−−−FID
Grad −−−−−Gzt−−−−−−
where t he gr adi ent compl et e crus hes t he magnet i z at i on
wi t h some s mal l eps er r or from t he i dea
∗/
using namespace Bl ochLi b ;
using namespace std ;
ti mer stopwatch ;
void pri ntTi me ( i nt nrounds =1)¦
std : : cout <<std : : endl << ”Time taken : ”
<< (stopwatch ( ) / nrounds )
<< ” seconds ¸n” ;
¦
void I nf o ( std : : s t r i ng mess )
¦ std : : cout<<mess ; std : : cout . f l us h ( ) ; ¦
//some t y pe de f s t o make t ypi ng e as i e r
typedef XYZcyl i nder TheShape ;
typedef XYZshape<TheShape> TheGridS ;
typedef Gradi entGri d<TheGridS > TheGrid ;
typedef Li stBl ochParams < TheGrid ,
BPopti ons : : Pa r t i c l e [ BPopti ons : : Hi ghFi el d ,
double > MyPars ;
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 275
// Extra i ne r ac t i ons
typedef I nt e r ac t i ons <Of f s et <MyPars>,
Relax<>,
RadDamp,
ModulatedDemagField > MyI nt er act i ons ;
// t y pe de f s f or Bl och parameter s e t s
typedef Bloch < MyPars , Pul se , MyI nteracti ons > Pul seBl och ;
typedef Bloch < MyPars , NoPulse , MyI nteracti ons > NoPul seBl och ;
i nt main( i nt argc , char ∗ argv [ ] )
¦
//Get a l l t he var i ous paramet ers
std : : s t r i ng f n ;
query parameter ( argc , argv , 1 , ”Enter f i l e to par s e : ” , f n ) ;
Parameters ps et ( f n ) ;
double pang1=ps et . getParamD( ” pul s e angl e1 ” ) ;
double amp=ps et . getParamD( ”pulseamp” ) ;
i nt ns t eps=ps et . getParamI ( ” npts ” ) ;
double t f=ps et . getParamD( ” t f ” ) ;
std : : s t r i ng f out=ps et . getParamS ( ” f i dout ” ) ;
std : : s t r i ng magout=ps et . getParamS ( ”magout” ) ;
i nt cv=ps et . getParamI ( ” l yps ” , ”” , f al se ) ;
std : : s t r i ng l y p f i l e=ps et . getParamS ( ” l ypout ” , ”” , f al se , ” l yps ” ) ;
std : : s t r i ng dataou=ps et . getParamS ( ” t r a j e c t o r i e s ” , ”” , f al se ) ;
// gr adi ent pars
double gradti me1=ps et . getParamD( ” gradti me1 ” ) ;
/∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/
// Gri ds
coord<int > dims ( ps et . getParamCoordI ( ”dim” ) ) ;
coord<> mins ( ps et . getParamCoordD( ”gmin” ) ) ;
coord<> maxs ( ps et . getParamCoordD( ”gmax” ) ) ;
coord<> smi ns ( ps et . getParamCoordD( ”smin” ) ) ;
coord<> smaxs ( ps et . getParamCoordD( ”smax” ) ) ;
I nf o ( ” Creati ng gr i d . . . . ¸ n” ) ;
Grid<UniformGrid > gg ( mins , maxs , dims ) ;
I nf o ( ” Creati ng i n i t a l shape . . . . ¸ n” ) ;
TheShape t e s t e r ( smins , smaxs ) ;
I nf o ( ” Creati ng t o t a l shape−gr i d . . . . ¸ n” ) ;
TheGridS gr i ds ( gg , t e s t e r ) ;
//dump t he g r i d t o a f i l e
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 276
std : : of stream goo ( ” gr i d ” ) ;
goo<<gr i ds <<std : : endl ;
// cr eat e t he gr adi ent g r i ds . .
char i de a l=ps et . getParamC( ” i de a l ” ) ;
coord<> grad=ps et . getParamCoordD( ”grad” ) ;
I nf o ( ” Creati ng Gradi ent map gr i ds . . . . ¸ n” ) ;
TheGrid j j ( gr i ds ) ;
j j .G( grad ) ;
/∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/
/∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/
// s e t up Parameter l i s t s
i nt nsp=j j . s i z e ( ) ;
I nf o ( ” Creati ng e nt i r e s pi n parameter l i s t f o r ”
+i t o s t ( nsp)+
” s pi ns . . . . ¸ n” ) ;
MyPars mypars ( j j . s i z e ( ) , ”1H” , j j ) ;
nsp=mypars . s i z e ( ) ;
double inBo=ps et . getParamD( ”Bo” ) ;
double inTemp=ps et . getParamD( ” temperature ” ) ;
std : : s t r i ng s pi nt ype=ps et . getParamS ( ” s pi nt ype ” ) ;
double mol es=ps et . getParamD( ”mol es ” ) ;
std : : s t r i ng detsp=s pi nt ype ;
I nf o ( ” s e t t i ng s pi n parameter o f f s e t s . . . . ¸ n” ) ;
for ( i nt j =0; j <nsp ; j ++)¦
mypars ( j )=s pi nt ype ;
mypars ( j ) . Bo( inBo ) ;
mypars ( j ) . temperature ( inTemp ) ;
¦
mypars . cal cTotal Mo ( ) ;
mypars . pr i nt ( std : : cout ) ;
/∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/
//The pul s e l i s t f or a r e al pul s e on prot ons . .
I nf o ( ” Creati ng r e a l pul s e l i s t s . . . ¸ n” ) ;
// ( spi n , ampl i t ude , phase , o f f s e t )
Pul se PP1( spi ntype , amp , 0 . ) ;
PP1. pr i nt ( std : : cout ) ;
double t pul s e=PP1. ti meForAngl e ( pang1∗Pi /180. , s pi nt ype ) ;
/∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/
// ti me t r ai n
double t c t =0;
I nf o ( ” I n i t i a l i z i n g Time t r ai n f o r f i r s t Pul se . . . . ¸ n” ) ;
TimeTrain<UniformTimeEngine > P1 ( 0 . , t pul s e , 1 0 , 1 0 0 ) ;
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 277
t c t+=t pul s e ;
I nf o ( ” I n i t i a l i z i n g Time t r ai n f o r Fi r s t Gradi ent Pul se . . . . ¸ n” ) ;
TimeTrain<UniformTimeEngine > G1( tct , t c t+gradti me1 , 5 0 , 1 0 0 ) ;
t c t+=gradti me1 ;
I nf o ( ” I n i t i a l i z i n g Time t r ai n f o r FID . . . . ¸ n” ) ;
TimeTrain<UniformTimeEngine > F1( tct , t f+tct , nsteps , 5 ) ;
i f ( i de a l==’ y ’ ) ¦ F1 . setBegi nTi me ( 0 ) ; F1 . setEndTime ( t f ) ; ¦
/∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/
/∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/
// i nt e r a c t i o ns
double t 2s=ps et . getParamD( ”T2” ) ;
double t 1s=ps et . getParamD( ”T1” ) ;
double o f f s e t=ps et . getParamD( ” o f f s e t ” )∗ PI2 ;
//demag f i e l d ’ ti me cons t ant ’
// because we are i n t he ’ p a r t i c l e ’ rep
// we need t o c a l c u l a t e t he r e al Mo s e par at e l y
double mo=mypars [ 0 ] . gamma( ) ∗ hbar ∗
tanh ( hbar ∗PI ∗( inBo∗mypars [ 0 ] . gamma( ) / PI2 )/kb/inTemp)
∗No∗mol es ∗1 e6 / 2 . 0 ;
double demag=1. 0/(mo∗permVac∗mypars [ 0 ] . gamma ( ) ) ;
double t r=ps et . getParamD( ”raddamp” ) ;
I nf o ( ” s e t t i ng I nt e r a c t i o ns . . . . ¸ n” ) ;
Of f s et <MyPars> myOffs ( mypars , o f f s e t ) ;
Relax<> myRels ( mypars , ( ! t 2s ) ? 0 . 0 : 1 . 0 / t2s , ( ! t 1s ) ? 0 . 0 : 1 . 0 / t 1s ) ;
RadDamp RdRun( t r ) ;
ModulatedDemagField DipDip( demag , j j .G( ) ) ;
std : : cout<<” Total Mangeti zati on : ”<<mo<<std : : endl ;
std : : cout<<DipDip<<” Td: ”<<DipDip . td ( )
<<” axi s : ”<<DipDip . di r e c t i o n()<<std : : endl ;
MyI nt er act i ons MyInts ( myOffs , myRels , RdRun, DipDip ) ;
demag=ps et . getParamD( ”demagOff ” , ”” , f al se , 0 . 0 ) ;
i f ( demag! =0) DipDip . o f f ( ) ;
/∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/
//Thi s i s t he ‘ Bl och ’ t o perf orm a pul s e
I nf o ( ” I n i t i a l i z i n g t o t a l parameter l i s t wi th a pul s e . . . . ¸ n” ) ;
Pul seBl och myparspul se ( mypars , PP1 , MyInts ) ;
//Thi s i s t he Bl och s o l v e r t o Col l e c t t he FID
// ( i . e . has no pus l e s . . . FASTER)
I nf o ( ” I n i t i a l i z i n g t o t a l parameter l i s t f o r FID c o l l e c t i o n . . . . ¸ n” ) ;
NoPul seBl och me ;
me=myparspul se ;
Vector<coord <> > tm=me . currentMag ( ) ;
std : : cout<<”TOTAL mag i n i t i a l c ondi t i on : ”<<sum(tm)<<std : : endl ;
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 278
// t he ’ er r or ’ i n t he h e l i x
double emp=ps et . getParamD( ” eps ” , ”” , f al se , 1 e −3);
// s e t t he c i r c ul a r i n i t i a l c o n d i t i o n . . a s i ng l e h e l i x
i f ( i de a l==’ y ’ )¦
MyPars : : i t e r a t o r myit ( mypars ) ;
double lmax=smaxs . z()−smi ns . z ( ) ;
coord<> tp ;
while ( myit )¦
tp=myit . Poi nt ( ) ;
tm[ myit . curpos ( ) ] . x()=s i n ( tp . z ( ) / lmax∗PI2)+emp;
tm[ myit . curpos ( ) ] . y()=cos ( tp . z ( ) / lmax∗PI2 ) ;
tm[ myit . curpos ( ) ] . z ( ) =0. 0;
++myit ;
¦
¦
stopwatch . r e s e t ( ) ;
// t he two main s o l v e r s
Bl ochSol ver <Pul seBl och > dri vP( myparspul se , tm) ;
Bl ochSol ver <NoPul seBl och > drivD(me , tm) ;
// i nt e g r a t e pul s e and gr adi ent pul s e
// onl y i f NOT i d e a l experi ment
i f ( i de a l==’ n ’ )¦
// out put t r a j e c t o r y dat a i f wanted
i f ( dataou!=”” )¦
dri vP . s et Wr i t ePol i cy ( Sol verOps : : Conti nous ) ;
dri vP . setRawOut ( dataou , std : : i o s : : out ) ;
¦ el se ¦
dri vP . s et Wr i t ePol i cy ( Sol verOps : : Hold ) ;
¦
dri vP . s e t Co l l e c t i o nPo l i c y ( Sol verOps : : Fi nal Poi nt ) ;
// i nt e g r a t e t he f i r s t pul s e
myOffs . o f f ( ) ; // t urn o f f gr adi ent
I nf o ( ” I nt e gr at i ng f i r s t Pul se . . . . ¸ n” ) ;
i f ( ! dri vP . s ol ve (P1) ) ¦
I nf o ( ” ERROR! ! . . coul d not i nt e g r a t e pul s e P1 . . . . ¸ n” ) ;
return −1;
¦
// i nt e g r a t e t he gr adi ent pul s e
I nf o ( ”¸ nI nt e gr at i ng the Gradi ent Pul se . . . . ¸ n” ) ;
drivD . s e t I ni t i a l Co ndi t i o n ( dri vP . l as t Poi nt ( ) ) ;
// out put t r a j e c t o r y dat a i f wanted
i f ( dataou!=”” )¦
drivD . s et Wr i t ePol i cy ( Sol verOps : : Conti nous ) ;
drivD . setRawOut ( dataou , std : : i o s : : app [ std : : i o s : : out ) ;
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 279
¦ el se ¦
drivD . s et Wr i t ePol i cy ( Sol verOps : : Hold ) ;
¦
i f ( gradti me1 >0)¦
myOffs . on ( ) ; // t urn on gr adi ent
i f ( ! drivD . s ol ve (G1) ) ¦
I nf o ( ” ERROR! ! . . coul d not i nt e g r a t e G1 . . . . ¸ n” ) ;
return −1;
¦
¦
¦
// i nt e g r a t e FID
i f ( cv )¦
me . c a l c Va r i a t i o na l ( ) ;
drivD . s e t Var i at i onal I ni t Cond (me . c ur Var i at i onal ( ) ) ;
drivD . setLyapunovPol i cy ( Sol verOps : : LypContinous ) ;
drivD . setLypDataFi l e ( l y p f i l e ) ;
¦
myOffs . o f f ( ) ;
I nf o ( ”¸ nI nt e gr at i ng f o r FID . . . . ¸ n” ) ;
// out put t r a j e c t o r y dat a i f wanted
drivD . s e t Co l l e c t i o nPo l i c y ( Sol verOps : : MagAndFID) ;
i f ( dataou!=”” )¦
drivD . s et Wr i t ePol i cy ( Sol verOps : : Conti nous ) ;
i f ( i de a l==’ y ’ ) drivD . setRawOut ( dataou , std : : i o s : : out ) ;
el se drivD . setRawOut ( dataou , std : : i o s : : app [ std : : i o s : : out ) ;
¦ el se ¦
drivD . s et Wr i t ePol i cy ( Sol verOps : : Hold ) ;
¦
// s ol v e t he FID and wr i t e i t t o a f i l e
i f ( drivD . s ol ve ( F1) ) ¦
drivD . wri teSpectrum( f out ) ;
drivD . writeMag ( magout ) ;
¦
pri ntTi me ( ) ;
// r i ng a b e l l when we are done
std : : cout<<”¸a”<<std : : endl ;
¦
Input Conﬁg File
#parameter f i l e f or 1 pul s e − 1 Grad Z sequences
#g r i d uni t s i n cm
dim 1 , 1 , 100
A.3. BLOCHLIB CONFIGURATIONS AND SOURCES 280
gmin −0. 02 , −0. 02 , −0. 004693
gmax 0 . 0 2 , 0 . 0 2 , 0 . 0 0 4 6 9 3
#c y l i nde r shape min and max
smin 0 , 0 , −0. 004693
smax . 0 0 3 , 6 . 2 8 , . 0 0 4 6 9 3
#f i d pi e c e s
npts 512
t f 2
#t he pul s e b i t s
pul s eangl e1 90
pulseamp 80000
#b as i c s pi n paramet esr
Bo 14. 1
temperature 300
o f f s e t 0
T2 0
T1 0
s pi nt ype 1H
#er r or i n i d e a l gr adi ent pul s e
# al ong t he x−ax i s
eps 1 e−3
#t urn on ( 0) or o f f ( 1) t he demagnet i zi ng f i e l d
demagOff 0
#95% wat er ( 2 prot ons a pop )
mol es 0. 1045
#t he ext r a i nt e r a c t i o ns par t s
raddamp 0. 01
## #gr adi ent t hi ng s
#choose ’ r e al gr adi ent ’ ( n) or i d e a l i n i t i a l condi t i on ( y )
#i f i d e a l magnet i z at i on wi l l be spread evenl y
#around a c i r c l e i n t he xy pl ane
i de a l y
#non−i d e a l b i t s ( grad uni t s i n Gauss/cm)
grad 0 , 0 , 1
gradti me1 0. 005
#ouput dat a f i l e names
f i dout data
magout mag
t r a j e c t o r i e s t r a j
The dissertation of Wyndham Bolling Blanton is approved:
Chair
Date
Date
Date
Date
University of California, Berkeley
Fall 2002
High Performance Computations in NMR
Copyright c 2002 by Wyndham Bolling Blanton
1
Abstract
High Performance Computations in NMR by Wyndham Bolling Blanton Doctor of Philosophy in Chemistry University of California, Berkeley Professor Alexander Pines, Chair
As an analytic noninvasive technique to study molecules in their natural environment, NMR has little equal. The advancement of the technique is beginning to enter a new phase, where many body dynamics, complex control, and precise measurements of many body spin properties preclude any exact theoretical treatment. Approximation methods and other reductions in the set of parameter spaces are currently used to obtain some form of intuition about a simpliﬁed NMR system; however, to exactly proﬁle a real system, numerical simulation is required. The scope of most NMR simulations is chieﬂy regulated to small spin systems, where the dynamics are simpliﬁed enough to simulate eﬃciently. The cause is typically based on a poor understanding of how to simulate an NMR situation eﬀectively and eﬃciently. This seems consistent with the fact that most NMR spectroscopists are not computer scientists as well. The introduction of novel programming paradigms and numerical techniques seems to have eluded the ﬁeld. A complete simulation environment for NMR is
2 presented here marrying three fundamental aspects of simulations 1) numerical speed and eﬃciency, 2) simplicity in implementation, and 3) NMR speciﬁc algorithmic developments. The majority of numerical NMR is reduced to a simple simulation framework. The framework allows for more complex simulations for explorations of both many body spin dynamics and control. A speciﬁc large scale simulation is applied to recoupling sequences in solid–state NMR. Using simple permutations on base pulse sequences can result in control enhancements on both the simple system and the many body system beyond a theoretical approach. The sheer number of permutations required to solve the problem would have certainly been impossible without the aid of this framework. This new framework now opens other unexplored possibilities of using simulation as a development tool for the larger problems of many body dynamics and control.
Professor Alexander Pines Dissertation Committee Chair
.i To my Grandmother and Grandfather. Lucy and Wyndham Jr.
. . . .4 Optimizing For Hardware . . . . . . . . . . . . . . .1 Classical Algorithms . .4 Expression Template Implementation 2. . .4. . . . . . . . . . . 3. . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. .2 The Object . . . . . . . . . . . . . .1 Data Types . . . 2. . . .1 Classical Mechanics . .3. . . . . . . . . . . . . . . . . . . . . . . . . 3. . . . . . . . . . . . . . . . v viii 1 4 7 8 9 13 13 14 15 19 27 30 37 42 42 43 59 60 64 67 73 73 74 76 76 76 78 82 . . . .3 An Array Object and Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . 2. . . . . . . . . . . . . . . . . 3. . 4 NMR Algorithms 4. . . . . . . . . . . . . . . . . . . .1 Rotations . . . . . . . . 4. . . . . . . . 2. . . . . . . . . . . .2 Quantum Algorithms . . . . . . . . . . . . . . 2. .4. . . . . . . . . .2 Classical . . . . . 2. . . . . . . . . . . . . . . .3.2 ODE solvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . 3. . . . . . . .4 NMR Initial Conditions . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4. . . . . . . . . . . . 2. . . . . . . . . . . . . . . . . . 3 NMR Forms 3. . . . . . . . . . . . . . . 2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .ii Contents List of Figures List of Tables 1 Introduction 2 Computer Mechanics 2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Expression Templates . . . . . .2.3. . . . . . . . . . . . . . . 3. .2 A Faster Matrix Multiplication . . . . . . . . . . . . . . . .2 Bloch Equation Magnetic Fields 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. . . . . . 3. . . . . . . . . . . . . . . .1 Eigenvalue Problem 4. . . . . . . .3 Quantum Mechanics . . . . . . .1 Basic Computer Architecture . .3 The Hamiltonians .1 Motivations . .2 Stacks . . . 3. 2. . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . .1 Syntax . . .2 Rotational Frames . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . .1 Quantum .3. . . .
. . . . . . . . 4. . . . . 5. . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . 6. . . . .4. . . . . . . . . .2. . . . . . . . . . . . .4. . . . . . . . . . 4. .3. . . . . .5 Conclusions . . . . 6 Massive Permutations of Rotor Synchronized Pulse Sequences 6. . . . . . . . 6. . . .2. . . . . . . . . . . . . . 6. . . . . . . . 5. . . . . . . . . . . . . . . . . . 5. . . . . . . . . . . . . . . . .1 Sequence Measures . . . . . . . . . . .3 5 BlochLib 5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. .2 Background Theory . . . . . . . . . . . 6. . . . . . . . . . . . . . .3 C7 . . . . . . . . . . . . . . .3. . 6. . . . . . . . . . . . . . . . . . . . . . . . 4. . . .1 Evolutionary Algorithms (EA) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5. . . . . .2. . . . . . . . . . . . .2. 5. . . .4. . .4 Removable of Higher Order Terms . . . . . . 5. . . . . . . . . 6. . . . . . . . . .1 The Sub–Units . . . . . . . .1 Introduction .3 Algorithmic Flow . .1. . . . . . . . . . . . . . . . . simulations . . . . . . . . . . . . . . . . . . 5. .2. . .2 Transfer Eﬃciencies . . . . . . . . .5 Nonperiodic Hamiltonians . .2 Classical Program: Magnetic Field Calculators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 7. . .3. . . . . . . . . . . . . . 5. . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1 Experimental Evolutions (EE) . . . . 209 7. . . . .4 Data and Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . .1 Rotor Synchronization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 Periodicity and Propagator Reduction 4. . . . . 6. . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . 6. . . . . . . . . . . . . . . . . . . . . . . . .6 Powder Average Integration . . . 5. . . . . . . . . . . . . . .2 The Measure . . . . .2 Neural Networks . . . . . . . . . . . . . . . . . . . . .1 Average Hamiltonian .3 Eigenspace . . . .2. . . . . . . . . . . . 6.4 Various Implementations . . . 6. . . . .4. . .2 The Abstract NMR Simulation . . 5. . . . . . . . . . . .2. . . . . . . . . . . . . . . Conclusions and Comments . . . . .3 Final Remarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5. . . . . . . . . . . . . . . . . . . . . . . .1 Existing Numerical Tool Kits . . . . .2 Recoupling RSS . . .4 Drawbacks . . . . . . .3 BlochLib Design . . . . . . . . . . . . . . 82 83 89 95 100 100 103 105 105 106 106 106 108 109 109 110 111 112 121 123 124 129 131 140 141 141 142 143 143 145 150 151 155 155 156 158 161 161 185 196 4. . . .2 Theoretical Evolutions (TE) . . . . .3. . . . . . . . . .4 Periodicity and Eigen–Space methods 4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3 Permutations . . . .3. . . . . . . . 7 Future Expansions 201 7. . . . . . . . . . . . .3 Existing NMR Tool Kits . . . . . . . . . . . . . . . . . 6. . . . . . . . . . . . . . . . . . . .5 Conclusions . . . . . . . . . . . . . . 6. . 6. . .4. .3 Classical Programs: Bloch Simulations . . . . . . . . . . . .3 BlochLib Layout . . . . . . . . . . .1 Solid . . 6. . . . . . . . .1 The Direct Method . . . 5. . . . .iii 4. . . . . . . . . . . .2.4 Why Create a new Tool Kit? . . . . . . . . . . . . 211 . . . .2 Experimental and Theoretical Evolutions for NMR 5. . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . .2. . . . . . . . . . . . 5. . . . . . .1 Introduction . . . 5. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .2 C++ Template metaprogram to unroll a ﬁxed length vector at compilation time . . . . . A. . . . . . . . . . . . . . . A.4 γ − COM P U T E C++ Class . . . . .1 Mathematica Package to generate Wigner Rotation matrices and Spin operators. . . . . . . . . . . A.2. . . . . .2 Rational Reduction C++ Class . .3. . . . . . . . . . A. . . .3. . . . . . . . .3 C++ code for performing a matrix multiplication with L2 cache blocking and partial loop unrolling. .1. . .2. .1 Solid conﬁguration ﬁles . A. . . . . . . . . . . . . . . .1 C++ Template code used to generate prime number at compilation A. . . . . . . . . . . . . . . . . . . . . .1. . . . . A. . . .3 Optimized static Hamiltonian FID propogation . . . . . . . .5 C++ class for a 1 hidden layer Fully connected back–propagation Neural Network .2.3. . .3. . . . . A. . . . . . . . . . . . . . . . . . A. . . . . .3. . . . A. . A.5 Example Classical Simulation of the Modulated Demagnetizing Field 213 225 225 225 226 228 230 232 239 239 244 252 253 263 263 266 267 267 274 .2 NMR algorithms . . .4 An MPI master/slave implimentation framework . . . . . . . . . . . . . . . . . . . . . . . . . . .3 BlochLib Conﬁgurations and Sources . . . .1. . . . . . . . . . . . . . . . . . A. . . . . . . . A. . .4 Example Classical Simulation of the Bulk Susceptibility .2 Magnetic Field Calculator input ﬁle . A. . . . . . . . . . . . . . . . . .1 General C++ code and examples . . . .1. . . . . . . . .2. . . . . . . . . . . . . . . .3 Quantum Mechanical Single Pulse Simulations . .iv Bibliography A Auxillary code A. . . A. . . . . . . . . . . . . . . . . . . . . . . . . . A. . . . .1. . .
. . . . How the compiler unrolls an expression template set of operations. Magnetization in iso–surfaces versus the applied magnetic ﬁeld. . . . . . .1 4. . . . .v List of Figures 2. . . . . The design of the EE program Solid derived from the input syntax. . . . . The magnetization of a sample inside a magneti ﬁeld. . 1D static and spinning 2 spin simulation . . . . .12 3. . Various propagators needed for an arbitrary rational reduction. . . . . . . . .4 5. . . . . . . . . . . .4 2. . .3 4. . . . . . . . . . . . . . . . . . . DAXPY speed tests . . . . . . . . . . Simpson . . . . . . . . . Diagram of one Hamiltonian period and the propagator labels used for the COMPUTE algorithm . . . . . .2 5. . . . . . . .2 4. . . . . . . . C=A*B*adjoint(A) speed of BlochLib . . . . . . . . . . . . Pipe lines and loop unrolling . .6 5. A 128 bit SIMD registers made of 4–32 bit data values . . . . . . . . . . . . . . . . . . . . . . . A generic computer data path.3 2. . . The basic design for the Field Calculator program. . . . . . Bo . . . . . . . . . . . . . . . . . . . . . . .10 . . . . .1 3. A rough design for a classical Bloch simulation over various interactions. Solid vs. . . A simple stack tree . . . . 6 15 25 26 28 29 30 34 35 36 39 40 52 55 75 84 89 96 102 107 113 115 125 127 128 128 130 132 133 The magnitude of the dipole ﬁeld . . . . . . . . . Magnetic ﬁeld of a D–circle .2 2. . The basic design layout of the BlochLib NMR tool kit. . . Speed comparison in MFLOPS of L2 cache blocking and loop unrolling . . . . . . Speed comparison in MFLOPS of loop unrolling . . .10 2. . . . . . .9 5. . 1D and 2D postC7 simulation . . . . . . . the temperature T . . . . . . . .5 5. . . . . . . . Eﬀectiveness of the rational propagator reduction method. . . . . . . . . . . . . . . . . . . . Octants of equal volume of a sphere. . . . . . . .1 2. . . . . . . . . . . . . . . . .11 2. . . . .7 2.2 3. . . . .1 5. . . . . . . . . . . . . . . . . .3 5. . . . . . . . . . . . . . . . . . . . . . . .6 2. . . . Experimental Evolutions and Theoretical Evolutions . . . Speed in MFLOPS of a matrix–matrix multiplication . . . . . . . . . . . .7 5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5 2. . . . . . . . . . . . . . . . . . . . .9 2. . . . . . . . . . . .8 5. . . . . . . . . . . .4 5. . . . . . . . . . . . . . . . .8 2. . . . . . . . . . . . . . . . . . . . . . . . and number of moles. . . .3 A two state Turing machine . . . . . . . . . . . Cache levels in modern Processors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4. . . . . . . . . . . . . . A pictorial representation for the matrix–matrix tensor multiplication .
. . . . . . . . . . . . . . . . . . .16 fold application of the postC7 and the best permutation cycles for the SS1 system as a function of 13 C1 and 13 C oﬀsets at ω = 5kHz.16 fold application of the postC7 and the best permutation cycles for the SS1 system as a function of 13 C1 and 13 C2 oﬀsets at ωr = 5kHz. . . . .6 6. Spin system SS2 with 16 total number of C7s applied. . . . . Spin system SS3 with 12 total number of C7s applied. . . . . . . . .10 6.21 6.11 6. .5 6. . . . .18 6. . . . . . . . . . . . . . . . . . . . . . . . . Spin system SS3 with 16 total number of C7s applied. . . . . 135 136 138 139 6. . . . . . .14 6. . . . . .2 6. . . . . . . . . Spin system SS2 with 4 total number of C7s applied. . . . . . . . . . . . . . . . . Spin system SS1 with 40 total number of C7s applied.20 6. . . . . . . . . . Magnetic ﬁeld of a solenoid . . . . . . . . . . . . . . . . .16 fold application of the postC7 and the best permutation cycles for the SS2 system as a function of 13 C1 and 13 C oﬀsets at ω = 5kHz. . . . .22 6. Spin system SS1 with 24 total number of C7s applied. . . . . . . 2 r Contour–gradient transfer eﬃciencies plots for a 4. . . . PostC7 transfer eﬃciencies on a two spin system with ωr = 5kHz for various dipolar coupling frequencies . . .4 6. The two RSS classes C (a) and R (b). . . . Compensated C (a).17 6. . . . . .29 Bulk susceptibility HETCOR . .13 6. . .12 5. . . . . . . . . . . . . . . . . . . . Spin system SS3 with 32 total number of C7s applied. . modulated local ﬁeld .13 5. . .15 6. . . . .3 6. . . . . . . . . .30 6.12 6. . . . . . Spin system SS2 with 12 total number of C7s applied. .8. . . . . . .16 6. . . . . . . . .vi 5. . .8. . . . . . . . . . . .26 6. . . . .7 6. . . . . . 2 r 142 147 149 152 153 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 187 188 190 191 192 . . . . . .11 5.24 6. .12. . . . . . . . . . . . . . . . . . . . . . Spin system SS2 with 8 total number of C7s applied. . R (b) and posted C (c). . . Spin system SS2 with 24 total number of C7s applied. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .27 6. Spin system SS1 with 20 total number of C7s applied. . . . . . . . . . . . . . . . . . . Spin system SS1 with 4 total number of C7s applied.9 6. . . . . . . . . . . . . .19 6. . . Spin system SS3 with 4 total number of C7s applied. . . . . . . . . . . . . .23 6. . . . . . . . . . . 3D transfer eﬃciencies plots for a 4. .28 6. . . . . . initial density matrices and detection for a transfer eﬃciency measurement. . . Spin system SS1 with 48 total number of C7s applied. . . . Transfer eﬃciencies for a 4 fold application of the basic C7 and the postC7 for the SS1 system as a function of 13 C1 and 13 C2 oﬀsets at ωr = 5kHz. . . . . . . . . . . . . . Simulation of radiation damping and Magnetic ﬁeld of a split solenoid . . . . R(d) RSS sequences. . . . . Spin system SS1 with 12 total number of C7s applied. 3D transfer eﬃciencies plots for a 4. . . . . . . . . . . . . . Spin system SS1 with 8 total number of C7s applied. . Spin system SS1 with 32 total number of C7s applied. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .31 A general rotor synchronized pulse sequence a) using pulses and delays. . . . . . . Spin system SS3 with 24 total number of C7s applied. . Spin system SS3 with 8 total number of C7s applied. .25 6. . .12. . . . . . . . . . . . . . Diﬀerent base permutations on the postC7 seqeunce . the . . . . . . . . . . . . .14 6. . . . . . . . . . . . . Spin system SS1 with 16 total number of C7s applied. . . . . . . Spin system SS2 with 32 total number of C7s applied. . . . . . . . . . . .8. . . . . Pulse sequence. and b) using a quasi continuous RF pulse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .12. . . . . . .8 6. . .1 6. . .
.12. . . . 7. . . . .5 7. . . . . . .6 The standard evolutionary strategy methods and controls. 6. . . . . . . . 2 r 6. . . . . . . . . . . . .16 fold application of the postC7 and the best permutation cycles for the SS2 system as a function of 13 C1 and 13 C2 oﬀsets at ωr = 5kHz. .1) strategy. . . .3 7. . . Diﬀerential Evolution (DE) generation step for an ES(3. . . . . 6. . . . . . . . . . . . . . . .2 7.vii 6. 193 194 195 197 198 199 204 205 206 207 208 209 . .2) strategy. . .16 fold application of the postC7 and the best permutation cycles for the SS3 system as a function of 13 C1 and 13 C2 oﬀsets at ωr = 5kHz. .37 Transfer eﬃciencies using the postC7 and the best permutated cycles across over diﬀerent cycles for the SS3 spin system. . . . . . . . .33 3D transfer eﬃciencies plots for a 4. . . . . . Genetic Algorithm (GA) generation step for an ES(3. . . . . .4 7. . . . . . . 6.36 Transfer eﬃciencies using the postC7 and the best permutated cycles across over diﬀerent cycles for the SS2 spin system. .16 fold application of the postC7 and the best permutation cycles for the SS3 system as a function of 13 C1 and 13 C oﬀsets at ω = 5kHz. . . . . .1 7. . . . . .8.8. . . . . . . . .35 Transfer Eﬃciencies using the postC7 and the best permutated cycles across over diﬀerent cycles for the SS1 spin system.12. .12. . . . . . . . . . . . . . . . . . . . . . . . . . .32 Contour–gradient transfer eﬃciencies plots for a 4. .8. . . . . . . . Evolution Programming (EP) generation step for an ES(2. . . . Basic 1 and 2 layer feed–forward neural networks. .34 Contour–gradient transfer eﬃciencies plots for a 4. . . . . . 6. An arbitrary permutation cycle parent genes and resulting child. . . . . . . . . .1) strategy. . . .
1 4. . . . . . . . A list of some sub–units for a C7 permutation cycle. . .m . . . . . . . .viii List of Tables 2. Key examples and implementation programs inside BlochLib . . . . . . . . . . . . . . . . . All units are in Hz . . . . . . . . . . . . . . . . . . . . . . . . . . . Matrix Multiplication (MM) reduction use rational reduction . . . . . . . . Dm. . . . . . . . . . .2 3. . . . . For m = 1 and n = 5 we have this series of propagators necessary to calculate the total evolution . . . . . . . .1 6. . . . . . . . . . .1 5. 5. .2 6. .1 3. . . . . m. . . . . Spin System parameters for the three sets of permutations. . . . .2 6. . . . . .4 6. . d2 . . . . . . . . . . .4 Basic High Level Language Data Types . . . . 1 Wigner rank 1 rotation elements. . . . A reduced set of individual propagators for m = 9 and n = 7 . . . .17 . . . . . . . . . . SIMD registers available of common CPUs . .m Spherical tensor basis as related to the Cartesian basis for spin i and spin j 8 34 62 63 67 86 86 88 90 121 124 156 160 161 161 162 186 Time propagation using individual propagators via the Direct Method . . . . Relevant weighting factors for Eq. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2 3. . . . . . . Spin operators and tensors generated to probe the eﬀective Hamiltonians . . .3 6. . . . . . . Available Matlab visualization functions in BlochLib .1 2. . . . . . . .3 4.5 6. Reduced Wigner rank 2 rotation elements. . . . . . . .6 . . . Best C7 permutation sequences for each spin system and C7 cycle length. . . . . . . . .3 4. . . . . . . . 6. . . . . . .2 4. . . . Sequence Permutation set for the eﬀective Hamiltonian calculations of the postC7 sequence. . . .
the CPU was inevitable. Andreas Trabesinger for calling to my attention the classical/quantum crossover opening up totally new CPU problems and solutions. . John’s constant testing and back and forth has helped me improve almost every aspect of my coding life. In particular Jamie Walls and Bob Havlin seem to always have something new to try. Dimitris Sakellariou pushed the development of speed. So to this yokel. In essence the mathematical background was brought to bare by Jamie as Bob enlightened the experimental side of NMR. type. From this point I thank Dr. From that point on. Nowledge To say that one ﬁnished anything here without any help would be a nasty lie. Their constant volley of questions and ‘requests’ give me the impetuous to push my own skills higher. Those many years staring at a computer screen have made me appreciate the comments and discussions from those who do not. From many years of discussion with these two. I give my thanks. I have learned most everything I claim to know. I give my estranged thanks. John Logan and Dr. There is always something new spewing forth from the voice boxes of the pines folk.ix Acknowledgments Ack None of this thesis would have even existed without the aid of an SUV knocking me oﬀ my motor cycle at the beginning of my years in the Pines group. To all those Pine Nuts I have run into. With only my left (not my ‘good’ arm) functioning I had to leave the experimental track I had started and venture into the only thing I could do. It left my arm in a state of mushy goo for 6 months.
and brain power that seem to fuel the everyday life in the lab as well as pushing new thoughts to the end. my family has suﬀered many losses. Were it not for him. I certainly would not be at this point • So I thank all y’all. Dr. cool air and patience. Alex seems to have an uncanny foresight into peoples capabilities and personalities creating an interesting blend of skills. as it seemed my instrument of choice was not a common NMR tool. I only hope to leave something behind for this group to take to the next stage. Were it not for all friend and family. It has been a privilege to have had the ability to explore the capabilities of the CPU even if it was not on the main research track of the group. One cannot forget the friends as well. Sir Wright. yet still has the strength to support my own endeavors. During my stay here. Brown and ma’am Shirl have been around for many ages and are always a breath of clean.x Ment Sadly. I was not able to work with many others in the lab. however crazy and obnoxious they made me act towards them. ideas. P. . Prof. S We must not forget those folks that have constantly dealt with the emotional sideshow that is grad school. For this I thank Alex Pines. this exploration and assembly would not have been possible.
The basic function of numerical simulations is to provide insight into theoretical structures. and knowledge about many systems from nuclear reactions and global weather patterns to describing bacteria populations and protein folding. Its use in science comes from the necessity to extend understanding where analytic techniques fail to produce any insight. coding language. Now.1 Chapter 1 Introduction Before the arrival of the computer. there are few analytic solutions to Ordinary Diﬀerential Equations (ODEs) in comparison to the massive number that can be generated from simple physical systems. computers and simulations have increased the scale. algorithms. Though many . and especially development cost. There are typically hundreds of ways to tackle numerical problems based on the available computer architecture. This limited the scale of the problems that could be solved. complexity. and to aid in experimental design. For instance. analytic mathematical techniques were the only methods to gain insight into physical systems (aside from experiment of course). physical systems. Numerical techniques are as much an art form as experimental techniques. Nonlinearities in ODEs are extraordinarily hard to treat analytically.
Even though there is this wide spread usage of simulation. Including other toolkits at this level 1 2 The MathWorks.com . The theory usually produces the equations of motion for the system and the simulations task is to evolve a particular system in time. The theory of Nuclear Magnetic Resonance (NMR) is over 50 years[1. Champaign. some execute too slowly. The two largest and most popular toolkits available today are Matlab1 and Mathematica2 .. 4] strong.com Wolfram Research.2 numerical solutions to problems exist. approximation methods and other simpliﬁcation techniques are prevalent. slow.http://mathworks. However. 6]. and the techniques for experimental veriﬁcation are very powerful. others are too complicated for anybody but the creator to use. 100 Trade Center Drive. This leaves the majority of the numerical formulation to the scientist. The equations of motion are well established. 3. The basic scientiﬁc simulation begins with a theory. These two packages provide a huge number of tools for development of almost any numerical situation. Inc. MA 017602098. and have no tools for NMR applications. when an appropriate tool kit can simplify the procedure a hundred fold. 3 Apple Hill Drive. The theory is so well developed that simulations have become the corner stone to which all experimental results are measured[5. 2. Natick. Inc. Much of the advancement in NMR today comes from the aid provided by numerical investigations (to list single references would be futile. but then the users will have to get the basic programs.. Wolfram. This is the perfect setting for numerical simulations. Matheworks. as virtually all NMR publications include a simulation of some kind). there is surprisingly little available to assist in the task. Numerical tool kits are a collection of numerical routines that make the users life easy (or at least easier). they are both costly. and still others are not easily extendable. IL 61820. Of course it is possible to use these two to create almost any other tool kit. http://wolfram.
However. speed. and more importantly. After we describe the tool kit. The third chapter then goes through the various equations of motion for an NMR system in detail. The forth chapter describes most all the possible algorithmic techniques used to solve NMR problems. the presented tool kit here can easily provide a basis to include the rest. The ﬁfth chapter will demonstrate the basic algorithms. not everything can be covered in a single thesis. more importantly. and ﬁnally close with several possible future applications and techniques.3 is next to impossible as is creating parallel or distributed programs. Six chapters will follow this introduction. how it can be used to aid the ever toiling researcher to develop more and more interesting techniques. Here I investigate the eﬀect of massive permutations on simple pulse sequences. . data structures. The next chapter includes a demonstration of a class of simulations now possible using the techniques developed in previous chapters. This thesis attempts to collapse the majority of NMR research into a fast numerical tool kit. and design issues and how to contain them all into one tool kit called BlochLib. The second chapter describes the computational knowledge required to create algorithms and code that achieve both simplicity in usage and. It is these interactions that we need to calculate eﬃciently and provide the abstract interface. we will show how much easier it is to create NMR simulations from the tiny to the large. but because there are over 50 years of mathematics to include.
” This basically says that a Turing machine is a computational machine. This machine can move to any section on the tape. They specify for any given state of the machine and value on the tape. and a table of instructions. This discussion is best begun with the bad deﬁnition of a Turing Machine from MerriamWebster Dictionary. erase that spot and write a new one. The machine can read the current spot on the tape. the value on the tape. “A hypothetical computing machine that has an unlimited amount of information storage. which does not help us at all. and the tape has a ﬁnite number of allowed values. The table of instructions is the more important aspect of the machine. The machine has a ﬁnite number of allowed states. rather then the fundamentals of NMR. What the machine writes and does afterwards is determined by three factors: the state of the machine.4 Chapter 2 Computer Mechanics Contrary to almost every other Pines’ Lab thesis. this discussion will begin with the fundamentals of computation. Imagine a machine that can both read and write along one spot on a one dimensional tape divided into sections (this tape can be of inﬁnite length). what the machine should write on . What Turing really said is something like the following[7].
Both can be made to perform the same task. hardware is typically much faster when optimally designed then software. where as hardware suﬀers the opposite extreme. Our discussions will be limited to software. causes the machine to stop. but in comparison hardware is very hard to make. To be a bit more concrete. multiply).ams.html.org/newinmath/cover/turing. When computers ﬁrst were born. There is no distinction made between hardware (a physical device that performs computations) or software (a set of instructions to be run by a computing device). Once a useful set of instructions is given. Software allows the massive generalizations of particular ideas and algorithms. 1 . A simple example of a two state Turing machine is shown in Figure 2.1. This very general principle deﬁnes all computations. only introducing hardware where necessary. a function is a reference to a set of independent instructions. the machine performs no writing. writing complex programs using just a Turing machine instruction set is very hard and tedious. This particular example does not do much of anything except demonstrate the basic principles of a Turing machine. including a Turing machine multiplication instruction set is at this web address http://www.5 the tape and where the machine should move to on the tape. A function is now born. The lack of an instruction for a possible combination of machine state (B) and tape value (0). if we had some translator take A good place to ﬁnd more Turing machine information. To demonstrate a Turing machines instruction set for even simple operations (like multiplication or addition) would take a few pages. the Turing machine approach was how computer programming was actually performed. and is beyond the scope here1 .e. One can easily see that we should be able represent a function by a simple name (i. and the instructions change the state of the machine and move the machine. however. Of course. In this very simple Turing machine example. we can collapse the instructions into a single reference for another Turing machine to use.
the tape inputs values can be 0 or 1. go into state A move Right. and the machine states can be A or B. go into state B b) Start 0 1 1 0 1 1 0 0 1 1 0 1 1 0 0 1 1 0 1 1 0 0 1 1 0 1 1 0 Machine State: A Machine State: A Machine State: B Machine State: B Halted.. The instruction set is designed to stop because one of the four possible combinations of states and inputs is undeﬁned.1: A two state Turing machine.End Figure 2. B Instructions Set machine state tape value A A B B 0 1 0 1 action move Right. The current machines position is represented by the gray box. go into state B not defined move Right. ..6 a) Our machine the tape 0 1 1 0 1 1 0 Machine States: A.
Every other data type is some combination and construction of the bit. A compiler is such an entity. translates the names into working machine instructions.1 shows the data available to almost all modern high level languages. [8]. we may think.2. . or a bit. but this is simple a generality of an object already handled by C++ and Java. For a history of C++ look to Ref. we could spend much less time and eﬀort to get our computer to calculate something for us. would be an object of objects. For instance a byte is simple the next smallest data type consisting of eight bits. and learned by the user). [9].1. DATA TYPES 7 our function name and write out the Turing machine equivalent. that when the compiler is run. because there is no need for a user to write in the low level machine instruction set.1 Data Types Besides simple functions. For an in depth history of the various languages see Ref. The next level of language would the function of functions. It uses a known language (at least known to the compiler. high level languages also provide basic data types. Table 2. Cobal) were only “words” to “machine–code” translators. A data type is a collection of more basic data types. Until the development of programming languages like C++. Algol. These would translate a set of functions into a series of functions then to a machine code level. The next level. Such a set of functions and actions are now referred to as a class or an object. 2. Programming languages can then be created from a set of translation functions. and the languages C++ and Java are such languages. Compilers and their associated languages are called High Level Languages. many of the older languages (Fortran. where the most basic data type for a computer is a binary value (0 or 1).
Only the basic data types (plus a few more) shown in Table 2. . and creation of more complex types are not allowed. Beyond these basic types.2. most compilers will know how to add an integer and a ﬂoat. For example we can create a complex data type composed of two floats or two doubles. These are what we referred to as objects and are the subject of the next section.1: Basic High Level Language Data Types Name Composition bit None. multiplication. 2. the language also gives one the ability to create their own data types from the basic built in ones. THE OBJECT Table 2. the basic syntax and language is the same. etc. as well as conversion between diﬀerent data types.). Although Fortran has come a long way since its creation in the early 1950s.1 are allowed to be used. In current versions of Fortran. C and most other modern languages. Suppose we wish to have the ability to mix data types and functions: creation of a data type immediately deﬁnes the functions and operations available to it. the compiler knows only how to make functions and to manipulate these data types.2 The Object Scientiﬁc computation has seen much of its life stranded in the abyss of Fortran. addition. the basic block byte 8 bits character 1 byte integer 2 to 4 bytes ﬂoat 4 bytes double 8 bytes 8 The languages also deﬁne the basic interactions between the basic data types.e. For example.2. then we must create the functions that manipulate this data type (i.
[11] as inheritance is an important programming paradigm. It will be assumed that the reader has had some minor experience a very high level language like Matlab. Code Example 2. The implementation determines the speed of the algorithms execution. Given the scientiﬁc need for speed in computation. 2 Look to the Netlib repository. Its saving grace is that it performs almost ideal machine translation. meaning it is fast (few unnecessary instructions are used during the translation). As is turns out. and thus its overall usefulness. that can be used by the name myInt later on. both the algorithmic steps and actual code will be presented. [10] is a good example of many). this all may change soon due to fairly recent developments in C++ programming paradigms.2.2. Another topic to grasp when using C++ is the idea of inheritance. but the reader should look to Ref. • The ﬁrst necessary fact of C++ (and C) is declaration of data types. 2. THE OBJECT 9 The functions and function usage are typically long and hard to read and understand2 . This is not discussed here. Fortran is still the choice today for many applications.2. it is necessary to introduce some syntax.org for many examples of what is claimed here. .1 Syntax Before we can go any further. It will be short and the reader is encouraged to look towards an introductory text for more detail (Ref.1 declares an integer data type. I will try to present actual code for algorithms when possible. The next several paragraphs will attempt to introduce the syntax of C++ as it will be the implementation language of choice for the remainder of this document. much of the algorithmic literature uses “pseudocode” to deﬁne the working procedures for algorithms. Although this usually makes the algorithm easier to understand. it leaves out the details that are crucial upon implementation of an algorithm. However. http://netlib. Where appropriate. Throughout this document.
a name.4.1 Integer declaration int myInt. int b) { return a+b. Code Example 2.3 is a function that adds two integers.2 Function declarations: general syntax Return_T functionname(Arg_T1 myArg1. We do not . • The deﬁnition of functions requires a return type. . Suppose we wanted to create one using a float or an int... A complex number data type is shown in Code Example 2. The above example shows the syntax for both creation of the a data type and how to access its sub elements.2.5. • Templates allow the programmer to create generic data types. For instance in the class complex example in Code Example 2. The diﬀerence between them illustrated in the example in Code Example 2.3 the Return T is the return data type.2. and references are aliases to an address in memory.2. THE OBJECT 10 Code Example 2..3 Function declarations: speciﬁc example int addInt(int a.5. } • Pointers (via the character ‘*’ ) and references (via the character ‘&’) claim to be what they say: Pointers point to the address (in memory) of the data type. Arg T1 through Arg TN Code Example 2. Arg_TN myArgN) are the argument data types. and arguments where both the return type and the arguments must be valid data types as shown in Code Example 2. For example. In code example 2. in Code Example 2. we assigned the two sub elements to a double. • Creating diﬀerent data types can be performed using a class or struct.
double imag. image(0) {} //The constructor defines how to create a complex number //with input values complex(double r. . //here we use the new data type complex myCmx(7. //assign it a value //the ‘*’ now acts to extract the memory // not the address *myPoinerToInt=8. point to this new integer // using the reference myPoinerToInt=&myInt.4). double i): real(r). image(i) {} }. //this will print ‘‘10 10’’ cout<<myInt<<" "<<*myPoinerToInt<<endl. //now when we change ’myInt’ BOTH objects will change myInt=10.real<<"+i"<<myCmx. //this will print ‘‘7+i4’’ cout<<myCmx. //this will print ‘‘4 8’’ cout<<myInt<<" "<<*myPoinerToInt<<endl.2. //make out pointer above.5 Object declaration Syntax class complex{ public: //a complex number contains has two real numbers double real. THE OBJECT 11 Code Example 2.4 Pointers and References //declare a pointer int *myPoinerToInt. //declare an integer int myInt=4.2.imag<<endl. Code Example 2. //The constructor defines how to create a complex number complex(): real(0).
one must code a diﬀerent function for each diﬀerent data type making the creation of general algorithms tedious[12].4). In C++ we can template both classes and the arguments of functions. //here we use the new data type // use a double as the sub element complex<double> myCmx(7. THE OBJECT 12 wish to create a new class for each type. //The constructor defines how to create a complex number complex(): real(0). Code Example 2. Given M data types. In Fortran. and N functions. //this will print ‘‘7+i4’’ cout<<myCmxInt.4). using templates can in principle reduce the O(M × N ) number of procedures in a Fortran environment to O(N + M ) procedures.imag<<endl. image(i) {} }. This template procedure allows the creation of a wide range of generic data types and function that operate over a large range of data types without having to code a diﬀerent function or object for each diﬀerent combination of data types. Type_T i): real(r). // use a int as the sub element complex<int> myCmxInt(7. .6.real<<"+i"<<myCmx.2. Type_T imag.imag<<endl.2.real<<"+i"<<myCmxInt. //this will print ‘‘7+i4’’ cout<<myCmx. instead we can template the class as in Code Example 2. image(0) {} //The constructor defines how to create a complex number //with input values complex(Type_T r.6 Template Objects template<class Type_T> class complex{ public: //a complex number contains has two real numbers Type_T real.
.3 2.imag..real+b. Let us revisit the class complex example and deﬁne the addition operator.7 Deﬁning operators template<class Type_T> class complex{ public: //define the sub elements .3.3. we can move forward to explain the object and the power that resides in a templated object. //define the assignment operator //an INTERNAL CLASS FUNCTION complex operator=(complex a){ real=a.1 Expression Templates Motivations Until recently[13].7. EXPRESSION TEMPLATES 13 Given those simple syntax rules. We also must deﬁne the assignment (‘=’) operator before we can deﬁne an addition operator as shown in Code Example 2.9. C++ has been avoided for scientiﬁc computation because of an issue with speed. Now we can use our addition operator to add two complex numbers. } (and any others we deﬁne) can be nested into a long sequence as shown in Code Example 2.} }. The addition operator Code Example 2. a.real. or operators. imag=a. that deﬁne the mathematics of the object.real. template<class Type_T> complex<Type_T> operator+(complex<Type_T> a..2.imag+b. 2. We have shown how to create an object. but we can also create speciﬁc functions.imag). complex<Type_T> b) { return complex<Type_T>(a..
Another way to perform the same operations shown in Code Example 2.2. then B(result of (B+A)). The order of the list is determined by the syntax. then A+(result of (B(result of (B+A)).3).real<<"+i"<<C. 2. Code Example 2. The expression will be parsed from the last element to the ﬁrst in the sequence. C. using standard mathematical rules (e. C=A+BB+A. EXPRESSION TEMPLATES 14 Code Example 2.8 simple addition complex<double> A(4.9.5). and can be best represented as a stack tree shown in Figure 2. C=A+B.). B(2.g. the compiler writes the appropriate instruction set to complete the operation once the program is run.3.5). When the program is run.2 Stacks We should take note as to what the compiler and the computer are doing when it sees an expression like the one in Code Example 2. Initially the compiler will attempt to translate our mathematical expression into a stack. It is then easy to see that in the process of using the operators we necessitate the . After this stack is created.2. B(2.imag<<endl. //this will print ‘‘6+i8’’ cout<<C. in the code itself as shown in Code Example 2.9 Single operations complex<double> A(4. the machine must go to the bottom of the stack and perform each operation as it works its way up the stack tree.3.10.imag<<endl. //this will print ‘‘8+i10’’ cout<<C. B+A.9 is to follow the exact stack tree. multiplication is performed before addition. A stack is a list with a last–in–ﬁrst–out property. C. etc. ﬁnally C=result of (A+(result of (B(result of (B+A))).3).real<<"+i"<<C. Each step represents a stack step. items inside parentheses are treated ﬁrst.
2. The code examples in Code Example 2.5). For individual data types (doubles. multiplication. The complex functions like sin and cos are now included on the microchips instruction set which then increase the speed of the produced code by reducing the stack tree length. ints.3). EXPRESSION TEMPLATES 15 C=A+BB+A C A B B Figure 2. the ever increasing complexity of microchip architectures are actually creating new instruction sets that give the compiler the ability to. C=A+tmp2. complex tmp2=Btmp1.3 An Array Object and Stacks First we shall deﬁne a templated Vector class so that we can continue our discus sion. The vector class shown in Code Example 2. C. 3 . there is no way around this fact3 . ﬂoats. assign subtract add A use of temporary objects.112. and division of two Vectors. subtraction.3. But for arrays of values. add and multiply two numbers under the same instruction as in a PowerPC chip. and our complex example). for example. 2. B(2. we can potentially create a much more optimal situation. However.10 Code representation of a stack tree complex A(4.12 maintains a list of numbers and deﬁnes appropriate operators for addition. complex tmp1=B+A.11 also gives the deﬁnitions for element There is no easy way to see how such a stack tree can be simpliﬁed.3.2: A simple stack tree Code Example 2.
++i) { data_[i]=fillval. } }. } Vector &operator=(Vector rhs) { if(data_!=NULL) delete [] data_.size(). len_(0){} Vector(int len. } T &operator()(int i){ return data_[i]. } return *this. for(int i=0. EXPRESSION TEMPLATES 16 Code Example 2.2. data_=new T[rhs. int len_.size()]. for(int i=0.i<len.++i) { data_[i]=rhs(i). public: Vector():data_(NULL). } } //this is the ‘destructor’ or how we free the memory // after we are done with the Vector ~Vector() { if(data_!=NULL) delete [] data_. } int size(){ return len_. . len_=rhs. len_=len.3. T fillval=0) { data_=new T[len].11 a simple Template Vector class template<class T> class Vector{ private: T *data_. } T &operator[](int i){ return data_[i].i<len.
Vector<T> b) { Vector c(a.size()).i<len.i<len. } } .3.size()). for(int i=0. Vector<T> b) { Vector c(a. Vector<T> b) { Vector c(a.2.++i) { c[i]=a[i]+b[i]. } } template<class T> Vector<T> operator/(Vector<T> a.i<len. for(int i=0.size()). } } template<class T> Vector<T> operator(Vector<T> a. for(int i=0.size()). EXPRESSION TEMPLATES 17 Code Example 2.i<len.12 a simple Template Vector operations template<class T> Vector<T> operator+(Vector<T> a.++i) { c[i]=a[i]/b[i].++i) { c[i]=a[i]b[i]. } } template<class T> Vector<T> operator*(Vector<T> a. Vector<T> b) { Vector c(a.++i) { c[i]=a[i]*b[i]. for(int i=0.
however. Vector<double> t1(5). as well as the ﬁnal assignment loop. c(5. our stack representation.15. t3(5). but add clutter to the code.size(). and this example is an accurate representation of the expression d=c+b+ba.13 as the stack produced code as shown in Code Example 2. int i=0.8). } for(i=0. d(5.i<d.3).3).9). This case is at least a factor of 3 faster then the previous .7). EXPRESSION TEMPLATES 18 access (the operator()(int) and operator[](int) ) as wells as a way to determine how long the Vector is (the int size() function). In general. c(5.8). d=c+b+ba. t2(5). } could have both saved the temporary vectors (t1. An experienced programmer could easily reduce everything to a single loop requiring no temporary vectors as shown in Code Example 2.size().i<d.9). so they will be left out here.++i) { t1[i]=b[i]a[i]. b(5.13. t2. In the example in Code Example 2. b(5.14.++i) { t3[i]=c[i]+t2[i].14 a simple vector expression as it would be represented on the stack. we can also write the example in Code Example 2.13 a simple vector expression Vector<double> a(5.size().7). } for(i=0.2.3.size(). Such checks are easy to implement. } for(i=0. Vector<double> a(5. Using Code Example 2. this optimization is not possible for the compiler to see. d(5.++i) { t2[i]=b[i]+t1[i].++i) { d[i]=t3[i].14 we Code Example 2. for(i=0. The destruction (the ~Vector()) function is also important as it frees the memory used by the vector. Also note that in the examples there are no error checking on the sizes of the vectors when we perform an operation.i<d. and t3). A simple expression using our new object is shown in Code Example 2.i<d.
size(). Now we can describe the technique in painful detail. EXPRESSION TEMPLATES 19 Code Example 2.2. It is for this reason that C++ has been avoided for scientiﬁc or other numerically intensive computations.13[14]. It uses fact that any template 4 See http://netlib.i<d. they must be expressed. b(5. or they must have a real data type replace the template argument (as in our examples of using the Vector class with the double replacing the class T argument). Vector<double> a(5. 2.15 a simple vector expression in an optimal form.7). } case in Code Example 2.14 (it is a even faster the three because we did not have to create the temporaries). This trickery with templates began with Erwin Unruh when he made the compiler itself calculate prime numbers[15]. In fact the Netlib4 is full of such speciﬁc functions.3.org . In fact Erwin showed that the compiler itself could be used as Turing machine (albeit a very slow one).4 Expression Template Implementation A few years ago Todd Veldhuizen developed a technique that uses templates to trick the compiler into creating the optimized case shown in Code Example 2. Because the technique is a template technique.9). it is applicable to many data types without much alteration.3. This technique is called expression templates.8). c(5.15 from a simple expression like the one shown in Code Example 2. One may as well write a single function that performs the speciﬁc optimal operations of vectors (or any other array type). d(5.++i) { d[i]=c[i]+b[i]+b[i]a[i]. for(int i=0. The code that generated the prime numbers can be found in Appendix A. He could do this because for templated objects to be compiled into machine code.3).
A typedef is essentially a short cut to naming data types. Notice that they are binary operations using a single index.17 does.3. For instance if we had a data type that was templated like Vector<Vector<double> > we could create a short hand name to stand for that object like typedef Vector<Vector<double> > VDmat. A new object can be created that performs the binary operation of the two values a[i] and b[i] as shown in Code Example 2. VecBinOp. a single element on the lefthandside (lhs) should be able to be assignable by only one index reference on the rhs. EXPRESSION TEMPLATES 20 augment must be expressed before it can be used. The class. The addition operation has been eﬀectively reduced to a class. . To allow a bit of ease in the discussion we will assume that only one data type. This statement simply means that the entire rhs should be collapsible into one loop. The beginning is already given. meaning they require two elements to perform correctly (the a[i] and b[i] with the index i).17. VecBinOp stands for a VectorVector binary operation. The class shown in Code Example 2. as most other data types are simply extensions to a vector type. 6 A static function or variable is one that never changes from any declaration of the object. in Code Example 2. we will restrict the code to the addition operation.2. is inside the array object5 . Given an arbitrary righthandside (rhs) of a given expression. namely the operator()(int i) function shown in Code Example 2. But the key is in the realization that we require the index for both the lhs and the rhs.16. The reason why the apply function is static6 will be come apparent in the Code Example 2. We will restrict ourselves to the Vector. We can analyze the inside of the operators in Code Example 2. Note that the object is 5 We can perform more generic procedures if we use the typedef. in our discussions. The remaining task is to ﬁgure out how to take an arbitrary rhs and make it indexable by this operator. Second.16 does not give us the single index desired. which means the operation can be templated into another class.12. the double. A better deﬁnition of what we wish to accomplish is given below. as other operations are easily implemented in exactly the same way.11.
} }. public: VecBinOp(V1 &a. and it does not allow us to nest multiple operations (e. class Op> class VecBinOp{ private: V1 *vec1. This addition operator did nothing more then make that code more complex.17 A Binary operator class template<class V1.2. created by creating pointers to the input vectors not copying the vectors.3. } }. the reasons for this will be clear below. we are far from ﬁnished. Our object creates the desired single index operator. however. and actually slowed down the addition operation because of the creation of the new VecBinOp object. double b) { return a + b.16 A Binary operator addition class class ApAdd { public: ApAdd() { } static double apply(double a.g. One may wonder Code Example 2. EXPRESSION TEMPLATES 21 Code Example 2. vec2(&b){} ~VecBinOp(){ vec1=NULL. vec2=NULL. But we are a step closer to realizing our goal and we wish to nest the template arguments and . class V2. why we templated the two vector class V1 and V2 as we know we are dealing with only Vector<double> objects. vec2(i)). } //requires ’Op::apply’ to be static // to be used in this way double operator()(int i) { return Op::apply(vec1(i). We could use the VecBinOp alone. This object takes three template arguments. to create our new addition operator as shown in Code Example 2. V2 *vec2. the two vector types and the operation class. V2 &b): vec1(&a).18. d=a+b+c) with any improvement.
V2 &b) { Vector<double> out(a. we need to create another object that can maintain the binary operation in name only (e.b. V2.3.19. This object can then be passed back to the VecBinOp object as a template argument (the reason why we left the ‘Vector’ template input for VecBinOp as a template argument and not directly assigned it to the Vector). In order to nest the template operations. then use this name to pass to the next operation. This new object gives use the ability to pass an arbitrary expression Code Example 2. The expression is only evaluated when the operator()(int) is called. around as an object. Thus we can delay the evaluation until have an assignment. } not the operations themselves. VecBinOp<V1.ApAdd()).size()). V2.2. EXPRESSION TEMPLATES 22 Code Example 2. ApAdd> addObj(a.++i) { out(i)=addObj(i).size(). Now we can rewrite out addition operator to simply pass back the VecExpr object as shown in Code Example 2. Now the . public: VecExpr(TheExpr &a): expr(&a){} double operator()(int i) { return expr(i)). ApAdd>). class V2> Vector &operator+(V1 &a. for(int i=0. } return out.g. VecBinOp<V1.20. } }. but not evaluating the expression.i<a.18 A bad expression addition operator template<class V1.19 A simple Vector Expression Object template<class TheExpr> class VecExpr{ private: TheExpr *expr. Such an object is shown in Code Example 2.
The ﬁnal step is the evaluation/assignment. but it will more then likely give you many errors because of conﬂicts of data types. log. we can now show in Figure 2.11 we must deﬁne this operator as shown in Code Example 2. EXPRESSION TEMPLATES 23 Code Example 2. sin. we partially express the templates to show that they are only for Vector’s and VecExpr’s. /. ApAdd> > operator+(Expr_T1 &a. Since all the operators return a VecExpr object.Expr_T2.3. ApAdd()). it simply passes a staging expression that we will need to ﬁnd another means to evaluate.21. ApAdd> >(a.20 A good expression addition operator template<class Expr_T1. etc. Now we have any rhs that will be condensed into a single expression. } addition operation does not evaluate any arguments. exp. Expr_T2 &b) { return VecExpr< VecBinOp<Expr_T1. thus the compiler will give you an error. Assignments can only be written internal to the class. Here.2. The best method around this problem is to create a quadruple of operators using the more speciﬁc objects as shown in Code Example 2. Expr_T2> VecExpr< VecBinOp<Expr_T1. we simply need to deﬁne an assignment operator (operator=(VecExpr)).3 what the compiler actually performs upon compilation of an . For instance there is not operator()(int) deﬁned for a simple double number.22. Now that we have a working expression template structure.) where we would create a VecUniOp object.b. *) and unary types (cos. this completes the entire expression template arithmetic for adding a series of vectors. It can also be used for any object as well. Besides the good practice checking the vector sizes and generalization to types other then doubles. so inside of our Vector class in Code Example 2.Expr_T2. It is easy to extend this same procedure for the other operators (. This new addition operator can be used for any combination of Vector or VecExpr objects.
2. //Vector+Vector template<class Expr_T2> VecExpr< VecBinOp<Vector<double>.Vector<double>.Vector<double>. ApAdd> > operator+(VecExpr<Expr_T1> &a.ApAdd> >(a.Vector<double>.VecExpr<Expr_T2>.3. ApAdd> > operator+(Vector<double> &a.ApAdd> >(a.b. } . ApAdd> > operator+(VecExpr<Expr_T1> &a.VecExpr<Expr_T2>. } //VecExpr+VecExpr template<class Expr_T1. VecExpr<Expr_T2> &b) { return VecExpr< VecBinOp<Vector<double>. ApAdd()). } //VecExpr+Vector template<class Expr_T1> VecExpr< VecBinOp<VecExpr<Expr_T1>.b.21 A quadruple of addition operators to avoid compiler conﬂicts. ApAdd()).b. } //Vector+VecExpr template<class Expr_T2> VecExpr< VecBinOp<Vector<double>. VecExpr<Expr_T2> &b) { return VecExpr< VecBinOp<VecExpr<Expr_T1>.b. ApAdd()). ApAdd> > operator+(Vector<double> &a.VecExpr<Expr_T2>. ApAdd> >(a. class Expr_T2> VecExpr< VecBinOp<VecExpr<Expr_T1>. EXPRESSION TEMPLATES 24 Code Example 2. ApAdd> >(a.VecExpr<Expr_T2>. Vector<double>. Vector<double> &b) { return VecExpr< VecBinOp<VecExpr<Expr_T1>. Vector<double> &b) { return VecExpr< VecBinOp<Vector<double>. ApAdd()).
ApAdd> >.3. The degree of matching depends greatly on the compiler and the platform.4 shows a benchmark for performing a DAXPY (Double precision A times X Plus Y) for a variety of languages and programming techniques. VecExpr<Vector<double>. This technique is not limited to vectors but also matrices and any other indexable data type.i<size(). ApAdd> >. VecExpr<Vector<double>.22 An internal VecExpr to Vector assignment operator template<class Expr_T> Vector &operator=(VecExpr< Expr_T > &rhs) { for(int i=0. ApAdd::apply(b(i).3) the results match even better. VecExpr<Vector<double>.++i) { this>operator(i)=rhs(i). The data in the ﬁgure is using gcc3. } return *this. ApAdd> >. You can see from the ﬁgure that the results are comparable to a highly optimized Fortran version. under Linux (Red Hat 7.2. From the ﬁgure it is apperent that if the size of the vector is known and ﬁxed before the code .2. Vector<double>. To show the actual beneﬁt of using the expression templates.3: How the compiler unrolls an expression template set of operations. } d=c+b+b+a add c b b add add a expr1= VecExpr<Vector<double>. all one has to do is change the operator() to the size and the index type desired. ApAdd::apply(b(i). ApAdd> > VecExpr<Vector<double>. Figure 2. Vector<double>.1 under the Cygwin environment. ApAdd> > expr3= VecExpr<Vector<double>. expression such as d=c+b+b+a. Vector<double>. EXPRESSION TEMPLATES 25 Code Example 2. ApAdd> > expr2= d(i)=expr3(i) expr3(i)=ApAdd::apply(c(i). a(i)) ) ) Figure 2.
DAXPY benchmarks in Millions of FLoating Point operations per Second (MFLOPS) for a ﬁxed length expression template vector (‘*’).2. EXPRESSION TEMPLATES 26 DAXPY(a+=const*b) 933 MHz Pentium III Xeon 1200 fixed length Vector expressionVector F77 BLAS nonexpression Vector 1000 800 MFLOPS 600 400 200 0 0 10 10 1 10 10 Vector Length 2 3 10 4 10 5 Figure 2. .2.3. the optimized Fortran 77 routine (‘o’) and the normal non–expression template vector (‘x’). the basic expression template vector (the box). All code was compiled under the Cygwin environment using gcc3.4: Double precision A times X Plus Y.1.
To compute each element in the resulting matrix. not the element–by–element multiplication. Such operations typically require the use of a workspace.4. situations where this simple expression unrolling does not improve the speed. then we can perform even further optimizations using the template structures. 2. [20].1 is shown in Figure 2. and basically the only case in NMR) for our speed test.1. Figure 2.23. so we can perform some speed tests using our simple algorithm.2.1.5 depicts a representation of a matrix multiplication.1. 7 . We will stick to square matrices (the most common case. 17. 18. 19] and exploits the compilers ability to be a Turing machine as in the example in Appendix A. This technique is called meta–programming[16. The element–by–element case is handled well by the expression templates. an entire row of the ﬁrst matrix and an entire column of the second matrix is needed. We can implement a simple matrix multiplication via the Code Example 2.2. OPTIMIZING FOR HARDWARE 27 is compiled. More about template based programming can be found in Ref.2. Consider the matrix multiplication7 . but still maintain the powerful ease and readability of the produced code. The results on a 933 MHz Pentium III using gcc3.6. they require the use of temporary data structures. This type of optimization is the topic of the next section.4 Optimizing For Hardware Expression templates provide a nice technique for reducing complex expressions into a single expression allowing similare speed of a hand produced reduction. however. An example metaprogram for unrolling ﬁxed length vectors is shown in Appendix A. There are. Assume that we have deﬁned a matrix<T> class already. A Basic matrix multiplication takes N 3 operations where the matrix is of A tensor multiplication.
j.4 B K M = M * N .rows().. B1.++j){ c(i. matrix<T> b) { matrix<T> c(a..++i){ for(j=0. for(k=1.0) * b(0.4. } } } return c..cols().j).i<a. OPTIMIZING FOR HARDWARE 28 C=A*B C C2. The sub box indicates the required elements from each matrix to compute one element in the resulting matrix C. for(i=0.4 A A2.4 B2.2 . C=A*B.k. N K Figure 2.j)+=a(i. } .j)=a(i.5: A pictorial representation for the matrix–matrix tensor multiplication.j).2... k<b. b.rows().1 A2.++k){ c(i.j<b.k) * b(k.23 Simple tensor matrix multiplication template<class T> matrix<T> operator*(matrix<T> &a. int i.cols()).cols().. Code Example 2.
complex <ﬂoat> (b) and complex < double > (c) matrix–matrix multiplication (C=A*B).3.3 ATLAS 3. The ATLAS library is enormously faster and approaches the theoretical maximum for the 933 MHz processor of 933 MFLOPS. How does ATLAS actually perform the multiplication this much faster? .6: Speed in MFLOPS of a double(a). Also shown in Figure 2.4 100 200 300 NxN 400 500 600 Figure 2. this size N × N .6 is the matrix multiplication from another library called ATLAS[21] and the algorithm inside Matlab version 5. so those speed tests are not performed. Ci = (Ar ∗ Bi ). OPTIMIZING FOR HARDWARE 29 C=A*B Matrix Multiplication 933 MHz Pentium III a) Double b) complex<float> 800 700 600 MFLOPS Basic Matlab 5. Ci + = (Ar ∗ Bi )). In all cases the ATLAS algorithm performs an order of magnitude better. Matlab does not have a float as a precision value.3 ATLAS 3. Cr + = (Ai ∗ Bi ).2. A complex matrix multiplication is actually 4 separate noncomplex multiplications (Cr = (Ar ∗ Br ).4 500 400 300 200 100 100 200 300 NxN 400 500 600 0 0 100 200 300 NxN 400 500 600 Basic ATLAS 3.4 800 700 600 MFLOPS 500 400 300 200 100 0 0 800 700 600 MFLOPS 500 400 300 200 100 0 0 c) complex<double> Basic Matlab 5.4.
Each element in the data path shown in Figure 2.7 shows a simple generic layout of a Central Processing Unit’s (CPU) data path.4. load. Figure 2.). this can be the diﬀerence in waiting days as apposed to weeks for simulations to ﬁnish.2.4. OPTIMIZING FOR HARDWARE 30 Program Counter Instruction Memory Registers Memory Arithmetic Logical Unit Data Memory Figure 2. The data path is the ﬂow of a single instruction. they simply are designed to show how one can manipulate programs to use the full potential of speciﬁc computer architectures. etc. multiply. The data path shown in Figure 2. However. we must know how the computer functions on a relatively basic level.7: A generic computer data path. save. the computer architecture is a secondary concern with algorithms and designs taking precedent. [22].1 Basic Computer Architecture The Data Path To most programmers. [22]. where an instruction tells the computer what to do with selected data stored in memory (things like add. So before we can continue with the explanation.7 is based on the ﬁgures and discussion in Ref.7 can be implemented in a . we must ﬁrst describe a generic computer. The answer is buried deep in the computer architecture. To get the most optimum performance from a computer architecture. For numerically intense programs. ignoring the architecture can reduce overall performance by orders of magnitude.6 demonstrates clearly that for even simple algorithms. Figure 2. The discussion in the following sections are not thorough by any means. A good place to learn more nasty details is from Ref. 2.
etc.). • Regsiter Memory–This element holds ‘immediate’ data. a network connection. This can be the RAM (Random Access Memory). The data path for each of the various CPU’s can be described in much the same way based on the simple fact that both data and instructions can be represented as numbers. • Program Counter–This element controls which instruction should be executed and takes care of jumps (function calls) or branches (things like if/else statements). OPTIMIZING FOR HARDWARE 31 variety of diﬀerent ways giving rise to the production of many diﬀerent brands (Intel. a Hard disk. . etc. The most important element of control for the programmer is in what order speciﬁc instructions are given. it must be placed into the Register Memory before an operation on it can occur. PowerPC. • Arithmetic Logical Unit (ALU)–This element is the basic number cruncher of the CPU. The immediate data is the data closest to the Arithmetic Logical Unit (ALU) and is the only data that can have any operation performed on it.4. Thus if a data element is stored in the Data Memory. • Instruction Memory–This element holds the number representations of the various instructions the program wishes to perform. A programmer cannot divide two numbers any faster then the data path allows. RISC. each of the elements in the data path above and the instruction set are ﬁxed entities. • Data Memory–The main data memory of a computer. It typically takes in two data elements and performs a bit wise operation on them (like add or multiply). Given a speciﬁc architecture. The Program Counter then gives the correct address inside the Instruction memory of the instruction to execute.2.
but this is in fact taking advantage of pipe lining on the processor. which is an action that is hard to pipeline because of the dependence on a condition. OPTIMIZING FOR HARDWARE 32 Programmer Control There are a number of enhancements to the basic data path described above. For the 4fold unrolled case. • Single Instruction Multiple Data (SIMD)–This is called more generically vector processing where more then two data elements can be operated on in one ALU operation.4.2. another can be accessing the Register Memory. the for condition (i<16) must be evaluated each time before continuing.24). caches provide various levels inside the Data Memory that are closer to the ALU. For instance while one instruction is in the ALU. This 4fold unrolling may look simply like more typing and added confusion about the algorithm. but they are the three major features available to a programmer to enhance the speed of a calculation. Pipelining is easily described in the context of loop unrolling. • pipelines–This enhancement allows the next instruction to be executed before the previous one has ﬁnished. • caches–The closest memory to the ALU is the fastest memory. In the not unrolled case. In almost every modern processor today there are numerous other hardware additions. Many of you may have noticed that in certain codes that there is typically a 4fold unrolling of for/do/while loops (see Code Example 2. the slowest farthest away. The above list is only partial. Thus we can add 4 ﬂoating point number to 4 another in a single instruction rather then the usual method of 4 instructions for each addition of two ﬂoats. the fastest being closer to the ALU. not only can each of the four .
SIMD optimizations are highly system speciﬁc and until recently were only available in super computers like Cray system machines. Some compilers (namely the GNU compiler) perform this sort of loop unrolling automatically when called with optimizations. C[i3]=A[i3]+B[i3].2 lists a few of the basic CPUs and there available SIMD . //length 16 vectors Vector<double> A(16). C(16). consumer CPUs now have these instructions. } //a ‘loopunrolled’ loop for(int i=0. However.++i){ C[i]=A[i]+B[i]. so having a good picture of pipelining is still necessary to achieve optimal throughput.9 shows pictorially how a 128 bit SIMD register can be thought of as 4. } operations be pipelined. Figure 2. These instructions act on vectors worth of data at a time. They require both special data types and special CPU instructions. so writing the fully unrolled loop of the type shown here are becoming a thing of the past. 32 bit data values.i3=i+2.24 A simply loop unrolling to a 4 fold pipe line. OPTIMIZING FOR HARDWARE 33 Code Example 2. C[i4]=A[i4]+B[i4].i<16. B(16).8 shows a pictorial representation of the data path as the loop shown in Code Example 2. Table 2. C[i]=A[i]+B[i].i4=i+3. the harder it becomes for the compiler to unroll them eﬀectively.i<16.4. rather then just two elements at a time. C[i2]=A[i2]+B[i2].24 is run. In recent years.i+=4){ int i2=i+1. if there are more complex data types in the loop or even other branch conditions. but the condition testing is reduced by a factor of four.2. Figure 2. //a standard for loop for(int i=0.
2 ﬂoats Intel Pentium IV SSE2 128 bit 8 ints. As a result programming using SIMD Table 2. { { D E A B A { time D C B A E D C B E D C E D A B . 4 ﬂoats Motorola G4 128 bit 8 ints.8: Pipe lines and loop unrolling data types. 4 ﬂoats AMD K5 3Dnow! 64 bit 4 ints.. 2 ﬂoats AMD K6 3Dnow2! 128 bit 8 ints. 64 ﬂoats . 4 ﬂoats Cray J90 64 bit 4 ints.2: SIMD registers available of common CPUs Architecture SIMD size number of common data types Intel Pentium II MMX 64 bit 4 ints (only int) Intel Pentium III SSE1 64 bit 4 ints.. 2 ﬂoats Fujitsu VPP300 2048 bit 128 ints. but currently most compilers are not able to optimize for these registers..2. It may be up to the compiler to attempt to use the SIMD where it can. time Figure 2. Programming using the SIMD types is almost never portable to other CPUs.. OPTIMIZING FOR HARDWARE 34 Program Counter Instruction Memory Registers Memory Arithmetic Logical Unit Data Memory { { C Loop Standard Operation Test i<16 C[i]=A[i]+B[i] i=i+1 A B C D E wait until test is finished Loop Unrolled Operation C[i]=A[i]+B[i] C[i2]=A[i2]+B[i2] C[i3]=A[i3]+B[i3] C[i4]=A[i4]+B[i4] A B A C B A .4.
2. For large continuous data structure like vectors or matrices. over 5 CPU cycles were wasted doing no work. if each element took multiple cycles simply to retrieve and save. Figure 2. meaning that while the CPU waits for the data element to arrive from memory. calculations would be exceedingly ineﬃcient. In actuality the number is much higher because the data element must be found in RAM then sent back. This simply means that data just accessed will probably be accessed again soon. the data next to the one just accessed will also be accessed soon. The reason for this is based on the ever growing speed diﬀerence between memory access and CPU clock speeds. Caching turns out to be one of the more important aspect in optimizing for modern CPUs. The ﬁnal optimizing technique involves caching.10 shows the various levels of caching . and more then likely. For instance a 2GHz Pentium IV processor can only access the main data memory (RAM) at rate less then 400 MHz. OPTIMIZING FOR HARDWARE 35 32 bit data 32 bit data 32 bit data 32 bit data Operation 32 bit data 32 bit data 32 bit data 32 bit data 32 bit data 32 bit data 32 bit data 32 bit data Figure 2.4. provide a method to increase performance using the spatial and temporal locality of a program. Thus caches tend to load blocks of memory at a time with the hope that the data elements within the block will also be used. however. Caches.9: A 128 bit SIMD registers made of 4–32 bit data values tends to be limited to a speciﬁc CPU and up to the programmer.
if it is in the cache we call this a hit. the entire vector or matrix of interest) here initially. OPTIMIZING FOR HARDWARE 36 Register Memory L1 Cache 8128 bytes 832 kbytes RAM L2 Cache 324096 kbytes 0.e. Our desire is to minimize misses.2. Because the L2 cache is much larger we can place much more data (i.4. then the RAM. Some computers provide Level 3 cache. then place smaller data chunks inside the L1 cache as needed.10: Cache levels in modern Processors available to most computers today. the L2 cache is checked. The key is to do optimal replacements of the block inside the L1 cache. If a data element is not in the cache then we call this a miss. The next level is the actual RAM with the slowest access times but is the largest. Level 2 (L2) caches range in size from 32 kb . To make software as fast as possible. This avoids many as many misses as possible. we only want to operate on those elements. . Simply meaning that when we ﬁll the L1 cache. Level 1 (L1) cache is the smallest ranging is size from 8 kb64 kb but is the fastest with access times very close to the internal CPU Register Memory. A miss can cost diﬀerent amounts depending on which cache level misses. but these are few. If the data is not in the L1 cache.012 Gbytes Figure 2. careful management of the caches must be maintained.4 Mb and is much slower then the L1 cache with access time about a fact of 25 more then the L1 cache. then place the entire block back to the next level and retrieve a new block.
j)+=a(i.++i){ c(i.k have been ﬂipped.k<b.j. template<class T> matrix<T> operator*(matrix<T> &a.++k){ for(j=0. We will do this in a sequential manner. The Code Example 2. //fill with zeros int i. } GNU compiler will rearrange the loops automatically as shown.j) is in the innermost loop as in Code Example 2. matrix<T> b) { matrix<T> c(a. The loop unrolling technique discussed above is also performed by the GNU complier and even better then by hand as it will unroll the higher level loops also.26 we ﬁnd a partially unrolled loop. b.cols(). In Code Examples 2.2.j.++j){ for(i=0.k.j). Here the indexes i.rows(). c=0.cols()). The comparison with the Code Example 2.4.k) * b(k. i<a.j<b.4. I found that using ﬁve fold unrolling was a bit better then the four fold unrolling on the 933 MHz Pentium III.rows(). Here we can simply rearrange the loop such that the most accessed element c(i. } } } return c. The ﬁrst step is to look at the loops in Code Example 2.23.25 is shown in Figure 2. The next level of optimization would be to make sure the L2 cache is completely .25 Simple tensor matrix multiplication with loop indexes rearranged.cols().11.25. for(k=0. Here we demonstrate its eﬀect for completeness sake.2 A Faster Matrix Multiplication We can now develop a method to improve the matrix multiply. OPTIMIZING FOR HARDWARE 37 2. so we cannot show the improvement in MFLOPS for this particular optimization.
cols().rows().j) more then once T tmpBkj=b(k.j)+=a(i. T tmpAi3j=a(i3.++i) { c(i. i4=i+4. OPTIMIZING FOR HARDWARE 38 Code Example 2. b. .j). static int Unrolls=5. T tmpAi4j=a(i4.26 Partial loop unrolling for the matrix multiply.j)+=tmpAi2j c(i3.i<leftover.j)+=tmpAi1j c(i2.k. //read the a(i.k).rows().k). * tmpBkj. //avoid reading the b(k.k) * b(k. * tmpBkj. matrix<T> mulmatLoopUnroll(matrix<T> &a.j)+=tmpAij * c(i1. for(k=0. i3=i+3. matrix<T> c(a. //do the elements that do not fit //in the unrolling for(.k<c.k)’s first into the registers T tmpAij=a(i. c(i.j).i+=Unrolls){ //avoid calculating the indexes twice int i1=i+1.i<c.k).j)+=tmpAi4j } } } return c. * tmpBkj. //figure out how many do not fit in the Pipeline unrolling leftover=c.j.k). i2=i+2.++j){ i=0. T tmpAi1j=a(i1.k).j<c. matrix<T> &b) { int i.j)+=tmpAi3j c(i4. } tmpBkj. leftover. * tmpBkj. T tmpAi2j=a(i2.4.cols() % (Unrolls). 0).cols().2. } //do the rest for(.++k){ for(j=0.cols().
full. For a L2 cache of 1 Mb.26 (‘*’).2. If one looks back to Figure 2. Cygwin. We will halve this number to 20000 doubles as the L2 block size.4. Of course we have 3 matrices to consider so that would drop us to ~42000 doubles per matrix.6a and Figure 2. GNU gcc Basic Partial Loop unrolling 0 100 200 300 NxN 400 500 600 Figure 2. we can ﬁt approximately 125000 doubles.11: MFLOPS of a matrix multiplication: comparison of Code Example 2. but we will need some space for the indexes and other functional elements as well as operating system elements and other programs running as well as the required instruction set. The largest square matrix that will ﬁt into a 20000 data chunk is ~140x140.11 you can see the unoptimized multiply has a performance drop when the matrix size is over 160. This is a result of the L2 cache being unoptimally used on the 1Mb L2 cache of the Pentium III. OPTIMIZING FOR HARDWARE 39 22 20 18 16 MFLOPS 14 12 10 8 6 4 C=A*Bdouble*double. For large matrices we would have to divide the matrix into sub matrices that ﬁt into the L2 cache.25 (solid line) and Code Example 2.933 MHz Pentium III. Each sub–matrix is then added to . we must copy sub–matrices of the larger matrix into smaller matrices that ﬁt into the L2 cache. This assumes that we would have the total L2 cache. In order to realize our blocking technique.
3 because it is a bit long. GNU gcc 100 80 MFLOPS 60 40 20 0 50 100 Partial Partial Partial Partial 150 200 250 300 NxN 350 400 450 500 550 Loop unrolling Loop unrolling+L2 cache Blocking Loop unrolling+compiler Optimizations Loop unrolling+L2 cache Blocking+compiler Optimizations Figure 2. This turns out to be a very .2. Cygwin. the output matrix.12: MFLOPS of a matrix multiplication: comparison of simple loop unrolling Code Example 2. The next level of optimization is L1 cache blocking. This is same as performing a normal matrix multiply as in Figure 2.1. In general the L2 cache is still relatively slow when compared to the L1 cache and thus the speed enhancement is small (a few MFLOPs here).3).12 shows the improvement over not blocking the L2 cache.4. We can turn on the compiler optimizations to see a much more dramatic eﬀect of L2 blocking and this is also shown in Figure 2. Figure 2.26 (solid line) and the L2 cache blocking with unrolling (see Appendix A.933 MHz Pentium III. OPTIMIZING FOR HARDWARE 40 120 C=A*Bdouble*double. The code for performing the L2 cache blocking is shown in Appendix A.1. Compiler optimizations increase the total MFLOPS (line with dots) and the beneﬁt of L2 blocking (dashed line).12.5 if we consider each box a sub matrix rather then one element.
6 is much higher then anything we have shown here. expression templates. For this reason. hardware optimizations. However. there is a large penalty for copying the sub matrix if the copying is not optimized. ATLAS[21] performs this search eﬀectively and this is why its performance in Figure 2. The second. OPTIMIZING FOR HARDWARE 41 hard problem to optimize for many reasons.4. involves an abstract software interface to reduce the number of operations on a stack while still maintaining a highlevel of simplicity for the user. there is not generic abstraction like the expression template technique. Conclusions In this chapter I have given you the tools and methods for constructing highly optimal computer algorithms. There are two essential levels. optimizing for one architecture will most deﬁnitely not work on another.2. or a computer search algorithm to ﬁnd the best one. Being in the L1 cache does not guarantee the highest performance. Because the sub matrices need to be much smaller to ﬁt into the L1 cache size. both code abstraction and hardware optimization are necessary for fully optimal solutions. Each hardware has a diﬀerent memory structure for the L1 cache that needs to be matched exactly to improve performance. Finding an optimal solution would take a single person much tinkering with all of them. The ﬁrst. because the register memory must then be used eﬀectively. All of these factors can then have varying degrees of pipe line optimizations. Because each hardware is diﬀerent. Finally we would need to program in assembly to use the SIMD extensions. . is an art unto itself and should be used where we cannot rely on the compiler to generate the optimized code.
1) Most of the world calls this the Bloch Equation [23]. Both are treated in fundamentally diﬀerent ways both mathematically and numerically. we shall start there. we need to know what sort of mathematics we are dealing with. The basic interaction is easily described by a ﬁrst order diﬀerential equation dM = −γM × B.1 Classical Mechanics The basic tenant of the classical description of NMR is a magnetic dipole inter acting with some external magnetic ﬁeld. can be reduced to two extremes. like most physical systems. Here M is the magnetic . Since classical mechanics is usually a bit more intuitive. As we will show later there are many ‘external magnetic ﬁelds’. 3.42 Chapter 3 NMR Forms Before we can lay down the foundation of a complete NMR tool kit. the Quantum and the Classical. What kind of interactions must we take into account to achieve a real physical model? All of NMR. dt (3.
Oﬀsets The ﬁrst sets of ﬁelds of interest are those that we can apply to the system using electro–magnets. superconducting magnets. An individual spin’s magnetic moment will be called µ. Mz ). or simple coils. and our Bloch equation is dM = −γM × Bz . The gyromagnetic ratio is spin speciﬁc. Various ortho– normal representations can be given to the three components. In most NMR circumstances we apply a very large static magnetic ﬁeld along the z–axis (actually we deﬁne our z–axis about this large ﬁeld).3. a magnetic ﬁeld as a function of position and time. The cross product is representative of a torque which the magnetic moment feels from the external ﬁeld. B is also a 3vector and represents the external magnetic ﬁeld. we will stick to simple Cartesian M = (Mx . t). 3. dt (3. γ is a nuclear spin’s gyromagnetic ratio which converts the ‘Gauss’ or ‘Tesla’ of M × B to a frequency. (3. here.2. m. BLOCH EQUATION MAGNETIC FIELDS 43 moment (sometimes called µ) and is a 3 dimensional vector (a coordinate).3) . Typically superconducting ﬁelds are of the order of Telsa or higher.2 Bloch Equation Magnetic Fields We are interested in any and all magnetic ﬁelds of the most general form B(r. and a normalized bulk magnetic moment will be called little m.2) M is usually considered a bulk property: the entire macroscopic sample of spins add together to produce M. My .
but at the same rate as the magnetization is spinning. A solver would be required to evaluate millions of functions.8) (3.4) (3. r dM dt r (3.3.11) . We wish to satisfy the condition. For a Bz = 1 Telsa we get a oscillation frequency of 42.58 MHz for a proton. we get the analytic solution x y z of o o Mx (t) = Mx cos(γBz t) − My sin(γBz t) o o My (t) = My cos(γBz t) + Mx sin(γBz t) o Mz (t) = Mz (3. Mo ). r (3.5) (3. dM dt = 0. To solve such a fast solution is akin to suicide when a typical NMR experiments can last on order of seconds. The γBz term is very large.9) The magnetization therefore spins around the z–axis. That is. The physics should not change in this new frame but we need to add this new term to the equations of motion dM dM = M × Ωr + dt dt . BLOCH EQUATION MAGNETIC FIELDS 44 This simple equation gives us a three equations of motion dMx dt dMy dt dMz dt = −γBz My = γBz Mx = 0 (3.2.6) If we specify an initial condition with M(0) = (Mo . spin ourselves at the in the opposite direction. we can go into the rotating frame of the ﬁeld.7) (3. making it very ineﬃcient.10) is the term for how Where Ωr is the rotational frequency of the rotating frame and M appears in the rotating frame. Mo . Given that the ﬁeld is static.
Comparing Eq. which give us Ωr dM dt r = −γBz = −γM × Bz − γM × (−Bz ).13) = −γ((Bz + ∆Bz )My − ∆By Mz ) = γ((Bz + ∆Bz )Mx − ∆Bx Mz ) = −γ(∆By Mx − ∆Bx My ) (3. (3. a rather boring frame.12) As you can see we have gone into a frame where nothing evolves. If we then apply to the rotating frame to this reduced form we get dMx dt r dMy dt r dMz dt r = −γ∆Bz My = γ∆Bz Mx = 0.14) If we assume that Bz >> ∆Bi by orders of magnitude. Call the diﬀerence ∆B then our applied ﬁeld.2. where the only terms that remain in a small perturbation are the ones parallel to the axis of the main interaction.11 and Eq.15) . So suppose that our magnetization feels a slightly diﬀerent ﬁeld. we then must add a term that describes this new oﬀset dM = −γM × (Bz + ∆B) dt Looking at the 3–vector of equations of motion. We will also call this truncation of an interaction because we essentially drop some terms from the interaction. = 0 (3. then the only terms that contribute to the observable evolution will be terms with Bz .10 we see we need to rotate counter to the Bz ﬁeld. we want everything to be still in the rotating frame.3. thus eliminating any solo ∆Bi terms in our equations of motion. BLOCH EQUATION MAGNETIC FIELDS 45 In other words. 3. This approximation is sometimes called ﬁrst order perturbation theory. 3. dMx dt dMy dt dMz dt (3.
1. 3. If the sample is not under the inﬂuence of a large external ﬁeld. instead of the usual ‘Radio Frequency Pulses’ because the ‘Radio Frequency’ applies only when there is a large Telsa external ﬁeld already applied on the system.14. Oﬀsets are usually independent of time. as we showed in section 3. If we are under the inﬂuence of a large external ﬁeld. I describe them here as magnetic pulses. First lets assume that we can make our applied ﬁeld time dependant. If we cannot assume that Bz >> ∆Bi . So we need some other way to use an applied ﬁeld to give us some control. Furthermore if the main static applied ﬁeld is not along the z–axis then we still can reduce the equations of motion to the form shown in Eq. 3.2. then we must use the full form of the equations of motion in Eq.3.3 and we can use the technique in section 4. 3.2. Magnetic Pulses Magnetic pulses are how one can manipulate the magnetization. BLOCH EQUATION MAGNETIC FIELDS 46 As you can see we are back to the form of the equation in Eq.2. then any directed DC (Direct Current) pulse will behave as the external ﬁeld and the spins will evolve under that ﬁeld according to the same equations as section 3. We will call . This does have an analytic solution but it is a bit messy to write here. the ∆Bz terms are much smaller: anywhere from Hz to kHz.1 to solve the problem. In general a magnetic pulse is similar to an oﬀset with the exception that it is applied along an arbitrary direction and for some length of time. a relatively weak DC pulse (all one can muster experimentally) will do nothing unless applied along the same axis as the main ﬁeld.4 except now. In the rotating frame a small oﬀset from the main magnetic ﬁeld will appear to oscillate about the main magnetic ﬁeld axis. All we would observe then is a larger oﬀset.
y (t) sin(Ωz t) dˆ r dt (3.x (t) cos(Ωz t) r B1.y 1. BLOCH EQUATION MAGNETIC FIELDS 47 this ﬁeld B1 by convention. B1 (t) = RBr (t) 1 where R is a rotation matrix is given by the solution to Eq. We have already made the assumption that our Bz was much larger then anything else. 1 r (3. We wish that the rotated B1 (t) (calling it Br (t)) appear in the 1 rotating frame and not become truncated.2. the solution given in ˆ (3. a resonance condition). Then our equation of motion in the nonrotating frame become dM = −γM × (Bz + B1 (t)). . 3.19) (3.7 with M → r and Bz → Ωz . but now we have an added complication.x r B1.17) Now we can express the nonrotating frame B1 ﬁeld as the rotating ﬁeld multiplied by the reverse time dependant rotation around the z–axis.z (t) r B1. it must be rotating at the same frequency as the rotating frame (i.20) So we see that in order for the external applied pulse to remain in the rotating frame. cos(Ωz t) sin(Ωz t) 0 R = − sin(Ωz t) cos(Ωz t) 0 0 0 1 thus + B1 (t) = B r (t) cos(Ωz t) − B r (t) sin(Ωz t) 1.18) = r × Ωz .3. so our rotating frame will still be −γBz giving our rotating frame equation of motion as dM dt = −γM × (Br (t)). dt (3.16) We wish to go into the rotating frame to make things numerically simple.e.
B1z ) . Bg = Bg (r. B1y sin(φ). We represent this in the rotating frame as B1 = (B1x cos(φ). the quantity of interest is its derivative with respect to the spatial coordinates which have 9 components. In the rotating frame a perfectly resonant pulse is typically applied perpendicular to the z–axis.23) Because they are spatially varying. t) (3. and can reside any where in the xy–plane.3. If we move oﬀ resonance. 0) (3.21) where φ is a phase factor. which give much more complicated expressions. we still have a time dependence of Ωz t.2. The result is the gradient . thus Br (t) = Br . This most typical magnetic pulse NMR uses is one that is constant in the rotating frame. B1y sin(φ). In the lab frame. A gradient magnetic ﬁeld will be called Bg . (3. Gradients Gradients can be thought of as a combination of magnetic pulses with a spatial dependence. but the result for oﬀ axis rotating frames and other time dependant interactions. BLOCH EQUATION MAGNETIC FIELDS 48 We could have intuitively guessed this result. the B1 vector points out of the xy–plane thus introducing an extra z term B1 = (B1x cos(φ). The nonrotating frame from this point on will be called the lab 1 1 frame.22) Shaped pulses introduce time dependent amplitudes (B1i = B1i (t)) or phase factors (φ = φ(t)) or both. can all be derived the same way as this example.
BLOCH EQUATION MAGNETIC FIELDS 49 tensor G= δBgx δx δBgx δy δBgx δz δBgy δx δBgy δy δBgy δz δBgz δx δBgz δy δBgz δz .27 is not valid for nonlinear gradients.3. Gxx Gxy Gxz = G yx Gyy Gyz Gzx Gzy Gzz (3. The ﬁrst occurs from energy transfer from the system we are interested in to a system outside of our control (usually called the lattice).25) If we apply a linear gradient (the most common NMR situation) then δBgi = const = Gij δj (3. Relaxation Relaxation itself is not a ‘magnetic ﬁeld’ but more a heuristic addition to the equations of motion. There are two fundamentally diﬀerent forms of relaxation.26) and we can get the total ﬁeld along the z–axis via a sum of all the z derivatives times its position hf Bgz = Giz • r = Gxz ∗ x + Gyz ∗ y + Gzz ∗ z (3.27) As you can see this simply acts like a spatial dependent oﬀset. 0.2. Bgz ). the only components of the gradient tensor that contribute to any observed eﬀect are the terms along the z direction hf Bg = (0. 3. This form is usually called longitudinal relaxation or T1 relaxation. If we are not in a high ﬁeld the entire tensor must be used. This basic relaxation is . furthermore. (3.24) In a high magnetic ﬁeld along the z–axis. This phenomenon occurs in almost every physical system where we must separate control to the system of interest from the outside world. the simple formula in Eq.
In a bulk sample (where we have an Avogadros number of spins) this eﬀect manifests itself as a dephasing of any previously inphase magnetization. dt T1 (3. Thus we have a new term in the equation of motion deﬁned as 1 dM = (Mo − M(t)). Because it acts on the plane perpendicular to the equilibrium condition. Mo . If we remain in our Cartesian basis.3. The second form of relaxation is usually an internal phase disturbance relaxation. [24] for more information and the various equations. 1/T1 . The reversibility or irreversibility of the relaxation mechanism is what deﬁnes T2 from T1 .28) In NMR there are many ways to calculate T1 based on the system of study. BLOCH EQUATION MAGNETIC FIELDS 50 the driving force that drives the dynamics of the system back to it equilibrium condition at some rate.2. then at any given x y z time. This type of relaxation is typically called transverse or T2 relaxation. For many computational studies. relaxation will move the magnetization back towards this vector. It is called transverse relaxation because it acts in plane perpendicular to the equilibrium condition. 0. If our equilibrium condition is Mo = (Mo . this interaction can be reversed because it is still within our system (under our control). we have to rotate from this axis to the perpendicular plane. Mo ). all we need to know is this value. The most common case in NMR is the high ﬁeld case. In a many body system there are slight diﬀerences in the local environments of each individual spin cause slightly diﬀerent evolutions between them. the reader is referred to Ref. so T1 relaxation is only applicable to the z z part of our equations. Mo ). Unlike T1 relaxation. where Mo = (0. then we can get two angles from the z–axis to unit .
29) T2 does not ‘return’ magnetization to equilibrium.30) where C is the rotation matrix to take us into the plane perpendicular to the equilibrium axis. I say ‘luckily’. the third angle here would describe the relative rotation of a vector in that plane perpendicular to Mo . For those of you that are paying attention. not just two.2. leads us to the normal z form of the T2 relaxation equations dM −1 = (Mx (t). we need three angles. then rotate the ˆ x and y axis to the M o axis. so our equation of motion should look something like dM −1 = C · M(t) dt T2 (3. where it actually is directionally independent in that plane.3. Luckily for us. However. The standard high ﬁeld NMR case of Mo = (0. My (t). and the rotation about the xy–plane. dt T2 (3. φ as ˆo θ = arccos Mz φ = arctan o Mx o My (3. 0. 0) . to accurately describe a three dimensional rotation. Mo ). θ. cos(φ) sin(φ)cos(θ) 0 cos(φ)cos(θ) C= sin(φ) 0 .31) To get C we can ﬁrst assume that our plane of interest is the xy–plane. it simply removes magnetization. BLOCH EQUATION MAGNETIC FIELDS 51 ˆ equilibrium vector (M o = Mo / Mo ). 0 sin(θ) 0 (3. this third angle is irrelevant as it would place the T2 relaxation along a speciﬁc axis. because there is no way for us to get this third angle.32) .
33) where µo is the permeability of free space (12. Dipole Interaction The dipole interactions is one of the most important to the ﬁeld of NMR. 1/µo ).3. If we are not in a high ﬁeld. The interaction is a spinspin interaction: the dipolar ﬁeld on one spin is felt as a magnetic ﬁeld by its neighbor. The dipolar ﬁeld from a single magnetic moment µ at r is proportional to the cube of the distance away from the spin. BLOCH EQUATION MAGNETIC FIELDS 52 a) b) z y x Figure 3. To get the images in Figure 3. 0. for simplifying (and complicating) spectra.1 in 0. Each shell represents B D • B D at r = 0.1 steps and ˆ where µ = (0. In its most general form the dipolar ﬁeld.1a.. It is also one of the chief mechanisms for T2 type relaxation. we have to ﬁrst transform .566370614 ∗ 10−7 T 2 m3 /J). and basically adding a second dimension on to our normal 1D oﬀset interaction. at position r from a single spin is given by BD (r) = r r µo 3 (µ • ˆ) ˆ − µ 4π (r • r)3/2 (3. B D .2.1: The magnitude of the dipole ﬁeld in no static external ﬁeld (a).1. for in this one interaction we have a method for determination of distances between atoms.7. this give us the ‘dumbbell’ picture of the magnetic ﬁeld as shown in Figure 3. and in a high magnetic ﬁeld along z (b).
In a bulk sample. where this one has N 2 scaling.35 which has a ﬁeld shown in Figure 3.1b D Bx (r) = D By (r) = D Bz (r) = µ o µx 4π µ o µy 4π µ o µz 4π −1 2r−ri 3 −1 2r−ri 3 3ˆ2 −1 z 2r−ri 3 = = = µ o µx 4π µ o µz 4π µo µ z 4π −1 r−ri 3 −1 r−ri 3 3 cos2 θ−1 2r−ri 3 (3. In more interesting simulation we are interested in many spins. If M is not chosen properly the value of B D will grow out of . There are other complications as well. sees a ﬁeld generated by all of its neighbors. Then we get the following sets of equations ˆ shown in Eq. which has total magnetization M . thus every spin. spin j. and every spin has a magnetic D dipole moment.36) where N is the total number of spins and ri − rj is the vector separating the two spins. to get the following equations D Bx (r) = D By (r) = D Bz (r) = µo µz 4π µo µz 4π µo µz 4π 3ˆz xˆ 2r−ri 3 3ˆz yˆ 2r−ri 3 3ˆ2 −1 z 2r−ri 3 = = = µ o µz 4π µ o µz 4π µ o µz 4π 3 cos φ sin θ cos θ r−ri 3 3 sin φ sin θ cos θ r−ri 3 3 cos2 θ−1 2r−ri 3 (3. BLOCH EQUATION MAGNETIC FIELDS 53 the spherical basis. 0. µz ). 3.34) . we must remember that the only terms that survive from the above equations are those that either contribute to the z–axis or those that are invariant to any rotation (terms like µ • µ and µ • r). Here we choose µ = (0. Bi . as N BD j = i=j BD (ri − rj ) i (3. All the previous interaction have been had N fold scaling. but a small volume of spins. This sum is one of the computationally limiting steps as it requires the sum to be calculated for every spin j at every integration step. In the high ﬁeld case.2. and choose a direction for µ.35) . we are not concerned with a single spin magnetic moment.3. Thus we need to calculate the dipole ﬁelds due to the small volume.
For a perfect sphere this constant is 0. M is the sample magnetization.2. called the magnetic susceptibility. The constant in front of M. Inside the sample the total ﬁeld. 3. We can further simply this equation by realizing that the applied ﬁeld H is responsible for the magnetization M M = µo χH (3. D depends on the sample shape.37) where H = is the applied ﬁeld intensity. B. for diamagnetic materials (like water.2.3. for a ﬂat disk it is 1/3. a liquid NMR sample tube) it is 1 (its maximum value). We have made the assumption that the material is isotropic and linear in H. χ is quite . A sample of nuclear spins is a slightly polarized by an applied ﬁeld so it creates a magnetic moment in the sample. and for a long cylinder (i. Also in macroscopic samples.36 becomes an integral. Bulk Susceptibility The next three and ﬁnal ﬁelds I will discuss are all high ﬁeld. The ﬁrst and easiest to understand is the bulk susceptibility. BLOCH EQUATION MAGNETIC FIELDS 54 control. A pictorial representation of this eﬀect is shown in Figure 3. bulk eﬀects simply meaning they are inherently due to the high magnetic ﬁeld and the fact that we are dealing with a macroscopic magnetized sample. the local ﬁeld and will be discussed below. For paramagnetic material this constant is large.38) where χ. These considerations dealing with what M really is will be treated at the end of this chapter. the sum in Eq. and most NMR samples). is related to the sample. this integral is the topic of another eﬀect. All matter when exposed to a magnetic or electric ﬁeld ‘reacts’ to the ﬁeld’s presence by either opposing the ﬁeld or aligning with the ﬁeld.e. is simply a sum of the two ﬁelds B = µo (H + DM) Bapplied µo (3.
Thus our ﬁeld equation becomes. small (of order 10−6 ) and negative. Bbs = µo (1 − Dχ)H. for diamagnetic samples. if we align our magnetization along the ﬁeld. as a consequence. turn oﬀ and on the oﬀset).39) In a high H intensity (by high we mean H >> M ). BLOCH EQUATION MAGNETIC FIELDS 55 B = µ o ( H − D M ) = µo (1 − D χ ) H H M Figure 3. (3.3. However. we would see a slight oﬀset. our rotating frame transformation indicates that only the components of χH that are parallel with H will contribute to any observed eﬀect. The equations of motion are only eﬀected by the magnetic ﬁeld along z and we get these terms in our equations of motion: dMx dt dMy dt dMz dt T = χγMz (t)My T = −χγMz (t)Mx (3.2.2: The magnetization of a sample inside a magneti ﬁeld. If you place our magnetization on the xy–plane. We will discuss this constant more later.e.40) =0 . this eﬀect behaves simply like an oﬀset. on the other hand. Again. we can control M using the magnetic pulses. in a high ﬁeld along z we can turn ˆ on and oﬀ this eﬀect (i. then there is no contributing eﬀect.
2. simply represents the strength of the back reaction ﬁeld. Typically. which apposes our magnetization. still on resonance). Q is dependant on the coil size.43) The constant. a solenoid is placed around the sample that acts both as the ‘magnetic pulse’ and the detection apparatus. This is in essence applying another magnetic pulse as its time dependence is the same as the magnetization’s time dependence (i.42) This new emf . dt (3. Radation Damping Another high ﬁeld eﬀect comes from the actual hardware used in an NMR experiment.41) In a simple solenoid Φ is simply the magnetic ﬁeld B times a constant area. thus we can relate this emf to our Bloch equations dM ∗ Area = emf. We now have a nonlinear equation as this eﬀect depends directly on how much magnetization is present (but in the opposite rotating sense). dt dt dt (3. which states any moving magnetic ﬁeld creates a reaction electric current inside a conductor. d(emf ) d(B rd ) dM =− = −α . which is time dependant. It detects signal using Faraday’s Law. .e. inductance. The changing current/voltage (or electromotiveforce (emf )) is related to the changing magnetic ﬂux.3. BLOCH EQUATION MAGNETIC FIELDS 56 T Mz (t) is the TOTAL magnetization along the z axis at any given time. η. α. Φ as dΦ = emf dt (3. then creates another magnetic ﬁeld (Lenz’s Law). The amount of back reaction depends on a number of physical parameters which can be reduced to two constants called the Quality factor or Q and the sample ﬁlling factor.
We have made the assumption that the back reaction ﬁeld is the same at any point in the sample.44 becomes an integral where Bi (t) → rd Bi (r. BLOCH EQUATION MAGNETIC FIELDS 57 and a slew of other details. and MiT (t).44) = T 1/τr Mx (t)/γ where the 1/τr is related to the coils parameters (1/τr = QηM o γ). so our rotating frame approximation still holds. Obviously the eﬀect is also driven by how much total magnetization exists in the sample.2. the new terms in our equations of motion are dMx dt dMy dt dMz dt T = − τ1 Mx (t)Mz r T = − τ1 My (t)Mz r T T = − τ1 Mx (t)Mx + My (t)My . In macroscopic samples. is the total magnetization at a given time t. t). If this is not the case (which it is not in a real rd experiment due to coil edge eﬀects) then Eq. η is simply how much of the sample ﬁlls the space in the coil. making this sum impossible to . We can ﬁnally write down the radiation damping reaction ﬁeld as rd T Bx (t) = −1/τr My (t)/γ rd By (t) (3. and MiT (t) → MiT (r. This magnetization is only appreciable enough to create any eﬀect in high magnetic ﬁelds. so the reaction ﬁeld is then only applied in the xy–plane. 3. Another key bit of information is that the coils are aligned perpendicular to the main ﬁeld. The trouble with performing this sum is that there is an Avogadros number of them.3. r (3.45) Local Field The ﬁnal ﬁeld I will discuss involves extending the bulk susceptibility and the dipole ﬁelds to a more closed form. τr → τr (r). t). the dipole ﬁeld at a position r would be the sum over all the various dipoles in the sample. Assuming a uniform interaction.
28]. There is another special case that is of greater interest . If the sample shape is an ellipsoid (a sphere. Those techniques however are for the most general case and the algorithmic complexity becomes daunting. 2 r − r 3 (3.46 assumes that we are in a high ﬁeld. Eq. cylinder) then the integral in Eq. BLOCH EQUATION MAGNETIC FIELDS 58 perform. If we assume uniform magnetization (M (r) = M ). then in general we would have to evaluate this integral. z 2 r − r 3 (3. and we are left with something that looks very similar to the bulk susceptibility. however. 3. 26]. disk.2.3. nz = 1/3 for a sphere. then the integral reduces to BLF (r) = µo [3Mz z − M] ˆ 4π 1 − 3 cos(θr−r ) 3 dr . nz = 1 for a thin disk). The problem with this technique is that it require many volume cells for the integral to converge properly. which can result in very long function evaluations. There do exist techniques using Fourier transforms to simply this integral[27. This integral can only be integrated analytically for a few special cases. BLF still exists in low ﬁeld situations.47) The remaining term in the integral will give a simple constant which is dependant on the shape of the sample.48) where nz is called the demagnetizing factor (nz = 0 for a long rod. we can reduce the sum to an integral BLF (r) = µo 4π 1 − 3 cos(θr−r ) 3Mz (r )ˆ − M(r ) dr 3 . The dipolar local ﬁeld looks like BLF (r) = µo (3nz − 1) [3Mz z − M] ˆ 6 (3. but with such a large number.47 is soluble[25. that it all but eliminates this eﬀect. 3. If the magnetization is not uniform.46) where θr−r is the angle between the r − r vector and the magnetic ﬁeld. the bulk property of the magnetization is so small. In this case the best we can do numerically is break the integral into a sum over little ∆r3 volume elements.
3.1 also form the basis of the quantum mechanical description. NMR speciﬁcally treats atomic spin states.3 Quantum Mechanics The fundamental ﬁelds discussed in section 3. with two fundamental diﬀerences: instead of ﬁelds. There are a variety of properties associated with the spin states based on the spins quantum number I. s 3 [Mz (ˆ) − Mz ] − s (3. s is the direction of the ˆ modulation. we can write the ﬁeld as BM LF (r) = 3(ˆ · z )2 − 1 s ˆ 2τD 1 [M(ˆ) − M ] . The . s indicate the mean of the magnetization.50) where [. so this interaction scales a N as well. A spin of quantum number I has 2I − 1 possible states.] is a commutator operator ([A..3. 3. H. Like the classical case we must pick a basis in which to describe our .. The most common NMR spin of I = 1/2 has two states.49.49) We will call this the Modulated local ﬁeld.3. rather then a magnetization.. This form of the BMLF does not require any explicit sums. In Eq. of the system which evolve a density operator. we are interested in the Hamiltonian. This equation is called the LiouvillVon Neumann equation. Finally the time constant τd is 1/(µo γM o ) where M o is the total equilibrium magnetization. B] = AB − BA). NMR is a measure of bulk phenomena so simple states are not an accurate description. QUANTUM MECHANICS 59 because it allow manipulation of this ﬁeld[29. and M(ˆ) is the magnetization along the direction of the modulation. ρ] dt (3.. 27]. Upon an applied external gradient which completely ‘crushes’ (the magnetization in the total volume sums to 0) the magnetization along a single direction. dρ = −i [H. ρ.
QUANTUM MECHANICS 60 spin(s). The higher order terms (terms proportional to Ii to some power n > 1) are typically ignored in NMR and will be discussed further in Section 3.1 Rotations Almost all of the mathematics of NMR can be reduced to rotations on quantum operators. Ie never eﬀects any evolution nor is aﬀected by any interactions). ρ = aIe + bIy + cIx + dIz + higher order terms (3.3.51) Iz = 1 0 0 −1 1 2 1 0 .3. Cartesian rotations should be a slight bit faster in execution in the general case. There are two equally important views in treating quantum mechanical rotations: Cartesian based rotations and spherical tensor rotations.52) Usually. we work in a reduced basis. Ie = 0 1 The density operator has four possible states or linear combinations of any of the above 4 operators.3. and non–manipulable set of states (the identity operator. 3. here we choose the spin operator basis in a Cartesian frame Ix = 1 2 0 1 1 0 0 i −i 0 Iy = 1 2 (3. Computationally. where we factor out an Ie term which describe a nonpolarized.4. as rotation matrices . From this point on we will mention ρ as the reduced density operator.
γ). Spherical tensor rotations are typically used for theoretical/symmetry based rotational considerations because of their nice symmetry properties. γ rotates x and y about the z –axis into the ﬁnal new rotated state (x . Mathematically.3. there are (2L − 1)2 elements to rotate each of the 2L − 1 eigenvectors . (3. Given the total angular momentum L. this can be represented by a 3 × 3 matrix cos γ sin φ − cos θ sin γ cos φ sin γ sin θ cos γ cos φ − cos θ sin γ sin φ R(Ω) = − sin γ cos φ − cos θ cos γ sin φ − sin γ sin φ − cos θ cos γ cos φ cos γ sin θ sin θ sin φ − cos φ sin θ cos θ . however. y . may be used to treat NMR computationally or theoretically. and z ).7.53) This can easily be generates by taking into account the separate three rotations R(Ω) = Rz (γ)Ry (θ)Rz (φ). The angle φ rotates the xy–plane around the z–axis into new axes x and y .3. y . Cartesian Based Rotations All 3 dimensional rotations can be reduced to three angles Ω = (φ. (3. QUANTUM MECHANICS 61 are all 3x3. Both. then θ around the old y–axis to rotate z–axis creating three new rotated basis x . 3. The most common form for rotation of angular momentum are the Wigner matrix elements [30]. Spherical Tensor Rotations The spherical tensor rotation representation actually comes about by treating symmetry and invariants of angular momentum in quantum mechanics. and z . θ.54) Each Ri has the form shown in Eq. Finally.
Hamiltonians are scalar/energy operators and invariant under a total system rotation. and the m...m (θ) is called the ‘reduce Wigner element’ because the two z–rotation angles φ and γ are easily factored out of the total matrix element.m where l is the rank of the matrix.3. 62 . There is a reduced notation given as ei(mγ+m φ) dl m. m correspond to a particular matrix element. l These matrix elements are usually called Dm. . 1 0 1 1 e−i(γ+φ) cos √ e−iγ sin(θ) 2 θ 2 2 θ 2 2 0 √ −e−iφ sin(θ) 2 1 e−i(φ−γ) sin θ 2 2 √ −e−iγ sin(θ) 2 i(γ+φ) cos θ 2 e 2 cos(θ) √ eiφ sin(θ) 2 e−i(γ−φ) sin (m).1 using the same three angles as in the Cartesian case.56) We can decompose our Hamiltonian into a spherical tensor basis. . and 2 matrix elements.2. Dm.55) where dl m. (3. The reduced Wigner elements for l = 2 are shown in Table 3. For L = 1. Like the Cartesian these rotations matrices can be generated from the individual rotations Rz (γ)Ry (θ)Rz (φ) = R(Ω) = eiIz γ eiIy θ eiIz φ . 1.m (θ) (3.m .3. Almost all NMR interactions are some form of the rank 0.1: Wigner rank 1 rotation elements. m m . we have 9 matrix elements as shown in Table 3. we end up with a . QUANTUM MECHANICS 1 Table 3.
m Tl.. QUANTUM MECHANICS Table 3.58) Using the explicit form of the product we get l l l m = m=−l (−1) Al.−m Tl. It is l contains all the m subcomponents. . m .57) where each implied that l is a spherical tensor basis element and each αl is a complex constant. . So we can rewrite as a tensor product of the two l = Al · T l . m .−m = m=−l (−1)m Al. d2 . sin(θ)2 cos(θ) sin(θ) 2 3 2 3 2 cos(θ) sin(θ) 1+3 cos(2θ) 4 θ θ 2 cos( 2 )sin( 2 ) θ sin( 2 ) 4 θ (1 + 2 cos(θ)) sin( 2 ) θ θ 2 cos( 2 )sin( 2 ) 3 cos(θ) sin(θ) 3 sin(θ)2 2 2 1 θ θ −2 cos( 2 )sin( 2 ) θ (1 + 2 cos(θ)) sin( 2 )2 3 2 cos(θ) sin(θ) 2 θ cos( 2 ) (−1 + 2 cos(θ)) θ 3 θ 2cos( 2 ) sin( 2 ) 3 2 θ sin( 2 )4 θ θ −2 cos( 2 ) sin( 2 )3 3 sin(θ)2 2 m 2 1 0 1 2 − 2 θ 3 θ −2cos( 2 ) sin( 2 ) 4 θ cos( 2 ) Hamiltonian of the form H= l αl l. (3. An important aspect in NMR is that the Hamiltonians can be separated into a spatial tensor component. 63 2 θ cos( 2 )4 θ θ 2 cos( 2 )3 sin( 2 ) 3 2 1 θ θ 2 cos( 2 )3 sin( 2 ) θ cos( 2 )2 (−1 + 2 cos(θ)) 3 2 3 0 2 − 2 3 2 sin(θ) m 2 1 0 1 2 . m.59) .m . .. Al and a spin tensor component Tl .m (3. (3..3.2: Reduced Wigner rank 2 rotation elements.3.. .
In the Cartesian frame these are typically given the labels δx .m = m=−l l Dm . δani (anisotropic) and η (asymmetry).3. and δz in the spherical frame they are given the labels δiso (isotropic).m (3. and in the PAS it is given as AP AS cart δx 0 0 = 0 δy 0 0 0 δz .61) The Cartesian interaction frame is a 3 × 3 matrix. This atomic frame is given the name Principle Axis System (PAS). (3. QUANTUM MECHANICS 64 Each tensor component can be rotated by using our Wigner rotation matrix elements l Al. In the PAS frame the arbitrary interaction in NMR can be reduced to 3 components.60) 3. We can think of the atomic frame as being the diagonal representation of the interaction. δy . and are related via δiso = 1/3(δx + δy + δz ) δani = δz η= δx +δy δz (3.3.62) .m (Ω)Al.3.2 PAS Rotational Frames The Hamiltonians are typically created with an initial reference frame centered on the atom. As soon as we move from this frame via some rotation then elements become mixed combinations of the atomic frame.
53) or a spherical Wigner rotations (Eq. A2. If the sample is a liquid. In a solid powder sample. Amol = R(Ωmol ). 3. Again we need to pick a reference axis by which all molecules are to be rotated.0 = 2 Molecule Frame The next frame is the molecular frame.3.0 = 0 A2.AP AS .±2 = 1 δani η. A2. then rotate each of the atomic interactions to this new frame. The Euler angles used to perform this rotation will be called Ωmol . either by a Cartesian Euler rotation (Eq.63) A1.3. QUANTUM MECHANICS 65 The spherical basis reduced to a sum over the various rank l components as AP AS = A0 + A1 + A2 sph √ A0.±1 = A1.±1 = 0. In a liquid this rotation is unnecessary as usually the time dependence of this rotation (on the order of micro seconds) is much faster then the observable on the NMR 2 m =−2 3 2 δani .60).m . where we have gone past the atomic frame and now look at the various atomic frames relationship to each other on a molecule where we assume the atoms are ﬁxed in space.0 = − 3δiso (3. then there are many diﬀerent orientations relative to the chosen reference axis and they are ‘ﬁxed’ in time.64) 2 Dm. one needs to deﬁne another axis system in the molecular frame. then this particular rotation would be time dependant as all the molecules are rotating in various ways in time inside the liquid. 3.R(Ωmol )−1 cart cart Amol = AP AS + 0 sph Rotor Frame This particular rotation takes the molecule frame into the frame of the physical sample. To create this transformation.m (Ωmol )AP AS 2. (3.
m (Ωrot ) m =−2 (Ωmol )AP AS 2. This assumption is not true for large molecules like proteins or bicelles that have a very slow rotational rate.AP AS .R(Ωmol )−1 . However.R(Ωmol ).R(Ωrot ). We will call the Euler angles that rotate the rotor into the lab frame Ωlab . NMR experiments are performed in a ‘rotor’ (the sample holder) which is aligned in some arbitrary direction.R(Ωlab )−1 cart cart Alab = AP AS + 0 sph 2 m =−2 2 Dm. The ﬁnal set of rotations is then given by Alab = R(Ωlab ). we could simply choose that static frame as the rotor frame and there is then no need to perform this rotation. So the eﬀect of this rotation in a liquid averages away.m 2 (3.m (Ωrot ) 2 m =−2 2 Dm .R(Ωmol ).3.m = AP AS 0 2 + m =−2 2 Dm.R(Ωrot )−1 . however.3.R(Ωmol )−1 . then the rotational average is only partial and must be included to achieve a proper model. many solid–state techniques use the fact that a rotating rotor provides another method of control over the interactions. otherwise.m (3. This frame needs to be included only when the rotor frame moves.R(Ωrot )−1 cart cart Arot sph Lab Frame The ﬁnal rotational frame relates the rotor frame back a chosen lab frame.65) 2 Dm . In solids.AP AS .66) .m (Ωmol )AP AS 2.m (Ωlab ) 2 m =−2 2 Dm . The lab frame is the ﬁnal resting point for all interactions and is static (like the superconducting magnet is static). QUANTUM MECHANICS 66 times scale (on the order of seconds/millisconds). So we call the Euler angles to rotate into this frame Ωrot and this rotation is given by Arot = R(Ωrot ).
51. In the last section. .3: Spherical tensor basis as related to the Cartesian basis for spin i and spin j spin Spherical Tensor Tl.0 I·I i i T1.j) 1 √ 6 1 2 i j 3Iz Iz − Ii · Ij i j i j I± Iz + Iz I± 1 2 i j I± I± 3. there was no mention of any spin system or an NMR system (except of some small enlightenments). the ﬁnal Hamiltonian will be of the form of Eq. 3. 3.0 (i. so in the absence of any ‘rotating frame’ transformation. Applying a large magnetic ﬁeld removes the spherical symmetry of the Hamiltonian in Eq. To show this we need to look at the spherical spin tensors basis (Tl ) shown in Table 3. QUANTUM MECHANICS 67 Table 3.3.3 and how they relate to the Cartesian basis in Eq. we again must address the rotating frame/truncation in the new basis.57.3 The Hamiltonians Now that we know how to move our interaction into any frame we desire. However.±1 T2. the rotations DO NOT eﬀect the ﬁnal energy spectrum of the Hamiltonian.59 holds.±1 Ix ± iIy 2 ± 2 2 T2. 3.j) (i. then the rotation discussion and Eq. 3. we can describe the system Hamiltonians in the PAS frame.±2 (i. The above discussion is general for any Hamiltonian.3.j) T2.m Cartesian Representation i T0.3.0 Iz 1 i 1 i i i √ I = √ T1. so that now the spectrum of the Hamiltonian has a directional dependence.57. If the spatial components can be separated from the other components. Before we will discuss the speciﬁc interactions.
3 only T0.69) In the spherical basis this interaction reduces to i HCSA = δiso (Ii · B) + ACSA. thus the main Hamiltonian has a term proportional to Iz .i T2. in this case Iz . Using the fact that the ﬁrst order perturbation theory only keeps those terms that commute with the main Hamiltonian.68) (3. Like Eq.3 except that we desire an energy term. the Zeeman Hamiltonian is Hzee = γI · B (3.67) Again. Looking at Table 3.3.70) . The electron cloud slightly deforms in a ﬁeld causing shifts in the oﬀset in 3 directions in the PAS.0 2. 3. Chemical Shift Anisotropy The Chemical Shift Anisotropy (CSA) Hamiltonian is caused by the electronic shielding around the nucleus.3.0 and T2. if B is large and static. then all the remain interactions will be truncated with respect to this axis.0 survive this truncation. not a torque. In Cartesian space we still perform the standard I · B. HCSA = Ii · Ci · B where Ci is the chemical shielding tensor on spin i Ci.P AS cart δx 0 0 = 0 δy 0 0 0 δz (3. QUANTUM MECHANICS 68 Zeeman The Zeeman interaction is the one responsible for the symmetry breaking. but with respect to the PAS system. For simplicity B = Bz .0 (3.
0 2Iz Iz (3. This is called ‘weak’ coupling. the other case being ‘strong’.3. it will.72 is the weak coupling limit where we have assumed the high magnetic ﬁeld (and thus the Chemical shifts) are along the z–axis. we can get a rotor frame angular dependence of the frequencies (rad/sec) to be ω csa = 2πδiso + πδani 3 cos2 θ − 1 + η sin2 θ cos(2φ) . and the lab frame rotations. For most NMR the Jcoupling is considered solely isotropic. through bond interaction. Excluding the molecular rotation.3. if the two atoms have huge chemical shift diﬀerences when compared to the J coupling. Below in Eq.73 shows the strong case. (3. i. upon a rotation (where all m components become mixed). but there can easily be an electron cloud distortion like the CSA . The atoms must be inequivalent for one to observe this eﬀect.72) i. so there is an anisotropic component as well. Furthermore.j i j J i j HJ weak = δiso Iz Iz + A2.71) Scalar Coupling Scalar coupling (or J) comes as a 2 atom. QUANTUM MECHANICS 69 Even though the η term does not explicitly appear in the original Hamiltonian. so atoms with the same chemical shift do not have a J.73) . 3.0 3Iz Iz − Ii · Ij (3.j J i j HJ strong = δiso Ii · Ij + A2. Eq. then the Jcoupling is truncated again with respect to the isotropic part of the chemical shifts. There is no equivalent of this interaction in the classical sense because it is a result of the antisymmetry of the electron (a purely quantum mechanical eﬀect). 3.
where as dipoledipoles are on the order kHz. except that we are interested in the relative orientation of the two nuclei spin degree of freedom (Ii .j where ωD is i. 3. but there can be an asymmetry (η) to the gradient. Ij ).33. QUANTUM MECHANICS 70 Dipole Coupling The dipole interaction in a high ﬁeld looks much like Eq. Much like J couplings. there is no part of the total dipolar Hamiltonian invariant under rotations. ˆ For two homonuclear spins the scale of the dipolar interaction is usually the same as the chemical shift. so no chemical shift truncation will occur (Eq.j HD = AD T2.75) γi γj µo 4π ri − rj 3 (3.0 het i. and it only eﬀect nuclei with spin > 1/2.0 = ωD 1 − 3 cos2 θ 2. The heteronuclear coupling is truncated with respect to the chemical shift diﬀerence on the two hetero nuclear spins (Eq.0 2.3. HD hom = i. The dipolar coupling is symmetric about the z–axis. The anisotropic component (the gradient along the z–axis) is 2 Q δz = e2 qQ = 2I(2I − 1)ωQ 3 (3.j ωD 1 − 3 cos2 θ = 2 i j 3Iz Iz − Ii · Ij (3. therefore there will be no η terms. 3. the chemical shift diﬀerence is in the MHz. 3.76) Quadrupole The quadrupolar coupling is due to electric ﬁeld gradients around a single nucleus.j i.77) . Also. For heteronuclear dipole systems.j ωD = i j Iz Iz (3.3.74).0 i. therefore there is no isotropic component.j AD T2. So the µ · r terms switch to Ii · Ij terms.74) i. therefore there is no isotropic component. there are two extremes. If the gradient is 0 (spherical) then the interaction is also 0.75).
1 T2.−1 and T2.2 T2. the same order as our magnetic ﬁeld.2 (3.1 2.80) Computationally.2 T2. Let Q be the quadrupolar Cartesian tensor in the PAS frame as Q= η−1 2 − η+1 2 1 . the second order quadrupole under the many rotations is best done in the Cartesian basis where we only need to multiply 3 × 3 matrices rather then the many 5 × 5 rotations. The second rank quadrupolar Hamiltonian is HQ = 2 = 2 ωQ Iz γBz 2 ωQ γBz AQ AQ 2. so the second order quadrupole is needed for an accurate description.−1 T2.1 T2.2 2 2 AQ AQ 4Ie I(I + 1) − 8Iz − Ie + AQ AQ 2Ie I(I + 1) − 2Iz − Ie 2.−2 2. 2I(2I − 1) (3. QUANTUM MECHANICS 71 where e is the charge of an electron. (3. (3. qQ is the actual gradient value.81) .−2 ). Note that the rank 4 component is obtained from the rank 2 components via rules for tensor multiplication [30].−1 2.1 + AQ AQ 2.0 = ωQ 1 − 3 cos2 θ 1 2. Simulations need only ω Q which can be expressed as ωQ = Q 3δz .0 2 3Iz − Ie (I(I − 1)) .79) The quadrupole interaction tends to be very large.78) The ﬁrst order truncated Hamiltonian is then HQ = AQ T2. The functional form of this interaction can be broken down into a total rank 2 component and a total rank 4 component. and I is the spin of the nucleus.3. Our truncation approximation breaks down. and ω Q is the coupling constant.3. The second order eﬀect is proportional to 2 ωQ γBz and includes contributions of all the commutors of the basic spin tensors that commute with the Zeeman interaction (terms proportional to T2.−2 2.−2 T2.−1 2. on the order of MHz.
a called ‘soft’ pulse. The second case. . is the opposite extreme where the pulse is either applied for long times and/or is relatively weak. 1)2 (3. 2)T2. j) indicate the matrix elements within Q and c4 and c2 are 2 c2 = 2Iz 4I(I + 1) − 8Iz − Ie (3. The ﬁrst are called ‘hard’ pulses where the pulse is very strong (much larger then any other Hamiltonian) and of short duration such that the eﬀective Hamiltonian for this small time is only the pulse. 2)2 where Q(i.1))2 4 + Q(0. the ﬁrst and second order quadrupole can be reduced to HQ = ωQ Q(2. the total Hamiltonian is the system Hamiltonian plus the pulse. and ∆ω1 is the oﬀset. QUANTUM MECHANICS 72 After our series of rotations all the elements will be mixed and non–zero.3.84) where ω1 = γj B1 . The pulse i) Hamiltonian on spin j is then given as j j j Hj = ω1 (cos φIx + sin φIy ) + ∆ω1 Iz RF (3.0 1 HQ 2 = 2 ωQ γBz c4 Q − γBz c2 (Q(0. 2)2 − Q(1. There are typically two extremes of pulses when we treat the problem computationally. our magnetic pulse can be described simply by giving each direction (ˆ the corresponding spin tensor.83) c4 = 2Iz 2I(I + 1) − Pulses 2 2Iz − Ie Much like the classical case in the rotating frame.0)−Q(1.82) ω2 Q(0. φ a the phase factor.3. In this case.
. i Iz + 1 2 (3.4.i j i Iz Iz − 1 6 − H kB T γBz kB T 3 j. At this point most of NMR makes a fundamental approximation. j.4.. . The indexes i. the ‘high temperature limit’ where kB T >> γBz . The density matrix at equilibrium is given by a simple Boltmann distribution exp Tr Ie − −H kB T − H kB T γBz kB T exp ρo = = i − γBz kB T i i Iz Tr − H kB T γBz kB T 2 j.k k j i Iz Iz Iz +. then all the interactions will have second order aﬀects much like the quadrupole. Much of our quantum discussion above appeared as if we were treating one or two nuclei.1 NMR Initial Conditions Quantum NMR measures bulk magnetic properties of nuclei.i. Not only will they need to be treated individually to second order. We have also assumed the Hamiltonian is simply the Zeeman Hamiltonian as it is the largest of all the other interactions. NMR INITIAL CONDITIONS 73 Other Second Order Eﬀects If the main ﬁeld is weak enough (or the interactions strong enough). not a single nucleus. when in fact the Hamiltonians apply to the bulk sample of identical particles. This results in very messy expressions for the Hamiltonians that require much care to evalutate. The density matrix ρ is the quantum mechanical way to treat many quantum states in terms populations of states rather then explicit eigen–states. but the total Hamiltonian will have to be treated to second order.85) ρo ≈ Tr where kB is Boltzamann constant and T is the temperature. A good reference how to treat the second order components is by Sungsool and Frydman[31]. 3.4 3. k sum over the entire number of spins.3.
However. 40] Mo = N γ2 Bo + 1)Bo =χ = χH 3µo kB T µo 2 I(I (3. This value. certain experimental observations lead Warren[32] to include higher order terms of the density matrix. The quantum density matrix picture describes a polarization diﬀerence.3 ∗ 10−5 . or a single spin. The reduced density matrix is then simply ρo ≈ − γBz kB T i Iz /2 i γBz kB T ≈− 1 2 i Iz i (3. so ignoring any spins that we cannot manipulate or measure (the Identity (Ie ) term). NMR INITIAL CONDITIONS 74 Thus the only term that contributes any component to the density matrix is the ﬁrst term. 39] 3.2 Classical In the classical case we are concerned with the total magnetization of a volume. 3.87) where N is the number of spins and I is the nuclear spin. 34. 38. The magnitude of a single spin’s magnetic moment is simply a nuclear Bohr magneton. and one mole of nuclei is about 4.3. 35. 36] and new imaging techniques[37. This results in an explanation for certain cross peaks in 2D NMR[33.86 is usually the initial condition in most NMR situations. This polarization diﬀerence manifests itself as a bulk magnetization of the form[3.3 shows this magnetization as a function of temperature. which for a Bz of 7 Telsa.86) Since we are dealing with identical particles. Eq. we only have about 1 in every 105 spins that we can control. The deviation from an equal distribution is only 4.4.4. Figure 3.3 ∗ 10−5 . So the Hamiltonians discussed above are valid to this reduced spin matrix as well as the individual nuclei. concentration. . T = 300K. the sum over Iz is easily reduced to a single Iz matrix with the implication that when we eﬀect this term we eﬀect every spin. like the polarization is very small.
3: Magnetization in iso–surfaces versus the applied magnetic ﬁeld. Bo . and the applied magnetic ﬁeld. the temperature T .3. and number of moles. . NMR INITIAL CONDITIONS 75 Figure 3.4.
M .1) This is a standard tensor equation dM =B·M dt (4. We also know that B is always real as it represents a real physical quantity.76 Chapter 4 NMR Algorithms 4.e.1.2) and due to the properties of the cross product we know that B is an antisymmetric matrix (i. 3.1 can be given as a matrix equation Bxx Bxy Bxz dM = −γ Byx Byy Byz dt Bzx Bzy Bzz Mx .1 Classical Algorithms Eigenvalue Problem The most general form for Eq.1 4. where we know that there is some transformation matrix Λ such that B = Λ · Ω · Λ−1 (4. We can easily solve this equation using standard the eigensystem. y Mz (4. Bij = −Bji ).3) .
Λ forms a set of orthonormal basis states equivalent to our Cartesian set.4. M2 .5) ˜ ˜o ˜o ˜o This has the trivial solution given at t = 0. M = (M1 . If we transform our Cartesian set into this eigen–basis via ˜ M = Λ · M. numerically. ˜2 ˜2 o eω3 t ˜ ˜ M3 (t) M3 (4.8) This is a general solution to the Bloch equations. ˜2 ˜ M3 (4.6) The ﬁnal step is then to transform back to our Cartesian basis to get the solutions in a space we can visualize ˜ M(t) = Λ−1 · M(t).9) .1. CLASSICAL ALGORITHMS 77 where Ω is a diagonal matrix. Because of the antisymmetry of B. Ω are the eigen–frequencies and the matrix Λ are the eigen– vectors of B. the equations of motion become ˙ ˜ M1 ω 1 0 0 M = 0 ω ˙ ˜2 0 2 ˙ ˜ 0 0 ω3 M3 (4. is the solution directly in the Cartesian basis of M(t) = eBt Mo (4. to perform a matrix diagonalization (or matrix exponentiation) which for one spin is very simple. rendering this method unusable especially if B is a function of time. It requires. M3 ) of ˜ 1 (t) M o eω1 t ˜ M 1 M (t) = M o eω2 t . as we then must perform a numerical matrix integration of the form t B(t )∂t M(t) = e0 Mo (4. and algorithmically simple. the matrix becomes huge.4) ˜ M1 · M .7) Another equally valid. however for many spins. (4.
Other algorithms treat more then one point of knowledge (a npoint boundary value problem).e. CLASSICAL ALGORITHMS 78 which requires many matrix diagonalizations and is prohibitively numerically expensive for large (i. and spinspin interactions are what make NMR interesting in the ﬁrst place. point(s) to calculate the next one. .1. So it seems we need another method to solve our systems of equations. time). to evolve the system we simply need a diﬀerential equation solver.’ • Explicit–An explicit need only the previous. Looking at the form of the these equations. eﬃciency and usefulness. 4. This is true so long as there are no spinspin interactions.4. There are an abundance of such solvers all of them with certain accuracy. k − 1. here we are concerned only with the ‘Initial Value Problem’ where we have an initial condition and that is all we know at the beginning. hundred to thousands) of spins. where k is some step in the integration (for us k is always t. then correct the ‘guess. These are sometimes called predictor–corrector methods because they must ‘guess’ the k value at least initially (although the guess can be quite educated). ODE solvers come in many varieties.1. For our initial value problem there are a few subclasses of solvers • Implicit–Requires a guess of the k point in order to evaluate a correct point at k. you may think that this very large matrix is simply many 3 × 3 sub matrices along the diagonal. The basic property of an ODE solver is that it marches through time using the approximation that step size is small enough such that the integrating function is constant.2 ODE solvers Becuase the classical time evolution we wish to probe is a ﬁrst order ordinary diﬀerential equation (ODE).
but is the basic model for all others that follow it.11) (4.4. and use two function evaluations. to ) (4. ∆t. Taking 4 function evaluations between to and to + ∆t is a fourth order solver. from to in an explicit fashion as y(to + ∆t) = y(to ) + ∆t ∗ f (y(to ). The typical diﬀerence from one ODE solver to the next is how many other f (yn . You can see the implicit formula Eq. tn ) in between to and to + ∆t are sampled. . Each sampled point would have a series of coeﬃcients associated with it.10) (4. It is essentially worthless in real life applications because it is much too ineﬃcient. to + ∆t). The number of sampled points determines the order of the algorithm. (4. 4.1. • SemiImplicit–Uses a tad of both integration techniques.12) or in an implicit fashion y(to + ∆t) = y(to ) + ∆t ∗ f (y(to + ∆t). we could simply split ∆t in half. t) dt and an initial value y(to ) = x then we can approximate the next point. Given an ODE like dy = f (y.13 requires a guess at the starting value for y(to + ∆t) to be useful. thus the new coeﬃcients would be 1 2 rather then 1.13) The Euler solver is a linear solver. To extend the Euler solver. Both forms demonstrate how most ODE solvers function. CLASSICAL ALGORITHMS 79 k. The simplest Initial Value Problem ODE solver is the Euler solver.
then we could monitor the error by the diﬀerence between the 4th and 5th order results. For example if we had a 4th order algorithm. A stiﬀ ODE is one that has two or more solutions that diﬀer tremendously in there rates of evolution (one is very slow. Implicit methods can usually handle stiﬀ ODEs. • Errors–To know our accuracy in the results. because the time step must be very small. This is typically done by using the information of the next order. . Explicit methods can operate very eﬃciently and without the starting problems so long as the system is not stiﬀ. The small time step is required for explicit solvers because one must be certain not to ‘jump’ over the fast solution. tend to be very hard to start accurately (because they need an initial guess and there is no previous points to get a educated guess from). the other very fast). Before we describe these two solver.1. This results a large algorithmic complexity that can slow down the solvers and other accuracy diﬃculties. CLASSICAL ALGORITHMS 80 The three classes of solvers have their beneﬁts and hardships. This problem is reminiscent of the Nyquist Frequency problem encountered in experimental spectra in NMR where if the time step is too large. It turns out that the NMR classical ﬁelds do not lead to stiﬀ equations. Implicit methods. lets review a few algorithmic points/sections that one should be aware of when treating ODE solvers. so we can freely use any explicit method we desire. higher frequencies appear as much lower frequencies in the resulting spectrum. Explicit methods cannot treat these sorts of equations eﬃciently.4. we must have some way to measure errors. The two basic solvers are the RungeKutta and Richardson Extrapolation. then correct it and continue this prediction–correction scheme until the solution stabilizes. however. Implicit methods perform better because we initially must guess the solution.
This kernel is a standard work horse for most any problem. ..1. . It should simply produce the next point.. It is very robust. • Kernel–The kernel should operate independently of any errors or time step adjusters. for a review of even more ODE solvers one should look to these two references [45] and [46]. and even slightly stiﬀ problems cause this method to fail. 8. for hard problems m should be 12. and so on up to n + m where m can be arbitrary (i. storing the ﬁnal value. The beneﬁt here over the Runge–Kutta kernel is ∆t can be quite large due to the extrapolation. 44]. This kernel evaluates n points between t and t + ∆t (usually equally spaced). 4. CLASSICAL ALGORITHMS 81 • Time step adjustment–In order for an ODE algorithm to be eﬃcient. The 5th order embedded Runge–Kutta[42] algorithm uses a total of 6 function evaluations and has the error estimate ‘embedded’ into the formula. 6. for easier problems m can be 6). stores another point at y(t + ∆t).e. Given these m point at y(t + ∆t) we can ﬁt an m–order polynomial and use our ﬁtted function to extrapolate to the case where m → ∞ (or as ∆tm → 0) . it should be able to take large time steps when the solution is slowly evolving and shrink the time step when the solution is evolving faster. The only problem with this kernel is that it is not nearly as robust as the Runge–Kutta. It then performs n + 2 points. m = 2. That is all the algorithmic complexity we really need to solve the classical form of Bloch equation. but can be slow as it requires 6 function evaluations. A much better kernel is the Bulirsh– Stoer–Richardson–extrapolation method[43. y(t + ∆t). We can simply adjust the step size based on the ratio of a desired accuracy and our error monitor[41]. thus we can minimize many function calls.4.. By embedded we mean it uses all 6 function evaluations to produce a 5th order result and a 6th order error estimate.
As shown in chapter 2. For 10 spin 1/2 nuclei the matrix sizes are 1024 × 1024 equivalent to about 35000 classical spins. The product cannot be performed in any other order then in the series given. to + ∆t) = T exp −i to +∆t (4. This product requires two time consuming operations to calculate.4. the matrix multiplication can be quite time consuming. By time order it is easiest to look at the approximation to the integral solution.2. T is the Dyson Time Ordering operator.14) where U is called the propagator and is deﬁned as U (to . QUANTUM ALGORITHMS 82 4. to Here. not the approximation. we do not wish to use any theoretical approximations to simplify the problem because we are more interested in the exact solution numerically. (4. 4. to + ∆t) = k=1 exp −iδtH to + kδt 2 (4. and maintains that a propagator is multiplied in correct time order.15) H(t )dt .2. 3. Unless other techniques are used to address the problem. In this section.50 is given by ρ(t) = U ρo U −1 . The ﬁrst is the matrix exponential which takes about N 3 operations . even a 10 spin system may prove prohibitively long.16) where δt is much smaller then any time dependence in H.2 Quantum Algorithms Unlike the classical case where everything can be placed in to one ODE solver and the trajectories of N spins can be easily calculated. kδt=∆t U (to .1 The Direct Method The solution to our Eq. the Quantum Mechanical nature prohibits the calculation of arbitrary N .
2 Periodicity and Propagator Reduction Periodic Hamiltonians appear over and over in NMR. Figure 4. called the periodicity factor. ∆t. The typical NMR experiment requires observation of the system at speciﬁc intervals of time.2.2. There are three cases where using periodicity reduces the total number of calculated propagators necessary to have a complete description of the dynamics. The n factor. the second is the matrix product. ∆t.17) where τp is the period. this method is not as bad as it sounds. We run into computational trouble when time dependence is introduced into the system. then the integral vanishes only to produce a constant. Given that every observation is the same distance in time away from the last observation and that the time dependence of the Hamiltonian is periodic. In this section we will go over the few algorithmic tricks we can play with periodic Hamiltonians and their propagators.1 shows the three possible situations given those conditions if we wish to observe the Hamiltonian at some rational fraction of the this periodicity time ( m τp ). Most liquid state or static solid state NMR simulations can be calculated very quickly using the direct method. calling it the ‘Direct Method. In general the m factor. This reduces the problem to a single matrix exponentiation.’ For many cases of NMR simulation. . we can have 3 possible situations. By periodic we mean that H(t) = H(t + τp ) (4. If the Hamiltonian is not time dependant. multiplication factor. another N 3 operations. 4. represents the number of perin ods of length τp that must occur before an observation is synchronous with τp . QUANTUM ALGORITHMS 83 to complete.4. Numerically we can solve any NMR problem in this fashion.
.4. .2....... ..1: Various propagators needed for an arbitrary rational reduction. . U1 Um2 observe m%n=0 t observe (m=n=1) U0 t m=1. n>1 U0 observe Uτp/n1 observe U1 t Figure 4.. QUANTUM ALGORITHMS 84 m>n t=0 Ucarry U0 t=τp Ucarry Um1 .
Consider still that our Hamiltonian is periodic with a cycle of τp . represents the number of the m sub–propagators necessary to advance one observation time step. not arbitrary points. and we can take much larger steps in multiples of τp .1) that will span over a τp . .1) is assumed to been calculated via the direct method. QUANTUM ALGORITHMS 85 called the observation factor.2. n) = 0 The simplest way to use the periodicity is to realize that at each interval n of τp (for a total time of nτp ) the propagator equation reduces to U (nτp ) = (U (τp ))n . all we have to do here is switch the indices and perform an extra observation step at the begining of the sequence.18) This means we only have to evaluate the computationally expensive integral in Eq. The condition is where n > m is also the case where n < m. Point–to–Point. (4. This method I will call ‘Point–to–Point’ (PtoP) as we can only get the dynamics at the speciﬁc point of t = nτp .1. mod (m. In this case there will be a propogator (Ucarry in Figure 4. Rational Reduction.4. The method is well suited for any rotor synchronized pulse sequences where the pulses we apply and the rotor spinning frequency are synchronized. Each sub propagator (Ui in Figure 4.16 n times. m > n (also n > m) Many times one wants to get dynamics inbetween the period and we may be reduced back to using the ‘Direct Method’ for such tasks. Here we only have to store and calculate n propagators. To see how the reduction works ﬁrst consider a normal propagation series in time shown in Table 4. 4.
QUANTUM ALGORITHMS Table 4.2: A reduced set of individual propagators for m = 9 and n = 7 observation time step Propagator series T 1 U6 U5 U4 U3 U2 U1 U0 =U0 T T 2 U4 U3 U2 U1 U0 U8 U7 ∗ U0 =U1 T =U T U2 U1 U0 U8 U7 U6 U5 ∗ U1 3 2 T T 4 U0 U8 U7 U6 U5 U4 U3 ∗ U2 =U3 T T 5 U7 U6 U5 U4 U3 U2 U1 ∗ U3 =U4 T =U T 6 U5 U4 U3 U2 U1 U0 U8 ∗ U4 5 T T 7 U3 U2 U1 U0 U8 U7 U6 ∗ U5 =U6 T =U T U1 U0 U8 U7 U6 U5 U4 ∗ U6 8 7 T T 9 U8 U7 U6 U5 U4 U3 U2 ∗ U7 =U8 T T T 10 U6 U5 U4 U3 U2 U1 U0 ∗ U8 =U0 ∗ U8 86 Consider now the case where m = 9. From this table.2. The hardest calculation is the using . each with a relatively small ∆t of τp /m using the direct method. and we know that every 9 observations the propagator sequence repeats. Ui . and n = 7.2 shows a clearer picture of this situation.1: Time propagation using individual propagators via the Direct Method time step Propagator series 1 U0 2 U1 U0 3 U2 U1 U0 4 U3 U2 U1 U0 5 U4 U3 U2 U1 U0 6 U5 U4 U3 U2 U1 U0 7 U6 U5 U4 U3 U2 U1 U0 8 U7 U6 U5 U4 U3 U2 U1 U0 9 U8 U7 U6 U5 U4 U3 U2 U1 U0 10 U9 U8 U7 U6 U5 U4 U3 U2 U1 U0 11 U10 U9 U8 U7 U6 U5 U4 U3 U2 U1 U0 Table 4. Table 4. Here we require 7 time steps for each observation.4. Then calculate 9 more larger time step propagators (that span a ∆t of n/mτp ) of propagators. we can see that based on the repetition number m we only need to calculate m sub–propagators. UiT using simple matrix multiplication.
.. and this step cannot be avoided. Calculating the ‘backwards’ sequences (U8 .7 ∗U6 ∗ = U8.6 . However the remaining 9 larger propagators require 63 extra matrix multiplications to calculate. this can be an expensive operation. Of course to ﬁgure out this rational reduction of propagators.. then i = 1. (i.. . U2 ∗U1.. To further reduce the number of matrix multiplications. . i = 0. j.2.2. One can look over this entire set of propagators UiT looking at the ones we have already saved from the above operations to see that there will be some optimal set of backwards and forward propagators that reduce the total number of matrix multiplications needed. If the matrices are large. U8 U7 = U8.. i = m − 1) and that no matter what other tricks T we play. so we could save at least 5 matrix multiplications by simply storing the U8 U7 result the ﬁrst time we calculate it.7.1.0 = U2. For instance the sub sequence U8 U7 (a backwards propagator) appears 6 times in Table 4.7 . Call these sequences the ‘forward’ sequences.0 . j. U8.0 . k. because we have stored all the Ui ’s and we can use the same procedure as in calculating the ‘forward’ sequences resulting in another m multiplications. Calculating these results in at least m matrix multiplications.) is also relatively simple.e..) and the ‘backward’ labels (k. simply the indexes i.. we can use that fact that one typically calculates the Ui in sequence (i. Along the way to T calculating U0 we can easily store each sub sequence (U0 . .4.). Calculating the forward propagators seems to at least make intuitive sense. . Given m and n these can all be automatically generated as simple integers. QUANTUM ALGORITHMS 87 the direct method to calculate the initial 9 propagators. no propagators are actually necessary. the ‘forward’ labels. ... U1 U0 = U1. we know that we will have to calculate U6 U5 U4 U3 U2 U1 U0 = U0 . so why did we calculate the seemingly unnecessary backwards propagators? The next step is to realize that inside each UiT are sequences of Ui that repeat many times..). i.
Sub–Point–to–Point. This case is another special case closely related to the PtoP method because it essentially means that n is a factor of τp . it is up to the user to provide the set of Ui .2 shows better picture as to the eﬀectiveness of the rational reduction method. After the minimum is found. Figure 4. the spikes are where m becomes a multiple of n and we get a PtoP method... The problem is then reduced to ﬁnding each forward and backward set of indexes inside each U T set of indices.3 shows the eﬀective reduction in the matrix multiplications using this technique for a range of m and n.i. .2 that performs this task. QUANTUM ALGORITHMS 88 Table 4. In the Figure.4. so each n sub–propagators calculated leads us ..j.2. Table 4. and Uk. From the Table it is easy to see that if the periodicity factor m is much diﬀerent then the observation factor n then the rational reduction does not produce much improvement because we still need to calculate the m sub–propagators. Ui.3: m 4 6 11 21 9 11 13 104 104 Matrix Multiplication (MM) reduction use rational reduction n Original MMs Reduced MMs % reduction 3 12 8 33% 5 30 14 53% 5 55 39 29 % 5 105 89 15% 7 54 21 61% 7 77 41 47% 7 91 55 40% 7 728 692 5% 103 10712 308 97% as can each UiT sequence.2.. m = 1. n > 1. Comparing the number of multiplications required by each set and pick the minimum. A C++ class is given in Appendix A..j. and the algorithm can then generate the basic m propagators of observation.k.
4.2. QUANTUM ALGORITHMS
percent reduction for observation factor n
89
90 80 70 60 reduction (%) 50 40 30 20 10 0 0 20
n=3 n=5 n=7 n=20
40
60 80 Periodicity factor, m
100
120
140
Figure 4.2: Eﬀectiveness of the rational propagator reduction method. back to the τp . This special conditions leaves us only to calculate n sub–propagators and the various combinations for an optimal minimization of the matrix multiplications. For instance if n = 5 then we need to calculate the n sub–propagators (U0 ...U4 ) each spanning a time of ∆t = τp /n = τp /5. While we complete one τp cycle, we collect the sub multiplications shown in Table 4.4 using them in later observation points. This particular case is a typical NMR situation, and will be used later.
4.2.3
Eigenspace
The past methods have only been treating time dependence explicitly. We can
easily do a transformation to treat the frequency domain. This is a natural transformation because our Hamiltonians all have the units of Hz and NMR typically collects data in equal spaced time steps. Because Hamiltonians are Hermetian, we know that all the eigenvalues
4.2. QUANTUM ALGORITHMS
90
Table 4.4: For m = 1 and n = 5 we have this series of propagators necessary to calculate the total evolution observe Point 1 2 3 4 5 6 7 8 ... Propogators T U0 = U0 T U1 U0 = U1 T U2 U1 U0 = U2 T U3 U2 U1 U0 = U3 T U4 U3 U2 U1 U0 = U4 T UT U0 4 T T U1 U4 T T U2 U4 ...
are real and the eigenvectors form an orthonormal basis. The next few methods use the eigenp–basis to potentially remove the explicit time dependence to avoid performing many matrix multiplications, when only a matrix digitalization is necessary.
Eigenvalue propagation One special case involves non time dependant Hamiltonians, or inhomogeneous Hamiltonians. In this case the eigenvectors to not change in time, and we can easily write our Hamiltonian in terms of the eigenvectors and eigenvalues. H = ∆ · Ω · ∆† . (4.19)
where ∆ is a matrix of the eigenvectors, and Ω is a diagonal matrix of the eigenvalues and the † is the adjoint (complex transpose) of a matrix, which for unitary matrices is also the inverse. To propagate forward in time, we can use this property of unitary matrices and matrix exponentials exp[H] = ∆ exp[Ω]∆−1 . Placing this solution back into Eq. 4.15, we get ρ(t) = ∆ exp[−itΩ]∆† ρ∆ exp[itΩ]∆† (4.21) (4.20)
4.2. QUANTUM ALGORITHMS
91
In NMR we can only detect certain components of the density matrix at a given time, those two components are the two axis where our coils sit, x and y . So we can only ˆ ˆ detect Ix and Iy . We then need to project out the component of our detection operator (which from now on will be called Idet ) in the density matrix via the trace operator T r.
† Idet (t) = T r[ρ(t)Idet ]Idet
(4.22)
Using Eq. 4.21, Eq. 4.22, and the cyclic permutation properties of the trace we ﬁnd that
† ˜ ˜ Idet (t) = T r[ρ(t)Idet ]Idet .
(4.23)
˜ where the A = ∆A∆† , a similarity transform. Our recorded signal, S is then just the T r constant ˜ † S(t) = T r[exp[−itΩ]ρo exp[itΩ]Idet ]. ˜ (4.24)
This results in a sum over all over the diagonal of the multiplication inside the result. The multiplied result contains all the diﬀerences in frequencies in Ω with coeﬃcients given by the ρ(t) and Idet . Thus when we calculate this signal, we only need to be concerned with the multiplied quantities along the diagonal, turns a previously N 3 operation into an approximately N 2 operation.
N N
T r[AB] =
i j
Aij Bji
(4.25)
If we assume that we are sampling n steps the evolution in equal times of ∆t, our signal in terms of explicit elements is
N N det Ikj ρo (Φkj )n jk k j
S(n∆t) =
(4.26)
where Φkj is the transition frequency matrix, Φkj = exp(−i(ωk − ωj )∆t). An algorithm for this static case is shown in Appendix A.2.3.
4.2. QUANTUM ALGORITHMS
92
Eﬀective Hamiltonians This method is an extension to the PtoP method. In the PtoP method, we have only one propagator necessary to calculate our evolution. To move in to observation point n of τp it is then necessary to multiply the propagator n times, resulting in n ∗ N 3 operations. We can in principle invert the propagator to ﬁnd the eﬀective Hamiltonian, Heﬀ for this one period using the matrix logarithm. −i log(U) τp
Heﬀ =
(4.27)
This eﬀective form is now time independent on the period. Once we have the eﬀective Hamiltonian, we can easily use this as the Hamiltonian in the static eigenvalue propagation discussed in section 4.2.3. Thus we avoid performing many matrix multiplications. There is a problem using this technique in general in that it has a very narrow frequency bandwidth. The largest frequency it can accurately obtain from the propagator is one that falls within the ±1/τp /2 range. If the real Hamiltonian has frequencies outside this range (which it usually does) then they will be folded into the resulting spectrum and will result in a cluttering of spectral features. This problem leads to amplitude discrepancies in any frequencies of integer multiples of the period, as higher order multiples that are not in the range will be folded on top of ones that are in range. This technique works very well when τp is very short when compared to the real Hamiltonian time dependence because our spectral window is quite large. If one can accurately claim this condition, no other technique can match this one for its speed in calculating the evolution.
4.2. QUANTUM ALGORITHMS
93
Fourier components and the Floquet approximation The Floquet method corrects the folding and amplitude problems of the eﬀective Hamiltonian by working totally in frequency space. But before we can venture further into using frequency domain methods, we need to decompose our Hamiltonian into its Fourier components. Mind you, this is not necessarily where the Hamiltonian is diagonal, but where the Hamiltonian can be broken into a form Hm e−imφ .
m
H=
(4.28)
where φ is some phase factor, and Hm is a Fourier component of H. Given our entire sequence of possible rotations in Eq. 3.66 we have these Fourier components already calculated as well as the phase factor. So the Fourier components of the Hamiltonian are nothing more the rotated l, m components. Assuming that the time dependence is in the φ phase factor, and can be factored (φ = φ(t) = ωt), we can write a Floquet Hamiltonian HF [47, 48, 49, 50, 51] of the form pn HF qm = nωδnm δpq + p Hn−m q (4.29)
where p, q are spin states and n, m and Fourier indices. Both n and m have the range (−∞, ∞) in integer multiples. Thus the Floquet Hamiltonian is an inﬁnity sized matrix
It is still a valuable tool for theoretical studies[52. then we must go to higher and higher N . 56.4. (4. .... 57] and multi–photon eﬀects [3. We do have another simpliﬁcation for this matrix. It turns out that the size of HF necessary to handled most normal NMR spin systems is much to large to be used eﬃciently... so computationally we must set an arbitrary size of N × N . . H0 + 2ω . So the Floquet matrix is a banded matrix... . However the matrix is inﬁnitely sized. . . . but this time we get the raw frequencies and amplitudes that are time independent. 55. H−2 H−1 H0 − ω H1 . 53. Hn . .2. If N is contains all the necessary frequencies to describe our system Hamiltonian then this matrix truncation is valid. Cases like rotational resonance [53.. QUANTUM ALGORITHMS 94 which looks like . H−2 H−1 H0 − 2ω . H−n . We then get N frequencies and amplitudes. So this technique is not used much in computational NMR. .. . . . . It can be used rather powerfully when there are only a few frequencies that describe the Hamiltonian. 58]. .. H−1 H0 + ω H1 H2 HF = H−2 H−1 H0 H1 H2 . if not. . . Eventually we will hit a computational limit as digonalization become prohibitively hard. H1 H2 . so we only have to diagonalize it once.. 54] and simuation of small (12 spins) systems.30) To evolve this matrix we must diagononalize it.. in that the only Hn−m terms that appear in NMR are usually n − m = ±2 for most interactions and n − m = ±4 for second order interactions (the second order quadrupole for instance). . .
3.31) Our signal at each k∆t. and elucidated a bit more in Figure 4.2.2. where ∆t = τp /n.4.3. First. is then following the same discussion as in section .2. Eden shows we can use the sub–propagators along with the total period propagator to remove the frequencies wrapping and amplitude problems of the eﬀective Hamiltonian method.2. as well as observe our system at times inbetween periods.4. I will use the notation as in Table 4.4 Periodicity and Eigen–Space methods This section will cover the blending of both aspects of periodicity in the Hamilto nians and the fact that they are easily decomposed Fourier components. COMPUTE The COMPUTE algorithm was ﬁrst proposed by Eden et. First we note that we can separate the total period T propagator Un−1 can be factored into its diagonal form via T Un−1 = Γe−iτp Ω Γ† (4. let’s assume that we are observing in the regime in our periodic picture where m = 1 and n > 1. al in 1996[59].2 and the Fourier methods discussed in 4. In essence we wish to combine the aspects of both the propagator reductions discussed in section 4. QUANTUM ALGORITHMS 95 4. To describe the algorithm in more detail.
4.k = The next part comes from the realization that the NMR signal is simply of sum of frequencies and amplitudes.2. 4.3: Diagram of one Hamiltonian period and the propagator labels used for the COMPUTE algorithm 4. T Idet. There are no explicit frequencies in the form of the signal in Eq.2.3.33) T † T Γ† Uk Idet Uk Γ.32) T Ie Idet Uk = Tr ΓΓ† ρ T † ΓΓ† I T o Uk det Uk † T Idet Uk Γ T = T r Γ† ρo ΓΓ† Uk T = T r ρT Idet o where we have deﬁned these two important matrices ρT o = Γ† ρo Γ (4. . and using the fact that ΓΓ† = Ie S(k∆t) = T r ρT (k∆t)Idet T T = T r Uk ρo Uk T = T r Ie ρo Uk † † Idet (4.32. QUANTUM ALGORITHMS 96 T U n1 t=0 U0 T t=(n1)τp/n U U U n2 T n1 U t=τp/n 0 U 1 U1 T n2 t=2τp/n Figure 4.
[59] that this function is periodic with k → k + n.s=1 S(k∆t) = (4.34) where k T frs = Idet.37) We now have the the complex amplitudes and the system frequencies exactly for this speciﬁc ∆t. In essence what we have done is 1) use the Eﬀective Hamiltonian method to get the frequencies of within the period and 2) used the sub–propagators to correct these frequencies and amplitudes due to the wrapping problems of the eﬀective approach. We still must calculated the list of transformed detection k T matrices Idet. We did all this only calculating n propagators. and thus can be expanded as a discrete Fourier series as n/2 k frs = j=−n/2+1 aj exp[i2πkj/n] rs (4.36) This form can be easily inverted to give us the complex amplitudes. where ωrs = (ωr − ωs ). When we do this we get N k frs exp[iωrs k∆t] r.4.k and amplitude functions frs resulting in a few more multiplications. T • Calculate all the Uk from the system Hamiltonian.2. T • Invert Un−1 using the matrix logarithm. (4.k rs ρT o sr exp[−iωrs k∆t]. (4. The algorithm proceeds as follows • Choose an n such that n∆t = τp . QUANTUM ALGORITHMS 97 but we are always free to multiply the signal by 1=exp[−i(ωrs )t] exp[i(ωrs )t].35) Eden showed in Ref. . aj rs n/2 aj rs = 1/n j=−n/2+1 k frs exp[−i2πkj/n].
Below we write out explicitly all the rotational sums in the spherical tensor basis if we only rotate the spatial degree of freedom l l l Hm = l m =−l m =−l m =−l e−imγlab dl m.34 using the frs and ωrs k k+n realizing that at k > n that frs = frs .6 I describe methods of integrating over space using various powder averages. k • Using Eq.m m Al m (θmol l Tm (4. we have made minor assumptions about the Hamiltonians we are dealing with: 1) they are periodic.4. The powder average is necessary to properly simulate all the various orientations of a single crystal in a real powder solid sample. I will need to expand the Hamiltonian more then I have thus far.m (θrot ) e−im m e−im γmol dl . 4. o T T • Using the Uk propagators calculate and store all the Idet. ωrs from the eigenvalues of the eﬀective Hamiltonian and use eigenvectors to calculate ρT . and 2) the time dependence can usually be easily factored out of the rest of the Hamiltonian.2.m (θlab ) e−im φlab φrot e−im γrot dl .3. 4.35 calculate and store all the frs . This corresponds the molecule–to–rotor rotation described in section 3. In order to describe the γCOMPUTE algorithm. k • To generate the observed spectrum simply apply Eq. because of a more recent extension to this algorithm which we will discuss in the next section. QUANTUM ALGORITHMS 98 • Calculate frequency diﬀerences. A code example of this is not given. γCOMPUTE Up until now. In the section 4.38) ) e−im φmol .2.2.k matrices.
l l −im ωr t dl m. QUANTUM ALGORITHMS 99 The typical φlab is the rotor spinning rate ωr t. we get a more compact form of the full Hamiltonian.41) Because of the relation of the time shift and the γ angle to previously calculated propagators. Because the high magnetic ﬁeld is assumed cylindrically symmetric. 0) = U ( .m (θlab ) e l m =−l m =−l Hm = e−im γrot dl . we simply reuse the ones we have previously calculated saving us from have to perform a direct method integration step for these angles. t2 + . t2 . To be a bit more explicit.m (θrot ) e−im m φrot ˆ Al m l Tm (4. and n is exactly the same n as discussed in the COMPUTE algorithm.2. We can factor out an ωr from the expression ωr + γrot to get ωr (t + γrot /ωr ). say γrot = c2πτp /n. . (4. in essence the γrot powder angles acts like a shift in in time. In most circumstances ωr = 2π/τp .39) Collecting terms we get l l −im (ωr t+γrot ) dl m.c ωr ωr nωr nωr n (4.m (θrot ) e−im m φrot ˆ Al m l Tm . the γlab is arbitrary and constant. We see the eﬀect of performing a γrot powder average in the COMPUTE framework is simply reordering the sub–propagators Uk . we can write the propagator at k th or t = k/ωr /n division relating to the previous k − 1 propagator as U (t1 . so we can easily choose ˆ 0. We can pick γrot to be some multiple of our periodicity. .4. Condensing the molecule rotation into new Am terms.m (θlab ) e l m =−l m =−l Hm = dl . ) = Uk. γ) = U (t1 + γ γ k − 1 k 2πc . γrot is typically considered constant through the evolution. Rather then recalculating the Uk for each diﬀerent γrot angle. where c is some integer index.40) Now we notice that the rotor spinning rate and the γrot powder angle are in the same exponent of the sum.
Pulse shaping[63]. then become T Uk.(0) U(n−1).(0) † : m = int k+c n − c . 4. [60].42) U(p mod n).(0) m (4. Our total sub–period propagators. In this picture the γ–COMPUTE . 66]. U T . slow molecular motion[64. 4.43) We then use these propagators in the same analysis as in the COMPUTE method to get an improved algorithm which shortens the total simulation time if we need to include γrot angles.4. The ﬁrst is a single crystal experiment where each molecule is aligned in the same direction.2. However.6 Powder Average Integration There are two extremes in solid–sate NMR.2. and chemical exchange[67. 68] are the largest classes of non–periodic Hamiltonians. An implementation of the γ–COMPUTE algorithm is given in Appendix A.c = U(c+k−1 mod n). The direct method is very slow for problems in general. in Ref. This method was ﬁrst elucidated by Hohwy et. There is almost no other technique to perform the simulations other then the direct method.c = U(c+k−1 mod n). n (4.5 Nonperiodic Hamiltonians Non–periodic time dependant Hamiltonians are the hardest problems computa tionally.(0) . 65. there is only one angle that describes a molecular frame to the rotor frame. the γrot as a time shift was realized by both Charpentier[61] and Levitt[62]. al.4.2.2. Molecular motion and chemical exchange are sometimes treated in the fast regime where the time dependence can be averaged away. QUANTUM ALGORITHMS 100 we can remove the c dependence so that Uk. but it is the only one. In this extreme.
4. Instead of sampling the entire sphere represented by θ and φ we can look at the angular dependences of the Hamiltonians.j. where we have many diﬀerent crystallites oriented randomly throughout the sample. π] and the valid range of φ = [0. and each octant has the range as shown in Figure 4. γk ) can be time consuming itself. (4. The γ–COMPUTE algorithm does aid us by attempting to eliminate the γk part of the sum. It is typically assumed that over the billions of crystallites. Using our high ﬁeld expressed Hamiltonian form in Eq 4. Ωrot )dΩrot (4. γk ). both θj and φi remain. The valid range of θ = [0.40 where m = 0 (the only part that survives the truncation) 2 2 H = T0 m =−2 m =−2 2 d2 (θlab ) e−im (ωr t+γrot ) 0.45) Calculation of each S(t. every possible orientation is present. 2π). so we are forced to perform the integral as a discrete sum S(t) = i. θj . however. It also only requires us to compute the observed signal once.46) . making it similar in speed to a liquid NMR simulation. The second extreme is much more common in solid–state NMR. QUANTUM ALGORITHMS 101 algorithm is invalid as there is only one γ angle. φi .2.44) In most cases we do not know the analytical form of S(t.k S(t. Ωrot ). S(t) = S(t.4.m d2 . To get our total signal then requires a sum over all these crystallites over the entire volume. and to get the proper S(t) we may have to perform many evaluations. θj . We can divide our spherical distribution into equal volume octants as shown in Figure 4. Sampling the volume as best as possible using as fewest possible angles is desired to achieve computational eﬃciency.m (θrot ) e−im m φrot ˆ A2 m (4. φi .4.
0≤θ≤π 0 ≤ φ < 2π 0 ≤ γ < 2π • Ci –Inversion symmetric therefore no γ dependence and inversion symmetric therefore requiring only half a sphere. Such cases exist when the eigen–states of the Hamiltonian do not change as the rotor rotates.4. • No Symmetry–requires all 3 angles to be fully integrated over the entire range. QUANTUM ALGORITHMS 102 θ=0 φ=3π/2 φ=0 φ=π φ=π/2 θ=π Figure 4.47) (4. 0≤θ≤ π 2 (4. There are essentially 3 diﬀerent symmetry groups that exist in NMR[69].4: Octants of equal volume of a sphere.2. Speciﬁc Hamiltonians can lead to a simpliﬁcation of this form and diﬀerent symmetry sets.48) 0 ≤ φ < 2π .
Since we cannot sample an inﬁnite number of angles.4. 0≤θ≤ 0≤φ≤ π 2 π 2 (4. 78]. CONCLUSIONS AND COMMENTS 103 • D2h –Cylindrically symmetric and inversion symmetric therefore no γ dependence. an master/slave based MPI[83] implementation backbone is given in Appendix A. 76.1. and cylindrically symmetric requires two octants (where 0 ≤ θ ≤ π). For each three symmetry groups there are a variety of ways of generating points with in the required ranges[70. 80] using Gaussian quadrature[69]. Each new processor should allow for linear scaling of the problem (i. 71. 4.e. Static NMR problems do not require that much time (by comparison to spinning simulations). The choice for a D2h average should be handled best by the Lebedev schemes[81. Powder averaging is the easiest point at which to parallelize a NMR simulation. 73. 72. From my own experience the best powder averaging schemes for no symmetry are the 3DZCW schemes[79. 82]. For the Ci symmetry the 2DZCW schemes seem to work the best. 77.3 Conclusions and Comments Much of the numerical aspects of NMR are treated in the framework of the algo rithms presented in this chapter using the speciﬁc forms of the equations of motion found . To help create such parallel programs. and the molecule frame is the same for all atoms under static conditions[6]. 74.3. 75. n processors reduces the running time by n). we wish to optimally choose the angles.4.49) This case is most prevalent when there is no η term in the Hamiltonians. Inversion symmetric requires only half a sphere. The two together implies one octant.
powder averaging type. CONCLUSIONS AND COMMENTS 104 in chapter 3. and Hamiltonians there are hundreds of diﬀerent simulations one can perform. The basic reason to review all the available algorithms is to be able to choose the proper algorithm. . and sub–routines needed to construct the correct program is more useful in general. interaction set. rotational evolution. The algorithms presented here are the sum total of the ones available to the NMR spectroscopist. Given each diﬀerent algorithm. Instead a package that includes the algorithms. and other parameters that will perform the simulation at the greatest speed and eﬃciency. as a result constructing a master program that performs every possible simulation will not prove fruitful. There is no ‘one’ algorithm that will provide the best answer every time.4.3. data structures. The next chapter will treat the development and implementation of such a toolkit.
the quantum mechanical and classical. the Vector and the Matrix. Having very fast Vectors and Matrix operations will then determine the speed and functionality of the rest of the code that uses them. For this point on. I will now present a tool kit speciﬁc to both classes of NMR. I will take for granted the fact that we have these fast data structures. basically the Complex number.105 Chapter 5 BlochLib 5. Everything else in computational NMR is based on these simple data types.1 Introduction In the chapter 2. I laid down a foundation for a set of highly optimized data structures needed for NMR. This ability to create foundation and foundations upon foundations provide a simple way to construct our total simulation with ease. C++ and objects allow such creation of nice ‘block– boxes’ that performs speciﬁc tasks without any input from the user. .
5.1a. we ﬁnd a pictorial representation of an EE. thus Output. The main function of an experiment is to apply diﬀerent sets of pulse sequences to retrieve diﬀerent sets of information.1b). A basic EE simulation/program is one designed to mimic some experimental condition.2. are used to explore theoretical frameworks and theoretical modeling. Of course there can be much overlap between the EEs and TEs.2.5. even those not assessable . 5. but the basic tenet of a TE simulation is they are a designed to explore the physical properties of the system. The Parameter Parser takes in some set of conditions and sets the various mathematical structures (the Objects) such that a Kernel can perform the proper calculation(s) which produces some sort of data we wish to see. an EE must ﬁrst act as a Parameter Parser.2 Theoretical Evolutions (TE) The other class of simulation.1 Experimental Evolutions (EE) In Figure 5.2. These types of simula tions are some of the simplest to construct and generalize.2 The Abstract NMR Simulation Numerical simulations in NMR (and most other physical systems) can be divided into 2 basic classes: Experimental Evolutions (EEs) and Theoretical Evolutions (TEs). THE ABSTRACT NMR SIMULATION 106 5. The main object of both is some sort of generated data and both typically require some input of parameters. The basic experimental condition is an RF coil that applies RF pulses and provides a detection mechanism within chemical sample. Because of the wide variety of diﬀerent pulse sequences. Theoretical Evolutions (TEs) (see Figure 5.
For this reason EEs can be developed to a large degree of generality on the input parameters (e. are usually complex in the kernels and transparent interfaces are not necessarily their primary goal. on the other hand. and b) Theoretical Evolutions (TE). .5. a) Experimental Evolutions (EE). and so forth. THE ABSTRACT NMR SIMULATION 107 a) Experimental Evolutions P a r a m e t e r P a r s e r Parameters Obj1 ObjN Kernel Output b) Theoretical Evolutions Parameters Obj1 ObjN Kernel1 KernelN Output Figure 5. whereas the TEs can use many diﬀerent Kernels. Their main function is a parameter parser where much attention is given to the interface.2. EEs tend to use a solid Kernel driver. various diﬀerent types of pulse sequences). use the generated data in other kernel.1: Many basic simulations can be divided into two main subgroups. TEs.g. feedback upon itself.
A good overview of the methods desired in NMR can be found in Ref.g. pulse sequences. and ideas used for the theoretical modeling.1). Development of a master TE program proves near impossible simply because of the magnitude of diﬀerent methods and ideas used to explore an arbitrary model. Surprisingly. Currently there is only one TE tool kit available to the NMR spectroscopists. up until this tool kit. THE ABSTRACT NMR SIMULATION 108 to experiments.2. including only radiation damping in a spin system) to see their eﬀect is one such example. NMR experimentation is evolving past the basic high ﬁeld liquid experiment. structures. These tool kits should be a simple starting places for more complex ideas (see Figure 5. rotor angles.5. The best one can do today is to create a tool kit that provides the most common algorithms. EEs are essential to understand or discover any anomalies in experimentally observed data. to develop an understanding and intuition about the systems involved. Simpson seems to be the only EE publicly available. However. there are very few complete NMR EE packages. [84]. In fact. Gamma[86].2. 5. EEs typically require only a few algorithms to solve the dynamics of the systems. The main focus of Gamma is liquid state NMR (the solid state practicalities are becoming developed in later versions). Simulations of singular interactions (e.).g.3 Existing NMR Tool Kits Programs such as Simpson[85] have generalized the EE form of NMR simulation into a simple working structure not unlike programming a spectrometer itself. . the rest of the program is simply a user interface to input experimental parameters (e. etc. Another common usage of EEs is to give the experimenter a working picture of ‘what to expect’ from the experiment.
2. Pulse shaping[92] and multiple rotor angle liquid crystal experiments[93] are also becoming more frequent.5. Exsitu NMR is a new branch currently under exploration[88. Low ﬁeld experiments(see [91] and references there in) are also becoming more common. They will attempt to demonstrate both the generality of the library as well as how to set up a basic program ﬂow from parameter inputs to data output. The following section will discuss some generic classes of NMR simulations that drive the basic design of the BlochLib.3. speed. etc). The tool kit is quite large and the documentation that describes all of its functionality is well over 1000 pages. the incorporation of existing numerical techniques and implementations. and the ability to eas . These objectives then determine the implementation (code language. code structure. 5. [87] and references there in). Following the design overview. I will try in thesis chapter to give a general overview of library itself.4 Why Create a new Tool Kit? Complex interactions like the demagnetizing ﬁeld and radiation damping are be coming important and are best treated classically (see Ref.3 BlochLib Design The design of a given tool kit relies heavily on the objectives one wishes to accom plish. To treat all these newer developments (and to use the fast data structures described in chapter 2) I have created BlochLib to be the next generation NMR simulation tool kit. Solid state NMR (SSNMR) is being used more frequently and with better and better resolution and techniques. 90] requiring detailed knowledge of magnetic ﬁelds in the sample. 89. in order of importance. BLOCHLIB DESIGN 109 5. ease of use. Gamma and Simpson are illequipped to handle these new developments. The key objectives for BlochLib are. several example programs will be discussed.
Most any scientiﬁc endeavor eventually will have to perform data ﬁtting of experimental data to theoretical models. 83] provides a generic interface for parallel programming. There are many diﬀerent types of minimization routines and im . Data ﬁtting is usually a minimization process (usually minimizing a χ2 function). To use both of them eﬀectively one needs to know how to program in parallel.3. several issues are addressed before the major design of BlochLib is discussed. A tool kit called ATLAS (Automatically Tuned Linear Algebra Software)[21] performs these optimizations. This task is not simple. the basic operation is matrix mul tiplication. BLOCHLIB DESIGN 110 ily create both TEs and EEs for NMR in almost any circumstance. So the task becomes one of optimizing a matrixmatrix multiplication. Since that time several fast algorithms have been developed an implemented in a very eﬃcient way. Below. in fact it is probably one of the more complex operations to optimize because it depends dramatically on the systems architecture. The Message Passing Interface (MPI)[95. 5. The same expression template methodology can also by applied to matrices. The Fastest Fourier Transform in the West (FFTW)[94] is one of the best libraries for the FFT. Another relatively recent development in scientiﬁc simulations is the movement away from supercomputers to workstation clusters. The introduction of the fast Fourier transform made possible another class of simulations. One cannot unroll these operations because an evaluated point depends on more then one element in the input.3. However.5. Matrix multiplication is one such operation. there are certain matrix operations that will always require the use of a temporary matrix.1 Existing Numerical Tool Kits For the quantum mechanical aspects of NMR.
The objects Parameters. The Main Kernel drivers need to be able to handle the two distinct classes of NMR simulation the quantum mechanical and the classical as described in Chapter 4.2 Experimental and Theoretical Evolutions for NMR simulations As stated above TEs tend to require more possible conﬁgurations then an EE program. while a TEs are basically open ended in both parameters and kernels (a better assumption about a TE simulation is that one cannot really make any assumptions). Parameter parser. EEs are easily parsed into four basic sections: Parameters. and Data Output. With these extendable objects almost any complex input state can be treated with minimal programming eﬀort. the Main Kernel performs the desired computation. BlochLib is designed to make the Parameters. .3. Main Kernel. One used fairly frequently for its power. With these basic ideas of a TE and EE. multiple types of algorithms is the CERN package MINUIT[96]. 5. BlochLib also has several helper objects to aid in the creation of the parser. and the Data Output decides what to do with any generated data. The Parameter Parser tends to be the majority of programming an EE. the basic design of BlochLib will be described in the next section. The Parameters deﬁne a program’s input. EEs tend to be heavily parameter based using a main driver kernel. speed.1 shows a rough diagram of an NMR simulation for both types (of course it can be applied to many simulation types). Parser and ScriptParse are designed to be extended.5. Main Kernel and Data Output relatively simple for any NMR simulation. Figure 5.3. BLOCHLIB DESIGN 111 plementations. the Parameter parser decided what to do with the parameters. They serve as a base for EE design.
and MINUIT. The ﬁrst levels are the main numerical and string kernels. Aux Libs. Containers. the third levels uses these objects to perform complex manipulations. coords of any type.2 shows the basic layout of the tool kit. the second levels utilize the kernels to create valid mathematical objects. It uses C++ wrappers to interface with MPI. Parameters.3 BlochLib Layout BlochLib is written entirely in C++. and the fourth levels creates a series of modules speciﬁc to NMR for both the classical and quantum sense. Figure 5. BlochLib uses MPI to allow for programming in parallel and to pass the parallel objects to various classes to achieve a seamless implementation in either parallel or serial modes. ATLAS. Finally the Programs section assembles the NMR pieces into functional programs which perform general NMR simulations (like Solid). The Utilities. It is designed to be as modular as possible with each main section shown in Figure 5.3 shows you some speed tests for the basic quantum mechanical NMR propagation operations.2 treated as separate levels of sophistication. The ATLAS library provides the backbone of the matrix multiplication for BlochLib. Figure 5. It also allows the user to put and get the libraries basic data types (vectors of any type.3. vectors of coords of any type) with simple commands to any processor. matrices of any type. calculate arbitrary ﬁelds from coil geometries.3. and a wide range of investigative programs on NMR systems (see Table 5.2). FFTW. and Kernels sections comprise the basic beginning of the tool kit and have little to do with NMR. They form a basic generic data structure framework to perform almost any high performance scientiﬁc simulations. Each code sample (except for Matlab) was compiled using the GNU . BLOCHLIB DESIGN 112 5.5. The Quantum Mechanic and Bloch Equation sections assemble objects that comprise the backbone for the NMR simulations. strings.
Pulses Field calculators Interactions Bloch Solvers Programs Solid Fields 'Others' Figure 5.5. spin operators Spin systems Interactions.3. BLOCHLIB DESIGN 113 Utilities Global functions Constants Matlab I/O VNMR I/O Aux Libs FFTW MPI ATLAS MINUIT Parameters Parameter sets Math parser Script parser Containers Coords Vectors Matrices Grids Kernels Shapes ODE solvers Stencils Quantum Mechanics Isotopes Tensors. . rotations Solid System FID algorithms Bloch Equations Bloch Parameters Gradients.2: The basic design layout of the BlochLib NMR tool kit.
The reason for this discrepancy is discussed in section 5.3. with speciﬁc functions like rotations . Matrix operations are critical for quantum mechanical evolutions and integration. shows normal nonoptimized performance. You may notice that BlochLib’s speed is slower then ATLAS’s even though the same code is used.4. It is critical that the operations on these objects are as fast as possible. Both a. but BlochLib using ATLAS as a base is not far behind. The optimizations of vector operations are critical to performance of classical simulations as the solving of diﬀerential equations take place on the vector level. The coord<> object is speciﬁcally made for 3space representations. with the exception of the matrix multiplication and matrix division which use the ATLAS and LU decompositions algorithms respectively. and matrix classes are all written using expression templates. The containers are the basic building blocks. Matlab’s algorithm is slowed appreciably by this expression because the overhead on its use of temporaries is very high. BLOCHLIB DESIGN 114 compiler (g++) using the same optimizations (O3 ﬁnlinefunctions funroolloops). ATLAS shows the fastest speed. a ‘5 spin 1/2’ matrix is 32 × 32. An existing C++ library. The coord<> object is exceptionally fast and should be used for smaller vector operations. The matrix sizes are incremented in typical numbers of spin 1/2 particles. A ‘1 spin 1/2’ matrix is a 2 × 2. It may be interesting to note that the speed of Matlab’s single matrix multiply (c = a ∗ b) is much better (and close to that of Gamma’s) then the performance shown for (c = a ∗ b ∗ a† ) because of this temporary problem. b. and c are full. and a ‘9 spin 1/2’ matrix is 512 × 512. and allows the usage of the MINUIT algorithms with little or no other conﬁguration. Vector. square. For this reason the coord. Gamma. complex matrices. BlochLib uses FFTW to perform FFTs on its vectors and matrices.3.5.
2).5.3: This ﬁgure shows a speed test for the common NMR propagation expression c = a ∗ b ∗ a† in Millions of Floating point operations (MFLOPS) per second performed on a 700 MHz Pentium III Xeon processor running Linux (Redhat 7. BLOCHLIB DESIGN 115 c=a*b*adjoint(a) 600 ATLAS BlochLib1.3. .0w/ ATLAS BlochLib1.0NO ATLAS Gamma 4.0.5 Matlab 6.0.88 500 MFLOPS/second 400 300 200 100 0 0 10 10 1 log(N) NxN Matrices 5 spin 1/2 10 2 10 3 1 spin 1/2 9 spin 1/2 Figure 5.
However.3. The Grid class consists of a basic grid objects and allows for creation of rectangular Cartesian grid sets.5. The matrix class has several structural types available: Full (all elements in the matrix are stored). Symmetric (same as Hermitian). The Parser object can be used to evaluate string input expressions. and Identity (assumed ones along the diagonal). matrix inverses. QR decompositions. Several basic objects designed to manipulate parameters are given. Diagonal (only the diagonal is stored). These string functions power the parameter parsing capabilities of BlochLib. the Parser object can use that variable in an expression.85. and return the correct value. The object can also use variables either deﬁned globally (visible by every instance of Parser) or local (visible only be the speciﬁc instance of Parser). if a program registers a variable x = 6. and with much less compilation times. like “sin(x)∗3”.4 shows. the Parser can evaluate this expression to be −15. The utilities/IO objects include several global functions that are useful string manipulation functions. The Tridiagonal structure has an exceptionally fast LU decomposition. Hermitian (only the upper triangle of the matrix are stored). any length is allowed. Tridiagonal (only the diagonal. −0. There are also a wide range of other matrix operations: LU decompositions. matrix exponentials and matrix logarithms. For instance if “3 ∗ 4/ sin(4)” was entered. but as Figure 2. and the sub–diagonal elements are stored). Each of these structures has speciﬁc optimized operations. the Vector speed approaches the coord<> for large N. however. the ATLAS matrix multiplication is only used for Full matrices. For examples. GramSchmidt orthonormalization. the super–diagonal. BLOCHLIB DESIGN 116 and coordinate transformations which only function on a 3space. Large parameter sets can be easily grouped into sections and passed between other objects . The Parameters object comprises the basic parameter input capabilities.83.
The shapes themselves can be used in combination (e. Available ODE solvers are .g. The ODE solvers require function generation objects .5. using normal operators and (&& ) and or (). It basically allows the construction of nonrectangular shapes within a Cartesian grid. The Parameters object can output and update speciﬁc parameters. as well as the capability to construct other shapes. One can write matrices. vectors. you can easily specify a grid to contain all the points within a cylinder and a rectangle. The ScriptParse object is used to deﬁne speciﬁc commands to be used in conjunction with any mathematical kernels. A VNMR (Varian) reader and writer of 1D and 2D data is available as well as a XWinNMR (Bruker) and SpinSight (Chemagnetics) 1D and 2D readers are also included. “XYZcylinder && XYZrect”). For instance the XYZcylinder object will remove all the points not included in the cylinder dimensions.3. The next level comprises the function objects. Creation of simple custom scripts can be performed using the ScriptParse object in conjunction with Parser. meaning they require some other object to function properly. of any type to the Matlab ﬁle. and coords. Similar shapes exist for slice planes and rectangles. Several visualization techniques are best handled in the native format of NMR spectrometer software. Data output can be as complicated as the data input. as well as read these data elements from a Matlab binary ﬁle. The XYZshape objects require the Grid objects. Any other text or binary formats can be constructed as needed using the basic containers. These combine a set of rules that allow speciﬁc Cartesian points to be included in a set. The parameter sets can be nested (parameters sets within parameters sets) and separated. Any large amount of data (like matrices and vectors) can be written to either Matlab (5 or greater) format. BLOCHLIB DESIGN 117 in the tool kit using this object.
The semiimplicit BulirschStoer extrapolation method is base on the BulirschStoer extrapolation method for solving stiﬀ sets of equations. All the methods use adaptive steps size controls for optimal performance. float. template<class Engine T. BLOCHLIB DESIGN 118 listed in section 4. class ElementType T. coord<>. etc. coords. the stencils perform the basic ﬁnite diﬀerence algorithms over vectors and grids. matrices. 44. Vector<>. Container T. For instance if ElementType T=double.3. Engine T is another class which deﬁnes the function(s) required by the solver. It is a good ﬁrst attempt for attempting to solve ODEs[42. float. It uses the jacobian of the system to handle the stiﬀ equations by using a combination of LU decompositions and extrapolation methods[97. stiﬀ equations are not handled well and it is highly sensitive to impulse type functions. ElementType T is the precision desired or another container type (it can be things like double. The BulirschStoer extrapolation method (the bs class) is of relatively high accuracy and very eﬃcient (minimizes function calls).1. N>. The ODE solver requires another object that deﬁnes a function. Finally. 45]. The solvers are created as generically as possible. The CashKarpRungeKutta 5th order method (the ckrk class) is a basic work horse medium accuracy. They are included in this version of BlochLib for completeness. Because there is no array greater then two dimensional in BlochLib yet.5.). class Container T> . the stencils over grid spaces are treated much diﬀerently then they would be over a standard three dimensional array. 45]. then Container T will usually be Vector<double> or coord<double. The ElementType T is the type inside the container. 41. 45]. However. allowing for various data types (double.2. The BlochSolver object uses the bs class as its default ODE solver [43. complex) and containers (Vectors. All the algorithms require the same template arguments. and vectors of coords). .
BLOCHLIB DESIGN 119 however. The quantum mechanical structures begin with the basic building blocks of spin dynamics: the spin and spatial tensors. can be parsed by the HamiltonianGen much like the Parser object. the input strings “45 ∗ pi ∗ (Ix 1 + Iz 0)” (Ix 1 + Iz 0 are the x and z spin operators for spin 1 and 0 respectively). 1 ∗ 56” (T 21 0. These objects use the Rotations object in the Cartesian representation to generate rotated Hamiltonians. . using the inheritance properties of SolidSys is imperative for further operation of the algorithm classes oneFID and compute. After the basic tensor components are developed. Scalar couplings.3. labels. This class can be extended to any generic Hamiltonian function.5. spin operators. the Ndimensional array and tools should be included in later versions. and spin operators into a combined object which generates entire system Hamiltonians and provides easy methods for performing powder averages and rotor rotations to the system Hamiltonian. mass. There is a Rotations object to aid in optimal generation of rotation matrices and factors given either spherical or Cartesian spaces. At this point the tool kit is split into a classical section and a quantum section. quantum numbers. momentum). The spin operators are also generated to minimize any computational demand. The HamiltonianGen object allows for string input of Hamiltonians to make arbitrary Hamiltonians or matrix forms more powerful. The SolidSys object combines the basic Hamiltonians. Both sections begin with the basic isotropic information (spin. and spin systems. rotations. and Quadrupoles as described in section 3. Spatial tensors are explicitly written out for optimal performance. BlochLib provides the common Hamiltonians objects: Chemical Shift Anisotropy (CSA). gamma factors. and “T 21 0. m=1 spin tensor between spin 0 and spin 1).3. For example. Dipoles. 1 is the second rank. In fact.
These interactions comprise the basis for the classical simulations. The interactions are optimally collected using the Interactions object. rotations. dipole–dipole interactions. and diﬀusion1 as described in section 3. circles. pulses. If the FID is desired over a powder. centering. helmholtz. The grids and shapes interact directly with the Bloch parameters to creates large sets of conﬁgured spins either in gradients or rotating environment. The basic shapes of coils. Each interaction is treated separately from the rest. lines.1. act as the basis for the oneFID object that will choose the valid FID collection method based on rotor spinning or static Hamiltonians. . or another derivative. and interactions. start and end points. One can also create other coil geometries and add 1 In the current version of BlochLib. etc. BLOCHLIB DESIGN 120 The Hamiltonian functions from the SolidSys object. lengths. the demagnetizing ﬁeld. For classical simulations the relevant interactions are oﬀsets (magnetic ﬁelds). radiation damping. The Bloch object is the master container for the spin parameters. This object is then used as the main function driver for the BlochSolver object (a useful interface to the ODE solvers). diﬀusion is not treated. there is an entire set of objects devoted to calculating magnetic ﬁelds for a variety of coil geometries. requiring positions. These particular objects are heavily parameter based.5. and spirals. the algorithm is parallelized using a powder object. As magnetic ﬁelds are the main interactions of classical spins. and can be either extended or used in any combination to solve the system. It uses normal eignvalue propagation for static samples and the γCOMPUTE[60] algorithm for spinning samples.3. T2 and T1 relaxation. bulk susceptibility. The powder object allows for easy input of powder orientation ﬁles and contains several built–in powder angle generators. are built–in. turns. helices. which is a crucial part of the Bloch object. New interactions can be added using the framework given in the library.
There are several problems inherent to C++ that can be debilitating to the developer if they are not understood properly. BLOCHLIB DESIGN Table 5. plotter2D and Solidplotter. For example to add two vectors. Because templated objects and algorithms are generic. No toolkit would be complete without examples and useful programs. The magnetic ﬁelds can be added to the oﬀset interaction object to automatically create a range of ﬁelds over a grid structure. The ﬁrst three problems revolve around the templates.1: Matlab Function Solidplotter plotter2D plotmag plottrag Available Matlab visualization functions in BlochLib Desicrption A GUI that plots many of the NMR ﬁle formats A function that performs generic data plotting Visualization functions for the magnetic ﬁeld calculators Magnetization trajectories classical evolutions visualizations 121 them to the basic coil set (examples are provided in the tool kit). plottraj. the trajectories from solving the Bloch equations.4 Drawbacks As discussed before. 5. the power of C++ lies within the object and templates that allow for the creation of generic objects. vectors. and optimization. Also included are several Matlab visualization functions (see Table 5. they cannot be compiled until used in a speciﬁc manner (the template is expressed).2). generic algorithms. Most of the main mathematical kernels in BlochLib cannot be compiled until expressed (matrices.1) that interact directly with the data output from the magnetic ﬁeld generators plotmag. grids. as well as into other objects to create rotating or other time dependant ﬁeld objects. .3. the compiler must know what data types are inside the vector. and generic FID and data visualization.3.5. Many programs come included with BlochLib (see Table 5.
The eﬀect is best seen in Figure 5. Each time a new operation is performed on an expression template data type (like the vectors). The two template problems combined require large amounts of memory and CPU time to perform.95. The last problem for C++ is one of standardization. The C++ standard is not well adhered to by every compiler vendor. especially if the operations are complex. which require a memory copy upon assignment (i. The other template problem arises from the expression template algorithms. then create the actual machine code. the compiler must ﬁrst unravel the expression. For instance Microsoft’s Visual C++ will not even compile the most basic template code. The ﬁnal template problem arises from expression template arithmetic. Other compliers cannot handle the memory . the gcc 3. but the speed increase is approximately a factor of 10 or greater. For example the bulksus example in BlochLib takes approximately 170 Mb of memory and around 90 seconds (using gcc 2.3 where the pointer copying used for the ATLAS test saves a few MFLOPS as opposed to the BlochLib version. A=B).3.1. the cost of this copying can be signiﬁcant with respect to the operation cost. and the ODE solvers). For smaller array sizes.5. however. BLOCHLIB DESIGN 122 shapes. the numerical beneﬁts usually overshadow these constraints. For the same bulksus example. coords. This can leave a tremendous amount of overhead for the compiler to unravel when a program is actually written and compiled. Nonexpression template data types can pass pointers to memory rather than the entire memory chunk.3) to optimally compile one source ﬁle. This can require a large amount of time to perform.e. Compiler’s themselves are getting better at handling the template problems. However. as the matrices get larger the relative cost becomes much smaller.1 compiler took approximately 100 Mb of memory and around 45 second of compilation time.
VARIOUS IMPLEMENTATIONS 123 requirements for expression template unraveling (CodeWarrier (Metrowerks) crashes constantly because of memory problems from the expression templates).2 is a list of the programs included and their basic function.4 Various Implementations This section will describe a basic design template to create programs from BlochLib using the speciﬁc example of the program Solid. GNU g++ 3. In Table 5. There is potentially an inﬁnite number of programs that can be derived from BlochLib. however. but more on their creation and the modular nature the tool kit.5. Solid is a generic NMR simulator.1 adheres to almost every standard and performs optimization of templates eﬃciently. 5. which is a good optimizing compiler for almost every platform. the tool kit comes with many of the basic NMR simulations programs already written and optimized. The emphasis will not be on the simulations themselves. Describing each one will show a large amount of redundancy in how they are created. A few of the programs which represent the core ideologies used in BlochLib will be explicitly considered in the following sections.2. The saving grace for these problems is the GNU compiler. Several other programs are brieﬂy described within the design template. These programs serve as a good starting place for many more complex programs. Some of them are quite complicated while others are very simple.4. .
VARIOUS IMPLEMENTATIONS Table 5. It behaves much like Simpson but is faster for large spin sets as shown in Figure 5. All simulations were performed on a 700 MHz Pentium III Xeon (Redhat 7.4b shows the speed of the simulation of a C7 with simulations conditions the same as those shown in Figure 6e of Ref. In both cases the extra spins are protons with random CSAs that have no interactions between with the detected 13 C nuclei.3).0 General Solid State NMR simulator classes Several ‘HowTo’ class examples data readers Data reader and conversion programs Other diffusion 1D diﬀusion example mpiplay Basic MPI examples 124 5. The EE diagram (Figure . compiled with gcc 2. [85] for Figure 5.4.4. Solids basic function is to simulate most 1D and 2D NMR experiments. [85]. Figure 5.4.95.2: Key examples and implementation programs inside BlochLib Folder Description Catagory bulksus Bulk susceptibility interaction dipole Dipole–dipole interaction over a cube echo A gradient echo EPI an EPI experiment[98] magfields Magnetic ﬁeld calculators rotating field Using ﬁeld calculators and oﬀset interactions Classical splitsol Using ﬁeld calculators for coil design mas Simple spinning grid simulation raddamp Radiation damping interaction relaxcoord T1 and T2 oﬀ the zaxis simple90 Simple 90◦ pulse on an interaction set yylin Modulated demagnetizing ﬁeld example[87] MMMQMAS A complex MQMAS program nonsec Nonsecular quadrupolar terms exploration perms Permutations on pulse sequences Quantum shapes A shaped pulse reader and simulator Solid2.3. Solid is essentially a parameter parser which then sends the obtained parameters to the main kernel for evaluation.1 Solid The program Solid represents the basic EE quantum mechanical simulation pro gram.5.4a.4. Solid tends to be slower for small spin sets as explained in section 5.3 with donditions the same as those shown in Figure 5 of Ref.
and b) shows the speed of the simulation of a C7.4. VARIOUS IMPLEMENTATIONS 125 10 7 10 6 a) log(time) seconds 10 5 10 4 10 3 10 2 10 4 log(time) seconds b) 10 3 10 2 10 1 10 0 2 3 4 number of spins 5 6 Figure 5. a) shows the simulation of a rotary resonance experiment on set pair of spins.4: Time for simulations of Solid (solid line) and Simpson (dashed–dotted line) as a function of the number of spins. .5.
4. a standard 2D. and output). SolidRunner then combines the basic FID algorithms (in the oneFID object). Using the extendable ScriptParse object. Based on this input syntax. Solid has three stages.3. parameter parser.1) can be extended to more speciﬁc object usage used in Solid (Figure 5. The pulses section contains the majority of Solid’s functionality. Three basic sections are needed. It extends the ScriptParse object to add more simulation speciﬁc commands (spin evolution. deﬁnition of powder average types. and the output classes to perform the NMR experimental simulation. parameter input. the Propagation object.5). Deﬁnitions of a solid system (spins). Appendix A. and ﬁnally the deﬁnition of a pulse section where spin propagation and ﬁd collection is deﬁned (pulses). and a pointtopoint (obtains the indirect dimension of a 2D experiment without performing the entire 2D experiment).1 shows the input conﬁguration scripts for the generation of this data. a simple object trail can be constructed. main kernel composition. and output structures.6. other basic variables and parameters (parameters and the subsection powder). the SolidRunner object deﬁnes the new functions available to the user. The results of a 2D and pointtopoint simulation of the postC7 sequence[99] are shown in Figure 5. VARIOUS IMPLEMENTATIONS 126 5. There are three basic acquisition types Solid can perform: a standard 1D. MultiSolidSys contains at least one (or more) SolidSys objects.5. This combined with the powder section/object deﬁnes the Propagation object where the basic propagation algorithms are deﬁned. The EE normal section. FID calculation. Simple 1D simulations are shown in Figure 5. was written to be the main interface to the kernel and output sections.7. .
alterSys.5: The design of the EE program Solid derived from the input syntax. . amplitude. offset ro. detect. delay. VARIOUS IMPLEMENTATIONS 127 Input Syntax Pulse sequence Simulation parameters Spin parameters spins{ numspin 2 T 1H 0 T 13C 1 C 1000 2000 0 0 D 231 0 1 } parameters{ powder{ aveType zcw thetaStep 233 phiStep 144 } wr=3000 npts1D=512 ro=Iz detect=Ip_0 } Object Trail SolidSys Parameters MultiSolidSys Propagation powder Parameters oneFID matstream pulses{ amp=15000 amplitude(amp) 1H:pulse(1/amp/4. show. 2D.4. savefid Figure 5. use. fid.0) fid() savefidtext(data) } Parser ScriptParse Parameters SolidRunner Defined Commands pulse.5. ptop. reuse.
4. and b) is a full 2D collection.6: A two spin system as simulated by Solid where a) is with no spinning. The spins system included 2 CSA’s with the ﬁrst spins parameters as ωiso = 5000 ∗ 2π. ωani = 6012 ∗ 2π.3. and η = 0.1 for input conﬁguration ﬁle. Hz . and b) is with a rotor speed of 2000 Hz at the magic angle (54.5 x 10 2 4 b) 2 (Hz) Figure 5.7 deg). ωani = 4200 ∗ 2π. VARIOUS IMPLEMENTATIONS 128 a) ωr=0 ωr=2000 1. with a scalar J coupling of 400 Hz between the two spins.5.5 0 0. See Appendix A.5. See Appendix A. and η = 0. The spins system includes a dipole coupling of 1500 Hz.1 for input conﬁguration ﬁle. a) pointtopoint 2500 2000 1500 1000 500 0 500 1000 1500 2000 2500 Hz b) 2D Figure 5. For a) and b) 3722 and 2000 powder average points were used respectively.5 1 0. For both a) and b) 233 powder average points were used.7: A two spin system as simulated by Solid of the postC7 sequence where a) is collected using a pointtopoint acquisition. the second spin’s parameters as ωiso = 5000 ∗ 2π.3.5 1 1.
VARIOUS IMPLEMENTATIONS 129 5. divided by the square of the distance between the observation point. a true helmholtz pair (two sets of overlapping helices). and constant ﬁelds.8 shows the basic design of the ﬁeld calculator using the MultiBiot object. The parameters then feed into the . r . circles. helices. dl(r ). and the current point. input ﬁles of points. B(r). There are two basic parameters sections needed. The ﬁrst describes the coil geometry using the basic elements (see text) and any user written coils geometries. BlochLib also allows the user to create their own coil primitives and combine them along with the rest of the basic primitives. an ideal helmholtz pair (basically 2 circles separated by a distance).4. The second describes the Cartesian grids where the ﬁeld will actually be calculated.4. The geometric primitives included in BlochLib are lines. The main ﬁeld algorithm calculates a discrete integral of Ampere’s equation for the magnetic ﬁeld. Initially the user must register their own geometries into the BiotCoil list. crossed into a observation direction. spirals. the coil must be divided into small line segments.5. Figure 5. B(r) = µo 4π I(r ) × dl(r ) r − r 2 (5. the integral is broken into a sum over little lines of current (the BiotSavart Law). For this to function properly numerically.1) where the magnetic ﬁeld at the point r. Again once the parameter sets are known a simple object trail can be developed. I(r ). is the volume integral of the current at r . r. There are numerous coil geometries. One way to evaluate this integral numerically. but most of the more complex designs can be broken into a set of primitive objects.2 Classical Program: Magnetic Field Calculators Included in BlochLib is the ability to calculate magnetic ﬁelds over arbitrary coil geometries.
XYZshape and MultiBiot objects.... Parallelization can be implemented simply by deﬁning the MPIworld object and passing it to the MultiBiot object. } subcoil2{ type line . The data is written in 2 formats...2).5..mat . Figure 5..3. } } Register Coils BiotCoil Biot Coil Parameters Parameters MultiBiot Register MPI object Aux Parameters parameters section coils matout fname.4.9 shows the data generated by the program. into one readable by Matlab for visualization and into a ﬁle readable by the MultiBiot object. VARIOUS IMPLEMENTATIONS 130 Input Syntax Grid Parameters grid{ min 1..1 dim 5. } Parameters Calc field Write data Figure 5.5.8: The basic design for the Field Calculator program.2.1. It should be noted that the convergence of the integral in ..1 max 1.1. The input ﬁle for this program can be seen in Appendix A. This program is included in BlochLib under the magfields directory (see Table 5.5 } Object Trail Grid Parameters XYZshape coils{ subcoil1{ type circle ..
3 Classical Programs: Bloch Simulations Programs of this type are designed to function on large spin sets optimally based on the interactions present. The Grid serves as the basis for much of the rest of the Bloch interactions and Bloch parameters. As a result. Grids also serve as the basis for gradients and physical rotations. VARIOUS IMPLEMENTATIONS 131 Eq. 5. Bulk Susceptibility One such implementation attempts to simulate the result obtained in Ref. A pulse on a Bloch system represents a type of impulse function to the system. that can be simply added in the speciﬁc object chain to be used. The pulses.5. The basic layout for these simulation can be see in Figure 5. Bloch. The interactions are also a key part of the simulation and can rely on the grid structures as well as any magnetic ﬁelds calculated. the parameter input is expected to be minimal. The z .4.1 is simply a function of the number of line segments you choose for the coils. The delay in the HETCOR sequence (see Figure 5.10.4. These programs typically need as much optimization as possible in order to function optimally over large spin sets. with the bulk of the design to aid in optimization of the interaction sets and pulse sequences used. Next the 1 H magnetization is placed back on the zaxis.11a) allows the oﬀset of the 1 H to evolve. Items in gray are optional objects. which is then fed into the BlochSolver object which performs the integration. [100] Figure 2. and interactions are ﬁnally wrapped into a master object. 5. Bloch parameters. This is a HETCOR (Heteronuclear Correlation) experiment between a proton and a phosphorous. A pulse should be treated as a separate numerical integral step due to this impulse nature (such impulses can play havoc with ODE solvers).
was used to generate these ﬁgures (see Table 5.1). .5. z directions are shown in b) –d) respectively.9: The magnetic ﬁeld calculated by the program shown in Figure 5. plotmag.2 The Matlab function.3. y.4. The conﬁguration ﬁle is shown in Appendix A. VARIOUS IMPLEMENTATIONS 132 5 z axis (cm) a) 0 5 2 1 0 1 x axis (cm) 2 2 1 0 1 2 3 y axis (cm) b) Bx G a u s s z(cm) y(c m) x(cm) c) By G a u s s d) Bz G a u s s Figure 5.8. The coil and the sampled grid are shown in a). the ﬁelds along the x.
VARIOUS IMPLEMENTATIONS 133 Basic Parameters Input Parameters Grid XYZshape Gradient Grids.5. . Rotating Grids Field Calculators Interactions ListBlochParameter Pulses Bloch TimeTrains BlochSolver Data Ouput Figure 5.4.10: A rough design for a classical Bloch simulation over various interactions.
In order to correctly replicate the ﬁgure.Y. The T2 relaxation of the 1 H also had to be altered to 0. The results is shown in Figure 5. however the diagram shows a much faster decay then this time).4. the 1 H oﬀset had to be changed to 722 Hz (the reference quotes 115 Hz as the oﬀset. The data was plotted using plottrag in the distribution.5. . the interplay between radiation damping and the demagnetizing ﬁeld resurrect a completely crushed magnetization (a complete helical winding). Thus in the indirect dimension an oscillation of the 31 P magnetization due to the protons will be observed. et. will feel a slight oﬀset shift due to the varying 1 H zmagnetization (eﬀect of the bulk susceptibility).11b and matches the result obtained in [100].002 seconds (the reference quotes 0. Radiation damping is responsible for the resurrection as the demagnetizing ﬁeld alone does not resurrect the crushed magnetization. Radiation Damping Another interesting implementation attempts to emulate the result obtained by Y.015 seconds. but it seems a factor of 2π was omitted (722 = 2π ∗ 115).3. The input parameters are those in the reference [87]. The simulated data (Figure 5. VARIOUS IMPLEMENTATIONS 134 magnetization of the proton will oscillate (its magnitude eﬀected by the time evolved under the delay).5. Lin. The code for this diagram is in the yylin folder of the distribution and can be seen in Appendix A.al[87]. If one then places the the 31 P 31 P magnetization in the xyplane and collects an FID. In this simulation. The code for this diagram is in the bulksus folder of the distribution. The result is a nonlinear partial resurrection of magnetization.12b) matches Figure 2 in reference [87].
6 4.4 4.4 5.8 5 5.8 6 f2 (Hz) Figure 5. VARIOUS IMPLEMENTATIONS 135 a) 1H t1 90y 90y t2 90y 31P 8 7 6 b) t1(ms) 5 4 3 2 1 4 4.2 5.6 5.4. a) shows the simulated .11: Simulated data from a HETCOR (Heteronuclear Correlation) experiment showing the eﬀect of bulk susceptibility on the oﬀset of the pulse sequence and b) shows the simulated data. 31 P .5.2 4.
8 1.4 0.5 My 0 1 0.5.5 Mx time(s) 35 30 25 <M>% 20 15 10 5 0 5 10 0 0.12: Simulated resurrection of magnetization after a crusher gradient pulse in the sequence shown in a). .2 0. VARIOUS IMPLEMENTATIONS 136 90y a) 1H Gz b) 1 0.6 1.4 2 0.2 1.8 2 Figure 5.4.4 0.5 0 1 0.4 1.5 1.8 <Mx> <My> <Mz> time(s) 1 1. b) shows the eﬀect of radiation damping and the modulated local ﬁeld.2 0.6 0.
Correcting this by a U shape (or equivalent) should aid in correcting the proﬁle. because the stator is large compared to the sample.5. . there would be little detected signal (or pulse power transmission) because the high static magnetic ﬁeld and coils ﬁeld are parallel (resulting in a 0 cross product). Figure 5. The wire that connects the two helices creates the majority of the asymmetric ﬁeld proﬁle. The ﬁgure also shows us a weak spot in the splitcoil design. To optimize the split solenoid design one needs to see factors like inhomogeneities and eﬀective power within the sample area. the ﬁeld proﬁle is much more distorted. the solenoid has 6 times more ﬁeld in the region of interest then the splitcoil design. A suitable alteration to the solenoid would be to split it.4. But this represents its own problem if the coil is a solenoid. One can remove this shortcoming by removing the coil from the stator. and thus the solenoid would also have to be large thus reducing the ﬁlling and quality factor too much to detect any signal. Dynamic Angle Spinning (DAS)[101] experiments require the probe stator to move during the experiment. also given the same current in the two coils. Compared with a normal solenoid. as the stator angle approaches 0 degrees (with respect to the high ﬁeld). however. A solenoid coil moves with the stator.13 shows a split solenoid design as well the inhomogeneity proﬁle along the xyplane (the high ﬁeld removes any need for concern about the zaxis). VARIOUS IMPLEMENTATIONS 137 Probe Coils The ﬁnal example involves analyzing an NMR probe coil design. The entire probe design is the subject of a forth coming paper[102]. and is the major contributor to the inhomogeneity across the sample. Figure 5.14.
b) shows the ﬁeld proﬁle along the xdirection given 3 amps of current. a) shows the coil as well as the region of interest for the magnetic ﬁeld (black points). The majority of the inhomogeneity is due to the small line connecting the two helical segments. c) shows the eﬀective inhomogeneity of such a coil for a proton.4.5 0 x axis (cm) 0. The average ﬁeld of the coil was subtracted from the result in c).4 1 0.8 1 1.5 0 0. VARIOUS IMPLEMENTATIONS 138 a) 0.6 0.2 0 0.6 cm). a practical coil to work around the Dynamic Angle Spinning problem of the solenoid coil (see text).4 0.5 1 0.3175 cm and a splitting of 0.5.2 0.4 z axis (cm) SplitSolenoid 0.5 y axis (cm) b) Bx G a u s s y (cm) x (cm) z (cm) c) 4 3 2 1 kHz 0 1 2 3 4 Figure 5. .2 1.13: The magnetic ﬁeld proﬁle of a split solenoid (3 turns/cm with a radius of 0.
4 y axis (cm) b) Bx G a u s s y (cm) x (cm) z (cm) c) 4 3 2 1 kHz 0 1 2 3 4 Figure 5.5 0 x axis (cm) 0.2 0 0. 10 turns/cm with a radius of 0. VARIOUS IMPLEMENTATIONS 139 a) 0.2 0. . The average ﬁeld of the coil was subtracted from the result in c).4.5.4 z axis (cm) 0.14: The magnetic ﬁeld of a standard Solid State NMR probe detection coil (the solenoid. a) shows the coil as well as the region of interest for the magnetic ﬁeld (black points).4 0. c) shows the eﬀective inhomogeneity of such a coil for a proton.5 amps of current. The small peak to the right of the main peak is the edges of the sampled rectangle close to the coil.2 0 0. b) shows the ﬁeld proﬁle along the x direction given 0.3175 cm ).5 1 0.4 1 Normal Solenoid 0.2 0.
coding.edu/blochlib/. Relaxation using normal Louville space operators and Redﬁeld approximations should also be included. Diﬀusion and other partial diﬀerential equation entities (like ﬂuid ﬂow) are currently being designed for inclusion into the tool kit.5. BlochLib. I hope to maintain a copy and updates at http://theaddedones. . and creation of programs should be easy and highly optimized for both the classical and quantum mechanical aspects of NMR. The total tool kit and documentation can be found at http://waugh.cchem. It has been shown that utilizing relatively modern numerical techniques and algorithms allows a study of more complicated spin dynamics under various interactions and experimental designs then previous NMR tool kits. If it not there.5 Conclusions Throughout this chapter. BlochLib is designed to be the next generation of simulation tool kits for NMR.berkeley. The input of complex parameters. It is highly optimized and generalized for almost any NMR simulation situation. adheres to these basic design ideas: speed using expressiontemplates and ease of use using C++ and objects/operators. The created tool kit. CONCLUSIONS 140 5.5. or so I hope it remains after I leave. emphasis on the generic physical simulation design is discussed for the speciﬁc case of NMR.com/.
In this chapter I wish to develop a general frame work to optimize any rotor synchronized pulse sequence over long time evolutions with an explicit example applied to the postC7[99] sequence. The usual NMR simulation consists of a pulse sequence that either needs to be simulated to validate an experiment or add subtle corrections. I will now go into an application beyond the typical scope of the ‘usual’ NMR simulation. Such simulations abound in the NMR literature to the point that even including a reference list for such types of simulation. . I would probably have to reference 80% of the NMR publications around.141 Chapter 6 Massive Permutations of Rotor Synchronized Pulse Sequences 6.1 Introduction Given that we have a large set of fast computational tools.
1a. 6.1.1: A general rotor synchronized pulse sequence a) using pulses and delays. however. and b) using a quasi continuous RF pulse. Within the n cycle. n.1.6. there are typically two diﬀerent types of sequences. The RF pulses can be of arbitrary amplitude.1 Rotor Synchronization Rotor synchronized sequences (RSS) are designed to create a speciﬁc average Hamiltonian when observed in multiples of the synchronized parameter. The second applies continuous RF radiation during the rotor cycles as in Figure 6.1b. These RF sequences are designed to manipulate the spin space degree of freedom (the Tl.m tensors) in conjunction with the spatial rotation the rotor performs (the Al. INTRODUCTION 142 nτr RF: τr τr Rotor: a) Pulse Delay: = ϕ θ delay ϕ' θ' b) Continous Pulse: = ϕ θ ϕ' θ' ϕi θi Figure 6. The ﬁrst contains a series of pulses and delays as in Figure 6. the rotor can only be spun in a constant direction and is not adjustable in terms of duration . phases and duration. n is the amount of rotor periods the sequence takes to complete.m tensors).
1) . we typically say the rotor has a ﬁxed spinning rate. but experimentally. The pulse–delay–pulse versions have better properties theoretically. The speed of its rotation is manipulable. however. For this reason. The limitation is the assumption of hard pulses. doing so during the course of an experiment is unstable and unrealizable due to the physical aspects of the rotation. The synchronized parameter then refers to n multiples of τr . we can write the integrated Hamiltonian at time τr as series of time independent Hamiltonians τr ¯ ¯ ¯ H(t)∂t = H 0 + H 1 + H 2 + .2 6. BACKGROUND THEORY 143 or phase during the course of an experiment. they are extremely hard to implement..1 Background Theory Average Hamiltonian Before we can really to describe speciﬁc RSSs.6. Each continuous pulse version has a pulse–delay–pulse version counterpart. Almost all RSS sequences use continuous pulses.2. we need Average Hamiltonian The ory (AHT)[103] to attempt to understand the dynamics of such sequences.. 0 (6.2. AHT uses the periodicity of the Hamiltonian to render a time dependant problem into an approximate time independent form. for many experimental parameters and real spin systems. ωr and a corresponding period of τr = 2π/ωr . 6. Given a periodic Hamiltonian of period τr . a hard pulse is extremely hard to implement correctly.
2e When integrated over a period τr .m (Ωrot )A2 T0 m (6. [H(t ). The Hamiltonian for a disordered sample in a high ﬁeld reduces to H= m exp [−imωr t] d2 (θr ) 0. I will show the eﬀect of the average Hamiltonian of a simple dipole under a rotor rotation of ωr spinning at the rotor axis θr . θ. BACKGROUND THEORY 144 where ¯ H0 = 1 τr τr H(t)∂t 0 τr t i ¯ H 1 = − 2τr [H(t). we get δz ¯ H0 = 16 3 2 (1 + 3 cos(2θ)) (1 + 3 cos(2θr )) T0 .2. are all zero because this Hamiltonian commutes with itself at diﬀerent times i ¯ H 1 = − 2τr i = − 2τr τr t 2 2 [A(t)T0 .m m 2 2 Dm.6) 2 2 2 2 A(t)A(t )T0 T0 − A(t)A(t )T0 T0 ∂t∂t = 0. H n . Given that there is no molecular orientation to worry about. The physical rotation only eﬀects the spatial tensors.4) 3 −i(2φ+2ωr t) 2 sin2 θ sin2 θr T0 + 2e 3 i(2φ+2ωr t) 2 sin2 θ sin2 θr T0 . H(t )]∂t∂t 0 0 (6. H(t )]] + [H(t ).2) [H(t). H(t)]] ∂t∂t ∂t . [H(t ). 2 (6. we easily expand the sum to get δz 16 3δz 2 3 2 2 (1 + 3 cos(2θ)) (1 + 3 cos(2θr )) T0 + 2 sin θ sin θr T0 + 2 sin θ sin θr T0 + 3 −i(φ+ωr t) cos θ cos θr 2e 3 i(−φ+ωr t) cos θ cos θr 2e H= 3δz 2 3δz 8 3δz 8 (6. γ) is the powder rotation. ¯ H2 = −1 6τr τr t t 0 0 0 As an example. A(t )T0 ]∂t∂t 0 0 τr t (6.5) ¯ The rest of the average Hamiltonian orders.3) where Ωrot = (φ. in a disordered solid. 0 0 .6.
113. 116. the R and C classes. but the methods introduced here are easily extendable to decoupling. Magic angle spinning therefore removes any isolated second order spatial tensors. 107. 6. here we will simply give the results. For dipoles and 1st order quadrupoles. for CSAs and J couplings it removes the anisotropic shift. Eden’s [105]. Thus an entire RSS is made up from C or R subcomponents. 118. The char . called the magic angle. 123. The reader is encouraged to look to Carravetta’s [110].2 show the main diﬀerence between the two. BACKGROUND THEORY 145 ¯ Using Eq. 115. Most RSS recoupling methods have been categorized very nicely by their symmetries. and Brinkmann’s [112] papers for more information. the extra terms the introduce ‘sidebands’ in the resulting eigen–spectrum. The typical NMR experiment does not observe every t = τr but something less.7◦ . is arccos(1/ 3) = 54. 112.2. 105. 106. 121. 116]. at any other time. In real systems. 114.2 Recoupling RSS RSSs have two main uses in NMR 1) to remove unwanted terms from the AHT (decoupling) [104. 124. 99.2. 117. Then our commutation rule breaks down and we must consider higher order AHT terms to get the proper answer. this completely removes the interaction. the other components of Eq. 120.5 it is then easily to see we can pick θr such that H 0 = 0.4 do not integrate to zero. This √ angle. RSSs are broken into two diﬀerent main classes. It also should be noted that the solution here is only valid for the time t = τr . 122. we typically have CSAs and dipoles and J couplings to consider over multiple spins. 109] and 2) to selectively reintroduce certain terms into an AHT (recoupling) [110. 111. 6. By isolated we mean there there is NO other interaction in the main Hamiltonian that does not commute with the rest of the interactions. 6. Here we will focus on recoupling. 119.6. Figure 6. 108.
To correct for higher order AHT terms one can apply further symmetry if we know which tenser elements we desire. The next factor ν along with N represent the phase iteration from one sub element to the next as shown in Figure 6. to ﬁrst order. Another aspect of a real experimental system is that RF pulses are not perfect and resonant on only one frequency. caused by oﬀsets and inhomogeneity problems. This technique is called compensation. BACKGROUND THEORY 146 acteristics of the C sub element is that they perform a total rotation of 2π. this is easily performed by applying the same amplitude. The R subunit rotates the spin states by π. and ν that one ¯ can select almost arbitrary elements to comprise the H 0 element of the AHT given a certain set of interactions (dipoles.6.3a and b. we apply a second R with the phase equal to −φ from the last (as we are treating a π pulse).2. CSAs. For the C cycles. each C or R cycle must be internally compensated. To classify them even further Eden and Carravetta introduce 2 more symmetry elements along with n. we have a total rotation of 4π. in eﬀect reversing the damage done by a bad RF pulse. which is why this only works to ﬁrst order. N represents the time duration of a single sub element as nτr /N = π for the R class and nτr /N = 2π for the C class. Given a real experimental system. n. which reverses any problems. Carravetta and Eden showed that given proper selection of N . For the R cycles. For the C cycles. but reversing the RF phase by π. the basic C7 as shown in Figure 6. which can be divided into (π/2)φ − (2π)φ+π − (3π/2)φ which . Each C/R cycle gets an extra C/R attached as shown in Figure 6.2. etc). To make a C or R cycle robust towards inhomogeneity and oﬀset of the RF pulse itself. We have assumed that over the course of a single C/R cycle that the real system Hamiltonian can be considered constant.2 will fail to produce desired results because of higher order commutators.
.2: The two RSS classes C (a) and R (b). .2.6..N1 θ=2 π n/ωr b)R class: j ν = RN n ϕ= ϕo + ϕ ν*π Ν j=0.N1 θ=π n/ωr Figure 6. BACKGROUND THEORY 147 nτr RF: j= 0 1 2 3 4 N3 N2 N1 τr τr Rotor: a) C class: j = CNn ν ϕj = ϕo + j*ν*2π Ν j=0.
6.0 m=−2 d2 (2πωrf t)e−imφj T2. (6.3c and d show this posting explicitly.m . [99]. θr . on an arbitrary tensor can be represented using the notation for tensor rotation l Fl. ωr t) where θr the angle of the rotor.m is a scaling factor. Figure 6. and ωr is the spinning speed (ωr t then represents the total phase). φ). This simple reordering of the 4π is called ‘posting’ hence the name postC7. They also rotate the spin part through the Euler angles (0.m (θ)e−imφ Fl. φ) where ωrf is the pulse amplitude (thus ωrf t represents the total rotation angle) and φj is the pulse phase. Our dipolar Hamiltonian under such a rotation becomes 2 2 A2.m Al. A2. the spatial part.c. m (6.m m.0 T2. of the form ¯ H0 = l m. The RSS sequences rotate the spatial part by (0. θ. means the complex conjugate and gm.7) where c.m is the rotated tensor and Fl.c.m = m=−l e−im ψ dl . The scaling factors is also an important aspect of these sequences as they determine the apparent spectral distribution. Calculation of the scaling factors for the R or a C type is a simple application of AHT. The R sequences have now a total rotation of 2π which can be split arbitrarily into many forms like a (θ1 )φ − (2π − θ1 )φ+π − (θ1 )−φ − (2π − θ1 )−φ−π sequence.2. A general rotation through the Euler angles (ψ.9) . and are needed when using these RSS in larger pulse sequences designs[113]. T2. and a spin part.0 . A given dipolar or CSA interaction contains two unique tensor elements.m + c. The RSS sequences produce desired tensor elements from a system Hamiltonian. ωrf t. BACKGROUND THEORY 148 has better oﬀset and inhomogeneity properties as demonstrated in Ref.8) where Fl.m Tl.m g m.0 → m=−2 d2 (θr )e−im2πωr t A2.0 (6.m m.0 .m is the original tensor.
N1 θ=2 π ϕj+π θ=2 π 2n/ωr RNν n ϕ= b)R class compensated: ϕo + θ=π ν*π Ν j=0.N1 ϕj ϕ ϕj+π θ=2 π j θ=π/2 θ=3π/2 2n/ωr ν RNn ϕ= ϕ θ1 d)R class posted: ϕo + ν*π Ν j=0.. R(d) RSS sequences..N1 ϕ+π 2 π−θ1 −ϕ θ1 −ϕ−π 2 π−θ1 4 n/ωr Figure 6.3: Compensated C (a)..N1 ϕ −ϕ θ=π 2 n/ωr CNν n ϕj = c) C class posted: ϕo + j*ν*2π Ν j=0. R (b) and posted C (c). . BACKGROUND THEORY 149 a) C class compensated: n CNn ϕj = ϕj ϕo + j*ν*2π Ν j=0..6.2.
6.2. BACKGROUND THEORY
150
Assuming that the symmetry pulse phases, φj , selects only terms with l = 2, m, m then our rotated Hamiltonian becomes A2,0 T2,0 → (d2 (θr )e−i4πωr t A2,m )(d2 (2πωrf t)e−i2φ T2,m ) + c.c. 2,0 2,0
(6.10)
In order to calculate what the scaling factor in front of the A2,±m T2,±m terms we need to make some assumptions about the applied sequence. First, the entire sequence is applied over a multiple of a rotor cycle such that the sequence is periodic and AHT can be used to continue the calculation. Second, the rotor cycle is divided into N subsections such that during the j th subsection only the pulse phase, φi is varied and all subsections are the same length in time, τ =
N −1 n ωr N .
The zeroth order average Hamiltonian is then
¯ H0 =
j=0
1 τ
(j+1)τ jτ
d2 (θr )e−imπωr t d2 ,0 (2πωrf t)e−im φj dtA2,m T2,m + c.c. m,0 m
(6.11)
The scaling factor, g, is given as the total constant that multiplies A2,m T2,m in the above equation.
N
gm,m =
j=0
1 τ
(j+1)τ jτ
d2 (θr )e−imπωr t d2 ,0 (2πωrf t)e−im φj dt m,0 m
(6.12)
6.2.3
C7
From this point I will discuss the C71 , the C7[115] as it was originally named 2
before Eden’s paper. The post version was also tagged before Eden’s paper in Ref. [99]. This sequence produces a double quantum (DQ) AHT to zeroth order using the dipolar Hamiltonian
∗ ¯ H 0 = g−1,2 A2,−1 T2,2 + g1,−2 A2,1 T2,−2 .
(6.13)
This Hamiltonian can then be used to determine approximate distance information between atoms as it selectively evolves only terms from the dipolar Hamiltonian, creating
6.2. BACKGROUND THEORY
151
DQ coherences between them. The accuracy of the distances is related directly to how well the C7 performs under all the various experimental and molecular considerations. A good measure of the C7 ability to create DQ coherences, is to measure the transfer between two spins. The transfer can be measured directly starting from an initial
1 polarization on spin 1 (ρo = Iz ), application of the sequence, then a measurement of the 2 polarization on the second spin (Idet = Iz ). Application of the sequence n times where
n >> 1 results in a steady state transfer of the coherence. Figure 6.4 shows the transfer for diﬀerent dipole couplings between the two spins. Introduction of CSA terms reduce the eﬀect of the transfer as also shown in Figure 6.4. The sequence used in the Figures is shown in Figure 6.5a. The dipole coupling determines the rate of transfer: a large rate means a closer distance. One can easily see from Figure 6.4 that introduction of large CSA terms cause the steady state transfer to fail. In multi–spin systems, this then leads to confusion of the distance information. The eﬀect is most pronounced at small dipole couplings where we would like to achieve the best data for longer range distance determination. It would be beneﬁcial to remove as many higher order average Hamiltonians as possible in these RSS type sequences.
6.2.4
Removable of Higher Order Terms
Usage of the RSS sequences is restricted to multiples of the rotor period. So long as
we create the zeroth order average Hamiltonian at the ﬁnal observation point, the number of rotor periods is arbitrary. The higher order terms of the C7 sequence introduce a variety of other unwanted tensor components due to the commutators between the CSA and dipolar Hamiltonians during each cycle. For a simple two spin system, the only other terms that
6.2. BACKGROUND THEORY
152
0.8 0.6 0.4 0.2 0 0.2 0.8 0.6 0.4 0.2 0 0.8 0.6 0.4 0.2 0 0.8 0.6 0.4 0.2 0 0 0 0 0
ωd=100Hz
0.8 0.6 0.4 0.2 0
ωd=400Hz
0.8 0.6 0.4 0.2 0
ωd=700Hz
0.8 0.6 0.4 0.2
ωd=1000Hz
50
100
0.2 0.8 0.6 0.4 0.2
0
50
100
0.2 0.8 0.6 0.4 0.2
0
50
100
0 0.8 0.6 0.4 0.2
0
50
100
ωd=1300Hz
ωd=1600Hz
ωd=1900Hz
ωd=2200Hz
50
100
0 0.8 0.6 0.4 0.2
0
50
100
0 0.8 0.6 0.4 0.2
0
50
100
0 0.8 0.6 0.4 0.2
0
50
100
ωd=2500Hz
ωd=2800Hz
ωd=3100Hz
ωd=3400Hz
50
100
0 0.8 0.6 0.4 0.2
0
50
100
0 0.8 0.6 0.4 0.2 0
0
50
100
0 0.8 0.6 0.4 0.2 0
0
50
100
ωd=3700Hz
ωd=4000Hz
ωd=4300Hz
ωd=4600Hz
50
100
0
0
50
100
0.2
0
50
100
0.2
0
50
100
Figure 6.4: PostC7 transfer eﬃciencies on a two spin system with ωr = 5kHz for various dipolar coupling frequencies (ωd ). The dashed lines indicate a CSA on spin one of (δiso = 12000Hz, δani = 8700Hz, η = 0) and on spin two of (δiso = −5400Hz, δani = 12300Hz, η = 0).
6.2. BACKGROUND THEORY
153
C72
1
2τr
1 1 2 2 3 3 4 4 5 5 6 6
a)
0
0
2τr
Bar
2τr
b)
0 0 1 1 2 2 3 3 4 4 5 5 6 6
0 0 1 1 2 2 3 3 4 4 5 5 6 6
2τr
Permutation
2τr
c) d)
0 0 1 1 2 2 3 3 4 4 5 5 6 6
3 2 2 1 1 0 0 6 6 5 5 4 4 3
n =
ϕn θ=π/2
ϕn+π
θ=3π/2
Figure 6.5: Diﬀerent base permutations on the postC7 seqeunce: a) the basic sequence, b) the barred sequence, c)permuted sequence, and d) the composition of a sub element.
6.2. BACKGROUND THEORY
154
can appear are combinations of T2,0 , T2,±1 and T2,±2 . Because we only wish to observe the Iz terms in the evolved density matrix any solo T2,0 terms can be ignored as [T2,0 , Iz ] = 0. Thus the only tensor component we wish to remove are the T2,±1 terms. These terms have the property that they are odd under a parity transformation where as the T2,±2 are not. In the ﬁrst application of a RSS sequence generates any T2,±1 terms, the same sequence with a global π phase shift (called barring) from the last will ‘reverse’ any evolution from the T2,±1 while maintaining the evolution under T2,±2 terms. We can then easily alter the original C7 sequence to include two C7 sequences with one barred relative to the last as shown in Figure 6.5b. Even in the 2–cycle C7 sequence errors can still accumulate from even higher order terms, thus we can even bar the next 2 C7 relative to the last 2 resulting in a sequence like ¯ ¯ C7 − C7 − C7 − C7. We can continue this process, which is called super–cycling, until the signal has decayed beyond observation. This super–cycling process was initially used most dramatically in liquid state broadband decoupling[125, 126, 127, 128, 129, 130, 131, 132] but is becoming more prevalent in solid–state NMR as the hardware and theory improve[112, 113].
Problems with super–cycling The removal of higher order terms comes typically with a penalty. The sequence is usually altered to include super cycles which can make the sequence very long. Most solid– state samples have a relatively fast decay rate (T2 is on the order of mille–seconds where as in liquids it can be seconds) meaning that super cycles cannot get very long. Perhaps not long enough to remove all the terms necessary for eﬃcient use of the sequence. Not only do we have a time constraint, but even super–cycling for anisotropic samples can lead to
We still would like to use the basic symmetry principles that do provide useful removal of terms. PERMUTATIONS 155 diminishing returns due to hardware imperfections[133. In order to investigate the eﬀect of diﬀerent cycles. 6. Techniques like MLEV[131. but use them in a time constrained way.5a.6. but from there. 6.3 Permutations The problem of determining the best set of RSS sub–cycles to use for a super–cycle was one best handled using the symmetries of the underlying Hamiltonians. 130]. We will use simple permutation to determine the best sequence. we will use the symmetry principles as our starting point. a barred RSS sequence is labeled ‘O’ (capital ‘O’) as in Figure 6. an internal . the problem is open ended. 126].3. We now wish to see if there is a way for us to determine the best set of cycles where the symmetry principles seem to fail.1 The Sub–Units Any particular deﬁned ordering of RSS sequences is a subunit.3. SPARC[125]. WALTZ [127. This technique works very well for liquid samples where the Hamiltonians as isotropic and have nice symmetry properties.5b. The anisotropic and spinning environment of a solid–state experiment makes such application of super–cycles hard and less eﬀective. and GARP[128] use the symmetries to decouple liquid state spins in a highly optimal way. 134]. This leads us to ask weather or not we can improve on the basic RSS in a framework that allows us to measure the eﬀectiveness of a particular super–cycle. We will use a particular naming scheme: the basic RSS sequence is labeled ‘o’ (lowercase ‘o’) as in Figure 6.
when expanded.5c.3. (6. Because of the commutation relations of Tl. sub–units o O w W oO oOOo wW wWWw oOWw OooO WwwW 156 permutation (a reordering internal to a single RSS cycle) of a RSS sequence is given the label ‘w’ (lowercase ‘w’) as in Figure 6.2 The Measure To determine the best sequence.m Tl.m the higher order terms..1 lists a few of the sub units for a C7.3.±2 terms above all other elements. Since we are performing exact numerical simulations of the propagator of the various C7 cycles.14) where τ is the total time of the super–cycle. α and β are complex constants.m ] + . So the measure of a good sequence will have large T2. Tl .±2 terms while minimizing any other terms. Table 6. the bared version of the internal permutation is given the label ‘W’ (captial ‘W’).. we need to use the eﬀective Hamiltonian Hef f = −i log(U ) = τ αl.l βl.m [Tl. we need some sort of measure that typiﬁes the RSS sequence.m . For a C7 sequence we desire T2.6. PERMUTATIONS Table 6. A sub unit can be constructed from other subunits as well so long as the subunit return the Average Hamiltonian to the desired result. 6.1: A list of some sub–units for a C7 permutation cycle. will reduce to terms .m + l l.
m (Tl.±2 αi  l. 6. thus numerically the sum is reduced to a sum of only the αl. The second.m terms are not relevant to the MR measure.y terms.m=±2 where bl. A i revised version of the MR is given as i α2. . For the C7 we can now deﬁne two measures.m=±2 αi  2. αl.±2 α0 2. they should not be counted in the sum.17) l=2.15 the integral over all the power angles as the eﬀective Hamiltonian is Ω dependent. i Mmag = i MR = αi  2. For instance. using MR as the master measure. Some of the αl.±2 i bl. For the C7. i the maximum being the best sequence.6. Also other generated tensors are more harmful to the evolution then others.m i MR = (6.±2 terms to the rest the undesired tensor terms.15) In Eq. Given n diﬀerent sequences. so the MR should weight the z terms more.m is easily extracted by the trace operation αl.±2 l=2. and better i theoretical measure is the ratio of the α2. (6. extra Iz terms are worse then Ix.16) i Mmag = max(Mmag ) i MR = max (MR ) The goal for any given master cycle is to maximize both of these measures.m (6.m )† dΩ .0 terms do not eﬀect the evolution.m αl. the ratio of the i 0 magnitudes of the ith sequences α2.m components.±2 should i be large for good signal–to–noise in an experimental setting. MR . We are also only interested in the magnitude as any sign changes are easily phased away.m  = T r Hef f (Ω) (Tl. Mmag .m )† T r Tl.±2 to the original ‘nonpermuted’ sequence α2.3. PERMUTATIONS 157 proportional to a single tensor.m are the relevant weighting factors. because the T2.
3. P. we then must select the largest subset. {1. P/N = 0).6. Generate all the possible Ksubsets given N sequences and the length P . PERMUTATIONS 158 6. 3}.e. the available Ksubsets are {1. 4.3 Algorithmic Flow There are many algorithmic sub components to optimally generate and measure each master cycle. P . generate all the permutations.3. Using the K–subsets can produce similar permutations from another K–subset. Determine the number of distinct sub–units. so when we generate the permutation list we must remove all the duplicates and reverse duplicates (i. The task is to generate all the valid permutations of length P from subset of length N . and {2. 3}. and P = 3. The general method to generate the permutations lists can be summarized below. The time symmetry all the RSS sequences indicates that a master cycle will give exactly the same results as the reverse of that master cycle. P is not necessarily a factor of N (i. Because we have a time constrained problem. Then to generate all the valid permutations we need to generate all the K–subsets of N of length N . For instance a Ksubset of {1. 2. 1. 3. N . N .e. 3} . An arbitrary permutation can be constructed from any subset of the available sub–units. of the N sub–units that are factors of P . there will be a maximum number of sub–units that we can apply. [1234] is the same as [4321]). The number of sub–units in a given subset is called N . Ksubsets that are the reverse of another Ksubset are also removed from further calculation. then generate all the permutations of length P . so we need to remove any duplicates. 2. For instance if N = 2. For each Ksubset. 2}. Determine the sequence length (the total number of individual sub–units to apply).
Remove all the reverse permutations from the master list. On a 700 MHz Pentium III Xeon. 3}. Since there can be duplicate permutations within diﬀerent Ksubset permutation lists. As an algorithmic point.6. For instance the permutation {2. To save much computational eﬀort. 3. P . 2} is already included. 2}. {3. 1. The generation of a length 40 list proved too memory intensive as over 1047 permutations would need to be searched for duplicates and reversed duplicates. 3. The removal of the reversed permutations for large permutations sets (sets larger than 20 items) is a computational limitation because of the searching problem. 3}. 3. 3. Given a list that large (even if it was cut in half) would also prove prohibitively time demanding to calculate the eﬀective Hamiltonians for all 1047 /2 sequences. For a 2 × 20 segment there are 92504 unique non reversible permutations and for a 4 × 12 segment there are 184800 unique non reversible permutations. {2. 2. permutations for a length 20 system takes 2 hours. 1} would be removed because {1. We will apply the label N × P for each of the calculated permutation data segments. 1}. . This saves much time when both generating the permutations and comparing them for duplication. 2. 6. the generation of unique nonreversed. {1. {2. the simulations used either N = 2 or N = 4 distinct sequences. and {3. 1.3. 5. 1}. 2}. PERMUTATIONS 159 and P = 3 has these permutations: {1. remove all duplicates. for the sequences was found to be 20 for N = 2 and 12 for N = 4 that can be handled without huge memory and time requirements. The maximal length. only integers representing each sub–unit are necessary to perform the permutation calculation. the removal of reverse permutations and duplicates can occur at the time each permutation is generated. In practice.
The program is able to distribute the powder average over multiple workstations to allow linear scaling of the calculation. The tensors themselves can be generated using similar permutation techniques as the N × P by labeling each spin by an integer and each direction as an integer. Table 6.24. Ww.8.24. WwwW (8. O (2.20) oO. OowW (8. PERMUTATIONS Table 6. W (2.40) oOOo.32.12.16. The next stage is generating all of the spin tensors desired to ﬁgure out the tensor components.8.16.8.16. O.48) o.20) 4 (4.12.16.32. Table 6.16.W (4.12) basic Units (length) o. N P 2 (2.32. .20) w.2 shows the calculated sequences calculated for the postC7 permutations. To calculate all the eﬀective Hamiltonians and their spin operator components in a 2 × 20 system for 2 spins spinning at a rotor speed of 5000 Hz for 1154 powder average points took 5 days on a single processor.40) wO.48) oOWw.24.4. wWWw. 40) Because the 2 × 20 and 4 × 12 was our computer limitation.8.16.8.48) oOOo.4.4.6. and many of the desired sequences are applied for many more cycles then 20 or 12.8.32.16.32.32. wWWw (8.OW (4.8.16.3.w.12) oOOo.8.16. wW (4.2: 160 Sequence Permutation set for the eﬀective Hamiltonian calculations of the postC7 sequence.40) ow .12. oW (4. Oo (12. the third stage of the program allows the ability to use any number of sub permutations for each index N.3 shows which tensors were used for this study.
l) 2nd order spherical Tl.m (i. m = −l. η = 0) 1 13 C CSA (δiso = −1544.l) Table 6.6.i ) 1st order spherical Tl. η = 0) SS1 2 13 C –13 C dipole (δani = 2146) 1 2 13 C CSA (δiso = 1254. η = 0) 1 13 C CSA (δiso = −1544.. y.. .3: Spin operators and tensors generated to probe the eﬀective Hamiltonians Type Form i 1st order Cartesian Ir .4. Table 6.4.m . (l = 1. All units are in Hz System Label Spin parameters 13 C CSA (δiso = 1254. η = 0) 1 13 C CSA (δiso = −1544.m Tl . δani = 12345. There are three diﬀerent sets of data corresponding to 3 diﬀerent numbers of nuclei. δani = 12345.. z) (i. 2. δani = 8552.i ) . η = 0) 2 13 C –13 C dipole (δani = 2146) SS2 1 2 1 H –13 C dipole (δani = 4506) 3 1 1 H –13 C dipole (δani = 7564) 3 2 13 C CSA (δiso = 1254. The next few ﬁgures will show the data. (l = l = 1..4 shows the spin system parameters for each set.1 Data and Results Sequence Measures There were over 500000 diﬀerent C7 master cycles simulated and measured. giving both the MR and Mmax for all the sequences of a given number of C7 cycles as well as the best one showing you the tensor components. y. δani = 12345. (r = x. DATA AND RESULTS 161 Table 6. δani = 8552. η = 0) 2 13 C –13 C dipole (δani = 2146) 1 2 1 H –13 C dipole (δani = 4506) 3 1 SS3 1 H –13 C dipole (δani = 7564) 3 2 1 H –13 C dipole (δani = 2150) 4 1 1 H –13 C dipole (δani = 4562) 4 2 1 H –1 H dipole (δani = 15546) 3 4 6. z) i i 2nd order Cartesian Ir Ir (r = r = x. 2.4: Spin System parameters for the three sets of permutations. m = m = −l.4 6. δani = 8552.i ) (i .
4.17 Tensor weight(bl. To handled the data more eﬀectively. but as stated before. The relevant weighting factors for Eq.5. then the average Hamiltonian series converges must faster as each order falls oﬀ like (1/ωr )n . which could potentially be used to construct sequences and phase cycles that remove these pathways.6.0476 (a factor of 1) 162 The spin parameters were chosen to avoid any rotational resonance conditions with either the spinning rate or the RF amplitude. For the C7 this implies a maximum rotor speed of about 20 kHz.j T2.17 are given in Table 6. The higher order tensors can give better insight as to the coherence pathways the error terms follow. They were also chosen as a representative organic molecule so dipole and CSA values are consistent with peptides and amino acids (although no one amino acid was used). As with most of the RSS sequences. .5: Relevant weighting factors for Eq. only the ﬁrst order tensors of Table 6.m ) i Iz 0. For other CN sequences ωr is much less. an experimental limit is usually reached with an RF power of 150kHz.±1 0. The higher order tensors were recorded.0 0 (a factor of 0) i.2381 (a factor of 5) i 0. 6. the commutation relations of 2 spin 1/2 nuclei reduce them all to ﬁrst order tensors. If the spinning rate (and consequently the RF power) are high. so 5000 kHz is a good value to investigate the properties of the sequences.0952 (a factor of 2) Ix.3 were considered in the MR measure.j T2.y i. The couplings were chosen to be all the same order of magnitude as the spinning frequency ωr = 5kHz as to be in the regime of truly nonideal conditions where the beneﬁts of the permutation cycles would show more dramatically. 6. DATA AND RESULTS Table 6.
8.32 respectively.20.14 show the data recorded for the SS1 system for a total sequence length of 4.24.32 respectively.24.4.16.6.15–6.32.24.20 show the data recorded for the SS2 system for a total sequence length of 4. Figures 6.26 show the data recorded for the SS3 system for a total sequence length of 4.8. Figures 6.16.12. DATA AND RESULTS 163 Figures 6.40.12.6–6.16.12. .48 respectively.8.21–6.
2 0.25 0.0 0.1 T 2.1 T2.6: Spin system SS1 with 4 total number of C7s applied.2 0.1 T2.1 T2.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 (o)4 postC7 (woOW)1 M (oOOo)1 M 10 mag R 1 1 Figure 6.15 0.2 0.1 0.1 0.15 i M R 0.05 0.25 0. Total number of postC7's=4 i Mmag 3 2 1 5 3 10 15 20 Sequence (i) 25 3 i M R 0. . DATA AND RESULTS 164 Spin System.1 0.4. SS1.1 5 10 15 20 Sequence (i) 25 bin count 1 bin count 1 2 3 4 2 2 1 0 0 i Mmag 0 0.6.1 0.3 T2.
12 0.0 0. DATA AND RESULTS 165 Spin System.1 0.1 T 2.7: Spin system SS1 with 8 total number of C7s applied.05 200 400 600 800 1000 1200 Sequence (i) 4 i Mmag 2 5 0 0 2 4 i Mmag 6 8 0.1 T2.08 200 400 600 800 1000 1200 Sequence (i) 15 bin count bin count 10 20 15 10 5 0 0. Total number of postC7's=8 6 0.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 10 3 Magnitude of Tensor 10 2 1 oooooooo postC7 wWwwwWWW M oOOoOooO M mag R 10 1 Figure 6.1 0.16 0.1 T2.25 T2.6.2 0. .1 0. SS1.2 0.1 0.4.1 0.15 i M R 0.1 T2.14 i M R 0.
2 0.0 0.25 T2.1 T2.1 T2.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 postC7 oooooooooooo oooooOoOOOOO M mag 1 10 1 oOOooOoOOooO M R Figure 6.1 0. SS1.05 400 600 Sequence (i) 800 10 i M R 0.1 0.1 T 2.14 0.1 0.8: Spin system SS1 with 12 total number of C7s applied.1 0.16 6 i Mmag 4 2 200 8 bin count bin count 6 4 2 0 0 0 0.2 0.4.1 T2.12 0.15 i M R 0. DATA AND RESULTS 166 Spin System.08 200 400 600 Sequence (i) 800 5 2 4 i Mmag 6 8 0. .6.1 0. Total number of postC7's=12 0.
2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 postC7 oooooooooooooooo owowowOWowOWOWOW M mag oOoOOoooOOOooOoO MR 1 10 1 Figure 6.1 T2.06 2000 4000 6000 8000 10000 12000 Sequence (i) 80 bin count 60 40 20 0 0 150 bin count Sequence (i) 100 50 2 4 6 i Mmag 8 10 0 0.9: Spin system SS1 with 16 total number of C7s applied.4.2 0.1 T2.2 0.1 T 2.08 0.15 i M R 0.16 i 0. SS1.1 0.18 i Mmag 8 6 4 2 2000 4000 6000 8000 10000 12000 0.1 T2.14 M R 0.6.1 0.25 T2.12 0. DATA AND RESULTS 167 Spin System.1 0. Total number of postC7's=16 0. .0 0.1 0.05 0.1 0.
2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 1 postC7 oooooooooooooooooooo 10 oooOOOOOOOOoooooooOO M mag ooooOoooooOOOOoOOOOO M R 1 Figure 6.08 0.06 4 x 10 0. SS1. Total number of postC7's=20 14 12 i Mmag 10 8 6 4 2 5 1500 bin count bin count 10 Sequence (i) 15 4 x 10 0.10: Spin system SS1 with 20 total number of C7s applied. DATA AND RESULTS 168 Spin System.12 0. 1 5 10 15 Sequence (i) 1500 1000 1000 500 500 0 0 5 i Mmag 10 15 0 0.1 T 2.6.1 T2.1 0.4. .2 0.12 i M R 0.1 T2.1 0.04 0.1 0.08 0. 1 i M R 0.1 T2.0 0.14 T2.06 0.
08 0.6.4. .1 0.04 0. 0 0.2 0.08 0.1 0.1 T 2.1 i M R 0. SS1. Total number of postC7's=24 0.11: Spin system SS1 with 24 total number of C7s applied.1 0.14 T 2.1 T2.2 1 Ix 0 Ix 1 Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 postC7 oooooooooooooooooooooooo t owOWOWowowowowOWOWOWOWow M mag owOWOWowowOWowOWOWowowOW M R 10 1 Figure 6.1 T2.06 0.12 0.1 0.12 10 i Mmag 8 6 4 200 400 600 800 1000 1200 1400 Sequence (i) 15 10 i M R 0.06 200 400 600 800 1000 1200 1400 Sequence (i) bin count 5 bin count 4 6 8 10 12 10 5 0 2 i Mmag 0 0.1 T 2. DATA AND RESULTS 169 Spin System.
6.08 i 0.1 0.4.1 0. Total number of postC7's=32 15 i Mmag 10 0.1 0.12: Spin system SS1 with 32 total number of C7s applied.2 0.12 i M R 0.06 0.12 0. 1 0.1 T2.1 T 2.04 5000 10000 15000 Sequence (i) bin count 50 0 0 5 i 10 Mmag 15 20 bin count 100 0.08 5 0. SS1. . 1 M R 0.06 5000 150 10000 15000 Sequence (i) 200 150 100 50 0 0.0 0.1 T2.14 T2.1 T2.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 10 1 1 oooooooooooooooooooooooooooooo postC7 owOWOWowowowowOWowOWOWOWOWowowOW M mag owowOWowowOWOWOWowowowOWOWowOWOW M R Figure 6. DATA AND RESULTS 170 Spin System.
SS1. .06 0. 5 2 Sequence (i) 5 2.1 T2. x 10 5 5 3000 0.1 0.4.1 T 2. Total number of postC7's=40 i Mmag 15 i M R 0. 5 2000 1500 bin count 1000 500 0 0 bin count 1 1.1 0.1 T2. 1 0.6.04 0. x 10 5 10 5 2000 1000 5 10 i Mmag 15 20 0 0.06 0. 5 1 1. 5 2 Sequence (i) 2.0 0.12 T2.1 0.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 10 1 postC7 oooooooooooooooooooooooooooooooooooooooo owOWowOWowOWowowowowOWOWOWOWowOWowOWowOW M mag owowowowowowOWOWOWOWowowowowOWOWOWOWOWOW M R 1 Figure 6.1 T2.2 0. DATA AND RESULTS 171 Spin System. 1 0.08 i M R 0.08 0.13: Spin system SS1 with 40 total number of C7s applied.
6.07 0.0 0.11 i M R 0.08 0.1 T2.12 T2. DATA AND RESULTS 172 Spin System.2 0. Total number of postC7's=48 10 i Mmag 8 6 4 2 500 30 bin count bin count 1000 1500 2000 2500 Sequence (i) 20 15 10 5 0 0.11 0.1 T2.09 i M R 0.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 10 1 1 postC7 oooooooooooooooooooooooooooooooooooooooooooo OowWoOWwoOWwoOWwoOWwOowWOowWoOWwoOWwOowWOowWOowW M mag oOOooOOooOOooOOooOOooOOowWWwwWWwwWWwwWWwwWWwwWWw M R Figure 6.1 T2.4.1 0.1 0.14: Spin system SS1 with 48 total number of C7s applied.1 T 2.08 500 1000 1500 2000 2500 Sequence (i) 20 10 0 0 5 i Mmag 10 15 0.1 0.1 0.09 0. SS1.1 0. .
14 0.11 0.1 0.1 5 10 15 20 Sequence (i) 25 bin count 1 bin count 0.5 5 3 10 15 20 Sequence (i) 25 3 i M R 0.5 1 i Mmag 1. SS2.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 oooo postC7 woOW M mag OooO M R 1 Figure 6.15: Spin system SS2 with 4 total number of C7s applied.2 0.08 0.1 0.1 T2.1 T 2.14 0.13 0.1 T2.1 0.4.12 0.5 1 0. Total number of postC7's=4 2 i Mmag 1.1 T2.1 0. .0 0.5 2 2 1 0 0 0 0.6.12 i M R 0.16 T2. DATA AND RESULTS 173 Spin System.5 2 2.
6.1 T2.08 0.1 0.1 0.4. .1 T 2.16: Spin system SS2 with 8 total number of C7s applied.2 0.08 200 400 600 800 1000 1200 Sequence (i) 10 10 5 5 0 0 1 2 i Mmag 3 4 5 0 0.14 0.1 i M R 0.0 0.12 0.12 R 0.1 0.14 i M 0.1 T2.06 0. DATA AND RESULTS 174 Spin System.1 T2.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 10 1 oooooooo postC7 wWwoOoOW M mag oOOoOooO M R 1 Figure 6.1 0. SS2.16 T2. Total number of postC7's=8 4 i Mmag 3 2 1 200 400 600 800 1000 1200 Sequence (i) 15 bin count bin count 15 0.
0 0.12 M R 0. Total number of postC7's=12 5 i 4 Mmag 3 2 1 200 15 400 600 800 15 200 400 600 800 Sequence (i) Sequence (i) i M R 0.14 0.6. DATA AND RESULTS 175 Spin System.4.12 0.1 0. SS2.1 T2.1 i 0.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 postC7 oooooooooooo wWwWwwwWWWWw M mag MR oooOOOoooOOO 1 10 1 Figure 6.06 0. .1 T 2.2 0.16 T2.08 0.1 0.1 0.08 bin count 10 5 bin count 2 4 6 10 5 0 0 i Mmag 0 0.1 T2.17: Spin system SS2 with 12 total number of C7s applied.1 T2.1 0.
1 T2.1 0.0 0.1 0.12 0.2 0.08 0.06 Sequence (i) 2000 4000 6000 8000 10000 12000 Sequence (i) 100 bin count 100 bin count 2 4 6 50 50 0 0 i Mmag 0 0.4.18: Spin system SS2 with 16 total number of C7s applied.1 M R 0.14 T2.1 0.12 i Mmag 5 4 3 2 1 2000 4000 6000 8000 10000 12000 i M 0. . DATA AND RESULTS 176 Spin System.06 0.6.1 T2.1 R 0.1 T2.1 T 2.04 0. Total number of postC7's=16 0. SS2.08 i 0.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 postC7 oooooooooooooooo wWWwwWwwwWWWwWwW M mag MR ooooOOOoOoooOOOO 1 10 1 Figure 6.
Total number of postC7's=24 0. SS2. .1 0.1 T2.1 0.4.07 200 400 600 800 1000 1200 1400 Sequence (i) 15 bin count 15 bin count 200 400 600 800 1000 1200 1400 Sequence (i) 2 10 10 5 5 0 0 2 4 i Mmag 6 8 0 0.1 0.12 T2.1 0.08 i M R 0.1 T2.1 0.09 0.11 6 i Mmag M R 4 i 0.08 0.2 0.0 0.1 T 2.1 T2.06 0. DATA AND RESULTS 177 Spin System.19: Spin system SS2 with 24 total number of C7s applied.6.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 postC7 oooooooooooooooooooooooo owowowOWowOWowOWowOWOWOW M mag owOWowowOWowOWowOWOWowOW M R 1 10 1 Figure 6.
06 5000 10000 15000 Sequence (i) 100 50 2 4 i Mmag 6 8 10 0 0.08 i M R 0. 1 0.1 0.2 0.6. .09 M R 0.0 0.12 T2.06 0.20: Spin system SS2 with 32 total number of C7s applied.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 10 1 1 postC7 oooooooooooooooooooooooooooooooo wOwOoWoWwOwOwOoWoWoWwOoWoWoWwOwO M mag oOOowWWwoOOowWWwoOOowWWwoOOowWWw M R Figure 6.1 T 2.1 T2.1 T2. 1 i 0.1 T2.07 0.4. SS2.04 0. DATA AND RESULTS 178 Spin System.1 0.1 0.08 0. Total number of postC7's=32 8 i Mmag 6 4 2 5000 200 bin count 150 100 50 0 0 10000 15000 Sequence (i) 150 bin count 0.
5 1 1.1 0.5 Mmag 1 0.0 0.12 0.1 0.21: Spin system SS3 with 4 total number of C7s applied.6.1 i M R 0.08 0.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 oooo postC7 woOW M mag ooOO M R 1 Figure 6. .1 T2.4.12 0. SS3.16 T2.5 2 2.14 0.5 5 3 10 15 20 Sequence (i) 25 3 i M R 0. DATA AND RESULTS 179 Spin System.14 0.1 5 10 15 20 Sequence (i) 25 bin count 1 bin count 0.2 0. Total number of postC7's=4 2 i 1.5 2 2 1 0 0 i Mmag 0 0.1 T2.1 0.1 T2.1 T 2.
2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 postC7 oooooooo woOWwoOW M mag woOwoWOW M R 1 10 1 Figure 6. Total number of postC7's=8 i Mmag 0.2 0. SS3.1 0.1 T2.08 0. .08 1 200 400 600 800 1000 1200 Sequence(i) 20 bin count bin count 15 10 5 0 0 20 15 10 5 0 0.06 200 400 600 800 1000 1200 Sequence(i) i M R 0.6.1 T 2.1 1 i Mmag 2 3 4 0.1 0.1 T2.16 T2.0 0.1 0.12 3 2 0.1 i M R 0. DATA AND RESULTS 180 Spin System.12 0.22: Spin system SS3 with 8 total number of C7s applied.14 0.4.1 T2.
1 10 5 0 0 1 i Mmag 2 3 4 5 0.1 0.1 i M R 0.06 200 400 600 Sequence (i) 800 i M R 0.12 1 10 1 Figure 6.12 0.08 1 200 15 bin count bin count 400 600 Sequence(i) 800 8 6 4 2 0 0.23: Spin system SS3 with 12 total number of C7s applied.1 T2.1 T 2.14 0.16 T2.0 0.1 0.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 postC7 oooooooooooo wWwWwwwWWWWw M mag MR oOooooOOOoOO 0. SS3.1 T2.1 0.6. Total number of postC7's=12 4 i Mmag 3 2 0.08 0.1 T2.2 0.4. DATA AND RESULTS 181 Spin System. .
06 2000 4000 6000 8000 10000 12000 100 Sequence (i) 100 Sequence (i) bin count 50 bin count 1 2 3 4 5 50 0 0 i Mmag 0 0.08 0. SS3.1 T2.08 0.6.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 10 postC7 oooooooooooooooo wWwWWWWWwwwwwWwW M mag MR oOoOooooOOOOoOoO 1 1 Figure 6.1 0. 1 0.04 0.12 T2.4.1 0.1 T 2.1 0. DATA AND RESULTS 182 Spin System.06 i M R 0. Total number of postC7's=16 4 i Mmag 0.1 T2.2 0. 1 3 2 1 2000 4000 6000 8000 10000 12000 i M R 0.0 0. .24: Spin system SS3 with 16 total number of C7s applied.1 T2.
1 0.1 T2.1 0. Total number of postC7's=24 5 i Mmag 4 3 2 200 400 600 800 1000 1200 1400 0. .09 M R 0.08 0.07 0. SS3.25: Spin system SS3 with 24 total number of C7s applied.07 200 400 600 800 1000 1200 1400 Sequence (i) 15 bin count 15 bin count Sequence (i) 10 10 5 5 0 1 2 3 4 i Mmag 5 6 0 0.1 0.2 0.11 T2.06 0. DATA AND RESULTS 183 Spin System.1 T 2.4.0 0.09 M R 0.1 T2.6.1 0.08 i 0.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 10 postC7 oooooooooooooooooooooooo t oWwOwOwOoWoWwOwOoWoWwOoW M mag owOWOWowOWOWowowOWowowOW M R 1 1 Figure 6.1 i 0.1 T2.
DATA AND RESULTS 184 Spin System.1 T2.09 0.26: Spin system SS3 with 32 total number of C7s applied. SS3.07 0. Total number of postC7's=32 6 5 i Mmag 4 3 2 5000 10000 15000 Sequence (i) 200 bin count bin count 150 100 50 0 0 150 i M R 0.1 0.6.07 5000 10000 15000 Sequence (i) 100 50 2 i Mmag 4 6 8 0 0.09 0. 1 0.06 0. 1 0.1 T 2.4.11 T2.2 1 Ix 0 Ix Iy 0 Iy I1 z I0 z 3 10 Magnitude of Tensor 2 10 postC7 oooooooooooooooooooooooooooooooo t owOWowOWowOWowowowOWOWowOWOWOWow M mag wOoWoWwOwOwOoWwOwOoWoWoWwOoWwOoW M R 1 10 1 Figure 6.1 T2.1 T2.1 0.0 0. .08 i M R 0.1 0.2 0.08 0.
4. In both the SS2 and the SS3 data sets.6. Of course calculating the average Hamiltonian sequence is a much harder problem then simply probing the eﬀectiveness of a given sequence. There is a problem using decoupling in RSS sequences. DATA AND RESULTS 185 6. and the generated sequences are thus diﬀerent for each spin system. we should be able to use this as our decoupling ﬁeld.e. This is in fact what we were looking for. Because ωrfC can be large itself.6 lists the best permutation cycles found for each spin system and total number of C7 calculated. the protons were not decoupled from the 13 C nuclei. One can then use similar the symmetry 13 . creating more recoupling then decoupling. 135. Table 6. Small permutation cycles (i. the total number of C7 was less then 8) give expected results from symmetry considerations. As we are applying a continuous rotation to the carbons.4. The power ratio condition has been empirically decoupling found to be ωrf > 3 ∗ ωrfC [108. any master cycles where the total number of C7s is greater then 8 give results seemingly uncorrelated results using only the basic symmetry principles. the results can be consolidated into a single sentence. It does not necessarily mean that for the longer sequences. 136]. In fact the generated sequences are the best conditions to cancel higher order terms. Ideal decoupling of the protons would give the same sequences as SS1 . that symmetry considerations could not have produced the desired sequences. For large N or large spinning rates in RSS 13 sequences this is very hard to satisfy experimentally.2 Transfer Eﬃciencies The amount of data is quite overwhelming however. the RF power applied to the protons must be larger then the power applied to the carbons for decoupling otherwise we would eﬀectively synchronize the motions of the two nuclei. which if the full average Hamiltonian sequence was generated could have designed by hand.
4. DATA AND RESULTS 186 Table 6.6: Best C7 permutation sequences for each spin system and C7 cycle length. System permutations length best permutation 4 oOOo 8 oOOoOooO 12 oOOooOoOOooO 16 oOoOOoooOOOooOoO SS1 20 ooooOoooooOOOOoOOOOO 24 owOWOWowowOWowOWOWowowOW owowOWowowOWOWOWowowowOWOWowOWOW 32 40 owowowowowowOWOWOWOWowowowowOWOWOWOWOWOW 48 oOOooOOooOOooOOooOOooOOowWWwwWWwwWWwwWWwwWWwwWWw length best permutation 4 OooO 8 oOOoOooO SS2 12 oooOOOoooOOO 16 ooooOOOoOoooOOOO owOWowowOWowOWowOWOWowOW 24 32 oOOowWWwoOOowWWwoOOowWWwoOOowWWw length best permutation 4 ooOO 8 woOwoWOW SS3 12 oOooooOOOoOO 16 oOoOooooOOOOoOoO 24 owOWOWowOWOWowowOWowowOW 32 wOoWoWwOwOwOoWwOwOoWoWoWwOoWwOow .6.
we looked at the transfer eﬃciencies over a range of oﬀset conditions. considerations to also remove higher order 1 H −13 C cross terms. 2 The basic C7 is only eﬀective when the diﬀerence between two oﬀsets is zero. with dramatic increases when 1 2 a rotational resonant condition is met (δiso − δiso  = nωr ). . comparing them to the original 1 Unitary evolution cannot increase the polarization of the system.1) C7 2τ r n 13C 2 ρo=0 C7 n S(nt)=Tr[ ρf.±2 term the less the 1 H −13 C cross terms as our polarization is conserved1 .2)] Figure 6. DATA AND RESULTS 187 1H n ρo=I(z.27: Pulse sequence. The next few ﬁgures will show the transfer eﬃciencies for each of the best sequences as determined from the total C7 length of 4.6. 12. with a sharp drop after a positive oﬀset diﬀerence over the spinning rate. To investigate the eﬀectiveness of the generated sequences. The eﬃciencies for the original C7 and the postC7 are shown in Figure 6. The applied pulse sequence is shown in Figure 6.27. and 16.n) 2τ r 13C 1 ρo=I(z.I(z. 8.28 for the SS1 system changing only the oﬀset parameters of 13 C 1 and 13 C .4. initial density matrices and detection for a transfer eﬃciency measurement. For systems SS2 and SS3 the search found permutation sequences that minimized these as well simply because the larger a T2. The postC7 is eﬀective over a much wider range of oﬀsets.
28: Transfer eﬃciencies for a 4 fold application of the basic C7 and the postC7 for the SS1 system as a function of 13 C 1 and 13 C 2 oﬀsets at ωr = 5kHz.63848 20 0 0 0 00.4.3 0 0 0 0 20 20 0 .3 1 20 10 13C 1 offset (kH 0 10 z) 20 20 et (k 13 C 2offs 10 0 20 20 15 0 10 5 0.4 0.4 0 5 0 10 15 20 13C offset (kHz) 2 Figure 6.48497 Min: 0.2 1 efficiency 0 0.1754 Max: 0.1 0 0 15 20 15 10 5 0 5 10 postC7 after 4 applications Max: 0. 2 01 0.6. 2 0.3 0 0 10 20 Hz) 0 0.1 0. Offsets For the Basic C7 sequences C7 after 4 applications Min: 0.074039 20 0. 1 0 0 0.4 0. 4 0. .1 0 0 0.4 . DATA AND RESULTS 188 Transfer Efficiencies for SS1 spin System vs.3 0. 2 0.5 0.
The ﬁrst is the 3D proﬁle. There are two diﬀerent views for each data set. and 16.32.6.33 and 6. the second is the gradient–contour plot for numerical representations. Data for spin system SS2 are shown in Figures 6. which gives a better view of the form of transfer function. DATA AND RESULTS 189 postC7 sequence given a length of 4. 12.30.31 and 6. Data for spin system SS1 are shown in Figures 6.34.4. . 8.29 and 6. and data for spin system SS3 are shown in Figures 6.
DATA AND RESULTS 190 Transfer Efficencies for SS1 spin System vs.2668 Max: 0. 13 C 1 and 13 C 2 oﬀsets at .7384 Min: 0. Offsets postC7 after 4 applications Max: 0.58538 Min: 0.46827 Best 8 permutation Max: 0.4.6.062431 postC7 after 8 applications Min: 0.074039 Best 4 permutations Max: 0.28484 0 10 10 20 20 10 1 20 0 13C offset (kHz) 1 10 20 20 10 0 0 13C offset (kHz) 2 13C offset (kHz) 1 10 20 20 10 0 10 20 13C offset (kHz) 2 Figure 6.8.16 fold application of the postC7 and the best permutation cycles for the SS1 system as a function of ωr = 5kHz.12.52092 Min: 0.48497 Min: 0.48441 Min: 0.29: 3D transfer eﬃciencies plots for a 4.29139 Best 12 permutation Max: 0.18802 Max: 0.30411 postC7 after 12 applications Max: 0.37582 postC7 after 16 applications 1 efficiency Best 16 permutation Min: 0.2434 Min: 0.52742 Max: 0.
2 0.2 0. 1 0.5 0. 1. 2 01 0.2 0.1 10 5 0 5 0 10 15 20 20 20 02 5 0 5 0 10 0 15 0 20 0.3 0 0 postC7 after 12 applications 0 0. 0.3 0 0 . 4 0 0 0. 0.1 0.1 0 0.7 0. 3 0 0.2 0 0 0 0. DATA AND RESULTS 191 Transfer Efficencies for SS1 Spin System vs. 2 0 0.4 0.6.1 0 0 0.1 0.3 0.1 0.1 0.3 0 7 0.1 0 0 Best 8 permutation 0 0 0.2 0.12.3 postC7 after 8 applications 0 0.6 0.3 0. 1 0.4 0 0.3 0.2 0.3 0 0.1 0.2 0.2 1 0 0. 0.2 0. 2 postC7 after 16 applications 13C offset (kHz) 1 20 0 0 20 Best 16 permutation 0 0. 3 0 0 .2 0 0 01 0. Best 12 permutation 0 0 0.2 0. 2 0 0. Offsets postC7 after 4 applications 0 0. 0.4 0. 1 0.2 0.4 0.4 0.3 Best 4 permutation 0.1 0 20 20 15 0 0. 1 0.1 0 0 0.4 0 0 0. 2 1 1 0.1 0.8. 4 4 0. 5 0.16 fold application of the postC7 and the best permutation cycles for the SS1 system as a function of 13 C 2 13 C 1 0.3 0 0.1 0 0.4 0. .2 0.30: Contour–gradient transfer eﬃciencies plots for a 4.1 0 .1 0 0.5 6 0 0.2 0 and oﬀsets at ωr = 5kHz. 5 0. 3 0 .1 0. 2 0 0.4.1 0 0.1 0.1 15 10 1 0. 1 0.4 0. 0. 1 0.4 0.2 0.1 0. 1 13C offset (kHz) 2 13C offset (kHz) 2 Figure 6. 0 0.
16 fold application of the postC7 and the best permutation cycles for the SS2 system as a function of ωr = 5kHz.8.49721 Min: 0.31: 3D transfer eﬃciencies plots for a 4.41516 Min: 0.13172 Best 8 permutation Max: 0.38451 Min: 0.24223 postC7 after 12 applications Max: 0.042667 Best 4 permutation Max: 0.21751 Best 12 permutation Max: 0.32125 Min: 0.34481 Min: 0.038105 postC7 after 8 applications Max: 0.6. DATA AND RESULTS 192 Transfer Efficiencies for SS2 spin System vs.17093 postC7 after 16 applications 1 0 1 20 0 20 13C offset (k 1 Hz) 20 Max: 0.32221 Min: 0.31563 1 0 Best 16 permutation Max: 0.4.17917 efficiency 20 ) et (kHz 13 C 2offs 0 1 20 0 20 13C 1 offset (kHz) 20 0 20 ) et (kHz 13 C 2offs Figure 6.69145 Min: 0.12.42129 Min:0. Offsets postC7 after 4 applications Max: 0. 13 C 1 and 13 C 2 oﬀsets at .
1 0 1 0 10 13C offset (kHz) 2 0 20 20 20 10 00 1 0 0 10 13C offset (kHz) 2 20 Figure 6.2 0. 0. 4 postC7 after 8 applications 0 0. 0. 2 0 .4.3 0.3 0.2 0.2 0 0 0 0. 3 Best 8 permutation 0 1 0.1 10 0. 1 0 1 2 0.2 0.1 0.2 0 0.8. 3 0 0 0 3 0.3 0. 0 0.6 0. 1 0.1 0 0. DATA AND RESULTS 193 Transfer Efficencies for SS2 Spin System vs. 2 0.32: Contour–gradient transfer eﬃciencies plots for a 4. 1 0 0 postC7 after 16 applications 13C offset (kHz) 1 20 0. 0 20 0 20 0.16 fold application of the postC7 and the best permutation cycles for the SS2 system as a function of 13 C 2 13 C 1 0 1 0 0. 4 2 0.2 0 0 0. 5 0. 1 0. 3 0 0 0. and . 0 0 0. 2 0 postC7 after 12 applications 0 0. 4 0.2 0.2 0 0 0.2 0 0.1 0 0. 1 0.6.1 1 0. 1 0 0 0.2 20 0. 4 0. 3 0. 1 0 Best 16 permutation 0 0 0. 1 0 0 0.3 0 0. 1 0.1 0.1 oﬀsets at ωr = 5kHz. 3 0.1 0 0 0. Offsets postC7 after 4 applications 0 Best 4 permutation 0 0 0.3 0.2 0. 1 0. 2 0 .1 Best 12 permutation 0 0 0 0.2 0. 2 0 0 0. 0.12.5 0 0.
26525 Min: 0.34514 Min: 0.074689 Max: 0.12. 13 C 1 and 13 C 2 oﬀsets at .086714 Best 8 permutation Min: 0.4.019694 postC7 after 8 applications Max: 0.13923 Best 12 permutation Max: 0. DATA AND RESULTS 194 Transfer Efficiencies for SS3 Spin System vs.054667 Best 4 permutation Max: 0.26315 efficiency 20 0 20 20 ) 13C et (kHz 1 offset (kHz) 13 C 2offs 0 20 0 20 13C 1 offset (kHz) 20 0 20 ) et (kHz 13 C 2offs Figure 6.13129 1 0 1 Best 16 permutation Min: 0. Offsets postC7 after 4 applications Max: 0.6.16 fold application of the postC7 and the best permutation cycles for the SS3 system as a function of ωr = 5kHz.25827 Min 0.12314 Max: 0.20661 postC7 after 12 applications Max: 0.12492 postC7 after 16 applications 1 0 1 20 Max: 0.8.5872 Min: 0.25547 Min: 0.30833 Min: 0.33: 3D transfer eﬃciencies plots for a 4.
3 Best 4 permutation 0 0 0.6.3 2 0 0. 1 0.2 0 0. Offsets postC7 after 4 applications 0 0.2 0.1 0.2 20 0 0 0.2 0 0.16 fold application of the postC7 and the best permutation cycles for the SS3 system as a function of 13 C 2 13 C 1 and oﬀsets at ωr = 5kHz. 4 0 postC7 after 8 applications 0 0. 2 0.5 0.1 0.10. 2 0 0 0 0.1 0 0 0 0.4 0. 1 0 0.1 0 0 0 postC7 after 12 applications 0 0.2 0 0.4. 0 2 0. 1 0. .2 0 0 0.1 0 0.1 0 Best 16 permutation 0 0 0.1 0.2 0. 2 0. 1 1 0.2 0. Best 8 permutation 0.1 10 20 20 10 0 13C offset (kHz) 2 10 20 20 20 0.12. DATA AND RESULTS 195 Transfer Efficencies for SS3 Spin System vs. 1 0 0 0.1 0 0 postC7 after 16 applications 13C offset (kHz) 1 20 0. 1 2 0 0. 2 0 0 0 1 0 0. 2 0.3 0 0. 0. 1 Best 12 permutation 0 0 0 0.1 0 0 0 0 0.0.34: Contour–gradient transfer eﬃciencies plots for a 4.8.1 0 0 10 13C offset (kHz) 2 20 Figure 6. 3 0 0.
These complete transfer diagrams are shown in Figures 6. attempts to compensate for errors in the total sequence. 6. use a permutation approach to the problem as . We can generate similar views as in Figure 6. However. Determination of the best super–cycle for these longer sequences becomes a tedious task as many orders of the average Hamiltonian must be calculated and analyzed for weaknesses. however.37 for systems SS1 . a task for the general spin system is nearly impossible analytically. They rely on generation of a speciﬁc zeroth order average Hamiltonian.5. the permuted sequences are always better then the standard postC7 sequences. As the sequence becomes longer and longer. in real systems the desired eﬀect this zeroth order average Hamiltonian is destroyed by many experimental and system speciﬁc parameters. The resulting transfers are on average 50% better in eﬃciency transfer and 25% more stable (the standard deviation across speciﬁc oﬀset value) then the original sequence. Internal compensation and posting techniques as designed to act on a small part of the total sequence. To correct for these problem. This process. CONCLUSIONS 196 As the Figures clearly show. SS2 and SS3 respectively.5 Conclusions RSS sequences represent a large class of the pulse sequences used in solidstate NMR. Super–cycling takes these internally compensated sequences through application of phase shifts. for small number of sequence applications tends to work very well to compensate for errors. The implementation of symmetry is usually broken into two parts.356. the symmetry of the system and the zeroth order average Hamiltonians are used to cancel other terms in the expansion. We can.6. this simple approach breaks down as higher and higher order terms and errors accumulate.4 by taking slices along each of the above ﬁgures using each the best permutation cycle.
δiso 12000 Hz 0 Hz 12000 Hz 12000 Hz 0 Hz 12000 Hz 0 Hz 12000 Hz  12000 Hz Figure 6.5.6 0. 4 Original postC7 4 6 8 10 12 14 16 0. CONCLUSIONS 197 0.4 0.6 4 6 Best Permutation 8 10 cycles 12 14 16 13C1.6. 2 0.35: Transfer Eﬃciencies using the postC7 and the best permutated cycles across over diﬀerent cycles for the SS1 spin system.8 0.4 0.4 0. .2 0.δiso 12000 Hz 12000 Hz 12000 Hz 0 Hz 0 Hz 12000 Hz 0 Hz 12000 Hz 12000 Hz         13C2.2 0 0.2 0 0.6 Transfer Effiency 0.
1 0 0.5.4 0.δiso 12000 Hz 0 Hz 12000 Hz 12000 Hz 0 Hz 12000 Hz 0 Hz 12000 Hz  12000 Hz Figure 6.2 0.3 0.6.36: Transfer eﬃciencies using the postC7 and the best permutated cycles across over diﬀerent cycles for the SS2 spin system.δiso 12000 Hz 12000 Hz 12000 Hz 0 Hz 0 Hz 12000 Hz 0 Hz 12000 Hz 12000 Hz         13C2.1 0 0. 1 0.4 0. .2 4 6 Best Permutation 8 10 cycles 12 14 16 13C1.1 0. 4 4 6 Original postC7 8 10 12 14 16 0. 2 0.5 Transfer Effiency 0. CONCLUSIONS 198 0.3 0. 3 0.2 0.
15 0.05 0.1 0 0.5.2 4 6 Best Permutation 8 10 cycles 12 14 16 13C1.15 4 6 Original postC7 8 10 12 14 16 0. CONCLUSIONS 199 0.1 0.1 0.δiso 12000 Hz 0 Hz 12000 Hz 12000 Hz 0 Hz 12000 Hz 0 Hz 12000 Hz  12000 Hz Figure 6.25 0.2 0.3 0. .6.37: Transfer eﬃciencies using the postC7 and the best permutated cycles across over diﬀerent cycles for the SS3 spin system.4 0.3 0.05 0 0.5 Transfer Effiency 0.2 0.δiso 12000 Hz 12000 Hz 12000 Hz 0 Hz 0 Hz 12000 Hz 0 Hz 12000 Hz 12000 Hz         13C2. 1 0.
CONCLUSIONS 200 we have shown here to generate markedly improved sequences using only the well known symmetry principles used for the shorter sequences.6.5. .
Using gradient or simulate annealing methods would almost certainly ﬁnd local minima and not global minima unless it can sample most of the space. The ﬁrst is the vast dimensionality of the system.201 Chapter 7 Future Expansions The permutation technique described in chapter 6 is limited only by computer power and memory. For the study of the postC7 the total simulation time for all of the permutations in all of the spin systems took about 3 weeks running on 4 diﬀerent processors. We are still not even sure if the function at point a parameter point A has any method of getting us to A + 1 without a look–up . We cannot say it is continuous or single valued making gradient searching techniques inaccurate and not robust. The second problem is that there is little information about the functional form of this ‘minimization’ function. One may ask themselves why every permutation had to be calculated? Why not perform a minimization (or maximization in this case)? There are two fundamental problems associated with using normal minimization methods. If we had not optimized each step as we have done in chapters 4 and 2 this problem would have taken months to accomplish.
and why the permutation approach seems to be the best alternative at the time. We wish to ﬁnd the best gene/phenotype we can. EVOLUTIONARY ALGORITHMS (EA) 202 table approach. or even both.1. 7. There two diﬀerent classes of EA using the blending mechanisms. There are techniques for both search very large dimensional spaces as well as the ‘look–up’ table problem and both are easily tackled using Evolutionary type Algorithms (EA)[137. then running all the permutations seems to be the only way. just mutated versions . unless we leave gradient/distance based minimization techniques.1 Evolutionary Algorithms (EA) The basics of an EA begin with a ‘gene. I will discuss both in turn a give the basic structure for implementing both of these structures and the problems that should be able to be tackled using them. It will die oﬀ if the child has inherited most of the bad ones. The ﬁtness can simply be an evaluation of the function giving a χ2 or distance value or in our case the MR value.’ A gene in the biological sense propagates forward by creating children. These children contain some mixture of both parents due to crossover/breeding and mutations. 138]. a ‘bad’ gene has a worse ﬁtness then the parents. If it is a look–up table. the children have no ‘parents’. The relevance of ‘good’ and ‘bad’ from an algorithmic point of view is given as follows: a ‘good’ gene is one which has a ﬁtness better or close to the parents value. A gene is then only likely to survive if it has a suitable mixture of the good qualities from the parents. 141].e. if it only uses mutation (i. It is for these reasons that these techniques failed to produce any reasonable answers. If the blending mechanism includes both crossover and mutation it is called a Genetic Algorithm (GA)[139.7. 140. or Neural Networks (NN).
w.)). EVOLUTIONARY ALGORITHMS (EA) 203 of itself) then this is call Evolutionary Programming (EP)[142. Here p is the number of parents.W.o.W..w. These are usually called p–c Evolutionary strategies[145.7. where as EPs use only the pure evolution. O.. These ESs are best shown pictorially as shown in Figure 7. A sub class of GAs is the Diﬀerential Evolution (DE)[144] where a child can have more then one parent.).o.3. In fact it would be better to pick genes that span the extremes of the parameter space. Using the methodology we discussed in chapter 6 our gene is of the length of the total number of cycles in we wish to optimize. a strategy is typically devised for moving one step in a generation. or that it will take many iterations to get out of the local area. the ‘plus’ and ‘comma’ methods..O. (w.c) .2.o..W. Initially we would generate a random set of these genes for the parents and evaluate the ﬁtness for each of them (here it would be MR ) . (O. w. If one does not span the entire range initially. 146]. Two arbitrary parent genes and a resulting child gene is shown in Figure 7.).1) for an EP algorithm is shown in Figure 7.e. W ). This means including genes with all of one type of base–pair (i. ES(p.O. and c is the number of children to be generated.w. (W.). A ‘base–pair’ is then one of the 4 possible symmetry cycles (o. 143].w.. A diagram showing the .w.. Which ever strategy is used we need to make our problem and the parameter space ﬁt into a gene..W..O... The next step is where a GA or DE or EP algorithmic decision comes into play as well as the rates of mutation and cross over points.o..W.O. it is likely that the minimization will remain in the local range initially generated. GAs use both a type of forced evolution from the parents and a pure evolution from mutation. A diagram showing the plus and comma type of ES(2. There are many diﬀerent ways of implementing such a gene using the RSS structure based on the number of assumption we can/wish to make.1.1 along with a sub class of ESs. However the blending is performed. (o.
c > Environment considerations (steep decents require much fewer children) p/c > small=fast local convergence large=slower 'global' convergence method > plus.1. .1: The standard evolutionary strategy methods and controls. EVOLUTIONARY ALGORITHMS (EA) 204 p Parents c children "Plus strategy" "Comma strategy" p Parents c children c children Survive both Survive children Controls p.7.keeps best solution always commaallows local minima escape Figure 7.
and a diagram showing the plus and comma type of ES(3. type of values we want to search for.5. Both EP and GA methods are better suited for the ‘4–switch’. [120].2) for a GA algorithm is shown in Figure 7. (w. we should be able to use the DE types for more complex searches. This phase changing should provide even better cycles then the permutation method as hinted by the super–cycle of Ref. In the permutations examples. we have limited ourselves to the basic symmetry 4–switch. . DE methods are particular suited for continuous parameter space problems where the ‘add’ function in Figure 7.2: An arbitrary permutation cycle parent genes and resulting child.W.o.1. For instance we can arbitrarily change the phase of each RSS cycle and ﬁnd a minimum. This leads us to another branch of searches were we may begin to ﬁnd diﬀerent ‘post’ methods for an internal compensation of sequences. plus and comma type of ES(3.1) for a DE algorithm is shown in Figure 7.O). using the gradient techniques and for a much less general problem[147]. EVOLUTIONARY ALGORITHMS (EA) 205 p1 o o w O O w W o p2 O o o o w w O O crossover point=3 c1 O o o O no mutations O w W o Figure 7.5 makes some sort of since.4. This type of internal search has been performed before.7. however.
22 R O keep M =0.23 R O w o o o O o o mutate O o W M =0. EVOLUTIONARY ALGORITHMS (EA) 206 Evolutionary Programming PlusES(2.1) p1 o CommaES(2.14 R O O keep w o W O o w O O o o o M =0.1) p1 o p2 O p2 O ext generation.1) strategy.1.22 R O w M =0.7.14 R w w w O w O w w c2 for the next generation .3: Evolution Programming (EP) generation step for an ES(2. mutate p1 and p2 again to find another ch c2 for the n ild Keep p1 and Keep W O W keep keep M =0.23 R M =0.26 R c2 O O w O o O M =0.26 R O W o o o w O mutate w W o o c1 c2 c1 o w o O Figure 7. o M =0.
O o o o o keep for the next generation.2) p1 p2 p3 M =0.14 R M =0.4: Genetic Algorithm (GA) generation step for an ES(3.7.04 R CommaES(3.1.2) p1 p2 p3 M =0. perform breeding again to find anothe r child keep keep keep keep .23 c2 Figure 7.22 o o o w O o O o o o O O o o o w o w o o o o o o o O O O o random parent & breed & mutate random parent & breed & mutate o o o W o w O O o O o W w o o O o O o W o O w W c1 MR=0. EVOLUTIONARY ALGORITHMS (EA) 207 Genetic Algorithm PlusES(3.23 c2 MR=0.23 R M =0.2) strategy.22 c1 MR=0.04 R O O o o o o c2 & c1 Keep W W O O o w w w w o O O w w o o MR=0.14 R M =0.23 R M =0.
7.04 R O o O o o o ext generation.14 R M =0. perform breeding again to find another ch c1 for the n ild Keep W O W O o w w w w o O w O w o O o O o o w o w o o o o o o o O O o O 'add' 3 parents & breed 'add' 3 parents & breed o W w O o W o O c1 MR=0.1) p1 p2 p3 M =0.23 Figure 7.04 R CommaES(3.23 MR=0. O o W o O w W o c1 O o o o o o o o keep keep keep keep .23 R M =0. EVOLUTIONARY ALGORITHMS (EA) 208 Differential Evolution PlusES(3.5: Diﬀerential Evolution (DE) generation step for an ES(3.1) strategy.1) p1 p2 p3 M =0.23 R M =0.14 R M =0.1.
a one and two level feed–forward (FF) NN (there are others. 7. then it should be able to give the correct outputs based on arbitrary inputs for a particular model. Figure 7. in order to make predictions it must be trained. 149.2 Neural Networks There is astounding amount of literature on neural networks. To go through all but the most basic of neural networks here. Like a brain. NEURAL NETWORKS 209 input layer INPUT 0 i0 INPUT 1 i1 hidden layer w01 w11 1 Level NN output layer b1 OUTPUT 0 input layer INPUT 0 i0 INPUT 1 i1 w10 w01 w00 w11 hidden layer 2 Level NN w02 b1 w20 output layer OUTPUT 0 Figure 7.7. A neural network is simply a number of nodes connected by weights to other nodes based on neurons in a brain. it must be trained with known inputs and outputs. 150]. such as self organizing .6: Basic 1 and 2 layer feed–forward neural networks.6 represents a subclasse of NNs used for predictive purposes. The training process is the hardest part of designing a NN and is crucial to the prediction power of a network[151]. For a network to begin to model a function. so I will only attempt to scratch the surface of their capability.2. much of it organized towards programmers[148. would be much too much. It is designed to recreate a function when the function is unknown.
1. This minimization process does not try to determine any relevance or node removal.1) The relevance function is used to model a typical neuron electronic switch where if the electric potential is high enough. NEURAL NETWORKS 210 networks). i. There is also a bias value bi applied to each value. if it is not. Each node. The relevance function is usually a sigmoid function (1/(1 + exp(−A))) or a simple step function. Ni . If we choose a learning rate of 1 then the network will quickly adjust the weights to match that one data set.2. The simplest technique is back propagation. The sigmoid allows for a small range of valid signals to pass. we can specify a ‘learning–rate’ (a value from 0. wij of the previous layer passed through a ‘relevance’ or threshold (R) function which determines its value. the signal will halt there. if it is 0 then no adjustments occur with the diﬀerences. A simple back propagation fully connected FFNN C++ class is given in Appendix A. This is where the majority of the NN literature is based as in essence we wish to choose only the relevant data[152] and determine the connectivity and weights as a minimization process. Ni = R connected nodes wij Ij + bi (7. it simply uses the diﬀerences in the desired values and the current output values to adjust the weights and biases. Given a training data set. eﬀects the next layer(s) (called the hidden layers) and these eﬀect the output layers. where as the step function does not.7. To train a network one typically picks a ﬁxed number of hidden layers. One can even use evolutionary techniques to perform the minimization[153]. it will pass on the signal. So what could a NN provide in our optimization of the generic RSS (and potentially . in a FFNN uses the weights. The ‘feedforward’ implies that the input layer.5.. and then manipulates the weights wij and the connectivity of the nodes.1) that determines the amount to adjust the weights given the distances. I.
7. which we could then infer possible symmetry classes to the general analytical problem. we should be able to see two things. simply looking at the statistical distributions of one RSS sequence has demonstrated that the solution NMR seeks is usually the far outlier (our evolutionary searches). are unknown.3 Final Remarks The majority of this thesis is geared to the creation of fast numerical techniques and algorithms to simulate NMR situations as fast as one can. however.±2 condition and see what the network produces as an answer. while most of the data ﬁts into a nice distribution (the neural .3. We can now tackle problems that were before next to impossible before this assembly. We now have a huge training data set. 7. As we generate more and more data we could even output a phases of an RSS sub–cycle from the tensor coeﬃcients. these newer algorithmic techniques may not give much new insight to the basic problems and control of NMR. and I think more interesting. From these particular values. The ﬁrst. The genetic and neural networks applications are some of the more interesting paths to follow as their implementation is now some what possible given the processes shown here. it could be possible to ﬁnd the most relevant pathways. only a much smaller subset of them. For all I know. The second. If nothing else. FINAL REMARKS 211 other) sequences? As you can see the amount of data generated from the permutation study is quite large. is the information that can be obtained from the relevant weights and connectivity’s. In essence one could train a NN given the permutation order as the output. a properly trained NN can at least give us the good answers from a sequence abstraction without having to go through each permutation or phase shift. is attempting to input our maximum T2. and most obvious. However. After the training. There results. and the tensor coeﬃcients as the input.
FINAL REMARKS 212 networks). while the evolutionary techniques ﬁnd the optimum. .3. Optimal control of a given NMR system may soon be reduced to a trained neural network producing ﬁrst order results (pulse sequences).7.
MA. Stroustrup. Pound. 1961). The design and evolution of C++ (AddisonWesley. in Proceedings of the London Mathematical Society. Rev. W. Oxford. 70 (78). 1994). Slichter. C. Series 2 (Oxford University Press. The Principles of Nuclear Magnetism: The International Series of Monographs on Physics (Clarendon Press. . Boston. Principles of Magnetic Resonance (Springer. Phys. Wokaun. Turing. Vol. and P. Spin Dynamics: Basics of Nuclear Magnetic Resonance (John Wiley & Sons. 2000). [4] M. Sebesta. Abragam. 2001). Heidelberg. 1989). [2] F. H.213 Bibliography [1] E.. M. [7] A. Boston. Ernst. 474 (1946). Hansen. [5] R. Oxford. G. Oxford. M. R. and R. MA. Bodenhausen. W. Levitt. [6] C. Principles of Nuclear Magnetic Resonance in One and Two Dimensions (Clarendon Press. Bloch. Concepts of Programming Languages 5/E (Addison Wesley Higher Education. 37 (1946).. H. 69 (12). Torrey. 42. W. 1978). [8] R. Rev. [9] B. [3] A. P. 1936). Purcell. Chichester. V. ltd. and A. Phys.
Croz. Unruh. J. A. [16] U. Philadelphia. Helm. Sorensen. C++ Report 9(7). 1999). Demmel. C. C++ Report 7(5). [15] E. A. R. and J. in Proceedings of the 1st International Scientiﬁc Computing in ObjectOriented Parallel Environments (ISCOPE’97). C++ Report 7(5).Towards a New Paradigm of Software Engineering (Addison Wesley. MA. S. Master’s thesis. [19] T. J. Johnson. (AddisonWesley. 1995). 1997). J. (Society for Industrial and Applied Mathematics. Gamma. Boston. 26 (1995). Blackford. Clint. Boston. Greenbaum. automatically Tuned Linear Algebra Software (ATLAS). Dongarra. [11] E. G. Siek. PA. Generative Programming . third ed. and D. [21] W. R. 1997). W. [20] J. L. third ed.BIBLIOGRAPHY 214 [10] B. Anderson. Lecture Notes in Computer Science (SpringerVerlag. Veldhuizen. McKenney. (1997). Pescio. Veldhuizen and M. . University of Notre Dame. D. Jernigan. 1994. 1994. Veldhuizen.. 2001). [14] T. Czarnecki. The C++ Programming Language. Design Patterns: Elements of Reusable ObjectOriented Software (AddisonWesley. Boston. Hammarling. Eisenecker and K. Bischof. C. MA. S. Z. [12] E. LAPACK Users Guide. [17] C. Stroustrup. C++ Report 7(4). [18] N. [13] T. Myers. 42 (1995). MA. 36 (1995). Bai. aNSI X3J16940075/ISO WG21462. E. New York. Vlissides.
BIBLIOGRAPHY
215
[22] J. L. Hennessy and D. A. Patterson, Computer Organization and Design (Mogan Kaufmann Publishers Inc., San Francisco, Ca, 1998). [23] F. Bloch, Phys. Rev. 70 (78), 460 (1946). [24] M. Mehring and V. A. Weberruss, Object–Oriented Magnetic Resonance (Academic Press, London, UK, 2001). [25] A. Vlassenbroek, J. Jeener, and P. Broekaert, J. Mag. Reson. A 118, 234 (1996). [26] J. Jeener, A. Vlassenbroek, and P. Broekaert, J. Chem. Phys. 103(9), 1309 (1995). [27] G. Deville, M. Bernier, and J. M. Delrieux, Phys. Rev. B 19(11), 5666 (1979). [28] T. Enss, S. Ahn, and W. S. Warren, Chem. Phys. Lett. 305, 101 (1999). [29] W. S. Warren, S. Lee, W. Richter, and S. Vathyan, Chem. Phys. Lett. 247, 207 (1995). [30] R. N. Zare, Angular Momentum: Understanding Spatial Aspects in Chemistry and Physics (John Wiley & Sons, Inc., Chichester, 1988). [31] S. Wi and L. Frydman, J. Chem. Phys. 112(7), 3248 (2000). [32] W. Warren, W. Richter, A. Andreotti, and B. Farmer, Science 262 (5142), 2005 (1993). [33] W. Richter and W. Warren, Conc. Mag. Reson. 12(6), 396 (2000). [34] S. Lee, W. Richter, S. Vathyam, and W. S. Warren, J. Chem. Phys. 105(3), 874 (1996). [35] W. S. Warren, S. Y. Huang, S. Ahn, and Y. Y. Lin, J. Chem. Phys. 116(5), 2075 (2002).
BIBLIOGRAPHY
216
[36] Q. H. He, W. Richter, S. Vathyam, and W. S. Warren, J. Chem. Phys. 98(9), 6779 (1993). [37] R. R. Rizi, S. Ahn, D. C. Alsop, S. GarrettRoe, M. Mescher, W. Richter, M. D. Schnall, J. S. Leigh, and W. S. Warren, Mag. Reson. Med. 18, 627 (2000). [38] W. Richter, M. Richtera, W. S. Warren, H. Merkle, P. Andersen, G. Adriany, and K. Ugurbil, Mag. Reson. Img. 18, 489 (2000). [39] W. Richter, S. Lee, W. Warren, and Q. He, Science 267 (5198), 654 (1995). [40] J. H. V. Vleck, Electric and Magnetic Susceptibilities (Oxford University Press, Great Britan, 1932). [41] P. Deuﬂhard, Numerische Mathematik 41, 399 (1983). [42] J. R. Cash and A. H. Karp, ACM Transactions on Mathematical Software 16, 201 (1990). [43] J. Stoer and R. Bulirsch, Introduction to Numerical Analysis (SpingerVerlag, New York, 1980). [44] P. Deuﬂhard, SIAM Rev. 27, 505 (1985). [45] W. H. Press, S. A. Teukolsky, W. T. Vetterling, and B. P. Flannery, Numerical Recipes in C, The Art of Scientiﬁc Computing (Cambridge University Press, Cambridge, 1997). [46] C. W. Gear, Numerical Initial Value Problems in Ordinary Diﬀerential Equations (Prentice–Hall, Englewood Cliﬀs, NJ, 1971).
BIBLIOGRAPHY
217
[47] J. H. Shirley, Phys. Rev. B. 138, 979 (1965). [48] A. Schmidt and S. Vega, J. Chem. Phys. 96 (4), 2655 (1992). [49] R. Challoner and M. CA, J. Mag. Reson. 98 (1), 123 (1992). [50] O. Weintraub and S. Vega, J. Mag. Reson. Ser. A. 105 (3), 245 (1993). [51] T. Levante, B. H. Baldus, M.and Meier, and R. Ernst, Mol. Phys. 86 (5), 1195 (1995). [52] J. W. Logan, J. T. Urban, J. D. Walls, K. H. Lim, A. Jerschow, and A. Pines, Solid State NMR 22, 97 (2002). [53] J. Walls, K. Lim, J. Logan, J. Urban, A. Jerschow, and A. Pines, J. Chem. Phys 117, 518 (2002). [54] J. Walls, K. Lim, and A. Pines, J. Chem. Phys. 116, 79 (2002). [55] M. H. Levitt, D. P. Raleigh, F. Creuzet, and R. G. Griﬃn, J. Chem. Phys. 92(11), 6347 (1990). [56] D. P. Raleigh, M. H. Levitt, and R. G. Griﬃn, Chem. Phys. Lett. 146, 71 (1988). [57] M. G. Colombo, B. H. Meier, and R. R. Ernst, Chem. Phys. Lett. 146, 189 (1988). [58] Y. Zur and M. H. Levitt, J. Chem. Phys. 78(9), 5293 (1983). [59] M. Eden, Y. K. Lee, and M. H. Levitt, J. Magn. Reson. A. 120, 56 (1996). [60] M. Hohwy, H. Bildse, and N. C. Nielsen, J. Magn. Reson. 136, 6 (1999). [61] T. Charpentier, C. Fermon, and J. Virlet, J. Magn. Reson. 132, 181 (1998). [62] M. H. Levitt and M. Eden, Mol. Phys. 95(5), 879 (1998).
BIBLIOGRAPHY
218
[63] H. Geen and r. Freeman, J. Mag. Reson. 93(1), 93 (1991). [64] P. Bilski, N. A. Sergeev, and J. Wasicki, Solid State Nuc. Mag. Reson. 22(1), 1 (2002). [65] A. Baram, J. Phys. Chem. 88(9), 1695 (1984). [66] M. Mortimer, G. Oates, and T. B. Smith, Chem. Phys. Lett. 115(3), 299 (1985). [67] A. Kumar and P. K. Madhu, Conc. Mag. Reson. 8(2), 139 (1996). [68] P. Hazendonk, A. D. Bain, H. Grondey, P. H. M. Harrison, and R. S. Dumont, J. Mag. Reson. 146, 33 (2000). [69] M. Eden and M. H. Levitt, J. Mag. Reson. 132, 220 (1998). [70] D. W. Alderman, M. S. Solum, and D. M. Grant, J. Chem. Phys. 84, 3717 (1986). [71] M. J. Mombourquette and J. A. Weil, J. Mag. Reson. 99, 37 (1992). [72] L. Andreozzi, M. Giordano, and D. Leporini, J. Mag. Reson. A 104, 166 (1993). [73] D. Wang and G. R. Hanson, J. Mag. Reson. A 117, 1 (1995). [74] S. J. Varner, R. L. Vold, and G. L. Hoatson, J. Mag. Reson. A 123, 72 (1996). [75] M. Bak and N. C. Nielsen, J. Mag. Reson. 125, 132 (1997). [76] S. K. Zaremba, Ann. Mat. Pura. Appl. 4:73, 293 (1966). [77] J. M. Koons, E. Hughes, H. M. Cho, and P. D. Ellis, J. Mag. Reson. A 114, 12 (1995). [78] L. GonzalezTovany and V. BeltranLopez, J. Mag. Reson. 89, 227 (1990). [79] C. V. B., H. H. Suzukawa, and M. Wolfsberg, J. Chem. Phys. 59(8), 3992 (1973).
BIBLIOGRAPHY
219
[80] H. Conroy, J. Chem. Phys. 47(2), 5307 (1967). [81] V. I. Lebedev, Zh. Vychisl. Mat. Fiz. 16, 293 (1976). [82] V. I. Lebedev, Zh. Vychisl. Mat. Fiz. 15, 48 (1975). [83] J. Dongarra, P. Kacsuk, and N. P. (eds.)., Recent advances in parallel virtual machine and message passing interface: 7th European PVM/MPI users group meeting (Springer, Berlin, 2000). [84] P. Hodgkinson and L. Emsley, Prog. Nucl. Magn. Reson. Spectrosc. 36, 201 (2000). [85] M. Bak, J. T. Rasmussen, and N. C. Nielsen, J. Magn. Reson. 147, 296 (2000). [86] S. Smith, T. Levante, B. Meier, and R. Ernst, J. Mag. Reson. 106a, 75 (1994). [87] Y. Y. Lin, N. Lisitza, S. D. Ahn, and W. S. Warren, Science 290 (5489), 118 (2000). [88] C. A. Meriles, D. Sakellariou, H. Heise, A. J. Moule, and A. Pines, Science 293, 82 (2001). [89] H. Heise, D. Sakellariou, C. A. Meriles, A. Moule, and A. Pines, J. Mag. Reson. 156, 146 (2002). [90] T. M. Brill, S. Ryu, R. Gaylor, J. Jundt, D. D. Griﬃn, Y. Q. Song, P. N. Sen, and M. D. Hurlimann, Science 297, 369 (2002). [91] R. McDermott, A. H. Trabesinger, M. Muck, E. L. Hahn, A. Pines, and J. Clarke, Science 295, 2247 (2002). [92] J. D. Walls, M. Marjanska, D. Sakellariou, F. Castiglione, and A. Pines, Chem. Phys. Lett. 357, 241 (2002).
Blanton. Zilm. and N. Computing and Networks Division CERN Geneva. 1976). Numerische Mathematik 41.T. Jakobsen. Palmas. J. Technical report. 129 (1994). 373 (1983). and D. 43 (1991). J. and A. P. Havlin. Switzerland (unpublished). M. M. A 107(2). Ser. Mag. Canet. University of Tennessee (unpublished). Havlin. and A. Technical report. Hohwy. [105] M. J. J. [96] F. H.BIBLIOGRAPHY 220 [93] R. 86 (3). B. Haeberlen. [97] G. Phys. Massachusetts Institute of Technology (unpublished). T. J. [95] E. [99] M. 111(4). 123. [100] M. G. Levitta. Chinga. H. 145 (1996). Pines. in preparation. K. H. K. Mag. Sun. Lusk. H. Principles of High Resolution NMR in Solids (Springer. [104] M. Mansﬁeld. and A. Chem. New York. Mag. Reson. 2686 (1998). Frigo and S. Chem. 1983). Terao. [106] P. Tekely. B. [102] R. Johnson. Pines. W. [101] M. W. Deuﬂhard. Nielsen. Bader and P. J. James. 1511 (1999). Zwanziger. J. G. Turner. Mazur. and P. Reson. A. Pines. Mehring. 470 (1990). [103] U. Augustine and K. Stehling. Science 254 (5028). 163 (2002). Technical report. H. (2002). C. 157. P.. Reson. Levitt. G. . R. Reson. [94] M. Phys. Berlin. 108. [98] M. Mag. High Resolution NMR in Solids: Selective Averaging (Academic Press. Eden. Eden and M. Park. J. T.
C. 6951 (1995). Phys. H. Brinkmann. D. S. C. Phys. Mueller. Havlin. Phys. Walls. 131 (1995). C. Eden. J. E. G. Levitt. B. and R. . Bennett. Carravetta. A. 341 (1989). Hohwy. Eden. and R. Lakshmi. R. Magn. Soc. 3387 (1996). 105 (9). and M. E. Auger. Hohwy. [114] R. [118] M. M. Kurur. Hatcher. J. A 113(1). Lett. 205 (2000). 372 (2002). 110(16). Bielecki. Chem. Eden. Chem. [109] A. and H. A. Phys. Nielsen. Griﬃn. 173. Levitt. C. Phys. 363 (34). Sun. K. and A. Fesik. M. M. M.BIBLIOGRAPHY 221 [107] M. Levitt. and M. G. M. Pines. Lee. Chem. [119] M. and M. Phys. Jaroniec. 242(3). W. Dabbagh. M. J. 8539 (2000). Am. Chem. C. Lett. M. P. G. Lett. Levitt. Phys. [111] X. Rienstra. Rienstra. [116] W. Kolbert. Chem. J. 117(10). Chem. M. N. N. Chem. A. X. H. H. W. Levitt. J. Lett. Demco. Chem. Ernst. [110] M. Phys. and R. Johannessen. and M. S. Gottwald. [117] C. D. O. [112] A. and R. 120(41). J. Bush. Kolbert. 112(19). H. 321. Zhao. Zhao. J. H. B. Chem. 4973 (2002). Lett. [113] J. Griﬃn. 304 (1995). Phys. Reson. 353 (2001). Rienstra. C. 234. G. Helmle. 103 (16). M. Tycko and G. Brinkmann. Spiess. Phys. Q. 155(45). Phys. J. [115] Y. Chem. Pines. Lett. 461 (1990). L. J. Rienstra. Griﬃn. and M. M. Chem. V. G. and A. Sommer. 10602 (1998). 7983 (1999). Blanton. [108] A. Griﬃn. K. Chem.
11. Solid State Nucl. Freeman. C. 3218 (2000). 157 (1982). 2(4). [130] M. [124] M. J. A. Levitt. Shaka and J. Mag. 115(1). P. 79 (2002). S. Levitt. [121] A. Mag. Jaroniec. Reson. Mag. 53. 335 (1983). and T. 205 (1984). NMR Spectrosc. [122] M.BIBLIOGRAPHY 222 [120] A. . H. J. J. B. Reson. 130. and A. 357 (2001). Ruben. Murdoch. Freeman. [134] J. Keeler. H. Mag. 313 (1983). [131] M. 151 (1993). R. Fung. R. [125] Y. 60(2). Brinkmann. Rienstra. C. D. 47(2). [126] A. Levitt. Rienstra. J. [133] W. 236 (1984). 47 (1987). J. R. Keeler. 60(2). Phys. K. J. Mag. J. Am. Reson. [127] A. Jaroniec. and M. Shaka. Reson. J. 317 (1998). 43(3). P. 50(1). Mag. Shaka. Yu and B. and R. and A. Adv. J. H. J. Reif. 156(1). Weitekamp. W. Frenkiel. Warren. [123] B. Reson. H. Frenkiel. Mag. 502 (1981). Magn. Freeman.. Reson. and D. Levitt. 52(2). Mag. M. Reson. H. 145. C. 19. Freeman. [132] M. Prog. T. B. Frenkiel. Chem. 47 (1983). Freeman. Soc. Hohwy. Chem. Reif. Levitt. J. R. [128] A. G. Frenkiel. Reson. J. Griﬃn. 132 (2000). J. J. S. J. M. Mag.. J. Pines. Freeman. Reson. M. C. and T. J. and R. J. Brinkmann and M. C. 328 (1982). G. Bielecki. Keeler. P. J. H. and R. Mag. and G. Levitt and R. and T. Murdoch. Reson. auf der Gnne. Pines. S. Reson. A. H. J. Levitt. Hohwy. Warren. 122(13). Mag. Reson. M. [129] M.
NJ. B. Evolutionary Computation 3. [139] M. M. 1993). 1978). Fogel. with an added introduction by Fogel. K. 16: Classiﬁer Systems. in Proceedings of a the European Conference on Machine Learning (ECML93). 1994). Fogel. pp. 9463 (1998). Spears. River Edge. Whitley (Morgan Kaufmann. edited by P. this is a reprint of (Holland and Reitman. The Fossil Record. Vose. Selected Readings on the History of Evolutionary Computation (IEEE Press. 1995). pp. [143] in Proceedings of the 1995 IEEE International Conference on Evolutionary Computation. Sebald and L. Phys. and R. Vose. D. 667 of LNAI. 63–73. Griﬃn. Jong. Brazdil (Springer Verlag. Zhen. Ph. Vol. and H. B. Fogel (World Scientiﬁc Publishing. [137] D. [141] M. thesis. Rienstra. 442–459. B. [136] A. [140] M. Vienna. Bennett. C. edited by ???? (IEEE Press. Vol. edited by L. J. Cambridge. Massachusetts Institute of Technology. Vose. Lansbury. T. Bennett. Chap. W. San Mateo. B¨ck. [138] W.BIBLIOGRAPHY 223 [135] A. 1. D. Austria. de Garis. The simple genetic algorithm: foundations and theory (MIT Press. MA. 453 (1996). [142] in Evolutionary Programming – Proceedings of the Third International Conference. J. J. D. 1995. 1998). . D. edited by A. Massachusetts Institute of Technology. A. Griﬃths. in Foundations of Genetic Algorithms 2. P. Chem. Philadelphia. Piscataway. 1999). CA. 108(22).D. D. V. D. in Evolutionary Computation. 1993).
International Computer Science Institute. 253 (200).UC Berkeley (unpublished). [147] D. Chem. 400–407. 539 (1993). Lett. 319. L. edited by R. Technical report. Bishop. Arti. 1993). in Evolutionary Algorithms. D. Barnard. [152] A. Langley. Storn and K. N.. Lesage. Intel. Steele. 1999). [146] D. [148] C. Price. IEEE Transactions on Neural Networks 3(2). 1996). Albrecht. De Jong. Neural Networks for Pattern Recognition (Clarendon Press. [150] T. K. pp. F. M. 232 (1992). . New York. Sakellariou. Oxford. Davis. [153] X. pp. [149] A. Deugo and F. Hodgkinson. Emsley. edited by L. [151] E. Yao. Wien. Masters. Oppacher. C. Fogel. 1995). Neural networks in C++ (Wiley & Sons. 1994). Boston. M. Phys. Vose. B. 97. Practical Neural Network Recipes in C++ (Academic Press. D. 245 (1997). and C. Whitley (Springer. and L. Blum. [145] D. Blum and P. 89–109. R. in Artiﬁcial Neural Nets and Genetic Algorithms. International Journal of Intelligent Systems 8. and L. P. Reeves (Springer Verlag. New York. A. D.BIBLIOGRAPHY 224 [144] R.
net/). You can get it here http://waugh.1 A.cchem.225 Appendix A Auxillary code The code presented here are all dependant on the BlochLib library and tool kit. int prim > struct D { } . template<> struct i s p r i m e <0 . A. }. } . } .com/ and perhaps http://sourceforge. cc  & g r e p c o n v e r s i o n // The ’ g r e p ’ command p i c k s o u t o n l y t h e e r r o r s we want t o s e e // namely t h o s e w i t h t h e prime numbers // C l a s s t o c r e a t e ” o u t p u t ” a t c o m p i l e time ( e r r o r messages ) // g i v e s e r r o r template < int // no e r r o r on template < int on D=i n t i . i −1 >:: prim ) } . } . 0 > { D( int ) .1.1 General C++ code and examples C++ Template code used to generate prime number at compilation // −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− // Program by Erwin Unruh // Compile w i t h : g++ −c prime . // s p e c i f i c i n s t a n c e s t o s t o p template<> struct i s p r i m e <0 . // C l a s s t o compute prime c o n d i t i o n template < int p .0 > { enum { prim = 1 } . int i > struct i s p r i m e { enum { prim = ( ( p%i ) && i s p r i m e < ( i >2 ? p : 0 ) . . As a result you will probably need it to compile this code. The code examples here are relatively short and should be easily typed in by hand.berkeley.edu/blochlib/ (and if it is not there I hope to maintain a copy here http://theaddedones.1 > { enum { prim = 1 } . D=i n t i > struct D<i .
1 > ’ r e q u e s t e d prime . void f o o ( ) { P r i m e p r i n t <25> a . // w i l l produce an e r r o r i f ’ prim’==1 // ( i f we have a prime number ) void f ( ) { a . t h i s i s o n l y a code p i e c e s . void f ( ) { D<2. f (). cc : 3 0 : c o n v e r s i o n from ‘ P r i m e p r i n t <2 >::{ anonymous enum } ’ t o non−s c a l a r t y p e ‘D<2 . } }.A. cc : 2 5 : c o n v e r s i o n from ‘ P r i m e p r i n t <19 >::{ anonymous enum } ’ t o non−s c a l a r t y p e ‘D<19 . D<i . } /∗ ∗∗∗∗∗ e x p e c t e d o u t p u t from ’ P r i m e p r i n t <25> a . f ( ) . cc : 2 5 : c o n v e r s i o n from ‘ P r i m e p r i n t <13 >::{ anonymous enum } ’ t o non−s c a l a r t y p e ‘D<13 . // s p e c i f i c i n s t a n c e t o s t o p a t i =2 template<> struct P r i m e p r i n t <2> { enum { prim = 1 } . .1 > ’ r e q u e s t e d prime .1 > ’ r e q u e s t e d prime . f ( ) . cc : 2 5 : c o n v e r s i o n from ‘ P r i m e p r i n t <11 >::{ anonymous enum } ’ t o non−s c a l a r t y p e ‘D<11 .1. and N t h e v e c t o r l e n g t h we w i l l c a l l t h i s v e c t o r a ‘ coord<T. i t w i l l not work u n l e s s .1 > ’ r e q u e s t e d prime . ’ prime .1 > ’ r e q u e s t e d prime . } }.1 > ’ r e q u e s t e d prime . cc : 2 5 : c o n v e r s i o n from ‘ P r i m e p r i n t <5 >::{ anonymous enum } ’ t o non−s c a l a r t y p e ‘D<5 .1 > ’ r e q u e s t e d prime . cc : 2 5 : c o n v e r s i o n from ‘ P r i m e p r i n t <23 >::{ anonymous enum } ’ t o non−s c a l a r t y p e ‘D<23 . prim > d = prim . GENERAL C++ CODE AND EXAMPLES 226 // C l a s s t o i t e r a t e t h r o u g h a l l v a l u e s : 2 . // c a s c a d e from i t o 2 enum { prim = i s p r i m e <i . a. prim > d = prim . cc : 2 5 : c o n v e r s i o n from ‘ P r i m e p r i n t <3 >::{ anonymous enum } ’ t o non−s c a l a r t y p e ‘D<3 .N> ’ t o d i s t i n g u i s h betw een t h e g e n e r a l v e c t o r c a s e . cc : 2 5 : c o n v e r s i o n from ‘ P r i m e p r i n t <7 >::{ anonymous enum } ’ t o non−s c a l a r t y p e ‘D<7 . a .1. cc : 2 5 : c o n v e r s i o n from ‘ P r i m e p r i n t <17 >::{ anonymous enum } ’ t o non−s c a l a r t y p e ‘D<17 . i −1 >:: prim } .1 > ’ r e q u e s t e d prime .1 > ’ r e q u e s t e d ∗/ A.2 // // // // // // // C++ Template metaprogram to unroll a ﬁxed length vector at compilation time This meta program a p l i e s t o a f i x e d l e n g t h v e c t o r where t h e t e m p l a t e arguments f o r t h i s v e c t o r would be T=t h e d a t a t y p e s . i template < int i > struct P r i m e p r i n t { P r i m e p r i n t <i −1> a .
0 > : : a s s i g n ( ∗ this . // we g e t h e r e we s t o p t h e t e m p l a t e u n r o l l i n g template<> c l a s s c o o r d A s s i g n <0 . ApAssign ) . int I> class coordAssign { public : // t h i s i s t e l l s us when t o s t o p t h e c a s s c a d e enum { l o o p F l a g = ( I < N− 1 ) ? 1 : 0 } . rhs . // b e l o w a s p e c i f i c i n s t a n c e (N=0 . int N> template<c l a s s Expr T> coord<T . I =0) i s e x p r e s s e d // t o s t o p t h e t e m p l a t e c a s s c a d e template<int N. Op u ) . template<c l a s s CoordType . // a ’ q u i c k ’ meta program ( one t h e c o m p i l e r p e r f o r m s ) // t o u n r o l l l o o p s c o m p l e t e l y . N> : : operator=(const coordExpr<Expr T> &r h s ) { c o o r d A s s i g n <N. t h i s i s t h e ’ e n t r y ’ p o i n t . c l a s s Op> s t a t i c i n l i n e void a s s i g n ( CoordType& vec . expr ( I ) ) . GENERAL C++ CODE AND EXAMPLES 227 // one has d e f i n e d a v a l i d coord . c l a s s Expr . Expr expr . apply ( vec [ I ] . and t h e coordExpr c l a s s e s // Here i s t h e = o p e r a t o r t h a t p a s s e s // t h e e x p r e s s i o n To t h e ’ c o o r d A s s i g n ’ meta program template<c l a s s T . } }.A. T &b ) { a=b . expr . ( I + 1 ) ∗ l o o p F l a g > : : a s s i g n ( vec . } // This i s a ‘ ApAssign ’ c l a s s f o r // a d a t a t y p e ‘T ’ template<c l a s s T> c l a s s ApAssign { public : ApAssign ( ) { } s t a t i c i n l i n e void apply (T &a . // t h e c l a s s t o ’ k i l l ’ or s t o p t h e a bo ve one . N> &coord<T . return ∗ t h i s .0 > { public : template<c l a s s VecType . Op u ) { // a s s i g n t h e two e l e m e n t s u . u ) . c l a s s Op> s t a t i c i n l i n e void a s s i g n ( VecType& vec .1. . } }. . c l a s s Expr . . //move on t h e t h e n e x t i n s t a n c e ( I +1) c o o r d A s s i g n <N ∗ l o o p F l a g . Expr expr .
int L2colMAX=140. i 2=i +2 .++ i ) { c ( i . s t a t i c int U n r o l l s =5. i <l e f t o v e r . } // do t h e r e s t fo r ( . i 4=i +4. GENERAL C++ CODE AND EXAMPLES 228 { } }. j <b . template<c l a s s T> void mulmatUnrool ( matrix<T> &c . f o r ( k=0. matrix<T> &a . j ) more t h e n once typename matrix<T> : : numtype tmpBkj=b ( k . k ) ’ s f i r s t into the r e g i s t e r s matrix<T> : : numtype tmpAij=a ( i . matrix<T> : : numtype tmpAi2j=a ( i 2 . j ) . c o l s (). // f i g u r e o u t how many do not f i t i n t h e u n r o l l i n g l e f t o v e r=a . k . l e f t o v e r . k ) . matrix<T> : : numtype tmpAi1j=a ( i 1 .++ j ) { i =0. ∗ tmpBkj . k ) . rows (). j . tmpBkj . k ) . rows ( ) % ( U n r o l l s ) . A. ∗ tmpBkj . c ( i1 c ( i2 c ( i3 c ( i4 } } } } /∗ L2 b l o c k i n g ∗ ∗ ∗ ∗/ int L2rowMAX=140. i+=U n r o l l s ) { // a v o i d c a l c u l a t i n g t h e i n d e x e s t w i c e int i 1=i +1 . j )+=tmpAi2j .A. matrix<T> &b ) { int i . k ) . matrix<T> : : numtype tmpAi4j=a ( i 4 . rows ( ) . k ) ∗ b ( k . j ) . ∗ tmpBkj .1. k ) .k<b . i 3=i +3 . a( i . matrix<T> : : numtype tmpAi3j=a ( i 3 . // do t h e e l e m e n t s t h a t do not f i t i n t h e u n r o l l m e n t for ( . i <a .++ k ) { fo r ( j =0. // a v o i d r e a d i n g t h e b ( k .3 C++ code for performing a matrix multiplication with L2 cache blocking and partial loop unrolling. j )+=tmpAi1j . j )+=tmpAij ∗ . // read t h e typename typename typename typename typename c(i . j )+=a ( i . j )+=tmpAi4j . ∗ tmpBkj . j )+=tmpAi3j .1.
c o l s ( ) < L2colMAX ) { mulmatLUnrool (C. ++ ctC ) { out ( ctR . ctC ) . i <enR. j . B ) . // b e g i n i n g row i n d e x int enR . f or ( int i=beR . } } } // p u t s t h e sub m a t r i x e l e m e n t s i n t o t h e // p r o p e r p l a c e i n t h e o r i g i n a l template<c l a s s T> void putSubMatrixTo ( matrix<T> &in . return . rows ( ) . r e s i z e ( enR−beR . enC−beC ) .A. B . ++ ctR ) { for ( int j=beC . ctC =0. } // t h e int int int int number o f d i v i s i o n s a l o n g rows and c o l s rDiv=( int ) c e i l ( double (A. j <enC. j ) . matrix<T> &Orig .1. // e n d i n g row i n d e x int beC . ctR =0. ctC =0. ctR =0. rows ( ) ) / double ( L2colMAX ) ) . c o l s ( ) ) . rows ( ) ) / double (L2rowMAX ) ) . r e s i z e (A. cDiv=( int ) c e i l ( double (B . matrix<T>& B) { // r e s i z e our r e t u r n m a t r i x t o t h e p r o p e r s i z e C . int beR . // b e g i n i n g column i n d e x int enC ) // e n d i n g column i n d e x { out . ctC)= Orig ( i . GENERAL C++ CODE AND EXAMPLES 229 // makes t h e sub m a t r i x e l e m e n t s i n t o t h e // p r o p e r p l a c e from t h e o r i g i n a l template<c l a s s T> void makeSubMatrixFrom ( matrix<T> &out . } } } template<c l a s s T> void L2BlockMatMul ( matrix<T> &C . ++ ctC ) { Orig ( i . matrix<T> &A. BDiv=( int ) c e i l ( double (B . int beR .++i . // e n d i n g row i n d e x int beC . rows ()<L2rowMAX && B . C=0.++i . j )+=i n ( ctR . c o l s ( ) ) / double ( L2colMAX ) ) . i . j <enC. matrix<T> &Orig . // no need t o do t h i s i f m a t r i x i s l e s s t h e n L2 s i z e i f (A. i <enR.++j . // b e g i n i n g row i n d e x int enR . // b e g i n i n g column i n d e x int enC ) // e n d i n g column i n d e x { f o r ( int i=beR . A. . ++ ctR ) { for ( int j=beC .k.++j .
h” . i f ( enCr>A. } // p u t t h e sub C m a t r i x b a c k i n t o t h e o r i g i n a l putSubMatrixTo ( Cij .++ j ) { // t h e c u r r e n t b e g i n n i n g Column i n d e x f o r o u t m a t r i x int beCc=j ∗L2colMAX . i f ( enCc>B . // t h i s v a l u e i s f o r end t h e columns // o f A and t h e rows o f B int enAB=(k+1)∗L2colMAX . makeSubMatrixFrom ( Bkj . GENERAL C++ CODE AND EXAMPLES 230 //now do C( i . c o l s ( ) . matrix<T> Bkj . enCr . beAB . rows ( ) ) enCr=A. // z e r o o u t t h e m a t r i x C i j =0. rows ( ) . rows ( ) . j <cDiv. // sub o u t p u t m a t r i x f o r o u t m a t r i x matrix<T> C i j ( enCr−beCr . enAB ) . B .1. A. enAB . // sub A and B m a t r i c e s matrix<T> Aik . enCc−beCc ) . k )∗ b ( k .++k ) { // t h i s v a l u e i s b e g i n n i n g f o r t h e columns // o f A and t h e rows o f B int beAB=k∗L2colMAX . i <rDiv. C. c o l s ( ) ) enCc=B . enCc ) . // perform t h e m u l t i p l y on t h e s u b s // n o t e i n g t h a t t h e e l e m e n t s i n C i j w i l l be // added t o ( not o v e r w r i t t e n ) mulmatLUnrool ( Cij . } } } A. beCc .4 An MPI master/slave implimentation framework #include ” b l o c h l i b .k<BDiv. // t h e c u r r e n t e n d i n g Row i n d e x f o r o u t m a t r i x int enCc=( j +1)∗L2colMAX .1.A. // t h e c u r r e n t e n d i n g Row i n d e x f o r o u t m a t r i x int enCr=( i +1)∗L2rowMAX . j ) ) f or ( i =0. beCc . i f (enAB>B . makeSubMatrixFrom ( Aik . beCr . for ( j =0. Bkj ) . j )=Sum k ( a ( i . Aik . beAB . beCr .++ i ) { // t h e c u r r e n t b e g i n n i n g Row i n d e x f o r o u t m a t r i x int beCr=i ∗L2rowMAX . //now l o o p t h r o u g h t h e B Row d i v i s i o n s for ( k=0. enCr . c o l s ( ) ) enAB=B . enCc ) .
s i z e ().++ qq ) . } int main ( int argc . char ∗ argv [ ] ) { // S t a r t up t h e Master c o n t r o l l e r MPIworld . s i z e ()<< s t d : : endl<<e n d l . argv ) . GENERAL C++ CODE AND EXAMPLES 231 // need t o use t h e p r o p e r namespaces using namespace BlochLib .qq<MPIworld . s i z e . . i f (CT >Max ) break . int c u r = 0 . put (CT. s l e e p ( MPIworld . i f s i z e >Max we need t o send no more for ( int qq =1. ++CT. qq ). r r =−1. rank ( ) <<” with v a l u e : ”<<kk<<e n d l . //we must perform an i n i t i a l send t o a l l t h e proc // from 1 . master ( ) ) { // t h e e l e m e n t s i n h e r e w i l l be s e n t t o t h e s l a v e p r o c s int Max= 1 0 . and k e e p p u t t i n g v a l u e s u n t i l we run o u t while (CT <Max) { // g e t an i n t ( ’ g e t ’= t h e proc i s came from ) g e t=MPIworld . g e t ) . rank ( ) − 1 ) . MPIworld . // t h e c u r r e n t v a l u e // i f we a r e t h e master . // t h i s i n t g e t s s e n t went t h e Master has s e n t // e v e r y t h i n g ( t h e k i l l s w i t c h ) int done =−1. // o n l y want t o send 1 0 t h i n g s int CT=0 . s t a r t ( argc .qq<MPIworld . we need t o i n i t i a l i z e some t h i n g s i f ( MPIworld . put (CT. rank ( ) <<”/”<<MPIworld . getAny ( r r ) . name()<<” : : ”<<MPIworld . // d e f i n e o u t f u n c t i o n we w i s h t o run i n p a r a l l e l void MyFunction ( int kk ) { cout<<endl<<” I was c a l l e d on : ”<<MPIworld . } int g e t . //now we g e t an I n t e g e r from ANY p r o c e s s o r t h a t i s NOT // t h e master . //dump o u t i n f o a b o u t what and where we a r e s t d : : cout<<MPIworld . .1. using namespace s t d . . // p u t t h e n e x t v a l u e ++CT .A. s i z e ().++ qq ) { MPIworld . // advance } // p u t t h e ’We −Are−Done ’ f l a g t o a l l t h e p r o c s once we f i n i s h for ( int qq =1.
return 0 . end ( ) . } A. i f ( c u r==done ) break . } i n l i n e s t a t i c Num T sigm (Num T num) // The s i g m o i d f u n c t i o n . template<c l a s s Num T> class sigmoid { public : Num T operator ( ) ( int i . 0 ) .A. // Weights f o r t h e neurons hidden−−>o u t p u t r m a t r i x s HOweights . // run o u t f u n c t i o n w i t h t h e g o t t e n v a l u e MPIworld . / ( 1 . h” using namespace BlochLib . g e t ( cur . Vector<f l o a t > Vector<f l o a t > Vector<Num T> Vector<Num T> IHbias HObias hlayer outTry . put ( done .1.5 C++ class for a 1 hidden layer Fully connected back–propagation Neural Network /∗ A s i m p l e 1 h i d d e n l a y e r Back p r o p g a t i o n f u l l y c o n n e c t e d Feed Foward n e u r a l Net ∗/ #include ” b l o c h l i b . GENERAL C++ CODE AND EXAMPLES 232 MPIworld . // t h e // t h e // t h e // t h e in−−h i d d e n b i a s e s hidden−−o u t b i a s e s hidden l a y e r ’ v a l u e s ’ attempted outputs Vector<Num T> o u t E r r o r . qq ) . . 0 ) . } e l s e { // s l a v e p r o c s // k e e p l o o p i n g u n t i l we t h e master t e l l s us t o q u i t while ( 1 ) { MPIworld . } }. . // send b a c k a r e q u e s t f o r more } } // e x i t MPI and l e a v e t h e prog MPIworld . Num T & i n ) { return s i g m o i d ( i n ) . // t h e ouput−−>h i d d e n e r r o r s .1. put ( cur . template<c l a s s Num T> //Num T i s t h e o u t p u t / i n p u t d a t a t y p e c l a s s BackPropNN { private : // Weights f o r t h e neurons i n p u t −−h i d d e n rmatrixs IHweights . + exp(−num ) ) ) . . { return ( 1 . // i d we g e t t h e k i l l s w i t c h g e t o u t MyFunction ( c u r ) .
} // s e t t h e l e a r i n g r a t e void fowardPass ( Vector<Num T> & i n ) . } //dumps a m a t l a b f i l e t h a t // p l o t s t h e neurons w i t h l i n e s be tw een // them b a s e d on t h e w e i g h t void p r i n t ( s t d : : s t r i n g fname ) .5. int numout ) . int numH=0 ) { l r a t e =0. inline float learningRate () { return l r a t e . }. int numout . Vector<Num T> t r a i n ( Vector<Num T> &input . // t h e hidden−−>i n p u t e r r o r s float l r a t e . ˜BackPropNN ( ) { } . r e s i z e ( numin . } template<c l a s s Num T> void BackPropNN<Num T> : : r e s i z e ( int numin . numH ) . Vector<Num T> & t a r g e t ) . BackPropNN ( int numin . f l o a t e r r o r ( Vector<Num T> & t a r g e t ) . int numH.A. // r e s i z e t h e i n s and o u t s void r e s i z e ( int numin . // r e s e t t h e w e i g h t s t o random void r e s e t ( ) .1. r m a t r i x s IH w e ig hts ( ) { return I H w e i g h t s . GENERAL C++ CODE AND EXAMPLES 233 Vector<Num T> h i d d e n E r r o r . . void backPass ( Vector<Num T> &input . } r m a t r i x s HOweights ( ) { return HOweights . int numout . numout . Vector<Num T> run ( Vector<Num T> &i n p u t ) . } // g e t t h e l e a r i n g r a t e void l e a r n i n g R a t e ( f l o a t l r ) { l r a t e =l r . int numout ) . int numH=0) { RunTimeAssert ( numin >=1). int numH. template<c l a s s Num T> BackPropNN<Num T> : : BackPropNN ( int numin . Vector<Num T> & t a r g e t ) . public : BackPropNN ( ) .
. } outTry [ i ]= sigmoid <Num T> : : sigm ( tmp+HObias ( i ) ) . 1 ) . apply (myR ) . } // h i d d e n −−> o u t p u t f or ( i =0. apply (myR ) . } template<c l a s s Num T> void BackPropNN<Num T> : : r e s e t ( ) { Random<UniformRandom<f l o a t > > myR( − 1 . r e s i z e (numH. I H b i a s . s i z e (). r e g i s t e r Num T tmp=0.++ i ) { f o r ( j =0. r e s i z e ( numout .++ j ) { tmp+=i n ( j ) ∗ I H w e i g h t s ( j . } } . template<c l a s s Num T> void BackPropNN<Num T> : : fowardPass ( Vector<Num T> & i n ) { r e g i s t e r int i .A. RunTimeAssert (numH>=1). numH ) . 0 ) . } h l a y e r [ i ]= sigmoid <Num T> : : sigm ( tmp+I H b i a s ( i ) ) . j <HOweights . r e s i z e (numH. r e s i z e ( numin . r e s i z e ( numout . i f (numH==0) numH=numin . o u t E r r o r . f i l l ( 0 . r e s i z e ( numout . s i z e (). r e s i z e (numH. I H w e i g h t s . 0 ) . i <outTry . numout ) . rows (). r e s i z e (numH. j . . GENERAL C++ CODE AND EXAMPLES 234 RunTimeAssert ( numout>=1).++ j ) { tmp+=h l a y e r ( j ) ∗ HOweights ( j . j <i n . // t h e w e i g h t s i z e i s ( numin+1)x ( numin+1) // t h e ’ + 1 ’ f o r t h e b i a s e n t r i e s I H w e i g h t s .1. 0 ) . 0 ) .++ i ) { f or ( j =0. 0 ) . h i d d e n E r r o r . HOweights . tmp=0. tmp=0. c o l s (). i ) . f i l l (0. apply (myR ) . i ) . HObias . // i n p u t −−> h i d d e n f o r ( i =0. 0 ) . 0 ) . hlayer . i <I H w e i g h t s . reset (). I H b i a s . HOweights . } // t h i s d o e s t h e foward p r o p o g a t i o n . HObias . outTry . apply (myR ) .0). outTry . h l a y e r .
1 . i <HOweights . } // a d j u s t hidden−−>o u t p u t w e i g h t s Num T l e n =0.0 − h l a y e r ( i ) ) ∗ tmp ) . r e g i s t e r Num T tmp=0. } // t h i s d o e s t h e backwards p r o p o g a t i o n .++ i ) { HObias ( i )+=f l o a t ( l r a t e ∗ o u t E r r o r ( i ) / l e n ) .++ j ) { I H w e i g h t s ( i . f or ( i =0. j <I H w e i g h t s .1) l e n = 0 .++ i ) { f or ( j =0. GENERAL C++ CODE AND EXAMPLES 235 template<c l a s s Num T> f l o a t BackPropNN<Num T> : : e r r o r ( Vector<Num T> & t a r g e t ) { return norm ( t a r g e t −outTry ) . f o r ( i =0. . i <HOweights . } } // a d j u s t h i d d e n b i a s l e v e l s f o r ( i =0.1) l e n = 0 . j )+=f l o a t ( l r a t e ∗ h i d d e n E r r o r ( j ) ∗ i n p u t ( i ) / l e n ) . template<c l a s s Num T> void BackPropNN<Num T> : : backPass ( Vector<Num T> &input . } h i d d e n E r r o r ( i )= f l o a t ( h l a y e r ( i )∗(1.1. l e n=sum ( s q r ( h l a y e r ) ) . . .A. i <HObias . j )+=f l o a t ( l r a t e ∗ o u t E r r o r ( j ) ∗ h l a y e r ( i ) / l e n ) . s i z e (). c o l s ().++ j ) { HOweights ( i .++ j ) { tmp+=o u t E r r o r [ j ] ∗ HOweights ( i . i <I H w e i g h t s . // t h e mean l e n g t h o f t h e h i d d e n i f ( l e n <=0. c o l s (). 1 . . j ) . i f ( l e n <=0. s i z e (). j . j <outTry . c o l s ().++ i ) { f o r ( j =0. tmp=0. // e r r o r f o r h i d d e n f or ( i =0.++ i ) { f or ( j =0. // e r r o r f o r o u p u t s o u t E r r o r =t a r g e t −outTry . i <i n p u t . } // a d j u s t w e i g h t s from i n p u t t o h i d d e n l e n=sum ( s q r ( i n p u t ) ) . s i z e (). s i z e (). rows (). . . j <outTry . } } // a d j u s t i n p u t b i a s l e v e l s f or ( i =0. // do not r e d u c e t o o much .++ i ) { . Vector<Num T> & t a r g e t ) { r e g i s t e r int i . // do not r e d u c e t o o much .
A. Vector<Num T> &out ) { fowardPass ( i n ) . //we want each node t o be s p e a r a t e d by // 5 i n t h e on t h e x ’ a x i s ’ and 1 0 on t h e y a x i s // we need t o s c a l e t h e x a x i s b a s e d on t h e maxNode oo<<” inNodes=”<<I H w e i g h t s . \ n” . s i z e ()<<” . p r i n t ”<<s t d : : e n d l . c o l s ()<<” . \ n” <<” c l f r e s e t . \ n” <<” hNodes=”<<I H w e i g h t s . return outTry . GENERAL C++ CODE AND EXAMPLES 236 I H b i a s ( i )+=f l o a t ( l r a t e ∗ h i d d e n E r r o r ( i ) / l e n ) . } // t h i s dumps t h e i n f o t o a matab // s c r i p t so t h a t i t can be e a s i l y p l o t t e d template<c l a s s Num T> void BackPropNN<Num T> : : p r i n t ( s t d : : s t r i n g fname ) { s t d : : o f s t r e a m oo ( fname . backPass ( in .1. } } template<c l a s s Num T> Vector<Num T> BackPropNN<Num T> : : t r a i n ( Vector<Num T> &in . i f ( oo . return outTry . } /∗we w i s h t h e p i c t u r e t o l o o k l i k e O O / \ / \ O O O \ / \ / O O ∗/ // t h e ’ d o t ’ f o r a Neuron oo<<” f i g u r e ( 1 5 3 ) . f a i l ( ) ) { s t d : : c e r r <<s t d : : endl<<”BackPropNN . } template<c l a s s Num T> Vector<Num T> BackPropNN<Num T> : : run ( Vector<Num T> &i n ) { fowardPass ( i n ) . c s t r ( ) ) . \ n” <<” outNodes=”<<outTry . return . \ n” . s t d : : c e r r <<” cannot open ouput f i l e ”<<s t d : : e n d l . rows()<<” . out ) .
f o r ( int i =0. \ n” <<” ySep =10.A.++ j ) { oo<<I H w e i g h t s ( i . f or ( int i =0. ” . \ n” <<” i n S e p =(xSep / ( inNodes + 2 ) ) . . max( hNodes .++ i ) { oo<<” [ ” . max(max( abs ( HOweights ) ) ) ) . − 1 ] . 0 . j <I H w e i g h t s . c o l o r . \ n” <<”maxW=max(maxW. // p r i n t o u t t h e w e i g h t s and b i a s e s oo<<” I H we i ght s =[” . \ n” <<” yc = [ − 1 . 0 .++ i ) { oo<<” [ ” . max(max( abs ( HObias ) ) ) ) . \ n” <<”maxW=max(maxW. j )<<” ” . . \ n” . rows (). \ n” <<” i f I H w e i g ht s ( j . \ n” <<”maxW=max(maxW. \ n” <<” ybSep =2. \ n” <<” l i =l i n e ( [ j ∗ i n S e p i ∗ hSep ] . 8 ] . f or ( int j =0. \ n” <<” xc = [ − 1 . 0 ] . \ n” <<”maxWidth=5. ’ LineWidth ’ . \ n” <<” xSep=2∗ySep . − 1 . } oo<<” ] . \ n” <<” outSep=(xSep / ( outNodes + 1 ) ) . } oo<<” ] \ n” . i <I H w e i g h t s . c o l o r=a l t C o l o r . \ n” <<” hSep=(xSep / ( hNodes + 2 ) ) . i ) <0 . 1 ] . j )<<” ” . } oo<<” ] \ n” . c o l s (). GENERAL C++ CODE AND EXAMPLES 237 <<”maxNodes=max( inNodes . \ n” // p r i n t a l i n e f o r each one o f them . \ n” <<”HObias=[”<<HObias <<” ] . max(max( abs ( I H b i a s ) ) ) ) .\n” <<” a l t C o l o r = [ 0 . ” <<” ’ C o l o r ’ . 1 . f or ( int j =0. end . <<”% INput−−>HIdden l i n e s \ n” <<” f o r i =1: hNodes \ n” <<” f o r j =1: inNodes \ n” <<” c o l o r=p o s C o l o r . 1 .1. outNodes ) ) . [ 2 ∗ ySep ySep ] . \ n” // f i n d t h e max o f a l l o f them <<”maxW=max(max( abs ( I H w e i g h t s ) ) ) .++ j ) { oo<<HOweights ( i . 1 . i <HOweights . 0 . oo<<” HOweights =[” . j <HOweights . rows (). \ n” <<” p o s C o l o r = [ 0 . c o l s (). } oo<<” ] . \ n” <<” I H b i a s =[”<<I H b i a s <<” ] . \ n ” <<” h o l d on\n” . 8 . .
\ n” <<” i f I H b i a s ( i ) <0 . c o l o r . \ n” <<” end \n” . ’ g ’ ) . [ 2 ∗ ySep−ybSep ySep ] . ” <<” maxWidth∗ abs ( HObias ( i ) ) /maxW) . [ ySep−ybSep 0 ] . yc / hNodes+ySep . ” <<” ’ C o l o r ’ . c o l o r=a l t C o l o r . \ n” <<” l i =l i n e ( [ j ∗ hSep i ∗ outSep ] . ’ r ’ ) . \ n” <<” i f HOweights ( i . j ) <0 . c o l o r=a l t C o l o r . j ) ) /maxW) . ” <<” yc / inNodes+2∗ySep−ybSep . \ n” <<” end \n” <<” end \n” <<”% Hidden−−>out l i n e s \ n” <<” f o r i =1: hNodes \ n” <<” f o r j =1: outNodes \ n” <<” c o l o r=p o s C o l o r . \ n” <<” end \n” <<”%b i a s H−−>O node \n” <<” f i l l ( xc / hNodes+(hNodes +1)∗hSep . GENERAL C++ CODE AND EXAMPLES 238 <<” maxWidth∗ abs ( I H w e ig h ts ( j . ’ g ’ ) . ” <<” maxWidth∗ abs ( I H b i a s ( i ) ) /maxW) . yc / hNodes+ySep−ybSep . ’ LineWidth ’ . end . yc / inNodes+2∗ySep . oo<<” f o r i =1: inNodes \n” <<” f i l l ( xc / inNodes+i ∗ inSep . end . . c o l o r=a l t C o l o r . \ n” . \ n” <<” end \n” <<” d a s p e c t ( [ 1 1 1 ] ) . i ) ) /maxW) . ” <<” ’ C o l o r ’ . \ n” <<”\n” <<” f o r i =1: outNodes \n” <<” f i l l ( xc / outNodes+i ∗ outSep .\n” <<” f o r i =1: hNodes \ n” <<” c o l o r=p o s C o l o r . ’ k ’ ) . ” <<” ’ C o l o r ’ . c o l o r . \ n” <<” end \n” <<”%b i a s I−−>H node \n” <<” f i l l ( xc / inNodes +( inNodes +1)∗ inSep . yc / outNodes . \ n” <<” a x i s t i g h t . . ” <<” maxWidth∗ abs ( HOweights ( i .A. \ n” <<” l i =l i n e ( [ j ∗ i n S e p i ∗ hSep ] . \ n” <<” \n” <<” f o r i =1: hNodes \n” <<” f i l l ( xc / hNodes+i ∗hSep .\n” <<” f o r i =1: outNodes \ n” <<” c o l o r=p o s C o l o r . [ ySep 0 ] . ’ LineWidth ’ . ’ LineWidth ’ . . ’ b ’ ) . c o l o r . \ n” <<” end \n” <<”% Hidden Bias−−>output l i n e s \ n” <<” j=hNodes +1. \ n” <<” end \n” <<” end \n” <<”% i n p u t Bias−−>Hidden l i n e s \ n” <<” j=inNodes +1. \ n” <<” l i =l i n e ( [ i ∗ hSep j ∗ outSep ] .1. end . \ n” <<” i f HObias ( i ) <0 .
To generate the Wigner rotation matrix call Wigner[Spin] where Spin is the same as the MakeSpace value. MakeIplus . p e r s o n ” . This small and simple Mathematica package (a . mp. Ipp .m ﬁle) allows the creation of the basic Cartesian spin operators and Wigner rotation matrices of a given spin space of spin I. Iz . d12 ] BeginPackage [ ” s p i n t e n ‘ ” ] Unprotect [ MakeSpace . MakeIz . MakeIy . c r e a t e d . Wigner : : errms=”m’ s i n ’ Wigner ’ i s s m a l l e r then −L . rank . WignerExpIy . beta . \n Other p o s s i b l e s i n c l u d e : \n Wigner [ L] −−> For an e n t i r e matrix \n Wigner [ L . Bad . gamma ’ symbols ” . MultWig .e. { alpha . beta . . gamma ’ \n Wigner [ L . MakeExpIz ] Clear [ MakeSpace . beta .2. D i r e c t . Ix. It will make Iz. m] −−> u s i n g d e f a u l t \n ’ alpha .1 NMR algorithms Mathematica Package to generate Wigner Rotation matrices and Spin operators.2 A. MakeIplus . Iz . Ipp . MakeSpinSys . . beta . MakeIz . c r e a t e d . mp. To use the package. gamma}] −−> matrix u s i n g d e f a u l t ’ alpha .2. . Ipp and Imm as global matrices. Imm . Bad . MakeIy . L  Exp[− I I z a l p h a ] Exp[− I I y b e t a ] ∗ Exp[− I I z gamma ]  m. D12 . D i r e c t . 1. MultWig . Imm . 1/2. } A. WignerExpIy . (∗ s p i n t e n . gamma } ] g e n e r a t e s a w i g n e r \n r o t a t i o n e l e m e n t \n <mp. MakeSpinSys . Wigner . Iy . MakeIx . Wigner . d12 ] Clear [ Ix .m. L>. Iy. { alpha . p e r s o n ” . etc). D12 . bad . MakeImin . MakeImin . 3/2. MakeExpIz ] (∗ Usages ∗) Wigner : : u s a g e= ” Wigner [ L .A. MakeIx . rank . Iy . NMR ALGORITHMS 239 <<” h o l d o f f \n” <<” \n” . simple call MakeSpace[Spin] where Spin is the total spin (i.m∗) (∗ In t h i s p a c k a g e we t r y t o c r e a t a l l t h e n e s s e s a r y b i t s f o r g e n e r a t i n g e v e r y t h i n g we c o u l d p o s s i b l y want t o do w i t h s p i n t e n s o r s and r o t a t i o n s ∗) Unprotect [ Ix . Wigner : : errmb=”m’ s i n ’ Wigner ’ i s b i g g e r then L . bad .
{0 . L2 } . \ n and I y Which can then be c a l l e d up a s Ix . L3 } ] t h i s f u n c t i o n g e n e r a t e s a l l t h e \ n m a t r i c e s f o r s p i n=L s y s t e m s .0} . NMR ALGORITHMS 240 MultWig : : us a g e= ”MultWig [ { L1 . Iy . L ] A f a s t e r way o f d o i n g exp [ a I z ] ” . MatrixExp[− I Global ‘ \ [Gamma] Global ‘ I z ] ) / / Simplify . \ n and I y Which can then be c a l l e d up a s Ix .{0 .A. Direct : : usage = ” D i r e c t [m. − I } . MultWig : : errm=”You J o r M3p o r M3 i s t o o b i g f o r L1+L2” . MakeIx : : u s a ge=”MakeIx [ L ] G e n e r a t e s I x i n s p a c e o f rank L” . MakeIy : : u s a ge=”MakeIy [ L ] G e n e r a t e s I y i n s p a c e o f rank L” .{1 . p ] t h e c r e a t e s a d i r e c t p r o d uc t o f two m a t r i c e s {m and p} ” . M3} ] W i l l g i v e t h e Dj (m3p . MakeIplus : : u s a g e =” MakeIplus [ L ] G e n e r a t e s I + i n s p a c e o f rank L” . Ix .{1 . MakeSpinSys : : us a g e= ” MakeSpinSys [ { L1 . MakeExpIz : : u s a g e=”MakeExpIz [ a . MakeSpace : : us ag e= ” MakeSpace [ L ] t h i s f u n c t i o n g e n e r a t e s a l l t h e m a t r i c e s f o r s p i n=L \ n s y s t e m s . 0 } } ‘ Ipp ={{0 . L2 .0}} ‘ Imm={{0 . −1}} ‘ I x = 1/2{{0 . m3) w i g n e r \n e l e m e n t s from two Other Wigner M a t r i c e s ! ! ” . { I .0}} ‘ numspin =1. \ n I f you have d e f i n e d them p r e v i o u s l y t h i s w i l l r e d e f i n e them” . I z .1} .0}} ‘ I y =1/2{{0 . Ix . { J .0} . Iy . MakeIz : : u s a ge =” MakeIz [ L ] G e n e r a t e s I z i n s p a c e o f rank L” . Global ‘ d12 . M3p. ‘ rank =1/2 ‘ d12=MatrixExp[− I Global ‘ \ [ Beta ] \ Global ‘ I y ] / / ExpToTrig // Simplify Global ‘ D12=(MatrixExp[− I Global ‘ \ [ Alpha ] Global ‘ I z ] . I z . \ n I f you have d e f i n e d them p r e v i o u s l y t h i s w i l l r e d e f i n e them” . MakeSpace : : e r r = ” you have e n t e r e d i n a v a l u e f o r L t h a t i s not \ n and i n t e r g e r o r h a l f an i n t e r g e r ” . The output s i m p l y c r e a t e s d e f i n i t i o n s f o r Iz .2.1} . Begin [ ” ‘ P r i v a t e ‘ ” ] (∗ h e r e we d e f i n e t h e ’ b a s e / d e f a u l t ’ p a u l i m a t r i c e s ( t h e y a r e f o r s p i n 1 / 2 ) ∗) Global Global Global Global Global Global Global Global ‘ I z =1/2{{1 . \ n The output s i m p l y c r e a t e s A LIST f o r Iz . MakeImin : : u s a g e =”MakeImin [ L ] G e n e r a t e s I − i n s p a c e o f rank L” .
L. m. −L . 0 . L. Sqrt [ L(L+1)−m(m+ 1 ) ] . { j . dimP } . 0 ] . −L . − 1 } ] ] MakeIplus [ L ] : = Table [ Table [ I f [mp==(m+ 1 ) . 0 . I f [Mod[ L . L.m m L . 0 ] .2. m+/−1> ∗) MakeIz [ L ] : = Table [ Table [ I f [mp==m. {m.k + 1 ] ] ∗ p [ [ i . j ] ] . and Imin m a t r i x f o r a Rank L s p i n s p a c e by d o i n g using these simple i d e n t i t i e s I z  L . L. − 1 } ] ] MakeIx [ L ] : = 1 / 2 ( MakeIplus [ L]+MakeImin [ L ] ) MakeIy [ L ] := − I 1 / 2 ( MakeIplus [ L]−MakeImin [ L ] ) MakeSpace [ L ] : = Module [ { } . Print [ ”Bad person . − 1 } ] ] MakeImin [ L ] : = Table [ Table [ I f [mp==m−1 . {mp .−L . {mp . NMR ALGORITHMS 241 (∗ t h i s f l a g t e l l s me t h a t i have a l r e a d y c r e a t e d made t h e m a t r i x Exp[− I b e t a I y ] . you gave me a ’NULL’ f o r a matrix ” ] ] . I f [ Global ‘ rank !=L . dimM − 1 } ] ] ] . e s p e c i a l l y s y m b o l i c a l l y and need o n l y be done once ( o f c o u r s e u n l e s s i change my L) ∗) Global ‘ c r e a t e d w i g =0. . dimM−1} . −L . 1 . 0 ] . f r o o=Table [ 0 . \ { i . j +(k∗dimP ) ] ] =m[ [ l +1. −L . (∗ h e r e i s a f u n c t i o n t h a t c r e a t e s t h e I z . − 1 } . dimP } .m >= > I +/−L . 0 .A. This can be a l a r g e l a r g e .{k . I f [ dimM==0dimP==0. (∗ h e r e i s a f u n c t i o n t h a t d o e s a d i r e c t p r o d u c t b e tw e e n two m a t r i c e s ∗) Direct [m . dimP∗dimM } ] .m >=S q r t [ L(L+1)−m(m+/ −1)] L . dimP=Dimensions [ p ] [ [ 1 ] ] . p ] := Module [ { dimM=Dimensions [m ] [ [ 1 ] ] . f r o o } . {m. I p l u s . {m. 1 . Message [ MakeSpace : : e r r ] . \ 1 . . {mp . 1 . 5 ] ! = 0 . − 1 } . { i . Table [ Table [ f r o o [ [ i +( l ∗dimP ) . −L . { j . L. Sqrt [ L(L+1)−m(m− 1 ) ] . − 1 } . dimP∗dimM} . { l . L.
m . m. I f [m . m ] : = Wigner [ L . Message [ Wigner : : errms ] ] ] ] ] . Message [ Wigner : : errmb ] . I f [ mp<−L . mp . −L . ‘ I x =1/2( Global ‘ Ipp+Global ‘ Imm ) . MakeExpIz[− I Global ‘ \ [Gamma] . L ] : = Table [ Table [ I f [mp==m. ‘ Ipp=MakeIplus [ L ] . While [ l <L−1/2 . l=l +1/2. I f [ L==1/2 . ]. MakeExpIz[− I Global ‘ \ [ Alpha ] . Global ‘ d12 } . ] ] ] MakeExpIz [ a . Message [ Wigner : : errmb ] . { a l p h a . Global ‘ D12 .A. L ] . beta . tmp } . ‘ createdwig = 0 . mp . {m. gamma } ] : = Module [ { l =1/2} . − 1 } ] ] Wigner [ L . 0 ] . >L I f [m <−L . 1 . I f [ L==1/2 . Exp [m a ] . mp . Message [ Wigner : : errmb ] . MultWig [ { Global ‘ d12 . I f [ mp>L . I f [m . ‘ I z=MakeIz [ L ] . { alpha . −L . gamma } ] Wigner [ L . mp . L ] ] ] . ]. 1 . tmp } . L ] ] . tmp } . tmp=Global ‘ d12 . { L . Message [ Wigner : : errms ] . Global ‘ d12 } . L. l=l +1/2. m} ] ] ] ] Wigner [ L . ‘ I y=−I 1 / 2 ( Global ‘ Ipp−Global ‘ Imm ) . l + 1 / 2 ] . I f [ L==0 . I f [ mp<−L . l =1/2. Global ‘ d12 . tmp=Global ‘ d12 . Message [ Wigner : : errms ] . NMR ALGORITHMS 242 Global Global Global Global Global Global Global ‘ rank=L . Message [ Wigner : : errms ] ] ] ] ] . l + 1 / 2 ] . ‘ Imm=MakeImin [ L ] . While [ l <L−1/2 . b e t a .2. >L I f [m <−L . tmp=MultWig [ { Global ‘ d12 . Exp[− I mp Global ‘ \ [ Alpha ] ] ∗ Exp[− I m Global ‘ \ [Gamma] ] ∗ MultWig [ { tmp . I f [ mp>L . { a l p h a . gamma } ] : = Module [ { l . Message [ Wigner : : errmb ] . b e t a . {mp . L. − 1 } . tmp=MultWig [ { tmp . I f [ L==0 .
g } ] . { J . −1}] MakeSpinSys [ s p i n s i z e s ] : = Module [ { i } .] (∗T=Function [ { L . L2 } . \ { i . { J . I f [ J>l 1+l 2 . { i . L2 ?MatrixQ } . J . m3 } . − 1 } ] ) / / Simplify ] ] MultWig [ { L1 ?MatrixQ . J . { l 2 . \ { i . J . { j . m. I f [ Length [ s p i n s i z e s ]==1 . i . \ { i . s p i n s y s } . l 2 =(Dimensions [ L2 ] [ [ 1 ] ] − 1 ) / 2 } . beta . (Sum[ Sum[ I f [ m3−m1>l 2   m3−m1<−l 2   m3p−m1p>l 2   m3p−m1p<−l 2 . Global ‘ I x=Table [ MakeIx [ s p i n s i z e s [ [ i ] ] ] . 1 . 0 . L2 } . { j . m3p . {m1 . 1 . Module [ { tmpix } . { l 2 .A. ]]. Length [ s p i n s i z e s ] } ] . g } ] : = Table [ MultWig [ { L1 . 1 . MakeSpinSys [ s p i n s y s ] . gamma } ] MultWig [ { L1 ?MatrixQ . Length [ s p i n s i z e s ] } ] . MakeSpace [ s p i n s i z e s ] . { J . b . m3p . m3} . { J . {m1p . Global ‘ numspin=Length [ s p i n s i z e s ] . Global ‘ Ipp=Table [ MakeIplus [ s p i n s i z e s [ [ i ] ] ] . J. ClebschGordan [ { l 1 . m1} . Global ‘ I y=Table [ MakeIy [ s p i n s i z e s [ [ i ] ] ] . L2 ?MatrixQ } . m3p } ] ∗ L1 [ [ l 1 −m1p + 1 ] ] [ [ l 1 −m1+ 1 ] ] ∗ L2 [ [ l 2 −(m3p−m1p ) + 1 ] ] [ [ l 2 −(m3−m1 ) + 1 ] ] ] . Length [ s p i n s i z e s ] } ] . MakeSpace .− l 1 . Length [ s p i n s i z e s ] } ] . − 1 } . l 1 . \ { i . { J . m3−m1} . −1} . b . \ { i . −1}] . { J . 1 . J ] : = Table [ MultWig [ { L1 . m3p . { a . J. g } ] : = Module [ { l 1 =(Dimensions [ L1 ] [ [ 1 ] ] − 1 ) / 2 . j } . − J . m3} ] ∗ ClebschGordan [ { l 1 . j } ] . { J . i . \ { i . MakeImin . I f [ Length [ s p i n s i z e s ]==0 . −J . − 1 } ] MultWig [ { L1 . { a . b . l 1 . L2 } . Global ‘ Imm=Table [ MakeImin [ s p i n s i z e s [ [ i ] ] ] . − l 1 . ∗) End [ ] Protect [ Wigner . − J . MakeIz . 1 . L2 } . b . NMR ALGORITHMS 243 Wigner [ L ] : = Wigner [ L . . Message [ MultWig : : errm ] . L2 } . MakeIplus . Length [ s p i n s i z e s ] } ] . m1p} . MakeSpace [ s p i n s i z e s [ [ 1 ] ] ] . Global ‘ I z=Table [ MakeIz [ s p i n s i z e s [ [ i ] ] ] . m3 } ] : = MultWig [ { L1 .2. m3p−m1p} . g } ] MultWig [ { L1 . −J . { alpha . { a . { a .
D i r e c t . i <b a s e . Vector<int > SpecialName . maxBackReduce (). . h” /∗ ∗ ∗ ∗ This c l a s s s h o u l d be used as f o l l o w s . . UseMe . . f a c t . r e d u c e ( ) . bac ) . MakeSpinSys . MakeExpIz ] EndPackage [ ] A. baseTag . The header ﬁle #i f n d e f #define Prop Reduce h Prop Reduce h 1 #include ” b l o c h l i b . Vector<int > BackName . > } f o r ( i n t i =0. Vector<Vector<int > > FowardRed .2 Rational Reduction C++ Class This includes both the C++ header ﬁle. fow . NMR ALGORITHMS 244 MakeIx . . the C++ source ﬁle and an example usage ﬁle.A. .2. g e n e r a t e P r o p s ( ind . Vector<int > FowardName . i <myred . l o g ) . PropReduce myred ( base . c l a s s PropReduce { private : Vector<Vector<int > > BackRed . . ∗∗ ∗/ /∗ ∗ ∗ The R a t i o n a l Reduction o f P r o p o g a t o r s ∗ ∗ ∗/ using namespace BlochLib . . f o r ( i n t i =0. MultWig .2. > } Vector<matrix > myred .++ i ){ < g e n e r a t e t h e i n d and fow p r o p s . MakeIy . int Mults . Vector<Vector<int > > S p e c i a l R e d . speTag . myred . Vector<Vector<int > > dat .++ i ){ < generate the back props .
t h e f i r s t a r e t h e // i n d i v i d u a l p r o p o g a t o r s ( ” 0 ” .A. s t d : : ostream ∗ oo =0). void s p e c i a l R e d u c e ( ) . #endif The source ﬁle #include ” b l o c h l i b . ) // t h e second t h e ’ Foward ’ p r o p s ( ” 0 ∗ 1 ” . s t d : : ostream ∗ l o g f . ) // t h e t h i r d th e . h” #include ” p r o p r e d u c e .} . Vector<matrix > &F i l l M e ) . }. void r e d u c e ( ) . ” 0 ∗ 1 ∗ 2 ” . void fowardReduce ( ) . ) // t h e f o r t h i s t h e p l a c e t o f i l l . Vector<Vector<int > > &subN . PropReduce : : PropReduce ( int bas . Mults =0. oo ) . h” using namespace BlochLib . int f a c t o r . } i n l i n e int maxFowardReduce ( ) const { return FowardRed . . Vector<matrix > &Back . NMR ALGORITHMS 245 bool i t e r a t i o n ( Vector<Vector<int > > &dat . } // t h e s e f u n c t i o n s w i l l c r e a t e t h e p r o p o g a t o r s // from 3 i n p u t m a t r i x l i s t s . . f a c . s t d : : ostream ∗ oo =0). s i z e ( ) . Vector<int > &name ) . void g e n e r a t e P r o p s ( Vector<matrix > &i n d i v . Vector<matrix > &Foward . void setParams ( int bas . Vector<Vector<int > > &propRed . } i n l i n e int maxBackReduce ( ) const { return UseMe+1. UseMe = 0 . i n l i n e int b e s t M u l t i p l i c a t i o n s ( ) { return Mults .2. ” 2 ” . int f a c . int f a c t . ” 6 ∗ 7 ∗ 8 ” . f a c t o r . . . void backReduce ( ) . . using namespace s t d . . ’ Back ’ p r o p s ( ” 7 ∗ 8 ” . . // c o n s t r u c t o r s PropReduce ( ) { } PropReduce ( int bas . ” 1 ” . . ostream ∗ oo ) { setParams ( bas . public : int base . .
A.2. NMR ALGORITHMS
246
void PropReduce : : setParams ( int bas , int f a c , ostream ∗ oo ) { // f i n d t h e g r e a t e s t common d i v i s o r . . . int u = abs ( bas ) ; int v = abs ( f a c ) ; int q , t ; while ( v ) { q = int ( f l o o r ( double ( u ) / double ( v ) ) ) ; t = u − v∗q ; u = v; v = t; } b a s e=bas /u ; f a c t o r=f a c /u ; l o g f=oo ; dat . r e s i z e ( base , Vector<int >( f a c t o r ) ) ; FowardName . r e s i z e ( base −1); FowardRed . r e s i z e ( base −1); BackName . r e s i z e ( base −1); BackRed . r e s i z e ( base −1);
int c t =0 , c t 2 =0; for ( int i =0; i <b a s e ∗ f a c t o r ;++ i ) { dat [ c t ] [ c t 2 ]= i%b a s e ; ++c t 2 ; i f ( ct2>=f a c t o r ){ ++ c t ; c t 2 =0; } } baseTag =100∗ BlochLib : : max( base , f a c t o r ) ; f o r ( int i =1; i <b a s e;++ i ) { FowardName [ i −1]= i ∗ baseTag ; FowardRed [ i − 1 ] . r e s i z e ( i +1); f o r ( int j =0; j<=i ;++ j ) FowardRed [ i − 1 ] [ j ]= j ; if ( logf ) ∗ l o g f <<”Foward r e d u c t i o n : ”<<FowardName [ i −1]<<”=” <<FowardRed [ i −1]<< s t d : : e n d l ; BackName [ i −1]=− i ∗ baseTag ; BackRed [ i − 1 ] . r e s i z e ( i +1); f or ( int j=base−i −1 , k=0; j <b a s e;++j ,++k ) { BackRed [ i − 1 ] [ k]= j ; } if ( logf ) ∗ l o g f <<”Back r e d u c t i o n : ”<<BackName [ i −1]<<”=” <<BackRed [ i −1]<< s t d : : e n d l ; } // t h i s i s a s p e c i a l one which s i m p l y t r i m s t h e t h e 0 and base −1
A.2. NMR ALGORITHMS
247
// f a c t o r and can be c a l c e d by U( 0 ) ’ ∗U( t r )∗U( base −1) ’ S p e c i a l R e d . r e s i z e ( 1 , Vector<int >(base − 2 ) ) ; speTag =20000∗ BlochLib : : max( base , f a c t o r ) ; SpecialName . r e s i z e ( 1 , speTag ) ; f or ( int i =1; i <base −1;++ i ) { S p e c i a l R e d [ 0 ] [ i −1]= i ; } if ( logf ) ∗ l o g f <<” S p e c i a l r e d u c t i o n : ”<<S p e c i a l R e d [0]<<”=” <<SpecialName [0]<< s t d : : endl<<s t d : : e n d l ; }
bool PropReduce : : i t e r a t i o n ( Vector<Vector<int > > &dat , Vector<Vector<int > > &propRed , Vector<Vector<int > > &subN , Vector<int > &name ) { // l o o p s t o f i n d t h e matches bool gotanyTot=f a l s e ; f or ( int i =0; i <dat . s i z e ();++ i ) { bool gotany=f a l s e ; Vector<int > curU ; f or ( int M=0;M <dat [ i ] . s i z e ();++M) { bool g o t=f a l s e ; int p=0; for ( p=subN . s i z e () −1; p>=0;−−p ) { i f ( subN [ p ] . s i z e ()+M <=dat [ i ] . s i z e ( ) ) { i f ( subN [ p]==dat [ i ] ( Range (M,M +subN [ p ] . s i z e ( ) − 1 ) ) ) { g o t=true ; break ; } } } i f ( got ){ for ( int k=0;k< M;++k ) { curU . push back ( dat [ i ] [ k ] ) ; } curU . push back ( name [ p ] ) ; fo r ( int k=subN [ p ] . s i z e ()+M; k<dat [ i ] . s i z e ();++ k ) { curU . push back ( dat [ i ] [ k ] ) ; } propRed [ i ]=curU ; gotany=true ; break ; } } i f ( ! gotany ) { for ( int k=0;k<dat [ i ] . s i z e ();++ k ) { curU . push back ( dat [ i ] [ k ] ) ; }
A.2. NMR ALGORITHMS
248
propRed [ i ]=( curU ) ; } else { gotanyTot=true ; } } return gotanyTot ; }
/∗ ∗ ∗ foward r e d u c t i o n s . . . ∗ ∗ ∗/ void PropReduce : : fowardReduce ( ) { Vector<Vector<int > > propRed ( base , Vector<int > ( 0 ) ) ; while ( i t e r a t i o n ( dat , propRed , FowardRed , FowardName ) ) { dat=propRed ; } int m u l t i =0; f o r ( int i =0; i <dat . s i z e ();++ i ) { i f ( l o g f ) ∗ l o g f <<” Sequence ”<<i <<” : ”<<dat [ i ]<< e n d l ; m u l t i+=dat [ i ] . s i z e ( ) ; } if ( logf ) ∗ l o g f <<” A f t e r Foward Reduction . . . Number o f m u l t i p l i c a t i o n s : ” <<multi <<endl<<e n d l ; } /∗ ∗∗ Back R e d u c t i o n s ∗ ∗ ∗/ // t h e b a c k r e d u c t i o n s we do n o t e g e t f o r f r e e // ( l i k e t h e f o r w a r d ones whcih we have t o c a l c // from t h e exp (H) o p e r a t i o n ) , so t h e number // o f b a c k r e d u c t i o n s used depends on t h e t o t a l m u l t i p l i c a t i o n // s a v e i n g s . . . so we need t o go t h r o u g h t h e e n t i r e l o o p s o f // b a c k r e d u c t i o n s . . . void PropReduce : : backReduce ( ) { Vector<Vector<int > > propRed ( base , Vector<int > ( 0 ) ) ; Vector<Vector<int > > holdDat ( dat . s i z e ( ) ) ; f o r ( int i =0; i <dat . s i z e ();++ i ) holdDat [ i ]= dat [ i ] ; Mults =1000000; int m u l t i =0; UseMe=0; Vector<Vector<int > > curBack ; Vector<int > curName ; f o r ( int k=0;k<BackRed . s i z e ();++k ) { i f ( l o g f ) ∗ l o g f <<” Number o f ’ Back R e d u c t i o n s ’ : ”<<k<<e n d l ; curBack=BackRed ( Range ( 0 , k ) ) ; curName=BackName ( Range ( 0 , k ) ) ; f o r ( int i =0; i <dat . s i z e ();++ i ) dat [ i ]= holdDat [ i ] ;
A.2. NMR ALGORITHMS
249
while ( i t e r a t i o n ( dat , propRed , curBack , curName ) ) { dat=propRed ; m u l t i=curBack . s i z e ( ) ; f or ( int j =0; j <dat . s i z e ();++ j ) { m u l t i+=dat [ j ] . s i z e ( ) ; } i f ( Mults>m u l t i ) { UseMe=k ; Mults=m u l t i ; } } f o r ( int j =0; j <dat . s i z e ();++ j ) { if ( logf ) ∗ l o g f <<” Sequence ”<<j <<” : ”<<dat [ j ]<< s t d : : e n d l ; } if ( logf ) ∗ l o g f <<” A f t e r Back Reduction . . . Number o f m u l t i p l i c a t i o n s : ” <<multi <<s t d : : endl<<s t d : : e n d l ; } // need t o ’ r e g e n ’ t h e b e s t one f o r d i s p l a y i n g i f ( l o g f ) ∗ l o g f <<” Number o f ’ Back R e d u c t i o n s ’ : ”<<UseMe<<s t d : : e n d l ; curBack=BackRed ( Range ( 0 , UseMe ) ) ; curName=BackName ( Range ( 0 , UseMe ) ) ; f o r ( int i =0; i <dat . s i z e ();++ i ) dat [ i ]= holdDat [ i ] ; Vector<int > BackNeedToGen ; while ( i t e r a t i o n ( dat , propRed , curBack , curName ) ) { dat=propRed ; } } /∗ ∗ ∗ S p e c i a l R e d u c t i o n s ∗ ∗ ∗/ void PropReduce : : s p e c i a l R e d u c e ( ) { Vector<Vector<int > > propRed ( base , Vector<int > ( 0 ) ) ; while ( i t e r a t i o n ( dat , propRed , S p e c ia lR e d , SpecialName ) ) { dat=propRed ; } Vector<Vector<int > > curBack=BackRed ( Range ( 0 , UseMe ) ) ; int m u l t i=curBack . s i z e ( ) ; f o r ( int i =0; i <dat . s i z e ();++ i ) { i f ( l o g f ) ∗ l o g f <<” Sequence ”<<i <<” : ”<<dat [ i ]<< s t d : : e n d l ; m u l t i+=dat [ i ] . s i z e ( ) ; } int t t t=Mults−m u l t i ; // s a v i n g s f o r ’ s p e c i a l s ’ Mults−=t t t ; if ( logf ) ∗ l o g f <<” A f t e r S p e c i a l Reduction . . . Number o f m u l t i p l i c a t i o n s : ”
A.2. NMR ALGORITHMS
250
<<Mults<<s t d : : endl<<s t d : : e n d l ; }
void PropReduce : : r e d u c e ( ) { fowardReduce ( ) ; backReduce ( ) ; specialReduce ( ) ; i f ( l o g f ){ ∗ l o g f <<endl<<” The Best Reduction i s f o r u s i n g ” <<UseMe+1<<” Back R e d u c t i o n s ”<<s t d : : e n d l ; ∗ l o g f <<” For a grand t o t a l o f ”<<Mults <<” m u l t i p i c a t i o n s ”<<s t d : : e n d l ; ∗ l o g f <<” The t o t a l Sequence . . . . ”<<s t d : : e n d l ; } f o r ( int j =0; j <dat . s i z e ();++ j ) { i f ( l o g f ) ∗ l o g f <<” Sequence ”<<j <<” : ”<<dat [ j ]<< s t d : : e n d l ; } } // t h e s e f u n c t i o n s w i l l c r e a t e t h e p r o p o g a t o r s // from 3 i n p u t m a t r i x l i s t s . . t h e f i r s t a r e t h e // i n d i v i d u a l p r o p o g a t o r s ( ” 0 ” , ” 1 ” , ” 2 ” . . . ) // t h e second t h e ’ Foward ’ p r o p s ( ” 0 ∗ 1 ” , ” 0 ∗ 1 ∗ 2 ” . . . ) // t h e t h i r d the , ’ Back ’ p r o p s ( ” 7 ∗ 8 ” , ” 6 ∗ 7 ∗ 8 ” . . . ) // t h e f o r t h i s t h e p l a c e t o f i l l . . . void PropReduce : : g e n e r a t e P r o p s ( Vector<matrix > &i n d i v , Vector<matrix > &Foward , Vector<matrix > &Back , Vector<matrix > &F i l l M e ) { i f ( i n d i v . s i z e ( ) ! = base ){ s t d : : c e r r <<” PropReduce : : g e n e r a t e P r o p s ( ) ”<<e n d l ; s t d : : c e r r <<” I n d i v i d u a l M a t r i c i e s must have l e n g t h ’ b a s e ’ ”<<e n d l ; exit (1); } i f ( Foward . s i z e ( ) ! = base −1){ s t d : : c e r r <<” PropReduce : : g e n e r a t e P r o p s ( ) ”<<e n d l ; s t d : : c e r r <<” Foward M a t r i c i e s must have l e n g t h ’ base −1’”<<e n d l ; exit (1); } i f ( FillMe . s i z e ( ) ! = base ){ s t d : : c e r r <<” PropReduce : : g e n e r a t e P r o p s ( ) ”<<e n d l ; s t d : : c e r r <<” F i l l M e M a t r i c i e s must have l e n g t h ’ b a s e ’ ”<<e n d l ; exit (1); } i f ( Back . s i z e ( ) ! = UseMe+1){ s t d : : c e r r <<” PropReduce : : g e n e r a t e P r o p s ( ) ”<<e n d l ;
” Enter l o g f i l e name : ” . q u e r y p a r a m e t e r ( argc . exit (1). i <dat .2. s t d : : c e r r <<” l e n g t h from ’ maxBackReduce ( ) ’ ”<<e n d l . s i z e (). . ” Enter Base : ” . argv . } else { F i l l M e [ i ]= i n d i v [ dat [ i ] [ j ] ] . } e l s e i f ( dat [ i ] [ j ]==speTag ) { F i l l M e [ i ]= a d j o i n t ( i n d i v [ base −1])∗ Foward [ base −2]∗ a djoi nt ( indiv [ 0 ] ) ∗ FillMe [ i ] . 2 . ” Enter f a c t o r : ” . } e l s e i f ( dat [ i ] [ j ] <0){ F i l l M e [ i ]=Back[− dat [ i ] [ j ] / baseTag − 1 ] . h” #include ” p r o p r e d u c e . 3 . h” using namespace BlochLib . j <dat [ i ] . } e l s e i f ( dat [ i ] [ j ] <0){ F i l l M e [ i ]=Back[− dat [ i ] [ j ] / baseTag −1]∗ F i l l M e [ i ] . } else { F i l l M e [ i ]= i n d i v [ dat [ i ] [ j ] ] ∗ F i l l M e [ i ] . } e l s e i f ( dat [ i ] [ j ]==speTag ) { F i l l M e [ i ]= a d j o i n t ( i n d i v [ base −1])∗ Foward [ base −2]∗ adjoint ( indiv [ 0 ] ) . q u e r y p a r a m e t e r ( argc . } } } } } Example usage /∗ ∗ ∗ Sample Usage o f t h e ’ PropReduce ’ c l a s s ∗ ∗ ∗/ #include ” b l o c h l i b . j ++){ i f ( j ==0){ i f ( dat [ i ] [ j ]>=baseTag && dat [ i ] [ j ] ! = speTag ) { F i l l M e [ i ]=Foward [ dat [ i ] [ j ] / baseTag − 1 ] . 1 . f a c t o r ) . s i z e ( ) .++ i ) { f or ( int j =0. char ∗ argv [ ] ) { int base . using namespace s t d . f a c t o r . argv . b a s e ) . int main ( int argc . q u e r y p a r a m e t e r ( argc . } f or ( int i =0. argv . s t d : : s t r i n g fname .A. NMR ALGORITHMS 251 s t d : : c e r r <<” Back M a t r i c i e s must have t h e p r o p e r ”<<e n d l . } } else { i f ( dat [ i ] [ j ]>=baseTag && dat [ i ] [ j ] ! = speTag ) { F i l l M e [ i ]=Foward [ dat [ i ] [ j ] / baseTag −1]∗ F i l l M e [ i ] . fname ) .
matrix &rho . // exp [− i 2 p i t Omega ] // s t o r a g e f o r t h e c o e i f f i c e n t s complex ∗A=new complex [ l s ] . // a d j o i n t ( e v e c t )∗ ro ∗( e v e c t ) . e v e c t ) . k++){ . r e d u c e ( ) . // c a l c u l a t e them .2.2. 0 ∗ Pi ) . } A. e v a l . double dt ) { Vector<complex > f i d ( npts . // p u t rho i n t o e i g e n b a s e o f H matrix s i g 0=a d j p r o p ( e v e c t . t o e i g e n b a s e o f H matrix Do=a d j p r o p ( e v e c t . − dt ∗ 2 . i ) .3 Optimized static Hamiltonian FID propogation Vector<complex > S t a t i c F I D ( matrix &H. i <hs . e v a l=exp ( z ∗ e v a l ) . // a d j o i n t ( e v e c t )∗ de ∗( e v e c t ) . // s t o r a g e f o r t h e e i g e n v a l u e d i f f e r e n c e s complex ∗B=new complex [ l s ] . complex z ( 0 . j ++){ // t h e s h o r t e r m a t r i x t r a c e and m a t r i x m u l t i p i c a t i o n // i n t o an Nˆ 2 l o o p r a t h e r t h e n an Nˆ 3 l o o p A[ pos ] = Do( i . // Put d e t e c t i o n op . & oo ) . j ) ∗ s i g 0 ( j .A. 0 ) . // i ∗ 2 p i d t matrix e v e c t . // do not c a r e a b o u t t h e v a l u e i f t h e c o i e f // i s b e l o w our c u t o f f v a l u e i f ( square norm (A[ pos ]) > c u t o f f ) { pos ++. rho ) . int npts . NMR ALGORITHMS 252 o f s t r e a m oo ( fname . ommiting a n y t h i n g t h a t i s ’ 0 ’ f o r ( i = 0 . // e i g e n v e c t o r s o f H dmatrix e v a l . f a c t o r . matrix & d e t e c t . c s t r ( ) ) . // e i g e n v a l u e s o f H // d i a g o n a l i z e t h e Hamiltonian d i a g (H. int hs = hamil . pos =0. j . 0 . d e c t e c ) . 0 e −10. myReduce . const double c u t o f f = 1 . j <hs .} } } //move n p t s ∗ d t i n time f or ( int k = 0 . // c a l c u l a t e t h e e i g e n v a l u e terms from // t h e m u l t i p l i c a t i o n B [ pos ] = e v a l ( i ) ∗ c o n j ( e v a l ( j ) ) . rows ( ) . int i . i ++){ f o r ( j = 0 . k<n pt s ( ) . PropReduce myReduce ( base . int l s = hs ∗ hs .
” . NMR ALGORITHMS 253 z = 0 . A. Magn . volume =”136” . A p p l i c a t i o n t o D e c o u p l i n g and R o t a t i o n a l Resonance ” . t i t l e =” E f f i c i e n t S p e c t r a l S i m u l a t i o n s i n {NMR} o f R o t a t i n g S o l i d s . M. delete [ ] B . C. A[ p ] ∗ = B [ p ] .4 γ − COM P U T E C++ Class /∗ compute . d o u b l e WR) ’ . N . The $\gamma$− COMPUTE A l g o r i t h m ” . e . Lee and Malcolm H . I t u s e s a l l t h e l i t t l e c o m p u t e s t e p used t o c a l c u l a t e ∗ p r o p o g a t o r t o r e c o n s t r u c t t h e e n t i r e f r e q u e c y range ∗ and t h u s a f i d from t h e f r e q u e n c i e s ∗ ∗ i t a l s o c a l c u l a t e s p r o p o g a t o r s v i a a d i r e c t method ∗ i . a u t h o r=”M a t t i a s Eden and Young K. } A. H . and B i l d s e . p a g e s =”56−71”. return f i d . j o u r n a l =”J . p<pos .2. y e a r =1999 } ∗ i t c a l c u a l t e s a s i n g l e p r o p o g a t o r f o r some m o d u l a t i o n ∗ p e r i o d . p a g e s =”6−14”. T . ” . p++){ // add a l l t h e c o i e f f ∗ f r e q u e n c i e s z += A[ p ] . cc ∗ t h i s l i t t l e class develops s t r o p o s c o p i c a l l y observed s p e c t r a u s i n g t h e ’COMPUTE ’ method g i v e n i n @ A r t i c l e {Eden96 . // temporary s i g n a l // t h i s i s our r e d u c e d // m a t r i x and t r a c e m u l t i p l i c a t i o n f or ( int p = 0 . Reson . t i t l e =” E f f i c i e n t S i m u l a t i o n o f P e r i o d i c Problems i n NMR. a u t h o r=”Hohwy . U( t )=Prod ( exp(− i d t H( t ) ) ) t h e ’ f u n c t i o n t ’ c l a s s MUST have a f u n c t i o n c a l l e d ’ h m a t r i x Hamiltonian ( d o u b l e TIME1 . and N i e l s e n .2. d o u b l e TIME2 . } // a s s i g n temp s i g n a l t o f i d f i d ( k)=z . Magn .A. j o u r n a l =”J . } delete [ ] A. Reson . ” . volume =”120” . y e a r =1996 } @ A r t i c l e {Hohwy99 . L e v i t t ” .
const matrix & d e t ) { i f ( rosym==2){ i f ( r o ==0. h” // B l o c h l i b f i l e ” c o n t a i n e r / Vector / Vector . ∗/ #i f n d e f #define #include #include compute h compute h 1 ” c o n t a i n e r / matrix / matrix . e l s e return 0 . h” // B l o c h l i b f i l e BEGIN BL NAMESPACE template<c l a s s f u n c t i o n t > c l a s s compute { private : // s t o r a g e f o r U k s t a t i c Vector<matrix > U k .A. i t i s a l s o up t o t h e u s e r t o s e t t h e c o r r e c t ROTOR ANGLE BEFORE t h i s i s c a l l e d I t i s d e s g i n e d t o be p a r t o f t h e BLOCHLIB t o o l k i t t h u s t h e ’BEGIN BL NAMESPACE ’ macro and t h e ’ odd ’ i n c l u d e s . int isroSYM ( const matrix & ro .5) // as i t must be an i n t e g e r // t h u s t h e sweep w i d t h may need t o m o d i f i e d // t o a c c o m i d a t e ‘ n ’ // // This a l s o c a l c u l a t e s t h e number o f // ‘ gamma ’ powder a n g l e s we can c a l c u l a t e .5∗( d e t+a d j o i n t ( d e t ) ) ) return 1 . // 1 i f ro ==1/2( d e t+a d j o i n t ( d e t ) ) . 2= not c a l c u l a t e d YET // c a l c u l a t e d v i a ’ isroSYM ’ b e l o w int rosym . // a p o i n t e r t o t h e h a m i l t o n i a n f u n c t i o n c l a s s f u n c t i o n t ∗ mf . 0 = f a l s e . NMR ALGORITHMS 254 where ’ TIME1=t h e b e g i n i n g o f a d e l t a T s t e p ’TIME2=t h e END o f a d e l t a T s t e p ’WR’= t h e Rotor Speed The Hamiltonian f u n c t i o n Must perform t h e c o r r e c t r o t a t i o n under WR. } } // g i v e n a p e r i o d o f ‘ 1 / wr ’ // and a d e s i r e d sweep w i d t h ‘ sw ’ // t h e number ‘ n ’ ( c o m p u t e s t e p ) d i v i s i o n s o f // t h e r o t o r c y c l e i s f l o o r ( sw/wr +0.2. } else { return rosym .
// s t a r t time and end time and s t e p time f o r // t h e p e r i o d double tmin . } public : // t h e TOTAL one p e r i o d p r o p o g a t o r matrix Uf . } // f u n c t i o n s f o r i n t e r n a l v a r i a b l e s i n l i n e double wr ( ) const . int c o m p u t e s t e p I n . double tmin . } i f ( gammaloop <1) gammaloop =1. compute ( f u n c t i o n t & in . c o m p u t e s t e p=int ( f l o o r ( sw / wr + 0 . sw =double ( c o m p u t e s t e p ∗ wr ) . double tau .A. 5 ) ) . double tmax ) . gamma step=gammaloop∗ c o m p u t e s t e p . compute ( ) . i f ( gamma step>=c o m p u t e s t e p ) { gammaloop=gamma step / c o m p u t e s t e p . // compute time =1.2.0) return . // t o t a l number o f gamma a n g l e s // t o c a l c u l a t e int gamma step . double tmax ) . NMR ALGORITHMS 255 // g i v e n ‘ n ’ . we a l t e r t h e ‘ gammaloop ’ // f a c t o r t o > 1 t o perform t h e r e o r d e r i n g // gamma step nee ds t o be a m u t i p l e o f c o m p u t e s t e p void CalcComputeStep ( ) { i f ( wr ==0. s h o u l d we d e s i r e and gamma a n g l e s // computed a t a l l . compute ( f u n c t i o n t & . double tmin . ˜ compute ( ) { mf=NULL . int c o m p u t e s t e p I n ) compute ( f u n c t i o n t & . double tmax . compute ( f u n c t i o n t & ) . wr ./( d o u b l e ( c o m p u t e s t e p )∗ wr ) . // sweep w i d t h and r o t o r s p e e d i n Hz double sw . double wr . // number o f r o t o r d i v i s i o n s int c o m p u t e s t e p . // t o t a l number o f r e o r d e r i n g s o f p r o p o g a t o r s t o c a l c u l a t e // more gamma steps ( gamma step=c o m p u t e s t e p ∗gammaloop ) int gammaloop . double sw .
gammaloop =1. matrix ( ) ) . // t h j e s t a t i c l i s t o f U k m a t r i c e s template<c l a s s f u n c t i o n t > Vector<typename compute<f u n c t i o n > : : matrix> compute<f u n c t i o n t > : : U k ( 1 . NMR ALGORITHMS 256 { return wr . } // c a l c u l a t e s t h e U k p r o p o g a t o r s // g i v e n t h e c u r r e n t gamma a n g l e i n d e x d e s i r e d void calcUFID ( int gammaon ) . gamma step=i n . rosym =2. CalcComputeStep ( ) . // d e f a u l t c o n s t r u c t o r template<c l a s s f u n c t i o n t > compute<f u n c t i o n t > : : compute ( ) { mf=NULL. }. tmax = 0 . // computes t h e FID g i v e n i n i t i a l and d e t e c t i o n // m a t r i c e s and t h e number o f p r o p o g a t o r // p o i n t s d e s i r e d Vector<complex > FID ( matrix & ro . } i n l i n e double sweepWidth ( ) const { return sw . int n pt s ) . CalcComputeStep ( ) . tau = 0 . } // c a l c u l a t e s t h e U k p r o p o g a t o r s // g i v e n no a d d i t i o n a l r e o r d e r i n g // t o compute t h e gamma a n g l e s void calcUFID ( ) { calcUFID ( 1 ) . tmin = 0 . } . sw =0. } i n l i n e void setSweepWidth ( double i n ) { sw =i n . gamma step =10.pmax=0. } i n l i n e int gammaStep ( ) const { return gamma step . . . CalcComputeStep ( ) . wr =0.2.A. matrix & det . . } i n l i n e void setWr ( double i n ) { wr =i n . } i n l i n e void setGammaStep ( int i n ) { RunTimeAssert ( in >=1). c o m p u t e s t e p =0.
sw =sw . . r e s i z e ( c o m p u t e s t e p +1 . wr =0. tmin=t m i n i n . MatrixType T > : : compute ( f u n c t i o n t & in . mf−>Fe ( ) ) . pmax=c o m p u t e s t e p +1. . gamma step =10. } // c o n s t c t o r a s s i g n s f u n c t i o n p o i n t e r // and c o m p u t e s t e p template<c l a s s f u n c t i o n t > compute<f u n c t i o n t > : : compute ( f u n c t i o n t & in . tmin = 0 . } template<c l a s s f u n c t i o n t > compute<f u n c t i o n t . tmin = 0 . int c o m p u t e s t e p I n ) { mf=&i n . // r o t o r s p e e d double sw . gamma step =10. r e s i z e ( c o m p u t e s t e p +1 . . tmax = 0 . wr =wr . c o m p u t e s t e p =0. // sweep w i d t h double tminin . Uf=mf−>Fe ( ) . rosym =2. pmax=0. tau = 0 . .A. . // f u n c t i o n double wr . U k . ”<<s t d : : e n d l . tmax=tmaxin . s t d : : c e r r <<” your time f o r t h e p r o p g a t o r i s n e g a t i v e ”<<s t d : : e n d l . i f ( tau <=0){ s t d : : c e r r <<s t d : : endl<<s t d : : e n d l <<” E r r o r : compute : : compute ( ) ”<<s t d : : e n d l . . CalcComputeStep ( ) . . gammaloop =1. tmax = 0 . NMR ALGORITHMS 257 // c o n s t c t o r a s s i g n s f u n c t i o n p o i n t e r template<c l a s s f u n c t i o n t > compute<f u n c t i o n t > : : compute ( f u n c t i o n t & i n ) { mf=&i n . pmax=c o m p u t e s t e p +1. gammaloop =1. an e v i l s t e n c h f i l l s t h e room . . tau=(tmax−tmin ) / c o m p u t e s t e p . // s t a r t time o f a p e r i o d double tmaxin ) // end time o f a p e r i o d { mf=&i n . c o m p u t e s t e p=c o m p u t e s t e p I n . s t d : : c e r r <<” . rosym =2. .2. sw =0. U k . Uf=mf−>Fe ( ) . . tau = 0 . mf−>Fe ( ) ) . .
2. . . s t d : : c e r r <<” . r e s i z e ( c o m p u t e s t e p +1 .A. t2 . . i <c o m p u t e s t e p . Uf=mf−>Fe ( ) . gammaloop =1. } // c a l c u l a t e t h e U k p r o p o g a t o r s template<c l a s s f u n c t i o n t > void compute<f u n c t i o n t > : : calcUFID ( int gammaon ) { // t h e e f f e c i t v e ’ gamma ’ a n g l e i s // performed by ’ s h i f t i n g ’ time . t2 . i d e n t i t y ( hh . } U k [ i +1]=hh∗U k [ i ] . tmax=tmaxin . c o m p u t e s t e p=c o m p u t e s t e p i n . . ”<<s t d : : e n d l . . double t 1=tmin+tadd . wr ) . L I N E ) } rosym =2. // i n i t i a l compute s t e p s double tminin . double t 2=tmin+tadd+tau . tau=(tmax−tmin ) / c o m p u t e s t e p . BLEXCEPTION( F I L E . rows ( ) ) . U k . 1 ) ∗ tau ∗ PI2 ) . an e v i l s t e n c h f i l l s t h e room . double tadd=PI2 ∗double ( gammaon ) / double ( gammaloop∗ c o m p u t e s t e p ) / wr . LINE ) template<c l a s s f u n c t i o n t > compute<f u n c t i o n t > : : compute ( f u n c t i o n t & in . // t h e f u n c t i o n int c o m p u t e s t e p i n . − complex ( 0 . i ++){ hh=Mexp( mf−>Hamiltonian ( t1 . gamma step =10. s t a t i c matrix hh . t 2+=tau . mf−>Fe ( ) ) . // l o o p t h r o u g h t h e compute s t e p d i v i s i o n s // u s i n g t h e ’ Hamiltonian ( t1 . i f ( i ==0){ U k [ 0 ] . s t d : : c e r r <<” your time f o r t h e p r o p g a t o r i s n e g a t i v e ”<<s t d : : e n d l . i f ( tau <=0){ s t d : : c e r r <<s t d : : e n d l <<s t d : : endl<<” E r r o r : compute : : compute ( ) ”<<s t d : : e n d l . . wr ) f u n c t i o n // r e q u i r e d i n t h e f u n c t i o n t f or ( int i =0. // b e g i n i n g time o f t h e p e r i o d double tmaxin ) // t h e end time o f t h e p e r i o d { mf=&i n . t 1+=tau . . NMR ALGORITHMS 258 BLEXCEPTION( } rosym =2. tmin=t m i n i n . } FILE . .
q<gammaloop . r =0 . int n p t s ) { Vector<complex > f i d ( npts .2. } // f i d c a l c u l a t i o n // ne eds 1 ) t o l o o p t h r o u g h a l l p e r m u t a t i o n s // o f t h e gammaloop . 0 ) . f o r ( int q=0. matrix & det . i <N. N ) . int i =0 . // t h e m a t r i x o f f r e q u e n c i e s d i f f e r e n c e s // w r s matrix wrs (N. matrix e v e c t . // v e c t o r o f l o g ( e i g e n v a l u e s ) i n H e f f Vector<complex > ev (N . // e i g e n v a l u e s // c a l c u l a t e t h e e f f e c t i v e Hamiltonian // from t h e t o t a l p e r i o d p r o p o g a t o r d i a g ( Uf . q++){ calcUFID ( q ) . double ( c o m p u t e s t e p ) ∗ tau2PI ) . 0 ) . rows ( ) . i <N. NMR ALGORITHMS 259 } // t o t a l p e r i o d p r o p o g a t o r i s t h e l a s t s t e p Uf=(U k [ c o m p u t e s t e p ] ) . j =0 . p =0 . t o s h i f t time p r o p e r l y // 2 ) use t h e p r o p o g a t o r s t o c a l c u l a t e t h e FID template<c l a s s f u n c t i o n t > Vector<complex> compute<f u n c t i o n t > : : FID ( matrix & ro . . double tau2PI=tau . } return f i d . e v a l . int N=Uin . e v e c t ) . // e i g n e v e c t o r s dmatrix e v a l . s =0. int n p t s ) { // z e r o o u t a new FID v e c t o r Vector<complex > f i d ( npts . det . 0 ) . } template<c l a s s f u n c t i o n t > Vector<complex> compute<f u n c t i o n t > : : calcFID ( matrix & ro .A. i ) ) . // c a l c u l a t e t h e t r a n s i t i o n m a t r i x complex t o t t=complex ( 0 . matrix & det . i ++){ . n p t s ) . // i s ro and d e t symmetric ? rosym=isroSYM ( ro . f or ( i =0. f i d+=calcFID ( ro . d e t ) . i ++){ ev [ i ]= l o g ( e v a l ( i . } f or ( i =0.
1 e −10). . } } // t h e gamma−compute a l g o r i t h m use t h e symetry r e l a t i o n // be tw ee n t h e gamma powder a n g l e s DIVIDED i n t o 2 Pi / c o m p u t e s t e p // s e c t i o n s . . j <N. r e s i z e ( c o m p u t e s t e p ) . j )=chop ( ( ev [ i ]− ev [ j ] ) / t o t t . // b e c u a s e we have some n i c e time symetry be tw e en t h e gamma // a n g l e and t h e time e v e o l v e d . Qs .A. // t h e o p e r a t o r s 0 U k were c a l c u l a t e d i n t h e f u n c t i o n // ’ calcUFID ( i n t ) ’ // // t h e ’ p t h ’ one o f a l l o f t h e s e i s r e a l a t e d b a c k t o t h e ’ 0 t h ’ // o p e r a t o r by some s e r i e s o f i n t e r n a l m u l t i p l i c a t i o n s // complex tmp1 . s t a t i c Vector<matrix > Qs . t h e d e t e c t i o n o p e r a t o r // so f o r each gamma a n g l e we would t h i n k t h a t we would need // ( c o m p u t e s t e p ) p r o p o g a t o r s f o r each r o t o r c y c l e d i v i s i o n // ( which we s e t t o be e q u a l t o ( c o m p u t e s t e p ) a l s o ) t o // c o r i s p o n d t o each d i f f e r e n t gamma a n g l e . . . . . from t h i s one i n s e l e c t c o m b i n a t i o n s // we can g e n e r a t e a l l t h e ( c o m p u t e s t e p ) p r o p o g a t o r s from // t h e gamma=0 ones we s t i l l need t o d i v i d e our r o t o r c y c l e up // however .2. r e s i z e ( c o m p u t e s t e p ) . So we o n l y need t o c a l c u l a t e t h e // p r o p o g a t o r s f o r gamma = 0 . s t a t i c Vector<matrix > RoT . . . a l s o i n t o ( c o m p u t e s t e p ) p r o p o g a t o r s // f o r t h e remaining n o t e s i w i l l use t h i s l a b a l i n g c o n v e n t i o n // // pQs k −−> t h e t r a n s f o r m e d d e t e c t i o n o p e r a t o r f o r t h e k t h // r o t o r d i v i s i o n f o r a gamma a n g l e o f // ’ p ’∗2 Pi /( c o m p u t e s t e p ) // // pRoT −−> t h e t r a n s f o r m e d d e n s i t y m a t r i x f o r t h e f o r a gamma // a n g l e o f ’ p ’∗2 Pi /( c o m p u t e s t e p ) // NOTE : : t h e r o t o r d i v i s i o n i n f o i s c o n t a i n e d i n t h e Qs // // pU k −−> t h e u n i t a r y t r a s f o r m a t i o n f o r t h e i t h r o t o r d i v i s i o n // f o r a gamma a n g l e o f ’ p ’∗2 Pi /( c o m p u t e s t e p ) . j ++){ wrs ( i . NMR ALGORITHMS 260 f or ( j =0. w e l l not so . // c a l c u l a t i n g // t h e k t h d e n s i t y m a t r i x // (0RoT)ˆ d=( e v e c t )ˆ d ∗(0 U k )ˆ d∗ ro ∗(0 U k )∗ e v e c t // t h e k t h d e t e c t i o n op // (0 Qs k )ˆ d=( e v e c t )ˆ d ∗(0 U k )ˆ d∗ ro ∗(0 U k )∗ e v e c t // // IF ro =1/2( d e t+a d j o i n t ( d e t ) ) t h e n // t h e i t h d e n s i t y m a t r i x i s ( 0RoT)ˆ d=0Qs k +(0 Qs k )ˆ d // . RoT .
// a m p l i t u d e c a l c u l a t i n g int ind1 . NMR ALGORITHMS 261 // the ’ˆ d ’ is a adjoint operation f o r ( i =0. p++){ // p r o p e r ‘ p ‘ f o r Q s e l e c t i o n i n d e x i n d 1 =( i+p)%( c o m p u t e s t e p ) . r++){ . r ) // ∗ Qs [ k+p%c o m p u t e s t e p ] ( r . mf−>Fz ( ) ) . a d j p r o p ( U k [ i + 1 ] . i f ( ( i+p)>=c o m p u t e s t e p ) { i n d 2=p−c o m p u t e s t e p . . i <c o m p u t e s t e p .A. s ) // // pF k ( r . a d j p r o p ( U k [ i + 1 ] . fo r ( r =0. } tmp1=complex (0. s ) t a u ) s t a t i c Vector<matrix > Fave . s ) } // = 1/( c o m p u t e s t e p )∗Sum ( . s ) j t o t t ] // // h e r e m=i n t ( ( k+p )/ c o m p u t e s t e p )) − i n t ( p/ c o m p u t e s t e p ) // o f c o u r s e we have many ’ p ’ s e c t i o n s ( or gamma a n g e l s ) // t h a t c o n t r i b u t e t o t h e a m p l i t u d e f a c t o r s . r o ) ) . s ) t a u )} // ∗0 Qs ( k+p%n ) ( r . i <c o m p u t e s t e p . i n d 2 . s )= exp [ i m wrs ( r . i ++){ f o r ( p=0. s ) ] exp(− i // [ j+p−i n t ( ( j+p )/ c o m p u t e s t e p ) ) ∗ // c o m p u t e s t e p ] wrs ( r . r e s i z e ( compute step .p<c o m p u t e s t e p .2. i f ( rosym==0) RoT [ i ]= a d j p r o p ( e v e c t . } //The s i g n a l i s t h e n a n i c e sum o v e r t h e t r a n s i t i o n m a t r i x // and t h e pQs ’ s and pRos Of c o u r s e t h i s i s where we // m a n i p u l a t e t h e ’ 0 t h ’ o p e r a t o r s t o c r e a t e t h e ’ p t h ’ // and combine them a l l i n t o a ’ f ’ m a t r i x which // c o n t a i n s t h e a m p l i t u d e s // // pF k ( r . i ++){ Qs [ i ]= a d j p r o p ( e v e c t . e l s e RoT [ i ]=Qs [ i ]+ a d j o i n t ( Qs [ i ] ) . r ) // exp ( i [ p−i n t ( p/ c o m p u t e s t e p )∗ // c o m p u t e s t e p ] wrs ( r . we can // e a s i l y sum them i n t o a t o t a l a m p l i t u d e // // Fave k ( r . s ) t o t t ] ∗ Ro [ p%c o m p u t e s t e p ] ( s . matrix tmm(N. s ) exp [− i wrs ( r . } else { i n d 2=p .N ) . and b e c u a s e // t h e y a r e s t r i c t l y a m p l i t u d e s f o r s e p a r a t e gamma a n l g e s . . s )=1/ c o m p u t e s t e p ∗ Sum ( p =0)ˆ( p=n −1) { pF k ( r . d e t ) ) . r<N. Fave . f o r ( i =0. . − double ( i n d 2 ) ∗ tau2PI ) . s )= means t h e ’ p t h ’ gamma a n g l e f o r t h e // ’ kth ’ rotor d i v i s i o n element ( r . ) { [ ( p%c o m p u t e s t e p )R] ( s .
s )∗ exp [ i wrs ( r . // advance ’ time ’ exp ( d t ∗ w i j ) tmpmm( r . ( tau2PI ) ) . . f or ( r =0. . / double ( c o m p u t e s t e p / f f ) ) . } } } } complex tmp=complex ( 0 . s ) ∗tmpmm( r . } } } // need t o n o r m a l i z e t h e f i d as we have // added t o g e t h e r many ‘ sub f i d s ’ // b u t t h e t o t a l s h o u l d s t i l l be 1 int f f = rosym ==1?2:1. // copy w r s // t h e FID a t i n t e r v a l s o f i ∗ t a u i s t h e n g i v e n by // // s ( i ∗ t a u )=Sum ( r .++r ) { for ( s =0. s)+=Qs [ i n d 1 ] ( r . } /∗ ∗ ∗ END compute CLASS ∗ ∗ ∗/ END BL NAMESPACE #endif . r ) ∗ exp ( tmp1∗ wrs ( r . s ) . } } f or ( i =1. j ++){ f i d [0]+= Fave [ 0 ] ( i .A. return f i d . // c a l c u l a t e t h e exp ( i ∗ t a u ∗ wrs ) once wrs=exp ( tmp∗ wrs ) . i ++){ f or ( j =0. s ) { F a v e i ( r . s<N. NMR ALGORITHMS 262 for ( s =0. j ) . s<N. i <N. i <n p t s . s )∗= wrs ( r . f i d ∗=double ( 1 . s a v e s us a exp c a l c u l a t i o n f or ( i =0.++s ) { f i d [ i ]+=Fave [ p ] ( r . . . i ++){ // t o s e l e c t t h e p r o p e r ’ p ’ f o r Fave p p=i%c o m p u t e s t e p . matrix tmpmm( wrs ) . s ) . s ) ∗ RoT [ i ] ( s . j <N.2. . // i ∗ t a u // a l i t t l e c o m p ut a t io n time s a v e . s ) ) . s ) i ∗ t a u ] } // h e r e i s t h e j =0 p o i n t . r<N. s++){ Fave [ p ] ( r .
7a #p e r f o r m s a p o i n t −to−p o i n t C7 ( a 1D FID) . BLOCHLIB CONFIGURATIONS AND SOURCES 263 A.3.3 A.A.3.5 1 #j c o u p l i n g < i s o > <spin1 > <s p in 2 >} J 400 0 1 } parameters { #use a f i l e found w i t h t h e $ B l o c h L i b $ d i s t r i b u t i o n } powder { #powder f i l e used f o r t h e s t a t i c FID aveType ZCW 3 3722 #powder f i l e used f o r t h e s p i n n i n g FID #aveType rep2000 } #number o f 1D f i d p o i n t s npts1D=512 #sweep w i d t h } sw=40000 } pulses { #s e t t h e s p i n n i n g wr=0 #s e t f o r NON p i n n i n g FID −s #wr=2000 # s e t f o r SPINNING FID #s e t t h e r o t o r } r o t o r =0 #s e t f o r NON p i n n i n g f i d s −s #r o t o r=a c o s (1/ s q r t ( 3 ) ) ∗ r a d 2 d e g # s e t f o r SPINNING FID #s e t t h e d e t e c t i o n m a t r i x d e t e c t ( Ip ) #s e t t h e i n i t i a l m a t r i x ro ( Ix ) #no p u l s e s n e c e s s a r y f o r ro=I x #c o l l e c t t h e f i d fid () s a v e f i d t e x t ( s im p S t a t ) #s a v e as a t e x t f i l e } postC7 input ﬁle for the pointtopoint FID in Figure 5.1 BlochLib Conﬁgurations and Sources Solid conﬁguration ﬁles 1D static and spinning experiments shown in Figure 5.6 # a s i m p l e MAS and S t a t i c FID c o l l e c t i o n spins { #t h e g l o b a l o p t i o n s } numspin 2 T 1H 0 T 1H 1 #c s a < i s o > <d e l > <et a > <s p i n >} C 5000 4200 0 0 C −5000 6012 0.
s t p h +180) 1H: p u l s e ( t270 .A. s t p h ) s t p h=s tp h+p h s t end } #a s i n g l e f i d i s c o n s i d e r e d p o i n t t o p o i n t } ptop ( ) #s e t t h e s p i n n i n g wr=5000 r o t o r=rad2deg ∗ a c o s ( 1 / sqrt ( 3 ) ) #can use ’ r e u s e ’ as t h e v a r i a b l e s # a r e s e t once i n our s u b s e c t i o n r e u s e ( sub1 ) . BLOCHLIB CONFIGURATIONS AND SOURCES 264 spins { #t h e g l o b a l o p t i o n s numspin 2 T 1H 0 T 1H 1 D 1500 0 1 } parameters { powder { aveType zcw thetaStep 233 phiStep 144 } #t h e i n t e g r a t o r s t e p s i z e maxtstep=1e−6 #number o f 1D f i d p o i n t s npts1D=512 roeq= Iz d e t e c t=I z } pulses { #our p o s t −C7 sub p u l s e s e c t i o n sub1 { #p o s t C7 p u l s e a m p l i t u d e amp=7∗wr a m p l i t u de (amp) #p h ase s t e p p e r s s tp h=0 p hs t =360/7 #p u l s e t i m e s t 9 0 =1/amp/4 t 2 7 0 =3/amp/4 t 3 6 0 =1/amp #p o s t C7 l o o p l o o p ( k =1:7) 1H: p u l s e ( t90 . s t p h ) 1H: p u l s e ( t360 .3.
BLOCHLIB CONFIGURATIONS AND SOURCES 265 #c o l l e c t t h e f i d } fid () s a v e f i d t e x t ( simpC7 ) #s a v e t a s a t e x t f i l e } postC7 input ﬁle for the 2D FID in Figure 5. s t p h ) 1H: p u l s e ( t360 . s t p h +180) 1H: p u l s e ( t270 .A.3.7b # performs a ’ r e a l ’ experiment # f o r t h e p o s t −C7 ( a s e r i e s o f 2D f i d s a r e c o l l e c t e d ) spins { #t h e g l o b a l o p t i o n s numspin 2 T 1H 0 T 1H 1 D 1500 0 1 } parameters { powder { aveType zcw thetaStep 233 phiStep 144 } #number o f 1D f i d p o i n t s npts1D=512 } pulses { #our p o s t −C7 sub p u l s e s e c t i o n sub1 { #p o s t C7 p u l s e a m p l i t u d e amp=7∗wr a m p l i t u de (amp) #phase s t e p p e r s s tp h=0 p hs t =360/7 #p u l s e t i m e s t 9 0 =1/amp/4 t 2 7 0 =3/amp/4 t 3 6 0 =1/amp #p o s t C7 l o o p } l o o p ( k =1:7) 1H: p u l s e ( t90 . s t p h ) s t ph=s tp h+p hs t end } .
3. par MyCoil { subcoil1 { type h e l m h o l t z loops 25 amps −4 numpts 4 0 0 0 R 2 length 3 axis z } subcoil2 { .3. m) #p u l s e t h e IZ down t o t h e xy p l a n e f o r d e t e c t i o n 1H: p u l s e ( t90 . amp) #c o l l e c t t h e f i d a t t h e ’ mth ’ p o s i t i o n f i d (m) #r e s e t t h e ro b a c k t o t h e eq ro ( Iz ) end s a v e f i d m a t l a b ( 2 dc7 ) #s a v e t h e m a t l a b f i l e } } A. BLOCHLIB CONFIGURATIONS AND SOURCES 266 #number o f 2D p o i n t s f i d p t =128 #c o l l e c t i o n a m a t r i x o f d a t a 2D( ) #s e t t h e s p i n n i n g wr=5000 #t h e b a s i c r o t o r a n g l e r o t o r=rad2deg ∗ a c o s ( 1 / sqrt ( 3 ) ) ) #s e t t h e d e t e c t i o n m a t r i x d e t e c t ( Ip ) #r e s e t t h e ro b a c k t o t h e eq ro ( Iz ) #9 0 time a m p l i t u d e s amp=150000 t 9 0 =1/amp/4 #l o o p o v e r t h e r o t o r s t e p s l o o p (m=0: f i d p t −1) #may use ’ r e u s e ’ a l l v a r i a b l e s a r e s t a t i c i n sub1 # must be r e p e a t m t i m e s t o advance t h e d e n s i t y m a t r i x # f o r each f i d ( t h e f i r s t f i d g e t s no c7 ) r e u s e ( sub1 .A. 2 7 0 . and not part of the normal please view the source code in the distribution for details.2 Magnetic Field Calculator input ﬁle The input coil type ‘Dcircle’ is a user registered function.
1 . −5 } } grid { min −1.3. mat A.5 } subcoil3 { type D c i r c l e loops 1 amps 2 numpts 2 0 0 0 R 2 theta1 0 theta2 180 axis z c e n t e r 0 . It demonstrates the slight oﬀset eﬀect imposed by the magnetization of one spin on another. −.3. 1 0 } params { #which magnetic f i e l d s e c t i o n t o use s e c t i o n MyCoil #o u t p u t textout #o u t p u t matout } t e x t f i l e name shape .6 .3.A.3 A.6 . b i o t m a t l a b f i l e name f i e l d .−1. −. 1 dim 1 0 . Both the C++ source using the BlochLib framework and the . BLOCHLIB CONFIGURATIONS AND SOURCES 267 type D c i r c l e loops 1 amps 2 numpts 2 0 0 0 R 2 theta1 0 theta2 180 axis z center 0 . [100]. 1 0 .−1 max 1 .4 Quantum Mechanical Single Pulse Simulations Example Classical Simulation of the Bulk Susceptibility This simulation is a replication of the simulation performed by M. Augustine in Figure 2 of Ref.
. C++ source #include ” b l o c h l i b . char ∗ argv [ ] ) { std : : s t r i n g fn . argv . s t d : : s t r i n g f o u t=p s e t .3. getParamI ( ” n pt s ” ) . } int main ( int argc . getParamS ( ” s p i n t y p e 2 ” ) . getParamD ( ” t f ” ) . double t f=p s e t . h o p e f u l l y we s h a l l s e e s e v e r a l e c h o s i n t h e i n d i r e c t dimension a HETOCR i s a 2D e x p e r i e m e n t s p i n 1 :: 90 − − t −−90−−−−− s p i n 2:: −−−−−−−90−FID ∗/ t i m e r stopwatch . using namespace s t d . h” // t h e r e q u i r e d 2 namespaces using namespace BlochLib . . /∗ THis s i m u l a t e s t h e e f f e c t o f t h e Bulk S u s e p t i b i l i t y on a HETCOR e x p e r i e m e n t . . ” Enter f i l e t o p a r s e : ” . s t r i n g s p i n t y p e 1=p s e t . Results from this simulation can be found in Figure 5. getParamD ( ” moles ” ) . Parameters p s e t ( f n ) . } void I n f o ( s t d : : s t r i n g mess ) { cout<<mess<<e n d l . getParamS ( ” f i d o u t ” ) . double inTemp=p s e t . // t h e parameter f i l e q u e r y p a r a m e t e r ( argc . getParamS ( ” s p i n t y p e 1 ” ) .11. 1 .A. f n ) . getParamS ( ” d e t e c t ” ) . BLOCHLIB CONFIGURATIONS AND SOURCES 268 conﬁguration ﬁle is given. . s t r i n g s p i n t y p e 2=p s e t . cout . void printTime ( int nrounds =1){ s t d : : c o u t << s t d : : endl << ”Time taken : ” << ( stopwatch ( ) / nrounds ) << ” s e c o n d s ” . s t r i n g d e t s p=p s e t . getParamD ( ” t e m p e r a t u r e ” ) . // g e t t h e b a s i c p a r a m e t e r s int n s t e p s=p s e t . f l u s h ( ) . double moles=p s e t .
” ) . pang1 [ 2 ] ∗ PI2 . . . . phase . . BLOCHLIB CONFIGURATIONS AND SOURCES 269 coord<int > dims ( p s e t . TheShape t e s t e r . t e s t e r ) . // g e t t h e Bo double inBo=p s e t . Info ( ” Creating r e a l pulse l i s t s . . ” ) . } e l s e { mypars ( j )= s p i n t y p e 2 . pang2 [ 1 ] ∗DEG2RAD) . ” ) . Grid<UniformGrid > gg ( mins . I n f o ( ” C r e a t i n g i n i t a l shape . moles ( moles / nsp ) . getParamCoordD ( ”max” ) ) . s i z e ( ) . I n f o ( ” C r e a t i n g t o t a l shape−g r i d . nsp=mypars . I n f o ( ” C r e a t i n g e n t i r e s p i n parameter l i s t f o r ” +i t o s t ( nsp)+ ” spins . . getParamCoordD ( ”min” ) ) . pang2 [ 2 ] ∗ PI2 . . s t d : : s t r i n g dataou=p s e t . j j ) . getParamCoordI ( ”dim” ) ) . f a l s e ) . getParamCoordD ( ” p u l s e 2 ” ) . getParamS ( ” t r a j e c t o r i e s ” . s i z e ( ) . TheGrid j j ( gg . typedef XYZshape<TheShape > TheGrid . // g e t t h e i n f o from t h e p s e t coord <> pang1=p s e t . . //The p u l s e l i s t f o r a r e a l p u l s e on p r o t o n s .3. ” ) . . pang2 [ 2 ] ∗ PI2 . . . I n f o ( ” S e t t i n g s p i n parameter o f f s e t s . BPoptions : : D e n s i t y  BPoptions : : H i g h F ie l d . . pang1 [ 1 ] ∗DEG2RAD) . . P u l s e PP2( s p i n t y p e 1 . ”1H” . MyPars mypars ( nsp . for ( int j =0. ” ) . pang2 [ 1 ] ∗DEG2RAD) .A. getParamCoordD ( ” p u l s e 1 ” ) . . . // Grid S e t up typedef XYZfull TheShape . . j ++){ i f ( j %2==0){ mypars ( j )= s p i n t y p e 1 . . coord <> mins ( p s e t . . Info ( ” Creating grid . ” ) . ” ” . coord <> maxs ( p s e t . PP2+=P u l s e ( s p i n t y p e 2 . // ( s p i n . } mypars ( j ) . // L i s t BlochParameters typedef ListBlochParams< TheGrid . maxs . double > MyPars . a m p l i t u d e . getParamD ( ”Bo” ) . j <nsp . o f f s e t ) P u l s e PP1( s p i n t y p e 1 . dims ) . double d e l a y s t e p=p s e t . coord <> pang2=p s e t . . int nsp= j j . getParamD ( ” d e l a y ” ) .
p r i n t ( c o u t ) . // t y p e d e f s f o r Bloch parameter s e t s typedef Bloch < MyPars . double o f f s e t 2=p s e t . 0 / t2s1 . getParamD ( ” T2 1 ” ) . . Relax <>. myBs ) . double t 1 s 2=p s e t . O f f s e t <> myOffs ( mypars . } else { myOffs . myRels . o f f s e t 1 ) . getParamD ( ” o f f s e t 1 ” ) ∗ PI2 . ” ) .3. 0 : 1 . 0 / t 2 s 1 . ( ! t2s1 ) ? 0 . // R e l a x a t i o n double t 2 s 1=p s e t . for ( int i =0.++ i ) { // s e t t h e o f f s e t s and r e l a x t i o n v a l s i f ( i %2==0){ myOffs . T1( i ) = ( ! t 1 s 1 ) ? 0 . 0 / t 1 s 1 . t e m p e r a t u r e ( inTemp ) . getParamD ( ” T2 2 ” ) . } } // Bulk s u s e p t i b i l i t y double D=p s e t . myRels . calcTotalMo ( ) . . 0 / t 2 s 2 . 0 : 1 . } mypars . myRels . 0 / t 1 s 2 . T2( i ) = ( ! t 2 s 1 ) ? 0 . o f f s e t ( i )= o f f s e t 2 . ( ! t1s1 ) ? 0 . mypars . mypars . 0 / t1s1 ) . 0 : 1 . p r i n t ( c o u t ) . 0 : 1 . myRels . PP1 . o f f s e t ( i )= o f f s e t 1 . 0 : 1 . p r i n t ( c o u t ) . getParamD ( ”D” ) . Relax <> myRels ( mypars . M y I n t e r a c t i o n s > P u l s e B l o c h . getParamD ( ” o f f s e t 2 ” ) ∗ PI2 . . i <nsp. getParamD ( ” T1 2 ” ) . . Info (” Setting Interactions . BulkSus myBs(D) . Pulse . BulkSus > M y I n t e r a c t i o n s . PP2 . double t 2 s 2=p s e t . 0 : 1 . double t 1 s 1=p s e t . myRels . Bo ( inBo ) . BLOCHLIB CONFIGURATIONS AND SOURCES 270 mypars ( j ) . T2( i ) = ( ! t 2 s 2 ) ? 0 . T1( i ) = ( ! t 1 s 2 ) ? 0 . getParamD ( ” T1 1 ” ) .A. // t o t a l i n t e r a c t i o n o b e c t M y I n t e r a c t i o n s MyInts ( myOffs . // t h e o f f s e t s // g e t t h e f i r s t o f f s e t double o f f s e t 1=p s e t . // Extra i n t e r a c t i o n s typedef I n t e r a c t i o n s < O f f s e t <>.
getParamI ( ” npts2D ” ) . cout . 5 ) ) . M y I n t e r a c t i o n s > NoPulseBloch . 5)). // t h e time t r a i n s t h i s one w i l l a l w a y s be t h e same I n f o ( ” I n i t i a l i z i n g Time t r a i n f o r f i r s t P u l s e . 10)). MyInts ) . ” ) . . 1 0 . // g e t t h e time f o r t h e 2 9 0 p u l s e double t p u l s e 1=PP1 . 10 . // This i s t h e ‘ Bloch ’ t o perform a p u l s e P u l s e B l o c h myparspulse ( mypars . . FASTER) NoPulseBloch me . t p u l s e 2+t p u l s e 1+c urD e lay+t f . // t h e time t r a i n s f o r t h e d e a l y TimeTrain<UniformTimeEngine > P2 ( UniformTimeEngine ( t p u l s e 1+curDelay . . TimeTrain<UniformTimeEngine > F1 ( UniformTimeEngine ( t p u l s e 2+t p u l s e 1+curDelay . s p i n t y p e 1 ) . . // second dimension p o i n t s int npts2D=p s e t . BLOCHLIB CONFIGURATIONS AND SOURCES 271 typedef Bloch < MyPars . . // o u t i n i t i a l c o n d i t i o n Vector<coord <> > tm=me . nsteps . me=( myparspulse ) . TimeTrain<UniformTimeEngine > P1 ( UniformTimeEngine ( 0 . cout<<”On d e l a y : ”<<curDelay<<” ”<<kk<<” / ”<<npts2D <<” \ r ” .A. // our d a t a m a t r i x matrix FIDs ( npts2D . t p u l s e 1 . s p i n t y p e 1 ) . t p u l s e 2+t p u l s e 1+curDelay . f l u s h ( ) . // This i s t h e Bloch s o l v e r t o C o l l e c t t h e FID // ( i . NoPulse .++kk ) { double curDelay=double ( kk ) ∗ d e l a y s t e p . t p u l s e 1+curDelay . // t h e time t r a i n s f o r t h e d e a l y TimeTrain<UniformTimeEngine > D1( UniformTimeEngine ( t p u l s e 1 . timeForAngle ( pang2 [ 0 ] ∗ Pi / 1 8 0 . currentMag ( ) . double t p u l s e 2=PP2 . // l o o p o v e r a l l our D v a l u e s for ( int kk =0. has no p u s l e s . . . 1 0 . e . n s t e p s ) .kk<npts2D. timeForAngle ( pang1 [ 0 ] ∗ Pi / 1 8 0 . . .3. 1 0 ) ) . PP1 .
d r i v . return −1. i f ( ! drivP . } // t h e f i d s i n i t i a l c o n d i t i o n i s j u s t t h e p r e v i o u s // i n t e g r a t i o n s l a s t p o i n t B l o c h S o l v e r <NoPulseBloch > d r i v (me .3. ” ) . s o l v e ( P1 ) ) { I n f o ( ” ERROR ! ! . drivP . return −1. FIDs ) . l a s t P o i n t ( ) ) . . B l o c h S o l v e r <P u l s e B l o c h > drivP ( myparspulse . . . // i n t e g r a t e t h e Delay d r i v . s o l v e ( F1 ) ) { FIDs . s e t W r i t e P o l i c y ( SolverOps : : Hold ) . // i n t e g r a t e t h e P u l s e drivP . drivP . } . s e t I n i t i a l C o n d i t i o n ( drivP . . } // s e t t h e d e t e c t i o n s p i n driv . setDetect ( detsp ) . BLOCHLIB CONFIGURATIONS AND SOURCES 272 stopwatch . s o l v e (D1 ) ) { I n f o ( ” ERROR ! ! . . r e s e t ( ) . } // i n t e g r a t e second t h e P u l s e drivP . d r i v . printTime ( ) . . ” ) . put ( ” vdat ” . i f ( ! d r i v . d r i v . s e t W r i t e P o l i c y ( SolverOps : : Hold ) . . s e t C o l l e c t i o n P o l i c y ( SolverOps : : MagAndFID ) . c o u l d not i n t e g r a t e p u l s e P2 . s e t P u l s e s (PP2 ) . i f ( ! drivP . c l o s e ( ) . . tm . matout . c o u l d not i n t e g r a t e p u l s e P1 . ” ) . l a s t P o i n t ( ) ) . s e t I n i t i a l C o n d i t i o n ( d r i v . c o u l d not i n t e g r a t e d e l a y D1 . drivP . l a s t P o i n t ( ) ) . s e t P r o g r e s s B a r ( SolverOps : : O f f ) . } } matstream matout ( f o u t . ” out ” ) . . putRow ( kk . . s o l v e ( P2 ) ) { I n f o ( ” ERROR ! ! . // s e t v a r i o u s d a t a c o l l e c t i o n p o l i c i e s d r i v . s e t W r i t e P o l i c y ( SolverOps : : Hold ) . matout . s e t W r i t e P o l i c y ( SolverOps : : Hold ) . i o s : : b i n a r y  i o s : : out ) . return −1. s e t P r o g r e s s B a r ( SolverOps : : O f f ) . d r i v . . . // i n t e g r a t e t h e FID i f ( d r i v . // s e t t h e new p u l s e s e t myparspulse . FID ( ) ) .A.
5 max 0 . 5 #f i d p i e c e s npts 512 tf 8 #t h e p u l s e b i t s #a n g l e . 5 .A.000125 npts2D 6 4 #b a s i c s p i n p a r a m e t e r s s p i n t y p e 1 1H s p i n t y p e 2 3 1P d e t e c t 3 1P Bo 4 . 5 . −90 .80000 #t h e t 2 d e l a y delay 0.5 . 5 T1 2 0 #f o r t h e Bulk S u s e p t i b i l i t y D 1 # f i l e o u t p u t names f o r t h e d a t a f i d o u t data . 0 . 2 min −0.5 .9 #r e l a x a t i o n params f o r each s p i n T2 1 0 . 0 . −0. 1 0 4 #o f f s e t s f o r each s p i n o f f s e t 1 −722 o f f s e t 2 −4. phase . 0 0 2 T1 1 0 T2 2 0 . a m p l i t u d e pulse1 90 .80000 p u l s e 2 90 . BLOCHLIB CONFIGURATIONS AND SOURCES 273 Input Conﬁg File #parameter f i l e f o r l o o p i n g t h r o u g h # s e v e r a l BulkSus p a r a m e t e r s dim 1 .3.90 . 1 . 7 temperature 300 moles . −0.
C++ source #include ” b l o c h l i b . BPoptions : : P a r t i c l e  BPoptions : : H i g h F i e l d . Both the C++ source using the BlochLib framework and the conﬁguration ﬁle is given. . typedef ListBlochParams < TheGrid . f l u s h ( ) . } // some t y p e d e f s t o make t y p i n g e a s i e r typedef XYZcylinder TheShape . double > MyPars . typedef GradientGrid<TheGridS > TheGrid .12. Results of this simulation can be seen in Figure 5. BLOCHLIB CONFIGURATIONS AND SOURCES 274 A. typedef XYZshape<TheShape > TheGridS . It demonstrates the nonlinear properties of including both Radiation Damping and the Modulated Demagnetizing ﬁeld resulting in a resurrection of a completely crushed magnetization. t i m e r stopwatch . h” /∗ t h i s i s an a t t e m p t t o i m i t a t e t h e r e s u l t from YY Lin i n 6 OCTOBER 2 0 0 0 VOL 2 9 0 SCIENCE The s i m u l a t e d e f f e c t i v e p u l s e s e q u e n c e RF −−−90x−−−−FID Grad −−−−−Gzt−−−−−− where t h e g r a d i e n t c o m p l e t e c r u s h e s t h e m a g n e t i z a t i o n w i t h some s m a l l e p s e r r o r from t h e i d e a ∗/ using namespace BlochLib . using namespace s t d . } void I n f o ( s t d : : s t r i n g mess ) { s t d : : cout<<mess . s t d : : c o u t . void printTime ( int nrounds =1){ s t d : : c o u t << s t d : : endl << ”Time taken : ” << ( stopwatch ( ) / nrounds ) << ” s e c o n d s \n” .A.3.5 Example Classical Simulation of the Modulated Demagnetizing Field This simulation is a replication of the simulation performed by Y.Y Lin in Sci ence [87].3.
. coord <> maxs ( p s e t . I n f o ( ” C r e a t i n g i n i t a l shape . getParamD ( ” pulseamp ” ) . . . getParamCoordD ( ”gmin” ) ) . f a l s e ) . getParamCoordD ( ” smin ” ) ) . . I n f o ( ” C r e a t i n g g r i d . . getParamD ( ” g r a d t im e 1 ” ) . NoPulse . getParamCoordD ( ”gmax” ) ) . getParamS ( ” f i d o u t ” ) . int cv=p s e t . // g r a d i e n t p a r s double g ra dt i m e 1=p s e t . f n ) . Parameters p s e t ( f n ) . Grid<UniformGrid > gg ( mins . getParamI ( ” l y p s ” . double pang1=p s e t . BLOCHLIB CONFIGURATIONS AND SOURCES 275 // Extra i n e r a c t i o n s typedef I n t e r a c t i o n s <O f f s e t <MyPars>. t e s t e r ) . getParamS ( ”magout” ) . getParamI ( ” n p t s ” ) . s t d : : s t r i n g l y p f i l e =p s e t . f a l s e ) . M y I n t e r a c t i o n s > NoPulseBloch . \ n” ) . int main ( int argc . \ n” ) . Pulse . s t d : : s t r i n g f o u t=p s e t . getParamD ( ” p u l s e a n g l e 1 ” ) . s t d : : s t r i n g magout=p s e t . getParamCoordI ( ”dim” ) ) . getParamS ( ” t r a j e c t o r i e s ” . double amp=p s e t . \ n” ) . M y I n t e r a c t i o n s > P u l s e B l o c h . char ∗ argv [ ] ) { // Get a l l t h e v a r i o u s p a r a m e t e r s std : : s t r i n g fn . getParamS ( ” l y p o u t ” . coord <> mins ( p s e t . Relax <>.3. s t d : : s t r i n g dataou=p s e t . smaxs ) . getParamD ( ” t f ” ) . 1 . ” ” . double t f=p s e t . /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ // Grids coord<int > dims ( p s e t . TheShape t e s t e r ( smins . ” Enter f i l e t o p a r s e : ” . typedef Bloch < MyPars . ” l y p s ” ) . dims ) . I n f o ( ” C r e a t i n g t o t a l shape−g r i d . RadDamp . ModulatedDemagField > M y I n t e r a c t i o n s . // t y p e d e f s f o r Bloch parameter s e t s typedef Bloch < MyPars . argv . . coord <> smaxs ( p s e t . f a l s e . . ” ” . ” ” . q u e r y p a r a m e t e r ( argc . TheGridS g r i d s ( gg . maxs . int n s t e p s=p s e t .A. . coord <> smins ( p s e t . //dump t h e g r i d t o a f i l e . . getParamCoordD ( ”smax” ) ) .
. . . // ( s p i n . \ n” ) . getParamS ( ” s p i n t y p e ” ) . /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ // s e t up Parameter l i s t s int nsp= j j . 0 . . . double moles=p s e t . . I n f o ( ” C r e a t i n g Gr adie nt map g r i d s . s t d : : s t r i n g s p i n t y p e=p s e t . j <nsp . . Bo ( inBo ) . . I n f o ( ” C r e a t i n g r e a l p u l s e l i s t s . coord <> grad=p s e t . o f f s e t ) P u l s e PP1( s p i n t y p e . j j ) . s i z e ( ) . . } mypars .A. nsp=mypars . I n f o ( ” I n i t i a l i z i n g Time t r a i n f o r f i r s t P u l s e . . . .3. s i z e ( ) . // c r e a t e t h e g r a d i e n t g r i d s . goo<<g r i d s <<s t d : : e n d l . getParamCoordD ( ” grad ” ) . . t p u l s e . timeForAngle ( pang1 ∗ Pi / 1 8 0 . BLOCHLIB CONFIGURATIONS AND SOURCES 276 s t d : : o f s t r e a m goo ( ” g r i d ” ) . getParamD ( ” t e m p e r a t u r e ” ) . j ++){ mypars ( j )= s p i n t y p e . a m p l i t u d e . . getParamD ( ”Bo” ) . j j . ) . getParamC ( ” i d e a l ” ) . . p r i n t ( s t d : : c o u t ) . \ n” ) . getParamD ( ” moles ” ) . t e m p e r a t u r e ( inTemp ) . \ n” ) . TimeTrain<UniformTimeEngine > P1 ( 0 .G( grad ) . MyPars mypars ( j j . 1 0 0 ) . amp . I n f o ( ” C r e a t i n g e n t i r e s p i n parameter l i s t f o r ” +i t o s t ( nsp)+ ” s p i n s . . /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ // time t r a i n double t c t =0. . . \ n” ) . mypars ( j ) . /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ //The p u l s e l i s t f o r a r e a l p u l s e on p r o t o n s . calcTotalMo ( ) . p r i n t ( s t d : : c o u t ) . TheGrid j j ( g r i d s ) . I n f o ( ” s e t t i n g s p i n parameter o f f s e t s . f or ( int j =0. . \ n” ) . s i z e ( ) . double inBo=p s e t . mypars . double inTemp=p s e t . PP1 . 1 0 . s t d : : s t r i n g d e t s p=s p i n t y p e . phase . s p i n t y p e ) . double t p u l s e=PP1 . mypars ( j ) . char i d e a l=p s e t . ” 1H” .
\ n” ) . .G( ) ) . s t d : : cout<<”TOTAL mag i n i t i a l c o n d i t i o n : ”<<sum (tm)<<s t d : : e n d l . I n f o ( ” s e t t i n g I n t e r a c t i o n s . e . 1 0 0 ) . I n f o ( ” I n i t i a l i z i n g Time t r a i n f o r F i r s t Gra d ie n t P u l s e . getParamD ( ”T2” ) . O f f s e t <MyPars> myOffs ( mypars . } /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ // i n t e r a c t i o n s double t 2 s=p s e t . //demag f i e l d ’ time c o n s t a n t ’ // b e c a u s e we a r e i n t h e ’ p a r t i c l e ’ r e p // we need t o c a l c u l a t e t h e r e a l Mo s e p a r a t e l y double mo=mypars [ 0 ] . . . \ n” ) . getParamD ( ” o f f s e t ” ) ∗ PI2 . . ( ! t 1 s ) ? 0 . gamma ( ) / PI2 ) / kb/inTemp ) ∗No∗ moles ∗1 e6 / 2 . 5 ) . 0 : 1 . s t d : : cout<<DipDip<<” Td : ”<<DipDip . double demag =1. Vector<coord <> > tm=me . setBeginTime ( 0 ) . getParamD ( ”T1” ) . ( ! t 2 s ) ? 0 . 0 . ModulatedDemagField DipDip ( demag . .3. getParamD ( ” demagOff ” . f a l s e . I n f o ( ” I n i t i a l i z i n g Time t r a i n f o r FID . t c t+=g r ad t im e 1 . gamma ( ) ∗ hbar ∗ tanh ( hbar ∗ PI ∗ ( inBo ∗ mypars [ 0 ] . o f f ( ) . P u l s e B l o c h myparspulse ( mypars .A. TimeTrain<UniformTimeEngine > G1( t c t . F1 . ” ” . TimeTrain<UniformTimeEngine > F1 ( t c t . . n s t e p s . i f ( i d e a l==’ y ’ ) { F1 . o f f s e t ) . BLOCHLIB CONFIGURATIONS AND SOURCES 277 t c t+=t p u l s e . \ n” ) . . . t c t+gradtime1 . Relax <> myRels ( mypars . . RdRun . . demag=p s e t . /∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗/ // This i s t h e ‘ Bloch ’ t o perform a p u l s e I n f o ( ” I n i t i a l i z i n g t o t a l parameter l i s t with a p u l s e . NoPulseBloch me . . double o f f s e t=p s e t . setEndTime ( t f ) . DipDip ) . getParamD ( ”raddamp” ) . PP1 . td ( ) <<” a x i s : ”<<DipDip . FASTER) I n f o ( ” I n i t i a l i z i n g t o t a l parameter l i s t f o r FID c o l l e c t i o n . me=myparspulse . 0 / t 2 s . double t 1 s=p s e t . RadDamp RdRun( t r ) . \ n” ) . double t r=p s e t . . myRels . 0 . s t d : : cout<<” T o t a l M a n g e t i z a t i o n : ”<<mo<<s t d : : e n d l . has no p u s l e s . 0 / t 1 s ) . . // This i s t h e Bloch s o l v e r t o C o l l e c t t h e FID // ( i . 0 ) . \ n” ) . MyInts ) . . j j . 5 0 . d i r e c t i o n ()<< s t d : : e n d l . M y I n t e r a c t i o n s MyInts ( myOffs . gamma ( ) ) .0/(mo∗permVac∗ mypars [ 0 ] . . currentMag ( ) . t f+t c t . . 0 : 1 . i f ( demag ! = 0 ) DipDip . . .
z ( ) = 0 . setRawOut ( dataou . c u r p o s ( ) ] . . z ( ) / lmax ∗ PI2)+emp . . s e t C o l l e c t i o n P o l i c y ( SolverOps : : F i n a l P o i n t ) . setRawOut ( dataou . s t d : : i o s : : app  s t d : : i o s : : out ) . while ( myit ) { tp=myit . \ n” ) . s e t I n i t i a l C o n d i t i o n ( drivP . getParamD ( ” e p s ” . z ()− smins . . drivP . double lmax=smaxs . ” ” . . ++myit . s e t W r i t e P o l i c y ( SolverOps : : Continous ) . } // i n t e g r a t e t h e g r a d i e n t p u l s e I n f o ( ” \ n I n t e g r a t i n g t h e Gra die n t P u l s e . x()= s i n ( tp . . \ n” ) .A. // t u r n o f f g r a d i e n t I n f o ( ” I n t e g r a t i n g f i r s t P u l s e . s o l v e ( P1 ) ) { I n f o ( ” ERROR ! ! . BLOCHLIB CONFIGURATIONS AND SOURCES 278 // t h e ’ e r r o r ’ i n t h e h e l i x double emp=p s e t . z ( ) . Point ( ) . // s e t t h e c i r c u l a r i n i t i a l c o n d i t i o n . l a s t P o i n t ( ) ) . c o u l d not i n t e g r a t e p u l s e P1 . drivD . return −1. } else { drivP . s e t W r i t e P o l i c y ( SolverOps : : Continous ) . drivD . coord <> tp . B l o c h S o l v e r <NoPulseBloch > drivD (me . } } stopwatch . . // o u t p u t t r a j e c t o r y d a t a i f wanted i f ( dataou !=”” ) { drivD . tm [ myit . tm [ myit . . a s i n g l e h e l i x i f ( i d e a l==’ y ’ ) { MyPars : : i t e r a t o r myit ( mypars ) .3. \ n” ) . . c u r p o s ( ) ] . f a l s e . 1 e −3). c u r p o s ( ) ] . 0 . . i f ( ! drivP . . } drivP . // i n t e g r a t e p u l s e and g r a d i e n t p u l s e // o n l y i f NOT i d e a l e x p e r i m e n t i f ( i d e a l==’ n ’ ) { // o u t p u t t r a j e c t o r y d a t a i f wanted i f ( dataou !=”” ) { drivP . r e s e t ( ) . // i n t e g r a t e t h e f i r s t p u l s e myOffs . o f f ( ) . tm [ myit . s e t W r i t e P o l i c y ( SolverOps : : Hold ) . z ( ) / lmax ∗ PI2 ) . . y()= c o s ( tp . tm ) . tm ) . s t d : : i o s : : out ) . // t h e two main s o l v e r s B l o c h S o l v e r <P u l s e B l o c h > drivP ( myparspulse . .
s o l v e (G1 ) ) { I n f o ( ” ERROR ! ! . s e t W r i t e P o l i c y ( SolverOps : : Hold ) . i f ( dataou !=”” ) { drivD . o f f ( ) . \ n” ) . } printTime ( ) . setRawOut ( dataou . drivD . s e t L y p D a t a F i l e ( l y p f i l e ) . c u r V a r i a t i o n a l ( ) ) . drivD . i f ( i d e a l==’ y ’ ) drivD . // o u t p u t t r a j e c t o r y d a t a i f wanted drivD . return −1. // r i n g a b e l l when we a r e done s t d : : cout<<” \a”<<s t d : : e n d l . I n f o ( ” \ n I n t e g r a t i n g f o r FID . . w ri t e S p ec t r um ( f o u t ) . } i f ( gradtime1 >0){ myOffs . . } // s o l v e t h e FID and w r i t e i t t o a f i l e i f ( drivD . . } Input Conﬁg File #parameter f i l e f o r 1 p u l s e − 1 Grad Z s e q u e n c e s #g r i d u n i t s i n cm dim 1 . s e t V a r i a t i o n a l I n i t C o n d (me .A. . 1 . . s t d : : i o s : : out ) . // t u r n on g r a d i e n t i f ( ! drivD . s e t C o l l e c t i o n P o l i c y ( SolverOps : : MagAndFID ) .3. } } } // i n t e g r a t e FID i f ( cv ) { me . drivD . BLOCHLIB CONFIGURATIONS AND SOURCES 279 } else { drivD . . s t d : : i o s : : app  s t d : : i o s : : out ) . s e t L y a p u n o v P o l i c y ( SolverOps : : LypContinous ) . } else { drivD . on ( ) . setRawOut ( dataou . c a l c V a r i a t i o n a l ( ) . e l s e drivD . \ n” ) . writeMag ( magout ) . s o l v e ( F1 ) ) { drivD . s e t W r i t e P o l i c y ( SolverOps : : Continous ) . c o u l d not i n t e g r a t e G1 . 1 0 0 . s e t W r i t e P o l i c y ( SolverOps : : Hold ) . drivD . } myOffs . .
. 0 0 4 6 9 3 #c y l i n d e r shape min and max smin 0 . 0 0 4 6 9 3 #f i d p i e c e s npts 512 tf 2 #t h e p u l s e b i t s pulseangle1 90 pulseamp 8 0 0 0 0 #b a s i c s p i n p a r a m e t e s r Bo 1 4 . 0 . − 0. 6 . − 0. 0 2 . 0 2 . 2 8 . 1 0 4 5 #t h e e x t r a i n t e r a c t i o n s p a r t s raddamp 0 . 0 2 . 0 0 5 #ouput d a t a f i l e names f i d o u t data magout mag trajectories traj . − 0 . 0 0 4 6 9 3 smax . 02 . 0 0 3 . 0 1 ## #g r a d i e n t t h i n g s #c h o o s e ’ r e a l g r a d i e n t ’ ( n ) or i d e a l i n i t i a l c o n d i t i o n ( y ) #i f i d e a l m a g n e t i z a t i o n w i l l be s p r e a d e v e n l y #around a c i r c l e i n t h e xy p l a n e ideal y #non−i d e a l b i t s ( grad u n i t s i n Gauss /cm) grad 0 . 0 . 1 g r a dt i m e 1 0 . 004 69 3 gmax 0 . 0 . 0 .A. 1 temperature 300 offset 0 T2 0 T1 0 s p i n t y p e 1H #e r r o r i n i d e a l g r a d i e n t p u l s e # a l o n g t h e x−a x i s e p s 1 e−3 #t u r n on ( 0 ) or o f f ( 1 ) t h e d e m a g n e t i z i n g f i e l d demagOff 0 #95% w a t e r ( 2 p r o t o n s a pop ) moles 0 .3. BLOCHLIB CONFIGURATIONS AND SOURCES 280 gmin − 0.