• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sandialabs / sdynpy / 13036987848

29 Jan 2025 05:23PM UTC coverage: 15.675%. Remained the same
13036987848

push

github

dprohe
Bugfix due to breaking change in qtpy/PyQt5

1 of 2 new or added lines in 2 files covered. (50.0%)

1 existing line in 1 file now uncovered.

2606 of 16625 relevant lines covered (15.68%)

0.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

7.48
/src/sdynpy/signal_processing/sdynpy_lrm.py
1
import numpy as np
3✔
2
import scipy as sp
3✔
3
import math as mt
3✔
4
from joblib import Parallel, delayed
3✔
5
import psutil
3✔
6
import warnings
3✔
7
from time import time
3✔
8

9
def frf_local_model(references,responses,abscissa,f_out=None,bandwidth=None,
3✔
10
                    transient=True,modelset=[[1,1,0],[1,1,1],[1,1,2]],
11
                    export_ratio=.1,max_parallel=512,print_time=True):
12
    """
13
    Local modeling FRF estimator for MIMO systems.
14
    This function can be called directly or using
15
    frf.timedata2frf(...,method='LRM') or frf.fft2frf(...,method='LRM').
16
    
17
    Parameters
18
    ----------
19
    references : ndarray (N_in, N_freqs) or (N_in, 1, N_freqs)
20
        REFERENCES FREQUENCY-DOMAIN DATA.
21
    responses : ndarray, shape (N_out, N_freqs) or (N_out, 1, N_freqs)
22
        RESPONSES FREQUENCY-DOMAIN DATA.
23
    abcissa : ndarray (N_freqs,)
24
        INPUT FREQUENCY VECTOR.
25
        
26
    f_out: ndarray (N_freqs_out,), optional
27
        Output frequency vector. Finer resolution increases computational cost,
28
        but only to a finite limit. Compuatational cost will not increase beyond
29
        f_out = f_in, but large interpolation may result in a large export_ratio.
30
        The default is f_in
31
    bandwidth: float, optional
32
        Local model estimation bandwidth in units of f_in. Larger values yield
33
        better noise filtering but risk underfitting and take longer to run.
34
    transient : boolean, optional
35
        whetheter to include transient estimation in local models. Recommend False
36
        for impact testing or burst random, True otherwise. The default is True.
37
    modelset : iterable of numpy (3,) arrays, optional
38
        Arrays contain [num. order, trans. order, denom. order].
39
        Recommend increasing numerator order for nonlinear structures
40
        and complicated FRFs. The default is [[1,1,0],[1,1,1],[1,1,2]].
41
    export_ratio : f;pat, optional
42
        (centered) proportion of local bands that can be stored and exported.
43
        Larger values risk capturing volatile end behavior but yield faster
44
        computation times. The default is 0.1.
45
    max_parallel: int, optional
46
        for very large systems with high frequency resolution and large bandwidths,
47
        system RAM can be a constraint. The default number of parallel processes
48
        is the number of CPU threads. If this causes freezing, max_parallel can
49
        be set to reduce the allowed number of parallel threads. Also, smaller
50
        pools may initialize faster. The default is 512.
51
    print_time: boolean, optional
52
        print remaining time if more than 10 seconds
53

54
    Returns
55
    -------
56
    f_out: ndarray (N_freqs_out,)
57
        estimated FRM frequency vector
58
    H: ndarray (N_freqs_out, N_out, N_in)
59
        frequency response matrix
60
    model_data: dict
61
        candidate model info and selected model at each frequency in f_out
62
       
63
        
64
    Notes
65
    -------
66
    To improve noise and transient removal, recommend increasing bandwidth
67
    
68
    To reduce computation time, recommend decreasing f_out resolution,
69
    increasing export_ratio, or decreasing bandwidth. For very large systems,
70
    if freezing occurs, reduce max_parallel.
71
    
72
    For more information, see "A practitioner's guide to local FRF estimation",
73
    K. Coletti. This function uses the MISO parameterization
74

75
    """
76
    
