PBMC#

Library imports#

import os
import sys

import numpy as np
import pandas as pd
import torch

import matplotlib.pyplot as plt
import mplscience
import seaborn as sns

import scanpy as sc
import scvelo as scv
import scvi
from scvelo.plotting.simulation import compute_dynamics
from velovi import preprocess_data, VELOVI
from velovi._model import _compute_directional_statistics_tensor

sys.path.append("../..")
from paths import DATA_DIR, FIG_DIR
Global seed set to 0

General settings#

scvi.settings.dl_pin_memory_gpu_training = False
sns.reset_defaults()
sns.reset_orig()
scv.settings.set_figure_params('scvelo', dpi_save=400, dpi=80, transparent=True, fontsize=20, color_map='viridis')
SAVE_FIGURES = True
if SAVE_FIGURES:
    os.makedirs(FIG_DIR / 'pbmc', exist_ok=True)

Function definition#

def fit_velovi(bdata):
    VELOVI.setup_anndata(bdata, spliced_layer="Ms", unspliced_layer="Mu")

    vae = VELOVI(bdata)

    vae.train()

    df = vae.history["elbo_train"].iloc[20:].reset_index().rename(columns={'elbo_train': 'elbo'})
    df['set'] = 'train'

    _df = vae.history["elbo_validation"].iloc[20:].reset_index().rename(columns={'elbo_validation': 'elbo'})
    _df['set'] = 'validation'

    df = pd.concat([df, _df], axis=0).reset_index(drop=True)

    with mplscience.style_context():
        sns.set_style(style="whitegrid")
        fig, ax = plt.subplots(figsize=(6, 4))
        sns.lineplot(data=df, x='epoch', y='elbo', hue='set', palette=['#0173B2', '#DE8F05'], ax=ax)

    latent_time = vae.get_latent_time(n_samples=25)
    velocities = vae.get_velocity(n_samples=25, velo_statistic="mean")

    t = latent_time
    scaling = 20 / t.max(0)

    bdata.layers["velocities_velovi"] = velocities / scaling
    bdata.layers["latent_time_velovi"] = latent_time

    bdata.var["fit_alpha"] = vae.get_rates()["alpha"] / scaling
    bdata.var["fit_beta"] = vae.get_rates()["beta"] / scaling
    bdata.var["fit_gamma"] = vae.get_rates()["gamma"] / scaling
    bdata.var["fit_t_"] = (
        torch.nn.functional.softplus(vae.module.switch_time_unconstr)
        .detach()
        .cpu()
        .numpy()
    ) * scaling
    bdata.layers["fit_t"] = latent_time.values * scaling[np.newaxis, :]
    bdata.var['fit_scaling'] = 1.0

    return vae
def compute_sign_variance(adata, vae):
    v_stack = vae.get_velocity(n_samples=50, velo_statistic="mean", return_mean=False)
    pos_freq = (v_stack >= 0).mean(0)
    # neg_freq = (v_stack < 0).mean(0)

    adata.layers["velocity"] = v_stack.mean(0)

    var_freq = pos_freq * (1 - pos_freq)
    adata.obs["sign_var"] = var_freq.mean(1)

    adata.layers["sign_var"] = var_freq
    adata.layers["variance"] = v_stack.var(0)
def compute_sign_var_score(adata, labels_key, vae):
    compute_sign_variance(adata, vae)

    sign_var_df = adata.to_df("sign_var")
    expr_df = adata.to_df("Ms")

    prod_df = sign_var_df * np.abs(expr_df)
    prod_df[labels_key] = adata.obs[labels_key]
    prod_df = prod_df.groupby(labels_key).mean()

    sign_var_df[labels_key] = adata.obs[labels_key]
    sign_var_df = sign_var_df.groupby(labels_key).mean()

    return sign_var_df.mean(0)
