jiahuimbzuai/CoQuIR huggingface dataset:
//  [python]
def inv_ot(X1, X2, a=None, b=None, eps=0.0, max_iter=10, tol=1e-05):
    m, d1 = X1.shape
    n, d2 = X2.shape

    O = torch.eye(d1).to(X1)

    X1_norm2 = (X1**2).sum(dim=-1, keepdim=True)
    X2_norm2 = (X2**2).sum(dim=-1, keepdim=True)
    norms2 = X1_norm2 + X2_norm2.T

    P_old = X1.new_zeros((m, n))
    if a is None:
        a = X1.new_ones((m,)) / m
    if b is None:
        b = X2.new_ones((n,)) / n
    v_ot = None

    for i in range(max_iter):
        dot = torch.mm(X1.mm(O), X2.T)  # m x n
        dist = norms2 - 2 * dot
        P, v_ot = sinkhorn(dist, a=a, b=b, v=v_ot, return_v=True, eps=eps, max_iter=5)

        # update O
        O = X1.T.mm(P.mm(X2))
        u, s, v = torch.svd(O)
        O = u.mm(v)

        if torch.norm(P_old - P) < tol:
            break
        P_old = P
        err = torch.sum(dist * P)

    return P, O
//  [python]
def inv_ot(X1, X2, a=None, b=None, eps=0.0, max_iter=10, tol=1e-05):
    m, d1 = X1.shape
    n, d2 = X2.shape

    O = torch.eye(d1).to(X1)

    X1_norm2 = (X1**2).sum(dim=-1, keepdim=True)
    X2_norm2 = (X2**2).sum(dim=-1, keepdim=True)
    norms2 = X1_norm2 + X2_norm2.T

    P_old = X1.new_zeros((m, n))
    if a is None:
        a = X1.new_ones((m,)) / m
    if b is None:
        b = X2.new_ones((n,)) / n
    v_ot = None

    for i in range(max_iter):
        dot = torch.mm(X1.mm(O), X2.T)  # m x n
        dist = norms2 - 2 * dot
        P, v_ot = sinkhorn(dist, a=a, b=b, v=v_ot, return_v=True, eps=eps, max_iter=5)

        # update O
        O = X1.T.mm(P.mm(X2))
        u, s, v = torch.linalg.svd(O)
        O = u.mm(v)

        if torch.norm(P_old - P) < tol:
            break
        P_old = P
        err = torch.sum(dist * P)

    return P, O
//  [python]
def _orthogonalize(X):
    U, S, _ = torch.svd(X, some=False)
    return U*S
//  [python]
def _orthogonalize(X):
    U, S, _ = torch.linalg.svd(X, full_matrices=False)
    return U*S
//  [python]
def get_nearest_centroid(cameras: PerspectiveCameras):
    '''
    Given PyTorch3D cameras, find the nearest point along their principal ray
    '''

    #@ GET CAMERA CENTERS AND DIRECTIONS
    camera_centers = cameras.get_camera_center()

    c_mean = (cameras.principal_point).mean(dim=0)
    xy_grid = c_mean.unsqueeze(0).unsqueeze(0)
    ray_vis = _xy_to_ray_bundle(cameras, xy_grid.expand(len(cameras),-1,-1), 1.0, 15.0, 20, True)
    camera_directions = ray_vis.directions

    #@ CONSTRUCT MATRICIES
    A = torch.zeros((3*len(cameras)), len(cameras)+3)
    b = torch.zeros((3*len(cameras), 1))
    A[:,:3] = torch.eye(3).repeat(len(cameras),1)
    for ci in range(len(camera_directions)):
        A[3*ci:3*ci+3, ci+3] = -camera_directions[ci]
        b[3*ci:3*ci+3, 0] = camera_centers[ci]
    #' A (3*N, 3*N+3)   b (3*N, 1)

    #@ SVD
    U, s, VT = torch.svd(A)
    Sinv = torch.diag(1/s)
    if len(s) < 3*len(cameras):
        Sinv = torch.cat((Sinv, torch.zeros((Sinv.shape[0], 3*len(cameras) - Sinv.shape[1]), device=Sinv.device)), dim=1)
    x = torch.matmul(VT.T, torch.matmul(Sinv,torch.matmul(U.T, b)))
    
    centroid = x[:3,0]
    return centroid
//  [python]
def get_nearest_centroid(cameras: PerspectiveCameras):
    '''
    Given PyTorch3D cameras, find the nearest point along their principal ray
    '''

    #@ GET CAMERA CENTERS AND DIRECTIONS
    camera_centers = cameras.get_camera_center()

    c_mean = (cameras.principal_point).mean(dim=0)
    xy_grid = c_mean.unsqueeze(0).unsqueeze(0)
    ray_vis = _xy_to_ray_bundle(cameras, xy_grid.expand(len(cameras),-1,-1), 1.0, 15.0, 20, True)
    camera_directions = ray_vis.directions

    #@ CONSTRUCT MATRICIES
    A = torch.zeros((3*len(cameras)), len(cameras)+3)
    b = torch.zeros((3*len(cameras), 1))
    A[:,:3] = torch.eye(3).repeat(len(cameras),1)
    for ci in range(len(camera_directions)):
        A[3*ci:3*ci+3, ci+3] = -camera_directions[ci]
        b[3*ci:3*ci+3, 0] = camera_centers[ci]
    #' A (3*N, 3*N+3)   b (3*N, 1)

    #@ SVD
    U, s, VT = torch.linalg.svd(A)
    Sinv = torch.diag(1/s)
    if len(s) < 3*len(cameras):
        Sinv = torch.cat((Sinv, torch.zeros((Sinv.shape[0], 3*len(cameras) - Sinv.shape[1]), device=Sinv.device)), dim=1)
    x = torch.matmul(VT.T, torch.matmul(Sinv,torch.matmul(U.T, b)))
    
    centroid = x[:3,0]
    return centroid
//  [python]
def svd(
    x: torch.Tensor, /, *, full_matrices: bool = True, compute_uv: bool = True
) -> Union[torch.Tensor, Tuple[torch.Tensor, ...]]:
    if compute_uv:
        results = namedtuple("svd", "U S Vh")

        U, S, Vh = torch.svd(x, some=full_matrices)
        return results(U, S, Vh)
    else:
        results = namedtuple("svd", "S")
        U, S, Vh = torch.svd(x, some=full_matrices)
        D = S
        return results(D)