77
    """
78
    NOTES FOR MAINTAINER
79
    Outline of this function
80
    -------
81
        (1) f_in and f_out are parsed to generate an array of bands at which to
82
            estimate local models (bands) and a list of query points in each
83
            model at which to export solutions (query)
84
        (2) based on the model candidate set and problem size, functional
85
            parameterizations are generated which return linearized coefficient
86
            matrices (for solving) and the FRF matrix (for export)
87
        (3) computation time is estimated, and the LSQ algorithm is selected.
88
            Solving model parameters using interative LLSQ is the majority of
89
            computation time.
90
        (4) in parallel, local models are solved for each model in modelset and
91
            each frequency band. FRFs are exported. CTRL-F 'results = Parallel'
92
        (5) the winning model and corresponding FRM is returned in each band
93
        
94
    Notes on variable names
95
    -------
96
    U and Y are input and output data
97
    A,B,D are FRF numerator, transient numerator, and denom., respectively
98
    H is the frequency response matrix.
99
    LR is a function that produces linear coefficients (L) and remainder (R) for the local models
100
    See "A Practitioner’s Guide to Local FRF Estimation", K. Coletti for more information
101
    """
102
    
103
    #%% Process arguments
104
    U = references
×
105
    Y = responses
×
106
    f_in = abscissa
×
107
    del references; del responses; del abscissa
×
108
    
109
    df = float(f_in[1]-f_in[0]) # frequency step size
×
110
    if bandwidth is None: # local model bandwidth
×
111
        bandwidth = max( [min([300*df,40]) , max([30*df,10])] )
×
112
        # bounded below by the larger of 30 points and 10 Hz,
113
        # above by the smaller of 300 points and 40 Hz
114
    if f_out is None: # spacing between center frequencies for estimation
×
115
        f_out = f_in
×
116
    
117
    # Default parameters, hidden from user
118
    NitSK = 50 # max SK iterations
×
119
    conv_tol = .005 # SK parameter convergence tolerance
×
120
    
121
    #%% Calculate general helper vars
122
    Nfreq = f_in.size # number of frequencies
×
123
    Nin = U.shape[0]
×
124
    Nout = Y.shape[0]
×
125
    Nmod = len(modelset)
×
126
    U = U.reshape(Nin,Nfreq,order='F') # no realizations/frames needed
×
127
    Y = Y.reshape(Nout,Nfreq,order='F')
×
128
    f_in = f_in.reshape(-1,order='F')
×
129
    f_out = f_out.reshape(-1,order='F')
×
130
    
131
    if not Y.shape[-1] == U.shape[-1] == Nfreq:
×
132
        raise Exception('input arrays are not correctly sized')
×
133
    
134
    #%% Get estimation bands and query points (STEP 1)
135
    # Estimation bands contain the frequency bins used for parameter estimation,
136
    # and query points are the frequencies at which results are exported
137
    Nband = __round_odd(bandwidth/df) # number of data points in band
×
138
    N_half_band = int(Nband/2) # number of data points on either side of center
×
139
    max_from_center = export_ratio/2*bandwidth # maximum query distance from center point, except at ends
×
140
    
141
    bands,query = __parse_bands(N_half_band,max_from_center,f_in,f_out) # bands gives indices of f_in, and query gives r values for query. r=Hz/df.
×
142
    Ninds = bands.shape[0]    
×
143
    
144
    #%% Initialize parameterizations (STEP 2)
145
    LR,D,lsq_err,H = [[None]*Nmod,[None]*Nmod,[None]*Nmod,[None]*Nmod]
×
146
    Npar = np.zeros(Nmod)
×
147
    
148
    for k in range(Nmod-1,-1,-1):
×
149
        if transient:
×
150
            LR[k],D[k],lsq_err[k],H[k],Npar[k] = __parameterize(Nin,Nout,Nband,modelset[k])
×
151
        else:
152
            LR[k],D[k],lsq_err[k],H[k],Npar[k] = __parameterize_notrans(Nin,Nout,Nband,modelset[k])
×
153
            
154
        if Npar[k]>=Nband*Nout: # remove infeasible models
×
155
            warnings.warn('bandwidth too small for ' + str(modelset[k]) + ' model order')
