44def recursive_gf_cal (energy , mat_l_list , mat_d_list , mat_u_list ,
55 sd , su , sl , s_in = 0 , s_out = 0 , eta = 1e-5 ,
66 need_lesser = False , need_greater = False , need_gr_lc = False ,
7- stacked = False ):
7+ stacked = False , keep_gr_left = True ):
88 """The recursive Green's function algorithm is taken from
99 M. P. Anantram, M. S. Lundstrom and D. E. Nikonov, Proceedings of the IEEE, 96, 1511 - 1550 (2008)
1010 DOI: 10.1109/JPROC.2008.927355
@@ -92,10 +92,13 @@ def recursive_gf_cal(energy, mat_l_list, mat_d_list, mat_u_list,
9292 - mat_d [q + 1 ] - mat_l [q ] @ gr_left [q ] @ mat_u [q ],
9393 eye_bnn ,
9494 )
95+ # mat_d is dead after the forward sweep — backward sweep only reads mat_l/mat_u.
96+ del mat_d
9597
9698 grl = [None ] * (num_of_matrices - 1 )
9799 gru = [None ] * (num_of_matrices - 1 )
98- grd = [g .clone () for g in gr_left ]
100+ grd = [None ] * num_of_matrices
101+ grd [- 1 ] = gr_left [- 1 ].clone ()
99102 g_trans = gr_left [- 1 ].clone ()
100103 gr_lc = [g_trans ] if need_gr_lc else None
101104 for q in range (num_of_matrices - 2 , - 1 , - 1 ):
@@ -106,8 +109,13 @@ def recursive_gf_cal(energy, mat_l_list, mat_d_list, mat_u_list,
106109 g_trans = gU @ g_trans
107110 if need_gr_lc :
108111 gr_lc .append (g_trans )
112+ del gU
109113 if need_gr_lc :
110114 gr_lc .reverse ()
115+ if not need_lesser and not need_greater :
116+ # Stacked path: mat_l/mat_u are 4-D and can't be slice-freed mid-loop;
117+ # they are dead now if no lesser/greater pass will read them.
118+ del mat_l , mat_u
111119
112120 gnd = gnl = gnu = gin_left = None
113121 if need_lesser :
@@ -120,7 +128,8 @@ def recursive_gf_cal(energy, mat_l_list, mat_d_list, mat_u_list,
120128
121129 gnl = [None ] * (num_of_matrices - 1 )
122130 gnu = [None ] * (num_of_matrices - 1 )
123- gnd = [g .clone () for g in gin_left ]
131+ gnd = [None ] * num_of_matrices
132+ gnd [- 1 ] = gin_left [- 1 ].clone ()
124133 for q in range (num_of_matrices - 2 , - 1 , - 1 ):
125134 gLmH = mat_l [q ] @ gr_left [q ].mH # hoisted: used twice
126135 gnl [q ] = grd [q + 1 ] @ mat_l [q ] @ gin_left [q ] + gnd [q + 1 ] @ gLmH # (B10)
@@ -140,7 +149,8 @@ def recursive_gf_cal(energy, mat_l_list, mat_d_list, mat_u_list,
140149
141150 gpl = [None ] * (num_of_matrices - 1 )
142151 gpu = [None ] * (num_of_matrices - 1 )
143- gpd = [g .clone () for g in gip_left ]
152+ gpd = [None ] * num_of_matrices
153+ gpd [- 1 ] = gip_left [- 1 ].clone ()
144154 for q in range (num_of_matrices - 2 , - 1 , - 1 ):
145155 lcgc = mat_l [q ].conj () @ gr_left [q ].conj () # hoisted: used twice
146156 gpl [q ] = grd [q + 1 ] @ mat_l [q ] @ gip_left [q ] + gpd [q + 1 ] @ lcgc
@@ -150,6 +160,8 @@ def recursive_gf_cal(energy, mat_l_list, mat_d_list, mat_u_list,
150160 (gru [q ] @ mat_l [q ] @ gip_left [q ])
151161 gpu [q ] = gpl [q ].mH
152162
163+ if not keep_gr_left :
164+ gr_left = None
153165 return _pack_ans (g_trans , gr_lc , grd , grl , gru , gr_left ,
154166 gnd , gnl , gnu , gin_left ,
155167 gpd , gpl , gpu , gip_left ,
@@ -161,7 +173,9 @@ def recursive_gf_cal(energy, mat_l_list, mat_d_list, mat_u_list,
161173 e_bcast = (energy + 1j * eta ).view (- 1 , 1 , 1 )
162174
163175 for jj in range (len (mat_d_list )):
164- mat_d_list [jj ] = mat_d_list [jj ] - e_bcast * sd [jj ]
176+ # In-place: mat_d_list is a fresh tensor (wrapper's `* 1.` copy on D),
177+ # so we can fuse the energy shift without the e_bcast*sd transient.
178+ mat_d_list [jj ].addcmul_ (sd [jj ], e_bcast , value = - 1 )
165179 for jj in range (len (mat_l_list )):
166180 mat_l_list [jj ] = mat_l_list [jj ] - e_bcast * sl [jj ]
167181 for jj in range (len (mat_u_list )):
@@ -183,18 +197,29 @@ def _batched_eye(n):
183197 # ------------------ retarded Green's function ----------------------
184198 gr_left = [None ] * num_of_matrices
185199 gr_left [0 ] = tLA .solve (- mat_d_list [0 ], _batched_eye (mat_shapes [0 ][- 1 ]))
200+ mat_d_list [0 ] = None # consumed; free immediately
186201
187202 for q in range (num_of_matrices - 1 ): # (B2)
188203 gr_left [q + 1 ] = tLA .solve (
189204 - mat_d_list [q + 1 ] - mat_l_list [q ] @ gr_left [q ] @ mat_u_list [q ],
190205 _batched_eye (mat_shapes [q + 1 ][- 1 ]),
191206 )
207+ mat_d_list [q + 1 ] = None # consumed; backward sweep only reads mat_l/mat_u.
192208
193209 grl = [None ] * (num_of_matrices - 1 )
194210 gru = [None ] * (num_of_matrices - 1 )
195- grd = [i .clone () for i in gr_left ]
211+ grd = [None ] * num_of_matrices
212+ grd [- 1 ] = gr_left [- 1 ].clone ()
196213 g_trans = gr_left [- 1 ].clone ()
197214 gr_lc = [g_trans ] if need_gr_lc else None
215+ # Slots that go dead at the end of iteration q:
216+ # - mat_l_list[q], mat_u_list[q]: only re-read by the lesser/greater branches.
217+ # - gr_left[q]: dead unless the lesser/greater branch will consume it OR
218+ # the caller asked us to keep the list intact.
219+ # Nulling per slot lets the caching allocator coalesce its free list inside the
220+ # loop instead of holding a long fragmented tail until the sweep ends.
221+ drop_lu = not need_lesser and not need_greater
222+ drop_gl = drop_lu and not keep_gr_left
198223 for q in range (num_of_matrices - 2 , - 1 , - 1 ):
199224 gU = gr_left [q ] @ mat_u_list [q ] # hoisted
200225 grl [q ] = grd [q + 1 ] @ mat_l_list [q ] @ gr_left [q ] # (B5)
@@ -203,6 +228,12 @@ def _batched_eye(n):
203228 g_trans = gU @ g_trans
204229 if need_gr_lc :
205230 gr_lc .append (g_trans )
231+ del gU
232+ if drop_lu :
233+ mat_l_list [q ] = None
234+ mat_u_list [q ] = None
235+ if drop_gl :
236+ gr_left [q ] = None
206237 if need_gr_lc :
207238 gr_lc .reverse ()
208239
@@ -219,7 +250,8 @@ def _batched_eye(n):
219250
220251 gnl = [None ] * (num_of_matrices - 1 )
221252 gnu = [None ] * (num_of_matrices - 1 )
222- gnd = [i .clone () for i in gin_left ]
253+ gnd = [None ] * num_of_matrices
254+ gnd [- 1 ] = gin_left [- 1 ].clone ()
223255
224256 for q in range (num_of_matrices - 2 , - 1 , - 1 ):
225257 gLmH = mat_l_list [q ] @ gr_left [q ].mH # hoisted
@@ -243,7 +275,8 @@ def _batched_eye(n):
243275
244276 gpl = [None ] * (num_of_matrices - 1 )
245277 gpu = [None ] * (num_of_matrices - 1 )
246- gpd = [i .clone () for i in gip_left ]
278+ gpd = [None ] * num_of_matrices
279+ gpd [- 1 ] = gip_left [- 1 ].clone ()
247280
248281 for q in range (num_of_matrices - 2 , - 1 , - 1 ):
249282 lcgc = mat_l_list [q ].conj () @ gr_left [q ].conj () # hoisted
@@ -255,6 +288,8 @@ def _batched_eye(n):
255288 (gru [q ] @ mat_l_list [q ] @ gip_left [q ])
256289 gpu [q ] = gpl [q ].mH
257290
291+ if not keep_gr_left :
292+ gr_left = None
258293 return _pack_ans (g_trans , gr_lc , grd , grl , gru , gr_left ,
259294 gnd , gnl , gnu , gin_left ,
260295 gpd , gpl , gpu , gip_left ,
@@ -287,7 +322,8 @@ def _pack_ans(g_trans, gr_lc, grd, grl, gru, gr_left,
287322
288323
289324def recursive_gf (energy , hl , hd , hu , sd , su , sl , left_se , right_se , seP = None , E_ref = 0.0 , s_in = 0 , s_out = 0 ,
290- eta = 1e-5 , need_lesser = False , need_greater = False , need_gr_lc = False ):
325+ eta = 1e-5 , need_lesser = False , need_greater = False , need_gr_lc = False ,
326+ keep_gr_left = True ):
291327
292328 """The recursive Green's function algorithm is taken from
293329 M. P. Anantram, M. S. Lundstrom and D. E. Nikonov, Proceedings of the IEEE, 96, 1511 - 1550 (2008)
@@ -364,8 +400,10 @@ def _to_batch(t):
364400 return t
365401
366402 temp_mat_d_list = [_to_batch (hd [i ]) * 1. for i in range (len (hd ))]
367- temp_mat_l_list = [_to_batch (hl [i ]) * 1. for i in range (len (hl ))]
368- temp_mat_u_list = [_to_batch (hu [i ]) * 1. for i in range (len (hu ))]
403+ # L and U are only subtracted out-of-place inside the kernel; the expanded
404+ # view is fine, and skipping the copy saves K x B x n^2 per list.
405+ temp_mat_l_list = [_to_batch (hl [i ]) for i in range (len (hl ))]
406+ temp_mat_u_list = [_to_batch (hu [i ]) for i in range (len (hu ))]
369407 sd_b = [_to_batch (sd [i ]) for i in range (len (sd ))]
370408 sl_b = [_to_batch (sl [i ]) for i in range (len (sl ))]
371409 su_b = [_to_batch (su [i ]) for i in range (len (su ))]
@@ -421,15 +459,17 @@ def _to_batch(t):
421459 need_lesser = need_lesser ,
422460 need_greater = need_greater ,
423461 need_gr_lc = need_gr_lc ,
424- stacked = True )
462+ stacked = True ,
463+ keep_gr_left = keep_gr_left )
425464 else :
426465 ans = recursive_gf_cal (shift_energy , temp_mat_l_list , temp_mat_d_list , temp_mat_u_list ,
427466 sd_b , su_b , sl_b ,
428467 s_in = s_in_b , s_out = s_out_b , eta = eta ,
429468 need_lesser = need_lesser ,
430469 need_greater = need_greater ,
431470 need_gr_lc = need_gr_lc ,
432- stacked = False )
471+ stacked = False ,
472+ keep_gr_left = keep_gr_left )
433473
434474 if squeezed :
435475 ans = _squeeze_ans (ans )
0 commit comments