Mixture prior estimation for MASH#

Estimates the data-driven mixture of covariance matrices that a MASH model uses as its prior, from factor analyses and canonical patterns through residual-variance estimation to the fitted mixture.

Overview#

MASH borrows strength across conditions by putting a prior on effect-size vectors: a mixture of covariance matrices, each describing one pattern of sharing, such as an effect present in a single condition, shared equally everywhere, or shared with condition-specific magnitudes. This module estimates that mixture from the data before any MASH model is fitted, so one prior can serve several downstream analyses.

The work proceeds in four steps, in order.

  1. Covariance components. flash and flash_nonneg derive candidate patterns by empirical Bayes factor analysis, the second constraining the factors to be non-negative; pca derives them from the leading principal components of the strong effects; canonical writes the fixed patterns that need no fitting, namely an identity matrix, one matrix per condition, equal effects, and a few simple heterogeneity models. All four are produced by default, which is what --mixture-components lists.

  2. Residual variance. Correlated noise across conditions would otherwise be read as shared signal, so a residual covariance Vhat is estimated from the null effects. Five estimators are available: identity assumes no correlation, simple takes the empirical correlation of the null z-scores, simple_specific takes their covariance with a nearest-positive-definite correction, corshrink applies adaptive shrinkage to that correlation, and mle refines an estimate by EM on a random subset of the data.

  3. Mixture prior. One of three engines fits the mixture weights and refines the data-driven components. ed_bovy is the extreme deconvolution exported by mashr; ud and ud_unconstrained are the ED and TED updates from udr, and the code marks both as opt-in, ud for known numerical issues and ud_unconstrained because it expects independent, identically distributed data on the z-score scale. The engine reads the component files from step 1 together with the Vhat estimate from step 2.

  4. Plotting. plot_U draws the estimated covariance matrices as heatmaps ordered by their mixture weight.

Two settings tie the steps together. --effect-model decides whether effects are exchangeable on the effect-size scale (EE) or the z-score scale (EZ), and it appears in every file name from step 2 onwards. --vhat names which residual-variance estimate the engine should read, so it has to match the estimator that was actually run; the default is simple. mle is the exception in both directions: it needs a prior to exist already, and when --vhat mle is set the engines fall back to reading the simple estimate instead.

When to run it. After the per-condition summary statistics have been assembled into a single RDS by the effect-extraction step, and before fitting the MASH model, which consumes the prior produced here.

Input#

  • --data – the assembled summary statistics, an RDS holding effect sizes (.b), standard errors (.s) and z-scores (.z) for the strong, random and null subsets, plus XtX, each with one column per condition:

    List of 10
     $ random.z: num [1:68, 1:8] -0.286 0.909 1.362 -0.807 -0.185 ...
     $ strong.z: num [1:17, 1:8] 8.77 1.16 9.16 -4.99 -1.02 ...
     $ null.z  : num [1:68, 1:8] -0.508 -1.149 -0.422 -0.206 0.351 ...
     $ random.b: num [1:68, 1:8] -0.00799 0.0248 0.03869 -0.02005 -0.00388 ...
     $ strong.b: num [1:17, 1:8] 0.2491 0.0927 0.3318 -0.1553 -0.1398 ...
     $ null.b  : num [1:68, 1:8] -0.03792 -0.02839 -0.03397 -0.00571 0.03438 ...
     $ null.s  : num [1:68, 1:8] 0.0746 0.0247 0.0805 0.0278 0.0979 ...
     $ random.s: num [1:68, 1:8] 0.028 0.0273 0.0284 0.0249 0.021 ...
     $ strong.s: num [1:17, 1:8] 0.0284 0.0798 0.0362 0.0311 0.1374 ...
     $ XtX     : num [1:8, 1:8] 31.06 6.09 1.02 27.12 9.89 ...
    
  • --output-prefix – prefix for every output file; derived from the --data basename when omitted.

  • --cwd – output directory, default mashr_workflow_output.

  • --effect-modelEE or EZ, default EE.

  • --vhat – which residual-variance estimate the mixture engines read: identity, simple, mle, vhat_corshrink_xcondition or vhat_simple_specific. Default simple.

  • --mixture-components – which component files the engines combine, default flash flash_nonneg pca canonical.

  • --output-suffix – suffix carried on output names, default all.

