//  [python]
def get_dv_wo_J_e(self, bs_idx, v, Minv, Jac, Jac_v, v_star, mu, cor):
        n_cld, d, n_o, n_p, _ = Jac.shape
        n = n_o * n_p
        # calculate A_decom
        Minv_Jac_T = (Minv @ Jac.reshape(n_cld*d, n*d).t().reshape(n, d*n_cld*d)).reshape(n*d, n_cld*d)
        A = Jac.reshape(n_cld*d, n*d) @ Minv_Jac_T

        if torch.is_tensor(self.reg):
            reg = F.softplus(self.reg)
        else:
            reg = self.reg
        A_decom = torch.linalg.cholesky(A+torch.eye(A.shape[0]).type_as(A)*reg, upper=True)
        # make sure v_star is "valid", avoid unbounded cvx problem
        with torch.no_grad():
            if n_cld > n: # Jac is a tall matrix
                # v_star_c = J (J_T J)^-1 J_T v_star
                try:
                    v_star_euc = Jac.reshape(n_cld*d, n*d).t() @ v_star.reshape(n_cld*d, 1) # (n*d, 1)
                    v_star_euc = torch.solve(v_star_euc, Jac.reshape(n_cld*d, n*d).t() @ Jac.reshape(n_cld*d, n*d))[0]
                    v_star_c = Jac.reshape(n_cld*d, n*d) @ v_star_euc
                except RuntimeError as e:
                    # https://math.stackexchange.com/questions/748500/how-to-find-linearly-independent-columns-in-a-matrix
                    Q, R = torch.qr(Jac.reshape(n_cld*d, n*d), some=True)
                    # Q: (n_cld*d, n*d), R: (n*d, n*d)
                    idx_list = [0] ; ptr = 0
                    while ptr < n-1:
                        for ptr_r in range(ptr+1,n*d):
                            if R[len(idx_list), ptr_r] != 0:
                                idx_list.append(ptr_r)
                                ptr = ptr_r
                                break
                    Jac_full_rank = Q @ R[:, idx_list]
                    v_star_euc = Jac_full_rank.t() @ v_star.reshape(n_cld*d, 1)
                    v_star_euc = torch.solve(v_star_euc, Jac_full_rank.t() @ Jac_full_rank)[0]
                    v_star_c = Jac_full_rank @ v_star_euc
                    # print("performed QR decomposition")
            else:
                v_star_c = v_star
        # compression phase impulse
        try:
            impulse = self.solve_compression_impulse(
                A_decom, Jac_v, mu, n_cld, d
            )
        except SolverError as e:
            try:
                impulse = self.solve_compression_impulse_w_penalty(
                    A_decom, Jac_v, mu, n_cld, d
                )
            except SolverError as e:
                # self.save(e, "comp", bs_idx, v, Minv, Jac, Jac_v, v_star, mu, cor)
                impulse = torch.zeros(n_cld*d, 1).type_as(v)
        # velocity after compression phase (before retitution phase)
        dv_comp = (Minv_Jac_T @ impulse).reshape(n, d)
        v_prev_r = v[bs_idx] + dv_comp
        # restitution phase impulse
        Jac_v_prev_r = Jac.reshape(n_cld*d, n*d) @ v_prev_r.reshape(n*d, 1) # n_cld*d, 1
        target_impulse = (cor.reshape(-1) * impulse.reshape(n_cld, d)[:, 0]).reshape(n_cld, 1)
        try:
            impulse_star_r = self.solve_restitution_impulse(
                A_decom, Jac_v_prev_r, v_star_c, mu, n_cld, d, target_impulse=target_impulse
            )
        except SolverError as e:
            try:
                impulse_star_r = self.solve_restitution_impulse_w_penalty(
                    A_decom, Jac_v_prev_r, v_star_c, mu, n_cld, d, target_impulse=target_impulse
                )
            except:
                # self.save(e, "rest", bs_idx, v, Minv, Jac, Jac_v, v_star, mu, cor)
                impulse_star_r = torch.zeros(n_cld*d, 1).type_as(v)
        # velocity after restitution phase
        dv_rest = (Minv_Jac_T @ impulse_star_r).reshape(n, d)
        dv = torch.zeros_like(v)
        dv[bs_idx] = dv_comp + dv_rest
        return dv