a
    g:Y                     @   s  d Z ddlZddlZddlZddlmZ ddlmZ	 ddl
mZ ddlZddlmZ ddlZddlmZmZmZmZmZ ddlZdd ZG dd	 d	eZd!ddZd"ddZd#ddZdd Zd$ddZ d%ddZ!e"dkre Z#ej$%d e  e&d e e#   e	'  dS )&z[
Created on Mon Feb 13 16:05:41 2023

@author: Albert

Test function for the multiset CCA

    N)	Rectangle)eigh)OUTPUT_PATHTRACKED_FIBERS_PATHSESSION_NAMESSESSION_TIMESSESSION_REPEATSc                   C   s   d S N r
   r
   r
   FD:\MPI_Brain\Codes\aversive_scripts_2025\aversive_scripts_2025\mCCA.pymain   s    r   c                   @   s6   e Zd Zdd ZdddZdd Zdd	 Zd
d ZdS )MultisetCCAc                 C   s   d S r	   r
   selfr
   r
   r   __init__#   s    zMultisetCCA.__init__Nc                    s   t  _ fddtjD }t t|dkrBt| td fddtjD }t t|dkrrtd d jd _|dkrj}|_t	
jj fd	 }t	
jj fd	 }tjD ]}tjD ]}t	 |  | j}	|	||j |d j |j |d j f< ||kr|	||j |d j |j |d j f< qqdjd  ||  }
|}t|
|d
\}t	|}t	jdd|dj _fddtjD _fddtjD _jjfS )z dataset_list: list of datasets, each assumed to be of size (N features) X (N samples)
            feature number must be the same across datasets, but samples can be different
            If cca_dim is None, use all. Otherwise must be int.
        c                    s   g | ]} | j d  qS )r   shape.0mdataset_listr
   r   
<listcomp>-       z#MultisetCCA.fit.<locals>.<listcomp>   z1Not all datasets have the same number of featuresc                    s   g | ]} | j d  qS )r   r   r   r   r
   r   r   2   r   z0Not all datasets have the same number of samplesr   N   )baxisc                    s2   g | ]*} |j  |d  j  dj f qS )r   N)featuresr   )eigvecsr   r
   r   r   R   r   c                    s   g | ]}t j j| qS r
   )nplinalginvinv_transf_listr   r   r
   r   r   S   r   )lenMrangesetprint
ValueErrorr   r   cca_dimr!   zerosmatmulTr   flipeigvalsr$   transf_list)r   r   r+   features_listsamples_listRRDDm1m2RjkABr0   r
   )r   r    r   r   fit&   s:    
48
zMultisetCCA.fitc                 C   s   | j | | S )zl Transform from the specified dataset's space to canonical.
        Data must be (N features) X (N samples) )r1   )r   datar   r
   r
   r   to_canonicalW   s    zMultisetCCA.to_canonicalc                 C   s   | j | | S )z Transform from canonical space to the specified dataset's space 
            Canonical data must be of size (cca_dim) X (N samples))r$   )r   canonical_datar   r
   r
   r   from_canonical\   s    zMultisetCCA.from_canonicalc                 C   s   |  | |||S )zr Transform data from dataset m1's space to dataset m2's space
            data must be (N_features) X (N_samples) )r?   r=   )r   r<   r6   r7   r
   r
   r   aligna   s    zMultisetCCA.align)N)__name__
__module____qualname__r   r;   r=   r?   r@   r
   r
   r
   r   r   !   s
   
1r        positionTFc              
      s  t |  |rt D ]}| | }	t| }
