Skip to content

Commit 7f7fd3b

Browse files
jiegilletleios
authored andcommitted
Split-op in Haskell (#357)
* Added split-op in Haskell. Added energy calculations. Added a gnuplotscript to visualize wavefunctions. * Changed contributors? Not sure * Included code to chapter * Update all julia code to V1.0 (#389) * adding changes to update all julia code to V1.0 * adding more points to graham.jl * Verlet integration in Fortran90 (#364) * initial commit * Programming done * Minor style changes. Merge ready * hopefully fix PR * minor edits for code readability * indexed main procedure * fix typo * Euclidean's algorithm in Fortran90 (#369) * implementation and documentary * minor code clean up * fixing git again... * Delete monte_carlo_pi.f90 again fortan file automagically appears. * code style changes * code style * indexed main procedure * Cleaned up code * Refactored .*, .+ and normalization operators * Got rid of {-# LANGUAGE FlexibleContexts #-} by giving explicit signature to normalize * Small cosmetic changes * Undid change to CONTTRIBUTORS.md * Moved gnuplot script to own folder and updated instructions to use it * Removed old plot script * Changed import lines
1 parent d980ecf commit 7f7fd3b

File tree

5 files changed

+153
-1
lines changed

5 files changed

+153
-1
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Data.Array.CArray
2+
import Data.Complex
3+
import Math.FFT (dft, idft) -- Binding to fftw
4+
5+
type Vector = CArray Int (Complex Double)
6+
7+
calculateEnergy :: Double -> Vector -> Vector -> Vector -> Double
8+
calculateEnergy dx kin pot wfc = (* dx) . sum . map realPart $ elems total
9+
where
10+
total = liftArray2 (+) kineticE potentialE
11+
potentialE = wfcConj .* pot .* wfc
12+
kineticE = wfcConj .* idft (kin .* dft wfc)
13+
wfcConj = liftArray conjugate wfc
14+
a .* b = liftArray2 (*) a b

contents/quantum_systems/quantum_systems.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ This ultimately looks like this:
227227
{% method %}
228228
{% sample lang="jl" %}
229229
[import, lang:"julia"](code/julia/energy.jl)
230+
{% sample lang="hs" %}
231+
[import, lang:"haskell"](code/haskell/Energy.hs)
230232
{% sample lang="c" %}
231233
[import:28-, lang:"c_cpp"](code/c/energy.c)
232234
{% sample lang="py" %}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# use like: gnuplot -e "folder='/path/to/data/directory'" plot_output.plt
2+
3+
set terminal gif animate delay 10
4+
set output folder.'/wavefunction.gif'
5+
6+
set key off
7+
8+
set xrange [-10:10]
9+
set yrange [0:1]
10+
set y2range [0:50]
11+
12+
set ytics nomirror autofreq tc lt 1
13+
set ylabel 'Psi(x)' tc lt 1
14+
15+
set y2tics nomirror autofreq tc lt 2
16+
set y2label 'V(x)' tc lt 2
17+
18+
list = system('ls '.folder.'/output*.dat')
19+
20+
do for [file in list] {
21+
plot file u 1:2 smooth csplines, \
22+
file u 1:3 smooth csplines axes x1y2
23+
}
24+
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import Data.Array.CArray
2+
import Data.Complex
3+
import Data.List (intercalate, transpose)
4+
import Math.FFT (dft, idft)
5+
6+
type Vector = CArray Int (Complex Double)
7+
8+
(.*), (.+) :: Vector -> Vector -> Vector
9+
a .* b = liftArray2 (*) a b
10+
a .+ b = liftArray2 (+) a b
11+
12+
normalize :: Double -> Vector -> Vector
13+
normalize dx v =
14+
let factor = 1 / sqrt dx / norm2 v :+ 0
15+
in liftArray (factor *) v
16+
17+
data Parameters = Parameters
18+
{ xmax :: Double
19+
, res :: Int
20+
, dt :: Double
21+
, timesteps :: Int
22+
, dx :: Double
23+
, x :: Vector
24+
, dk :: Double
25+
, ks :: Vector
26+
, imTime :: Bool
27+
}
28+
29+
defaultParameters :: Parameters
30+
defaultParameters = makeParameters 10 512 0.01 1000 True
31+
32+
makeParameters :: Double -> Int -> Double -> Int -> Bool -> Parameters
33+
makeParameters xmax res dt timesteps imTime =
34+
let fi = fromIntegral
35+
rng = (0, res - 1)
36+
ks = [0 .. div res 2 - 1] ++ [-div res 2 .. -1]
37+
in Parameters
38+
xmax
39+
res
40+
dt
41+
timesteps
42+
(2 * xmax / fi res)
43+
(listArray rng $
44+
map (\n -> xmax * (-1 + 2 * fi n / fi res) :+ 0) [1 .. res])
45+
(pi / xmax)
46+
(listArray rng $ map ((:+ 0) . (pi / xmax *) . fi) ks)
47+
imTime
48+
49+
data Operators = Operators
50+
{ v :: Vector
51+
, rStep :: Vector
52+
, kStep :: Vector
53+
, wfc :: Vector
54+
}
55+
56+
makeOperators :: Parameters -> Complex Double -> Complex Double -> Operators
57+
makeOperators param v0 wfc0 =
58+
let rng = (0, res param - 1)
59+
time
60+
| imTime param = dt param :+ 0
61+
| otherwise = 0 :+ dt param
62+
v = liftArray (\x -> 0.5 * (x - v0) ^ 2) (x param)
63+
rStep = liftArray (\x -> exp (-0.5 * time * x)) v
64+
kStep = liftArray (\k -> exp (-0.5 * time * k ^ 2)) (ks param)
65+
wfc = liftArray (\x -> exp (-(x - wfc0) ^ 2 / 2)) (x param)
66+
in Operators v rStep kStep (normalize (dx param) wfc)
67+
68+
evolve :: Parameters -> Operators -> [Operators]
69+
evolve param op@(Operators _ rStep kStep _) = iterate splitop op
70+
where
71+
splitop op = op {wfc = wfc' op}
72+
wfc' = norm . (rStep .*) . idft . (kStep .*) . dft . (rStep .*) . wfc
73+
norm = if imTime param then normalize (dx param) else id
74+
75+
calculateEnergy :: Parameters -> Operators -> Double
76+
calculateEnergy param ops = (* dx param) . sum . map realPart $ elems totalE
77+
where
78+
totalE = potentialE .+ kineticE
79+
potentialE = wfcConj .* v ops .* wfc ops
80+
kineticOp = liftArray ((/ 2) . (^ 2)) (ks param)
81+
kineticE = wfcConj .* idft (kineticOp .* dft (wfc ops))
82+
wfcConj = liftArray conjugate $ wfc ops
83+
84+
-- Use gnuplot to make an animated GIF using ../gnuplot/plot_output.plt
85+
-- $ gnuplot -e "folder='../haskell'" plot_output.plt
86+
printEvolution :: Parameters -> [Operators] -> IO ()
87+
printEvolution param =
88+
mapM_ (export . (format <$>)) . zip [0 ..] . take 100 . skip
89+
where
90+
skip (x:xs) = x : skip (drop (div (timesteps param) 100 - 1) xs)
91+
format (Operators v _ _ wfc) =
92+
let density = liftArray ((^ 2) . abs) wfc
93+
values = map (map (show . realPart) . elems) [x param, density, v]
94+
in intercalate "\n" $ map (intercalate "\t") $ transpose values
95+
export (i, f) = writeFile ("output" ++ pad (show i) ++ ".dat") f
96+
pad n = replicate (5 - length n) '0' ++ n
97+
98+
main :: IO ()
99+
main = do
100+
let p = defaultParameters
101+
o = makeOperators p 0 4
102+
evol = evolve p o
103+
print $ calculateEnergy p (evol !! timesteps p)
104+
printEvolution p evol

contents/split-operator_method/split-operator_method.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Here's a flowchart of what we are looking for every timestep:
5555

5656

5757
For the most part, that's it:
58-
1. Multiply the wavefunction in real space with the real-space operator.
58+
1. Multiply the wavefunction in real space with the real-space operator.
5959
2. Flip to momentum space with a Fourier transform.
6060
3. Multiply the momentum-space wavefuntion by the momentum-space operator.
6161
4. Flip to position space with an inverse Fourier transform.
@@ -104,6 +104,8 @@ Regardless, we first need to set all the initial parameters, including the initi
104104
[import:51-72, lang:"c_cpp"](code/c/split_op.c)
105105
{% sample lang="py" %}
106106
[import:11-30, lang:"python"](code/python/split_op.py)
107+
{% sample lang="hs" %}
108+
[import:17-47, lang:"haskell"](code/haskell/splitOp.hs)
107109
{% endmethod %}
108110

109111
As a note, when we generate our grid in momentum space `k`, we need to split the grid into two lines, one that is going from `0` to `-kmax` and is then discontinuous and goes from `kmax` to `0`.
@@ -121,6 +123,8 @@ Afterwards, we turn them into operators:
121123
[import:74-95, lang:"c_cpp"](code/c/split_op.c)
122124
{% sample lang="py" %}
123125
[import:33-54, lang:"python"](code/python/split_op.py)
126+
{% sample lang="hs" %}
127+
[import:49-66, lang:"haskell"](code/haskell/splitOp.hs)
124128
{% endmethod %}
125129

126130
Here, we use a standard harmonic potential for the atoms to sit in and a gaussian distribution for an initial guess for the probability distribution.
@@ -138,6 +142,8 @@ The final step is to do the iteration, itself.
138142
[import:97-145, lang:"c_cpp"](code/c/split_op.c)
139143
{% sample lang="py" %}
140144
[import:57-95, lang:"python"](code/python/split_op.py)
145+
{% sample lang="hs" %}
146+
[import:68-73, lang:"haskell"](code/haskell/splitOp.hs)
141147
{% endmethod %}
142148

143149
And that's it.
@@ -161,6 +167,8 @@ Checking to make sure your code can output the correct energy for a harmonic tra
161167
[import, lang:"c_cpp"](code/c/split_op.c)
162168
{% sample lang="py" %}
163169
[import:5-127, lang:"python"](code/python/split_op.py)
170+
{% sample lang="hs" %}
171+
[import, lang:"haskell"](code/haskell/splitOp.hs)
164172
{% endmethod %}
165173

166174
<script>

0 commit comments

Comments
 (0)