//  [python]
def svd(
    x: torch.Tensor, /, *, full_matrices: bool = True, compute_uv: bool = True
) -> Union[torch.Tensor, Tuple[torch.Tensor, ...]]:
    if compute_uv:
        results = namedtuple("svd", "U S Vh")

        U, D, VT = torch.linalg.svd(x, full_matrices=full_matrices)
        return results(U, D, VT)
    else:
        results = namedtuple("svd", "S")
        svd = torch.linalg.svd(x, full_matrices=full_matrices)
        # torch.linalg.svd returns a tuple with U, S, and Vh
        D = svd[1]
        return results(D)
//  [python]
def batch_kabsch_torch(A, B):
    '''
    A: [B, N, 3]
    B: [B, N, 3]
    '''
    a_mean = A.mean(dim=1, keepdims=True)
    b_mean = B.mean(dim=1, keepdims=True)
    A_c = A - a_mean
    B_c = B - b_mean
    # Covariance matrix
    H = torch.bmm(A_c.transpose(1, 2), B_c)  # [B, 3, 3]
    U, S, Vt = torch.svd(H)  # [B, 3, 3]
    V = Vt.transpose(1, 2)
    # rms
    d = ((torch.det(U) * torch.det(V)) < 0.0).long()  # [B]
    nSS = torch.diag_embed(torch.tensor([1. for _ in range(len(U))], device=U.device, dtype=U.dtype))
    SS = torch.diag_embed(torch.tensor([1. for _ in range(len(U) - 1)] + [-1.], device=U.device, dtype=U.dtype))
    bSS = torch.stack([nSS, SS], dim=0)[d]  # [B, 3, 3]
    U = torch.bmm(U, bSS)
    # Rotation matrix
    R = torch.bmm(V, U.transpose(1, 2))  # [B, 3, 3]
    # Translation vector
    t = b_mean - torch.bmm(R, a_mean.transpose(1, 2)).transpose(1, 2)
    A_aligned = torch.bmm(R, A.transpose(1, 2)).transpose(1, 2) + t
    return A_aligned, R, t
//  [python]
def batch_kabsch_torch(A, B):
    '''
    A: [B, N, 3]
    B: [B, N, 3]
    '''
    a_mean = A.mean(dim=1, keepdims=True)
    b_mean = B.mean(dim=1, keepdims=True)
    A_c = A - a_mean
    B_c = B - b_mean
    # Covariance matrix
    H = torch.bmm(A_c.transpose(1, 2), B_c)  # [B, 3, 3]
    U, S, Vt = torch.linalg.svd(H)  # [B, 3, 3]
    V = Vt.transpose(1, 2)
    # rms
    d = ((torch.linalg.det(U) * torch.linalg.det(V)) < 0.0).long()  # [B]
    nSS = torch.diag(torch.tensor([1. for _ in range(len(U))], device=U.device, dtype=U.dtype))
    SS = torch.diag(torch.tensor([1. for _ in range(len(U) - 1)] + [-1.], device=U.device, dtype=U.dtype))
    bSS = torch.stack([nSS, SS], dim=0)[d]  # [B, 3, 3]
    U = torch.bmm(U, bSS)
    # Rotation matrix
    R = torch.bmm(V, U.transpose(1, 2))  # [B, 3, 3]
    # Translation vector
    t = b_mean - torch.bmm(R, a_mean.transpose(1, 2)).transpose(1, 2)
    A_aligned = torch.bmm(R, A.transpose(1, 2)).transpose(1, 2) + t
    return A_aligned, R, t
//  [python]
def orthogonal_procrustes(a, b, cancel_reflection: bool = False):
    svd_driver = "gesvd" if a.is_cuda else None
    u, _, v_t = torch.svd(a.T @ b, some=True)

    if cancel_reflection:
        u[:, -1] /= torch.det(u) * torch.det(v_t)

    transform = u @ v_t
    if not torch.isfinite(u).all():
        raise ValueError(
            f"determinant error: {torch.det(transform)}. "
            'This can happen when merging on the CPU with the "rotate" method. '
            "Consider merging on a cuda device, "
            "or try setting alpha to 1 for the problematic blocks. "
            "See this related discussion for more info: "
            "https://github.com/s1dlx/meh/pull/50#discussion_r1429469484"
        )

    return transform
//  [python]
def orthogonal_procrustes(a, b, cancel_reflection: bool = False):
    svd_driver = "gesvd" if a.is_cuda else None
    u, _, v_t = torch.linalg.svd(a.T @ b, driver=svd_driver)

    if cancel_reflection:
        u[:, -1] /= torch.det(u) * torch.det(v_t)

    transform = u @ v_t
    if not torch.isfinite(u).all():
        raise ValueError(
            f"determinant error: {torch.det(transform)}. "
            'This can happen when merging on the CPU with the "rotate" method. '
            "Consider merging on a cuda device, "
            "or try setting alpha to 1 for the problematic blocks. "
            "See this related discussion for more info: "
            "https://github.com/s1dlx/meh/pull/50#discussion_r1429469484"
        )

    return transform
//  [python]
def triangulate_batch(points, camera_mats, scores=None):
    """Given a CxNx2 array of points and Cx4x4 array of extrinsics,
    this returns a Nx3 array of points,
    where N is the number of points and C is the number of cameras.
    Optionally also takes a CxN array of scores for points."""
    # we build an A matrix that is (num_cams, num_points, 2d, 4)
    num_cams = len(camera_mats)
    A_base = camera_mats[:, None, 2:3].broadcast_to(num_cams, 1, 2, 4)
    A = points[:, :, :,None] * A_base - camera_mats[:, None, :2]
    if scores is not None:
        A = A * scores[:, :, None, None]
    # now shape A matrix to (num_points, num_cams*2, 4)
    A = A.swapaxes(0, 1).reshape(-1, num_cams*2, 4)
    u, s, vh = torch.svd(A)
    p3d = vh[:, -1]
    p3d = p3d[:, :3] / p3d[:,3:4]
    return p3d
//  [python]
def triangulate_batch(points, camera_mats, scores=None):
    """Given a CxNx2 array of points and Cx4x4 array of extrinsics,
    this returns a Nx3 array of points,
    where N is the number of points and C is the number of cameras.
    Optionally also takes a CxN array of scores for points."""
    # we build an A matrix that is (num_cams, num_points, 2d, 4)
    num_cams = len(camera_mats)
    A_base = camera_mats[:, None, 2:3].broadcast_to(num_cams, 1, 2, 4)
    A = points[:, :, :,None] * A_base - camera_mats[:, None, :2]
    if scores is not None:
        A = A * scores[:, :, None, None]
    # now shape A matrix to (num_points, num_cams*2, 4)
    A = A.swapaxes(0, 1).reshape(-1, num_cams*2, 4)
    u, s, vh = torch.linalg.svd(A, full_matrices=True)
    p3d = vh[:, -1]
    p3d = p3d[:, :3] / p3d[:,3:4]
    return p3d