×
156
            modelset.pop(k)
×
157
            Nmod -= 1
×
158
            LR.pop(k);D.pop(k);lsq_err.pop(k);H.pop(k);Npar = np.delete(Npar,k)
×
159
            
160
    dof = Nband*Nout-Npar
×
161
        
162
    #%% Compute (STEP 3/4)
163
    # Parallel function. # joblib can't parse lists (only arrays), so query is a parameter
164
    def __solve_ind(k,inq,lstsq):
×
165
        # Initialize. inq are radii of H to output in this band
166
        H_multi_model = np.empty((Nout,Nin,Nmod,inq.size),dtype=complex) # axis (0) output (1) input (2) model (3) frequency
×
167
        mdl = np.empty((Nmod,inq.size),dtype=complex)
×
168
        Uk = U[:,bands[k]]
×
169
        Yk = Y[:,bands[k]]
×
170
        
171
        for kk in range(Nmod): # model loop
×
172
            H_multi_model[:,:,kk,:],pstar = __solveLRM(Uk,Yk,LR[kk],D[kk],lsq_err[kk],H[kk],inq,conv_tol,NitSK,lstsq)
×
173
        
174
            # Model selection - MDL (see Pintelon, IEEE, 2021)
175
            mdl[kk,:] = pstar/dof[kk] * mt.exp( mt.log(2*Nband)*Npar[kk]/(dof[kk]-2) )
×
176
            
177
        return H_multi_model,mdl
×
178
    
179
    # Estimate time, choose algorithm - SEE APPENDIX NOTES 1 AND 2
180
    memory = psutil.virtual_memory().total/1e9
×
181
    n_parallel = min(psutil.cpu_count(),61,max_parallel)
×
182
    if memory/n_parallel < 2 and Nin*Nout > 500:
×
183
        warnings.warn('Many parallel processes relative to system RAM. For large problems, freezing may occur.')
×
184
    def test_lstsq_time(alg):
×
185
        start = time()
×
186
        __solve_ind( round(Ninds/2),query[round(Ninds/2)],alg )
×
187
        return time()-start
×
188
    if Nin*Nout < 500:
×
189
        time_numpy = test_lstsq_time(__lstsq_numpy)
×
190
    else:
191
        time_numpy = np.inf # numpy is very slow for large problems
×
192
    time_scipy = test_lstsq_time(__lstsq_scipy)
×
193
    
194
    if time_numpy < time_scipy:
×
195
        lstsq = __lstsq_numpy
×
196
    else:
197
        lstsq = __lstsq_scipy
×
198
        
199
    time_est = min(time_numpy,time_scipy)*Ninds/n_parallel
×
200
    if time_est > 10 and print_time: # parallel initialization takes 5 seconds
×
201
        print( 'Estimated time remaining: parallel init. + ' + str( round(1.1*time_est,1)) + ' seconds' )
×
202
    
203
    # Compute and extract results into arrays
204
    results = Parallel(n_jobs=n_parallel)(delayed(__solve_ind)(k,query[k],lstsq) for k in range(Ninds))
×
205
    H_multi_model = np.concatenate([x[0] for x in results],axis=3) # axis (0) Nout (1) Nin (2) model (3) Nind
×
206
    mdl = np.concatenate([x[1] for x in results],axis=1) # axis (0) model (1) Nind
×
207
            
208
    #%% Select winning models and export (STEP 5)
209
    msel = np.argmax(mdl,0) # axis. (0) freq. ind
×
210
    H = H_multi_model[:,:,msel,np.arange(f_out.size)] # axis. (0) Nout (1) Nin (2) freq. ind
×
211
    model_data = {'info': 'Model format is [H num. order, trans. num. order, denom. order].',
×
212
                  'modelset':modelset,
213
                  'model_selected':msel}
214
                
215
    # Return results
216
    return f_out, np.moveaxis(H,-1,0), model_data
×
217
  