Step-specific:

  • --npc (pca) – number of principal components, default 2. The comment beside it in the code says it is set to 3 as in the mash paper, which no longer matches the value.

  • --n-subset and --max-iter (vhat_mle) – rows sampled for the EM estimate, default 6000, and the iteration cap, default 6.

  • --max-comp, --to-cor, --tol, --remove-label, --name (plot_U) – how many components to draw (-1 for all), whether to convert covariances to correlations, the tolerance, whether to drop labels, and a suffix added to the plot file name. Note that plot_U redefines --data to mean the prior RDS rather than the summary statistics.

Runtime:

  • --container, --job-size, --walltime, --mem, --numThreads – software environment and cluster resources.

  • --modular-script-dir – location of the R drivers, default code/script.

Output#

  • <prefix>.flash.rds, <prefix>.flash_nonneg.rds, <prefix>.pca.rds, <prefix>.canonical.rds – the covariance components from step 1, each a named list of condition-by-condition matrices. On the toy data pca yields PCA_1, PCA_2 and tPCA and canonical yields 13 matrices, while both FLASH files are empty (NULL), so FLASH contributes nothing to the toy prior.

  • <prefix>.<effect_model>.V_<method>.rds – the residual variance estimate from step 2, one condition-by-condition matrix. The method is part of the name, so estimates from different estimators coexist.

  • <prefix>.<effect_model>.prior.rds – the prior itself: the list of covariance matrices U, their mixture weights w, and the log-likelihood trace of the fit. Components that received zero weight are kept in the list:

    List of 3
     $ U     :List of 17
     $ w     : Named num [1:17] 0.4714 0 0 0.0616 0.0949 ...
     $ loglik: num [1:1447] 0.705 3.455 3.719 4.142 5.068 ...
    names(U): XtX, PCA_1, PCA_2, tPCA, identity, ALL, Ast, End, Exc, Inh, Mic, OPC, Oli, equal_effects, simple_het_1, simple_het_2, simple_het_3
    
  • <prefix>.<effect_model>.prior_c.log and <prefix>.<effect_model>.prior_loglike.log – engine diagnostics.

  • <plot_prefix>.pdf – heatmaps of the covariance matrices, ordered by mixture weight.

Each step also writes .stdout and .stderr beside its output. Example results for the toy data are under output/mixture_prior/.

Minimal Working Example#

Step 1. Covariance components#

All four run independently of each other, and all four are expected by the engines in step 3, since --mixture-components defaults to the full set.

FLASH factors#

Empirical Bayes factor analysis of the strong effects.

Timing: ~5-15 min (on toy dataset)

sos run pipeline/mixture_prior.ipynb flash \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

FLASH factors, non-negative#

The same analysis with the factors constrained to be non-negative.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb flash_nonneg \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

Principal components#

Covariances built from the leading principal components; --npc sets how many.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb pca \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

Canonical patterns#

The fixed patterns that need no fitting: identity, one matrix per condition, equal effects, and simple heterogeneity models.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb canonical \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

Step 2. Residual variance#

Pick one estimator. Whichever is chosen has to be named to step 3 through --vhat, which defaults to simple.

identity#

Assume the conditions carry uncorrelated noise.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb vhat_identity \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

simple#

Empirical correlation of the null z-scores. This is the estimate the engines look for by default.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb vhat_simple \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

mle#

EM refinement on a random subset. It reads an existing prior, so it can only run after a first pass through step 3.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb vhat_mle \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

corshrink per condition#

Adaptive shrinkage applied to the null correlation.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb vhat_corshrink_xcondition \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

simple per condition#

Covariance of the null z-scores with a nearest-positive-definite correction.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb vhat_simple_specific \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

Step 3. Mixture prior#

Pick one engine. It reads the four component files from step 1 together with the Vhat estimate from step 2, and writes <prefix>.<effect_model>.prior.rds.

Ultimate Deconvolution (ud)#

The ED update from udr. The code marks it opt-in because of known numerical issues.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb ud \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

Ultimate Deconvolution, unconstrained (ud_unconstrained)#