def gene_rank(adata, vkey="velocities_velovi"):
    from scipy.stats import rankdata
    scv.tl.velocity_graph(adata, vkey=vkey)
    tm = scv.utils.get_transition_matrix(
        adata, vkey=vkey, use_negative_cosines=True, self_transitions=True
    )
    tm.setdiag(0)
    adata.layers["Ms_extrap"] = tm @ adata.layers["Ms"]
    adata.layers["Ms_delta"] = adata.layers["Ms_extrap"] - adata.layers["Ms"]

    prod = adata.layers["Ms_delta"] * adata.layers[vkey]
    ranked = rankdata(prod, axis=1)
    adata.layers["product_score"] = prod
    adata.layers["ranked_score"] = ranked
def plot_phase_portrait(adata, gene, color, figsize=(6, 6)):
    fig, ax = plt.subplots(figsize=figsize)

    df = pd.DataFrame(
        {
            'unspliced': adata[:, gene].layers['Mu'].squeeze().copy(),
            'spliced': adata[:, gene].layers['Ms'].squeeze().copy(),
            'color': color
        }
    )

    with mplscience.style_context():
        sns.scatterplot(data=df, x='spliced', y='unspliced', c=color, s=25, ax=ax);

        _, unspliced, spliced = compute_dynamics(adata, basis=gene, extrapolate=True, sort=True)
        df = pd.DataFrame(
            {
                'unspliced': unspliced.squeeze(),
                'spliced': spliced.squeeze(),
            }
        )

        ax.plot(spliced, unspliced, color="purple", linewidth=2)

        spliced_steady_state = np.linspace(np.min(spliced), np.max(spliced))
        unspliced_steady_state = adata.var.loc[gene, 'fit_gamma'] / adata.var.loc[gene, 'fit_beta'] * (spliced_steady_state - np.min(spliced_steady_state)) + np.min(unspliced)
        ax.plot(spliced_steady_state, unspliced_steady_state, color='purple', linestyle="--", linewidth=2);

    ax.axis('off')
    if SAVE_FIGURES:
        fig.savefig(
            FIG_DIR / 'pbmc' / f'phase_portrait_{gene}.svg',
            format="svg",
            transparent=True,
            bbox_inches='tight'
        )
def plot_phase_portrait(adata, gene, color, permuted=False, figsize=(6, 6)):
    fig, ax = plt.subplots(figsize=figsize)

    df = pd.DataFrame(
        {
            'unspliced': adata[:, gene].layers['Mu'].squeeze().copy(),
            'spliced': adata[:, gene].layers['Ms'].squeeze().copy(),
            'color': color
        }
    )

    with mplscience.style_context():
        sns.scatterplot(data=df, x='spliced', y='unspliced', c=color, s=25, ax=ax);

        _, unspliced, spliced = compute_dynamics(adata, basis=gene, extrapolate=True, sort=True)
        df = pd.DataFrame(
            {
                'unspliced': unspliced.squeeze(),
                'spliced': spliced.squeeze(),
            }
        )

        ax.plot(spliced, unspliced, color="purple", linewidth=2)

        spliced_steady_state = np.linspace(np.min(spliced), np.max(spliced))
        unspliced_steady_state = adata.var.loc[gene, 'fit_gamma'] / adata.var.loc[gene, 'fit_beta'] * (spliced_steady_state - np.min(spliced_steady_state)) + np.min(unspliced)
        ax.plot(spliced_steady_state, unspliced_steady_state, color='purple', linestyle="--", linewidth=2);

    ax.axis('off')
    
    if SAVE_FIGURES:
        if permuted:
            fname = f'phase_portrait_{gene}_permuted'
        else:
            fname = f'phase_portrait_{gene}'
        fig.savefig(
            FIG_DIR / 'pbmc' / f'{fname}.svg',
            format="svg",
            transparent=True,
            bbox_inches='tight'
        )