218
#%% Local model solver
219
def __solveLRM(U,Y,LR,D,lsq_err,H,query,conv_tol,NitSK,lstsq):
3✔
220
    """
221
    Local model solver. This function occupies most of the FRF local modeling estimation
222
    time. Inputs are functions that accept parameter values and output local model component
223
    matrices used for least-squares solving and model exporting. Uses Sanathanan-Koerner
224
    iterations to solve the nonlinear rational LSTSQ problem iteratively
225
    """
226
    # SK iteration preparations
227
    L,R = LR(U,Y) # linearized transfer matrix and residual vector. Lin objective = D^{-1}*(L*t-R)
×
228
    Dp = np.ones(Y.shape) # initial scaling matrix. Axis (0) Nout (1) Nband
×
229
    t0s,lsq_min = [float('inf')]*2
×
230
    
231
    # Snathanan-Koererner Iterations
232
    for k in range(NitSK+5):
×
233
        # Define adjusted LLSQ/GLSQ problem
234
        X = L/Dp[:,:,np.newaxis] # left matrix in LLSQ stacked over r. Axis (0) Nout (1) Nband (2) Npar
×
235
        q = R/Dp # residual vector stacked over r. Axis (0) Nout (1) Nband
×
236
        
237
        # Solve and advance. Vast majority of compution time in all of get_frf
238
        t0 = lstsq(X,q) # axis. (0) Nout (1) Npar
×
239
    
240
        # Convergence
241
        if abs((t0-t0s)/t0).mean() < conv_tol:
×
242
            break # exit if convergence is reached
×
243
        else:
244
            Dp = D(t0) # store new adjustment matrix
×
245
        t0s = t0
×
246

247
        # Capture nonconvergence (in my experience, this is very rare)
248
        if k > NitSK:
×
249
            lsq_t0 = lsq_err(U,Y,t0)
×
250
            if lsq_t0 < lsq_min:
×
251
                lsq_min = lsq_t0
×
252
                t1 = t0
×
253
                
254
    t0=t1 if 't1' in locals() else t0
×
255
    
256
    return H(t0,query),-lsq_err(U,Y,t0) # Hstar, pstar (log density)
×
257

258
#%% Parameterizations. See "A Practitioner’s Guide to Local FRF Estimation", K. Coletti
259
def __parameterize(Nin,Nout,Nband,order):
3✔
260
    """
261
    Generate parameterization functions for estimation including transient removal
262
    Parameterizations are passed into __solveLRM to generate local models
263
    """
264
    # Components of f, because f can be iterated
265
    # Radius and exponent matrices. r is radius, s power, and A/B/D component matrices
266
    N_half_band = (Nband-1)/2
×
267
    r = np.arange(-N_half_band,N_half_band+1)
×
268
    rsA = r**np.arange(order[0]+1)[:,np.newaxis] # axis. (0) power (1) radius
×
269
    rsB = r**np.arange(order[1]+1)[:,np.newaxis]
×
270
    rsD = r**np.arange(order[2]+1)[:,np.newaxis]
×
271
    
272
    # Define theta break points
273
    bA = np.arange(Nout*Nin*(order[0]+1)) # transfer numerator
×
274
    bB = np.arange(bA[-1]+1,bA[-1]+Nout*(order[1]+1)+1) # transient numerator
×
275
    bD = np.arange(bB[-1]+1,bB[-1]+Nout*order[2]+1) # denominator (wrap Nout first, then s)
×
276
    Npar = bB[-1]+Nout*order[2]+1
×
277
    
278
    def lsq_err(U,Y,t):
×
279
        t = t.reshape(-1,order='F') # transform from SO format to MO
×
280
        
281
        # Build matrices
282
        A = np.einsum('ij,jk->ik',t[bA].reshape(-1,order[0]+1,order='F'),rsA).reshape(Nout,Nin,-1,order='F')
×
283
        B = np.einsum('ij,jk->ik',t[bB].reshape(-1,order[1]+1,order='F'),rsB) # axis (0) Nout (1) Nfreq
×
284
        catmat = np.concatenate( (np.ones((Nout,1)),t[bD][:,np.newaxis]),0 )
×
285
        D =  np.einsum('ij,jk->ik',catmat.reshape(Nout,-1,order='F'),rsD) # axis. (1) Nout (2) Nfreq
