def _derivativenorm(self):
     """Compute the derivative of the norm
     Returns
     -------
     derivative : numpy array, shape (m_parameters,)
     """
     w2 = cp.reshape(self.w,(self.n_features,self.d,self.D,self.D))
     derivative = cp.zeros((self.n_features,self.d,self.D,self.D)) 
     
     tmp=cp.zeros((self.n_features,self.D))
     tmp2=cp.zeros((self.n_features,self.D))
     tmp[0,:]=cp.sum(cp.square(w2[0,:,0,:]),0)
     for i in range(1,self.n_features-1):
         tmp[i,:]=cp.dot(tmp[i-1,:],cp.sum(cp.square(w2[i,:,:,:]),0)) 
     tmp[self.n_features-1,:]=cp.inner(tmp[self.n_features-2,:],
             cp.sum(cp.square(w2[self.n_features-1,:,:,0]),0))
     tmp2[self.n_features-1,:]=cp.sum(cp.square(w2[self.n_features-1,:,:,0]),0)
     for i in range(self.n_features-2,-1,-1):
         tmp2[i,:]=cp.dot(cp.sum(cp.square(w2[i,:,:,:]),0),tmp2[i+1,:])
     tmp2[0,:]=cp.inner(cp.sum(cp.square(w2[0,:,0,:]),0),tmp2[1,:])
 
     for j in range(self.d):
         derivative[0,j,0,:]=cp.multiply(tmp2[1,:],2*(w2[0,j,0,:]))
         derivative[self.n_features-1,j,:,0]=\
             cp.multiply(tmp[self.n_features-2,:],2*(w2[self.n_features-1,j,:,0]))
     for i in range(1,self.n_features-1):
         temp3=cp.outer(tmp[i-1,:],tmp2[i+1,:])
         for j in range(self.d):
             derivative[i,j,:,:]=cp.multiply(temp3,2*(w2[i,j,:,:]))
     return derivative.reshape(self.m_parameters)
Beispiel #2
0
def _check_symmetric(op1, op2, vec, eps):
    r2 = op1 * op2
    s = cupy.inner(op2, op2)
    t = cupy.inner(vec, r2)
    z = abs(s - t)
    epsa = (s + eps) * eps ** (1.0 / 3.0)
    if z > epsa:
        return False
    return True
Beispiel #3
0
    def _derivativenorm(self):
        """Compute the derivative of the norm
        Returns
        -------
        derivative : numpy array, shape (m_parameters,)
        """

        w2 = np.reshape(self.w,
                        (self.n_features, self.d, self.D, self.D, self.mu))
        derivative = np.zeros(
            (self.n_features, self.d, self.D, self.D, self.mu),
            dtype=np.complex128)

        tmp = np.zeros((self.n_features, self.D * self.D), dtype=np.complex128)
        tmp2 = np.zeros((self.n_features, self.D * self.D),
                        dtype=np.complex128)

        tmp[0, :] = np.einsum('ijk,ilk->jl', w2[0, :, 0, :, :],
                              np.conj(w2[0, :,
                                         0, :, :])).reshape(self.D * self.D)
        for i in xrange(1, self.n_features - 1):
            newtmp = np.einsum('pimj,pklj->ikml', w2[i, :, :, :, :],
                               np.conj(w2[i, :, :, :, :])).reshape(
                                   (self.D * self.D, self.D * self.D))
            tmp[i, :] = np.dot(tmp[i - 1, :], newtmp)
        newtmp = np.einsum('ijk,ilk->jl', w2[self.n_features - 1, :, :, 0, :],
                           np.conj(w2[self.n_features - 1, :, :,
                                      0, :])).reshape(self.D * self.D)
        mpscontracted = np.inner(tmp[self.n_features - 2, :], newtmp)
        tmp[self.n_features - 1, :] = mpscontracted

        tmp2[self.n_features - 1, :] = newtmp
        for i in xrange(self.n_features - 2, -1, -1):
            newtmp = np.einsum('pimj,pklj->ikml', w2[i, :, :, :, :],
                               np.conj(w2[i, :, :, :, :])).reshape(
                                   (self.D * self.D, self.D * self.D))
            tmp2[i, :] = np.dot(newtmp, tmp2[i + 1, :])
        newtmp = np.einsum('ijk,ilk->jl', w2[0, :, 0, :, :],
                           np.conj(w2[0, :, 0, :, :])).reshape(self.D * self.D)
        tmp2[0, :] = np.inner(newtmp, tmp2[1, :])

        for j in xrange(self.d):
            derivative[0, j, 0, :, :] = 2 * np.einsum(
                'ij,il->lj', w2[0, j, 0, :, :], tmp2[1, :].reshape(
                    self.D, self.D))
            derivative[self.n_features-1,j,:,0,:]=\
            2*np.einsum('ij,il->lj',w2[self.n_features-1,j,:,0,:],
                            tmp[self.n_features-2,:].reshape(self.D,self.D))
        for i in xrange(1, self.n_features - 1):
            temp1 = tmp[i - 1, :].reshape(self.D, self.D)
            temp2 = tmp2[i + 1, :].reshape(self.D, self.D)
            for j in xrange(self.d):
                derivative[i, j, :, :, :] = 2 * np.einsum(
                    'ikm,ij,kl->jlm', w2[i, j, :, :, :], temp1, temp2)

        return derivative.reshape(self.m_parameters)