def plot_perm_scores(adata, perm_scores, gene, color_label, figsize=(6, 4)):
    df = pd.DataFrame(perm_scores.loc[gene])
    df["Cell type"] = df.index
    order = adata.obs[color_label].cat.categories.tolist()
    
    with mplscience.style_context():
        sns.set_style(style="whitegrid")
        fig, ax = plt.subplots(figsize=figsize)
        sns.barplot(
            data=df,
            y=gene,
            x="Cell type",
            palette=adata.uns[f"{color_label}_colors"],
            order=order,
            ax=ax,
        )

    if SAVE_FIGURES:
        fig.savefig(
            FIG_DIR / 'pbmc' / f'permutation_score_{gene}.svg',
            format="svg",
            transparent=True,
            bbox_inches='tight'
        )

Data loading#

adata = sc.read(DATA_DIR / 'pbmc' / 'pbmc_10k.h5ad')
adata
AnnData object with n_obs × n_vars = 11950 × 58367
    obs: 'celltype'
    uns: 'celltype_colors'
    layers: 'spliced', 'unspliced'

Data preprocessing#

scv.pp.filter_and_normalize(adata, min_shared_counts=20, n_top_genes=2000)
scv.pp.moments(adata, n_pcs=30, n_neighbors=30)
adata = preprocess_data(adata)
Filtered out 48988 genes that are detected 20 counts (shared).
Normalized count data: X, spliced, unspliced.
Extracted 2000 highly variable genes.
Logarithmized X.
computing neighbors
    finished (0:00:19) --> added 
    'distances' and 'connectivities', weighted adjacency matrices (adata.obsp)
computing moments based on connectivities
    finished (0:00:01) --> added 
    'Ms' and 'Mu', moments of un/spliced abundances (adata.layers)
computing velocities
    finished (0:00:00) --> added 
    'velocity', velocity vectors for each individual cell (adata.layers)
sc.tl.umap(adata)
scv.pl.scatter(adata, basis='umap', c='celltype', legend_loc='right', s=5, dpi=200)
../_images/486d29800940e3c8251810dfaa7cf021c9c8aef46ebb0f22ba94aad8fef900fa.png

Model training#

vae = fit_velovi(adata)
/home/icb/philipp.weiler/miniconda3/envs/velovi-py39/lib/python3.9/site-packages/torch/distributed/_sharded_tensor/__init__.py:8: DeprecationWarning: torch.distributed._sharded_tensor will be deprecated, use torch.distributed._shard.sharded_tensor instead
  warnings.warn(
GPU available: True, used: True
TPU available: False, using: 0 TPU cores
IPU available: False, using: 0 IPUs
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]
Set SLURM handle signals.
Epoch 276/500:  55%|█████▌    | 276/500 [03:40<02:59,  1.25it/s, loss=-2.59e+03, v_num=1]
Monitored metric elbo_validation did not improve in the last 45 records. Best score: -2679.995. Signaling Trainer to stop.
../_images/180b5a86a128f1868d9bcd06b3077354c02771fc9e45d04bbf1603acc840a3ef.png
scv.tl.velocity_graph(adata, vkey="velocities_velovi", sqrt_transform=False)
scv.tl.velocity_embedding(
    adata, vkey="velocities_velovi", use_negative_cosines=True, self_transitions=True
)
computing velocity graph (using 1/64 cores)
    finished (0:00:35) --> added 
    'velocities_velovi_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity embedding
    finished (0:00:02) --> added
    'velocities_velovi_umap', embedded velocity vectors (adata.obsm)
fig, ax = plt.subplots(figsize=(6, 4))
scv.pl.velocity_embedding_stream(
    adata, vkey="velocities_velovi", color=["celltype"], cmap="viridis", legend_loc=False, title='', ax=ax
)

if SAVE_FIGURES:
    fig.savefig(
        FIG_DIR / 'pbmc' / 'velocity_stream.svg',
        format="svg",
        transparent=True,
        bbox_inches='tight'
    )
../_images/c14e1bf236008e9be01dd77334b194c0d6080909b239348a503be1683639ed83.png

Uncertainty#