//  [python]
def test_svd(self):
        linear_op = self.create_linear_op().detach().requires_grad_(True)
        linear_op_copy = torch.clone(linear_op).detach().requires_grad_(True)
        evaluated = self.evaluate_linear_op(linear_op_copy)

        # Perform forward pass
        U_unsorted, S_unsorted, Vt_unsorted = torch.svd(linear_op)
        U_unsorted, V_unsorted = U_unsorted.to_dense(), Vt_unsorted.to_dense()

        # since LinearOperator.svd does not sort the singular values, we do this here for the check
        S, idxr = torch.sort(S_unsorted, dim=-1, descending=True)
        idxr = idxr.unsqueeze(-2).expand(U_unsorted.shape)
        U = torch.gather(U_unsorted, dim=-1, index=idxr)
        Vt = torch.gather(V_unsorted, dim=-2, index=idxr.mT)

        # compute expected result from full tensor
        U_actual, S_actual, Vt_actual = torch.svd(evaluated.double())
        U_actual = U_actual.to(dtype=evaluated.dtype)
        S_actual = S_actual.to(dtype=evaluated.dtype)
        Vt_actual = Vt_actual.to(dtype=evaluated.dtype)

        # Check forward pass
        self.assertAllClose(S, S_actual, **self.tolerances["svd"])
        lt_from_svd = U @ torch.diag_embed(S) @ Vt
        self.assertAllClose(lt_from_svd, evaluated, **self.tolerances["svd"])

        # if there are repeated singular values, we'll skip checking the singular vectors
        U_abs, U_actual_abs = U.abs(), U_actual.abs()
        Vt_abs, Vt_actual_abs = Vt.abs(), Vt_actual.abs()
        any_svals_repeated = False
        for idx in product(*[range(b) for b in S_actual.shape[:-1]]):
            Si = S_actual[idx]
            if torch.unique(Si.detach()).shape[-1] == Si.shape[-1]:  # detach to avoid pytorch/pytorch#41389
                self.assertAllClose(U_abs[idx], U_actual_abs[idx], **self.tolerances["svd"])
                self.assertAllClose(Vt_abs[idx], Vt_actual_abs[idx], **self.tolerances["svd"])
            else:
                any_svals_repeated = True

        # Perform backward pass
        svd_grad = torch.randn_like(S)
        ((S * svd_grad).sum()).backward()
        ((S_actual * svd_grad).sum()).backward()

        # Check grads if there were no repeated singular values
        if not any_svals_repeated:
            for arg, arg_copy in zip(linear_op.representation(), linear_op_copy.representation()):
                if arg_copy.requires_grad and arg_copy.is_leaf and arg_copy.grad is not None:
                    self.assertAllClose(arg.grad, arg_copy.grad, **self.tolerances["svd"])
//  [python]
def test_svd(self):
        linear_op = self.create_linear_op().detach().requires_grad_(True)
        linear_op_copy = torch.clone(linear_op).detach().requires_grad_(True)
        evaluated = self.evaluate_linear_op(linear_op_copy)

        # Perform forward pass
        U_unsorted, S_unsorted, Vt_unsorted = torch.linalg.svd(linear_op)
        U_unsorted, V_unsorted = U_unsorted.to_dense(), Vt_unsorted.to_dense()

        # since LinearOperator.svd does not sort the singular values, we do this here for the check
        S, idxr = torch.sort(S_unsorted, dim=-1, descending=True)
        idxr = idxr.unsqueeze(-2).expand(U_unsorted.shape)
        U = torch.gather(U_unsorted, dim=-1, index=idxr)
        Vt = torch.gather(V_unsorted, dim=-2, index=idxr.mT)

        # compute expected result from full tensor
        U_actual, S_actual, Vt_actual = torch.linalg.svd(evaluated.double())
        U_actual = U_actual.to(dtype=evaluated.dtype)
        S_actual = S_actual.to(dtype=evaluated.dtype)
        Vt_actual = Vt_actual.to(dtype=evaluated.dtype)

        # Check forward pass
        self.assertAllClose(S, S_actual, **self.tolerances["svd"])
        lt_from_svd = U @ torch.diag_embed(S) @ Vt
        self.assertAllClose(lt_from_svd, evaluated, **self.tolerances["svd"])

        # if there are repeated singular values, we'll skip checking the singular vectors
        U_abs, U_actual_abs = U.abs(), U_actual.abs()
        Vt_abs, Vt_actual_abs = Vt.abs(), Vt_actual.abs()
        any_svals_repeated = False
        for idx in product(*[range(b) for b in S_actual.shape[:-1]]):
            Si = S_actual[idx]
            if torch.unique(Si.detach()).shape[-1] == Si.shape[-1]:  # detach to avoid pytorch/pytorch#41389
                self.assertAllClose(U_abs[idx], U_actual_abs[idx], **self.tolerances["svd"])
                self.assertAllClose(Vt_abs[idx], Vt_actual_abs[idx], **self.tolerances["svd"])
            else:
                any_svals_repeated = True

        # Perform backward pass
        svd_grad = torch.randn_like(S)
        ((S * svd_grad).sum()).backward()
        ((S_actual * svd_grad).sum()).backward()

        # Check grads if there were no repeated singular values
        if not any_svals_repeated:
            for arg, arg_copy in zip(linear_op.representation(), linear_op_copy.representation()):
                if arg_copy.requires_grad and arg_copy.is_leaf and arg_copy.grad is not None:
                    self.assertAllClose(arg.grad, arg_copy.grad, **self.tolerances["svd"])
//  [python]
def orthonormal(inp: typing.Union[torch.Tensor, torch.nn.Parameter, typing.List[int]], gain: float):
    original_input = inp
    if isinstance(inp, list):
        inp = torch.zeros(inp)
    if isinstance(inp, torch.nn.Parameter):
        inp = inp.data
    flat_shape = (inp.shape[0], np.prod(inp.shape[1:]))
    a = torch.rand(flat_shape)
    u, _, v = torch.svd(a)
    inp.copy_((u if u.shape == flat_shape else v).reshape(inp.shape).mul(gain).to(device=inp.device, dtype=inp.dtype))
    if isinstance(original_input, list):
        return torch.nn.Parameter(inp)
    return original_input
//  [python]
def orthonormal(inp: typing.Union[torch.Tensor, torch.nn.Parameter, typing.List[int]], gain: float):
    original_input = inp
    if isinstance(inp, list):
        inp = torch.zeros(inp)
    if isinstance(inp, torch.nn.Parameter):
        inp = inp.data
    flat_shape = (inp.shape[0], np.prod(inp.shape[1:]))
    a = torch.rand(flat_shape)
    u, _, v = torch.linalg.svd(a, full_matrices=False)
    inp.copy_((u if u.shape == flat_shape else v).reshape(inp.shape).mul(gain).to(device=inp.device, dtype=inp.dtype))
    if isinstance(original_input, list):
        return torch.nn.Parameter(inp)
    return original_input