The TED update from udr, which expects independent, identically distributed data on the z-score scale.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb ud_unconstrained \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

Extreme Deconvolution (ed_bovy)#

The extreme deconvolution exported by mashr.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb ed_bovy \
    --output-prefix protocol_example \
    --data input/mash/protocol_example.mashr_input.rds \
    --cwd output/mixture_prior

Step 4. Plot the prior#

Draw the fitted covariance matrices as heatmaps, ordered by mixture weight.

Plot the prior#

Here --data is the prior produced in step 3, not the summary statistics, and --output-prefix names the plot.

Timing: TBD (on toy dataset)

sos run pipeline/mixture_prior.ipynb plot_U \
    --output-prefix protocol_example_plots \
    --data output/mixture_prior/protocol_example.EE.prior.rds \
    --cwd output/mixture_prior

Command Interface#

sos run pipeline/mixture_prior.ipynb -h
usage: sos run pipeline/mixture_prior.ipynb
               [workflow_name | -t targets] [options] [workflow_options]
  workflow_name:        Single or combined workflows defined in this script
  targets:              One or more targets to generate
  options:              Single-hyphen sos parameters (see "sos run -h" for details)
  workflow_options:     Double-hyphen workflow-specific parameters

Workflows:
  flash
  flash_nonneg
  pca
  canonical
  vhat_identity
  vhat_simple
  vhat_mle
  vhat_corshrink_xcondition
  vhat_simple_specific
  ud
  ud_unconstrained
  ed_bovy
  plot_U

Global Workflow Options:
  --cwd mashr_workflow_output (as path)
  --modular-script-dir code/script (as path)
  --data VAL (as path, required)
                        Input summary statistics data
  --output-prefix ''
                        Prefix of output files. If not specified, it will derive
                        it from data. If it is specified, for example,
                        `--output-prefix AnalysisResults` It will save output
                        files as `{cwd}/AnalysisResults*`.
  --output-suffix all
  --effect-model EE
                        Exchangable effect (EE) or exchangable z-scores (EZ)
  --vhat simple
                        Identifier of $\hat{V}$ estimate file Options are
                        "identity", "simple", "mle",
                        "vhat_corshrink_xcondition", "vhat_simple_specific"
  --mixture-components flash flash_nonneg pca canonical (as list)
  --container ''
  --job-size 1 (as int)
                        For cluster jobs, number commands to run per job
  --walltime 5h
                        Wall clock time expected
  --mem 16G
                        Memory expected
  --numThreads 1 (as int)
                        Number of threads

Sections
  flash:
  flash_nonneg:
  pca:
    Workflow Options:
      --npc 2 (as int)
                        Number of components in PCA analysis for prior set to 3
                        as in mash paper
  canonical:
  vhat_identity:        V estimate: "identity" method
  vhat_simple:          V estimate: "simple" method (null z-scores)
  vhat_mle:             V estimate: "mle" method (mash_estimate_corr_em on a
                        random subset; needs the prior U)
    Workflow Options:
      --n-subset 6000 (as int)
                        number of samples to use
      --max-iter 6 (as int)
                        maximum number of iterations
  vhat_corshrink_xcondition: V estimate: "corshrink" adaptive-shrinkage null
                        correlation (run globally on the null set; the legacy
                        per-gene GTEx SumstatQuery path is not portable)
  vhat_simple_specific: V estimate: "simple_specific" nearPD(cov(nullZ)) (run
                        globally on the null set; legacy per-gene GTEx path not
                        portable)
  ud:                   Prior engine: udr ED update (opt-in; known numerical
                        issues)
  ud_unconstrained:     Prior engine: udr TED update (opt-in; needs
                        i.i.d./z-scale data)
  ed_bovy:              Prior engine: mashr extreme deconvolution (the exported
                        bovy ED)
  plot_U:
    Workflow Options:
      --max-comp -1 (as int)
                        number of components to show
      --[no-]to-cor (default to False)
                        whether or not to convert to correlation
      --tol '1E-6'
      --[no-]remove-label (default to False)
      --name ''

Workflow implementation#

Global parameters#