Extrinsic#

extrapolated_cells_list = []
for i in range(25):
    vkey = "velocities_velovi_{i}".format(i=i)
    v = vae.get_velocity(n_samples=1, velo_statistic="mean")
    adata.layers[vkey] = v
    scv.tl.velocity_graph(adata, vkey=vkey, sqrt_transform=False, approx=True)
    t_mat = scv.utils.get_transition_matrix(
        adata, vkey=vkey, self_transitions=True, use_negative_cosines=True
    )
    extrapolated_cells = np.asarray(t_mat @ adata.layers["Ms"])
    extrapolated_cells_list.append(extrapolated_cells)
extrapolated_cells = np.stack(extrapolated_cells_list)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_0_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_1_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_2_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_3_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_4_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:06) --> added 
    'velocities_velovi_5_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_6_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_7_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_8_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_9_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_10_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_11_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_12_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_13_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_14_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_15_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_16_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:08) --> added 
    'velocities_velovi_17_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_18_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_19_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_20_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:06) --> added 
    'velocities_velovi_21_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_22_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_23_graph', sparse matrix with cosine correlations (adata.uns)
computing velocity graph (using 1/64 cores)
    finished (0:00:07) --> added 
    'velocities_velovi_24_graph', sparse matrix with cosine correlations (adata.uns)
df, _ = _compute_directional_statistics_tensor(extrapolated_cells, n_jobs=4, n_cells=adata.n_obs)
INFO     velovi: Computing the uncertainties...                                              
[Parallel(n_jobs=4)]: Using backend LokyBackend with 4 concurrent workers.
Global seed set to 0
Global seed set to 0
Global seed set to 0
Global seed set to 0
[Parallel(n_jobs=4)]: Done  24 tasks      | elapsed:   15.6s
[Parallel(n_jobs=4)]: Done 3348 tasks      | elapsed:   18.5s
[Parallel(n_jobs=4)]: Done 11819 tasks      | elapsed:   25.1s
[Parallel(n_jobs=4)]: Done 11950 out of 11950 | elapsed:   25.2s finished
for c in df.columns:
    adata.obs[c + "_extrinsic"] = np.log10(df[c].values)
with mplscience.style_context():
    fig, ax = plt.subplots(figsize=(6, 4))
    scv.pl.umap(adata, color='directional_cosine_sim_variance_extrinsic', perc=[5, 95], cmap='viridis', ax=ax)

if SAVE_FIGURES:
    fig.savefig(
        FIG_DIR / 'pbmc' / 'directional_cosine_sim_variance_extrinsic.svg',
        format="svg",
        transparent=True,
        bbox_inches='tight'
    )
../_images/59275ca0edbe814b782aa00d001b99a1eece930c3fa25463ed2dde081213a441.png
df = pd.DataFrame(
    {
        'Clusters': adata.obs['celltype'],
        'Extrinsic directional cosine sim. variance': adata.obs['directional_cosine_sim_variance_extrinsic']
    }
)
palette = dict(zip(adata.obs['celltype'].cat.categories, adata.uns['celltype_colors']))

with mplscience.style_context():
    sns.set_style(style="whitegrid")
    fig, ax = plt.subplots(figsize=(6, 4))
    sns.violinplot(data=df, x="Clusters", y="Extrinsic directional cosine sim. variance", palette=palette, ax=ax);
    ax.set_ylabel('')

if SAVE_FIGURES:
    fig.savefig(
        FIG_DIR / 'pbmc' / 'extrinsic_directional_cosine_sim_variance_violin.svg',
        format="svg",
        transparent=True,
        bbox_inches='tight'
    )
../_images/af6fa4d7c82cdcd491b4e2fc34bdc590138d6630952314bd8f7b06f56a2a439d.png

Velocity coherence#

sign_score = compute_sign_var_score(adata, 'celltype', vae)
gene_rank(adata)
computing velocity graph (using 1/64 cores)
    finished (0:00:36) --> added 
    'velocities_velovi_graph', sparse matrix with cosine correlations (adata.uns)