//  [python]
def svd_threshold(self, M, tau):
        U, S, V = torch.svd(M)
        return U@(torch.diag(self.shrink(S, tau))@V)
//  [python]
def svd_threshold(self, M, tau):
        U, S, V = torch.linalg.svd(M, full_matrices=False)
        return U@(torch.diag(self.shrink(S, tau))@V)
//  [python]
def fit(self, dataset: Tensor) -> None:
        """Fit a Gaussian model to dataset X.

        Covariance matrix is not calculated directly using:
        ``C = X.X^T``
        Instead, it is represented in terms of the Singular Value Decomposition of X:
        ``X = U.S.V^T``
        Hence,
        ``C = U.S^2.U^T``
        This simplifies the calculation of the log-likelihood without requiring full matrix inversion.

        Args:
            dataset (Tensor): Input dataset to fit the model.
        """

        num_samples = dataset.shape[1]
        self.mean_vec = torch.mean(dataset, dim=1)
        data_centered = (dataset - self.mean_vec.reshape(-1, 1)) / math.sqrt(num_samples)
        self.u_mat, self.sigma_mat, _ = torch.svd(data_centered)
//  [python]
def fit(self, dataset: Tensor) -> None:
        """Fit a Gaussian model to dataset X.

        Covariance matrix is not calculated directly using:
        ``C = X.X^T``
        Instead, it is represented in terms of the Singular Value Decomposition of X:
        ``X = U.S.V^T``
        Hence,
        ``C = U.S^2.U^T``
        This simplifies the calculation of the log-likelihood without requiring full matrix inversion.

        Args:
            dataset (Tensor): Input dataset to fit the model.
        """

        num_samples = dataset.shape[1]
        self.mean_vec = torch.mean(dataset, dim=1)
        data_centered = (dataset - self.mean_vec.reshape(-1, 1)) / math.sqrt(num_samples)
        self.u_mat, self.sigma_mat, _ = torch.linalg.svd(data_centered, full_matrices=False)
//  [python]
def get_interpolated_path(cameras: PerspectiveCameras, n=50, method='circle', theta_offset_max=0.0):
    '''
    Given a camera object containing a set of cameras, fit a circle and get 
    interpolated cameras

    Args:
        cameras (PyTorch3D Camera): input camera object
        n (int): length of cameras in new path
        method (str): 'circle'
        theta_offset_max (int): max camera jitter in radians

    Returns:
        path_cameras (PyTorch3D Camera): interpolated cameras
    '''
    device = cameras.device
    cameras = cameras.cpu()

    if method == 'circle':

        #@ https://meshlogic.github.io/posts/jupyter/curve-fitting/fitting-a-circle-to-cluster-of-3d-points/
        #@ Fit plane
        P = cameras.get_camera_center().cpu()
        P_mean = P.mean(axis=0)
        P_centered = P - P_mean
        U,s,V = torch.svd(P_centered)
        normal = V[2,:]
        if (normal*2 - P_mean).norm() < (normal - P_mean).norm():
            normal = - normal
        d = -torch.dot(P_mean, normal)  # d = -<p,n>    

        #@ Project pts to plane
        P_xy = rodrigues_rot(P_centered, normal, torch.tensor([0.0,0.0,1.0]))
        
        #@ Fit circle in 2D
        xc, yc, r = fit_circle_2d(P_xy[:,0], P_xy[:,1])
        t = torch.linspace(0, 2*math.pi, 100)
        xx = xc + r*torch.cos(t)
        yy = yc + r*torch.sin(t)

        #@ Project circle to 3D
        C = rodrigues_rot(torch.tensor([xc,yc,0.0]), torch.tensor([0.0,0.0,1.0]), normal) + P_mean
        C = C.flatten()

        #@ Get pts n 3D
        t = torch.linspace(0, 2*math.pi, n)
        u = P[0] - C
        new_camera_centers = generate_circle_by_vectors(t, C, r, normal, u)

        #@ OPTIONAL THETA OFFSET
        if theta_offset_max > 0.0:
            aug_theta = (torch.rand((new_camera_centers.shape[0])) * (2*theta_offset_max)) - theta_offset_max
            new_camera_centers = rodrigues_rot2(new_camera_centers, normal, aug_theta)

        #@ Get camera look at
        new_camera_look_at = get_nearest_centroid(cameras)

        #@ Get R T
        up_vec = -normal
        R, T = look_at_view_transform(eye=new_camera_centers, at=new_camera_look_at.unsqueeze(0), up=up_vec.unsqueeze(0), device=cameras.device)
    else:
        raise NotImplementedError
    
    c = (cameras.principal_point).mean(dim=0, keepdim=True).expand(R.shape[0],-1)
    f = (cameras.focal_length).mean(dim=0, keepdim=True).expand(R.shape[0],-1)
    image_size = cameras.image_size[:1].expand(R.shape[0],-1)


    path_cameras = PerspectiveCameras(R=R,T=T,focal_length=f,principal_point=c,image_size=image_size, device=device)
    cameras = cameras.to(device)
    return path_cameras