×
286
        
287
        # Compute sum of squared residuals
288
        Ysim = (np.einsum('jkl,kl->jl',A,U) + B)/D
×
289
        return (abs(Y-Ysim)**2).sum()
×
290
    
291
    def H(t,r_inq):
×
292
        t = t.reshape(-1,order='F') # transform from SO format to MO
×
293
        
294
        # Radius and exponent matrices
295
        rsA_inq = r_inq**np.arange(order[0]+1)[:,np.newaxis] # axis. (0) power (1) radius
×
296
        rsD_inq = r_inq**np.arange(order[2]+1)[:,np.newaxis]
×
297
        
298
        # Build matrices
299
        A = np.einsum('ij,jk->ik',t[bA].reshape(-1,order[0]+1,order='F'),rsA_inq).reshape(Nout,Nin,-1,order='F') # axis. (1) Nout (2) Nin (3) Nfreq
×
300
        temp = np.concatenate( (np.ones(Nout),t[bD]) )
×
301
        D = np.einsum('ij,jk->ik',temp.reshape(Nout,-1,order='F'),rsD_inq) # axis. (1) Nout (2) Nfreq
×
302
        
303
        return A/D[:,np.newaxis,:]
×
304

305
    # Build linearized (SK) transfer matrix L(U,y)*t + R(U,Y) = Dp^(-1) (D(t)*Y - N(t)*U - M(t))
306
    # Denominator matrix for SK iterations
307
    def D(t):
×
308
        return 1 + np.einsum( 'ij,lj->li',r[:,np.newaxis]**np.arange(1,order[2]+1) , t[:,np.arange(-order[2],0)] ) # axis (0) Nout (1) Nband
×
309
    
310
    def LR(U,Y):        
×
311
        # Compute residual
312
        R = -Y # axis (0) Nout (1) Nband
×
313
        
314
        # Compute components
315
        r1 = r[:,np.newaxis]
×
316
        A1 = Y.T.reshape(-1,1,Nout,order='F')* (r1**np.arange(1,order[2]+1))[:,:,np.newaxis] # D(t)*Y. Axis. (1) r (2) s (3) Nout
×
317
        A2 = (U.T[:,:,np.newaxis] * r1[:,:,np.newaxis] **np.arange(order[0]+1)).reshape(Nband,Nin*(order[0]+1),1,order='F') # A(t)*U. Axis. (1) r (2) Nin*s (3) Nout
×
318
        A3 = ( r1**np.arange(order[1]+1) )[:,:,np.newaxis] # B(t). Axis. (1) r (2) s (3) empty
×
319
        
320
        # Assemble into array
321
        if order[-1]>0:
×
322
            catmat = np.ones((1,1,Y.shape[0])) # A2 and A3 don't depend on Nout
×
323
            L = np.concatenate((-A2*catmat,-A3*catmat,A1),1).transpose(2,0,1) # axis (0) Nout (1) Nband (2) parameter
×
324
        else:
325
            L = np.concatenate((-A2,-A3),1).transpose(2,0,1) # axis (0) Nout (1) Nband (2) parameter
×
326
        
327
        return L,R
×
328

329
    return LR,D,lsq_err,H,Npar
×
330
def __parameterize_notrans(Nin,Nout,Nband,order):
3✔
331
    """
332
    Generate parameterization functions for estimation not including transient removal.
333
    Parameterizations are passed into __solveLRM to generate local models
334
    """
335
    # Components of f, because f can be iterated
336
    # Radius and exponent matrices. r is radius, s power, and A/B/D component matrices
337
    N_half_band = (Nband-1)/2
×
338
    r = np.arange(-N_half_band,N_half_band+1)
×
339
    rsA = r**np.arange(order[0]+1)[:,np.newaxis] # axis. (0) power (1) radius
×
340
    rsD = r**np.arange(order[2]+1)[:,np.newaxis]
×
341
    
342
    # Define theta break points
343
    bA = np.arange(Nout*Nin*(order[0]+1)) # transfer numerator
×
344
    bD = np.arange(bA[-1]+1,bA[-1]+Nout*order[2]+1) # denominator (wrap Nout first, then s)