[global]
parameter: cwd = path('./mashr_workflow_output')
parameter: modular_script_dir = path('code/script')
# Input summary statistics data
parameter: data = path
# Prefix of output files. If not specified, it will derive it from data.
# If it is specified, for example, `--output-prefix AnalysisResults`
# It will save output files as `{cwd}/AnalysisResults*`.
parameter: output_prefix = ''
parameter: output_suffix = 'all'
# Exchangable effect (EE) or exchangable z-scores (EZ)
parameter: effect_model = 'EE'
# Identifier of $\hat{V}$ estimate file
# Options are "identity", "simple", "mle", "vhat_corshrink_xcondition", "vhat_simple_specific"
parameter: vhat = 'simple'
parameter: mixture_components = ['flash', 'flash_nonneg', 'pca',"canonical"]
parameter: container = ""
# For cluster jobs, number commands to run per job
parameter: job_size = 1
# Wall clock time expected
parameter: walltime = "5h"
# Memory expected
parameter: mem = "16G"
# Number of threads
parameter: numThreads = 1
data = data.absolute()
cwd = cwd.absolute()
if len(output_prefix) == 0:
    output_prefix = f"{data:bn}"
prior_data = file_target(f"{cwd:a}/{output_prefix}.{effect_model}.prior.rds")
vhat_data = file_target(f"{cwd:a}/{output_prefix}.{effect_model}.V_{vhat}.rds")

Factor analyses#

[flash]
input: data
output: f"{cwd}/{output_prefix}.flash.rds"
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f'{_output:n}.stderr', stdout = f'{_output:n}.stdout', container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_covariance.R \
        --data ${_input} \
        --component flash \
        --effect-model ${effect_model} \
        --output ${_output}
[flash_nonneg]
input: data
output: f"{cwd}/{output_prefix}.flash_nonneg.rds"
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f'{_output:n}.stderr', stdout = f'{_output:n}.stdout', container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_covariance.R \
        --data ${_input} \
        --component flash_nonneg \
        --effect-model ${effect_model} \
        --output ${_output}
[pca]
# Number of components in PCA analysis for prior
# set to 3 as in mash paper
parameter: npc = 2
input: data
output: f"{cwd}/{output_prefix}.pca.rds"
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f'{_output:n}.stderr', stdout = f'{_output:n}.stdout', container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_covariance.R \
        --data ${_input} \
        --component pca \
        --npc ${npc} \
        --effect-model ${effect_model} \
        --output ${_output}
[canonical]
input: data
output: f"{cwd}/{output_prefix}.canonical.rds"
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f'{_output:n}.stderr', stdout = f'{_output:n}.stdout', container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_covariance.R \
        --data ${_input} \
        --component canonical \
        --effect-model ${effect_model} \
        --output ${_output}

Residual variance estimation#

# V estimate: "identity" method
[vhat_identity]
input: data
output: f'{vhat_data:nn}.V_identity.rds'
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f"{_output:n}.stderr", stdout = f"{_output:n}.stdout", container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_vhat.R \
        --data ${_input} \
        --method identity \
        --effect-model ${effect_model} \
        --output ${_output}
# V estimate: "simple" method (null z-scores)
[vhat_simple]
input: data
output: f'{vhat_data:nn}.V_simple.rds'
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f"{_output:n}.stderr", stdout = f"{_output:n}.stdout", container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_vhat.R \
        --data ${_input} \
        --method simple \
        --effect-model ${effect_model} \
        --output ${_output}
# V estimate: "mle" method (mash_estimate_corr_em on a random subset; needs the prior U)
[vhat_mle]
# number of samples to use
parameter: n_subset = 6000
# maximum number of iterations
parameter: max_iter = 6
input: data, prior_data
output: f'{vhat_data:nn}.V_mle.rds'
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f"{_output:n}.stderr", stdout = f"{_output:n}.stdout", container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_vhat.R \
        --data ${_input[0]} \
        --method mle \
        --prior-data ${_input[1]} \
        --n-subset ${n_subset} \
        --max-iter ${max_iter} \
        --effect-model ${effect_model} \
        --output ${_output}