//  [python]
def get_interpolated_path(cameras: PerspectiveCameras, n=50, method='circle', theta_offset_max=0.0):
    '''
    Given a camera object containing a set of cameras, fit a circle and get 
    interpolated cameras

    Args:
        cameras (PyTorch3D Camera): input camera object
        n (int): length of cameras in new path
        method (str): 'circle'
        theta_offset_max (int): max camera jitter in radians

    Returns:
        path_cameras (PyTorch3D Camera): interpolated cameras
    '''
    device = cameras.device
    cameras = cameras.cpu()

    if method == 'circle':

        #@ https://meshlogic.github.io/posts/jupyter/curve-fitting/fitting-a-circle-to-cluster-of-3d-points/
        #@ Fit plane
        P = cameras.get_camera_center().cpu()
        P_mean = P.mean(axis=0)
        P_centered = P - P_mean
        U,s,V = torch.linalg.svd(P_centered)
        normal = V[2,:]
        if (normal*2 - P_mean).norm() < (normal - P_mean).norm():
            normal = - normal
        d = -torch.dot(P_mean, normal)  # d = -<p,n>    

        #@ Project pts to plane
        P_xy = rodrigues_rot(P_centered, normal, torch.tensor([0.0,0.0,1.0]))
        
        #@ Fit circle in 2D
        xc, yc, r = fit_circle_2d(P_xy[:,0], P_xy[:,1])
        t = torch.linspace(0, 2*math.pi, 100)
        xx = xc + r*torch.cos(t)
        yy = yc + r*torch.sin(t)

        #@ Project circle to 3D
        C = rodrigues_rot(torch.tensor([xc,yc,0.0]), torch.tensor([0.0,0.0,1.0]), normal) + P_mean
        C = C.flatten()

        #@ Get pts n 3D
        t = torch.linspace(0, 2*math.pi, n)
        u = P[0] - C
        new_camera_centers = generate_circle_by_vectors(t, C, r, normal, u)

        #@ OPTIONAL THETA OFFSET
        if theta_offset_max > 0.0:
            aug_theta = (torch.rand((new_camera_centers.shape[0])) * (2*theta_offset_max)) - theta_offset_max
            new_camera_centers = rodrigues_rot2(new_camera_centers, normal, aug_theta)

        #@ Get camera look at
        new_camera_look_at = get_nearest_centroid(cameras)

        #@ Get R T
        up_vec = -normal
        R, T = look_at_view_transform(eye=new_camera_centers, at=new_camera_look_at.unsqueeze(0), up=up_vec.unsqueeze(0), device=cameras.device)
    else:
        raise NotImplementedError
    
    c = (cameras.principal_point).mean(dim=0, keepdim=True).expand(R.shape[0],-1)
    f = (cameras.focal_length).mean(dim=0, keepdim=True).expand(R.shape[0],-1)
    image_size = cameras.image_size[:1].expand(R.shape[0],-1)


    path_cameras = PerspectiveCameras(R=R,T=T,focal_length=f,principal_point=c,image_size=image_size, device=device)
    cameras = cameras.to(device)
    return path_cameras
//  [python]
def svd_centroid_conversion(centroids):
    U, S, Vh = torch.svd(centroids.transpose(0,1))
    # print(U.shape, S.shape, Vh.shape)
    # print(S, float(0.95*torch.sum(S)))

    # print(U.transpose(0,1),centroids)
    ind = 0
    for i in range(centroids.shape[0]):
        if i==0:
            ind = i
            if float(torch.sum(S[0]))>float(0.95*torch.sum(S)):
                break
        else:
            if float(torch.sum(S[:i+1]))>float(0.95*torch.sum(S)):
                break
            else:
                ind = i
    # print(ind, U[:, :ind].shape)#, Vh)

    cent = U[:, :ind] @ torch.diag(S[:ind]) @ Vh[:ind, :]
    return cent.transpose(0,1)
//  [python]
def svd_centroid_conversion(centroids):
    U, S, Vh = torch.linalg.svd(centroids.transpose(0,1), full_matrices=False)
    # print(U.shape, S.shape, Vh.shape)
    # print(S, float(0.95*torch.sum(S)))

    # print(U.transpose(0,1),centroids)
    ind = 0
    for i in range(centroids.shape[0]):
        if i==0:
            ind = i
            if float(torch.sum(S[0]))>float(0.95*torch.sum(S)):
                break
        else:
            if float(torch.sum(S[:i+1]))>float(0.95*torch.sum(S)):
                break
            else:
                ind = i
    # print(ind, U[:, :ind].shape)#, Vh)

    cent = U[:, :ind] @ torch.diag(S[:ind]) @ Vh[:ind, :]
    return cent.transpose(0,1)
//  [python]
def projx(cls, x):
        U, _, Vh = torch.svd(x)
        val = U @ Vh
        return val / val.det()[..., None, None].pow(1/3)
//  [python]
def projx(cls, x):
        U, _, Vh = torch.linalg.svd(x, full_matrices=False)
        val = U @ Vh
        return val / val.det()[..., None, None].pow(1/3)
//  [python]
def svd(a: Numeric, compute_uv: bool = True):
    u, s, v = torch.svd(a, some=False)
    return (u, s, v) if compute_uv else s
//  [python]
def svd(a: Numeric, compute_uv: bool = True):
    u, s, v = torch.linalg.svd(a, full_matrices=False)
    return (u, s, v) if compute_uv else s
//  [python]
def test_single_value_cal():
    A = torch.ones(2,2)
    print("A is {}".format(A))
    smallest, largest = get_singular_values(A)
    print("smallest is {}, largest is {}".format(smallest, largest))
    print(torch.svd(A))



    A = torch.rand(5,5)
    print("A is {}".format(A))
    smallest, largest = get_singular_values(A)
    print("smallest is {}, largest is {}".format(smallest, largest))
    print(torch.svd(A))
//  [python]
def test_single_value_cal():
    A = torch.ones(2,2)
    print("A is {}".format(A))
    smallest, largest = get_singular_values(A)
    print("smallest is {}, largest is {}".format(smallest, largest))
    print(torch.linalg.svd(A))



    A = torch.rand(5,5)
    print("A is {}".format(A))
    smallest, largest = get_singular_values(A)
    print("smallest is {}, largest is {}".format(smallest, largest))
    print(torch.linalg.svd(A))
//  [python]
def fit_transform(self, dataset: torch.Tensor) -> torch.Tensor:
        """Fit and transform PCA to dataset.

        Args:
            dataset (torch.Tensor): Dataset to which the PCA if fit and transformed

        Returns:
            Transformed dataset

        Example:
            >>> pca.fit_transform(embedding)
            >>> transformed_embedding = pca.fit_transform(embedding)
            >>> transformed_embedding.shape
            torch.Size([1000, 2])
        """
        mean = dataset.mean(dim=0)
        dataset -= mean
        num_components = int(self.n_components)
        self.num_components = torch.Tensor([num_components])

        v_h = torch.svd(dataset)[-1]
        self.singular_vectors = v_h.transpose(-2, -1)[:, :num_components]
        self.mean = mean

        return torch.matmul(dataset, self.singular_vectors)
//  [python]
def fit_transform(self, dataset: torch.Tensor) -> torch.Tensor:
        """Fit and transform PCA to dataset.

        Args:
            dataset (torch.Tensor): Dataset to which the PCA if fit and transformed

        Returns:
            Transformed dataset

        Example:
            >>> pca.fit_transform(embedding)
            >>> transformed_embedding = pca.fit_transform(embedding)
            >>> transformed_embedding.shape
            torch.Size([1000, 2])
        """
        mean = dataset.mean(dim=0)
        dataset -= mean
        num_components = int(self.n_components)
        self.num_components = torch.Tensor([num_components])

        v_h = torch.linalg.svd(dataset)[-1]
        self.singular_vectors = v_h.transpose(-2, -1)[:, :num_components]
        self.mean = mean

        return torch.matmul(dataset, self.singular_vectors)