Beispiel #4
0
    def _probability(self, x):
        """Unnormalized probability of one configuration P(x)
        Parameters
        ----------
        x : numpy array, shape (n_features,)
            One configuration
        Returns
        -------
        probability : float
        """
        w2 = np.reshape(self.w,
                        (self.n_features, self.d, self.D, self.D, self.mu))

        tmp = w2[0, x[0], 0, :, :]
        tmp2 = np.einsum('ij,kj->ik', tmp,
                         np.conj(tmp)).reshape(self.D * self.D)
        for i in xrange(1, self.n_features - 1):
            tmp = np.einsum('imj,klj->ikml', w2[i, x[i], :, :, :],
                            np.conj(w2[i, x[i], :, :, :])).reshape(
                                (self.D * self.D, self.D * self.D))
            tmp2 = np.dot(tmp2, tmp)

        tmp = np.einsum(
            'ij,kj->ik', w2[self.n_features - 1, x[self.n_features - 1], :,
                            0, :],
            np.conj(w2[self.n_features - 1, x[self.n_features - 1], :,
                       0, :])).reshape(self.D * self.D)
        probability = np.abs(np.inner(tmp2, tmp))
        return probability
Beispiel #5
0
def routine_gpu(data, sr, omega, morlet_frequency):
    n_chans, n_ts = data.shape

    data_gpu = cp.asarray(data)
    win = cp.array(mne.time_frequency.morlet(sr, [morlet_frequency], omega)[0])

    data_preprocessed = cp.zeros_like(data_gpu, dtype=cp.complex64)
    surr_data = cp.zeros_like(data_preprocessed)
    for i in range(n_chans):
        data_preprocessed[i] = cusignal.fftconvolve(data_gpu[i], win, 'same')
        data_preprocessed[i] /= cp.abs(data_preprocessed[i])

        surr_data[i] = cp.roll(data_preprocessed[i],
                               np.random.randint(n_ts - 1))

    plv = cp.inner(data_preprocessed, cp.conj(data_preprocessed)) / n_ts
    plv_surr = cp.inner(surr_data, cp.conj(surr_data)) / n_ts

    return cp.asnumpy(plv), cp.asnumpy(plv_surr)
    def _derivative(self, x):
        """Compute the derivative of P(x)
        Parameters
        ----------
        x : numpy array, shape (n_features,)
            One configuration
        Returns
        -------
        derivative : numpy array, shape (m_parameters,)
        """
        w2 = cp.reshape(self.w,(self.n_features,self.d,self.D,self.D))
        derivative = cp.zeros((self.n_features,self.d,self.D,self.D))

        #Store intermediate tensor contractions for the derivatives: 
        #left to right and right to left
        #tmp stores the contraction of the first i+1 tensors from the left 
        #in tmp[i,:,:], tmp2 the remaining tensors on the right
        #the mps contracted is the remaining contraction tmp[i-1]w[i]tmp2[i+1]
        tmp = cp.zeros((self.n_features,self.D))
        tmp2 = cp.zeros((self.n_features,self.D))
        tmp[0,:] = cp.square(w2[0,x[0],0,:])
        for i in range(1,self.n_features-1):
            tmp[i,:] = cp.dot(tmp[i-1,:],cp.square(w2[i,x[i],:,:]))  
        tmp[self.n_features-1,:] = cp.inner(tmp[self.n_features-2,:],
                cp.square(w2[self.n_features-1,x[self.n_features-1],:,0]))
        tmp2[self.n_features-1,:] = cp.square(w2[self.n_features-1,
                x[self.n_features-1],:,0])
        for i in range(self.n_features-2,-1,-1):
            tmp2[i,:] = cp.dot(cp.square(w2[i,x[i],:]),tmp2[i+1,:])
        tmp2[0,:] = cp.inner(cp.square(w2[0,x[0],0,:]),tmp2[1,:])
    
        #The derivative of each tensor is the contraction of the other tensors
        derivative[0,x[0],0,:] = cp.multiply(tmp2[1,:],2*(w2[0,x[0],0,:]))
        derivative[self.n_features-1,x[self.n_features-1],:,0] = \
                    cp.multiply(tmp[self.n_features-2,:],
                        2*(w2[self.n_features-1,x[self.n_features-1],:,0]))
        for i in range(1,self.n_features-1):
                derivative[i,x[i],:,:]=cp.multiply(cp.outer(tmp[i-1,:],
                tmp2[i+1,:]),2*(w2[i,x[i],:]))

        return derivative.reshape(self.m_parameters)
 def _computenorm(self):
     """Compute norm of probability distribution
     Returns
     -------
     norm : float
     """
     w2 = cp.reshape(self.w,(self.n_features,self.d,self.D,self.D))
     tmp = cp.sum(cp.square(w2[0,:,0,:]),0) #First tensor
     for i in range(1,self.n_features-1):
         tmp = cp.dot(tmp,cp.sum(cp.square(w2[i,:,:,:]),0)) #MPS contraction  
     norm = cp.inner(tmp,cp.sum(cp.square(w2[self.n_features-1,:,:,0]),0))
     return norm
Beispiel #8
0
    def _process_pair(self, x: int, y: int):
        n_bins = self.n_bins

        # compute mask of amplitude quantile for each sample in the data
        for i in range(n_bins):
            self.mask_buffer[i] = (self.data_amplitude_labels[x]
                                   == i) & (self.data_thresholded[x])
            self.mask_buffer[i + n_bins] = (self.data_amplitude_labels[y]
                                            == i) & (self.data_thresholded[y])

        # inner product of those masks is the same as compute sum of logical for each pair but without cycles => faster
        # however I dont like bool -> int type casting
        self.frequency_samples[:, :, x, y] = cp.inner(
            self.mask_buffer[:n_bins].astype(int),
            self.mask_buffer[n_bins:~0].astype(int))
        self.frequency_samples[:, :, y, x] = self.frequency_samples[:, :, x, y]

        min_count = int(self.frequency_samples[:, :, x, y].min())

        data_x = self.data_preprocessed[x]
        data_y = self.data_conj[y]

        for i, j in itertools.product(range(n_bins), range(n_bins)):
            cp.logical_and(self.mask_buffer[i],
                           self.mask_buffer[j + n_bins],
                           out=self.mask_buffer[~0])

            # select data according to amplitude mask of both channels and truncate it to avoid PLV bias
            vals_x = data_x[self.mask_buffer[~0]][:min_count]
            vals_y = data_y[self.mask_buffer[~0]][:min_count]

            # just in case there are some label combinations without any samples; unluckly though
            # if vals_x.shape[0] == 0 or vals_y.shape[0] == 0:
            #     continue

            self.frequency_plv[i, j, x,
                               y] = self.frequency_plv[i, j, y, x] = cp.inner(
                                   vals_x, vals_y) / min_count
 def _probability(self, x):
     """Unnormalized probability of one configuration P(x)
     Parameters
     ----------
     x : cupy array, shape (n_features,)
         One configuration
     Returns
     -------
     probability : float
     """
     w2 = cp.reshape(self.w,(self.n_features,self.d,self.D,self.D))
     tmp = cp.square(w2[0,x[0],0,:]) #First tensor
     for i in range(1,self.n_features-1):
         tmp = cp.dot(tmp,cupy.square(w2[i,x[i],:,:])) #MPS contraction  
     probability = cp.inner(tmp,cupy.square(w2[self.n_features-1,
                                             x[self.n_features-1],:,0]))
     return probability
    def gradient(self, input, expected_output):

        self.think(input)   # calculate the activations from 'input' that are needed for the backpropagation algorithm

        nabla_w = [cp.zeros(w.shape) for w in self.weights] # initialise the matrices for the w_gradient
        nabla_b = [cp.zeros(b.shape) for b in self.biases]  # initialise the matrices for the b_gradient
        z = [cp.dot(a, w) + b for a, w, b in zip(self.activations, self.weights, self.biases)] # a = sigmoid(z) ...

        nabla_a = self.activations[-1] - cp.array(expected_output)

        for i, a, z, w in zip(range(self.layer_number-2, -1, -1),
                              self.activations[-2::-1], z[::-1], self.weights[::-1]):

            nabla_w[i] = cp.outer(a, nabla_a * sigmoid_derivative(z))
            nabla_b[i] = nabla_a * sigmoid_derivative(z)
            nabla_a = cp.inner(nabla_a * sigmoid_derivative(z), w)

        nabla_b[-1] = cp.zeros_like(nabla_b[-1])

        return (nabla_w, nabla_b)
Beispiel #11
0
    def _computenorm(self):
        """Compute norm of probability distribution
        Returns
        -------
        norm : float
        """
        w2 = np.reshape(self.w,
                        (self.n_features, self.d, self.D, self.D, self.mu))

        tmp2 = np.einsum('ijk,ilk->jl', w2[0, :, 0, :, :],
                         np.conj(w2[0, :, 0, :, :])).reshape(self.D * self.D)
        for i in xrange(1, self.n_features - 1):
            tmp = np.einsum('pimj,pklj->ikml', w2[i, :, :, :, :],
                            np.conj(w2[i, :, :, :, :])).reshape(
                                (self.D * self.D, self.D * self.D))
            tmp2 = np.dot(tmp2, tmp)
        tmp = np.einsum('ijk,ilk->jl', w2[self.n_features - 1, :, :, 0, :],
                        np.conj(w2[self.n_features - 1, :, :,
                                   0, :])).reshape(self.D * self.D)
        norm = np.abs(np.inner(tmp2, tmp))
        return norm
Beispiel #12
0
def cosine_similarity(x, y):
    a = x.reshape((x.shape[1], ))
    b = y.reshape((y.shape[1], ))
    return cp.inner(a, b) / norm(a) * norm(b)
Beispiel #13
0
def inner(u,v):
    return cp.inner(u,v)
Beispiel #14
0
 def f(x1, x2):
     return cupy.inner(x1, x2)
Beispiel #15
0
 def f(x1, x2):
     return (cupy.inner(x1, x2) + coef)**power
Beispiel #16
0
 def time_inner_trans_a_ac(self):
     np.inner(self.a, self.ac)
Beispiel #17
0
    def _derivative(self, x):
        """Compute the derivative of P(x)
        Parameters
        ----------
        x : numpy array, shape (n_features,)
            One configuration
        Returns
        -------
        derivative : numpy array, shape (m_parameters,)
        """
        w2 = np.reshape(self.w,
                        (self.n_features, self.d, self.D, self.D, self.mu))
        derivative = np.zeros(
            (self.n_features, self.d, self.D, self.D, self.mu),
            dtype=np.complex128)

        #Store intermediate tensor contractions for the derivatives:
        #left to right and right to left
        #tmp stores the contraction of the first i+1 tensors from the left
        #in tmp[i,:,:], tmp2 the remaining tensors on the right
        #the mps contracted is the remaining contraction tmp[i-1]w[i]tmp2[i+1]
        tmp = np.zeros((self.n_features, self.D * self.D), dtype=np.complex128)
        tmp2 = np.zeros((self.n_features, self.D * self.D),
                        dtype=np.complex128)
        tmp[0, :] = np.einsum('ij,kj->ik', w2[0, x[0], 0, :, :],
                              np.conj(w2[0, x[0],
                                         0, :, :])).reshape(self.D * self.D)
        for i in xrange(1, self.n_features - 1):
            newtmp = np.einsum('imj,klj->ikml', w2[i, x[i], :, :, :],
                               np.conj(w2[i, x[i], :, :, :])).reshape(
                                   (self.D * self.D, self.D * self.D))
            tmp[i, :] = np.dot(tmp[i - 1, :], newtmp)
        newtmp = np.einsum(
            'ij,kj->ik', w2[self.n_features - 1, x[self.n_features - 1], :,
                            0, :],
            np.conj(w2[self.n_features - 1, x[self.n_features - 1], :,
                       0, :])).reshape(self.D * self.D)
        mpscontracted = np.inner(tmp[self.n_features - 2, :], newtmp)
        tmp[self.n_features - 1, :] = mpscontracted

        tmp2[self.n_features - 1, :] = newtmp
        for i in xrange(self.n_features - 2, -1, -1):
            newtmp = np.einsum('imj,klj->ikml', w2[i, x[i], :, :, :],
                               np.conj(w2[i, x[i], :, :, :])).reshape(
                                   (self.D * self.D, self.D * self.D))
            tmp2[i, :] = np.dot(newtmp, tmp2[i + 1, :])
        newtmp = np.einsum('ij,kj->ik', w2[0, x[0], 0, :, :],
                           np.conj(w2[0, x[0],
                                      0, :, :])).reshape(self.D * self.D)
        tmp2[0, :] = np.inner(newtmp, tmp2[1, :])

        #Now for each tensor, the derivative is the contraction of the rest of the tensors

        derivative[0, x[0],
                   0, :, :] = 2 * np.einsum('ij,il->lj', w2[0, x[0], 0, :, :],
                                            tmp2[1, :].reshape(self.D, self.D))
        derivative[self.n_features-1,x[self.n_features-1],:,0,:]=\
            2*np.einsum('ij,il->lj',w2[self.n_features-1,x[self.n_features-1],:,0,:],
                        tmp[self.n_features-2,:].reshape(self.D,self.D))
        for i in xrange(1, self.n_features - 1):
            temp1 = tmp[i - 1, :].reshape(self.D, self.D)
            temp2 = tmp2[i + 1, :].reshape(self.D, self.D)
            derivative[i, x[i], :, :, :] = 2 * np.einsum(
                'ikm,ij,kl->jlm', w2[i, x[i], :, :, :], temp1, temp2)

        return derivative.reshape(self.m_parameters)
Beispiel #18
0
def main(args):
    analysis_params = json.load(open(args.config_file))

    high_freqs = np.logspace(2.35, 9, base=2, num=20, endpoint=False)
    low_freqs = np.logspace(1, 6.75, base=2, num=15, endpoint=False)

    epleptic_windows = pd.read_csv(analysis_params['epleptic_windows_file'])
    epleptic_windows[['Start', 'End']] = (epleptic_windows[['Start', 'End']] *
                                          1000).astype(int)
    epleptic_windows = epleptic_windows.groupby('subject_number')

    root_path = os.path.join('../seeg_phases/data', 'SEEG_redux_BIDS')
    layout = BIDSLayout(root_path)

    for subject in layout.get(target='subject', extension='edf'):
        subject_code = int(subject.entities['subject'])
        res_fname = os.path.join(
            'derivatives', 'pac_no_ez',
            'pac_sub_{}.pickle'.format(subject.entities['subject']))

        if os.path.exists(res_fname):
            print('Subject {} is processed!'.format(
                subject.entities['subject']))
            continue

        montage_filename = os.path.join(
            subject.dirname,
            'sub-{}_montage.tcsv'.format(subject.entities['subject']))
        data_filename = subject.path

        if not (os.path.exists(montage_filename)
                and os.path.exists(data_filename)):
            print('Cannot find data for subject {}'.format(
                subject.entities['subject']))
            continue

        bipo = make_bipolar(data_filename, montage_filename,
                            analysis_params['lowpass_filter'])
        ref_mask = create_reference_mask(bipo)
        ref_mask_gpu = cp.array(ref_mask).astype(int)

        if subject_code in epleptic_windows.groups:
            subject_ez_windows = epleptic_windows.get_group(subject_code)
            subject_ez_samples_mask = get_ez_samples_mask(
                subject_ez_windows, bipo._data)
        else:
            subject_ez_samples_mask = np.ones(bipo._data.shape[1], dtype=bool)

        n_chans = len(bipo.ch_names)

        phase_amplitude_correlation = np.full(shape=(len(high_freqs),
                                                     len(low_freqs), n_chans,
                                                     n_chans),
                                              fill_value=np.nan)
        phase_amplitude_surrogates = np.full(shape=(len(high_freqs),
                                                    len(low_freqs), n_chans,
                                                    n_chans),
                                             fill_value=np.nan)

        data_gpu = cp.array(bipo._data[:, subject_ez_samples_mask])

        for low_f in tqdm.tqdm(low_freqs, desc='Preprocessing...',
                               leave=False):
            low_fname = 'temp/sub_{}_freq_{:.2f}.npy'.format(
                subject.entities['subject'], low_f)
            if os.path.exists(low_fname):
                continue

            data_low_f = filter_morlet_gpu(data_gpu, bipo.info['sfreq'],
                                           analysis_params['omega'], low_f)
            data_low_f /= cp.abs(data_low_f)
            data_low_f = cp.conj(data_low_f)

            cp.save(low_fname, data_low_f)

        data_low_f = None

        sr = bipo.info['sfreq']
        bar = tqdm.tqdm(total=225, leave=False)
        for high_idx, high_f in enumerate(high_freqs):
            high_amp = cp.abs(
                filter_morlet_gpu(data_gpu, sr, analysis_params['omega'],
                                  high_f))

            for low_idx, low_f in enumerate(low_freqs):
                if low_f > high_f:
                    break

                low_fname = 'temp/sub_{}_freq_{:.2f}.npy'.format(
                    subject.entities['subject'], low_f)
                slow_conj = cp.load(low_fname)

                high_amp_complex = filter_morlet_gpu(high_amp, sr,
                                                     analysis_params['omega'],
                                                     low_f)
                high_amp_complex /= cp.abs(high_amp_complex)

                corr = cp.inner(high_amp_complex,
                                slow_conj) / slow_conj.shape[1]

                create_surrogate_inplace(high_amp_complex)

                corr_surr = cp.inner(high_amp_complex,
                                     slow_conj) / slow_conj.shape[1]

                phase_amplitude_correlation[high_idx, low_idx] = cp.asnumpy(
                    cp.abs(corr) * ref_mask_gpu)
                phase_amplitude_surrogates[high_idx, low_idx] = cp.asnumpy(
                    cp.abs(corr_surr) * ref_mask_gpu)

                bar.update(1)

        bar.close()

        res = {
            'phase_amplitude_correlation': phase_amplitude_correlation,
            'surrogate': phase_amplitude_surrogates,
            'high_frequencies': high_freqs,
            'low_frequencies': low_freqs,
            'ref_mask': ref_mask,
            'ch_names': bipo.ch_names
        }
        pickle.dump(res, open(res_fname, 'wb'))

        for low_f in tqdm.tqdm(low_freqs, desc='Preprocessing...',
                               leave=False):
            low_fname = 'temp/sub_{}_freq_{:.2f}.npy'.format(
                subject.entities['subject'], low_f)
            if os.path.exists(low_fname):
                os.remove(low_fname)