# V estimate: "corshrink" adaptive-shrinkage null correlation (run globally on the null set; the legacy per-gene GTEx SumstatQuery path is not portable)
[vhat_corshrink_xcondition]
input: data
output: f'{vhat_data:nn}.V_vhat_corshrink_xcondition.rds'
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f"{_output:n}.stderr", stdout = f"{_output:n}.stdout", container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_vhat.R \
        --data ${_input} \
        --method corshrink \
        --effect-model ${effect_model} \
        --output ${_output}
# V estimate: "simple_specific" nearPD(cov(nullZ)) (run globally on the null set; legacy per-gene GTEx path not portable)
[vhat_simple_specific]
input: data
output: f'{vhat_data:nn}.V_vhat_simple_specific.rds'
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f"{_output:n}.stderr", stdout = f"{_output:n}.stdout", container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_vhat.R \
        --data ${_input} \
        --method simple_specific \
        --effect-model ${effect_model} \
        --output ${_output}

Mixture prior engines#

# Prior engine: udr ED update (opt-in; known numerical issues)
[ud]
input: [data, vhat_data if vhat != "mle" else f'{vhat_data:nn}.V_simple.rds'] + [f"{cwd}/{output_prefix}.{m}.rds" for m in mixture_components]
output: prior_data
comp_files = ",".join([f"{cwd}/{output_prefix}.{m}.rds" for m in mixture_components])
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f"{_output:n}.stderr", stdout = f"{_output:n}.stdout", container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_prior.R \
        --data ${_input[0]} \
        --engine ud \
        --vhat-data ${_input[1]} \
        --component-files ${comp_files} \
        --effect-model ${effect_model} \
        --output ${_output}
# Prior engine: udr TED update (opt-in; needs i.i.d./z-scale data)
[ud_unconstrained]
input: [data, vhat_data if vhat != "mle" else f'{vhat_data:nn}.V_simple.rds'] + [f"{cwd}/{output_prefix}.{m}.rds" for m in mixture_components]
output: prior_data
comp_files = ",".join([f"{cwd}/{output_prefix}.{m}.rds" for m in mixture_components])
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f"{_output:n}.stderr", stdout = f"{_output:n}.stdout", container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_prior.R \
        --data ${_input[0]} \
        --engine ud_ted \
        --vhat-data ${_input[1]} \
        --component-files ${comp_files} \
        --effect-model ${effect_model} \
        --output ${_output}
# Prior engine: mashr extreme deconvolution (the exported bovy ED)
[ed_bovy]
input: [data, vhat_data if vhat != "mle" else f'{vhat_data:nn}.V_simple.rds'] + [f"{cwd}/{output_prefix}.{m}.rds" for m in mixture_components]
output: prior_data
comp_files = ",".join([f"{cwd}/{output_prefix}.{m}.rds" for m in mixture_components])
task: trunk_workers = 1, trunk_size = job_size, walltime = walltime,  mem = mem, tags = f'{step_name}_{_output:bn}'
bash: expand = "${ }", stderr = f"{_output:n}.stderr", stdout = f"{_output:n}.stdout", container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_prior.R \
        --data ${_input[0]} \
        --engine cov_ed \
        --vhat-data ${_input[1]} \
        --component-files ${comp_files} \
        --effect-model ${effect_model} \
        --output ${_output}

Plotting#

A utility that reads the prior produced above and draws heatmaps of its covariance matrices, ordered by mixture weight.

[plot_U]
parameter: data = path
# number of components to show
parameter: max_comp = -1
# whether or not to convert to correlation
parameter: to_cor = False
parameter: tol = "1E-6"
parameter: remove_label = False
parameter: name = ""
input: data
output: f'{cwd:a}/{_input:bn}{("_" + name.replace("$", "_")) if name != "" else ""}.pdf'
bash: expand = "${ }", stderr = f'{_output:n}.stderr', stdout = f'{_output:n}.stdout', container = container
    Rscript ${modular_script_dir}/pecotmr_integration/mash_plot_prior.R \
        --data ${_input} \
        --max-comp ${max_comp} \
        --tol ${tol} \
        --name "${name}" \
        ${'--to-cor' if to_cor else ''} \
        ${'--remove-label' if remove_label else ''} \
        --output ${_output}