Пример #1
0
def poisson(tri, boundary):
    """
    Get elements and vertices from meshgrid using the triangle tri and boundary points boundary
    """
    elements = tri.triangle_nodes
    vertices = np.vstack((tri.x, tri.y)).T

    # number of vertices and elements
    N = vertices.shape[0]
    E = elements.shape[0]

    # Loop over elements and assemble LHS and RHS
    A = np.matlib.zeros((N, N))
    b = np.matlib.zeros((N, 1))
    for j in range(E):
        index = (elements[j, :]).tolist()
        A[ix_(index, index)] += A_e(vertices[index, :])
        b[index] += b_e(vertices[index, :])

    # find the "free" vertices that we need to solve for
    free = list(set(range(len(vertices))) - set(boundary))

    # initialise solution to zero so "non-free" vertices are by default zero
    u = np.matlib.zeros((N, 1))

    # solve for "free" vertices.
    u[free] = np.solve(A[ix_(free, free)], b[free])
    return np.array(u)
Пример #2
0
def getRecessionParams(discharge):
    dQdt, avgQ = getDerivative(discharge)
    y = log(-dQdt)
    A = np.concatenate((np.ones(len(discharge) - 1, 1), math.log(avgQ)),
                       axis=0)
    x = np.solve(A, y)

    a = math.exp(x[0])
    b = x[1]
    return a, b
Пример #3
0
    def evaluate_policy(self, P, R, policy):


        P_pi = np.zeros((self.nS, self.nS))
        R_pi = np.zeros((self.nS, self.nA))

        for s in range(self.nS):
            for s1 in range(self.nS):
                P_pi = np.sum(policy[s, :] * P[s, :, s1])
            for a in range(self.nA):
                R_pi = policy[s, a] * np.sum(P[s, a, :] * R[s, a, :])
        I = np.diag(np.ones(self.nS))
        V = np.solve(I - self.mdp_info.gamma * P_pi, R_pi)
        return V
Пример #4
0
    def _solve_(self):
        Z = self.P_Omega.toarray()
        Known = self.Omega
        if self.init == 0:
            X = np.zeros((self.n1, self.k))
            Y = np.eye(self.k, self.n2)
            Res = self.M_Omega
            res = self.datanrm
        if self.est_rank == 1: rank_max = min(self.rank_max, self.k)
        #if self.n1>self.n2: Z=Z.T

        #parameter for alf
        alf, increment, itr_rank = 0, 1, 0
        RMSE = []
        while True:
            X0, Y0, Res0, res0 = X, Y, Res, res
            itr_rank += 1
            if self.Zfull:
                Z0 = Z
                X = np.dot(Z, Y.T)
                if self.est_rank == 1:
                    X, R = np.linalg.qr(X)
                    Y = np.dot(X.T, Z)
                elif self.DoQR:
                    X, R = np.linalg.qr(X)
                    Y = np.dot(X.T, Z)
                else:
                    Xt = X.T
                    Y = np.solve(np.dot(Xt, X), np, dot(Xt, Z))
                Z = np.dot(X, Y)
                Res = self.M_Omega - Z[Known]
            res = np.linalg.norm(Res, 2)
            relres = res / self.datanrm
            ratio = res / res0
            reschg = abs(1 - res / res0)
            RMSE.append(res / np.linalg.norm(self.Morigin, ord='fro'))
            if itr_rank == 100: break
        x_coordinate = range(len(RMSE))
        plt.title('GMM-NOISE')
        plt.xlabel('Number of iterations')
        plt.ylabel('RMSE')
        #lg scale
        #plt.yscale('log')
        plt.plot(x_coordinate, RMSE, '-')
        plt.show()
        return 0
def gauss_newton(x, y, r, p0):
    length = len(x)
    initial = np.array(p0)
    x_solve = [length]
    y_solve = [length]
    vk = [length]
    x_solve[0] = p0[0]
    y_solve[0] = p0[0]
    vk[0] = p0[0]
    for i in range(length):
        A = fsolve(x_solve[i], p0)
        vk[i] = np.solve(
            -np.transpose(A) * np.sqrt((p0[0] - x_solve[i])**2 +
                                       (p0[1] - y_solve[i])**2),
            np.transpose(A) * A)
        x_solve[i + 1] = x_solve[i] + vk[i]
    print(x_solve[length])