//  [python]
def fit(self, dataset: torch.Tensor) -> None:
        """Fits the PCA model to the dataset.

        Args:
          dataset (torch.Tensor): Input dataset to fit the model.

        Example:
            >>> pca.fit(embedding)
            >>> pca.singular_vectors
            tensor([9.6053, 9.2763], device='cuda:0')

            >>> pca.mean
            tensor([0.4859, 0.4959, 0.4906, 0.5010, 0.5042], device='cuda:0')
        """
        mean = dataset.mean(dim=0)
        dataset -= mean

        U, S, V = torch.svd(dataset.double())
        num_components: int
        if self.n_components <= 1:
            variance_ratios = torch.cumsum(S * S, dim=0) / torch.sum(S * S)
            num_components = torch.nonzero(variance_ratios >= self.n_components)[0]
        else:
            num_components = int(self.n_components)

        self.num_components = torch.Tensor([num_components])

        self.singular_vectors = V.transpose(-2, -1)[:, :num_components].float()
        self.singular_values = S[:num_components].float()
        self.mean = mean
//  [python]
def fit(self, dataset: torch.Tensor) -> None:
        """Fits the PCA model to the dataset.

        Args:
          dataset (torch.Tensor): Input dataset to fit the model.

        Example:
            >>> pca.fit(embedding)
            >>> pca.singular_vectors
            tensor([9.6053, 9.2763], device='cuda:0')

            >>> pca.mean
            tensor([0.4859, 0.4959, 0.4906, 0.5010, 0.5042], device='cuda:0')
        """
        mean = dataset.mean(dim=0)
        dataset -= mean

        _, sig, v_h = torch.linalg.svd(dataset.double(), full_matrices=False)
        num_components: int
        if self.n_components <= 1:
            variance_ratios = torch.cumsum(sig * sig, dim=0) / torch.sum(sig * sig)
            num_components = torch.nonzero(variance_ratios >= self.n_components)[0]
        else:
            num_components = int(self.n_components)

        self.num_components = torch.Tensor([num_components])

        self.singular_vectors = v_h.transpose(-2, -1)[:, :num_components].float()
        self.singular_values = sig[:num_components].float()
        self.mean = mean
//  [python]
def _torch_svd_cast(input: Tensor) -> Tuple[Tensor, Tensor, Tensor]:
    """Helper function to make torch.svd work with other than fp32/64.

    The function torch.svd is only implemented for fp32/64 which makes
    impossible to be used by fp16 or others. What this function does, is cast
    input data type to fp32, apply torch.svd, and cast back to the input dtype.

    NOTE: in torch 1.8.1 this function is recommended to use as torch.linalg.svd
    """
    # if not isinstance(input, torch.Tensor):
    #    raise AssertionError(f"Input must be torch.Tensor. Got: {type(input)}.")
    dtype = input.dtype
    if dtype not in (torch.float32, torch.float64):
        dtype = torch.float32

    out1, out2, out3H = torch.svd(input.to(dtype))
    if torch_version_ge(1, 11):
        out3 = out3H.mH
    else:
        out3 = out3H.transpose(-1, -2)
    return (out1.to(input.dtype), out2.to(input.dtype), out3.to(input.dtype))
//  [python]
def _torch_svd_cast(input: Tensor) -> Tuple[Tensor, Tensor, Tensor]:
    """Helper function to make torch.svd work with other than fp32/64.

    The function torch.svd is only implemented for fp32/64 which makes
    impossible to be used by fp16 or others. What this function does, is cast
    input data type to fp32, apply torch.svd, and cast back to the input dtype.

    NOTE: in torch 1.8.1 this function is recommended to use as torch.linalg.svd
    """
    # if not isinstance(input, torch.Tensor):
    #    raise AssertionError(f"Input must be torch.Tensor. Got: {type(input)}.")
    dtype = input.dtype
    if dtype not in (torch.float32, torch.float64):
        dtype = torch.float32

    out1, out2, out3H = torch.linalg.svd(input.to(dtype))
    if torch_version_ge(1, 11):
        out3 = out3H.mH
    else:
        out3 = out3H.transpose(-1, -2)
    return (out1.to(input.dtype), out2.to(input.dtype), out3.to(input.dtype))
//  [python]
def solve_SVD(fixed_pnts: torch.Tensor, moving_pnts: torch.Tensor,
              fixed_cm: torch.Tensor, moving_cm: torch.Tensor):
    """
    Solve rigid motion using least suqare method with SVD
    Args:
        fixed_pnts: torch.Tensor, points in fixed image
        moving_pnts: torch.Tensor, corresponding points in moving image
        fixed_cm: torch.Tensor, center mass of fixed image
        moving_cm: torch.Tensor, center mass of moving image

    Returns:
        R: torch.Tensor, rotation matrix
        t: torch.Tensor, translation vector
    """
    # demean the point clouds
    fixed_cm_rw = fixed_pnts - fixed_cm
    moving_cm_rw = moving_pnts - moving_cm

    # solve rotation and translation with SVD
    H = fixed_cm_rw @ moving_cm_rw.T
    U, S, Vt = torch.svd(H)

    R = Vt.T @ U.T
    if torch.det(R) < 0:
        Vt[2, :] *= -1
        R = Vt.T @ U.T

    t = -R @ fixed_cm + moving_cm
    return R, t
//  [python]
def solve_SVD(fixed_pnts: torch.Tensor, moving_pnts: torch.Tensor,
              fixed_cm: torch.Tensor, moving_cm: torch.Tensor):
    """
    Solve rigid motion using least suqare method with SVD
    Args:
        fixed_pnts: torch.Tensor, points in fixed image
        moving_pnts: torch.Tensor, corresponding points in moving image
        fixed_cm: torch.Tensor, center mass of fixed image
        moving_cm: torch.Tensor, center mass of moving image

    Returns:
        R: torch.Tensor, rotation matrix
        t: torch.Tensor, translation vector
    """
    # demean the point clouds
    fixed_cm_rw = fixed_pnts - fixed_cm
    moving_cm_rw = moving_pnts - moving_cm

    # solve rotation and translation with SVD
    H = fixed_cm_rw @ moving_cm_rw.T
    U, S, Vt = torch.linalg.svd(H, full_matrices=True)

    R = Vt.T @ U.T
    if torch.linalg.det(R) < 0:
        Vt[2, :] *= -1
        R = Vt.T @ U.T

    t = -R @ fixed_cm + moving_cm
    return R, t