tdtj }tdtj }tjj||dtdd }t	t|
j
d }||d |d|  }|	| | |< qg }g t D ]L}| | }	t| }
tj|	|
||d|dd	\}}}|| | qt||d
\}d j
d t \}}|dkr|dkr fddt D }fddt D |d< |}n|dkr|dkr҇ fddt D }fddt D |d< |}n|dkr&|dkr& fddt D }fddt D |d< | }nd|dkr|dkrt| |d
\} fddt D }fddt D |d< |}||fS )a   
        Perform multiset CCA on "raw" M datasets. They are allowed to be of different sizes, so trimming, warping, etc. must be performed.
        Because of this, data warping (making each trial the same size, with each trial defined as passing the max_pos mark) is performed as well
        
        INPUT:
        
        pos_list: list of M position arrays, of size samples1, samples2, etc.
        data_list: list of M data arrays, each of size (i features) X (j samples)
            Note that neither features nor samples are assumed to be equal across datasets.
            They will be trimmed to account for that
        max_pos : maximum value that can be taken by position, we assume periodicity
        warping_bins : each trial will be split into this amount of bins
        warp_based_on : 'position' or 'time', type of warping (see warping() for details)
        return_warped_data : if True, returns the data warped to have the same number of bins per trial.
        return_trimmed_data : if True, returns the data trimmed so each session has the same number of trials. 
        shuffle: randomly shifts each session independently
        
        OUTPUT:
        pos_list_aligned : the position values of the aligned data
        data_dict_aligned : {dataset index : [list of datasets aligned to it]}
        mCCA: multiset CCa object (includes transformation matrices as attributes)
            
    g      ?g      ?r   )sizedtyper   N	averagingT)max_poswarp_sampling_typewarp_based_onreturn_flattenedrJ   Fc                    s*   i | ]"   fd dt D qS )c                    s&   g | ]} | d   |qS r	   r@   r   r7   )r+   data_list_warpedr6   mCCAr
   r   r      r   2perform_warped_mCCA.<locals>.<dictcomp>.<listcomp>r'   r   )r&   r+   rQ   rR   r6   r   
<dictcomp>   r   z'perform_warped_mCCA.<locals>.<dictcomp>c                    s$   g | ]} | d   |qS r	   r=   r   )r+   rQ   rR   r
   r   r      r   z'perform_warped_mCCA.<locals>.<listcomp>	canonicalc                    s(   i | ]    fd dt D qS )c                    s   g | ]}  | |qS r
   rO   rP   )data_list_trimmedr6   rR   r
   r   r      r   rS   rT   rU   )r&   rZ   rR   rV   r   rW      r   c                    s   g | ]}  | |qS r
   rX   r   )rZ   rR   r
   r   r      r   c                    s*   i | ]"   fd dt D qS )c                    s&   g | ]} | d   |qS r	   rO   rP   )r+   	data_listr6   rR   r
   r   r      r   rS   rT   rU   )r&   r+   r[   rR   rV   r   rW      r   c                    s$   g | ]} | d   |qS r	   rX   r   )r+   r[   rR   r
   r   r      r   c                    s*   i | ]"   fd dt D qS )c                    s&   g | ]} | d   |qS r	   rO   rP   )r+   rZ   r6   rR   r
   r   r      r   rS   rT   rU   )r&   r+   rZ   rR   rV   r   rW      r   c                    s$   g | ]} | d   |qS r	   rX   r   )r+   rZ   rR   r
   r   r      r   )r%   r'   r!   copyintpparamMAX_POSrandomrandintlistr   pfwarpingappendtrim_dataset_listr   r;   )pos_listr[   rJ   warping_binsrL   return_warped_datareturn_trimmed_datashuffler   posr<   	min_shift	max_shiftrandom_shiftidxspos_list_warped
pos_warpeddata_warped_pos_list_trimmedr1   r$   data_dict_alignedpos_list_alignedr
   )r&   r+   r[   rZ   rQ   rR   r   perform_warped_mCCAg   sP    rx   c                 C   s   t j}t| D ]\}}|jd }|du r.|}nt|ttt jfv rLt|}njt|tfv r|du rjt	d|| }d|v rt|d|