def simpson(func, v):

    a = v[0]  #Constantes
    b = v[1]

    x = Symbol('x')  #Inicializa "x" como símbolo
    f = sympify(func)  #Se traduce el string "func" a una función de sympy
    fx = lambdify(x, f, modules=['numpy'])  #Se inicializa la función f(x)

    x0 = a
    x1 = (a + b) / 2
    x2 = b

    h = (b - a) / 2  #Valor h

    f_resultado = (h / 3) * (fx(x0) + 4 * fx(x1) + fx(x2)
                             )  #Resultado de la aproximacion

    df = f.diff(x, 4)  #Cuarta derivada
    dfx = lambdify(x, df, modules=['numpy'])  #Se iniacilaiza la funcion f4(x)

    max_relativo = maximo(dfx,
                          v)  #Calculo del maximo en los extremos del intervalo

    #En casa de que la funcion no se indefina f(x) = 0
    try:
        sol = np.solve(f.diff(x, 1))
        punto = []
        for i in sol:  #Calcula cual de los resultados es el maximo
            if (abs(dfx(i)) > punto):
                punto = [i, abs(dfx(i))]

        if (punto[1] > max_relativo[1]
            ):  #Compara el maximo con el maximo de los extremos
            error = (h**5 / 90) * abs(dfx(punto[0]))
        else:
            error = (h**5 / 90) * abs(dfx(max_relativo[0]))

    except:
        error = (h**5 / 90) * abs(dfx(max_relativo[0]))

    print(f_resultado, error)
    return (f_resultado, error)  #Resultado (aproximacion, error)
Пример #7
0
    def test_lu(self):

        import math

        A = np.array([[3, 17, 10], [2, 4, -2], [6, 18, -12]])
        LU = np.array([[6, 18, -12], [1 / 2, 8, 16], [1 / 3, -1 / 4, 6]])
        P = np.array([2, 0, 1], dtype=np.uint16)

        # lu decomposition
        lu, p = np.lu(A)
        for i, ei in enumerate(lu):
            for j, eij in enumerate(ei):
                self.assertEqual(eij, LU[i][j])

        for i, ei in enumerate(p):
            self.assertEqual(ei, P[i])

        # determinant
        self.assertEqual(np.det(A), 6 * 8 * 6)

        # zero determinant, singular
        AA = np.array([[3, 17, 10], [2, 4, -2], [3, 17, 10]])
        self.assertEqual(np.det(AA), 0)

        # vector solve
        b = np.array([1, -1, 0])
        x = np.solve(A, b)
        X = [-1.375, 0.375, -0.125]
        for i, ei in enumerate(x):
            self.assertEqual(ei, X[i])

        res = np.dot(A, x) - b
        for i, ei in enumerate(res):
            self.assertEqual(ei, 0)

        # vector solve. singular
        with self.assertRaises(ValueError):
            x = np.solve(AA, b)

        # matrix solve
        b = np.array([[1, 0], [-1, 1], [0, 0]])
        x = np.solve(A, b)
        X = [[-1.375, 4 / 3], [0.375, -1 / 3], [-0.125, 1 / 6]]
        for i, ei in enumerate(x):
            for j, eij in enumerate(ei):
                self.assertEqual(eij, X[i][j])

        res = np.dot(A, x) - b
        for i, ei in enumerate(res):
            for j, eij in enumerate(ei):
                self.assertTrue(math.fabs(eij) < 1e-6)

        # matrix inverse
        Ai = np.inv(A)
        I = np.eye(3)
        res = np.dot(A, Ai) - I
        for i, ei in enumerate(res):
            for j, eij in enumerate(ei):
                self.assertTrue(math.fabs(eij) < 1e-6)

        res = np.dot(Ai, A) - I
        for i, ei in enumerate(res):
            for j, eij in enumerate(ei):
                self.assertTrue(math.fabs(eij) < 1e-6)

        Ais = np.solve(A, I)
        res = Ai - Ais
        for i, ei in enumerate(res):
            for j, eij in enumerate(ei):
                self.assertTrue(math.fabs(eij) < 1e-6)

        # inverse. singular
        with self.assertRaises(ValueError):
            Ai = np.inv(AA)
Пример #8
0
def mls(B, v, umin, umax, Wv=None, Wu=None, ud=None, u=None, W=None, imax=100):
    """
mls - Control allocation using minimal least squares.

[u,W,iter] = mls_alloc(B,v,umin,umax,[Wv,Wu,ud,u0,W0,imax])

 Solves the bounded sequential least-squares problem

   min ||Wu(u-ud)||   subj. to   u in M

 where M is the set of control signals solving

   min ||Wv(Bu-v)||   subj. to   umin <= u <= umax

 using a two stage active set method. Wu must be diagonal since the
 problem is reformulated as a minimal least squares problem. The
 implementation does not handle the case of coplanar controls.

  Inputs:
  -------
 B     control effectiveness matrix (k x m)
 v     commanded virtual control (k x 1)
 umin  lower position limits (m x 1)
 umax  upper position limits (m x 1)
 Wv    virtual control weighting matrix (k x k) [I]
 Wu    control weighting matrix (m x m), diagonal [I]
 ud    desired control (m x 1) [0]
 u0    initial point (m x 1)
 W0    initial working set (m x 1) [empty]
 imax  max no. of iterations [100]

  Outputs:
  -------
 u     optimal control
 W     optimal active set
 iter  no. of iterations (= no. of changes in the working set + 1)

                           0 if u_i not saturated
 Active set syntax: W_i = -1 if u_i = umin_i
                          +1 if u_i = umax_i

Directly Based on the code from:
    Ola Haerkegard, www.control.isy.liu.se/~ola
    see licsence.

"""

    #k = number of virtual controls
    #m = number of variables (actuators)
    k, m = B.shape
    if u == None:
        u = np.mean(umin + umax, 0)[:, None]
    if W == None:
        W = np.zeros((m, 1))
    if ud == None:
        ud = np.zeros((m, 1))
    if Wu == None:
        Wu = np.eye(m)
    if Wv == None:
        Wv = np.eye(k)
    phase = 1
    #Reformulate as a minimal least squares problem. See 2002-03-08 (1).
    A = Wv.dot(B).dot(np.linalg.pinv(Wu))
    b = Wv.dot(v - B.dot(ud))

    xmin = (umin - ud).flatten()
    xmax = (umax - ud).flatten()
    # Compute initial point and residual.
    x = Wu.dot(u - ud)
    r = np.atleast_2d(A.dot(x) - b)

    #Determine indeces of free variables
    i_free = (W == 0).flatten()
    m_free = np.sum(i_free)

    for i in range(imax):
        #print 'Iter: ', i
        if phase == 1:
            A_free = A[:, i_free]
            if m_free <= k:
                if m_free > 0:
                    p_free = np.linalg.lstsq(-A_free, r)[0]
            else:
                q1, r1 = qr(A_free.T)
                p_free = -q1.dot(np.solve(r1.T, r))
            p = np.zeros((m, 1))
            if A.shape[1] > 1:
                p[i_free] = p_free
            else:
                p[i_free] = p_free.flatten()

        else:
            i_fixed = np.logical_not(i_free)
            m_fixed = m - m_free

            if m_fixed > 0:
                HT = U[i_fixed.squeeze(), :].T
                V, Rtot = qr(np.atleast_2d(HT))
                V1 = V[:, :m_fixed]
                V2 = V[:, m_fixed + 1:]
                R = Rtot[:, m_fixed]
            else:
                V, Rtot = np.array([[]]), np.array([[]])
                V1 = V2 = R = V.T

            s = -V2.T.dot(z)
            pz = V2.dot(s)
            p = U.dot(pz)

        x_opt = x + p
        infeasible = np.logical_or(x_opt < xmin, x_opt > xmax)
        if not np.any(infeasible[i_free]):
            x = x_opt

            if phase == 1:
                r = r + A.dot(p)
            else:
                z = z + pz
            if phase == 1 and m_free >= k:
                phase = 2
                Utot, Stot = qr(A.T)
                U = Utot[:, k:]
                z = U.T.dot(x)
            else:
                lam = np.zeros((m, 1))
                if m_free < m:
                    if phase == 1:
                        g = A.T.dot(r)
                        lam = -W * g
                    else:
                        lam[i_fixed] = -W[i_fixed] * np.linalg.solve(
                            R, V1.T.dot(z))
                    if np.all(lam >= -eps):
                        u = np.linalg.solve(Wu, x) + ud
                        return u

                lambda_neg, i_neg = np.min(lam), np.argmin(lam)
                W[i_neg] = 0
                i_free[i_neg] = True
                m_free += 1
        else:
            dist = np.ones(m)
            i_min = np.logical_and(i_free, p.flat < 0).flatten()
            i_max = np.logical_and(i_free, p.flat > 0).flatten()
            dist[i_min] = (xmin[i_min] - x[i_min]) / p[i_min]
            dist[i_max] = (xmax[i_max] - x[i_max]) / p[i_max]
            alpha, i_alpha = np.min(dist), np.argmin(dist)

            x = x + alpha * p
            if phase == 1:
                r = r + A.dot(alpha * p)  #!!
            else:
                z = z + alpha * pz
            W[i_alpha] = np.sign(p[i_alpha])

            if i_free[i_alpha]:
                i_free[i_alpha] = False
                m_free -= 1

    u = np.linalg.solve(Wu, x) + ud
    return u