×
345
    Npar = bA[-1]+Nout*order[2]+1
×
346
    
347
    def lsq_err(U,Y,t):
×
348
        t = t.reshape(-1,order='F') # transform from SO format to MO
×
349
        
350
        # Build matrices
351
        A = np.einsum('ij,jk->ik',t[bA].reshape(-1,order[0]+1,order='F'),rsA).reshape(Nout,Nin,-1,order='F')
×
352
        B = np.zeros((Nout,Nband)) # axis (0) Nout (1) Nfreq
×
353
        catmat = np.concatenate( (np.ones((Nout,1)),t[bD][:,np.newaxis]),0 )
×
354
        D =  np.einsum('ij,jk->ik',catmat.reshape(Nout,-1,order='F'),rsD) # axis. (1) Nout (2) Nfreq
×
355
        
356
        # Compute sum of squared residuals
357
        Ysim = (np.einsum('jkl,kl->jl',A,U) + B)/D
×
358
        return (abs(Y-Ysim)**2).sum()
×
359
    
360
    def H(t,r_inq):
×
361
        t = t.reshape(-1,order='F') # transform from SO format to MO
×
362
        
363
        # Radius and exponent matrices
364
        rsA_inq = r_inq**np.arange(order[0]+1)[:,np.newaxis] # axis. (0) power (1) radius
×
365
        rsD_inq = r_inq**np.arange(order[2]+1)[:,np.newaxis]
×
366
        
367
        # Build matrices
368
        A = np.einsum('ij,jk->ik',t[bA].reshape(-1,order[0]+1,order='F'),rsA_inq).reshape(Nout,Nin,-1,order='F') # axis. (1) Nout (2) Nin (3) Nfreq
×
369
        catmat = np.concatenate( (np.ones((Nout,1)),t[bD][:,np.newaxis]),0 )
×
370
        D = np.einsum('ij,jk->ik',catmat.reshape(Nout,-1,order='F'),rsD_inq) # axis. (1) Nout (2) Nfreq
×
371
        
372
        return A/D[:,np.newaxis,:]
×
373

374
    # Build linearized (SK) transfer matrix L(U,y)*t + R(U,Y) = Dp^(-1) (D(t)*Y - N(t)*U - M(t))
375
    # Denominator matrix for SK iterations
376
    def D(t):
×
377
        return 1 + np.einsum( 'ij,lj->li',r[:,np.newaxis]**np.arange(1,order[2]+1) , t[:,np.arange(-order[2],0)] ) # axis (0) Nout (1) Nband
×
378
    
379
    def LR(U,Y):        
×
380
        # Compute residual
381
        R = -Y # axis (0) Nout (1) Nband
×
382
        
383
        # Compute components
384
        r1 = r[:,np.newaxis]
×
385
        A1 = Y.T.reshape(-1,1,Nout,order='F')* (r1**np.arange(1,order[2]+1))[:,:,np.newaxis] # D(t)*Y. Axis. (1) r (2) s (3) Nout
×
386
        A2 = (U.T[:,:,np.newaxis] * r1[:,:,np.newaxis] **np.arange(order[0]+1)).reshape(Nband,Nin*(order[0]+1),1,order='F') # A(t)*U. Axis. (1) r (2) Nin*s (3) Nout
×
387
        
388
        # Assemble into array
389
        if order[-1]>0:
×
390
            catmat = np.ones((1,1,Y.shape[0])) # A2 and A3 don't depend on Nout
×
391
            L = np.concatenate((-A2*catmat,A1),1).transpose(2,0,1) # axis (0) Nout (1) Nband (2) parameter
×
392
        else:
393
            L = -A2.transpose(2,0,1) # axis (0) Nout (1) Nband (2) parameter
×
394
        
395
        return L,R
×
396

397
    return LR,D,lsq_err,H,Npar
×
398

399
#%% Frequency parser
400
def __parse_bands(N_half_band,max_from_center,f_in,f_out):
3✔
401
    """
402
    Local modeling is not constrained to the input frequency vector. This function
403
    processes input parameters and parses f_in into bands to model (bands).
404
    In addition, it returns query points (query) at which to export each
405
    local model
406
    """