Beispiel #19
0
def minres(A, b, x0=None, shift=0.0, tol=1e-5, maxiter=None,
           M=None, callback=None, check=False):
    """Uses MINimum RESidual iteration to solve  ``Ax = b``.

    Args:
        A (ndarray, spmatrix or LinearOperator): The real or complex matrix of
            the linear system with shape ``(n, n)``.
        b (cupy.ndarray): Right hand side of the linear system with shape
            ``(n,)`` or ``(n, 1)``.
        x0 (cupy.ndarray): Starting guess for the solution.
        shift (int or float): If shift != 0 then the method solves
            ``(A - shift*I)x = b``
        tol (float): Tolerance for convergence.
        maxiter (int): Maximum number of iterations.
        M (ndarray, spmatrix or LinearOperator): Preconditioner for ``A``.
            The preconditioner should approximate the inverse of ``A``.
            ``M`` must be :class:`cupy.ndarray`,
            :class:`cupyx.scipy.sparse.spmatrix` or
            :class:`cupyx.scipy.sparse.linalg.LinearOperator`.
        callback (function): User-specified function to call after each
            iteration. It is called as ``callback(xk)``, where ``xk`` is the
            current solution vector.

    Returns:
        tuple:
            It returns ``x`` (cupy.ndarray) and ``info`` (int) where ``x`` is
            the converged solution and ``info`` provides convergence
            information.

    .. seealso:: :func:`scipy.sparse.linalg.minres`
    """
    A, M, x, b = _make_system(A, M, x0, b)

    matvec = A.matvec
    psolve = M.matvec

    n = b.shape[0]

    if maxiter is None:
        maxiter = n * 5

    istop = 0
    itn = 0
    Anorm = 0
    Acond = 0
    rnorm = 0
    ynorm = 0

    xtype = x.dtype

    eps = cupy.finfo(xtype).eps

    Ax = matvec(x)
    r1 = b - Ax
    y = psolve(r1)

    beta1 = cupy.inner(r1, y)

    if beta1 < 0:
        raise ValueError('indefinite preconditioner')
    elif beta1 == 0:
        return x, 0

    beta1 = cupy.sqrt(beta1)
    beta1 = beta1.get().item()

    if check:
        # see if A is symmetric
        if not _check_symmetric(A, Ax, x, eps):
            raise ValueError('non-symmetric matrix')

        # see if M is symmetric
        if not _check_symmetric(M, y, r1, eps):
            raise ValueError('non-symmetric preconditioner')

    oldb = 0
    beta = beta1
    dbar = 0
    epsln = 0
    qrnorm = beta1
    phibar = beta1
    rhs1 = beta1
    rhs2 = 0
    tnorm2 = 0
    gmax = 0
    gmin = cupy.finfo(xtype).max
    cs = -1
    sn = 0
    w = cupy.zeros(n, dtype=xtype)
    w2 = cupy.zeros(n, dtype=xtype)
    r2 = r1

    while itn < maxiter:

        itn += 1
        s = 1.0 / beta
        v = s * y

        y = matvec(v)
        y -= shift * v

        if itn >= 2:
            y -= (beta / oldb) * r1

        alpha = cupy.inner(v, y)
        alpha = alpha.get().item()
        y -= (alpha / beta) * r2
        r1 = r2
        r2 = y
        y = psolve(r2)
        oldb = beta
        beta = cupy.inner(r2, y)
        beta = beta.get().item()
        beta = numpy.sqrt(beta)
        if beta < 0:
            raise ValueError('non-symmetric matrix')

        tnorm2 += alpha ** 2 + oldb ** 2 + beta ** 2

        if itn == 1:
            if beta / beta1 <= 10 * eps:
                istop = -1

        # Apply previous rotation Qk-1 to get
        #   [deltak epslnk+1] = [cs  sn][dbark    0   ]
        #   [gbar k dbar k+1]   [sn -cs][alfak betak+1].

        oldeps = epsln
        delta = cs * dbar + sn * alpha  # delta1 = 0         deltak
        gbar = sn * dbar - cs * alpha  # gbar 1 = alfa1     gbar k
        epsln = sn * beta  # epsln2 = 0         epslnk+1
        dbar = - cs * beta  # dbar 2 = beta2     dbar k+1
        root = numpy.linalg.norm([gbar, dbar])

        # Compute the next plane rotation Qk

        gamma = numpy.linalg.norm([gbar, beta])  # gammak
        gamma = max(gamma, eps)
        cs = gbar / gamma  # ck
        sn = beta / gamma  # sk
        phi = cs * phibar  # phik
        phibar = sn * phibar  # phibark+1

        # Update  x.

        denom = 1.0 / gamma
        w1 = w2
        w2 = w
        w = (v - oldeps * w1 - delta * w2) * denom
        x += phi * w

        # Go round again.

        gmax = max(gmax, gamma)
        gmin = min(gmin, gamma)
        z = rhs1 / gamma
        rhs1 = rhs2 - delta * z
        rhs2 = - epsln * z

        # Estimate various norms and test for convergence.

        Anorm = numpy.sqrt(tnorm2)
        ynorm = cupy.linalg.norm(x)
        ynorm = ynorm.get().item()
        epsa = Anorm * eps
        epsx = Anorm * ynorm * eps
        diag = gbar

        if diag == 0:
            diag = epsa

        qrnorm = phibar
        rnorm = qrnorm
        if ynorm == 0 or Anorm == 0:
            test1 = numpy.inf
        else:
            test1 = rnorm / (Anorm * ynorm)  # ||r||  / (||A|| ||x||)
        if Anorm == 0:
            test2 = numpy.inf
        else:
            test2 = root / Anorm  # ||Ar|| / (||A|| ||r||)

        # Estimate  cond(A).
        # In this version we look at the diagonals of  R  in the
        # factorization of the lower Hessenberg matrix,  Q * H = R,
        # where H is the tridiagonal matrix from Lanczos with one
        # extra row, beta(k+1) e_k^T.

        Acond = gmax / gmin

        # See if any of the stopping criteria are satisfied.
        # In rare cases, istop is already -1 from above (Abar = const*I).

        if istop == 0:
            t1 = 1 + test1  # These tests work if tol < eps
            t2 = 1 + test2
            if t2 <= 1:
                istop = 2
            if t1 <= 1:
                istop = 1

            if itn >= maxiter:
                istop = 6
            if Acond >= 0.1 / eps:
                istop = 4
            if epsx >= beta1:
                istop = 3
            # epsr = Anorm * ynorm * tol
            # if rnorm <= epsx   : istop = 2
            # if rnorm <= epsr   : istop = 1
            if test2 <= tol:
                istop = 2
            if test1 <= tol:
                istop = 1

        if callback is not None:
            callback(x)

        if istop != 0:
            break

    if istop == 6:
        info = maxiter
    else:
        info = 0

    return x, info