d }	t||	d }nt|}	t||	}t |||g}qt|}g }
t| D ]\}}|
|d|  q|
S )a   Given a list of PCA arrays, reduce all to the SAME specified pca_dim.
            e.g. if specified dimension if 10, and one pca has only 8 components, then ALL will be reduced to 8
            
        pca_list: list of PCA arrays, each of dimension "num features" X "num timepoints"
        pca_dim: specifies dimensions to keep
            -if None, take all
            -if int, take that many
            -if 'X%', where "X" is a scalar, take dimensions that explain at least X% of the variance for each PCA (needs variance explained list)
            -if 'X', where "X" is a float between 0 and 1, interpret it as taking X*100% of the variance (needs variance explained list)
        variance_explained_list: list of variance explained (output from scikit learn's PCA)
    
    r   NzEPlease specify variance explained if "pca_dim" is set to a proportion%d   )r!   inf	enumerater   typer]   floatint32str	TypeErrorfindrc   dimensions_to_explain_varianceminre   )pca_listpca_dimvariance_explained_listnum_components_to_setpca_idxpcacurrent_num_componentsnum_componentsvariance_explainedvariance_to_explainpca_list_reducedr
   r
   r   set_dimension_of_pca_list   s,    

	r   c                    s   t |} fdd}|| tfddt|D }g }g }t|D ]d}| }	|	d d }
|	|d  d }| | |
| }|| || dd|
|f }|| qH||fS )z< Trim datasets to have same number of rounds and dimensions c                    s    fdd| D S )Nc                    s   g | ]}t j| d ddqS )T   )rJ   full_rounds_only
pos_thresh)rc   get_round_endtimes_pairs)r   rl   rN   r
   r   r      r   z7trim_dataset_list.<locals>.<lambda>.<locals>.<listcomp>r
   )rg   rN   r
   r   <lambda>   r   z#trim_dataset_list.<locals>.<lambda>c                    s   g | ]}t  | qS r
   )r%   )r   i)round_endtimes_listr
   r   r      r   z%trim_dataset_list.<locals>.<listcomp>r   r   N)r%   r!   r   r'   re   )rg   r[   rJ   r&   get_endtimes_from_list
min_roundsru   rZ   r   startend_tuplesstart_first_roundend_min_roundpos_trimmeddata_trimmedr
   )rJ   r   r   rf      s    
rf   c                 C   s   dd |   D }|S )zs Used on the output of perform_warped_mCCA. Adds the canonical space.
        Normalizes all PCAs individually
    c                 S   s    i | ]\}}|d d |D qS )c                 S   s   g | ]}t j|d dd qS )r   r   r   )rc   normalize_data)r   r   r
   r
   r   r     r   z9normalize_pca_dict_aligned.<locals>.<dictcomp>.<listcomp>r
   )r   kr   r
   r
   r   rW     r   z.normalize_pca_dict_aligned.<locals>.<dictcomp>)items)pca_dict_alignedrR   pca_dict_aligned_normalizedr
   r
   r   normalize_pca_dict_aligned  s    r      sseWienerc              	   C   s~  t | }g }	g }
t|D ]^}|| | |  }}tj|||||d\}}}|	| tj||d||d\}}}|
| qt||f}t|D ]b}|
| }t|D ]L}||kr|	| }n*|| | |  }}tj|||d|d\}}||||f< qqt||f}t|D ]l}|
| }t|D ]T}||kr4|	| }n.|| | ||  }}tj|||d|d\}}||||f< qq||fS )a   Given M sets of data, return the prediction errors when predicting on unaligned vs aligned data
    
        INPUTS:
        
        pos_list: list of M position arrays, of size samples1, samples2, etc.
        data_list: list of M data arrays, each of size (N features) X (j samples). Used as the reference for the self-prediction, may be different than the aligned data with itself
        pos_list_aligned list of M position arrays, of size samples1, samples2, etc. for the aligned datasets (can be the same as pos_list)
        data_dict_aligned: element i is a list of the datasets from data_list aligned with dataset i.
                            Each dataset must have N features, but can have different samples from the unaligned data
        For the other parameters, see predict_distance_CV
        
        OUTPUTS:
        unaligned_error_array: element (ij) is the error of predicting position in dataset j using predictor from i
    n_splitspmaxpredictor_namer   Tperiodicr   )r%   r'   rc   predict_position_CVre   r!   r,   &predict_position_from_predictor_object)rg   r[   rw   rv   rJ   r   