407
    df_out = f_out[1]-f_out[0] # query point step size
×
408
    df_in = f_in[1]-f_in[0] # input frequencies steps size
×
409
    inq_per_band = mt.floor(2*max_from_center/df_out) + 1 # query points per band
×
410
    
411
    # Assign export indices to band numbers
412
    n_lo = (f_out <= f_in[N_half_band]+max_from_center).sum()
×
413
    n_hi = (f_out >= f_in[-N_half_band-1]-max_from_center).sum()
×
414
    n_mid = f_out.size - n_lo - n_hi
×
415
    
416
    # Parsing is different if n_lo and n_hi > 0
417
    is_lo = n_lo > 0
×
418
    is_hi = n_hi > 0
×
419
    
420
    # Get band assignment numbers for each entry in f_out
421
    temp = np.ones((inq_per_band,1))*np.arange(is_lo,mt.ceil(n_mid/inq_per_band)+1)
×
422
    temp = temp.reshape(-1,order='F')[:n_mid]
×
423
    inq_assign = np.concatenate(( np.zeros(n_lo) , temp , (temp.max()+1)*np.ones(n_hi) ))
×
424
    inq_assign = inq_assign.astype(dtype='int32')
×
425
    
426
    # Get indices of center points and build bands
427
    c_inds_hz = np.array([f_out[inq_assign==k].mean() for k in range(is_lo,inq_assign.max()+1-is_hi)]) # ideal center locations of each export
×
428
    c_inds = ((c_inds_hz-f_in[0])/df_in).round() # actual center locations of each export on f_in
×
429
    if abs(c_inds_hz - f_in[c_inds.astype('int32')]).max() > max_from_center:
×
430
        warnings.warn("""f_in is too sparse for f_out specification, resulting in LM extrapolation 
×
431
                      beyond the export_ratio. Consider using default specification or increasing bandwidth.""")
432
    lo_ind = np.array([N_half_band]) if is_lo else np.ones(0)
×
433
    hi_ind = np.array([f_in.size-1-N_half_band]) if is_hi else np.ones(0)
×
434
    c_inds = np.concatenate(( lo_ind,c_inds,hi_ind )).astype(dtype='int32')
×
435
    bands = np.arange(-N_half_band,N_half_band+1) + c_inds[:,np.newaxis] # axis (0) center ind (1) r
×
436
    bands = bands.astype(dtype='int32')
×
437
    
438
    # Get normalized extraction radii
439
    query = [(f_out[inq_assign==k] - ind)/df_in for k,ind in enumerate(f_in[c_inds])]
×
440
    
441
    return bands,query
×
442
    
443
#%% General functions
444
def __round_odd(x):
3✔
445
    y = int(x)
×
446
    return y + int(y%2==0)
×
447
def __lstsq_scipy(A,b): # extension of np.linalg.lstsq to 3-D arrays. Not vectorized.
3✔
448
    X = np.empty( np.array(A.shape)[[0,-1]] , dtype=complex ) # axis. (0) Nout [1] Npar
×
449
    for k,Ak in enumerate(A):
×
450
        X[k] = sp.linalg.lstsq(Ak,b[k])[0]
×
451
    return X
×
452
def __lstsq_numpy(A,b): # extension of np.linalg.lstsq to 3-D arrays. Not vectorized.
3✔
453
    X = np.empty( np.array(A.shape)[[0,-1]] , dtype=complex ) # axis. (0) Nout [1] Np
×
454
    for k,Ak in enumerate(A):
×
455
        X[k] = np.linalg.lstsq(Ak,b[k],rcond=None)[0]
×
456
    return X
×
457

458
"""
3✔
459
APPENDIX
460
Note 1: Windows OS restricts parallel processes to 61
461

462
Note 2: linalg.lstsq in scipy and numpy vary wildly in relative speed. In some cases,
463
numpy is 10x faster. For other problem sizes, scipy is 20x faster. Both are tested,
464
and the faster algorithm is selected.
465
"""
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc