diff --git a/vaxrank/cli/parser.py b/vaxrank/cli/arg_parser.py similarity index 79% rename from vaxrank/cli/parser.py rename to vaxrank/cli/arg_parser.py index 3d7d3b9..8cf4927 100644 --- a/vaxrank/cli/parser.py +++ b/vaxrank/cli/arg_parser.py @@ -10,37 +10,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys -import logging -import logging.config -import pkg_resources -import msgspec + from argparse import ArgumentParser from isovar.cli import make_isovar_arg_parser from mhctools.cli import add_mhc_args -import pandas as pd -import serializable - -from .version import __version__ -from .core_logic import run_vaxrank -from .epitope_config import DEFAULT_MIN_EPITOPE_SCORE -from .gene_pathway_check import GenePathwayCheck -from .report import ( - make_ascii_report, - make_html_report, - make_pdf_report, - make_csv_report, - make_minimal_neoepitope_report, - TemplateDataCreator, -) -from .patient_info import PatientInfo -from .epitope_config import epitope_config_from_args +from .epitope_config_args import add_epitope_prediction_args +from .vaccine_config_args import add_vaccine_peptide_args +from ..version import __version__ -logger = logging.getLogger(__name__) def make_vaxrank_arg_parser(): @@ -199,39 +180,6 @@ def add_output_args(arg_parser): help="Number of mutations to report") -def add_vaccine_peptide_args(arg_parser): - vaccine_peptide_group = arg_parser.add_argument_group("Vaccine peptide options") - vaccine_peptide_group.add_argument( - "--vaccine-peptide-length", - default=25, - type=int, - help="Number of amino acids in the vaccine peptides. (default: %(default)s)") - - vaccine_peptide_group.add_argument( - "--padding-around-mutation", - default=5, - type=int, - help=( - "Number of off-center windows around the mutation to consider " - "as vaccine peptides. (default: %(default)s)" - )) - - vaccine_peptide_group.add_argument( - "--max-vaccine-peptides-per-mutation", - default=1, - type=int, - help=( - "Number of vaccine peptides to generate for each mutation. " - "(default: %(default)s)" - )) - - vaccine_peptide_group.add_argument( - "--num-epitopes-per-vaccine-peptide", - type=int, - help=( - "Maximum number of mutant epitopes to consider when scoring " - "each vaccine peptide. (default: %(default)s)")) - def add_supplemental_report_args(arg_parser): report_args_group = arg_parser.add_argument_group("Supplemental report options") @@ -262,12 +210,6 @@ def check_args(args): "--output-passing-variants-csv, " "--output-isovar-csv") -def configure_logging(args): - logging.config.fileConfig( - pkg_resources.resource_filename( - __name__, - 'logging.conf'), - defaults={'logfilename': args.log_path}) def choose_arg_parser(args_list): # TODO: replace this with a command sub-parser diff --git a/vaxrank/cli/entry_point.py b/vaxrank/cli/entry_point.py index cb725e1..8363d82 100644 --- a/vaxrank/cli/entry_point.py +++ b/vaxrank/cli/entry_point.py @@ -11,9 +11,18 @@ # limitations under the License. + +import logging +import logging.config + +import sys +import pkg_resources + + import pandas as pd import serializable + from varcode.cli import variant_collection_from_args from isovar import isovar_results_to_dataframe from mhctools.cli import ( @@ -21,8 +30,11 @@ mhc_binding_predictor_from_args, ) -from .parser import parse_vaxrank_args -from ..core_logic import run_vaxrank +from .arg_parser import parse_vaxrank_args +from .epitope_config_args import epitope_config_from_args +from .vaccine_config_args import vaccine_config_from_args + +from ..core_logic import run_vaxrank, run_vaxrank_from_parsed_args from ..gene_pathway_check import GenePathwayCheck from ..report import ( make_ascii_report, @@ -34,6 +46,16 @@ ) from ..patient_info import PatientInfo +logger = logging.getLogger(__name__) + +def configure_logging(args): + logging.config.fileConfig( + pkg_resources.resource_filename( + __name__, + 'logging.conf'), + defaults={'logfilename': args.log_path}) + + def main(args_list=None): """ Script to generate vaccine peptide predictions from somatic cancer variants, diff --git a/vaxrank/cli/epitope_config.py b/vaxrank/cli/epitope_config_args.py similarity index 77% rename from vaxrank/cli/epitope_config.py rename to vaxrank/cli/epitope_config_args.py index 794bf80..22eb4d1 100644 --- a/vaxrank/cli/epitope_config.py +++ b/vaxrank/cli/epitope_config_args.py @@ -10,21 +10,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -import msgspec - import argparse -DEFAULT_MIN_EPITOPE_SCORE = 0.00001 -DEFAULT_BINDING_AFFINITY_CUTOFF = 5000.0 - -class EpitopeConfig(msgspec.Struct): +import msgspec - """Parameters for score, filtering, and ranking both epitopes and vaccine peptides""" - logistic_epitope_score_midpoint : float = 350.0 - logistic_epitope_score_width : float = 150.0 - - min_epitope_score : float = DEFAULT_MIN_EPITOPE_SCORE - binding_affinity_cutoff : float = DEFAULT_BINDING_AFFINITY_CUTOFF +from ..epitope_config import EpitopeConfig def add_epitope_prediction_args(arg_parser : argparse.ArgumentParser): diff --git a/vaxrank/cli/vaccine_config_args.py b/vaxrank/cli/vaccine_config_args.py new file mode 100644 index 0000000..7f2f35d --- /dev/null +++ b/vaxrank/cli/vaccine_config_args.py @@ -0,0 +1,70 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse + +import msgspec + +from ..vaccine_config import VaccineConfig + + + +def add_vaccine_peptide_args(arg_parser : argparse.ArgumentParser) -> None: + vaccine_peptide_group = arg_parser.add_argument_group("Vaccine peptide options") + vaccine_peptide_group.add_argument( + "--vaccine-peptide-length", + default=25, + type=int, + help="Number of amino acids in the vaccine peptides. (default: %(default)s)") + + vaccine_peptide_group.add_argument( + "--padding-around-mutation", + default=5, + type=int, + help=( + "Number of off-center windows around the mutation to consider " + "as vaccine peptides. (default: %(default)s)" + )) + + vaccine_peptide_group.add_argument( + "--max-vaccine-peptides-per-mutation", + default=1, + type=int, + help=( + "Number of vaccine peptides to generate for each mutation. " + "(default: %(default)s)" + )) + + vaccine_peptide_group.add_argument( + "--num-epitopes-per-vaccine-peptide", + type=int, + help=( + "Maximum number of mutant epitopes to consider when scoring " + "each vaccine peptide. (default: %(default)s)")) + + + + + +def vaccine_config_from_args(args : argparse.Namespace) -> VaccineConfig: + """ + Extract config path and overrides from argument namespace + """ + epitope_config_kwargs = {} + if args.epitope_prediction_config: + with open(args.epitope_prediction_config) as f: + epitope_config_kwargs.update(msgspec.yaml.decode(f.read())) + + if args.min_epitope_score is not None: + epitope_config_kwargs["min_epitope_score"] = args.min_epitope_score + epitope_config = msgspec.convert(epitope_config_kwargs, EpitopeConfig) + return epitope_config \ No newline at end of file diff --git a/vaxrank/core_logic.py b/vaxrank/core_logic.py index a261ff8..e6e7d03 100644 --- a/vaxrank/core_logic.py +++ b/vaxrank/core_logic.py @@ -19,6 +19,7 @@ from mhctools.base_predictor import BasePredictor from .epitope_config import EpitopeConfig +from .vaccine_config import VaccineConfig from .epitope_logic import slice_epitope_predictions, predict_epitopes from .mutant_protein_fragment import MutantProteinFragment from .vaccine_peptide import VaccinePeptide @@ -32,7 +33,8 @@ def run_vaxrank( vaccine_peptide_length : int = 25, max_vaccine_peptides_per_variant : int = 1, num_mutant_epitopes_to_keep : int = 10000, - config : EpitopeConfig = None): + epitope_config : EpitopeConfig = None, + vaccine_config : VaccineConfig = None): """ Parameters ---------- @@ -54,8 +56,11 @@ def run_vaxrank( Number of top-ranking epitopes for each vaccine peptide to include in computation. - config + epitope_config Configuration options for epitope scoring, using defaults if not provided + + vaccine_config + Configuration options for vaccine peptide selection, using defaults if not provided """ variant_to_vaccine_peptides_dict = create_vaccine_peptides_dict( isovar_results=isovar_results, @@ -63,7 +68,8 @@ def run_vaxrank( vaccine_peptide_length=vaccine_peptide_length, max_vaccine_peptides_per_variant=max_vaccine_peptides_per_variant, num_mutant_epitopes_to_keep=num_mutant_epitopes_to_keep, - config=config) + epitope_config=epitope_config, + vaccine_config=vaccine_config) ranked_list = ranked_vaccine_peptides(variant_to_vaccine_peptides_dict) return VaxrankResults( @@ -78,7 +84,8 @@ def create_vaccine_peptides_dict( vaccine_peptide_length : int = 25, max_vaccine_peptides_per_variant : int = 1, num_mutant_epitopes_to_keep : int = 10 ** 5, - config : EpitopeConfig = None): + epitope_config : EpitopeConfig = None, + vaccine_config : VaccineConfig = None): """ Parameters ---------- @@ -99,9 +106,11 @@ def create_vaccine_peptides_dict( Number of top-ranking epitopes for each vaccine peptide to include in computation. - config + epitope_config Configuration options for epitope scoring, using defaults if not provided - + + vaccine_config + Configuration options for vaccine peptide selection, using defaults if not provided Returns ------- @@ -117,7 +126,8 @@ def create_vaccine_peptides_dict( vaccine_peptide_length=vaccine_peptide_length, max_vaccine_peptides_per_variant=max_vaccine_peptides_per_variant, num_mutant_epitopes_to_keep=num_mutant_epitopes_to_keep, - config=config) + epitope_config=epitope_config, + vaccine_config=vaccine_config) if any(x.contains_mutant_epitopes() for x in vaccine_peptides): vaccine_peptides_dict[variant] = vaccine_peptides @@ -130,7 +140,8 @@ def vaccine_peptides_for_variant( vaccine_peptide_length : int, max_vaccine_peptides_per_variant : int, num_mutant_epitopes_to_keep : int = None, - config : EpitopeConfig = None): + epitope_config : EpitopeConfig = None, + vaccine_config : VaccineConfig = None): """ Parameters ---------- @@ -146,14 +157,10 @@ def vaccine_peptides_for_variant( max_vaccine_peptides_per_variant Number of vaccine peptides to generate for each mutation. - num_mutant_epitopes_to_keep : int, optional + num_mutant_epitopes_to_keep Number of top-ranking epitopes for each vaccine peptide to include in computation. - - min_epitope_score : float, optional - Ignore peptides with binding predictions whose normalized score is less - than this. - + Returns ------- Sorted list of VaccinePeptide objects. If there are no suitable vaccine @@ -175,9 +182,11 @@ def vaccine_peptides_for_variant( epitope_predictions = predict_epitopes( mhc_predictor=mhc_predictor, protein_fragment=long_protein_fragment, - config=config, + epitope_config=epitope_config, genome=variant.ensembl).values() - + + # TODO: make a function called vaccine_peptides_from_epitopes that + # takes vaccine_config as an option candidate_vaccine_peptides = [] for offset, candidate_fragment in long_protein_fragment.sorted_subsequences( diff --git a/vaxrank/epitope_config.py b/vaxrank/epitope_config.py new file mode 100644 index 0000000..cd34119 --- /dev/null +++ b/vaxrank/epitope_config.py @@ -0,0 +1,25 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import msgspec + +DEFAULT_MIN_EPITOPE_SCORE = 0.00001 +DEFAULT_BINDING_AFFINITY_CUTOFF = 5000.0 + +class EpitopeConfig(msgspec.Struct): + + """Parameters for score, filtering, and ranking both epitopes and vaccine peptides""" + logistic_epitope_score_midpoint : float = 350.0 + logistic_epitope_score_width : float = 150.0 + + min_epitope_score : float = DEFAULT_MIN_EPITOPE_SCORE + binding_affinity_cutoff : float = DEFAULT_BINDING_AFFINITY_CUTOFF diff --git a/vaxrank/cli/vaccine_config.py b/vaxrank/vaccine_config.py similarity index 80% rename from vaxrank/cli/vaccine_config.py rename to vaxrank/vaccine_config.py index ad12ee9..7801da8 100644 --- a/vaxrank/cli/vaccine_config.py +++ b/vaxrank/vaccine_config.py @@ -10,15 +10,14 @@ # See the License for the specific language governing permissions and # limitations under the License. - import msgspec -VACCINE_PEPTIDE_LENGTH_DEFAULT = 25 - class VaccineConfig(msgspec.Struct): """Parameters for assembling epitope predictions into vaccine peptides""" - vaccine_peptide_length : int = VACCINE_PEPTIDE_LENGTH_DEFAULT + vaccine_peptide_length : int = 25 + padding_around_mutation : int = 5 + + max_vaccine_peptides_per_variant : int = 1 -def vaccine_config_from_args(args): - pass \ No newline at end of file + num_mutant_epitopes_to_keep : int = 1000 \ No newline at end of file