Monocytes#

cell_subset = adata.obs.query("celltype == 'Mono'").index
cluster_data = adata[cell_subset]
cluster_data.obs['mean_product_score_per_cell_mono'] = cluster_data.layers['product_score'].mean(axis=1)
cluster_data.var['mean_product_score_per_gene_mono'] = cluster_data.layers['product_score'].mean(axis=0)
fig, ax = plt.subplots(figsize=(6, 4))
sns.histplot(data=cluster_data.var, x='mean_product_score_per_gene_mono', ax=ax);

if SAVE_FIGURES:
    fig.savefig(
        FIG_DIR / 'pbmc' / 'mean_product_score_per_gene_mono_histogram.svg',
        format="svg",
        transparent=True,
        bbox_inches='tight'
    )
../_images/1416435d695dee8f4fda2e4e92076a7a315d9bc56678116d7b4cf754c6aaa978.png

B cells#

cell_subset = adata.obs.query("celltype == 'B'").index
cluster_data = adata[cell_subset]
cluster_data.obs['mean_product_score_per_cell_b'] = cluster_data.layers['product_score'].mean(axis=1)
cluster_data.var['mean_product_score_per_gene_b'] = cluster_data.layers['product_score'].mean(axis=0)
fig, ax = plt.subplots(figsize=(6, 4))
sns.histplot(data=cluster_data.var, x='mean_product_score_per_gene_b', ax=ax);

if SAVE_FIGURES:
    fig.savefig(
        FIG_DIR / 'pbmc' / 'mean_product_score_per_gene_b_histogram.svg',
        format="svg",
        transparent=True,
        bbox_inches='tight'
    )
../_images/05b582bd719e17313082014b94348bf431eeddc3642413fb08c46723b062b816.png

NK cells#

cell_subset = adata.obs.query("celltype == 'NK'").index
cluster_data = adata[cell_subset]
cluster_data.obs['mean_product_score_per_cell_nk'] = cluster_data.layers['product_score'].mean(axis=1)
cluster_data.var['mean_product_score_per_gene_nk'] = cluster_data.layers['product_score'].mean(axis=0)
fig, ax = plt.subplots(figsize=(6, 4))
sns.histplot(data=cluster_data.var, x='mean_product_score_per_gene_nk', ax=ax);

if SAVE_FIGURES:
    fig.savefig(
        FIG_DIR / 'pbmc' / 'mean_product_score_per_gene_nk_histogram.svg',
        format="svg",
        transparent=True,
        bbox_inches='tight'
    )
../_images/a0dcf2fb8c6ff6f632e51e0739dcfa32cc5f09546f5fd90f69d44ec9c95512ae.png

Permutation analysis#

perm_scores, permuted_adata = vae.get_permutation_scores(labels_key='celltype')
INFO     Input AnnData not setup with scvi-tools. attempting to transfer AnnData setup       
INFO     Input AnnData not setup with scvi-tools. attempting to transfer AnnData setup       
full_perm_df = pd.DataFrame(columns=["Score", "Dataset"])

max_ratio = np.nanmax(perm_scores.values, axis=1)
scores = max_ratio.tolist()
dataset = ['PBMC'] * len(max_ratio)

full_perm_df["Score"] = scores
full_perm_df["Dataset"] = dataset
color = adata.obs['celltype'].astype(str).replace(
    dict(zip(adata.obs['celltype'].cat.categories, adata.uns['celltype_colors']))
).tolist()

Lowest ranked genes#

for gene in adata.var_names[np.argsort(scores)[:5]]:
    plot_phase_portrait(adata, gene, color)
    plot_phase_portrait(permuted_adata, gene, color, permuted=True)
    plot_perm_scores(adata, perm_scores, gene, 'celltype')