Пример #9
0
def mls(B,v,umin,umax,Wv=None,Wu=None,ud=None,u=None,W=None,imax=100):
    """
mls - Control allocation using minimal least squares.

[u,W,iter] = mls_alloc(B,v,umin,umax,[Wv,Wu,ud,u0,W0,imax])

 Solves the bounded sequential least-squares problem

   min ||Wu(u-ud)||   subj. to   u in M

 where M is the set of control signals solving

   min ||Wv(Bu-v)||   subj. to   umin <= u <= umax

 using a two stage active set method. Wu must be diagonal since the
 problem is reformulated as a minimal least squares problem. The
 implementation does not handle the case of coplanar controls.

  Inputs:
  -------
 B     control effectiveness matrix (k x m)
 v     commanded virtual control (k x 1)
 umin  lower position limits (m x 1)
 umax  upper position limits (m x 1)
 Wv    virtual control weighting matrix (k x k) [I]
 Wu    control weighting matrix (m x m), diagonal [I]
 ud    desired control (m x 1) [0]
 u0    initial point (m x 1)
 W0    initial working set (m x 1) [empty]
 imax  max no. of iterations [100]
 
  Outputs:
  -------
 u     optimal control
 W     optimal active set
 iter  no. of iterations (= no. of changes in the working set + 1)

                           0 if u_i not saturated
 Active set syntax: W_i = -1 if u_i = umin_i
                          +1 if u_i = umax_i

Directly Based on the code from:
     Ola Härkegård, www.control.isy.liu.se/~ola   
    see licsence. 
     
"""

  
 #k = number of virtual controls
 #m = number of variables (actuators)

    k,m=B.shape    
    if u==None:
        u=np.mean(umin+umax,0)[:,None]                
    if W==None:
        W=np.zeros((m,1))
    if ud==None:
        ud=np.zeros((m,1))
    if Wu==None:
        Wu=np.eye(m)
    if Wv==None:
        Wv=np.eye(k)
    
    phase=1
    
    #Reformulate as a minimal least squares problem. See 2002-03-08 (1).    
    A=Wv.dot(B).dot(np.linalg.pinv(Wu))    
    #print B, v
    #A=B    
    b = Wv.dot(v-B.dot(ud))
    #b=v
    #print b
    xmin = (umin-ud)
    xmax = (umax-ud)
    # Compute initial point and residual.
    x = Wu.dot(u-ud)
    #x#=umin-umax
    r = A.dot(x)-b