error_typer   r&   self_error_listself_predictor_listr   r<   rF   pos_prederrorrt   	predictorunaligned_error_arrayr6   r7   aligned_error_arrayr
   r
   r   get_cross_prediction_errors  s8    



r   c           &   	      s  d}d}d}t | }	t|	d }
 fddt|	D }t| ||  ||||\}}ttj|dd}d} d	 | }| | }tj||d||d
\}}}g }t d	 D ]<\}}||krq| | }tj	|||d|d\}}|
| q|
t| t|}||	kr
d	}|dkr<tdtj|| dd tj|dd| |dkr | }dd|	 f}t j}t|
D ]\}}||	kr|d	k  | }tjd|	d||ddid\}} |d7 }t|	D ]l}t||	d k}!|| }"| | }tj|"||d d\}#}$}tj|$|#| d|f d|!d | d|f   q|  qlt|
D ]\}}||	krX|d	k  | }tj|dd}tjdd}%|d7 }t|	D ]T}t||	d k}!|| }"| | }tj|"||d d\}#}$}tj|$|#|%d|!d q|%  |  q>t|
D ]\}}||	kr|d	k  | }|  tj|dd}tjdd}%|d7 }t|	D ]:}t||	d k}!|| }"| | }tj|"||%d|!d qP|%  |  q|dkr|S ||fS dS )a;   
        Use the prediction error to find the best data alignment, and normalize each session separately if indicated
        pos_list: list of position arrays for each space
        pca_dict_aligned_with_canonical: output from "normalize_pca_dict_aligned" function. Assumes it has the "canonical" space as well
        set_align_to: if not None, all sessions will be aligned to this one (rather than choosing the one with most SI)
        return_aligned_dict: if True, the dictionary with all the aligned PCAs (after normalization) and the chosen space is returned
    r   r   r   r   c                    s   g | ]} | | qS r
   r
   r   r   r
   r   r   x  r   z*return_best_mCCA_space.<locals>.<listcomp>r   r   rY   r   Tr   z Optimized alignment, error: %.2fr   )decimals   F
projection3d)nrowsncolssqueezefigsizenum
subplot_kwr   )position_bin_size)axscattercbar)   r   )r   )r   N)r%   r'   r   rb   r!   meanrc   r   r|   r   re   argminr)   aroundpltgcfnumbersubplotsr]    compute_average_data_by_positionplot_pca_with_positionset_axis_offtight_layoutfiguresubplot)&rg   r   rJ   plotreturn_error_listverbosecv_foldsr   r   r&   sessions_to_test_alignmentr   r   r   
error_listcanonical_refpca_to_testrl   rt   r   error_list_to_avgr   position_predr   best_alignmentpca_list_alignedr   fig_numm_to_align_idx
m_to_alignpca_list_to_plotfigaxsr   r   pos_binspca_avgr   r
   r   r   return_best_mCCA_spacea  s    	


(





r   __main__r   zTime Ellapsed: %.1f)rD   rE   rF   TTF)N)rD   )rD   r   r   r   )rD   FFT)(__doc__timescipynumpyr!   numpy.mamamatplotlib.pyplotpyplotr   matplotlib.patchesr   seabornsnsscipy.linalgr   project_parametersr^   r   r   r   r   r   processing_functionsrc   r   objectr   rx   r   rf   r   r   r   rA   ZTTr`   seedr)   showr
   r
   r
   r   <module>   s8   F
]
5
  
D
 