//  [python]
def __init__(self, layer, scaling_diag_matrix, ratio, name, direct_update=False):
        self.layer = layer
        self.name = name
        self.dev = self.layer.weight.device
        # W = layer.weight.data.clone()
        W = layer.weight.data.clone()
        self.rows = W.shape[0]
        self.columns = W.shape[1]
        if direct_update:
            self.U, self.S, self.VT = torch.svd(W.data)
        else: 
            try:
                scaling_matrix_inv = torch.inverse(scaling_diag_matrix)
            except Exception as e:
                print("Warning: scaling_diag_matrix is not full rank!")
                scaling_diag_matrix += 1e-6 * torch.eye(scaling_diag_matrix.shape[0])
                scaling_matrix_inv = torch.inverse(scaling_diag_matrix)
            scaling_diag_matrix = scaling_diag_matrix.float()
            scaling_matrix_inv = scaling_matrix_inv.float()
            W_scale = torch.matmul(W, scaling_diag_matrix)
            self.U, self.S, self.VT = torch.svd(W_scale)  
        # trucation SVD
        num_s_after_trunc = int(W.shape[0] * W.shape[1] * ratio / (W.shape[0] + W.shape[1]))
        self.truc_s = self.S[:num_s_after_trunc].cuda()
        self.truc_u = self.U[:, :num_s_after_trunc].cuda()
        if direct_update:
            self.truc_v = self.VT[:num_s_after_trunc, :].cuda()
        else:
            self.truc_v = torch.matmul(self.VT[:num_s_after_trunc, :].cuda(), scaling_matrix_inv)
        self.truc_sigma = torch.diag(self.truc_s)
        self.new_w = torch.matmul(self.truc_u, torch.matmul(self.truc_sigma, self.truc_v[:num_s_after_trunc, :]))
        # intialize H for close form solution
        self.updated_err = self.error = 0
//  [python]
def __init__(self, layer, scaling_diag_matrix, ratio, name, direct_update=False):
        self.layer = layer
        self.name = name
        self.dev = self.layer.weight.device
        # W = layer.weight.data.clone()
        W = layer.weight.data.clone()
        self.rows = W.shape[0]
        self.columns = W.shape[1]
        if direct_update:
            self.U, self.S, self.VT = torch.linalg.svd(W.data, full_matrices=False)
        else: 
            try:
                scaling_matrix_inv = torch.linalg.inv(scaling_diag_matrix)
            except Exception as e:
                print("Warning: scaling_diag_matrix is not full rank!")
                scaling_diag_matrix += 1e-6 * torch.eye(scaling_diag_matrix.shape[0])
                scaling_matrix_inv = torch.linalg.inv(scaling_diag_matrix)
            scaling_diag_matrix = scaling_diag_matrix.float()
            scaling_matrix_inv = scaling_matrix_inv.float()
            W_scale = torch.matmul(W, scaling_diag_matrix)
            self.U, self.S, self.VT = torch.linalg.svd(W_scale, full_matrices=False)  
        # trucation SVD
        num_s_after_trunc = int(W.shape[0] * W.shape[1] * ratio / (W.shape[0] + W.shape[1]))
        self.truc_s = self.S[:num_s_after_trunc].cuda()
        self.truc_u = self.U[:, :num_s_after_trunc].cuda()
        if direct_update:
            self.truc_v = self.VT[:num_s_after_trunc, :].cuda()
        else:
            self.truc_v = torch.matmul(self.VT[:num_s_after_trunc, :].cuda(), scaling_matrix_inv)
        self.truc_sigma = torch.diag(self.truc_s)
        self.new_w = torch.matmul(self.truc_u, torch.matmul(self.truc_sigma, self.truc_v[:num_s_after_trunc, :]))
        # intialize H for close form solution
        self.updated_err = self.error = 0
//  [python]
def compute_optimal_rotation_alignment(A, B):
    """
    Compute optimal R that minimizes: || A - B @ R ||_F

    Args:
        A (torch.Tensor): (N, 3)
        B (torch.Tensor): (N, 3)

    Returns:
        R (torch.tensor): (3, 3)
    """
    # normally with R @ B, this would be A @ B.T
    H = B.T @ A
    U, S, Vh = torch.svd(H)
    s = torch.det(U @ Vh)
    S_prime = torch.diag(torch.tensor([1, 1, torch.sign(s)], device=A.device))
    return U @ S_prime @ Vh
//  [python]
def compute_optimal_rotation_alignment(A, B):
    """
    Compute optimal R that minimizes: || A - B @ R ||_F

    Args:
        A (torch.Tensor): (N, 3)
        B (torch.Tensor): (N, 3)

    Returns:
        R (torch.tensor): (3, 3)
    """
    # normally with R @ B, this would be A @ B.T
    H = B.T @ A
    U, _, Vh = torch.linalg.svd(H, full_matrices=True)
    s = torch.linalg.det(U @ Vh)
    S_prime = torch.diag(torch.tensor([1, 1, torch.sign(s)], device=A.device))
    return U @ S_prime @ Vh
//  [python]
def n_dim_rigid_transform_Kabsch_3D_torch(A: torch.Tensor, B: torch.Tensor)-> tuple[torch.Tensor, torch.Tensor]:
    assert A.shape == B.shape
    assert A.shape[2] == B.shape[2] == 3

    centroid_A = A.mean(1)
    centroid_B = B.mean(1)

    Am = A - centroid_A.reshape((-1, 1, 3))
    Bm = B - centroid_B.reshape((-1, 1, 3))

    H = torch.transpose(Bm, 1, 2) @ Am
    U, S, Vt = torch.svd(H)
    R = U @ Vt
    # print(R.shape)
    R = traced_reflect(U, R, Vt, A)

    t = centroid_A.reshape((-1, 1, 3)) - torch.bmm(centroid_B.reshape((-1, 1, 3)), R)
    # t = dim1dot_tscript(centroid_A, centroid_B, R)
    return R, t
//  [python]
def n_dim_rigid_transform_Kabsch_3D_torch(A: torch.Tensor, B: torch.Tensor)-> tuple[torch.Tensor, torch.Tensor]:
    assert A.shape == B.shape
    assert A.shape[2] == B.shape[2] == 3

    centroid_A = A.mean(1)
    centroid_B = B.mean(1)

    Am = A - centroid_A.reshape((-1, 1, 3))
    Bm = B - centroid_B.reshape((-1, 1, 3))

    H = torch.transpose(Bm, 1, 2) @ Am
    U, S, Vt = torch.linalg.svd(H)
    R = U @ Vt
    # print(R.shape)
    R = traced_reflect(U, R, Vt, A)

    t = centroid_A.reshape((-1, 1, 3)) - torch.bmm(centroid_B.reshape((-1, 1, 3)), R)
    # t = dim1dot_tscript(centroid_A, centroid_B, R)
    return R, t