#    print x.shape, r.shape, b.shape,x,r
    #Determine indeces of free variables
    i_free = W==0    
    m_free = np.sum(i_free)
    
    for i in range(imax):  
        #print 'Iter: ', i 
        if phase==1:
            A_free = A[:,i_free.squeeze()]
            if m_free<=k:                
                if m_free>0:
                    p_free=np.linalg.lstsq(-A_free,r)[0]                
            else:                
                q1,r1=qr(A_free.T)
                p_free=-q1.dot(np.solve(r1.T,r))
            p=np.zeros((m,1))
            p[i_free.squeeze()]=p_free
            
        
        else:            
            i_fixed=np.logical_not(i_free)
            m_fixed=m-m_free
            
            if m_fixed>0:            
                HT=U[i_fixed.squeeze(),:].T                      
                V,Rtot= qr(np.atleast_2d(HT))
                V1=V[:,:m_fixed]
                V2=V[:,m_fixed+1:]
                R=Rtot[:,m_fixed]      
            else:
                V,Rtot=np.array([[]]),np.array([[]])
                V1=V2=R=V.T
            
            s=-V2.T.dot(z)
            pz=V2.dot(s)
            p=U.dot(pz)
            
        x_opt=x+p
        infeasible=np.logical_or(x_opt<xmin,x_opt>xmax)
        if not np.any(infeasible[i_free]):
            x=x_opt
            
            if phase==1:
                r=r+A.dot(p)                
            else:
                z=z+pz
            if phase==1 and m_free>=k:
                phase=2
                Utot, Stot=qr(A.T)
                U=Utot[:,k:]
                z=U.T.dot(x)
                
                
            else:            
                lam=np.zeros((m,1))
                if m_free<m:
                    if phase==1:
                        g=A.T.dot(r)
                        lam=-W*g
                    else:
                        lam[i_fixed]=-W[i_fixed]*np.solve(R,V1.T.dot(z))
                    
                    if np.all(lam>= -eps):
                        u=np.linalg.solve(Wu,x)+ud
                        return u
                
                lambda_neg,i_neg=np.min(lam),np.argmin(lam)
                W[i_neg]=0
                i_free[i_neg]=1
                m_free+=1
        else:
            dist=np.ones((m,1))            
            
            i_min=np.logical_and(i_free,p<0)
            i_max=np.logical_and(i_free,p>0)
            
            
                
            dist[i_min]=(xmin[i_min]-x[i_min])/p[i_min]
            dist[i_max]=(xmax[i_max]-x[i_max])/p[i_max]
                
            
            alpha,i_alpha=np.min(dist),np.argmin(dist)
            
            x = x + alpha*p
            if phase==1:
                r=r+A.dot(alpha*p) #!!                
            else:
                z=z+alpha*pz
            W[i_alpha]=np.sign(p[i_alpha])
            i_free[i_alpha]=0
            m_free-=1
    u=np.linalg.solve(Wu,x)+ud
    return u
Пример #10
0
def main():
    parser = OptionParser(usage="usage: %prog [options] INPUT.mat OUTPUT.mat")
    if os.name == "posix":
        parser.add_option(
            "-p",
            "--parallel",
            dest="parallel",
            help="number of threads "
            "(default: available CPUs; use 0 to disable threading code)",
            default=None,
            metavar="N")
    parser.add_option(
        "-c",
        "--chunksize",
        dest="chunk_size",
        default=DEFAULT_X_STRIP_SIZE,
        metavar="N",
        help="number of matrix rows to process at a time in each "
        "parallel job (higher -> more memory usage, "
        "less parallelism, reduced threading overhead)")
    options, args = parser.parse_args()
    if len(args) != 2:
        parser.error("wrong number of arguments")
    if os.name != "posix":
        options.parallel = 0

    (input_path, output_path) = args
    input_data = loadmat(input_path, matlab_compatible=True)
    # A hack: we stash the big data into a global variable before using
    # multiproessing to fork off threads, so the child processes will inherit
    # the data in shared memory. (This only works on Unix, which is why we
    # disable the multi-threading code on other systems.)
    global _GLOBAL_SHARED_DATA_
    g = {}
    g["eeg_data"] = input_data["eeg_data"]
    # -1 converts from MATLAB-style indexing to Python-style indexing
    g["event_idx"] = input_data["event_idx"].squeeze() - 1
    g["design_matrix"] = input_data["design_matrix"]
    g["pre_event_samples"] = input_data["pre_event_samples"].item()
    g["post_event_samples"] = input_data["post_event_samples"].item()
    g["artifact_starts"] = input_data["artifact_starts"].squeeze() - 1
    # Need a -1 to convert this to 0-based indexing, then a +1 to convert it
    # to Python-style half-open interval ranges instead of MATLAB-style closed
    # interval ranges... which cancel out, so in fact we can use the numbers
    # as is:
    g["artifact_stops"] = input_data["artifact_stops"].squeeze()
    _GLOBAL_SHARED_DATA_ = g

    num_channels = g["eeg_data"].shape[1]
    epoch_len = g["pre_event_samples"] + 1 + g["post_event_samples"]
    X_columns = epoch_len * g["design_matrix"].shape[1]
    XtX_accumulator = np.zeros((X_columns, X_columns))
    XtY_accumulator = np.zeros((X_columns, num_channels))

    if options.parallel == 0:
        # Serial code, in-process
        pool = None
        imap_fn = itertools.imap
    else:
        # With any other value for parallel, we spawn worker processes. (This
        # includes parallel=1. parallel=1 won't be any faster than parallel=0,
        # but it might be useful for testing the worker process code.)
        pool = multiprocessing.Pool(options.parallel)
        imap_fn = pool.imap_unordered

    try:
        for (XtX, XtY) in imap_fn(
                compute_XtX_XtY_for_slice,
                pick_slices(g["eeg_data"].shape[0], g["artifact_starts"],
                            g["artifact_stops"], options.chunksize)):
            XtX_accumulator += XtX
            XtY_accumulator += XtY
    finally:
        if pool is not None:
            pool.terminate()
    betas = np.solve(XtX_accumulator, XtY_accumulator)
    savemat(output_path, {"betas": betas}, oned_as="column")