def basis_pursuit(A, b, tol=1e-4, niter=100, biter=32):
    """
    solves min |x|_1 s.t. Ax=b using a Primal-Dual Interior Point Method

    Args:
      A: design matrix of size (d, n)
      b: measurement vector of length d
      tol: solver tolerance
      niter: maximum length of central path
      biter: maximum number of steps in backtracking line search

    Returns:
      vector of length n
    """
    A = cp.asarray(A)
    b = cp.asarray(b)
    d, n = A.shape
    alpha = 0.01
    beta = 0.5
    mu = 10
    e = cp.ones(n)
    gradf0 = cp.hstack([cp.zeros(n), e])
    x = (A.T).dot(inv(A.dot(A.T))).dot(b)
    absx = cp.abs(x)
    u = 0.95 * absx + 0.1 * cp.max(absx)

    fu1 = x - u
    fu2 = -x - u
    lamu1 = -1.0 / fu1
    lamu2 = -1.0 / fu2
    v = A.dot(lamu2 - lamu1)
    ATv = (A.T).dot(v)
    sdg = -(cp.inner(fu1, lamu1) + cp.inner(fu2, lamu2))
    tau = 2.0 * n * mu / sdg
    ootau = 1.0 / tau

    rcent = cp.hstack([-lamu1 * fu1, -lamu2 * fu2]) - ootau
    rdual = gradf0 + cp.hstack([lamu1 - lamu2 + ATv, -lamu1 - lamu2])
    rpri = A.dot(x) - b
    resnorm = cp.sqrt(norm(rdual)**2 + norm(rcent)**2 + norm(rpri)**2)
    rdp = cp.empty(2 * n)
    rcp = cp.empty(2 * n)

    for i in range(niter):

        oofu1 = 1.0 / fu1
        oofu2 = 1.0 / fu2
        w1 = -ootau * (oofu2 - oofu1) - ATv
        w2 = -1.0 - ootau * (oofu1 + oofu2)
        w3 = -rpri

        lamu1xoofu1 = lamu1 * oofu1
        lamu2xoofu2 = lamu2 * oofu2
        sig1 = -lamu1xoofu1 - lamu2xoofu2
        sig2 = lamu1xoofu1 - lamu2xoofu2
        sigx = sig1 - sig2**2 / sig1
        if cp.min(cp.abs(sigx)) == 0.0:
            break

        w1p = -(w3 - A.dot(w1 / sigx - w2 * sig2 / (sigx * sig1)))
        H11p = A.dot((A.T) * (e / sigx)[:, cp.newaxis])
        if cp.min(sigx) > 0.0:
            dv = solve(H11p, w1p)
        else:
            dv = solve(H11p, w1p)
        dx = (w1 - w2 * sig2 / sig1 - (A.T).dot(dv)) / sigx
        Adx = A.dot(dx)
        ATdv = (A.T).dot(dv)

        du = (w2 - sig2 * dx) / sig1
        dlamu1 = lamu1xoofu1 * (du - dx) - lamu1 - ootau * oofu1
        dlamu2 = lamu2xoofu2 * (dx + du) - lamu2 - ootau * oofu2

        s = 1.0
        indp = cp.less(dlamu1, 0.0)
        indn = cp.less(dlamu2, 0.0)
        if cp.any(indp):
            s = min(s, cp.min(-lamu1[indp] / dlamu1[indp]))
        if cp.any(indn):
            s = min(s, cp.min(-lamu2[indn] / dlamu2[indn]))
        indp = cp.greater(dx - du, 0.0)
        indn = cp.greater(-dx - du, 0.0)
        if cp.any(indp):
            s = min(s, cp.min(-fu1[indp] / (dx[indp] - du[indp])))
        if cp.any(indn):
            s = min(s, cp.min(-fu2[indn] / (-dx[indn] - du[indn])))
        s = 0.99 * s

        for j in range(biter):
            xp = x + s * dx
            up = u + s * du
            vp = v + s * dv
            ATvp = ATv + s * ATdv
            lamu1p = lamu1 + s * dlamu1
            lamu2p = lamu2 + s * dlamu2
            fu1p = xp - up
            fu2p = -xp - up
            rdp[:n] = lamu1p - lamu2p + ATvp
            rdp[n:] = -lamu1p - lamu2p
            rdp += gradf0
            rcp[:n] = -lamu1p * fu1p
            rcp[n:] = lamu2p * fu2p
            rcp -= ootau
            rpp = rpri + s * Adx
            s *= beta
            if (cp.sqrt(norm(rdp)**2 + norm(rcp)**2 + norm(rpp)**2) <=
                (1 - alpha * s) * resnorm):
                break
        else:
            break

        x = xp
        lamu1 = lamu1p
        lamu2 = lamu2p
        fu1 = fu1p
        fu2 = fu2p
        sdg = -(cp.inner(fu1, lamu1) + cp.inner(fu2, lamu2))
        if sdg < tol:
            return cp.asnumpy(x)

        u = up
        v = vp
        ATv = ATvp
        tau = 2.0 * n * mu / sdg
        rpri = rpp
        rcent[:n] = lamu1 * fu1
        rcent[n:] = lamu2 * fu2
        ootau = 1.0 / tau
        rcent -= ootau
        rdual[:n] = lamu1 - lamu2 + ATv
        rdual[n:] = -lamu1 + lamu2
        rdual += gradf0
        resnorm = cp.sqrt(norm(rdual)**2 + norm(rcent)**2 + norm(rpri)**2)

    return cp.asnumpy(x)