//  [python]
def get_rigid_transform(A, B):
    """
    Estimate the rigid body transformation between two sets of 3D points.
    A and B are Nx3 matrices where each row is a 3D point.
    Returns a rotation matrix R and translation vector t.
    Args:
        A, B: [batch, N, 3] matrix of 3D points
    Outputs:
        R, t: [batch, 3, 3/1]
        target = R @ source (source shape [3, 1]) + t
    """
    assert A.shape == B.shape, "Input matrices must have the same shape"
    assert A.shape[-1] == 3, "Input matrices must have 3 columns (x, y, z coordinates)"

    # Compute centroids. [..., 1, 3]
    centroid_A = torch.mean(A, dim=-2, keepdim=True)
    centroid_B = torch.mean(B, dim=-2, keepdim=True)

    # Center the point sets
    A_centered = A - centroid_A
    B_centered = B - centroid_B

    # Compute the cross-covariance matrix. [..., 3, 3]
    H = A_centered.transpose(-2, -1) @ B_centered

    # Compute the Singular Value Decomposition. Along last two dimensions
    U, S, Vt = torch.svd(H)

    # Compute the rotation matrix
    R = Vt.transpose(-2, -1) @ U.transpose(-2, -1)

    # Ensure a right-handed coordinate system
    flip_mask = (torch.det(R) < 0) * -2.0 + 1.0
    # Vt[:, 2, :] *= flip_mask[..., None]

    # [N] => [N, 3]
    pad_flip_mask = torch.stack(
        [torch.ones_like(flip_mask), torch.ones_like(flip_mask), flip_mask], dim=-1
    )
    Vt = Vt * pad_flip_mask[..., None]

    # Compute the rotation matrix
    R = Vt.transpose(-2, -1) @ U.transpose(-2, -1)

    # print(R.shape, centroid_A.shape, centroid_B.shape, flip_mask.shape)
    # Compute the translation
    t = centroid_B - (R @ centroid_A.transpose(-2, -1)).transpose(-2, -1)
    t = t.transpose(-2, -1)
    return R, t
//  [python]
def get_rigid_transform(A, B):
    """
    Estimate the rigid body transformation between two sets of 3D points.
    A and B are Nx3 matrices where each row is a 3D point.
    Returns a rotation matrix R and translation vector t.
    Args:
        A, B: [batch, N, 3] matrix of 3D points
    Outputs:
        R, t: [batch, 3, 3/1]
        target = R @ source (source shape [3, 1]) + t
    """
    assert A.shape == B.shape, "Input matrices must have the same shape"
    assert A.shape[-1] == 3, "Input matrices must have 3 columns (x, y, z coordinates)"

    # Compute centroids. [..., 1, 3]
    centroid_A = torch.mean(A, dim=-2, keepdim=True)
    centroid_B = torch.mean(B, dim=-2, keepdim=True)

    # Center the point sets
    A_centered = A - centroid_A
    B_centered = B - centroid_B

    # Compute the cross-covariance matrix. [..., 3, 3]
    H = A_centered.transpose(-2, -1) @ B_centered

    # Compute the Singular Value Decomposition. Along last two dimensions
    U, S, Vt = torch.linalg.svd(H)

    # Compute the rotation matrix
    R = Vt.transpose(-2, -1) @ U.transpose(-2, -1)

    # Ensure a right-handed coordinate system
    flip_mask = (torch.det(R) < 0) * -2.0 + 1.0
    # Vt[:, 2, :] *= flip_mask[..., None]

    # [N] => [N, 3]
    pad_flip_mask = torch.stack(
        [torch.ones_like(flip_mask), torch.ones_like(flip_mask), flip_mask], dim=-1
    )
    Vt = Vt * pad_flip_mask[..., None]

    # Compute the rotation matrix
    R = Vt.transpose(-2, -1) @ U.transpose(-2, -1)

    # print(R.shape, centroid_A.shape, centroid_B.shape, flip_mask.shape)
    # Compute the translation
    t = centroid_B - (R @ centroid_A.transpose(-2, -1)).transpose(-2, -1)
    t = t.transpose(-2, -1)
    return R, t
//  [python]
def __init__(
        self,
        weight: torch.Tensor,
    ):
        super().__init__()
        self.weight = weight
        with torch.no_grad():
            _, s, vh = torch.svd(self.weight, some=False)

        self.register_buffer("u", vh[0])
        self.register_buffer("spectral_norm", s[0] * torch.ones(1))
//  [python]
def __init__(
        self,
        weight: torch.Tensor,
    ):
        super().__init__()
        self.weight = weight
        with torch.no_grad():
            _, s, vh = torch.linalg.svd(self.weight, full_matrices=False)

        self.register_buffer("u", vh[0])
        self.register_buffer("spectral_norm", s[0] * torch.ones(1))
//  [python]
def resize(self, if_starting, current_modes = None):
        self.current_modes = int(self.current_modes_tensor.data.item())
        self.iter = int(self.iter_tensor.data.item())
        if if_starting == True:
            matrix = torch.matmul(self.A.data[:self.current_modes+self.buffer_modes,:].T, self.B.data[:,:self.current_modes+self.buffer_modes].T)
            
            if matrix.dtype != torch.float:
                matrix = matrix.float()

            U, s, Vh = torch.svd(matrix)
            s_sqrt = torch.sqrt(s[:self.current_modes])
            del self._parameters['A']
            del self._parameters['B']
            self.A = nn.Parameter((torch.matmul(U[:,:self.current_modes], torch.diag(s_sqrt)).T).float())
            self.B = nn.Parameter((torch.matmul(torch.diag(s_sqrt), Vh[:self.current_modes,:]).T).float())

            if self.bias_flag:
                bias = self.bias.data
                del self._parameters['bias']
                self.bias = nn.Parameter(bias)
            
            del self._parameters['current_modes_tensor']
            del self._parameters['iter_tensor']
            self.current_modes_tensor = nn.Parameter(torch.tensor([self.current_modes], dtype = torch.float32))
            self.iter_tensor = nn.Parameter(torch.tensor([self.iter], dtype = torch.float32))
            self.current_modes_tensor.requires_grad = False
            self.iter_tensor.requires_grad = False

            return self.current_modes
        
        if if_starting == False:
            size_A = self.A[:current_modes,:].size()
            size_B = self.B[:,:current_modes].size()
            del self._parameters['A']
            del self._parameters['B']
            self.A = nn.Parameter(torch.empty(size_A))
            self.B = nn.Parameter(torch.empty(size_B))

            if self.bias_flag:
                bias = self.bias.data
                del self._parameters['bias']
                self.bias = nn.Parameter(bias)
            
            del self._parameters['current_modes_tensor']
            del self._parameters['iter_tensor']
            self.current_modes_tensor = nn.Parameter(torch.tensor([self.current_modes], dtype = torch.float32))
            self.iter_tensor = nn.Parameter(torch.tensor([self.iter], dtype = torch.float32))
            self.current_modes_tensor.requires_grad = False
            self.iter_tensor.requires_grad = False