@@ -69,6 +69,106 @@ template gaugeFlow*(g: array|seq, eps: float, measure: untyped) =
6969 gaugeFlow (g, 0 , eps):
7070 measure
7171
72+ import ../ algorithms/ rk
73+
74+ type
75+ GaugeFlowRKOp * [G] = object of RK2NOp [G,G]
76+ f: G
77+ GaugeFlowRKAdaptiveOp * [G] = object of RK2NAdaptiveOp [G,G]
78+ f: G
79+
80+ proc mkGaugeFlowRKOp * [G](g: G): GaugeFlowRKOp [G] =
81+ # # the returned op will hold a reference to the input g
82+ var op = GaugeFlowRKOp [G](
83+ y: g,
84+ delta: g[0 ].l.newGauge,
85+ f: g[0 ].l.newGauge)
86+ proc adv (base: var type (g), d: var type (g), a: float , b: float ) =
87+ const nc = op.y[0 ][0 ].nrows.float
88+ op.f.gaugeForce (base)
89+ let u = cast [ptr cArray [type (base [0 ])]](unsafeAddr (base[0 ]))
90+ let f = cast [ptr cArray [type (d [0 ])]](unsafeAddr (d[0 ]))
91+ let ff = cast [ptr cArray [type (d [0 ])]](unsafeAddr (op.f[0 ]))
92+ let n = base.len
93+ if a == 0.0 :
94+ threads:
95+ for mu in 0 ..< n:
96+ for e in u[mu]:
97+ var v {.noinit .}:type (load1 (u[0 ][0 ]))
98+ v := (- b * nc) * ff[mu][e]
99+ let t = exp (v) * u[mu][e]
100+ f[mu][e] := v
101+ u[mu][e] := t
102+ else :
103+ threads:
104+ for mu in 0 ..< n:
105+ for e in u[mu]:
106+ var v {.noinit .}:type (load1 (u[0 ][0 ]))
107+ v := a * f[mu][e] + (- b * nc) * ff[mu][e]
108+ let t = exp (v) * u[mu][e]
109+ f[mu][e] := v
110+ u[mu][e] := t
111+ op.advance = adv
112+ op
113+
114+ proc mkGaugeFlowRKAdaptiveOp * [G](g: G): GaugeFlowRKAdaptiveOp [G] =
115+ # # the returned op will hold a reference to the input g
116+ mixin simdMax
117+ var rk = g.mkGaugeFlowRKOp
118+ var op = GaugeFlowRKAdaptiveOp [G](
119+ y: rk.y,
120+ delta: rk.delta,
121+ f: rk.f,
122+ advance: rk.advance,
123+ y0: g[0 ].l.newGauge,
124+ )
125+ proc assignG (dst: var type (g), src: type (g)) =
126+ let u = cast [ptr cArray [type (dst [0 ])]](unsafeAddr (dst[0 ]))
127+ let s = cast [ptr cArray [type (src [0 ])]](unsafeAddr (src[0 ]))
128+ let n = dst.len
129+ threads:
130+ for mu in 0 ..< n:
131+ for e in u[mu]:
132+ u[mu][e] := s[mu][e]
133+ op.assign = assignG
134+ # op.errDelta = proc(d: type(g)): float =
135+ # var r = 0.0
136+ # threads:
137+ # var p2t = 0.0
138+ # for i in 0..<d.len:
139+ # p2t += d[i].norm2
140+ # threadMaster: r = p2t
141+ # const nc = d[0][0].nrows
142+ # sqrt(r / float((nc*nc-1)*d.len*d[0].l.physVol))
143+ op.errDelta = proc (d: type (g)): float =
144+ var res = 0.0
145+ threads:
146+ var r = 0.0
147+ for i in 0 ..< d.len:
148+ for s in d[i]:
149+ let e = d[i][s].norm2.simdMax
150+ if r< e: r = e
151+ threadRankMax r
152+ threadMaster: res = r
153+ const nc = d[0 ][0 ].nrows
154+ sqrt (res/ float (nc* nc- 1 ))
155+ op
156+
157+ proc gaugeFlowRK * [G](g: var G, steps: int , eps: float , coeffs: auto , measure: proc (wflowT: float ) {.closure .} = nil ) =
158+ # # Wilson flow using encapsulated 2N RK operator with arbitrary RK2N coefficients.
159+ tic (" flowProcRK" )
160+ var op = g.mkGaugeFlowRKOp
161+ rk2n (op, coeffs, steps, eps, measureCb= measure)
162+ toc (" endRK" )
163+
164+ proc gaugeFlowRKAdaptive * [G](g: var G, t: float , h0: float , coeffs: auto , tol: float , safety: float = 0.95 , maxSteps:int = 100000 , controllerExp: float = 1.0 / 3.0 , measure: proc (wflowT: float ) {.closure .} = nil ): RKAdaptiveStats =
165+ # # Adaptive Wilson flow to total time t using encapsulated 2N RK operator and arbitrary coefficients.
166+ # # See comments under rk2nAdaptive for more details.
167+ tic (" flowProcAdaptive" )
168+ var op = g.mkGaugeFlowRKAdaptiveOp
169+ result = rk2nAdaptive (op, coeffs, t, h0, deltaTol= tol, safety= safety, maxSteps= maxSteps, controllerExp= controllerExp, measureCb= measure)
170+ toc (" endAdaptive" )
171+
72172when isMainModule :
73173 import qex, gauge, physics/ qcdTypes
74174 import os, sequtils
@@ -96,8 +196,97 @@ when isMainModule:
96196 quit (- 1 )
97197 g.printPlaq
98198
199+ # Compare plaquettes
200+ proc relDiff (a, b: auto ): float =
201+ let pa = a.plaq
202+ let pb = b.plaq
203+ zip (pa, pb).foldl (a + abs (b[0 ] - b[1 ]), 0.0 ) / max (1 e-30 , pa.sum)
204+
205+ var gRef = g.newGauge
206+ gRef.gaugeFlowRK (120 , 0.0005 , RK64_2N , (proc (t:float ) =
207+ var nt {.global .} = 0.01
208+ if t < (1.0 - 1.0 e-12 )* nt:
209+ return
210+ nt += 0.01
211+ echo " WFLOW RK64 ref " , t
212+ gRef.printPlaq))
213+
214+ template runFlow (coeff:auto ) =
215+ echo " gauge flow " ,astToStr (coeff)
216+ var d = newseq [float ](0 )
217+ for (steps, eps) in [(6 ,0.01 ), (12 ,0.005 )]:
218+ var gRK = g.newGauge
219+ gRK.gaugeFlowRK (steps, eps, coeff)
220+ d.add relDiff (gRef, gRK)
221+ echo " steps = " ,steps," eps = " ,eps," rel plaq diff = " ,d[^ 1 ]
222+ echo " error scaling: " ,(ln (d[0 ]/ d[1 ])/ ln (2.0 ))," per stage coeff: " ,d[^ 1 ]/ pow (0.005 / coeff.beta.len,ln (d[0 ]/ d[1 ])/ ln (2.0 ))
223+ runFlow (RK3W6_2N )
224+ runFlow (RK3W7_2N )
225+ runFlow (RK43_1_2N )
226+ runFlow (RK43_2_2N )
227+ runFlow (RK43_3_2N )
228+ runFlow (RK43_4_2N )
229+ runFlow (RK53_1_2N )
230+ runFlow (RK53_2_2N )
231+ runFlow (RK53_3_2N )
232+ runFlow (RK53_4_2N )
233+ runFlow (RK4CK_2N )
234+ runFlow (RK4BBB_2N )
235+ runFlow (RK64_2N )
236+
237+ template runFlowAd (tol) =
238+ block :
239+ var gRK = g.newGauge
240+ let res = gRK.gaugeFlowRKAdaptive (6 * 0.01 , 0.01 , RK53_4_2N , tol)
241+ echo " gauge flow adaptive RK53_4 rel plaq diff = " ,relDiff (gRef, gRK), " steps=" , res.steps, " acc=" , res.accepts, " rej=" , res.rejects, " h∈[" , res.minH, " , " , res.maxH, " ]"
242+ runFlowAd (1 e-5 )
243+ runFlowAd (1 e-7 )
244+ runFlowAd (1 e-9 )
245+ runFlowAd (1 e-11 )
246+
247+ # Forward/backward gauge flow tests (fixed-step)
248+ template runFlowRev (coeff:auto ) =
249+ echo " Fwd/Back Fixed " ,astToStr (coeff)
250+ var d = newseq [float ](0 )
251+ for (steps, eps) in [(10 ,0.01 ), (20 ,0.005 )]:
252+ var gr = g.newGauge
253+ var op = gr.mkGaugeFlowRKOp
254+ op.rk2n (coeff, steps, eps)
255+ op.rk2n (coeff, steps, - eps)
256+ d.add relDiff (g, gr)
257+ echo " steps = " ,steps," eps = " ,eps," rel plaq diff = " ,d[^ 1 ]
258+ echo " error scaling: " ,(ln (d[0 ]/ d[1 ])/ ln (2.0 ))," per stage coeff: " ,d[^ 1 ]/ pow (0.005 / coeff.beta.len,ln (d[0 ]/ d[1 ])/ ln (2.0 ))
259+ runFlowRev (RK3W6_2N )
260+ runFlowRev (RK3W7_2N )
261+ runFlowRev (RK43_1_2N )
262+ runFlowRev (RK43_2_2N )
263+ runFlowRev (RK43_3_2N )
264+ runFlowRev (RK43_4_2N )
265+ runFlowRev (RK53_1_2N )
266+ runFlowRev (RK53_2_2N )
267+ runFlowRev (RK53_3_2N )
268+ runFlowRev (RK53_4_2N )
269+ runFlowRev (RK4CK_2N )
270+ runFlowRev (RK4BBB_2N )
271+ runFlowRev (RK64_2N )
272+
273+ template runFlowAdRev (tol) =
274+ block :
275+ var gRK = g.newGauge
276+ var op = gRK.mkGaugeFlowRKAdaptiveOp
277+ let resF = rk2nAdaptive (op, RK53_4_2N , 0.1 , 0.01 , tol)
278+ let resB = rk2nAdaptive (op, RK53_4_2N , - 0.1 , 0.01 , tol)
279+ echo " Fwd/Back adaptive RK53_4 rel plaq diff = " ,relDiff (g, gRK)
280+ echo " Fwd steps=" , resF.steps, " acc=" , resF.accepts, " rej=" , resF.rejects, " h∈[" , resF.minH, " , " , resF.maxH, " ]"
281+ echo " Bwd steps=" , resB.steps, " acc=" , resB.accepts, " rej=" , resB.rejects, " h∈[" , resB.minH, " , " , resB.maxH, " ]"
282+ runFlowAdRev (1 e-5 )
283+ runFlowAdRev (1 e-7 )
284+ runFlowAdRev (1 e-9 )
285+ runFlowAdRev (1 e-11 )
286+
287+ # Default flow (original 3-stage wrapper)
99288 g.gaugeFlow (6 , 0.01 ):
100- echo " WFLOW " , wflowT
289+ echo " WFLOW default " , wflowT
101290 g.printPlaq
102291
103292 when g[0 ][0 ].nrows == 1 or g[0 ][0 ].nrows == 3 :
0 commit comments