../_images/f20cff04799ed0a6f7b6c470b41b19765ccc4b19b98290faad83d0dae09ee692.png ../_images/c58ec60fb14a5a5f47a9aee9392995da738350cfa1caaa386e424cf6a881d5c1.png ../_images/b4c52ade0619be77722f1a72cf6df8890f4ec23cce22f89a1d34c1b46a2d9424.png ../_images/2e40bf0703f084061521b3a85f8c1efa2b02395d5a980e8cc4c23f27b4f2bbd9.png ../_images/1cc29be3f18403df4809a60f4c79dd7e8df25ab9f99434f39254324a427810e6.png ../_images/812b880470b9ae219f1a9f9d6f00ea123389d7a10aee281b9d99f6efa55e4755.png ../_images/b74e2a3e7e8ee84be572e159a014076d44f87424cb6e07d7ac6d9559d64a6e53.png ../_images/4fa5383a987e54b9c2e8fac497d7a72fad192a186f063101a56233c18c4e479a.png ../_images/9251cbdf7367a55859c6286002a4eccbc449c371218b7fd6676c4da31ddfc86e.png ../_images/c513f047cf7c5b618685a9d8c979fc2cc28fbdf7539a6b41c8ca452661f44b7c.png ../_images/5791a0283a1a6fbfdd82ed1227767d792a3092e8ced3c86b93495370494d1336.png ../_images/331dfb80561c071a71ba5819c35a82ea76236490e5207f7987e0d273c853d807.png ../_images/a8f1cc3fc3f9046935caad46cb933cfd9d2e45f4d86ca8ec81f3ccea47518e7e.png ../_images/7720fc018c751ba106de753c775fe200ef1b6179c44fb0655cea793b20c7c950.png ../_images/7a25010fe14a5d5491423503fbdea1ebc52713f0423b064fa751e9bd99ba63d8.png

Highest ranked genes#

for gene in adata.var_names[np.argsort(scores)[-5:]]:
    plot_phase_portrait(adata, gene, color)
    plot_phase_portrait(permuted_adata, gene, color, permuted=True)
    plot_perm_scores(adata, perm_scores, gene, 'celltype')
../_images/938a9d0ba377e972f904956651dbc815f188ff0dfa23edb8dc867ce326b85590.png ../_images/ae806cd6960a0d3d3aadadbaf9b5b9faf0e2cd5fdc52735a2e40f15b64cdd3a4.png ../_images/ec512cb3144fbd44b2cc80d02ffc93a7b0f6c4c551da49fda7ab1b2c32b96972.png ../_images/3832f34f7e84b50b8c500b0cf60685336280cb2117b843ae2cd426b068b8e652.png ../_images/bebbd940b902882c213ad394618dbb2a0d23a3027be0b9ad33daaedeb4aebaae.png ../_images/0d66b0ea2e3227822e3adbb82638f23cd3edb4f64948f691faf733d96aff875c.png ../_images/0a046039aa6e1d4ab4b1b717ba5b365badb4402b0e8812847030f29f8318c1f8.png ../_images/44aad37e1c8a3c08893da41e924d2cf22eab8ca17315c04d42644047abc0e941.png ../_images/57d468a0b1169d95f483234b6ba07feb789d076209e15a7c4c4da2189054466e.png ../_images/84c7129c558b60e2d735723956235003e4dc634e24f09ce18d2fd34909a2a3b4.png ../_images/9a7579ee4496866e9cda2ed69129ba66028cf3a249d4a6bcb64d240dc8fe3151.png ../_images/5a0fef5901aac7a0513b303efd5cd78a9478dd2ab246bfda1532420a38dd15ed.png ../_images/0aaba5542dabb4002cc2e95f6282843b563a4aadf4713008b27937e79e744b45.png ../_images/c2ae8b86fef0cd23ca2dac4167bffe06a9cb083fc1de641f0790c95cd42e762a.png ../_images/509ca9222c9a5f76a71ee0540a40b39df7ead332b222a51532a6c0a22ea88c0b.png