Пример #11
0
def main():
    parser = OptionParser(usage="usage: %prog [options] INPUT.mat OUTPUT.mat")
    if os.name == "posix":
        parser.add_option("-p", "--parallel", dest="parallel",
                          help="number of threads "
                          "(default: available CPUs; use 0 to disable threading code)",
                          default=None,
                          metavar="N")
    parser.add_option("-c", "--chunksize", dest="chunk_size",
                      default=DEFAULT_X_STRIP_SIZE,
                      metavar="N",
                      help="number of matrix rows to process at a time in each "
                           "parallel job (higher -> more memory usage, "
                           "less parallelism, reduced threading overhead)")
    options, args = parser.parse_args()
    if len(args) != 2:
        parser.error("wrong number of arguments")
    if os.name != "posix":
        options.parallel = 0

    (input_path, output_path) = args
    input_data = loadmat(input_path, matlab_compatible=True)
    # A hack: we stash the big data into a global variable before using
    # multiproessing to fork off threads, so the child processes will inherit
    # the data in shared memory. (This only works on Unix, which is why we
    # disable the multi-threading code on other systems.)
    global _GLOBAL_SHARED_DATA_
    g = {}
    g["eeg_data"] = input_data["eeg_data"]
    # -1 converts from MATLAB-style indexing to Python-style indexing
    g["event_idx"] = input_data["event_idx"].squeeze() - 1
    g["design_matrix"] = input_data["design_matrix"]
    g["pre_event_samples"] = input_data["pre_event_samples"].item()
    g["post_event_samples"] = input_data["post_event_samples"].item()
    g["artifact_starts"] = input_data["artifact_starts"].squeeze() - 1
    # Need a -1 to convert this to 0-based indexing, then a +1 to convert it
    # to Python-style half-open interval ranges instead of MATLAB-style closed
    # interval ranges... which cancel out, so in fact we can use the numbers
    # as is:
    g["artifact_stops"] = input_data["artifact_stops"].squeeze()
    _GLOBAL_SHARED_DATA_ = g

    num_channels = g["eeg_data"].shape[1]
    epoch_len = g["pre_event_samples"] + 1 + g["post_event_samples"]
    X_columns = epoch_len * g["design_matrix"].shape[1]
    XtX_accumulator = np.zeros((X_columns, X_columns))
    XtY_accumulator = np.zeros((X_columns, num_channels))

    if options.parallel == 0:
        # Serial code, in-process
        pool = None
        imap_fn = itertools.imap
    else:
        # With any other value for parallel, we spawn worker processes. (This
        # includes parallel=1. parallel=1 won't be any faster than parallel=0,
        # but it might be useful for testing the worker process code.)
        pool = multiprocessing.Pool(options.parallel)
        imap_fn = pool.imap_unordered

    try:
        for (XtX, XtY) in imap_fn(compute_XtX_XtY_for_slice,
                                  pick_slices(g["eeg_data"].shape[0],
                                              g["artifact_starts"],
                                              g["artifact_stops"],
                                              options.chunksize)):
            XtX_accumulator += XtX
            XtY_accumulator += XtY
    finally:
        if pool is not None:
            pool.terminate()
    betas = np.solve(XtX_accumulator, XtY_accumulator)
    savemat(output_path, {"betas": betas}, oned_as="column")