diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 0000000..06c4361 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 7ad60764a6863f28086f2b317bb1d13a +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_images/analysis-rel.png b/_images/analysis-rel.png new file mode 100644 index 0000000..b64e33f Binary files /dev/null and b/_images/analysis-rel.png differ diff --git a/_images/analysis-seq.png b/_images/analysis-seq.png new file mode 100644 index 0000000..cf09d24 Binary files /dev/null and b/_images/analysis-seq.png differ diff --git a/_images/gui_clustering.png b/_images/gui_clustering.png new file mode 100644 index 0000000..5caa3f6 Binary files /dev/null and b/_images/gui_clustering.png differ diff --git a/_images/gui_input.png b/_images/gui_input.png new file mode 100644 index 0000000..b55d783 Binary files /dev/null and b/_images/gui_input.png differ diff --git a/_images/gui_results.png b/_images/gui_results.png new file mode 100644 index 0000000..d8a7be7 Binary files /dev/null and b/_images/gui_results.png differ diff --git a/_images/line-desc.png b/_images/line-desc.png new file mode 100644 index 0000000..3527263 Binary files /dev/null and b/_images/line-desc.png differ diff --git a/_images/treesearch-rel.png b/_images/treesearch-rel.png new file mode 100644 index 0000000..07ccaa4 Binary files /dev/null and b/_images/treesearch-rel.png differ diff --git a/_images/treesearch-seq.png b/_images/treesearch-seq.png new file mode 100644 index 0000000..09da2b6 Binary files /dev/null and b/_images/treesearch-seq.png differ diff --git a/_sources/aizynthfinder.analysis.rst.txt b/_sources/aizynthfinder.analysis.rst.txt new file mode 100644 index 0000000..fc33748 --- /dev/null +++ b/_sources/aizynthfinder.analysis.rst.txt @@ -0,0 +1,37 @@ +aizynthfinder.analysis package +============================== + +Submodules +---------- + +aizynthfinder.analysis.routes module +------------------------------------ + +.. automodule:: aizynthfinder.analysis.routes + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.analysis.tree\_analysis module +-------------------------------------------- + +.. automodule:: aizynthfinder.analysis.tree_analysis + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.analysis.utils module +----------------------------------- + +.. automodule:: aizynthfinder.analysis.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.analysis + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.chem.rst.txt b/_sources/aizynthfinder.chem.rst.txt new file mode 100644 index 0000000..3277741 --- /dev/null +++ b/_sources/aizynthfinder.chem.rst.txt @@ -0,0 +1,37 @@ +aizynthfinder.chem package +========================== + +Submodules +---------- + +aizynthfinder.chem.mol module +----------------------------- + +.. automodule:: aizynthfinder.chem.mol + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.chem.reaction module +---------------------------------- + +.. automodule:: aizynthfinder.chem.reaction + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.chem.serialization module +--------------------------------------- + +.. automodule:: aizynthfinder.chem.serialization + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.chem + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.context.policy.rst.txt b/_sources/aizynthfinder.context.policy.rst.txt new file mode 100644 index 0000000..e23dcf4 --- /dev/null +++ b/_sources/aizynthfinder.context.policy.rst.txt @@ -0,0 +1,45 @@ +aizynthfinder.context.policy package +==================================== + +Submodules +---------- + +aizynthfinder.context.policy.expansion\_strategies module +--------------------------------------------------------- + +.. automodule:: aizynthfinder.context.policy.expansion_strategies + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.context.policy.filter\_strategies module +------------------------------------------------------ + +.. automodule:: aizynthfinder.context.policy.filter_strategies + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.context.policy.policies module +-------------------------------------------- + +.. automodule:: aizynthfinder.context.policy.policies + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.context.policy.utils module +----------------------------------------- + +.. automodule:: aizynthfinder.context.policy.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.context.policy + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.context.rst.txt b/_sources/aizynthfinder.context.rst.txt new file mode 100644 index 0000000..4f630ec --- /dev/null +++ b/_sources/aizynthfinder.context.rst.txt @@ -0,0 +1,39 @@ +aizynthfinder.context package +============================= + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aizynthfinder.context.policy + aizynthfinder.context.scoring + aizynthfinder.context.stock + +Submodules +---------- + +aizynthfinder.context.collection module +--------------------------------------- + +.. automodule:: aizynthfinder.context.collection + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.context.config module +----------------------------------- + +.. automodule:: aizynthfinder.context.config + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.context + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.context.scoring.rst.txt b/_sources/aizynthfinder.context.scoring.rst.txt new file mode 100644 index 0000000..d00112e --- /dev/null +++ b/_sources/aizynthfinder.context.scoring.rst.txt @@ -0,0 +1,29 @@ +aizynthfinder.context.scoring package +===================================== + +Submodules +---------- + +aizynthfinder.context.scoring.collection module +----------------------------------------------- + +.. automodule:: aizynthfinder.context.scoring.collection + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.context.scoring.scorers module +-------------------------------------------- + +.. automodule:: aizynthfinder.context.scoring.scorers + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.context.scoring + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.context.stock.rst.txt b/_sources/aizynthfinder.context.stock.rst.txt new file mode 100644 index 0000000..e8c6b5b --- /dev/null +++ b/_sources/aizynthfinder.context.stock.rst.txt @@ -0,0 +1,29 @@ +aizynthfinder.context.stock package +=================================== + +Submodules +---------- + +aizynthfinder.context.stock.queries module +------------------------------------------ + +.. automodule:: aizynthfinder.context.stock.queries + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.context.stock.stock module +---------------------------------------- + +.. automodule:: aizynthfinder.context.stock.stock + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.context.stock + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.interfaces.gui.rst.txt b/_sources/aizynthfinder.interfaces.gui.rst.txt new file mode 100644 index 0000000..31315da --- /dev/null +++ b/_sources/aizynthfinder.interfaces.gui.rst.txt @@ -0,0 +1,21 @@ +aizynthfinder.interfaces.gui package +==================================== + +Submodules +---------- + +aizynthfinder.interfaces.gui.clustering module +---------------------------------------------- + +.. automodule:: aizynthfinder.interfaces.gui.clustering + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.interfaces.gui + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.interfaces.rst.txt b/_sources/aizynthfinder.interfaces.rst.txt new file mode 100644 index 0000000..6740cc4 --- /dev/null +++ b/_sources/aizynthfinder.interfaces.rst.txt @@ -0,0 +1,37 @@ +aizynthfinder.interfaces package +================================ + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aizynthfinder.interfaces.gui + +Submodules +---------- + +aizynthfinder.interfaces.aizynthapp module +------------------------------------------ + +.. automodule:: aizynthfinder.interfaces.aizynthapp + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.interfaces.aizynthcli module +------------------------------------------ + +.. automodule:: aizynthfinder.interfaces.aizynthcli + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.interfaces + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.rst.txt b/_sources/aizynthfinder.rst.txt new file mode 100644 index 0000000..65a285b --- /dev/null +++ b/_sources/aizynthfinder.rst.txt @@ -0,0 +1,43 @@ +aizynthfinder package +===================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aizynthfinder.analysis + aizynthfinder.chem + aizynthfinder.context + aizynthfinder.interfaces + aizynthfinder.search + aizynthfinder.tools + aizynthfinder.utils + +Submodules +---------- + +aizynthfinder.aizynthfinder module +---------------------------------- + +.. automodule:: aizynthfinder.aizynthfinder + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.reactiontree module +--------------------------------- + +.. automodule:: aizynthfinder.reactiontree + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.search.breadth_first.rst.txt b/_sources/aizynthfinder.search.breadth_first.rst.txt new file mode 100644 index 0000000..d52a937 --- /dev/null +++ b/_sources/aizynthfinder.search.breadth_first.rst.txt @@ -0,0 +1,29 @@ +aizynthfinder.search.breadth\_first package +=========================================== + +Submodules +---------- + +aizynthfinder.search.breadth\_first.nodes module +------------------------------------------------ + +.. automodule:: aizynthfinder.search.breadth_first.nodes + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.search.breadth\_first.search\_tree module +------------------------------------------------------- + +.. automodule:: aizynthfinder.search.breadth_first.search_tree + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.search.breadth_first + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.search.dfpn.rst.txt b/_sources/aizynthfinder.search.dfpn.rst.txt new file mode 100644 index 0000000..cbc32db --- /dev/null +++ b/_sources/aizynthfinder.search.dfpn.rst.txt @@ -0,0 +1,29 @@ +aizynthfinder.search.dfpn package +================================= + +Submodules +---------- + +aizynthfinder.search.dfpn.nodes module +-------------------------------------- + +.. automodule:: aizynthfinder.search.dfpn.nodes + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.search.dfpn.search\_tree module +--------------------------------------------- + +.. automodule:: aizynthfinder.search.dfpn.search_tree + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.search.dfpn + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.search.mcts.rst.txt b/_sources/aizynthfinder.search.mcts.rst.txt new file mode 100644 index 0000000..7a72eef --- /dev/null +++ b/_sources/aizynthfinder.search.mcts.rst.txt @@ -0,0 +1,45 @@ +aizynthfinder.search.mcts package +================================= + +Submodules +---------- + +aizynthfinder.search.mcts.node module +------------------------------------- + +.. automodule:: aizynthfinder.search.mcts.node + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.search.mcts.search module +--------------------------------------- + +.. automodule:: aizynthfinder.search.mcts.search + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.search.mcts.state module +-------------------------------------- + +.. automodule:: aizynthfinder.search.mcts.state + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.search.mcts.utils module +-------------------------------------- + +.. automodule:: aizynthfinder.search.mcts.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.search.mcts + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.search.retrostar.rst.txt b/_sources/aizynthfinder.search.retrostar.rst.txt new file mode 100644 index 0000000..03af986 --- /dev/null +++ b/_sources/aizynthfinder.search.retrostar.rst.txt @@ -0,0 +1,37 @@ +aizynthfinder.search.retrostar package +====================================== + +Submodules +---------- + +aizynthfinder.search.retrostar.cost module +------------------------------------------ + +.. automodule:: aizynthfinder.search.retrostar.cost + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.search.retrostar.nodes module +------------------------------------------- + +.. automodule:: aizynthfinder.search.retrostar.nodes + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.search.retrostar.search\_tree module +-------------------------------------------------- + +.. automodule:: aizynthfinder.search.retrostar.search_tree + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.search.retrostar + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.search.rst.txt b/_sources/aizynthfinder.search.rst.txt new file mode 100644 index 0000000..ee2af14 --- /dev/null +++ b/_sources/aizynthfinder.search.rst.txt @@ -0,0 +1,32 @@ +aizynthfinder.search package +============================ + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aizynthfinder.search.breadth_first + aizynthfinder.search.dfpn + aizynthfinder.search.mcts + aizynthfinder.search.retrostar + +Submodules +---------- + +aizynthfinder.search.andor\_trees module +---------------------------------------- + +.. automodule:: aizynthfinder.search.andor_trees + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.search + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.tools.rst.txt b/_sources/aizynthfinder.tools.rst.txt new file mode 100644 index 0000000..a756a92 --- /dev/null +++ b/_sources/aizynthfinder.tools.rst.txt @@ -0,0 +1,37 @@ +aizynthfinder.tools package +=========================== + +Submodules +---------- + +aizynthfinder.tools.cat\_output module +-------------------------------------- + +.. automodule:: aizynthfinder.tools.cat_output + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.tools.download\_public\_data module +------------------------------------------------- + +.. automodule:: aizynthfinder.tools.download_public_data + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.tools.make\_stock module +-------------------------------------- + +.. automodule:: aizynthfinder.tools.make_stock + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.tools + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/aizynthfinder.utils.rst.txt b/_sources/aizynthfinder.utils.rst.txt new file mode 100644 index 0000000..9303de7 --- /dev/null +++ b/_sources/aizynthfinder.utils.rst.txt @@ -0,0 +1,93 @@ +aizynthfinder.utils package +=========================== + +Submodules +---------- + +aizynthfinder.utils.exceptions module +------------------------------------- + +.. automodule:: aizynthfinder.utils.exceptions + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.utils.files module +-------------------------------- + +.. automodule:: aizynthfinder.utils.files + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.utils.image module +-------------------------------- + +.. automodule:: aizynthfinder.utils.image + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.utils.loading module +---------------------------------- + +.. automodule:: aizynthfinder.utils.loading + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.utils.logging module +---------------------------------- + +.. automodule:: aizynthfinder.utils.logging + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.utils.math module +------------------------------- + +.. automodule:: aizynthfinder.utils.math + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.utils.models module +--------------------------------- + +.. automodule:: aizynthfinder.utils.models + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.utils.mongo module +-------------------------------- + +.. automodule:: aizynthfinder.utils.mongo + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.utils.paths module +-------------------------------- + +.. automodule:: aizynthfinder.utils.paths + :members: + :undoc-members: + :show-inheritance: + +aizynthfinder.utils.type\_utils module +-------------------------------------- + +.. automodule:: aizynthfinder.utils.type_utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aizynthfinder.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/_sources/cli.rst.txt b/_sources/cli.rst.txt new file mode 100644 index 0000000..bf3f896 --- /dev/null +++ b/_sources/cli.rst.txt @@ -0,0 +1,106 @@ +Command-line interface +====================== + +This tools provide the possibility to perform tree search on a batch of molecules. + +In its simplest form, you type + +.. code-block:: bash + + aizynthcli --config config_local.yml --smiles smiles.txt + +where `config_local.yml` contains configurations such as paths to policy models and stocks (see :doc:`here `) +and `smiles.txt` is a simple text file with SMILES (one on each row). + + +To find out what other arguments are available use the ``-h`` flag. + +.. code-block:: bash + + aizynthcli -h + +That gives something like this: + +.. include:: cli_help.txt + + +By default: + + * `All` stocks are selected if no stock is specified + * `First` expansion policy is selected if not expansion policy is specified + * `All` filter policies are selected if it is not specified on the command-line + +Analysing output +---------------- + + +The results from the ``aizynthcli`` tool when supplying multiple SMILES is an JSON or HDF5 file that can be read as a pandas dataframe. +It will be called `output.json.gz` by default. + +A `checkpoint.json.gz` will also be generated if a checkpoint file path is provided as input when calling the ``aizynthcli`` tool. The +checkpoint data will contain the processed smiles with their corresponding results in each line of the file. + +.. code-block:: + + import pandas as pd + data = pd.read_json("output.json.gz", orient="table") + +it will contain statistics about the tree search and the top-ranked routes (as JSONs) for each target compound, see below. + +When a single SMILES is provided to the tool, the statistics will be written to the terminal, and the top-ranked routes to +a JSON file (`trees.json` by default). + + +This is an example of how to create images of the top-ranked routes for the first target compound + + +.. code-block:: + + import pandas as pd + from aizynthfinder.reactiontree import ReactionTree + + data = pd.read_json("output.json.gz", orient="table") + all_trees = data.trees.values # This contains a list of all the trees for all the compounds + trees_for_first_target = all_trees[0] + + for itree, tree in enumerate(trees_for_first_target): + imagefile = f"route{itree:03d}.png" + ReactionTree.from_dict(tree).to_image().save(imagefile) + +The images will be called `route000.png`, `route001.png` etc. + + +Specification of output +----------------------- + +The JSON or HDF5 file created when running the tool with a list of SMILES will have the following columns + +============================= =========== +Column Description +============================= =========== +target The target SMILES +search_time The total search time in seconds +first_solution_time The time elapsed until the first solution was found +first_solution_iteration The number of iterations completed until the first solution was found +number_of_nodes The number of nodes in the search tree +max_transforms The maximum number of transformations for all routes in the search tree +max_children The maximum number of children for a search node +number_of_routes The number of routes in the search tree +number_of_solved_routes The number of solved routes in search tree +top_score The score of the top-scored route (default to MCTS reward) +is_solved If the top-scored route is solved +number_of_steps The number of reactions in the top-scored route +number_of_precursors The number of starting materials +number_of_precursors_in_stock The number of starting materials in stock +precursors_in_stock Comma-separated list of SMILES of starting material in stock +precursors_not_in_stock Comma-separated list of SMILES of starting material not in stock +precursors_availability Semi-colon separated list of stock availability of the staring material +policy_used_counts Dictionary of the total number of times an expansion policy have been used +profiling Profiling information from the search tree, including expansion models call and reactant generation +stock_info Dictionary of the stock availability for each of the starting material in all extracted routes +top_scores Comma-separated list of the score of the extracted routes (default to MCTS reward) +trees A list of the extracted routes as dictionaries +============================= =========== + +If you running the tool with a single SMILES, all of this data will be printed to the screen, except +the ``stock_info`` and ``trees``. diff --git a/_sources/configuration.rst.txt b/_sources/configuration.rst.txt new file mode 100644 index 0000000..3c506f6 --- /dev/null +++ b/_sources/configuration.rst.txt @@ -0,0 +1,178 @@ + +Configuration file +=================== + +The configuration file that is to be provided to the interfaces +specify file paths to policy models and stocks, in addition to +detailed parameters of the tree search. + +Simple usage +------------ + +Let's say you have a + + * A trained Keras expansion model that is called `uspto_expansion.onnx` + * A library of unique templates called `uspto_templates.csv.gz` + * A stock file in HDF5 format, called `zinc_stock.hdf5` + +(this could have been created by the ``smiles2stock`` tool, see :doc:`here `) + +then to use them in the tree search, create a file called `config.yml` that has the following content + +.. code-block:: yaml + + expansion: + full: + - uspto_expansion.onnx + - uspto_templates.csv.gz + stock: + zinc: zinc_stock.hdf5 + + +Advanced usage +-------------- + +A more detailed configuration file is shown below + +.. code-block:: yaml + + search: + algorithm: mcts + algorithm_config: + C: 1.4 + default_prior: 0.5 + use_prior: True + prune_cycles_in_search: True + search_reward: state score + max_transforms: 6 + iteration_limit: 100 + return_first: false + time_limit: 120 + exclude_target_from_stock: True + expansion: + my_policy: + type: template-based + model: /path/to/keras/model/weights.hdf5 + template: /path/to/hdf5/templates.hdf5 + template_column: retro_template + cutoff_cumulative: 0.995 + cutoff_number: 50 + use_rdchiral: True + use_remote_models: False + my_full_policy: + - /path/to/keras/model/weights.hdf5 + - /path/to/hdf5/templates.hdf5 + filter: + uspto: + type: quick-filter + model: /path/to/keras/model/weights.hdf5 + exclude_from_policy: rc + filter_cutoff: 0.05 + use_remote_models: False + uspto_full: /path/to/keras/model/weights.hdf5 + stock: + buyables: + type: inchiset + path: /path/to/stock1.hdf5 + emolecules: /path/to/stock1.hdf5 + +The (expansion) policy models are specified using two files + * a checkpoint files from Keras in ONNX or hdf5 format, + * a HDF5 or a CSV file containing templates. + +A key like ``my_policy`` should be set and the configuration contains ``type``, ``model`` and ``template`` that must be provided. +If the other settings are not assigned, their default values are taken. +The policy ``my_full_policy`` exemplifies a short-cut to the template-based expansion model when no other settings need to be +provided, only the ``model`` and ``templates`` can be provided. The default settings will be taken in this case. + +The template file should be readable by ``pandas`` using the ``table`` key and the ``retro_template`` column. +A policy can then be selected using the provided key, like ``my_policy`` in the above example. + +The filter policy model is specified using a single checkpoint file. +Any key like ``uspto`` can be set. The settings contains ``type`` and ``model`` that must be provided. If the other +settings are not assigned, their default values are taken. +The filter ``uspto_full`` exemplifies a short-cut to the quick-filter model when no other settings need to be +provided, only the ``model`` can be provided. The default settings will be taken in this case. + + +The stock files can be + * HDF5 files with the ``table`` key an the ``inchi_key`` column. + * A CSV file with a ``inchi_key`` column + * A text file a single column + +In all cases, the column should contain pre-computed inchi keys of the molecules. +The stocks can be set using any key, like ``buyables`` or ``emolecules`` in the above example. +The ``type`` and ``path`` parameters can also be set along with other parameters. +If no other settings need to be provided, only the ``path`` can be provided, whereby it will be +treated as a +short-cut to the inchiset class.. + +The values in the ``search`` sections are optional, and if missing, default values are considered. These +values can also be taken from environment variables. An example of this can be seen as below: + +.. code-block:: yaml + + search: + iteration_limit: ${ITERATION_LIMIT} + algorithm_config: + C: ${C} + time_limit: ${TIME_LIMIT} + max_transforms: ${MAX_TRANSFORMS} + +These are the available ``search`` settings. The ``algorithm_config`` refers to MCTS settings: + +============================================ ============== =========== +Setting Default value Description +============================================ ============== =========== +algorithm mcts The search algorithm. Can be set to `package.module.ClassName` to use a custom search method. +algorithm_config: C 1.4 The C value used to balance exploitation and exploration in the upper confidence bound score of the nodes. +algorithm_config: default_prior 0.5 The prior that is used if policy-provided priors are not used. +algorithm_config: use_prior True If True, priors from the policy is used instead of the `default_prior`. +algorithm_config: prune_cycles_in_search True If True, prevents the MCTS from creating cycles by recreating previously seen molecules when it is expanded. +algorithm_config: search_reward state score The scoring used for the MCTS search algorithm. +algorithm_config: immediate_instantiation [] list of expansion policies for which the MCTS algorithm immediately instantiate the children node upon expansion +algorithm_config: mcts_grouping - if is partial or full the MCTS algorithm will group expansions that produce the same state. If ``partial`` is used the equality will only be determined based on the expandable molecules, whereas ``full`` will check all molecules. +max_transforms 6 The maximum depth of the search tree. +iteration_limit 100 The maximum number of iterations for the tree search. +time_limit 120 The maximum number of seconds to complete the tree search. +return_first False If True, the tree search will be terminated as soon as one solution is found. +exclude_target_from_stock True If True, the target is in stock will be broken down. +============================================ ============== =========== + + +The ``post_processing`` settings are: + +============================================ ============== =========== +Setting Default value Description +============================================ ============== =========== +min_routes 5 The minumum number of routes to extract if ``all_routes`` is not set. +max_routes 25 The maximum number of routes to extract if ``all_routes`` is not set. +all_routes False If True, will extract all solved routes. +route_distance_model N/A If set, will load the quick route distance model from this checkpoint file. +route_scorer state score The scoring for routes when extracting them in the post processing step. +============================================ ============== =========== + + +The ``expansion`` settings are for template-based models: + +============================================ ============== =========== +Setting Default value Description +============================================ ============== =========== +template_column retro_template The column in the template file that contains the templates. +cutoff_cumulative 0.995 The accumulative probability of the suggested templates is capped at this value. All other templates above this threshold are discarded. +cutoff_number 50 The maximum number of templates that will be returned from the expansion policy. +use_rdchiral True If True, will apply templates with RDChiral, otherwise RDKit will be used. +use_remote_models False If True, will try to connect to remote Tensorflow servers. +rescale_prior False If True, will apply a softmax function to the priors. +============================================ ============== =========== + + +The ``filter`` settings are for quick-filter models: + +============================================ ============== =========== +Setting Default value Description +============================================ ============== =========== +exclude_from_policy [] The list of names of the filter policies to exclude. +filter_cutoff 0.05 The cut-off for the quick-filter policy. +use_remote_models False If True, will try to connect to remote Tensorflow servers. +============================================ ============== =========== diff --git a/_sources/gui.rst.txt b/_sources/gui.rst.txt new file mode 100644 index 0000000..ddabec5 --- /dev/null +++ b/_sources/gui.rst.txt @@ -0,0 +1,79 @@ +Graphical user interface +======================== + +This tool to provide the possibility to perform the tree search on a single compound using a GUI +through a Jupyter notebook. If you are unfamiliar with notebooks, you find some introduction `here `_. + +To bring up the notebook, use + +.. code-block:: bash + + jupyter notebook + +and browse to an existing notebook or create a new one. + +Add these lines to the first cell in the notebook. + +.. code-block:: + + from aizynthfinder.interfaces import AiZynthApp + app = AiZynthApp("/path/to/configfile.yaml") + +where the ``AiZynthApp`` class needs to be instantiated with the path to a configuration file (see :doc:`here `). + +To use the interface, follow these steps: + +1. Executed the code in the cell (press ``Ctrl+Enter``) and a simple GUI will appear +2. Enter the target SMILES and select stocks and policy model. +3. Press the ``Run Search`` button to perform the tree search. + +.. image:: gui_input.png + + +4. Press the ``Show Reactions`` to see the top-ranked routes + + +.. image:: gui_results.png + +You can also choose to select and sort the top-ranked routes based on another scoring function. + + +Creating the notebook +--------------------- + +It is possible to create a notebook automatically with the ``aizynthapp`` tool + +.. code-block:: bash + + aizynthapp --config config_local.yml + +which will also automatically opens up the created notebook. + +Analysing the results +--------------------- + +When the tree search has been finished. One can continue exploring the tree and extract output. +This is done by using the ``finder`` property of the app object. The finder holds a reference to an ``AiZynthFinder`` object. + +.. code-block:: + + finder = app.finder + stats = finder.extract_statistics() + + +Clustering +----------- + +There is a GUI extension to perform clustering of the routes. Enter the following a new cell + +.. code-block:: + + %matplotlib inline + from aizynthfinder.interfaces.gui import ClusteringGui + ClusteringGui.from_app(app) + + +A GUI like this will be shown, where you see the hierarchy of the routes and then can select how many +clusters you want to create. + +.. image:: gui_clustering.png \ No newline at end of file diff --git a/_sources/howto.rst.txt b/_sources/howto.rst.txt new file mode 100644 index 0000000..d13d068 --- /dev/null +++ b/_sources/howto.rst.txt @@ -0,0 +1,88 @@ +How-to +======= + +This page outlines a few guidelines on some more advanced use-cases of AiZynthFinder or +frequently raised issues. + + +Using Retro* +------------ + +AiZynthFinder implements other search algorithms than MCTS. This is an example of how Retro* can be used. + +The search algorithm is specified in the configuration file. + +.. code-block:: yaml + + search: + algorithm: aizynthfinder.search.retrostar.search_tree.SearchTree + + +This will use Retro* without a constant-valued oracle function. To specify the oracle function, you can +do + +.. code-block:: yaml + + search: + algorithm: aizynthfinder.search.retrostar.search_tree.SearchTree + algorithm_config: + molecule_cost: + cost: aizynthfinder.search.retrostar.cost.RetroStarCost + model_path: retrostar_value_model.pickle + fingerprint_length: 2048 + fingerprint_radius: 2 + dropout_rate: 0.1 + +The pickle file can be downloaded from `here `_ + + +Using multiple expansion policies +--------------------------------- + +AiZynthFinder can use multiple expansion policies. This gives an example how a general USPTO and a RingBreaker model +can be used together + +.. code-block:: yaml + + expansion: + uspto: + - uspto_keras_model.hdf5 + - uspto_unique_templates.csv.gz + ringbreaker: + - uspto_ringbreaker_keras_model.hdf5 + - uspto_ringbreaker_unique_templates.csv.gz + multi_expansion_strategy: + type: aizynthfinder.context.policy.MultiExpansionStrategy + expansion_strategies: [uspto, ringbreaker] + additive_expansion: True + +and then to use this with ``aizynthcli`` do something like this + +.. code-block:: + + aizynthcli --smiles smiles.txt --config config.yml --policy multi_expansion_strategy + + +Output more routes +------------------ + +The number of routes in the output of ``aizynthcli`` can be controlled from the configuration file. + +This is how you can extract at least 25 routes but not more than 50 per target + +.. code-block:: yaml + + post_processing: + min_routes: 25 + max_routes: 50 + +Alternatively, you can extract all solved routes. If a target is unsolved, it will return the number +of routes specified by ``min_routes`` and ``max_routes``. + +.. code-block:: yaml + + post_processing: + min_routes: 5 + max_routes: 10 + all_routes: True + diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000..1ea8fea --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,78 @@ +aizynthfinder documentation +=========================== + +aizynthfinder is a tool for retrosynthetic planning. The default algorithm is based on a Monte Carlo tree search that recursively breaks down a molecule to purchasable precursors. The tree search is guided by a policy that suggests possible precursors by utilizing a neural network trained on a library of known reaction templates. + +Introduction +------------ + +You run retrosynthesis experiments you need a trained model and a stock collection. You can download a public available model based on USPTO and a stock collection from ZINC database. + +.. code-block:: + + download_public_data . + +This will download the data to your current directory. The ``config.yml`` file can be used directly with the interfaces. + + +There are two main interfaces provided by the package: + + * a script that performs tree search in batch mode and + * an interface that is providing a GUI within a Jupyter notebook. + + +The GUI interface should be run in a Jupyter notebook. This is a simple example of the code in a Jupyter notebook cell. + +.. code-block:: + + from aizynthfinder.interfaces import AiZynthApp + app = AiZynthApp("/path/to/configfile.yaml") + +where the ``AiZynthApp`` class needs to be instantiated with the path to a configuration file (see :doc:`here `). + +To use the interface, follow these steps: + + 1. Executed the code in the cell (press ``Ctrl+Enter``) and a simple GUI will appear + 2. Enter the target SMILES and select stocks and policy model. + 3. Press the ``Run Search`` button to perform the tree search. + 4. Press the ``Show Reactions`` to see the top-ranked routes + + + +The batch-mode script is called ``aizynthcli`` and can be executed like: + +.. code-block:: bash + + aizynthcli --config config.yml --smiles smiles.txt + + +where `config.yml` contains configurations such as paths to policy models and stocks (see :doc:`here `), and `smiles.txt` is a simple text +file with SMILES (one on each row). + +If you just want to perform the tree search on a single molecule. You can directly specify it on the command-line +within quotes: + +.. code-block:: bash + + aizynthcli --config config.yml --smiles "COc1cccc(OC(=O)/C=C/c2cc(OC)c(OC)c(OC)c2)c1" + + +The output is some statistics about the tree search, the scores of the top-ranked routes, and the reaction tree +of the top-ranked routes. When smiles are provided in a text file the results are stored in a JSON file, +whereas if the SMILEs is provided on the command-line it is printed directly to the prompt +(except the reaction trees, which are written to a JSON file). + + +.. toctree:: + :hidden: + + gui + cli + python_interface + configuration + stocks + scoring + howto + aizynthfinder + sequences + relationships \ No newline at end of file diff --git a/_sources/modules.rst.txt b/_sources/modules.rst.txt new file mode 100644 index 0000000..bad1194 --- /dev/null +++ b/_sources/modules.rst.txt @@ -0,0 +1,7 @@ +aizynthfinder +============= + +.. toctree:: + :maxdepth: 4 + + aizynthfinder diff --git a/_sources/python_interface.rst.txt b/_sources/python_interface.rst.txt new file mode 100644 index 0000000..df283b9 --- /dev/null +++ b/_sources/python_interface.rst.txt @@ -0,0 +1,109 @@ +Python interface +================ + +This page gives a quick example of how the tree search can be completed +by writing your own python interface. This is not recommended for most users. + + +1. Import the necessary class + +.. code-block:: python + + from aizynthfinder.aizynthfinder import AiZynthFinder + + +2. Instantiate that class by providing a configuration file. + + +.. code-block:: python + + filename = "config.yml" + finder = AiZynthFinder(configfile=filename) + + +3. Select stock and policy + + +.. code-block:: python + + finder.stock.select("zinc") + finder.expansion_policy.select("uspto") + finder.filter_policy.select("uspto") + +`zinc` and `uspto` where the keys given to the stock and the policy in the configuration file. +The first policy set is the expansion policy and the second is the filter policy. The filter policy is optional. + +4. Set the target SMILES and perform the tree search + + +.. code-block:: python + + finder.target_smiles = "Cc1cccc(c1N(CC(=O)Nc2ccc(cc2)c3ncon3)C(=O)C4CCS(=O)(=O)CC4)C" + finder.tree_search() + + +5. Analyse the search tree and build routes + + +.. code-block:: python + + finder.build_routes() + stats = finder.extract_statistics() + + +The ``build_routes`` method needs to be called before any analysis can be done. + +Expansion interface +------------------- + +There is an interface for the expansion policy as well. It can be used to break down a molecule into reactants. + +.. code-block:: python + + filename = "config.yml" + expander = AiZynthExpander(configfile=filename) + expander.expansion_policy.select("uspto") + expander.filter_policy.select("uspto") + reactions = expander.do_expansion("Cc1cccc(c1N(CC(=O)Nc2ccc(cc2)c3ncon3)C(=O)C4CCS(=O)(=O)CC4)C") + +for this, you only need to select the policies. The filter policy is optional and using it will only add the +feasibility of the reactions not filter it out. + +The result is a nested list of `FixedRetroReaction` objects. This you can manipulate to for instance get +out all the reactants SMILES strings + +.. code-block:: python + + reactants_smiles = [] + for reaction_tuple in reactions: + reactants_smiles.append([mol.smiles for mol in reaction_tuple[0].reactants[0]) + +or you can put all the metadata of all the reactions in a pandas dataframe + +.. code-block:: python + + import pandas as pd + metadata = [] + for reaction_tuple in reactions: + for reaction in reaction_tuple: + metadata.append(reaction.metadata) + df = pd.DataFrame(metadata) + + +Further reading +--------------- + +The docstrings of all modules, classes and methods can be consulted :doc:`here ` + + +and you can always find them in an interactive Python shell using for instance: + +.. code-block:: python + + from aizynthfinder.chem import Molecule + help(Molecule) + help(Molecule.fingerprint) + + +If you are interested in the the relationships between the classes have a look :doc:`here ` +and if you want to dig deeper in to the main algorithmic sequences have a look :doc:`here ` \ No newline at end of file diff --git a/_sources/relationships.rst.txt b/_sources/relationships.rst.txt new file mode 100644 index 0000000..bef2980 --- /dev/null +++ b/_sources/relationships.rst.txt @@ -0,0 +1,24 @@ +Relationships +============= + +This page shows some relationship diagrams, i.e. how the different objects are connect in a typical retrosynthesis +analysis using Monte Carlo tree search. + +These are the tree different types of relationships used: + +.. image:: line-desc.png + +Tree search +----------- + +This diagram explains how the different object are connect that are responsible for the Monte-Carlo tree search. + +.. image:: treesearch-rel.png + + +Analysis / post-processing +-------------------------- + +This diagram explains how the different objects involved in the analysis of the search are connected. + +.. image:: analysis-rel.png diff --git a/_sources/scoring.rst.txt b/_sources/scoring.rst.txt new file mode 100644 index 0000000..6ff1d1d --- /dev/null +++ b/_sources/scoring.rst.txt @@ -0,0 +1,63 @@ +Scoring +======= + +aizynthfinder is capable of scoring reaction routes, both in the form of ``MctsNode`` objects when a search tree is available, +and in the form of ``ReactionTree`` objects if post-processing is required. + +Currently, there are a few scoring functions available + + * State score - a function of the number of precursors in stock and the length of the route + * Number of reactions - the number of steps in the route + * Number of pre-cursors - the number of pre-cursors in the route + * Number of pre-cursors in stock - the number of the pre-cursors that are purchaseable + * Average template occurrence - the average occurrence of the templates used in the route + * Sum of prices - the plain sum of the price of all pre-cursors + * Route cost score - the cost of the synthesizing the route (Badowski et al. Chem Sci. 2019, 10, 4640) + + +The *State score* is the score that is guiding the tree search in the :doc:`update phase `, and +this is not configurable. + +In the Jupyter notebook :doc:`GUI ` one can choose to score the routes with any of the loaded the scorers. + +The first four scoring functions are loaded automatically when an ``aizynthfinder`` object is created. + + +Add new scoring functions +------------------------- + + +Additional scoring functions can be implemented by inheriting from the class ``Scorer`` in the ``aizynthfinder.context.scoring.scorers`` module. +The scoring class needs to implement the ``_score_node``, ``_score_reaction_tree`` and the ``__repr__`` methods. + +This is an example of that. + +.. code-block:: python + + from aizynthfinder.context.scoring.scorers import Scorer + + class DeltaNumberOfTransformsScorer(Scorer): + + def __repr__(self): + return "delta number of transforms" + + def _score_node(self, node): + return self._config.max_transforms - node.state.max_transforms + + def _score_reaction_tree(self, tree): + return self._config.max_transforms - len(list(tree.reactions())) + + +This can then be added to the ``scorers`` attribute of an ``aizynthfinderfinder`` object. The ``scorers`` attribute is a collection +of ``Scorer`` objects. + +For instance to use this in the Jupyter notebook GUI, one can do + +.. code-block:: python + + from aizynthfinder.interfaces import AiZynthApp + app = AiZynthApp("config_local.yml", setup=False) + scorer = DeltaNumberOfTransformsScorer(app.finder.config) + app.finder.scorers.load(scorer) + app.setup() + diff --git a/_sources/sequences.rst.txt b/_sources/sequences.rst.txt new file mode 100644 index 0000000..c188505 --- /dev/null +++ b/_sources/sequences.rst.txt @@ -0,0 +1,62 @@ +Sequences +========= + +This page shows some sequence diagrams to aid in the understanding of how information +is passed between different objects in the Monte Carlo tree search. +The sequences are simplified, but explains the overall picture. +The flow of information / method call should be read top-down. + +Analysis / post-processing +-------------------------- + +This sequence explains how the ``AiZynthFinder`` object exports the top-ranked reaction tree +as a JSON. Note, this is only one possible sequence for analysis of the trees. + +.. image:: analysis-seq.png + + +Monte Carlo tree search +----------------------- + +This sequence explains how the Monte Carlo tree search is carried out by the ``AiZynthFinder`` object. + +.. image:: treesearch-seq.png + +The following text explains what is executed at each iteration of the tree search (the outer loop in the ``one_iteration()`` method of the ``MctsSearchTree`` class). + +First, a leaf is selected using the ``select_leaf()`` method of the ``MctsSearchTree`` class. This is called the `Selection` phase in the literature and will pick the most promising leaf to continue the search from. In the first iteration, this is simply the root node. For the rest of the iterations, the algorithm will execute the following: + +1. Set the current node to the root +2. Loop while the current is expanded, and the state of the current node is not solved + + a. Select the most promising child of the current node by calling the ``promising_child()`` method of the ``MctsNode`` class. + b. If there is such a child set current node to the child + +3. Return current node + +The loop condition in 2. will use the ``is_expanded`` flag of the current node and the ``is_solved`` flag of the state of the current node (see below). 2.a. might not return any child if all the children of the current node were rejected by the tree search (the templates were not applicable). + +Second, the selected leaf node is expanded. This is called the `Expansion` phase in the literature and is used to add new children to a node. The ``expand()`` method of the ``MctsNode`` class takes care of this, but it actually does not instantiate any children nodes. What it does is to use the expansion policy to extract ``RetroReaction`` objects and the probability of each such action. The probability for each action will also be the initial value of each child node. The ``expand()`` method will also set the visitation count for each child to 1. If the ``is_expanded`` flag of the ``MctsNode`` is set or if the ``is_expandable`` flag is not set (see below), the ``expand()`` method will not do anything. + +Third, we enter the inner loop of the tree search or the `Rollout` phase, which has the purpose of expanding the tree until we reach a terminal state., i.e. until the ``is_terminal`` flag of the current leaf node is set. The inner loop will execute the following steps: + +1. Retrieve the most promising child of the current leaf node (see above). +2. If such a child exists, expand it using the ``expand()`` method and the set current leaf node to this child. + +If 1. does return any child, the ``is_terminal`` flag of the leaf node will have been set and therefore the inner loop will break. Similarly, if the child returned by 1. and set to the current leaf in 2. contains a terminal state, the loop will break. + +Fourth, and finally the algorithm enters the `Backpropagation` phase, which is used to update the value of each node, from the current leaf node all the way to the root. This is done by calling the ``backpropagate()`` method of the ``MctsTreeSearch`` class, which in turn will call the ``backpropagate()`` method of each node on the path between the current leaf and the root. + +A few things are worth mentioning about the ``promising_child()`` method of the ``MctsNode`` class. If will select the most promising child by sorting them on the upper confidence bound (UCB) score. The child with the highest score will be selected for instantiation, which means that the ``RetroReaction`` associated with the child will be applied to create new precursors. These precursors will form the state of the new ``MctsNode`` object that is the child. If the application of the reaction failed to produce any precursors, the child value will be set to a large negative value that prevents it from being selected again. The child value will be set to a large negative value also if a filter policy is used in the search and the filter rejects the reaction. Furthermore, ``promising_child()`` will be called recursively until a child can be instantiated (the reaction can be applied). If none of the children can be instantiated the ``is_expanded`` and ``expandable`` flags are updated, and the method returns no child (``None``). + +This list explains the different flags of the Node and State objects that are used at various points in the tree search algorithm + +================= ============= =========================================================================================== ============================================ ================================================================================= ================================================================================================ +Flag Class Description Used when Initialized to Changed by +================= ============= =========================================================================================== ============================================ ================================================================================= ================================================================================================ +``is_expanded`` ``MctsNode`` is True when the node has been expanded Selection, Expansion False when node is created ``expand()``, sets it to True. ``promising_child()`` sets it to False if no child could be instantiated. +``is_expandable`` ``MctsNode`` is True if the node can be expanded Rollout (indirectly), Expansion Set to the opposite of the ``is_terminal`` flag of the state when node is created ``promising_child()`` sets it to False if no child could be instantiated. +``is_terminal()`` ``MctsNode`` is True if either the node is unexpandable or the ``is_terminal`` flag of the state is set Rollout N/A N/A +``is_solved`` ``MctsState`` is True if all precursors in the state is in stock Selection, State init(indirectly) True or False depending on stock Never +``is_terminal`` ``MctsState`` is True is ``is_solved`` is True or maximum number of transforms has been reached Rollout (indirectly), Node init (indirectly) True or False Never +================= ============= =========================================================================================== ============================================ ================================================================================= ================================================================================================ \ No newline at end of file diff --git a/_sources/stocks.rst.txt b/_sources/stocks.rst.txt new file mode 100644 index 0000000..a100a30 --- /dev/null +++ b/_sources/stocks.rst.txt @@ -0,0 +1,147 @@ +Stocks +====== + +The stock files specified in the configuration file are loaded and a set of inchi keys +are stored in-memory for lookup. However, the tool supports other stock queries as well as a way +to fully customize the lookup. + +Mongo database stock +-------------------- + +First, support for lookup inchi keys in a Mongo database is supported. The Mongo client should +have a database and a collection containing documents with at least two fields: `inchi_key` and `source`. +The `inchi_key` field will be used for lookup and `source` specifies the source database of the compound. + +By adding these lines to the configuration file, the Mongo database will be used: + +.. code-block:: yaml + + stock: + type: mongodb + host: user@myurl.com + database: database_name + collection: compounds + + +If no options are provided to the ``mongodb_stock`` key, the host, database and collection are taken to be `localhost`, +`stock_db`, and `molecules`, respectively. + +Stop criteria +------------- + +The stock can be used to stop the tree search based on three criteria: a) minimum price, b) maximum amount and c) count of different elements in the molecule. +Note that the stock query class need to support querying for price and amount, if the stop criteria should work properly. + +The stop criteria can be specified in the configuration file + +.. code-block:: yaml + + stock: + stop_criteria: + price: 10 + counts: + C: 10 + + +In the Jupyter GUI you can set the limit on the element occurences, but currently not the price and amount limits. + +Custom stock +------------ + +Support for any type of lookup is provided. You just need to write a python class that implements the ``__contains__`` +and subclasses the ``aizynthfinder.context.stock.queries.StockQueryMixin``. The ``__contains__`` method is used for lookup and should take a ``Molecule`` object as only argument. +The ``StockQueryMixin`` mixin class provide a default interface for some methods that perhaps isn't possible to implement in all query classes. + +This is an example: + +.. code-block:: + + from rdkit.Chem import Lipinski + from aizynthfinder.context.stock.queries import StockQueryMixin + class CriteriaStock(StockQueryMixin): + def __contains__(self, mol): + return Lipinski.HeavyAtomCount(mol.rd_mol) < 10 + + +To use this stock with the ``aizynthcli`` tool, save it in a ``custom_stock.py`` module that is located in a directory known to +the python interpreter. Add this line to the module. + +.. code-block:: + + stock = CriteriaStock() + + +and it will be automatically used in the tree search. + +Alternatively the custom query class can be used by the ``aizynthapp`` tool. + + +.. code-block:: + + from aizynthfinder import AiZynthApp + configfile="config_local.yml" + app = AiZynthApp(configfile, setup=False) + app.finder.stock.load(CriteriaStock(), "criteria") # This loads the custom stock class + app.setup() + + +Lastly, it is possible to specify a custom stock class in the configuration file if it is located in a module that +is known by the python interpreter. + +.. code-block:: + + stock: + type: aizynthfinder.contrib.stocks.CriteriaStock + + +can be used if the `aizynthfinder.contrib.stocks` is an existing sub-package and module. + + +Making stocks +------------- + +We provide a tool to create inchi key-based stocks from SMILES strings. Thereby, one +can create a stock based on for instance a subset of the ZINC database. + +The tool support both creating a stock in HDF5 format or adding them to an existing Mongo database. + +The tool is easiest to use if one has a number of plain text files, in which each row has one SMILES. + +Then one can use one of these two commands: + + +.. code-block:: + + smiles2stock --files file1.smi file2.smi --output stock.hdf5 + smiles2stock --files file1.smi file2.smi --output my_db --target mongo + + +to create either an HDF5 stock or a Mongo database stock, respectively. The ``file1.smi`` and ``file2.smi`` +are simple text files and ``my_db`` is the source tag for the Mongo database. + + +If one has SMILES in any other format, one has to provide a custom module that extract the SMILES from +the input files. This is an example of such a module that can be used with downloads from the Zinc database +where the first row contains headers and the SMILES are the first element on each line. + + +.. code-block:: + + def extract_smiles(filename): + with open(filename, "r") as fileobj: + for i, line in enumerate(fileobj.readlines()): + if i == 0: + continue + yield line.strip().split(" ")[0] + + +if this is saved as ``load_zinc.py`` in a path that is known to the Python interpreter, it can be +used like this + +.. code-block:: + + export PYTHONPATH=`pwd` + smiles2stock --files load_zinc file1.smi file2.smi --source module --output stock.hdf5 + + +where the first line adds the current directory to the python path (if you are using a Bash shell). diff --git a/_static/alabaster.css b/_static/alabaster.css new file mode 100644 index 0000000..fc325ec --- /dev/null +++ b/_static/alabaster.css @@ -0,0 +1,709 @@ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Georgia, serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: -20px -30px 20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} +@media screen and (min-width: 876px) { + div.sphinxsidebar { + position: fixed; + margin-left: 0; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Make nested-list/multi-paragraph items look better in Releases changelog + * pages. Without this, docutils' magical list fuckery causes inconsistent + * formatting between different release sub-lists. + */ +div#changelog > div.section > ul > li > p:only-child { + margin-bottom: 0; +} + +/* Hide fugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..bf18350 --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,906 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; + margin-right: 0.5em; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/custom.css b/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..e1bfd70 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,358 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + this.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated === 'undefined') + return string; + return (typeof translated === 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated === 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + var url = new URL(window.location); + url.searchParams.delete('highlight'); + window.history.replaceState({}, '', url); + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar : function() { + $('input[name=q]').first().focus(); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) + return; + + $(document).keydown(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box, textarea, dropdown or button + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' + && activeElementType !== 'BUTTON') { + if (event.altKey || event.ctrlKey || event.metaKey) + return; + + if (!event.shiftKey) { + switch (event.key) { + case 'ArrowLeft': + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) + break; + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + break; + case 'ArrowRight': + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) + break; + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + break; + case 'Escape': + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) + break; + Documentation.hideSearchWords(); + return false; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case '/': + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) + break; + Documentation.focusSearchBar(); + return false; + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000..7440b26 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,14 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '4.0.0', + LANGUAGE: 'None', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/jquery-3.5.1.js b/_static/jquery-3.5.1.js new file mode 100644 index 0000000..5093733 --- /dev/null +++ b/_static/jquery-3.5.1.js @@ -0,0 +1,10872 @@ +/*! + * jQuery JavaScript Library v3.5.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2020-05-04T22:49Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.5.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.5 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2020-03-14 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px"; + tr.style.height = "1px"; + trChild.style.height = "9px"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.analysis package

+
+

Submodules

+
+
+

aizynthfinder.analysis.routes module

+

Module containing classes to store and manipulate collections of synthetic routes.

+
+
+class aizynthfinder.analysis.routes.RouteCollection(reaction_trees, **kwargs)
+

Bases: object

+

Holds a collections of reaction routes.

+

If can be the top scored nodes, their scores and +the reaction trees created from them. +It can also be a cluster of such routes.

+

The class has the functionality to compute collective results +for the different routes such as images.

+

Properties of individual route can be obtained with simple indexing.

+
route0 = collection[0]
+
+
+
+
Variables
+
    +
  • all_scores – all the computed scores for the routes

  • +
  • nodes – the top-ranked MCTS-like nodes

  • +
  • scores – initial scores of top-ranked nodes or routes

  • +
  • reaction_trees – the reaction trees created from the top-ranked nodes

  • +
  • clusters – the created clusters from the collection

  • +
  • route_metadata – the metadata of the reaction trees

  • +
+
+
Parameters
+

reaction_trees (Sequence[ReactionTree]) – the trees to base the collection on

+
+
Return type
+

None

+
+
+
+
+classmethod from_analysis(analysis, selection=None)
+

Create a collection from a tree analysis.

+
+
Parameters
+
+
+
Returns
+

the created collection

+
+
Return type
+

aizynthfinder.analysis.routes.RouteCollection

+
+
+
+ +
+
+property dicts: Sequence[StrDict]
+

Returns a list of dictionary representation of the routes

+
+ +
+
+property images: Sequence[PilImage]
+

Returns a list of pictoral representation of the routes

+
+ +
+
+property jsons: Sequence[str]
+

Returns a list of JSON string representation of the routes

+
+ +
+
+cluster(n_clusters, max_clusters=5, distances_model='ted', **kwargs)
+

Cluster the route collection into a number of clusters.

+

Additional arguments to the distance or clustering algorithm +can be passed in as key-word arguments.

+

When distances_model is “lstm”, a key-word argument model_path needs to be given +when distances_model is “ted”, two optional key-word arguments timeout and content +can be given.

+

If the number of reaction trees are less than 3, no clustering will be performed

+
+
Parameters
+
    +
  • n_clusters (int) – the desired number of clusters, if less than 2 triggers optimization

  • +
  • max_clusters (int) – the maximum number of clusters to consider

  • +
  • distances_model (str) – can be ted or lstm and determines how the route distances are computed

  • +
  • kwargs (Any) –

  • +
+
+
Returns
+

the cluster labels

+
+
Return type
+

np.ndarray

+
+
+
+ +
+
+combined_reaction_trees(recreate=False)
+

Return an object that combines all the reaction tree into a single reaction tree graph

+
+
Parameters
+

recreate (bool) – if False will return a cached object if available, defaults to False

+
+
Returns
+

the combined trees

+
+
Return type
+

aizynthfinder.analysis.utils.CombinedReactionTrees

+
+
+
+ +
+
+compute_scores(*scorers)
+

Compute new scores for all routes in this collection. +They can then be accessed with the all_scores attribute.

+
+
Parameters
+

scorers (Scorer) –

+
+
Return type
+

None

+
+
+
+ +
+
+dict_with_extra(include_scores=False, include_metadata=False)
+

Return the routes as dictionaries with optionally all scores and +all metadata added to the root (target) node.

+
+
Returns
+

the routes as dictionaries

+
+
Return type
+

Sequence[StrDict]

+
+
+
+ +
+
+dict_with_scores()
+

Return the routes as dictionaries with all scores added +to the root (target) node.

+
+
Returns
+

the routes as dictionaries

+
+
Return type
+

Sequence[StrDict]

+
+
+
+ +
+
+distance_matrix(recreate=False, model='ted', **kwargs)
+

Compute the distance matrix between each pair of reaction trees

+

All key-word arguments are passed along to the route_distance_calculator +function from the route_distances package.

+

When model is “lstm”, a key-word argument model_path needs to be given +when model is “ted”, two optional key-word arguments timeout and content +can be given.

+
+
Parameters
+
    +
  • recreate (bool) – if False, use a cached one if available

  • +
  • model (str) – the type of model to use “ted” or “lstm”

  • +
  • kwargs (Any) –

  • +
+
+
Returns
+

the square distance matrix

+
+
Return type
+

np.ndarray

+
+
+
+ +
+
+make_dicts()
+

Convert all reaction trees to dictionaries

+
+
Return type
+

Sequence[StrDict]

+
+
+
+ +
+
+make_images()
+

Convert all reaction trees to images

+
+
Return type
+

Sequence[Optional[PilImage]]

+
+
+
+ +
+
+make_jsons()
+

Convert all reaction trees to JSON strings

+
+
Return type
+

Sequence[str]

+
+
+
+ +
+
+rescore(scorer)
+

Rescore the routes in the collection, and thereby re-order them.

+

This will replace the scores attribute, and update the all_scores +attribute with another entry.

+
+
Parameters
+

scorer (Scorer) – the scorer to use

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

aizynthfinder.analysis.tree_analysis module

+

Module containing classes to perform analysis of the tree search results.

+
+
+class aizynthfinder.analysis.tree_analysis.TreeAnalysis(search_tree, scorer=None)
+

Bases: object

+

Class that encapsulate various analysis that can be +performed on a search tree.

+
+
Variables
+
    +
  • scorer – the object used to score the nodes

  • +
  • search_tree – the search tree

  • +
+
+
Parameters
+
+
+
Return type
+

None

+
+
+
+
+best()
+

Returns the route or MCTS-like node with the highest score. +If several routes have the same score, it will return the first

+
+
Returns
+

the top scoring node or route

+
+
Return type
+

Union[MctsNode, ReactionTree]

+
+
+
+ +
+
+sort(selection=None)
+

Sort and select the nodes or routes in the search tree.

+
+
Parameters
+

selection (Optional[RouteSelectionArguments]) – selection criteria for the routes

+
+
Returns
+

the items

+
+
Returns
+

the score

+
+
Return type
+

Tuple[Union[Sequence[MctsNode], Sequence[ReactionTree]], Sequence[float]]

+
+
+
+ +
+
+tree_statistics()
+

Returns statistics of the tree

+

Currently it returns the number of nodes, the maximum number of transforms, +maximum number of children, top score, if the top score route is solved, +the number of molecule in the top score node, and information on pre-cursors

+
+
Returns
+

the statistics

+
+
Return type
+

StrDict

+
+
+
+ +
+ +
+
+

aizynthfinder.analysis.utils module

+

Helper routines and class for the aizynthfinder.analysis package. +To avoid clutter in that package, larger utility algorithms are placed herein.

+
+
+class aizynthfinder.analysis.utils.RouteSelectionArguments(nmin=5, nmax=25, return_all=False)
+

Bases: object

+

Selection arguments for the tree analysis class

+

If return_all is False, it will return at least nmin routes and if routes have the same +score it will return them as well up to nmax routes.

+

If return_all is True, it will return all solved routes if there is at least one is solved, otherwise +the nmin and nmax will be used.

+
+
Parameters
+
    +
  • nmin (int) –

  • +
  • nmax (int) –

  • +
  • return_all (bool) –

  • +
+
+
Return type
+

None

+
+
+
+
+nmin: int = 5
+
+ +
+
+nmax: int = 25
+
+ +
+
+return_all: bool = False
+
+ +
+ +
+
+class aizynthfinder.analysis.utils.CombinedReactionTrees(reaction_trees)
+

Bases: object

+

Encapsulation of an algorithm that combines several reaction trees into a +larger bipartite graph with all reactions and molecules.

+

The reactions at a specific level of the reaction trees are grouped based +on the reaction smiles.

+
+
Params reactions_trees
+

the list of reaction trees to combine

+
+
Parameters
+

reaction_trees (Sequence[ReactionTree]) –

+
+
Return type
+

None

+
+
+
+
+to_dict()
+

Returns the graph as a dictionary in a pre-defined format.

+
+
Returns
+

the combined reaction trees

+
+
Return type
+

StrDict

+
+
+
+ +
+
+to_visjs_page(filename, in_stock_colors=None)
+

Create a visualization of the combined reaction tree using the vis.js network library.

+

The HTML page and all the images will be put into a tar-ball.

+
+
Parameters
+
    +
  • filename (str) – the name of the tarball

  • +
  • in_stock_colors (Optional[FrameColors]) – the colors around molecules, defaults to {True: “green”, False: “orange”}

  • +
+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

Module contents

+

Sub-package containing analysis routines

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.chem.html b/aizynthfinder.chem.html new file mode 100644 index 0000000..fce5e71 --- /dev/null +++ b/aizynthfinder.chem.html @@ -0,0 +1,795 @@ + + + + + + + + + aizynthfinder.chem package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.chem package

+
+

Submodules

+
+
+

aizynthfinder.chem.mol module

+

Module containing classes to deal with Molecules - mostly wrappers around rdkit routines.

+
+
+class aizynthfinder.chem.mol.Molecule(rd_mol=None, smiles=None, sanitize=False)
+

Bases: object

+

A base class for molecules. Encapsulate an RDKit mol object and +functions that can be applied to such a molecule.

+

The objects of this class is hashable by the inchi key and hence +comparable with the equality operator.

+
+
Variables
+
    +
  • rd_mol – the RDkit mol object that is encapsulated

  • +
  • smiles – the SMILES representation of the molecule

  • +
+
+
Parameters
+
    +
  • rd_mol (Optional[RdMol]) – a RDKit mol object to encapsulate, defaults to None

  • +
  • smiles (Optional[str]) – a SMILES to convert to a molecule object, defaults to None

  • +
  • sanitize (bool) – if True, the molecule will be immediately sanitized, defaults to False

  • +
+
+
Raises
+

MoleculeException – if neither rd_mol or smiles is given, or if the molecule could not be sanitized

+
+
Return type
+

None

+
+
+
+
+property inchi: str
+

The inchi representation of the molecule +Created by lazy evaluation. Will cause the molecule to be sanitized.

+
+
Returns
+

the inchi

+
+
+
+ +
+
+property inchi_key: str
+

The inchi key representation of the molecule +Created by lazy evaluation. Will cause the molecule to be sanitized.

+
+
Returns
+

the inchi key

+
+
+
+ +
+
+property index_to_mapping: Dict[int, int]
+

Return a dictionary mapping to atom indices to atom mappings

+
+ +
+
+property mapping_to_index: Dict[int, int]
+

Return a dictionary mapping to atom mappings to atom indices

+
+ +
+
+property weight: float
+

Return the exact molecular weight of the molecule

+
+ +
+
+basic_compare(other)
+

Compare this molecule to another but only to +the basic part of the inchi key, thereby ignoring stereochemistry etc

+
+
Parameters
+

other (aizynthfinder.chem.mol.Molecule) – the molecule to compare to

+
+
Returns
+

True if chemical formula and connectivity is the same

+
+
Return type
+

bool

+
+
+
+ +
+
+fingerprint(radius, nbits=2048, chiral=False)
+

Returns the Morgan fingerprint of the molecule

+
+
Parameters
+
    +
  • radius (int) – the radius of the fingerprint

  • +
  • nbits (int) – the length of the fingerprint

  • +
  • chiral (bool) – if True, include chirality information

  • +
+
+
Returns
+

the fingerprint

+
+
Return type
+

numpy.ndarray

+
+
+
+ +
+
+has_atom_mapping()
+

Determines if a the molecule has atom mappings

+
+
Returns
+

True if at least one atom has a mapping

+
+
Return type
+

bool

+
+
+
+ +
+
+make_unique()
+

Returns an instance of the UniqueMolecule class that +is representing the same molecule but is not hashable or comparable.

+
+
Returns
+

the unique molecule

+
+
Return type
+

aizynthfinder.chem.mol.UniqueMolecule

+
+
+
+ +
+
+remove_atom_mapping(exceptions=None)
+

Remove all mappings of the atoms and update the smiles

+
+
Parameters
+

exceptions (Optional[Sequence[int]]) – keep the listed atom mappings

+
+
Return type
+

None

+
+
+
+ +
+
+sanitize(raise_exception=True)
+

Sanitizes the molecule if it has not been done before.

+
+
Parameters
+

raise_exception (bool) – if True will raise exception on failed sanitation

+
+
Raises
+

MoleculeException – if the molecule could not be sanitized

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+class aizynthfinder.chem.mol.TreeMolecule(parent, transform=None, rd_mol=None, smiles=None, sanitize=False, mapping_update_callback=None)
+

Bases: aizynthfinder.chem.mol.Molecule

+

A special molecule that keeps a reference to a parent molecule.

+

If the class is instantiated without specifying the transform argument, +it is computed by increasing the value of the parent.transform variable.

+

If no parent is provided the atoms with atom mapping number are tracked +and inherited to children.

+
+
Variables
+
    +
  • mapped_mol – the tracked molecule with atom mappings

  • +
  • mapped_smiles – the SMILES of the tracked molecule with atom mappings

  • +
  • original_smiles – the SMILES as passed when instantiating the class

  • +
  • parent – parent molecule

  • +
  • transform – a numerical number corresponding to the depth in the tree

  • +
+
+
Parameters
+
    +
  • parent (Optional['TreeMolecule']) – a TreeMolecule object that is the parent

  • +
  • transform (Optional[int]) – the transform value, defaults to None

  • +
  • rd_mol (Optional[RdMol]) – a RDKit mol object to encapsulate, defaults to None

  • +
  • smiles (Optional[str]) – a SMILES to convert to a molecule object, defaults to None

  • +
  • sanitize (bool) – if True, the molecule will be immediately sanitized, defaults to False

  • +
  • mapping_update_callback (Optional[Callable[['TreeMolecule'], None]]) – if given will call this method before setting up the mapped_smiles

  • +
+
+
Raises
+

MoleculeException – if neither rd_mol or smiles is given, or if the molecule could not be sanitized

+
+
Return type
+

None

+
+
+
+
+property mapping_to_index: Dict[int, int]
+

Return a dictionary mapping to atom mappings to atom indices

+
+ +
+
+property mapped_atom_bonds: List[Tuple[int, int]]
+

Return a list of atom bonds as tuples on the mapped atom indices

+
+ +
+ +
+
+class aizynthfinder.chem.mol.UniqueMolecule(rd_mol=None, smiles=None, sanitize=False)
+

Bases: aizynthfinder.chem.mol.Molecule

+

A special molecule with the hash set to the id of the object. +Therefore no two instances of this class will be comparable.

+
+
Parameters
+
    +
  • rd_mol (Optional[RdMol]) – a RDKit mol object to encapsulate, defaults to None

  • +
  • smiles (Optional[str]) – a SMILES to convert to a molecule object, defaults to None

  • +
  • sanitize (bool) – if True, the molecule will be immediately sanitized, defaults to False

  • +
+
+
Raises
+

MoleculeException – if neither rd_mol or smiles is given, or if the molecule could not be sanitized

+
+
Return type
+

None

+
+
+
+ +
+
+aizynthfinder.chem.mol.none_molecule()
+

Return an empty molecule

+
+
Return type
+

aizynthfinder.chem.mol.UniqueMolecule

+
+
+
+ +
+
+

aizynthfinder.chem.reaction module

+

Module containing classes to deal with Reactions.

+
+
+class aizynthfinder.chem.reaction.RetroReaction(mol, index=0, metadata=None, **kwargs)
+

Bases: abc.ABC, aizynthfinder.chem.reaction._ReactionInterfaceMixin

+

A retrosynthesis reaction. Only a single molecule is the reactant.

+

This is an abstract class and child classes needs to implement the _apply and _make_smiles functions +that should create the reactants molecule objects and the reaction SMILES representation, respectively.

+
+
Variables
+
    +
  • mol – the TreeMolecule object that this reaction is applied to

  • +
  • index – a unique index of this reaction, +to count for the fact that a reaction can produce more than one outcome

  • +
  • metadata – meta data associated with the reaction

  • +
+
+
Parameters
+
    +
  • mol (TreeMolecule) – the molecule

  • +
  • index (int) – the index, defaults to 0

  • +
  • metadata (Optional[StrDict]) – some meta data

  • +
  • kwargs (Any) –

  • +
+
+
Params kwargs
+

any extra parameters for child classes

+
+
Return type
+

None

+
+
+
+
+classmethod from_serialization(init_args, reactants)
+

Create an object from a serialization. It does +1) instantiate an object using the init_args and +2) set the reactants to a tuple-form of `reactants

+
+
Parameters
+
    +
  • init_args (StrDict) – the arguments passed to the __init__ method

  • +
  • reactants (List[List[TreeMolecule]]) – the reactants

  • +
+
+
Returns
+

the deserialized object

+
+
Return type
+

RetroReaction

+
+
+
+ +
+
+property reactants: Tuple[Tuple[TreeMolecule, ...], ...]
+

Returns the reactant molecules. +Apply the reaction if necessary.

+
+
Returns
+

the products of the reaction

+
+
+
+ +
+
+property smiles: str
+

The reaction as a SMILES

+
+
Returns
+

the SMILES

+
+
+
+ +
+
+property unqueried: bool
+

Return True if the reactants has never been retrieved

+
+ +
+
+copy(index=None)
+

Shallow copy of this instance.

+
+
Parameters
+

index (Optional[int]) – new index, defaults to None

+
+
Returns
+

the copy

+
+
Return type
+

RetroReaction

+
+
+
+ +
+
+mapped_reaction_smiles()
+

Get the mapped reaction SMILES if it exists +:return: the SMILES

+
+
Return type
+

str

+
+
+
+ +
+
+to_dict()
+

Return the retro reaction as dictionary +This dictionary is not suitable for serialization, but is used by other serialization routines +The elements of the dictionary can be used to instantiate a new reaction object

+
+
Return type
+

StrDict

+
+
+
+ +
+ +
+
+class aizynthfinder.chem.reaction.TemplatedRetroReaction(mol, index=0, metadata=None, **kwargs)
+

Bases: aizynthfinder.chem.reaction.RetroReaction

+

A retrosynthesis reaction that uses a reaction SMARTS and RDChiral to produce reactant molecules. +The SMILES representation of the reaction is the SMARTS (modified by RDKit)

+
+
Parameters
+
    +
  • mol (TreeMolecule) – the molecule

  • +
  • index (int) – the index, defaults to 0

  • +
  • metadata (Optional[StrDict]) – some meta data

  • +
  • smarts – a string representing the template

  • +
  • kwargs (Any) –

  • +
+
+
+
+
+property rd_reaction: RdReaction
+

Return the RDKit reaction created from the SMART

+
+ +
+
+to_dict()
+

Return the retro reaction as dictionary +This dictionary is not suitable for serialization, but is used by other serialization routines +The elements of the dictionary can be used to instantiate a new reaction object

+
+
Return type
+

StrDict

+
+
+
+ +
+ +
+
+class aizynthfinder.chem.reaction.SmilesBasedRetroReaction(mol, index=0, metadata=None, **kwargs)
+

Bases: aizynthfinder.chem.reaction.RetroReaction

+

A retrosynthesis reaction where the SMILES of the reactants are given on initiation

+

The SMILES representation of the reaction is the reaction SMILES

+
+
Parameters
+
    +
  • mol (TreeMolecule) – the molecule

  • +
  • index (int) – the index, defaults to 0

  • +
  • metadata (Optional[StrDict]) – some meta data

  • +
  • reactants_str – a dot-separated string of reactant SMILES strings

  • +
  • kwargs (Any) –

  • +
+
+
+
+
+to_dict()
+

Return the retro reaction as dictionary +This dictionary is not suitable for serialization, but is used by other serialization routines +The elements of the dictionary can be used to instantiate a new reaction object

+
+
Return type
+

StrDict

+
+
+
+ +
+ +
+
+class aizynthfinder.chem.reaction.FixedRetroReaction(mol, smiles='', metadata=None)
+

Bases: aizynthfinder.chem.reaction._ReactionInterfaceMixin

+

A retrosynthesis reaction that has the same interface as RetroReaction +but it is fixed so it does not support SMARTS application or any creation of reactants.

+

The reactants are set by using the reactants property.

+
+
Variables
+
    +
  • mol – the UniqueMolecule object that this reaction is applied to

  • +
  • smiles – the SMILES representation of the RDKit reaction

  • +
  • metadata – meta data associated with the reaction

  • +
  • reactants – the reactants of this reaction

  • +
+
+
Parameters
+
    +
  • mol (UniqueMolecule) – the molecule

  • +
  • smiles (str) – the SMILES of the reaction

  • +
  • metadata (Optional[StrDict]) – some meta data

  • +
+
+
Return type
+

None

+
+
+
+
+copy()
+

Shallow copy of this instance.

+
+
Returns
+

the copy

+
+
Return type
+

aizynthfinder.chem.reaction.FixedRetroReaction

+
+
+
+ +
+
+to_smiles_based_retroreaction()
+

Convert a FixedRetroReaction to a SmilesBasedRetroReaction.

+
+
Returns
+

the SmilesBasedRetroReaction.

+
+
Return type
+

aizynthfinder.chem.reaction.SmilesBasedRetroReaction

+
+
+
+ +
+ +
+
+aizynthfinder.chem.reaction.hash_reactions(reactions, sort=True)
+

Creates a hash for a list of reactions

+
+
Parameters
+
    +
  • reactions (Union[Iterable[RetroReaction], Iterable[FixedRetroReaction]]) – the reactions to hash

  • +
  • sort (bool) – if True will sort all molecules, defaults to True

  • +
+
+
Returns
+

the hash string

+
+
Return type
+

str

+
+
+
+ +
+
+

aizynthfinder.chem.serialization module

+

Module containing helper classes and routines for serialization.

+
+
+class aizynthfinder.chem.serialization.MoleculeSerializer
+

Bases: object

+

Utility class for serializing molecules

+

The id of the molecule to be serialized can be obtained with:

+
serializer = MoleculeSerializer()
+mol = Molecule(smiles="CCCO")
+idx = serializer[mol]
+
+
+

which will take care of the serialization of the molecule.

+
+
Return type
+

None

+
+
+
+
+property store: Dict[int, Any]
+

Return all serialized molecules as a dictionary

+
+ +
+ +
+
+class aizynthfinder.chem.serialization.MoleculeDeserializer(store)
+

Bases: object

+

Utility class for deserializing molecules. +The serialized molecules are created upon instantiation of the class.

+

The deserialized molecules can be obtained with:

+
deserializer = MoleculeDeserializer()
+mol = deserializer[idx]
+
+
+
+
Parameters
+

store (Dict[int, Any]) –

+
+
Return type
+

None

+
+
+
+
+get_tree_molecules(ids)
+

Return multiple deserialized tree molecules

+
+
Parameters
+

ids (Sequence[int]) – the list of IDs to deserialize

+
+
Returns
+

the molecule objects

+
+
Return type
+

Sequence[aizynthfinder.chem.TreeMolecule]

+
+
+
+ +
+ +
+
+aizynthfinder.chem.serialization.serialize_action(action, molecule_store)
+

Serialize a retrosynthesis action

+
+
Parameters
+
+
+
Returns
+

the action as a dictionary

+
+
Return type
+

StrDict

+
+
+
+ +
+
+aizynthfinder.chem.serialization.deserialize_action(dict_, molecule_store)
+

Deserialize a retrosynthesis action

+
+
Parameters
+
    +
  • dict – the (re)action as a dictionary

  • +
  • molecule_store (MoleculeDeserializer) – the molecule deserialization object

  • +
  • dict_ (StrDict) –

  • +
+
+
Returns
+

the created action object

+
+
Return type
+

RetroReaction

+
+
+
+ +
+
+

Module contents

+

Sub-package containing chemistry routines

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.context.html b/aizynthfinder.context.html new file mode 100644 index 0000000..2ce9d36 --- /dev/null +++ b/aizynthfinder.context.html @@ -0,0 +1,427 @@ + + + + + + + + + aizynthfinder.context package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.context package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

aizynthfinder.context.collection module

+

Module containing a class that is the base class for all collection classes (stock, policies, scorers)

+
+
+class aizynthfinder.context.collection.ContextCollection
+

Bases: abc.ABC

+

Abstract base class for a collection of items +that can be loaded and then (de-)selected.

+
+

One can obtain individual items with:

+
+
an_item = collection["key"]
+
+
+

And delete items with

+
del collection["key"]
+
+
+
+
Return type
+

None

+
+
+
+
+property items: List[str]
+

The available item keys

+
+ +
+
+property selection: Union[List[str], str, None]
+

The keys of the selected item(s)

+
+ +
+
+deselect(key=None)
+

Deselect one or all items

+

If no key is passed, all items will be deselected.

+
+
Parameters
+

key (Optional[str]) – the key of the item to deselect, defaults to None

+
+
Raises
+

KeyError – if the key is not among the selected ones

+
+
Return type
+

None

+
+
+
+ +
+
+abstract load(*_)
+

Load an item. Needs to be implemented by a sub-class

+
+
Parameters
+

_ (Any) –

+
+
Return type
+

None

+
+
+
+ +
+
+abstract load_from_config(**config)
+

Load items from a configuration. Needs to be implemented by a sub-class

+
+
Parameters
+

config (Any) –

+
+
Return type
+

None

+
+
+
+ +
+
+select(value, append=False)
+

Select one or more items.

+

If this is a single selection collection, only a single value is accepted. +If this is a multiple selection collection it will overwrite the selection completely, +unless append is True and a single key is given.

+
+
Parameters
+
    +
  • value (Union[str, List[str]]) – the key or keys of the item(s) to select

  • +
  • append (bool) – if True will append single keys to existing selection

  • +
+
+
Raises
+
    +
  • ValueError – if this a single collection and value is multiple keys

  • +
  • KeyError – if at least one of the keys are not corresponding to a loaded item

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+select_all()
+

Select all loaded items

+
+
Return type
+

None

+
+
+
+ +
+
+select_first()
+

Select the first loaded item

+
+
Return type
+

None

+
+
+
+ +
+
+select_last()
+

Select the last loaded item

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

aizynthfinder.context.config module

+

Module containing a class for encapsulating the settings of the tree search

+
+
+class aizynthfinder.context.config.Configuration(search=<factory>, post_processing=<factory>)
+

Bases: object

+

Encapsulating the settings of the tree search, including the policy, +the stock, the loaded scorers and various parameters.

+
+
Parameters
+
    +
  • search (aizynthfinder.context.config._SearchConfiguration) –

  • +
  • post_processing (aizynthfinder.context.config._PostprocessingConfiguration) –

  • +
+
+
Return type
+

None

+
+
+
+
+search: aizynthfinder.context.config._SearchConfiguration
+
+ +
+
+post_processing: aizynthfinder.context.config._PostprocessingConfiguration
+
+ +
+
+stock: aizynthfinder.context.stock.stock.Stock
+
+ +
+
+expansion_policy: aizynthfinder.context.policy.policies.ExpansionPolicy
+
+ +
+
+filter_policy: aizynthfinder.context.policy.policies.FilterPolicy
+
+ +
+
+scorers: aizynthfinder.context.scoring.collection.ScorerCollection
+
+ +
+
+classmethod from_dict(source)
+

Loads a configuration from a dictionary structure. +The parameters not set in the dictionary are taken from the default values. +The policies and stocks specified are directly loaded.

+
+
Parameters
+

source (StrDict) – the dictionary source

+
+
Returns
+

a Configuration object with settings from the source

+
+
Return type
+

Configuration

+
+
+
+ +
+
+classmethod from_file(filename)
+

Loads a configuration from a yaml file. +The parameters not set in the yaml file are taken from the default values. +The policies and stocks specified in the yaml file are directly loaded. +The parameters in the yaml file may also contain environment variables as +values.

+
+
Parameters
+

filename (str) – the path to a yaml file

+
+
Returns
+

a Configuration object with settings from the yaml file

+
+
Raises
+
+
ValueError: if parameter’s value expects an environment variable that

does not exist in the current environment

+
+
+
+
Return type
+

aizynthfinder.context.config.Configuration

+
+
+
+ +
+ +
+
+

Module contents

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.context.policy.html b/aizynthfinder.context.policy.html new file mode 100644 index 0000000..e2d7f12 --- /dev/null +++ b/aizynthfinder.context.policy.html @@ -0,0 +1,676 @@ + + + + + + + + + aizynthfinder.context.policy package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.context.policy package

+
+

Submodules

+
+
+

aizynthfinder.context.policy.expansion_strategies module

+

Module containing classes that implements different expansion policy strategies

+
+
+class aizynthfinder.context.policy.expansion_strategies.ExpansionStrategy(key, config, **kwargs)
+

Bases: abc.ABC

+

A base class for all expansion strategies.

+

The strategy can be used by either calling the get_actions method +of by calling the instantiated class with a list of molecule.

+
expander = MyExpansionStrategy("dummy", config)
+actions, priors = expander.get_actions(molecules)
+actions, priors = expander(molecules)
+
+
+
+
Parameters
+
    +
  • key (str) – the key or label

  • +
  • config (Configuration) – the configuration of the tree search

  • +
  • kwargs (str) –

  • +
+
+
Return type
+

None

+
+
+
+
+abstract get_actions(molecules, cache_molecules=None)
+

Get all the probable actions of a set of molecules

+
+
Parameters
+
    +
  • molecules (Sequence[TreeMolecule]) – the molecules to consider

  • +
  • cache_molecules (Sequence[TreeMolecule]) – additional molecules to submit to the expansion +policy but that only will be cached for later use

  • +
+
+
Returns
+

the actions and the priors of those actions

+
+
Return type
+

Tuple[List[RetroReaction], List[float]]

+
+
+
+ +
+
+reset_cache()
+

Reset the prediction cache

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+class aizynthfinder.context.policy.expansion_strategies.MultiExpansionStrategy(key, config, **kwargs)
+

Bases: aizynthfinder.context.policy.expansion_strategies.ExpansionStrategy

+

A base class for combining multiple expansion strategies.

+

The strategy can be used by either calling the get_actions method +or by calling the instantiated class with a list of molecules.

+
+
Variables
+
    +
  • expansion_strategy_keys – the keys of the selected expansion strategies

  • +
  • additive_expansion – a conditional setting to specify whether all the actions +and priors of the selected expansion strategies should be combined or not. +Defaults to False.

  • +
+
+
Parameters
+
    +
  • key (str) – the key or label

  • +
  • config (Configuration) – the configuration of the tree search

  • +
  • expansion_strategies – the keys of the selected expansion strategies. All keys +of the selected expansion strategies must exist in the expansion policies listed +in config

  • +
  • kwargs (str) –

  • +
+
+
Return type
+

None

+
+
+
+
+get_actions(molecules, cache_molecules=None)
+

Get all the probable actions of a set of molecules, using the selected policies.

+

The default implementation combines all the actions and priors of the +selected expansion strategies into two lists respectively if the +‘additive_expansion’ setting is set to True. This function can be overridden by +a sub class to combine different expansion strategies in different ways.

+
+
Parameters
+
+
+
Returns
+

the actions and the priors of those actions

+
+
Raises
+

PolicyException: if the policy isn’t selected

+
+
Return type
+

Tuple[List[RetroReaction], List[float]]

+
+
+
+ +
+ +
+
+class aizynthfinder.context.policy.expansion_strategies.TemplateBasedExpansionStrategy(key, config, **kwargs)
+

Bases: aizynthfinder.context.policy.expansion_strategies.ExpansionStrategy

+

A template-based expansion strategy that will return TemplatedRetroReaction objects upon expansion.

+
+
Variables
+
    +
  • template_column – the column in the template file that contains the templates

  • +
  • cutoff_cumulative – the accumulative probability of the suggested templates

  • +
  • cutoff_number – the maximum number of templates to returned

  • +
  • use_rdchiral – a boolean to apply templates with RDChiral

  • +
  • use_remote_models – a boolean to connect to remote TensorFlow servers

  • +
  • rescale_prior – a boolean to apply softmax to the priors

  • +
  • chiral_fingerprints – if True will base expansion on chiral fingerprint

  • +
+
+
Parameters
+
    +
  • key (str) – the key or label

  • +
  • config (Configuration) – the configuration of the tree search

  • +
  • model – the source of the policy model

  • +
  • template – the path to a HDF5 file with the templates

  • +
  • kwargs (str) –

  • +
+
+
Raises
+

PolicyException – if the length of the model output vector is not same as the +number of templates

+
+
Return type
+

None

+
+
+
+
+get_actions(molecules, cache_molecules=None)
+

Get all the probable actions of a set of molecules, using the selected policies and given cutoffs

+
+
Parameters
+
    +
  • molecules (Sequence[TreeMolecule]) – the molecules to consider

  • +
  • cache_molecules (Sequence[TreeMolecule]) – additional molecules to submit to the expansion +policy but that only will be cached for later use

  • +
+
+
Returns
+

the actions and the priors of those actions

+
+
Return type
+

Tuple[List[RetroReaction], List[float]]

+
+
+
+ +
+
+reset_cache()
+

Reset the prediction cache

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

aizynthfinder.context.policy.filter_strategies module

+

Module containing classes that implements different filter policy strategies

+
+
+class aizynthfinder.context.policy.filter_strategies.FilterStrategy(key, config, **kwargs)
+

Bases: abc.ABC

+

A base class for all filter strategies.

+

The filter can be applied by either calling the apply method +of by calling the instantiated class with a reaction.

+
filter = MyFilterStrategy("dummy", config)
+filter.apply(reaction)
+filter(reaction)
+
+
+
+
Parameters
+
    +
  • key (str) – the key or label

  • +
  • config (Configuration) – the configuration of the tree search

  • +
  • kwargs (Any) –

  • +
+
+
Return type
+

None

+
+
+
+
+abstract apply(reaction)
+

Apply the filter on the reaction. If the reaction +should be rejected a RejectionException is raised

+
+
Parameters
+

reaction (RetroReaction) – the reaction to filter

+
+
Raises
+

if the reaction should be rejected.

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+class aizynthfinder.context.policy.filter_strategies.QuickKerasFilter(key, config, **kwargs)
+

Bases: aizynthfinder.context.policy.filter_strategies.FilterStrategy

+

Filter quick-filter trained on artificial negative data

+
+
Variables
+
    +
  • use_remote_models – a boolean to connect to remote TensorFlow servers. Defaults +to False.

  • +
  • filter_cutoff – the cut-off value

  • +
+
+
Parameters
+
    +
  • key (str) – the key or label

  • +
  • config (Configuration) – the configuration of the tree search

  • +
  • model – the source of the policy model

  • +
  • kwargs (Any) –

  • +
+
+
Return type
+

None

+
+
+
+
+apply(reaction)
+

Apply the filter on the reaction. If the reaction +should be rejected a RejectionException is raised

+
+
Parameters
+

reaction (RetroReaction) – the reaction to filter

+
+
Raises
+

if the reaction should be rejected.

+
+
Return type
+

None

+
+
+
+ +
+
+feasibility(reaction)
+

Computes if a given reaction is feasible by given +the reaction fingerprint to a network model

+
+
Parameters
+

reaction (RetroReaction) – the reaction to query

+
+
Returns
+

if the reaction is feasible

+
+
Return type
+

Tuple[bool, float]

+
+
+
+ +
+ +
+
+class aizynthfinder.context.policy.filter_strategies.ReactantsCountFilter(key, config, **kwargs)
+

Bases: aizynthfinder.context.policy.filter_strategies.FilterStrategy

+

Check that the number of reactants is was expected from the template

+
+
Parameters
+
    +
  • key (str) – the key or label

  • +
  • config (Configuration) – the configuration of the tree search

  • +
  • kwargs (Any) –

  • +
+
+
Return type
+

None

+
+
+
+
+apply(reaction)
+

Apply the filter on the reaction. If the reaction +should be rejected a RejectionException is raised

+
+
Parameters
+

reaction (RetroReaction) – the reaction to filter

+
+
Raises
+

if the reaction should be rejected.

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

aizynthfinder.context.policy.policies module

+

Module containing classes that interfaces neural network policies

+
+
+class aizynthfinder.context.policy.policies.ExpansionPolicy(config)
+

Bases: aizynthfinder.context.collection.ContextCollection

+

An abstraction of an expansion policy.

+

This policy provides actions that can be applied to a molecule

+
+
Parameters
+

config (Configuration) – the configuration of the tree search

+
+
Return type
+

None

+
+
+
+
+get_actions(molecules, cache_molecules=None)
+

Get all the probable actions of a set of molecules, using the selected policies

+
+
Parameters
+
    +
  • molecules (Sequence[TreeMolecule]) – the molecules to consider

  • +
  • cache_molecules (Sequence[TreeMolecule]) – additional molecules that potentially are sent to +the expansion model but for which predictions are not returned

  • +
+
+
Returns
+

the actions and the priors of those actions

+
+
Raises
+

PolicyException: if the policy isn’t selected

+
+
Return type
+

Tuple[List[RetroReaction], List[float]]

+
+
+
+ +
+
+load(source)
+

Add a pre-initialized expansion strategy object to the policy

+
+
Parameters
+

source (aizynthfinder.context.policy.expansion_strategies.ExpansionStrategy) – the item to add

+
+
Return type
+

None

+
+
+
+ +
+
+load_from_config(**config)
+

Load one or more expansion policy from a configuration

+

The format should be +key:

+
+

type: name of the expansion class or custom_package.custom_model.CustomClass +model: path_to_model +template: path_to_templates +other settings or params

+
+

or +key:

+
+
    +
  • path_to_model

  • +
  • path_to_templates

  • +
+
+
+
Parameters
+

config (Any) – the configuration

+
+
Return type
+

None

+
+
+
+ +
+
+reset_cache()
+

Reset the cache on all loaded policies

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+class aizynthfinder.context.policy.policies.FilterPolicy(config)
+

Bases: aizynthfinder.context.collection.ContextCollection

+

An abstraction of a filter policy.

+

This policy provides a query on a reaction to determine whether it should be rejected

+
+
Parameters
+

config (Configuration) – the configuration of the tree search

+
+
Return type
+

None

+
+
+
+
+apply(reaction)
+

Apply the all the selected filters on the reaction. If the reaction +should be rejected a RejectionException is raised

+
+
Parameters
+

reaction (RetroReaction) – the reaction to filter

+
+
Raises
+

if the reaction should be rejected or if a policy is selected

+
+
Return type
+

None

+
+
+
+ +
+
+load(source)
+

Add a pre-initialized filter strategy object to the policy

+
+
Parameters
+

source (aizynthfinder.context.policy.filter_strategies.FilterStrategy) – the item to add

+
+
Return type
+

None

+
+
+
+ +
+
+load_from_config(**config)
+

Load one or more filter policy from a configuration

+

The format should be +key:

+
+

type: name of the filter class or custom_package.custom_model.CustomClass +model: path_to_model +other settings or params

+
+

or +key: path_to_model

+
+
Parameters
+

config (Any) – the configuration

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

aizynthfinder.context.policy.utils module

+

Module containing helper routines for policies

+
+
+

Module contents

+

Sub-package containing policy routines

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.context.scoring.html b/aizynthfinder.context.scoring.html new file mode 100644 index 0000000..ad9e983 --- /dev/null +++ b/aizynthfinder.context.scoring.html @@ -0,0 +1,736 @@ + + + + + + + + + aizynthfinder.context.scoring package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.context.scoring package

+
+

Submodules

+
+
+

aizynthfinder.context.scoring.collection module

+

Module containing classes used to score the reaction routes.

+
+
+class aizynthfinder.context.scoring.collection.ScorerCollection(config)
+

Bases: aizynthfinder.context.collection.ContextCollection

+

Store scorer objects for the aizynthfinder interface.

+

The scorers can be obtained by name with simple indexing

+
scorers = ScorerCollection()
+scorer = scorers['state score']
+
+
+

Scorers defined in this module and that does not require any +other argument to initialize than the config are auto-loaded.

+
+
Parameters
+

config (Configuration) – the configuration of the tree search

+
+
Return type
+

None

+
+
+
+
+create_default_scorers()
+

Setup the scores that only need the config as their input.

+
+
Return type
+

None

+
+
+
+ +
+
+load(scorer)
+

Add a pre-initialized scorer object to the collection

+
+
Parameters
+

scorer (aizynthfinder.context.scoring.scorers.Scorer) – the item to add

+
+
Return type
+

None

+
+
+
+ +
+
+load_from_config(**scorers_config)
+

Load one or several scorers from a configuration dictionary

+

The keys are the name of scorer class. If a scorer is not +defined in the aizynthfinder.context.scoring module, the module +name can be appended, e.g. mypackage.scoring.AwesomeScore.

+

The values of the configuration is passed directly to the scorer +class along with the config parameter.

+
+
Raises
+

ScorerException – if module or class could not be found

+
+
Parameters
+

scorers_config (Any) –

+
+
Return type
+

None

+
+
+
+ +
+
+make_subset(subset_names)
+

Make a new scorer collection by taking a subset of this +collection. The scorer instances will be shared between +the collections

+
+
Parameters
+

subset_names (List[str]) – the scorers to copy over

+
+
Returns
+

the newly formed collection

+
+
Return type
+

ScorerCollection

+
+
+
+ +
+
+names()
+

Return a list of the names of all the loaded scorers

+
+
Return type
+

List[str]

+
+
+
+ +
+
+objects()
+

Return a list of all the loaded scorer objects

+
+
Return type
+

List[Scorer]

+
+
+
+ +
+
+score_vector(item)
+

For the given item, score it with all selected scorers +and return a vector

+
+
Parameters
+

item (_Scoreable) – the item to be scored

+
+
Returns
+

the vector with the scores

+
+
Return type
+

Sequence[float]

+
+
+
+ +
+
+weighted_score(item, weights)
+

For the given item, score it with all selected scorers +and return a weighted sum of all the scores.

+

If weights is not the same length as the number of scorers +an exception is raised.

+

If no scorers are selected this will raise an exception

+
+
Parameters
+
    +
  • item (_Scoreable) – the item to be scored

  • +
  • weights (Sequence[float]) – the weights of the scorers

  • +
+
+
Returns
+

the weighted sum

+
+
Return type
+

float

+
+
+
+ +
+ +
+
+

aizynthfinder.context.scoring.scorers module

+

Module containing classes used to score the reaction routes.

+
+
+class aizynthfinder.context.scoring.scorers.SquashScaler(slope, xoffset, yoffset)
+

Bases: object

+

Squash function loosely adapted from a sigmoid function with parameters +to modify and offset the shape

+
+
Parameters
+
    +
  • slope (float) – the slope of the midpoint

  • +
  • xoffset (float) – the offset of the midpoint along the x-axis

  • +
  • yoffset (float) – the offset of the curve along the y-axis

  • +
+
+
Return type
+

None

+
+
+
+
+slope: float
+
+ +
+
+xoffset: float
+
+ +
+
+yoffset: float
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.MinMaxScaler(min_val, max_val, reverse, scale_factor=1)
+

Bases: object

+

Scaling function that normalises the value between 0 - 1, +the reverse variable controls the direction of scaling, +reverse should set to be true for rewards that need to be minimised +the scale_factor could be used to adjust the scores when they are too small or too big

+
+
Parameters
+
    +
  • val – the value that is being scaled

  • +
  • min_val (float) – minimum val param val could take

  • +
  • max_val (float) – maximum val param val could take

  • +
  • scale_factor (float) – scaling factor applied to the minmax scaled output

  • +
  • reverse (bool) –

  • +
+
+
Return type
+

None

+
+
+
+
+min_val: float
+
+ +
+
+max_val: float
+
+ +
+
+reverse: bool
+
+ +
+
+scale_factor: float = 1
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.Scorer(config=None, scaler_params=None)
+

Bases: abc.ABC

+

Abstract base class for classes that do scoring on MCTS-like nodes or reaction trees.

+

The actual scoring is done be calling an instance of +a scorer class with a Node or ReactionTree object as only argument.

+
scorer = MyScorer()
+score = scorer(node1)
+
+
+

You can also give a list of such objects to the scorer

+
scorer = MyScorer()
+scores = scorer([node1, node2])
+
+
+
+
Parameters
+
    +
  • config (Optional[Configuration]) – the configuration the tree search

  • +
  • scaler_params (Optional[StrDict]) – the parameter settings of the scaler

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'base'
+
+ +
+
+sort(items)
+

Sort nodes or reaction trees in descending order based on the score

+
+
Parameters
+

items (_Scoreables) – the items to sort

+
+
Returns
+

the sorted items and their scores

+
+
Return type
+

Tuple[_Scoreables, Sequence[float], Sequence[int]]

+
+
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.StateScorer(config, scaler_params=None)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Class for scoring nodes based on the state score

+
+
Parameters
+
    +
  • config (Configuration) –

  • +
  • scaler_params (Optional[StrDict]) –

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'state score'
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.MaxTransformScorerer(config=None, scaler_params=None)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Class for scoring nodes based on the maximum transform

+
+
Parameters
+
    +
  • config (Optional[Configuration]) –

  • +
  • scaler_params (Optional[StrDict]) –

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'max transform'
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.FractionInStockScorer(config, scaler_params=None)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Class for scoring nodes based on the fraction in stock

+
+
Parameters
+
    +
  • config (Configuration) –

  • +
  • scaler_params (Optional[StrDict]) –

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'fraction in stock'
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.NumberOfReactionsScorer(config=None, scaler_params=None)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Class for scoring nodes based on the number of reaction it took to get to a node

+
+
Parameters
+
    +
  • config (Optional[Configuration]) –

  • +
  • scaler_params (Optional[StrDict]) –

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'number of reactions'
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.NumberOfPrecursorsScorer(config=None, scaler_params=None)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Class for scoring nodes based on the number of pre-cursors in a node or route

+
+
Parameters
+
    +
  • config (Optional[Configuration]) –

  • +
  • scaler_params (Optional[StrDict]) –

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'number of pre-cursors'
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.NumberOfPrecursorsInStockScorer(config, scaler_params=None)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Class for scoring nodes based on the number of pre-cursors in stock a +node or route

+
+
Parameters
+
    +
  • config (Configuration) –

  • +
  • scaler_params (Optional[StrDict]) –

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'number of pre-cursors in stock'
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.AverageTemplateOccurrenceScorer(config=None, scaler_params=None)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Class for scoring the nodes based on the average occurrence of the +templates used to get to a node

+
+
Parameters
+
    +
  • config (Optional[Configuration]) –

  • +
  • scaler_params (Optional[StrDict]) –

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'average template occurrence'
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.PriceSumScorer(config, scaler_params=None, default_cost=1.0, not_in_stock_multiplier=10)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Scorer that sums the prices of all pre-cursors

+
+
Parameters
+
    +
  • config (Configuration) –

  • +
  • scaler_params (Optional[StrDict]) –

  • +
  • default_cost (float) –

  • +
  • not_in_stock_multiplier (int) –

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'sum of prices'
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.RouteCostScorer(config, scaler_params=None, reaction_cost=1, average_yield=0.8, default_cost=1, not_in_stock_multiplier=10)
+

Bases: aizynthfinder.context.scoring.scorers.PriceSumScorer

+

Score based on the cost of molecules and reactions. +From Badowski et al. Chem Sci. 2019, 10, 4640

+
+
Parameters
+
    +
  • config (Configuration) –

  • +
  • scaler_params (Optional[StrDict]) –

  • +
  • reaction_cost (int) –

  • +
  • average_yield (float) –

  • +
  • default_cost (int) –

  • +
  • not_in_stock_multiplier (int) –

  • +
+
+
Return type
+

None

+
+
+
+
+scorer_name = 'route cost'
+
+ +
+ +
+
+class aizynthfinder.context.scoring.scorers.ReactionClassMembershipScorer(config, reaction_class_set, in_set_score=1.0, not_in_set_score=0.1)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Scorer that checks if the reaction classes are in a specified set

+

The score is calculated as product over each reaction. For each reaction +the reaction classification is checked if it is in a given list of classes.

+
+
Parameters
+
    +
  • config (Configuration) –

  • +
  • reaction_class_set (Sequence[str]) –

  • +
  • in_set_score (float) –

  • +
  • not_in_set_score (float) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+class aizynthfinder.context.scoring.scorers.StockAvailabilityScorer(config, source_score, default_score=0.1)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Scorer that computes score based on the stock availability of the starting material

+

The score is calculated as a product of a stock score per starting material. The stock +score for each molecule is based on the source of the stock, or a default value if the +molecule is not in stock.

+
+
Parameters
+
    +
  • config (Configuration) –

  • +
  • source_score (StrDict) –

  • +
  • default_score (float) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+class aizynthfinder.context.scoring.scorers.CombinedScorer(config, scorers, weights=None)
+

Bases: aizynthfinder.context.scoring.scorers.Scorer

+

Class for scoring nodes and reaction trees by combining weighted scores from a list of scorers

+

If no weights are provided as input, the scorer provides default weights that are +equal for all input scorers.

+

The CombinedScorer cannot be instantiated from the config file as it requires the +names of the scorers to combine as input.

+
+
Parameters
+
    +
  • config (Configuration) –

  • +
  • scorers (Sequence[str]) –

  • +
  • weights (Optional[Sequence[float]]) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+

Module contents

+

Sub-package containing scoring routines

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.context.stock.html b/aizynthfinder.context.stock.html new file mode 100644 index 0000000..af61bbf --- /dev/null +++ b/aizynthfinder.context.stock.html @@ -0,0 +1,639 @@ + + + + + + + + + aizynthfinder.context.stock package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.context.stock package

+
+

Submodules

+
+
+

aizynthfinder.context.stock.queries module

+

Module containing classes that interfaces different stocks query classes

+
+
+class aizynthfinder.context.stock.queries.StockQueryMixin
+

Bases: object

+

Mixin class for all query classes, providing a default interface +to some methods that might not be possible to implement for each +query class.

+
+
+amount(mol)
+

Returns the maximum amount of the molecule in stock

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – the query molecule

+
+
Raises
+

StockException – if the amount cannot be computed

+
+
Returns
+

the amount

+
+
Return type
+

float

+
+
+
+ +
+
+availability_string(mol)
+

Returns the sources of the molecule

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – the query molecule

+
+
Raises
+

StockException – if the string cannot be computed

+
+
Returns
+

a comma-separated list of sources

+
+
Return type
+

str

+
+
+
+ +
+ +

Finds the entries of the molecule in the stock and cache them +if necessary.

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – the query molecule

+
+
Returns
+

if the molecule is in stock

+
+
Return type
+

bool

+
+
+
+ +
+
+clear_cache()
+

Clear the internal search cache if available

+
+
Return type
+

None

+
+
+
+ +
+
+price(mol)
+

Returns the minimum price of the molecule in stock

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – the query molecule

+
+
Raises
+

StockException – if the price cannot be computed

+
+
Return type
+

float

+
+
+
+ +
+ +
+
+class aizynthfinder.context.stock.queries.InMemoryInchiKeyQuery(path, inchi_key_col='inchi_key', price_col=None)
+

Bases: aizynthfinder.context.stock.queries.StockQueryMixin

+

A stock query class that is based on an in-memory list +of pre-computed inchi-keys.

+
+
This list can be instantiated from
    +
  • A Pandas dataframe in HDF5 or CSV format

  • +
  • A text file with an inchi key on each row

  • +
+
+
+

The dataframe must have a column with InChIkeys that by default is “inchi_key”. +The HDF5 file must have a dataset called “table”.

+

If the source is a dataframe, then optionally it can contain prices and this +columns can be specified with the “price_column” argument.

+
+
Parameters
+
    +
  • path (str) – the path to the file with inchi-keys

  • +
  • inchi_key_col (str) – the name of the column of the InChI keys

  • +
  • path

  • +
  • inchi_key_col

  • +
  • price_col (Optional[str]) –

  • +
+
+
Paramater price_col
+

the name of the column with the optional prices

+
+
Return type
+

None

+
+
+
+
+property stock_inchikeys: Set[str]
+

Return the InChiKeys in this stock

+
+ +
+
+price(mol)
+

Returns the minimum price of the molecule in stock

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – the query molecule

+
+
Raises
+

StockException – if the price cannot be computed

+
+
Return type
+

float

+
+
+
+ +
+ +
+
+class aizynthfinder.context.stock.queries.MongoDbInchiKeyQuery(host=None, database='stock_db', collection='molecules')
+

Bases: aizynthfinder.context.stock.queries.StockQueryMixin

+

A stock query class that is looking up inchi keys in a Mongo database.

+
+
The documents in the database collection should have at least 2 fields:
    +
  • inchi_key: the inchi key of the molecule

  • +
  • source: the original source of the molecule

  • +
+
+
+
+
Variables
+
    +
  • client – the Mongo client

  • +
  • database – the database instance

  • +
  • molecules – the collection of documents

  • +
+
+
Parameters
+
    +
  • host (Optional[str]) – the database host, defaults to None

  • +
  • database (str) – the database name, defaults to “stock_db”

  • +
  • collection (str) – the database collection, defaults to “molecules”

  • +
  • host

  • +
  • database

  • +
  • collection

  • +
+
+
Return type
+

None

+
+
+
+
+availability_string(mol)
+

Returns the sources of the molecule

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – the query molecule

+
+
Raises
+

StockException – if the string cannot be computed

+
+
Returns
+

a comma-separated list of sources

+
+
Return type
+

str

+
+
+
+ +
+ +
+
+class aizynthfinder.context.stock.queries.MolbloomFilterQuery(path, smiles_based=False)
+

Bases: aizynthfinder.context.stock.queries.StockQueryMixin

+

A stock query class that is based on an a molbloom filter +for SMILES strings or InChI keys

+
+
Parameters
+
    +
  • path (str) – the path to the saved bloom filter

  • +
  • smiles_based (bool) – if True will use SMILES for lookup instead of InChI keys

  • +
  • path

  • +
  • smiles_based

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+

aizynthfinder.context.stock.stock module

+

Module containing classes that interfaces different stock classes

+
+
+class aizynthfinder.context.stock.stock.Stock
+

Bases: aizynthfinder.context.collection.ContextCollection

+

A collection of molecules that are in stock

+

A molecule can be queried on the stock with:

+
my_mol = Molecule(smiles="CCO")
+my_mol in stock
+
+
+

One can obtain individual stocks with:

+
sub_stock = stock["key"]
+
+
+

One can obtain the number of molecules in the selected stock with:

+
number_of_molecules = len(stock)
+
+
+
+
Return type
+

None

+
+
+
+
+property stop_criteria: dict
+

Return a copy of the stop criteria used by the stock

+
+ +
+
+amount(mol)
+

Calculate the maximum amount of a molecule in stock

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – the molecule to query

+
+
Raises
+

StockException – if the amount could not be computed

+
+
Returns
+

the maximum amount

+
+
Return type
+

float

+
+
+
+ +
+
+availability_list(mol)
+

Return a list of what stocks a given mol is available

+

If the molecule is not in stock it will return any empty list

+
+
Parameters
+

mol (Molecule) – The molecule to query

+
+
Returns
+

string with a list of stocks that mol was found in

+
+
Return type
+

List[str]

+
+
+
+ +
+
+availability_string(mol)
+

Return a string of what stocks a given mol is available

+

If the molecule is not in stock it will return “Not in stock”

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – The molecule to query

+
+
Returns
+

string with a list of stocks that mol was found in

+
+
Return type
+

str

+
+
+
+ +
+
+exclude(mol)
+

Exclude a molecule from the stock. +When this molecule is queried it will return False, +regardless if the molecule is in the stock.

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – the molecule to exclude

+
+
Return type
+

None

+
+
+
+ +
+
+load(source, key)
+

Add a pre-initialized stock query object to the stock

+
+
Parameters
+
+
+
Return type
+

None

+
+
+
+ +
+
+load_from_config(**config)
+

Load one or more stock queries from a configuration

+

The key can be “stop_criteria” in case the config is given to the +set_stop_criteria method

+

The format should be +key:

+
+

type: name of the stock class or custom_package.custom_model.CustomClass +path: path to the stock file +other settings or params

+
+

or +key: path_to_model

+
+
Parameters
+

config (Any) – the configuration

+
+
Return type
+

None

+
+
+
+ +
+
+price(mol)
+

Calculate the minimum price of a molecule in stock

+
+
Parameters
+

mol (aizynthfinder.chem.mol.Molecule) – the molecule to query

+
+
Raises
+

StockException – if the price could not be computed

+
+
Returns
+

the minimum price

+
+
Return type
+

float

+
+
+
+ +
+
+reset_exclusion_list()
+

Remove all molecules in the exclusion list

+
+
Return type
+

None

+
+
+
+ +
+
+select(value, append=False)
+

Select one or more stock queries

+
+
Parameters
+
    +
  • value (Union[str, List[str]]) – the key of the stocks to select

  • +
  • append (bool) – if True and value is a single key append it to the current selection

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+set_stop_criteria(criteria=None)
+

Set criteria that stop the search

+

The keys of the criteria can be “price” or “amount” which accepts numerical settings, +or “counts” that should be dictionary of maximum allowed count for each atomic symbol.

+

Example:

+
criteria = {
+    "price": 5,
+    "amount": 100,
+    "counts": {
+        "C": 6,
+        "O": 4
+    }
+}
+
+
+
+
Parameters
+

criteria (Optional[Dict]) – the criteria settings

+
+
Return type
+

None

+
+
+
+ +
+
+smiles_in_stock(smiles)
+

Check if the SMILES is in the currently selected stocks

+
+
Parameters
+

smiles (str) – SMILES string (must be RDKit sanitizable)

+
+
Returns
+

if the SMILES was on stock

+
+
Return type
+

bool

+
+
+
+ +
+ +
+
+

Module contents

+

Sub-package containing stock routines

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.html b/aizynthfinder.html new file mode 100644 index 0000000..05ba6d1 --- /dev/null +++ b/aizynthfinder.html @@ -0,0 +1,766 @@ + + + + + + + + + aizynthfinder package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder package

+
+

Subpackages

+
+ +
+
+
+

Submodules

+
+
+

aizynthfinder.aizynthfinder module

+

Module containing a class that is the main interface the retrosynthesis tool.

+
+
+class aizynthfinder.aizynthfinder.AiZynthFinder(configfile=None, configdict=None)
+

Bases: object

+

Public API to the aizynthfinder tool

+

If instantiated with the path to a yaml file or dictionary of settings +the stocks and policy networks are loaded directly. +Otherwise, the user is responsible for loading them prior to +executing the tree search.

+
+
Variables
+
    +
  • config – the configuration of the search

  • +
  • expansion_policy – the expansion policy model

  • +
  • filter_policy – the filter policy model

  • +
  • stock – the stock

  • +
  • scorers – the loaded scores

  • +
  • tree – the search tree

  • +
  • analysis – the tree analysis

  • +
  • routes – the top-ranked routes

  • +
  • search_stats – statistics of the latest search

  • +
+
+
Parameters
+
    +
  • configfile (Optional[str]) – the path to yaml file with configuration (has priority over configdict), defaults to None

  • +
  • configdict (Optional[StrDict]) – the config as a dictionary source, defaults to None

  • +
+
+
Return type
+

None

+
+
+
+
+property target_smiles: str
+

The SMILES representation of the molecule to predict routes on.

+
+ +
+
+property target_mol: Optional[Molecule]
+

The molecule to predict routes on

+
+ +
+
+build_routes(selection=None, scorer=None)
+

Build reaction routes

+

This is necessary to call after the tree search has completed in order +to extract results from the tree search.

+
+
Parameters
+
    +
  • selection (Optional[RouteSelectionArguments]) – the selection criteria for the routes

  • +
  • scorer (Optional[str]) – a reference to the object used to score the nodes

  • +
+
+
Raises
+

ValueError – if the search tree not initialized

+
+
Return type
+

None

+
+
+
+ +
+
+extract_statistics()
+

Extracts tree statistics as a dictionary

+
+
Return type
+

StrDict

+
+
+
+ +
+
+prepare_tree()
+

Setup the tree for searching

+
+
Raises
+

ValueError – if the target molecule was not set

+
+
Return type
+

None

+
+
+
+ +
+
+stock_info()
+

Return the stock availability for all leaf nodes in all collected reaction trees

+

The key of the return dictionary will be the SMILES string of the leaves, +and the value will be the stock availability

+
+
Returns
+

the collected stock information.

+
+
Return type
+

StrDict

+
+
+
+ +
+ +

Perform the actual tree search

+
+
Parameters
+

show_progress (bool) – if True, shows a progress bar

+
+
Returns
+

the time past in seconds

+
+
Return type
+

float

+
+
+
+ +
+ +
+
+class aizynthfinder.aizynthfinder.AiZynthExpander(configfile=None, configdict=None)
+

Bases: object

+

Public API to the AiZynthFinder expansion and filter policies

+

If instantiated with the path to a yaml file or dictionary of settings +the stocks and policy networks are loaded directly. +Otherwise, the user is responsible for loading them prior to +executing the tree search.

+
+
Variables
+
    +
  • config – the configuration of the search

  • +
  • expansion_policy – the expansion policy model

  • +
  • filter_policy – the filter policy model

  • +
+
+
Parameters
+
    +
  • configfile (Optional[str]) – the path to yaml file with configuration (has priority over configdict), defaults to None

  • +
  • configdict (Optional[StrDict]) – the config as a dictionary source, defaults to None

  • +
+
+
Return type
+

None

+
+
+
+
+do_expansion(smiles, return_n=5, filter_func=None)
+

Do the expansion of the given molecule returning a list of +reaction tuples. Each tuple in the list contains reactions +producing the same reactants. Hence, nested structure of the +return value is way to group reactions.

+

If filter policy is setup, the probability of the reactions are +added as metadata to the reaction.

+

The additional filter functions makes it possible to do customized +filtering. The callable should take as only argument a RetroReaction +object and return True if the reaction can be kept or False if it should +be removed.

+
+
Parameters
+
    +
  • smiles (str) – the SMILES string of the target molecule

  • +
  • return_n (int) – the length of the return list

  • +
  • filter_func (Optional[Callable[[RetroReaction], bool]]) – an additional filter function

  • +
+
+
Returns
+

the grouped reactions

+
+
Return type
+

List[Tuple[FixedRetroReaction, …]]

+
+
+
+ +
+ +
+
+

aizynthfinder.reactiontree module

+

Module containing the implementation of a reaction tree or route and factory classes to make such trees

+
+
+class aizynthfinder.reactiontree.ReactionTree
+

Bases: object

+

Encapsulation of a bipartite reaction tree of a single route. +The nodes consists of either FixedRetroReaction or UniqueMolecule objects.

+

The reaction tree is initialized at instantiation and is not supposed to +be updated.

+
+
Variables
+
    +
  • graph – the bipartite graph

  • +
  • is_solved – if all of the leaf nodes are in stock

  • +
  • root – the root of the tree

  • +
  • created_at_iteration – iteration the reaction tree was created

  • +
+
+
Return type
+

None

+
+
+
+
+classmethod from_dict(tree_dict)
+

Create a new ReactionTree by parsing a dictionary.

+

This is supposed to be the opposite of to_dict, +but because that format loses information, the returned +object is not a full copy as the stock will only contain +the list of molecules marked as in_stock in the dictionary.

+

The returned object should be sufficient to e.g. generate an image of the route.

+
+
Parameters
+

tree_dict (StrDict) – the dictionary representation

+
+
Returns
+

the reaction tree

+
+
Return type
+

ReactionTree

+
+
+
+ +
+
+property metadata: StrDict
+

Return a dicitionary with route metadata

+
+ +
+
+depth(node)
+

Return the depth of a node in the route

+
+
Parameters
+

node (Union[UniqueMolecule, FixedRetroReaction]) – the query node

+
+
Returns
+

the depth

+
+
Return type
+

int

+
+
+
+ +
+
+distance_to(other, content='both')
+

Calculate the distance to another reaction tree

+

This is a tree edit distance, with unit cost to +insert and deleted nodes, and the Jaccard distance for substituting nodes

+
+
Parameters
+
+
+
Returns
+

the distance between the routes

+
+
Return type
+

float

+
+
+
+ +
+
+hash_key()
+

Calculates a hash code for the tree using the sha224 hash function recursively

+
+
Returns
+

the hash key

+
+
Return type
+

str

+
+
+
+ +
+
+in_stock(node)
+

Return if a node in the route is in stock

+

Note that is a property set on creation and as such is not updated.

+
+
Parameters
+

node (Union[UniqueMolecule, FixedRetroReaction]) – the query node

+
+
Returns
+

if the molecule is in stock

+
+
Return type
+

bool

+
+
+
+ +
+
+is_branched()
+

Returns if the route is branched

+

i.e. checks if the maximum depth is not equal to the number +of reactions.

+
+
Return type
+

bool

+
+
+
+ +
+
+leafs()
+

Generates the molecules nodes of the reaction tree that has no predecessors, +i.e. molecules that has not been broken down

+
+
Yield
+

the next leaf molecule in the tree

+
+
Return type
+

Iterable[UniqueMolecule]

+
+
+
+ +
+
+molecules()
+

Generates the molecule nodes of the reaction tree

+
+
Yield
+

the next molecule in the tree

+
+
Return type
+

Iterable[UniqueMolecule]

+
+
+
+ +
+
+reactions()
+

Generates the reaction nodes of the reaction tree

+
+
Yield
+

the next reaction in the tree

+
+
Return type
+

Iterable[FixedRetroReaction]

+
+
+
+ +
+
+subtrees()
+

Generates the subtrees of this reaction tree a +subtree is a reaction treee starting at a molecule node that has children.

+
+
Yield
+

the next subtree

+
+
Return type
+

Iterable[ReactionTree]

+
+
+
+ +
+
+to_dict(include_metadata=False)
+

Returns the reaction tree as a dictionary in a pre-defined format. +:param include_metadata: if True include metadata +:return: the reaction tree

+
+
Return type
+

StrDict

+
+
+
+ +
+
+to_image(in_stock_colors=None, show_all=True)
+

Return a pictorial representation of the route

+
+
Parameters
+
    +
  • in_stock_colors (Optional[FrameColors]) – the colors around molecules, defaults to {True: “green”, False: “orange”}

  • +
  • show_all (bool) – if True, also show nodes that are marked as hidden

  • +
+
+
Returns
+

the image of the route

+
+
Return type
+

PilImage

+
+
+
+ +
+
+to_json(include_metadata=False)
+

Returns the reaction tree as a JSON string in a pre-defined format.

+
+
Returns
+

the reaction tree

+
+
Return type
+

str

+
+
+
+ +
+ +
+
+class aizynthfinder.reactiontree.ReactionTreeLoader(*args, **kwargs)
+

Bases: abc.ABC

+

Base class for classes that creates a reaction tree object

+

This class makes sure that node attributes are set after the +graph is generated, and provides utility methods.

+
+
Parameters
+
    +
  • args (Any) –

  • +
  • kwargs (Any) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+class aizynthfinder.reactiontree.ReactionTreeFromDict(*args, **kwargs)
+

Bases: aizynthfinder.reactiontree.ReactionTreeLoader

+

Creates a reaction tree object from a dictionary

+
+
Parameters
+
    +
  • args (Any) –

  • +
  • kwargs (Any) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+class aizynthfinder.reactiontree.ReactionTreeFromExpansion(*args, **kwargs)
+

Bases: aizynthfinder.reactiontree.ReactionTreeLoader

+

Create a ReactionTree from a single reaction

+

This is mainly intended as a convenience function for the expander interface

+
+
Parameters
+
    +
  • args (Any) –

  • +
  • kwargs (Any) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+

Module contents

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.interfaces.gui.html b/aizynthfinder.interfaces.gui.html new file mode 100644 index 0000000..4a8bba8 --- /dev/null +++ b/aizynthfinder.interfaces.gui.html @@ -0,0 +1,196 @@ + + + + + + + + + aizynthfinder.interfaces.gui package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.interfaces.gui package

+
+

Submodules

+
+
+

aizynthfinder.interfaces.gui.clustering module

+

Module containing a GUI extension for clustering

+
+
+class aizynthfinder.interfaces.gui.clustering.ClusteringGui(routes, content='both')
+

Bases: object

+

GUI extension to cluster routes

+
+
Parameters
+
    +
  • routes (RouteCollection) – the routes to cluster

  • +
  • content (str) – what to cluster on

  • +
+
+
+
+
+classmethod from_app(app, content='both')
+

Helper function to create a GUI from a GUI app interface

+
+
Parameters
+
    +
  • app (AiZynthApp) – the app to extract the routes from

  • +
  • content (str) – what to cluster on

  • +
+
+
Returns
+

the GUI object

+
+
+
+ +
+ +
+
+

Module contents

+

Package for GUI extensions

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.interfaces.html b/aizynthfinder.interfaces.html new file mode 100644 index 0000000..e96a786 --- /dev/null +++ b/aizynthfinder.interfaces.html @@ -0,0 +1,237 @@ + + + + + + + + + aizynthfinder.interfaces package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.interfaces package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

aizynthfinder.interfaces.aizynthapp module

+

Module containing classes and routines for the GUI interface

+
+
+class aizynthfinder.interfaces.aizynthapp.AiZynthApp(configfile, setup=True)
+

Bases: object

+

Interface class to be used in a Jupyter Notebook. +Provides a basic GUI to setup and analyze the tree search.

+

Should be instantiated with the path of a yaml file with configuration:

+
from aizynthfinder.interfaces import AiZynthApp
+configfile = "/path/to/configfile.yaml"
+app = AiZynthApp(configfile)
+
+
+
+
Variables
+

finder – the finder instance

+
+
Parameters
+
    +
  • configfile (str) – the path to yaml file with configuration

  • +
  • setup (bool) – if True will create and display the GUI on instantiation, defaults to True

  • +
+
+
Return type
+

None

+
+
+
+
+setup()
+

Create the widgets and display the GUI. +This is typically done on instantiation, but this method +is for more advanced uses.

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+aizynthfinder.interfaces.aizynthapp.main()
+

Entry point for the aizynthapp command

+
+
Return type
+

None

+
+
+
+ +
+
+

aizynthfinder.interfaces.aizynthcli module

+

Module containing classes and routines for the CLI

+
+
+aizynthfinder.interfaces.aizynthcli.main()
+

Entry point for the aizynthcli command

+
+
Return type
+

None

+
+
+
+ +
+
+

Module contents

+

Module for interfaces to the AiZynthFinder application

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.search.breadth_first.html b/aizynthfinder.search.breadth_first.html new file mode 100644 index 0000000..6fb6f00 --- /dev/null +++ b/aizynthfinder.search.breadth_first.html @@ -0,0 +1,494 @@ + + + + + + + + + aizynthfinder.search.breadth_first package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.search.breadth_first package

+
+

Submodules

+
+
+

aizynthfinder.search.breadth_first.nodes module

+

Module containing a classes representation various tree nodes

+
+
+class aizynthfinder.search.breadth_first.nodes.MoleculeNode(mol, config, parent=None)
+

Bases: aizynthfinder.search.andor_trees.TreeNodeMixin

+

An OR node representing a molecule

+
+
Variables
+
    +
  • expandable – if True, this node is part of the frontier

  • +
  • mol – the molecule represented by the node

  • +
  • in_stock – if True the molecule is in stock and hence should not be expanded

  • +
  • parent – the parent of the node

  • +
+
+
Parameters
+
    +
  • mol (TreeMolecule) – the molecule to be represented by the node

  • +
  • config (Configuration) – the configuration of the search

  • +
  • parent (Optional[ReactionNode]) – the parent of the node, optional

  • +
+
+
Return type
+

None

+
+
+
+
+classmethod create_root(smiles, config)
+

Create a root node for a tree using a SMILES.

+
+
Parameters
+
    +
  • smiles (str) – the SMILES representation of the root state

  • +
  • config (Configuration) – settings of the tree search algorithm

  • +
+
+
Returns
+

the created node

+
+
Return type
+

MoleculeNode

+
+
+
+ +
+
+classmethod from_dict(dict_, config, molecules, parent=None)
+

Create a new node from a dictionary, i.e. deserialization

+
+
Parameters
+
    +
  • dict – the serialized node

  • +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • molecules (MoleculeDeserializer) – the deserialized molecules

  • +
  • parent (Optional[ReactionNode]) – the parent node

  • +
  • dict_ (StrDict) –

  • +
+
+
Returns
+

a deserialized node

+
+
Return type
+

MoleculeNode

+
+
+
+ +
+
+property children: List[ReactionNode]
+

Gives the reaction children nodes

+
+ +
+
+property prop: StrDict
+

Dictionary with publicly exposed properties

+
+ +
+
+add_stub(reaction)
+

Add a stub / sub-tree to this node

+
+
Parameters
+

reaction (RetroReaction) – the reaction creating the stub

+
+
Returns
+

list of all newly added molecular nodes

+
+
Return type
+

Sequence[MoleculeNode]

+
+
+
+ +
+
+ancestors()
+

Return the ancestors of this node

+
+
Returns
+

the ancestors

+
+
Return type
+

set

+
+
+
+ +
+
+serialize(molecule_store)
+

Serialize the node object to a dictionary

+
+
Parameters
+

molecule_store (MoleculeSerializer) – the serialized molecules

+
+
Returns
+

the serialized node

+
+
Return type
+

StrDict

+
+
+
+ +
+ +
+
+class aizynthfinder.search.breadth_first.nodes.ReactionNode(reaction, parent)
+

Bases: aizynthfinder.search.andor_trees.TreeNodeMixin

+

An AND node representing a reaction

+
+
Variables
+
    +
  • parent – the parent of the node

  • +
  • reaction – the reaction represented by the node

  • +
+
+
Parameters
+
    +
  • cost – the cost of the reaction

  • +
  • reaction (RetroReaction) – the reaction to be represented by the node

  • +
  • parent (MoleculeNode) – the parent of the node

  • +
+
+
Return type
+

None

+
+
+
+
+classmethod create_stub(reaction, parent, config)
+

Create a ReactionNode and creates all the MoleculeNode objects +that are the children of the node.

+
+
Parameters
+
    +
  • reaction (RetroReaction) – the reaction to be represented by the node

  • +
  • parent (MoleculeNode) – the parent of the node

  • +
  • config (Configuration) – the configuration of the search tree

  • +
+
+
Return type
+

ReactionNode

+
+
+
+ +
+
+classmethod from_dict(dict_, config, molecules, parent)
+

Create a new node from a dictionary, i.e. deserialization

+
+
Parameters
+
    +
  • dict – the serialized node

  • +
  • config (Configuration) – the configuration of the tree search

  • +
  • molecules (MoleculeDeserializer) – the deserialized molecules

  • +
  • parent (MoleculeNode) – the parent node

  • +
  • dict_ (StrDict) –

  • +
+
+
Returns
+

a deserialized node

+
+
Return type
+

ReactionNode

+
+
+
+ +
+
+property children: List[MoleculeNode]
+

Gives the molecule children nodes

+
+ +
+
+property prop: StrDict
+

Dictionary with publicly exposed properties

+
+ +
+
+serialize(molecule_store)
+

Serialize the node object to a dictionary

+
+
Parameters
+

molecule_store (MoleculeSerializer) – the serialized molecules

+
+
Returns
+

the serialized node

+
+
Return type
+

StrDict

+
+
+
+ +
+ +
+
+

aizynthfinder.search.breadth_first.search_tree module

+

Module containing a class that holds the tree search

+
+
+class aizynthfinder.search.breadth_first.search_tree.SearchTree(config, root_smiles=None)
+

Bases: aizynthfinder.search.andor_trees.AndOrSearchTreeBase

+

Encapsulation of the a breadth-first exhaustive search algorithm

+
+
Variables
+
    +
  • config – settings of the tree search algorithm

  • +
  • root – the root node

  • +
+
+
Parameters
+
    +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • root_smiles (Optional[str]) – the root will be set to a node representing this molecule, defaults to None

  • +
+
+
Return type
+

None

+
+
+
+
+classmethod from_json(filename, config)
+

Create a new search tree by deserialization from a JSON file

+
+
Parameters
+
    +
  • filename (str) – the path to the JSON node

  • +
  • config (Configuration) – the configuration of the search tree

  • +
+
+
Returns
+

a deserialized tree

+
+
Return type
+

SearchTree

+
+
+
+ +
+
+property mol_nodes: Sequence[MoleculeNode]
+

Return the molecule nodes of the tree

+
+ +
+
+one_iteration()
+

Perform one iteration expansion. +Expands all expandable molecule nodes in the tree, which should be +on the same depth of the tree.

+
+
Raises
+

StopIteration – if the search should be pre-maturely terminated

+
+
Returns
+

if a solution was found

+
+
Return type
+

bool

+
+
+
+ +
+
+routes()
+

Extracts and returns routes from the AND/OR tree

+
+
Returns
+

the routes

+
+
Return type
+

List[ReactionTree]

+
+
+
+ +
+
+serialize(filename)
+

Seralize the search tree to a JSON file

+
+
Parameters
+

filename (str) – the path to the JSON file

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

Module contents

+

Sub-package containing breadth first routines

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.search.dfpn.html b/aizynthfinder.search.dfpn.html new file mode 100644 index 0000000..cd4d219 --- /dev/null +++ b/aizynthfinder.search.dfpn.html @@ -0,0 +1,425 @@ + + + + + + + + + aizynthfinder.search.dfpn package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.search.dfpn package

+
+

Submodules

+
+
+

aizynthfinder.search.dfpn.nodes module

+

Module containing a classes representation various tree nodes

+
+
+class aizynthfinder.search.dfpn.nodes.MoleculeNode(mol, config, owner, parent=None)
+

Bases: aizynthfinder.search.dfpn.nodes._SuperNode

+

An OR node representing a molecule

+
+
Variables
+
    +
  • expandable – if True, this node is part of the frontier

  • +
  • mol – the molecule represented by the node

  • +
  • in_stock – if True the molecule is in stock and hence should not be expanded

  • +
  • parent – the parent of the node

  • +
  • pn – the proof number

  • +
  • dn – the disproof number

  • +
  • pn_threshold – the threshold for proof number

  • +
  • dn_threshold – the threshold for disproof number

  • +
+
+
Parameters
+
+
+
Return type
+

None

+
+
+
+
+classmethod create_root(smiles, config, owner)
+

Create a root node for a tree using a SMILES.

+
+
Parameters
+
    +
  • smiles (str) – the SMILES representation of the root state

  • +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • owner (SearchTree) –

  • +
+
+
Returns
+

the created node

+
+
Return type
+

MoleculeNode

+
+
+
+ +
+
+property prop: StrDict
+

Dictionary with publicly exposed properties

+
+ +
+
+expand()
+

Expand the molecule by utilising an expansion policy

+
+
Return type
+

None

+
+
+
+ +
+
+promising_child()
+

Find and return the most promising child for exploration +Updates the thresholds on that child

+
+
Return type
+

Optional[ReactionNode]

+
+
+
+ +
+
+update()
+

Update the proof and disproof numbers

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+class aizynthfinder.search.dfpn.nodes.ReactionNode(reaction, config, owner, parent)
+

Bases: aizynthfinder.search.dfpn.nodes._SuperNode

+

An AND node representing a reaction

+
+
Variables
+
    +
  • parent – the parent of the node

  • +
  • reaction – the reaction represented by the node

  • +
  • pn – the proof number

  • +
  • dn – the disproof number

  • +
  • pn_threshold – the threshold for proof number

  • +
  • dn_threshold – the threshold for disproof number

  • +
  • expandable – if the node is expandable

  • +
+
+
Parameters
+
+
+
Return type
+

None

+
+
+
+
+property children: List[MoleculeNode]
+

Gives the molecule children nodes

+
+ +
+
+property prop: StrDict
+

Dictionary with publicly exposed properties

+
+ +
+
+property proven: bool
+

Return if the node is proven

+
+ +
+
+property disproven: bool
+

Return if the node is disproven

+
+ +
+
+expand()
+

Expand the node by creating nodes for each reactant

+
+
Return type
+

None

+
+
+
+ +
+
+promising_child()
+

Find and return the most promising child for exploration +Updates the thresholds on that child

+
+
Return type
+

Optional[MoleculeNode]

+
+
+
+ +
+
+update()
+

Update the proof and disproof numbers

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

aizynthfinder.search.dfpn.search_tree module

+

Module containing a class that holds the tree search

+
+
+class aizynthfinder.search.dfpn.search_tree.SearchTree(config, root_smiles=None)
+

Bases: aizynthfinder.search.andor_trees.AndOrSearchTreeBase

+

Encapsulation of the Depth-First Proof-Number (DFPN) search algorithm.

+
+
This algorithm does not support:
    +
  1. Filter policy

  2. +
  3. Serialization and deserialization

  4. +
+
+
+
+
Variables
+
    +
  • config – settings of the tree search algorithm

  • +
  • root – the root node

  • +
+
+
Parameters
+
    +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • root_smiles (Optional[str]) – the root will be set to a node representing this molecule, defaults to None

  • +
+
+
Return type
+

None

+
+
+
+
+property mol_nodes: Sequence[MoleculeNode]
+

Return the molecule nodes of the tree

+
+ +
+
+one_iteration()
+

Perform one iteration of expansion.

+

If possible expand the frontier node twice, i.e. expanding an OR +node and then and AND node. If frontier not expandable step up in the +tree and find a new frontier to expand.

+

If a solution is found, mask that tree for exploration and start over.

+
+
Raises
+

StopIteration – if the search should be pre-maturely terminated

+
+
Returns
+

if a solution was found

+
+
Return type
+

bool

+
+
+
+ +
+
+routes()
+

Extracts and returns routes from the AND/OR tree

+
+
Returns
+

the routes

+
+
Return type
+

List[ReactionTree]

+
+
+
+ +
+ +
+
+

Module contents

+

Sub-package containing DFPN routines

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.search.html b/aizynthfinder.search.html new file mode 100644 index 0000000..9ad2322 --- /dev/null +++ b/aizynthfinder.search.html @@ -0,0 +1,304 @@ + + + + + + + + + aizynthfinder.search package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.search package

+
+

Subpackages

+ +
+
+

Submodules

+
+
+

aizynthfinder.search.andor_trees module

+

Module for base classes for AND/OR trees and some tree utility code

+
+
+class aizynthfinder.search.andor_trees.TreeNodeMixin
+

Bases: object

+

A mixin class for node in a tree

+
+
+property prop: StrDict
+

Dictionary with publicly exposed properties

+
+ +
+
+property children: List['TreeNodeMixin']
+

List of children nodes

+
+ +
+ +
+
+class aizynthfinder.search.andor_trees.AndOrSearchTreeBase(config, root_smiles=None)
+

Bases: abc.ABC

+

A base class for a search tree based on an AND/OR structure

+
+
Parameters
+
+
+
Return type
+

None

+
+
+
+
+property mol_nodes: List[TreeNodeMixin]
+

Return the molecule nodes of the tree

+
+ +
+
+abstract one_iteration()
+

Perform one iteration of the search

+
+
Return type
+

bool

+
+
+
+ +
+
+abstract routes()
+

Return the routes of the tree

+
+
Return type
+

List[ReactionTree]

+
+
+
+ +
+ +
+
+class aizynthfinder.search.andor_trees.SplitAndOrTree(root_node, stock, max_routes=25000)
+

Bases: object

+

Encapsulation of an algorithm to split an AND/OR tree into separate routes

+

This is a modified algorithm of the one detailed in the CompRet paper: +Shibukawa et al. (2020) J. Cheminf. 12, 52

+

This implementation sets an upper-bound on the number of extracted routes +to avoid combinatorial explosion.

+

The routes are extracted on instantiation and the routes can be access from +the routes attribute.

+
+
Parameters
+
    +
  • root_node (TreeNodeMixin) – the root of the AND/OR tree

  • +
  • stock (Stock) – the stock of the search

  • +
  • max_routes (int) – the maximum number of routes to extract

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+class aizynthfinder.search.andor_trees.ReactionTreeFromAndOrTrace(*args, **kwargs)
+

Bases: aizynthfinder.reactiontree.ReactionTreeLoader

+

Creates a reaction tree object from an AND/OR Trace

+
+
Parameters
+
    +
  • args (Any) –

  • +
  • kwargs (Any) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+

Module contents

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.search.mcts.html b/aizynthfinder.search.mcts.html new file mode 100644 index 0000000..40e06f9 --- /dev/null +++ b/aizynthfinder.search.mcts.html @@ -0,0 +1,720 @@ + + + + + + + + + aizynthfinder.search.mcts package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.search.mcts package

+
+

Submodules

+
+
+

aizynthfinder.search.mcts.node module

+

Module containing a class that represents a node in the search tree.

+
+
+class aizynthfinder.search.mcts.node.MctsNode(state, owner, config, parent=None)
+

Bases: object

+

A node in the search tree.

+

The children are instantiated lazily for efficiency: only when +a child is selected the reaction to create that child is applied.

+

Properties of an instantiated children to a node can be access with:

+
children_attr = node[child]
+
+
+

the return value is a dictionary with keys “action”, “value”, “prior” +and “visitations”.

+
+
Variables
+
    +
  • is_expanded – if the node has had children added to it

  • +
  • is_expandable – if the node is expandable

  • +
  • tree – the tree owning this node

  • +
+
+
Parameters
+
    +
  • state (MctsState) – the state of the node

  • +
  • owner (MctsSearchTree) – the tree that owns this node

  • +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • parent (Optional[MctsNode]) – the parent node, defaults to None

  • +
+
+
+
+
+classmethod create_root(smiles, tree, config)
+

Create a root node for a tree using a SMILES.

+
+
Parameters
+
    +
  • smiles (str) – the SMILES representation of the root state

  • +
  • tree (MctsSearchTree) – the search tree

  • +
  • config (Configuration) – settings of the tree search algorithm

  • +
+
+
Returns
+

the created node

+
+
Return type
+

MctsNode

+
+
+
+ +
+
+classmethod from_dict(dict_, tree, config, molecules, parent=None)
+

Create a new node from a dictionary, i.e. deserialization

+
+
Parameters
+
    +
  • dict – the serialized node

  • +
  • tree (MctsSearchTree) – the search tree

  • +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • molecules (MoleculeDeserializer) – the deserialized molecules

  • +
  • parent (Optional['MctsNode']) – the parent node

  • +
  • dict_ (StrDict) –

  • +
+
+
Returns
+

a deserialized node

+
+
Return type
+

MctsNode

+
+
+
+ +
+
+property children: List['MctsNode']
+

Returns all of the instantiated children

+
+
Returns
+

the children

+
+
+
+ +
+
+property is_solved: bool
+

Return if the state is solved

+
+ +
+
+property parent: Optional['MctsNode']
+

Return the parent of the node

+
+ +
+
+property state: aizynthfinder.search.mcts.state.MctsState
+

Return the underlying state of the node

+
+ +
+
+actions_to()
+

Returns the actions leading to this node

+
+
Returns
+

the list of actions

+
+
Return type
+

List[RetroReaction]

+
+
+
+ +
+
+backpropagate(child, value_estimate)
+

Update the number of visitations of a particular child and its value.

+
+
Parameters
+
+
+
Return type
+

None

+
+
+
+ +
+
+children_view()
+

Creates a view of the children attributes. Each of the +list returned is a new list, although the actual children +are not copied.

+

The return dictionary will have keys “actions”, “values”, +“priors”, “visitations” and “objects”.

+
+
Returns
+

the view

+
+
Return type
+

StrDict

+
+
+
+ +
+
+expand()
+

Expand the node.

+

Expansion is the process of creating the children of the node, +without instantiating a child object. The actions and priors are +taken from the policy network.

+

If immediate instantiation is marked for some policies, however, the +children nodes will be instantiated.

+
+
Return type
+

None

+
+
+
+ +
+
+is_terminal()
+

Node is terminal if its unexpandable, or the internal state is terminal (solved)

+
+
Returns
+

the terminal attribute of the node

+
+
Return type
+

bool

+
+
+
+ +
+
+path_to()
+

Return the path to this node, which is a list of actions and a list of node

+
+
Returns
+

the actions and nodes

+
+
Return type
+

Tuple[List[RetroReaction], List[MctsNode]]

+
+
+
+ +
+
+promising_child()
+

Return the child with the currently highest Q+U

+

The selected child will be instantiated if it has not been already.

+

If no actions could be found that were applicable, the method will +return None.

+
+
Returns
+

the child

+
+
Return type
+

Optional[‘MctsNode’]

+
+
+
+ +
+
+serialize(molecule_store)
+

Serialize the node object to a dictionary

+
+
Parameters
+

molecule_store (MoleculeSerializer) – the serialized molecules

+
+
Returns
+

the serialized node

+
+
Return type
+

StrDict

+
+
+
+ +
+
+to_reaction_tree()
+

Return reaction tree from the path of actions and nodes leading to this node

+
+
Returns
+

the constructed tree

+
+
Return type
+

ReactionTree

+
+
+
+ +
+ +
+
+

aizynthfinder.search.mcts.search module

+

Module containing a class that holds the tree search

+
+
+class aizynthfinder.search.mcts.search.MctsSearchTree(config, root_smiles=None)
+

Bases: object

+

Encapsulation of the search tree.

+
+
Variables
+
    +
  • root – the root node

  • +
  • config – the configuration of the search tree

  • +
+
+
Parameters
+
    +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • root_smiles (Optional[str]) – the root will be set to a node representing this molecule, defaults to None

  • +
+
+
Return type
+

None

+
+
+
+
+classmethod from_json(filename, config)
+

Create a new search tree by deserialization from a JSON file

+
+
Parameters
+
    +
  • filename (str) – the path to the JSON node

  • +
  • config (Configuration) – the configuration of the search

  • +
+
+
Returns
+

a deserialized tree

+
+
Return type
+

MctsSearchTree

+
+
+
+ +
+
+backpropagate(from_node, value_estimate)
+

Backpropagate the value estimate and update all nodes from a +given node all the way to the root.

+
+
Parameters
+
+
+
Return type
+

None

+
+
+
+ +
+
+graph(recreate=False)
+

Construct a directed graph object with the nodes as +vertices and the actions as edges attribute “action”.

+
+
Parameters
+

recreate (bool) – if True will construct the graph even though it is cached, defaults to False

+
+
Returns
+

the graph object

+
+
Raises
+

ValueError – if the tree is not defined

+
+
Return type
+

networkx.classes.digraph.DiGraph

+
+
+
+ +
+
+nodes()
+

Return all the nodes in the search tree

+
+
Return type
+

List[MctsNode]

+
+
+
+ +
+
+one_iteration()
+
+
Perform one iteration of
    +
  1. Selection

  2. +
  3. Expansion

  4. +
  5. Rollout

  6. +
  7. Backpropagation

  8. +
+
+
+
+
Returns
+

if a solution was found

+
+
Return type
+

bool

+
+
+
+ +
+
+select_leaf()
+

Traverse the tree selecting the most promising child at +each step until leaf node returned.

+
+
Returns
+

the leaf node

+
+
Raises
+

ValueError – if the tree is not defined

+
+
Return type
+

aizynthfinder.search.mcts.node.MctsNode

+
+
+
+ +
+
+serialize(filename)
+

Serialize the search tree to a JSON file

+
+
Parameters
+

filename (str) – the path to the JSON file

+
+
Raises
+

ValueError – if the tree is not defined

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

aizynthfinder.search.mcts.state module

+

Module contain a class that encapsulate the state of search tree node.

+
+
+class aizynthfinder.search.mcts.state.MctsState(mols, config)
+

Bases: object

+

Encapsulation of the molecular state of a node.

+

A state consists of an immutable list of molecules that are either solved +(can be found in stock) or that potentially can be expanded to new molecules +by applying a reaction on them.

+

The class is hashable and comparable by the inchi keys of all the molecules.

+
+
Variables
+
    +
  • mols – the list of molecules

  • +
  • expandable_mols – the list of molecules not in stock

  • +
  • stock – the configured stock

  • +
  • in_stock_list – for each molecule if they are in stock

  • +
  • is_solved – is true if all molecules are in stock:

  • +
  • max_transforms – the maximum of the transforms of the molecule

  • +
  • is_terminal – is true if the all molecules are in stock or if the maximum transforms has been reached

  • +
  • expandables_hash – an hash string computed on the expandable molecules

  • +
+
+
Parameters
+
    +
  • mols (Sequence[TreeMolecule]) – the molecules of the state

  • +
  • config (Configuration) – settings of the tree search algorithm

  • +
+
+
Return type
+

None

+
+
+
+
+classmethod from_dict(dict_, config, molecules)
+

Create a new state from a dictionary, i.e. deserialization

+
+
Parameters
+
    +
  • dict (dict) – the serialized state

  • +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • molecules (MoleculeDeserializer) – the deserialized molecules

  • +
  • dict_ (StrDict) –

  • +
+
+
Returns
+

a deserialized state

+
+
Return type
+

State

+
+
+
+ +
+
+property stock_availability: List[str]
+

Returns a list of availabilities for all molecules

+
+
Returns
+

the list

+
+
Return type
+

list of str

+
+
+
+ +
+
+serialize(molecule_store)
+

Serialize the state object to a dictionary

+
+
Parameters
+

molecule_store (MolecularSerializer) – the serialized molecules

+
+
Returns
+

the serialized state

+
+
Return type
+

dict

+
+
+
+ +
+
+to_image(ncolumns=6)
+

Constructs an image representation of the state

+
+
Parameters
+

ncolumns (int, optional) – number of molecules per row, defaults to 6

+
+
Returns
+

the image representation

+
+
Return type
+

a PIL image

+
+
+
+ +
+ +
+
+

aizynthfinder.search.mcts.utils module

+

Module containing utility routines for MCTS. This is not part of public interface

+
+
+class aizynthfinder.search.mcts.utils.ReactionTreeFromSuperNode(*args, **kwargs)
+

Bases: aizynthfinder.reactiontree.ReactionTreeLoader

+

Creates a reaction tree object from MCTS-like nodes and reaction objects

+
+
Parameters
+
    +
  • args (Any) –

  • +
  • kwargs (Any) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+aizynthfinder.search.mcts.utils.route_to_node(from_node)
+

Return the route to a give node to the root.

+

Will return both the actions taken to go between the nodes, +and the nodes in the route themselves.

+
+
Parameters
+

from_node (MctsNode) – the end of the route

+
+
Returns
+

the route

+
+
Return type
+

Tuple[List[RetroReaction], List[MctsNode]]

+
+
+
+ +
+
+

Module contents

+

Sub-package containing MCTS routines

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.search.retrostar.html b/aizynthfinder.search.retrostar.html new file mode 100644 index 0000000..6426c47 --- /dev/null +++ b/aizynthfinder.search.retrostar.html @@ -0,0 +1,676 @@ + + + + + + + + + aizynthfinder.search.retrostar package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.search.retrostar package

+
+

Submodules

+
+
+

aizynthfinder.search.retrostar.cost module

+

Module containing Retro* cost models

+
+
+class aizynthfinder.search.retrostar.cost.MoleculeCost(config)
+

Bases: object

+

A class to compute the molecule cost.

+

The cost to be computed is taken from the input config. If no molecule_cost is +set, assigns ZeroMoleculeCost as the cost by default. The molecule_cost can be +set as a dictionary in config under search in the following format: +‘algorithm’: ‘retrostar’ +‘algorithm_config’: {

+
+
+
‘molecule_cost’: {

‘cost’: name of the search cost class or custom_package.custom_model.CustomClass, +other settings or params

+
+
+

}

+
+

}

+

The cost can be computed by calling the instantiated class with a molecule.

+
calculator = MyCost(config)
+cost = calculator.calculate(molecule)
+
+
+
+
Parameters
+

config (Configuration) – the configuration of the tree search

+
+
Return type
+

None

+
+
+
+ +
+
+class aizynthfinder.search.retrostar.cost.RetroStarCost(**kwargs)
+

Bases: object

+

Encapsulation of the original Retro* molecular cost model

+

Numpy implementation of original pytorch model

+

The predictions of the score is made on a Molecule object

+
mol = Molecule(smiles="CCC")
+scorer = RetroStarCost()
+score = scorer.calculate(mol)
+
+
+

The model provided when creating the scorer object should be a pickled +tuple. +The first item of the tuple should be a list of the model weights for each layer. +The second item of the tuple should be a list of the model biases for each layer.

+
+
Parameters
+
    +
  • model_path – the filename of the model weights and biases

  • +
  • fingerprint_length – the number of bits in the fingerprint

  • +
  • fingerprint_radius – the radius of the fingerprint

  • +
  • dropout_rate – the dropout_rate

  • +
  • kwargs (Any) –

  • +
+
+
Return type
+

None

+
+
+
+
+calculate(mol)
+
+
Parameters
+

mol (Molecule) –

+
+
Return type
+

float

+
+
+
+ +
+ +
+
+class aizynthfinder.search.retrostar.cost.ZeroMoleculeCost
+

Bases: object

+

Encapsulation of a Zero cost model

+
+
+calculate(mol)
+
+
Parameters
+

mol (Molecule) –

+
+
Return type
+

float

+
+
+
+ +
+ +
+
+

aizynthfinder.search.retrostar.nodes module

+

Module containing a classes representation various tree nodes

+
+
+class aizynthfinder.search.retrostar.nodes.MoleculeNode(mol, config, molecule_cost, parent=None)
+

Bases: aizynthfinder.search.andor_trees.TreeNodeMixin

+

An OR node representing a molecule

+
+
Variables
+
    +
  • cost – the cost of synthesizing the molecule

  • +
  • expandable – if True, this node is part of the frontier

  • +
  • mol – the molecule represented by the node

  • +
  • in_stock – if True the molecule is in stock and hence should not be expanded

  • +
  • parent – the parent of the node

  • +
  • solved – if True the molecule is in stock or at least one child node is solved

  • +
  • value – the current rn(m|T)

  • +
+
+
Parameters
+
+
+
Return type
+

None

+
+
+
+
+classmethod create_root(smiles, config, molecule_cost)
+

Create a root node for a tree using a SMILES.

+
+
Parameters
+
    +
  • smiles (str) – the SMILES representation of the root state

  • +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • molecule_cost (MoleculeCost) –

  • +
+
+
Returns
+

the created node

+
+
Return type
+

MoleculeNode

+
+
+
+ +
+
+classmethod from_dict(dict_, config, molecules, molecule_cost, parent=None)
+

Create a new node from a dictionary, i.e. deserialization

+
+
Parameters
+
+
+
Returns
+

a deserialized node

+
+
Return type
+

MoleculeNode

+
+
+
+ +
+
+property children: List[ReactionNode]
+

Gives the reaction children nodes

+
+ +
+
+property target_value: float
+

The V_t(m|T) value, +the current cost of the tree containing this node

+
+
Returns
+

the value

+
+
+
+ +
+
+property prop: StrDict
+

Dictionary with publicly exposed properties

+
+ +
+
+add_stub(cost, reaction)
+

Add a stub / sub-tree to this node

+
+
Parameters
+
    +
  • cost (float) – the cost of the reaction

  • +
  • reaction (RetroReaction) – the reaction creating the stub

  • +
+
+
Returns
+

list of all newly added molecular nodes

+
+
Return type
+

Sequence[MoleculeNode]

+
+
+
+ +
+
+ancestors()
+

Return the ancestors of this node

+
+
Returns
+

the ancestors

+
+
Return type
+

set

+
+
+
+ +
+
+close()
+

Updates the values of this node after expanding it.

+
+
Returns
+

the delta V value

+
+
Return type
+

float

+
+
+
+ +
+
+serialize(molecule_store)
+

Serialize the node object to a dictionary

+
+
Parameters
+

molecule_store (MoleculeSerializer) – the serialized molecules

+
+
Returns
+

the serialized node

+
+
Return type
+

StrDict

+
+
+
+ +
+
+update(solved)
+

Update the node as part of the update algorithm, +calling the update() method of its parent if available.

+
+
Parameters
+

solved (bool) – if the child node was solved

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+class aizynthfinder.search.retrostar.nodes.ReactionNode(cost, reaction, parent)
+

Bases: aizynthfinder.search.andor_trees.TreeNodeMixin

+

An AND node representing a reaction

+
+
Variables
+
    +
  • cost – the cost of the reaction

  • +
  • parent – the parent of the node

  • +
  • reaction – the reaction represented by the node

  • +
  • solved – if True all children nodes are solved

  • +
  • target_value – the V(m|T) for the children, the current cost

  • +
  • value – the current rn(r|T)

  • +
+
+
Parameters
+
    +
  • cost (float) – the cost of the reaction

  • +
  • reaction (RetroReaction) – the reaction to be represented by the node

  • +
  • parent (MoleculeNode) – the parent of the node

  • +
+
+
Return type
+

None

+
+
+
+
+classmethod create_stub(cost, reaction, parent, config)
+

Create a ReactionNode and creates all the MoleculeNode objects +that are the children of the node.

+
+
Parameters
+
    +
  • cost (float) – the cost of the reaction

  • +
  • reaction (RetroReaction) – the reaction to be represented by the node

  • +
  • parent (MoleculeNode) – the parent of the node

  • +
  • config (Configuration) – the configuration of the search tree

  • +
+
+
Return type
+

ReactionNode

+
+
+
+ +
+
+classmethod from_dict(dict_, config, molecules, molecule_cost, parent)
+

Create a new node from a dictionary, i.e. deserialization

+
+
Parameters
+
+
+
Returns
+

a deserialized node

+
+
Return type
+

ReactionNode

+
+
+
+ +
+
+property children: List[MoleculeNode]
+

Gives the molecule children nodes

+
+ +
+
+property prop: StrDict
+

Dictionary with publicly exposed properties

+
+ +
+
+serialize(molecule_store)
+

Serialize the node object to a dictionary

+
+
Parameters
+

molecule_store (MoleculeSerializer) – the serialized molecules

+
+
Returns
+

the serialized node

+
+
Return type
+

StrDict

+
+
+
+ +
+
+update(value, from_mol=None)
+

Update the node as part of the update algorithm, +calling the update() method of its parent

+
+
Parameters
+
    +
  • value (float) – the delta V value

  • +
  • from_mol (Optional[TreeMolecule]) – the molecule being expanded, used for excluding propagation

  • +
+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

aizynthfinder.search.retrostar.search_tree module

+

Module containing a class that holds the tree search

+
+
+class aizynthfinder.search.retrostar.search_tree.SearchTree(config, root_smiles=None)
+

Bases: aizynthfinder.search.andor_trees.AndOrSearchTreeBase

+

Encapsulation of the Retro* search tree (an AND/OR tree).

+
+
Variables
+
    +
  • config – settings of the tree search algorithm

  • +
  • root – the root node

  • +
+
+
Parameters
+
    +
  • config (Configuration) – settings of the tree search algorithm

  • +
  • root_smiles (Optional[str]) – the root will be set to a node representing this molecule, defaults to None

  • +
+
+
Return type
+

None

+
+
+
+
+classmethod from_json(filename, config)
+

Create a new search tree by deserialization from a JSON file

+
+
Parameters
+
    +
  • filename (str) – the path to the JSON node

  • +
  • config (Configuration) – the configuration of the search tree

  • +
+
+
Returns
+

a deserialized tree

+
+
Return type
+

SearchTree

+
+
+
+ +
+
+property mol_nodes: Sequence[MoleculeNode]
+

Return the molecule nodes of the tree

+
+ +
+
+one_iteration()
+
+
Perform one iteration of
    +
  1. Selection

  2. +
  3. Expansion

  4. +
  5. Update

  6. +
+
+
+
+
Raises
+

StopIteration – if the search should be pre-maturely terminated

+
+
Returns
+

if a solution was found

+
+
Return type
+

bool

+
+
+
+ +
+
+routes()
+

Extracts and returns routes from the AND/OR tree

+
+
Returns
+

the routes

+
+
Return type
+

List[ReactionTree]

+
+
+
+ +
+
+serialize(filename)
+

Seralize the search tree to a JSON file

+
+
Parameters
+

filename (str) – the path to the JSON file

+
+
Return type
+

None

+
+
+
+ +
+ +
+
+

Module contents

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.tools.html b/aizynthfinder.tools.html new file mode 100644 index 0000000..5f4b130 --- /dev/null +++ b/aizynthfinder.tools.html @@ -0,0 +1,327 @@ + + + + + + + + + aizynthfinder.tools package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.tools package

+
+

Submodules

+
+
+

aizynthfinder.tools.cat_output module

+

Module containing a CLI for concatenating output files (hdf5/json.gz files)

+
+
+aizynthfinder.tools.cat_output.main()
+

Entry-point for the cat_aizynth_output CLI

+
+
Return type
+

None

+
+
+
+ +
+
+

aizynthfinder.tools.download_public_data module

+

Module with script to download public data

+
+
+aizynthfinder.tools.download_public_data.main()
+

Entry-point for CLI

+
+
Return type
+

None

+
+
+
+ +
+
+

aizynthfinder.tools.make_stock module

+

Module containing classes and routines for making stock input to the tree search.

+
+
+aizynthfinder.tools.make_stock.extract_plain_smiles(files)
+

Extract SMILES from plain text files, one SMILES on each line. +The SMILES are yielded to save memory.

+
+
Parameters
+

files (List[str]) –

+
+
Return type
+

_StrIterator

+
+
+
+ +
+
+aizynthfinder.tools.make_stock.extract_smiles_from_module(files)
+

Extract SMILES by loading a custom module, containing +the function extract_smiles.

+

The first element of the input argument is taken as the module name. +The other elements are taken as input to the extract_smiles method

+

The SMILES are yielded to save memory.

+
+
Parameters
+

files (List[str]) –

+
+
Return type
+

_StrIterator

+
+
+
+ +
+
+aizynthfinder.tools.make_stock.make_hdf5_stock(inchi_keys, filename)
+

Put all the inchi keys from the given iterable in a pandas +dataframe and save it as an HDF5 file. Only unique inchi keys +are stored.

+
+
Parameters
+
    +
  • inchi_keys (_StrIterator) –

  • +
  • filename (str) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+aizynthfinder.tools.make_stock.make_molbloom(smiles_list, filename, filter_size, approx_mols)
+

Put all the unique SMILES in a new bloom filter.

+
+
Params smiles_list
+

the SMILES

+
+
Params filename
+

the path to the saved filter

+
+
Params filter_size
+

the size of the filter in bits

+
+
Params approx_mols
+

approximately the number of compounds

+
+
Parameters
+
    +
  • smiles_list (_StrIterator) –

  • +
  • filename (str) –

  • +
  • filter_size (int) –

  • +
  • approx_mols (int) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+aizynthfinder.tools.make_stock.make_molbloom_inchi(inchi_keys, filename, filter_size, approx_mols)
+

Put all the unique InChI keys in a new bloom filter.

+
+
Params inchi_keys
+

the Inchi Keys

+
+
Params filename
+

the path to the saved filter

+
+
Params filter_size
+

the size of the filter in bits

+
+
Params approx_mols
+

approximately the number of compounds

+
+
Parameters
+
    +
  • inchi_keys (_StrIterator) –

  • +
  • filename (str) –

  • +
  • filter_size (int) –

  • +
  • approx_mols (int) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+aizynthfinder.tools.make_stock.make_mongo_stock(inchi_keys, source_tag, host=None)
+

Put all the inchi keys from the given iterable in Mongo database as +a molecules collection. Only unique inchi keys are stored.

+
+
Parameters
+
    +
  • inchi_keys (_StrIterator) –

  • +
  • source_tag (str) –

  • +
  • host (Optional[str]) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+aizynthfinder.tools.make_stock.main()
+

Entry-point for the smiles2stock tool

+
+
Return type
+

None

+
+
+
+ +
+
+

Module contents

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/aizynthfinder.utils.html b/aizynthfinder.utils.html new file mode 100644 index 0000000..a2fd17b --- /dev/null +++ b/aizynthfinder.utils.html @@ -0,0 +1,839 @@ + + + + + + + + + aizynthfinder.utils package — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder.utils package

+
+

Submodules

+
+
+

aizynthfinder.utils.exceptions module

+

Module containing custom exception classes

+
+
+exception aizynthfinder.utils.exceptions.CostException
+

Bases: Exception

+

Exception raised by molecule cost classes

+
+ +
+
+exception aizynthfinder.utils.exceptions.ExternalModelAPIError
+

Bases: Exception

+

Custom error type to signal failure in External model

+
+ +
+
+exception aizynthfinder.utils.exceptions.MoleculeException
+

Bases: Exception

+

An exception that is raised by molecule class

+
+ +
+
+exception aizynthfinder.utils.exceptions.NodeUnexpectedBehaviourException
+

Bases: Exception

+

Exception that is raised if the tree search is behaving unexpectedly.

+
+ +
+
+exception aizynthfinder.utils.exceptions.PolicyException
+

Bases: Exception

+

An exception raised by policy classes

+
+ +
+
+exception aizynthfinder.utils.exceptions.RejectionException
+

Bases: Exception

+

An exception raised if a retro action should be rejected

+
+ +
+
+exception aizynthfinder.utils.exceptions.ScorerException
+

Bases: Exception

+

Exception raised by scoring classes

+
+ +
+
+exception aizynthfinder.utils.exceptions.StockException
+

Bases: Exception

+

An exception raised by stock classes

+
+ +
+
+exception aizynthfinder.utils.exceptions.TreeAnalysisException
+

Bases: Exception

+

Exception raised when analysing trees

+
+ +
+
+

aizynthfinder.utils.files module

+

Module containing routines to work with files and processes.

+
+
+aizynthfinder.utils.files.read_datafile(filename)
+

Read aizynth output from disc in either .hdf5 or .json format

+
+
Parameters
+

filename (Union[str, Path]) – the path to the data

+
+
Returns
+

the loaded data

+
+
Return type
+

pd.DataFrame

+
+
+
+ +
+
+aizynthfinder.utils.files.save_datafile(data, filename)
+

Save the given data to disc in either .hdf5 or .json format

+
+
Parameters
+
    +
  • data (pd.DataFrame) – the data to save

  • +
  • filename (Union[str, Path]) – the path to the data

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+aizynthfinder.utils.files.cat_hdf_files(input_files, output_name, trees_name=None)
+

Concatenate hdf5 or json datafiles

+
+
Parameters
+
    +
  • input_files (List[str]) –

  • +
  • output_name (str) –

  • +
  • trees_name (Optional[str]) –

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+aizynthfinder.utils.files.cat_datafiles(input_files, output_name, trees_name=None)
+

Concatenate hdf5 or json datafiles

+

if tree_name is given, will take out the trees column +from the tables and save it to a gzipped-json file.

+
+
Parameters
+
    +
  • input_files (List[str]) – the paths to the files to concatenate

  • +
  • output_name (str) – the name of the concatenated file

  • +
  • trees_name (Optional[str]) – the name of the concatenated trees

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+aizynthfinder.utils.files.split_file(filename, nparts)
+

Split the content of a text file into a given number of temporary files

+
+
Parameters
+
    +
  • filename (str) – the path to the file to split

  • +
  • nparts (int) – the number of parts to create

  • +
+
+
Returns
+

list of filenames of the parts

+
+
Return type
+

List[str]

+
+
+
+ +
+
+aizynthfinder.utils.files.start_processes(inputs, log_prefix, cmd_callback, poll_freq=5)
+

Start a number of background processes and wait for them +to complete.

+

The standard output and standard error is saved to a log file.

+

The command to start for each process is given by the cmd_callback +function that takes the index of the process and an item of the input +as arguments.

+
+
Parameters
+
    +
  • inputs (Sequence[Any]) – a sequence of input to the processes

  • +
  • log_prefix (str) – the prefix to the log file of each processes

  • +
  • cmd_callback (Callable) – function that creates the process commands

  • +
  • poll_freq (int) – the polling frequency for checking if processes are completed

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+

aizynthfinder.utils.image module

+

This module contains a collection of routines to produce pretty images

+
+
+aizynthfinder.utils.image.molecule_to_image(mol, frame_color, size=300)
+

Create a pretty image of a molecule, +with a colored frame around it

+
+
Parameters
+
    +
  • mol (Molecule) – the molecule

  • +
  • frame_color (PilColor) – the color of the frame

  • +
  • size (int) – the size of the image

  • +
+
+
Returns
+

the produced image

+
+
Return type
+

PilImage

+
+
+
+ +
+
+aizynthfinder.utils.image.molecules_to_images(mols, frame_colors, size=300)
+

Create pretty images of molecules with a colored frame around each one of them.

+

The molecules will be resized to be of similar sizes.

+
+
Parameters
+
    +
  • mols (Sequence[Molecule]) – the molecules

  • +
  • frame_colors (Sequence[PilColor]) – the color of the frame for each molecule

  • +
  • size (int) – the sub-image size

  • +
+
+
Returns
+

the produced images

+
+
Return type
+

List[PilImage]

+
+
+
+ +
+
+aizynthfinder.utils.image.crop_image(img, margin=20)
+

Crop an image by removing white space around it

+
+
Parameters
+
    +
  • img (PilImage) – the image to crop

  • +
  • margin (int) – padding, defaults to 20

  • +
+
+
Returns
+

the cropped image

+
+
Return type
+

PilImage

+
+
+
+ +
+
+aizynthfinder.utils.image.draw_rounded_rectangle(img, color, arc_size=20)
+

Draw a rounded rectangle around an image

+
+
Parameters
+
    +
  • img (PilImage) – the image to draw upon

  • +
  • color (PilColor) – the color of the rectangle

  • +
  • arc_size (int) – the size of the corner, defaults to 20

  • +
+
+
Returns
+

the new image

+
+
Return type
+

PilImage

+
+
+
+ +
+
+aizynthfinder.utils.image.save_molecule_images(molecules, frame_colors, size=300)
+

Create images of a list of molecules and save them to disc +a globally managed folder.

+
+
Parameters
+
    +
  • molecules (Sequence[Molecule]) – the molecules to save as images

  • +
  • frame_colors (Sequence[PilColor]) – the color of the frame around each image

  • +
  • size (int) – the sub-image size for each molecule

  • +
+
+
Returns
+

the filename of the created images

+
+
Return type
+

Dict[Molecule, str]

+
+
+
+ +
+
+aizynthfinder.utils.image.make_visjs_page(filename, molecules, reactions, edges, frame_colors, hierarchical=False)
+

Create HTML code of a bipartite graph of molecules and reactions +using the vis.js network library.

+

Package the created HTML page and all images as tar-ball.

+
+
Parameters
+
    +
  • filename (str) – the basename of the archive

  • +
  • molecules (Sequence[Molecule]) – the molecules nodes

  • +
  • reactions (Sequence[FixedRetroReaction]) – the reaction nodes

  • +
  • edges (Union[Sequence[Tuple[Any, Any]], nx.digraph.OutEdgeView]) – the edges of the graph

  • +
  • frame_colors (Sequence[PilColor]) – the color of the frame around each image

  • +
  • hierarchical (bool) – if True, will produce a hierarchical layout

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+class aizynthfinder.utils.image.RouteImageFactory(route, in_stock_colors=None, show_all=True, margin=100)
+

Bases: object

+

Factory class for drawing a route

+
+
Parameters
+
    +
  • route (StrDict) – the dictionary representation of the route

  • +
  • in_stock_colors (FrameColors) – the colors around molecules, defaults to {True: “green”, False: “orange”}

  • +
  • show_all (bool) – if True, also show nodes that are marked as hidden

  • +
  • margin (int) – the margin between images

  • +
+
+
Return type
+

None

+
+
+
+ +
+
+

aizynthfinder.utils.loading module

+

Module containing routine to dynamically load a class from a specification

+
+
+aizynthfinder.utils.loading.load_dynamic_class(name_spec, default_module=None, exception_cls=<class 'ValueError'>)
+

Load an object from a dynamic specification.

+
+
The specification can be either:

ClassName, in-case the module name is taken from the default_module argument

+
+
or

package_name.module_name.ClassName, in-case the module is taken as package_name.module_name

+
+
+
+
Parameters
+
    +
  • name_spec (str) – the class specification

  • +
  • default_module (Optional[str]) – the default module

  • +
  • exception_cls (Any) – the exception class to raise on exception

  • +
+
+
Returns
+

the loaded class

+
+
Return type
+

Any

+
+
+
+ +
+
+

aizynthfinder.utils.logging module

+

Module containing routines to setup proper logging

+
+
+aizynthfinder.utils.logging.logger()
+

Returns the logger that should be used by all classes

+
+
Returns
+

the logger object

+
+
Return type
+

logging.Logger

+
+
+
+ +
+
+aizynthfinder.utils.logging.setup_logger(console_level, file_level=None)
+

Setup the logger that should be used by all classes

+

The logger configuration is read from the logging.yml file.

+
+
Parameters
+
    +
  • console_level (int) – the level of logging to the console

  • +
  • file_level (Optional[int]) – the level of logging to file, if not set logging to file is disabled, default to None

  • +
+
+
Returns
+

the logger object

+
+
Return type
+

logging.Logger

+
+
+
+ +
+
+

aizynthfinder.utils.math module

+

Module containing diverse math functions, including neural network-related functions.

+
+
+aizynthfinder.utils.math.softmax(x)
+

Compute softmax values for each sets of scores in x.

+
+
Parameters
+

x (numpy.ndarray) –

+
+
Return type
+

numpy.ndarray

+
+
+
+ +
+
+

aizynthfinder.utils.models module

+

Module containing helper routines for using Keras, Tensorflow and Onnx models

+
+
+aizynthfinder.utils.models.load_model(source, key, use_remote_models)
+

Load model from a configuration specification.

+
+
If use_remote_models is True, tries to load:
    +
  1. A Tensorflow server through gRPC

  2. +
  3. A Tensorflow server through REST API

  4. +
  5. A local Keras model

  6. +
+
+
+

otherwise it just loads the local model.

+
+
Parameters
+
    +
  • source (str) – if fallbacks to a local model, this is the filename

  • +
  • key (str) – when connecting to Tensorflow server this is the model name

  • +
  • use_remote_models (bool) – if True will try to connect to remote model server

  • +
+
+
Returns
+

a model object with a predict object

+
+
Return type
+

Union[‘LocalKerasModel’, ‘LocalOnnxModel’, ‘ExternalModelViaGRPC’, ‘ExternalModelViaREST’]

+
+
+
+ +
+
+class aizynthfinder.utils.models.LocalKerasModel(filename)
+

Bases: object

+

A keras policy model that is executed locally.

+

The size of the input vector can be determined with the len() method.

+
+
Variables
+
    +
  • model – the compiled model

  • +
  • output_size – the length of the output vector

  • +
+
+
Parameters
+

filename (str) – the path to a Keras checkpoint file

+
+
Return type
+

None

+
+
+
+
+predict(*args, **_)
+

Perform a forward pass of the neural network.

+
+
Parameters
+
    +
  • args (numpy.ndarray) – the input vectors

  • +
  • _ (numpy.ndarray) –

  • +
+
+
Returns
+

the vector of the output layer

+
+
Return type
+

numpy.ndarray

+
+
+
+ +
+ +
+
+class aizynthfinder.utils.models.LocalOnnxModel(filename)
+

Bases: object

+

An Onnx model that is executed locally.

+

The size of the input vector can be determined with the len() method.

+
+
Variables
+
    +
  • model – the compiled Onnx model

  • +
  • output_size – the length of the output vector

  • +
+
+
Parameters
+

filename (str) – the path to a Onnx model checkpoint file

+
+
Return type
+

None

+
+
+
+
+predict(*args, **_)
+

Perform a prediction run on the onnx model.

+
+
Parameters
+
    +
  • args (numpy.ndarray) – the input vectors

  • +
  • _ (numpy.ndarray) –

  • +
+
+
Returns
+

the vector of the output layer

+
+
Return type
+

numpy.ndarray

+
+
+
+ +
+ +
+
+class aizynthfinder.utils.models.ExternalModelViaREST(name)
+

Bases: object

+

A neural network model implementation using TF Serving via REST API.

+
+
Parameters
+

name (str) – the name of model

+
+
Return type
+

None

+
+
+
+
+predict(*args, **kwargs)
+

Get prediction from model.

+

If the keys in kwargs agree with the model input names, they +will be used in stead of arg

+
+
Parameters
+
    +
  • args (numpy.ndarray) – the input vectors

  • +
  • kwargs (numpy.ndarray) – the named input vectors

  • +
+
+
Returns
+

the vector of the output layer

+
+
Return type
+

numpy.ndarray

+
+
+
+ +
+ +
+
+class aizynthfinder.utils.models.ExternalModelViaGRPC(name)
+

Bases: object

+

A neural network model implementation using TF Serving via gRPC.

+
+
Parameters
+

name (str) – the name of model

+
+
Return type
+

None

+
+
+
+
+predict(*args, **kwargs)
+

Get prediction from model.

+

If the keys in kwargs agree with the model input names, they +will be used in stead of arg

+
+
Parameters
+
    +
  • args (numpy.ndarray) – the input vectors

  • +
  • kwargs (numpy.ndarray) – the named input vectors

  • +
+
+
Returns
+

the vector of the output layer

+
+
Return type
+

numpy.ndarray

+
+
+
+ +
+ +
+
+

aizynthfinder.utils.mongo module

+

Module containing routines to obtain a MongoClient instance

+
+
+aizynthfinder.utils.mongo.get_mongo_client(host='localhost', port=27017, user=None, password=None, tls_certs_path='')
+

A helper function to create and reuse MongoClient

+

The client is only setup once. Therefore if this function is called a second +time with different parameters, it would still return the first client.

+
+
Parameters
+
    +
  • host (str) – the host

  • +
  • port (int) – the host port

  • +
  • user (Optional[str]) – username, defaults to None

  • +
  • password (Optional[str]) – password, defaults to None

  • +
  • tls_certs_path (str) – the path to TLS certificates if to be used, defaults to “”

  • +
+
+
Raises
+

ValueError – if host and port is not given first time

+
+
Returns
+

the MongoDB client

+
+
Return type
+

Optional[pymongo.mongo_client.MongoClient]

+
+
+
+ +
+
+

aizynthfinder.utils.paths module

+

Module containing routines for returning package paths

+
+
+aizynthfinder.utils.paths.package_path()
+

Return the path to the package

+
+
Return type
+

str

+
+
+
+ +
+
+aizynthfinder.utils.paths.data_path()
+

Return the path to the data directory of the package

+
+
Return type
+

str

+
+
+
+ +
+
+

aizynthfinder.utils.type_utils module

+

Module containing all types and type imports

+
+
+

Module contents

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/cli.html b/cli.html new file mode 100644 index 0000000..7e6d808 --- /dev/null +++ b/cli.html @@ -0,0 +1,290 @@ + + + + + + + + + Command-line interface — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

Command-line interface

+

This tools provide the possibility to perform tree search on a batch of molecules.

+

In its simplest form, you type

+
aizynthcli --config config_local.yml --smiles smiles.txt
+
+
+

where config_local.yml contains configurations such as paths to policy models and stocks (see here) +and smiles.txt is a simple text file with SMILES (one on each row).

+

To find out what other arguments are available use the -h flag.

+
aizynthcli -h
+
+
+

That gives something like this:

+
usage: aizynthcli [-h] --smiles SMILES --config CONFIG
+                  [--policy POLICY [POLICY ...]]
+                  [--filter FILTER [FILTER ...]]
+                  [--stocks STOCKS [STOCKS ...]] [--output OUTPUT]
+                  [--log_to_file] [--nproc NPROC] [--cluster]
+                  [--route_distance_model ROUTE_DISTANCE_MODEL]
+                  [--post_processing POST_PROCESSING [POST_PROCESSING ...]]
+                  [--checkpoint CHECKPOINT]
+
+options:
+  -h, --help            show this help message and exit
+  --smiles SMILES       the target molecule smiles or the path of a file
+                        containing the smiles
+  --config CONFIG       the filename of a configuration file
+  --policy POLICY [POLICY ...]
+                        the name of the expansion policy to use
+  --filter FILTER [FILTER ...]
+                        the name of the filter to use
+  --stocks STOCKS [STOCKS ...]
+                        the name of the stocks to use
+  --output OUTPUT       the name of the output file (JSON or HDF5 file)
+  --log_to_file         if provided, detailed logging to file is enabled
+  --nproc NPROC         if given, the input is split over a number of
+                        processes
+  --cluster             if provided, perform automatic clustering
+  --route_distance_model ROUTE_DISTANCE_MODEL
+                        if provided, calculate route distances for clustering
+                        with this ML model
+  --post_processing POST_PROCESSING [POST_PROCESSING ...]
+                        a number of modules that performs post-processing
+                        tasks
+  --checkpoint CHECKPOINT
+                        the path to the checkpoint file
+
+
+

By default:

+
+
    +
  • All stocks are selected if no stock is specified

  • +
  • First expansion policy is selected if not expansion policy is specified

  • +
  • All filter policies are selected if it is not specified on the command-line

  • +
+
+
+

Analysing output

+

The results from the aizynthcli tool when supplying multiple SMILES is an JSON or HDF5 file that can be read as a pandas dataframe. +It will be called output.json.gz by default.

+

A checkpoint.json.gz will also be generated if a checkpoint file path is provided as input when calling the aizynthcli tool. The +checkpoint data will contain the processed smiles with their corresponding results in each line of the file.

+
import pandas as pd
+data = pd.read_json("output.json.gz", orient="table")
+
+
+

it will contain statistics about the tree search and the top-ranked routes (as JSONs) for each target compound, see below.

+

When a single SMILES is provided to the tool, the statistics will be written to the terminal, and the top-ranked routes to +a JSON file (trees.json by default).

+

This is an example of how to create images of the top-ranked routes for the first target compound

+
import pandas as pd
+from aizynthfinder.reactiontree import ReactionTree
+
+data = pd.read_json("output.json.gz", orient="table")
+all_trees = data.trees.values  # This contains a list of all the trees for all the compounds
+trees_for_first_target = all_trees[0]
+
+for itree, tree in enumerate(trees_for_first_target):
+    imagefile = f"route{itree:03d}.png"
+    ReactionTree.from_dict(tree).to_image().save(imagefile)
+
+
+

The images will be called route000.png, route001.png etc.

+
+
+

Specification of output

+

The JSON or HDF5 file created when running the tool with a list of SMILES will have the following columns

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Column

Description

target

The target SMILES

search_time

The total search time in seconds

first_solution_time

The time elapsed until the first solution was found

first_solution_iteration

The number of iterations completed until the first solution was found

number_of_nodes

The number of nodes in the search tree

max_transforms

The maximum number of transformations for all routes in the search tree

max_children

The maximum number of children for a search node

number_of_routes

The number of routes in the search tree

number_of_solved_routes

The number of solved routes in search tree

top_score

The score of the top-scored route (default to MCTS reward)

is_solved

If the top-scored route is solved

number_of_steps

The number of reactions in the top-scored route

number_of_precursors

The number of starting materials

number_of_precursors_in_stock

The number of starting materials in stock

precursors_in_stock

Comma-separated list of SMILES of starting material in stock

precursors_not_in_stock

Comma-separated list of SMILES of starting material not in stock

precursors_availability

Semi-colon separated list of stock availability of the staring material

policy_used_counts

Dictionary of the total number of times an expansion policy have been used

profiling

Profiling information from the search tree, including expansion models call and reactant generation

stock_info

Dictionary of the stock availability for each of the starting material in all extracted routes

top_scores

Comma-separated list of the score of the extracted routes (default to MCTS reward)

trees

A list of the extracted routes as dictionaries

+

If you running the tool with a single SMILES, all of this data will be printed to the screen, except +the stock_info and trees.

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/configuration.html b/configuration.html new file mode 100644 index 0000000..d69ffa6 --- /dev/null +++ b/configuration.html @@ -0,0 +1,408 @@ + + + + + + + + + Configuration file — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

Configuration file

+

The configuration file that is to be provided to the interfaces +specify file paths to policy models and stocks, in addition to +detailed parameters of the tree search.

+
+

Simple usage

+

Let’s say you have a

+
+
    +
  • A trained Keras expansion model that is called uspto_expansion.onnx

  • +
  • A library of unique templates called uspto_templates.csv.gz

  • +
  • A stock file in HDF5 format, called zinc_stock.hdf5

  • +
+
+

(this could have been created by the smiles2stock tool, see here)

+

then to use them in the tree search, create a file called config.yml that has the following content

+
expansion:
+  full:
+    - uspto_expansion.onnx
+    - uspto_templates.csv.gz
+stock:
+  zinc: zinc_stock.hdf5
+
+
+
+
+

Advanced usage

+

A more detailed configuration file is shown below

+
search:
+  algorithm: mcts
+  algorithm_config:
+    C: 1.4
+    default_prior: 0.5
+    use_prior: True
+    prune_cycles_in_search: True
+    search_reward: state score
+  max_transforms: 6
+  iteration_limit: 100
+  return_first: false
+  time_limit: 120
+  exclude_target_from_stock: True
+expansion:
+  my_policy:
+    type: template-based
+    model: /path/to/keras/model/weights.hdf5
+    template: /path/to/hdf5/templates.hdf5
+    template_column: retro_template
+    cutoff_cumulative: 0.995
+    cutoff_number: 50
+    use_rdchiral: True
+    use_remote_models: False
+  my_full_policy:
+    - /path/to/keras/model/weights.hdf5
+    - /path/to/hdf5/templates.hdf5
+filter:
+  uspto:
+    type: quick-filter
+    model: /path/to/keras/model/weights.hdf5
+    exclude_from_policy: rc
+    filter_cutoff: 0.05
+    use_remote_models: False
+  uspto_full: /path/to/keras/model/weights.hdf5
+stock:
+  buyables:
+    type: inchiset
+    path: /path/to/stock1.hdf5
+  emolecules: /path/to/stock1.hdf5
+
+
+
+
The (expansion) policy models are specified using two files
    +
  • a checkpoint files from Keras in ONNX or hdf5 format,

  • +
  • a HDF5 or a CSV file containing templates.

  • +
+
+
+

A key like my_policy should be set and the configuration contains type, model and template that must be provided. +If the other settings are not assigned, their default values are taken. +The policy my_full_policy exemplifies a short-cut to the template-based expansion model when no other settings need to be +provided, only the model and templates can be provided. The default settings will be taken in this case.

+

The template file should be readable by pandas using the table key and the retro_template column. +A policy can then be selected using the provided key, like my_policy in the above example.

+

The filter policy model is specified using a single checkpoint file. +Any key like uspto can be set. The settings contains type and model that must be provided. If the other +settings are not assigned, their default values are taken. +The filter uspto_full exemplifies a short-cut to the quick-filter model when no other settings need to be +provided, only the model can be provided. The default settings will be taken in this case.

+
+
The stock files can be
    +
  • HDF5 files with the table key an the inchi_key column.

  • +
  • A CSV file with a inchi_key column

  • +
  • A text file a single column

  • +
+
+
+

In all cases, the column should contain pre-computed inchi keys of the molecules. +The stocks can be set using any key, like buyables or emolecules in the above example. +The type and path parameters can also be set along with other parameters. +If no other settings need to be provided, only the path can be provided, whereby it will be +treated as a +short-cut to the inchiset class..

+

The values in the search sections are optional, and if missing, default values are considered. These +values can also be taken from environment variables. An example of this can be seen as below:

+
search:
+  iteration_limit: ${ITERATION_LIMIT}
+  algorithm_config:
+    C: ${C}
+  time_limit: ${TIME_LIMIT}
+  max_transforms: ${MAX_TRANSFORMS}
+
+
+

These are the available search settings. The algorithm_config refers to MCTS settings:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Setting

Default value

Description

algorithm

mcts

The search algorithm. Can be set to package.module.ClassName to use a custom search method.

algorithm_config: C

1.4

The C value used to balance exploitation and exploration in the upper confidence bound score of the nodes.

algorithm_config: default_prior

0.5

The prior that is used if policy-provided priors are not used.

algorithm_config: use_prior

True

If True, priors from the policy is used instead of the default_prior.

algorithm_config: prune_cycles_in_search

True

If True, prevents the MCTS from creating cycles by recreating previously seen molecules when it is expanded.

algorithm_config: search_reward

state score

The scoring used for the MCTS search algorithm.

algorithm_config: immediate_instantiation

[]

list of expansion policies for which the MCTS algorithm immediately instantiate the children node upon expansion

algorithm_config: mcts_grouping

    +
  • +
+

if is partial or full the MCTS algorithm will group expansions that produce the same state. If partial is used the equality will only be determined based on the expandable molecules, whereas full will check all molecules.

max_transforms

6

The maximum depth of the search tree.

iteration_limit

100

The maximum number of iterations for the tree search.

time_limit

120

The maximum number of seconds to complete the tree search.

return_first

False

If True, the tree search will be terminated as soon as one solution is found.

exclude_target_from_stock

True

If True, the target is in stock will be broken down.

+

The post_processing settings are:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Setting

Default value

Description

min_routes

5

The minumum number of routes to extract if all_routes is not set.

max_routes

25

The maximum number of routes to extract if all_routes is not set.

all_routes

False

If True, will extract all solved routes.

route_distance_model

N/A

If set, will load the quick route distance model from this checkpoint file.

route_scorer

state score

The scoring for routes when extracting them in the post processing step.

+

The expansion settings are for template-based models:

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Setting

Default value

Description

template_column

retro_template

The column in the template file that contains the templates.

cutoff_cumulative

0.995

The accumulative probability of the suggested templates is capped at this value. All other templates above this threshold are discarded.

cutoff_number

50

The maximum number of templates that will be returned from the expansion policy.

use_rdchiral

True

If True, will apply templates with RDChiral, otherwise RDKit will be used.

use_remote_models

False

If True, will try to connect to remote Tensorflow servers.

rescale_prior

False

If True, will apply a softmax function to the priors.

+

The filter settings are for quick-filter models:

+ +++++ + + + + + + + + + + + + + + + + + + + + +

Setting

Default value

Description

exclude_from_policy

[]

The list of names of the filter policies to exclude.

filter_cutoff

0.05

The cut-off for the quick-filter policy.

use_remote_models

False

If True, will try to connect to remote Tensorflow servers.

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/genindex.html b/genindex.html new file mode 100644 index 0000000..3bddb62 --- /dev/null +++ b/genindex.html @@ -0,0 +1,1680 @@ + + + + + + + + Index — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ + +

Index

+ +
+ A + | B + | C + | D + | E + | F + | G + | H + | I + | J + | L + | M + | N + | O + | P + | Q + | R + | S + | T + | U + | W + | X + | Y + | Z + +
+

A

+ + + +
+ +

B

+ + + +
+ +

C

+ + + +
+ +

D

+ + + +
+ +

E

+ + + +
+ +

F

+ + + +
+ +

G

+ + + +
+ +

H

+ + + +
+ +

I

+ + + +
+ +

J

+ + +
+ +

L

+ + + +
+ +

M

+ + + +
+ +

N

+ + + +
+ +

O

+ + +
+ +

P

+ + + +
+ +

Q

+ + +
+ +

R

+ + + +
+ +

S

+ + + +
+ +

T

+ + + +
+ +

U

+ + +
+ +

W

+ + + +
+ +

X

+ + +
+ +

Y

+ + +
+ +

Z

+ + +
+ + + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/gui.html b/gui.html new file mode 100644 index 0000000..7ec1dd6 --- /dev/null +++ b/gui.html @@ -0,0 +1,177 @@ + + + + + + + + + Graphical user interface — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

Graphical user interface

+

This tool to provide the possibility to perform the tree search on a single compound using a GUI +through a Jupyter notebook. If you are unfamiliar with notebooks, you find some introduction here.

+

To bring up the notebook, use

+
jupyter notebook
+
+
+

and browse to an existing notebook or create a new one.

+

Add these lines to the first cell in the notebook.

+
from aizynthfinder.interfaces import AiZynthApp
+app = AiZynthApp("/path/to/configfile.yaml")
+
+
+

where the AiZynthApp class needs to be instantiated with the path to a configuration file (see here).

+

To use the interface, follow these steps:

+
    +
  1. Executed the code in the cell (press Ctrl+Enter) and a simple GUI will appear

  2. +
  3. Enter the target SMILES and select stocks and policy model.

  4. +
  5. Press the Run Search button to perform the tree search.

  6. +
+_images/gui_input.png +
    +
  1. Press the Show Reactions to see the top-ranked routes

  2. +
+_images/gui_results.png +

You can also choose to select and sort the top-ranked routes based on another scoring function.

+
+

Creating the notebook

+

It is possible to create a notebook automatically with the aizynthapp tool

+
aizynthapp --config config_local.yml
+
+
+

which will also automatically opens up the created notebook.

+
+
+

Analysing the results

+

When the tree search has been finished. One can continue exploring the tree and extract output. +This is done by using the finder property of the app object. The finder holds a reference to an AiZynthFinder object.

+
finder = app.finder
+stats = finder.extract_statistics()
+
+
+
+
+

Clustering

+

There is a GUI extension to perform clustering of the routes. Enter the following a new cell

+
%matplotlib inline
+from aizynthfinder.interfaces.gui import ClusteringGui
+ClusteringGui.from_app(app)
+
+
+

A GUI like this will be shown, where you see the hierarchy of the routes and then can select how many +clusters you want to create.

+_images/gui_clustering.png +
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/howto.html b/howto.html new file mode 100644 index 0000000..4f701ff --- /dev/null +++ b/howto.html @@ -0,0 +1,188 @@ + + + + + + + + + How-to — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

How-to

+

This page outlines a few guidelines on some more advanced use-cases of AiZynthFinder or +frequently raised issues.

+
+

Using Retro*

+

AiZynthFinder implements other search algorithms than MCTS. This is an example of how Retro* can be used.

+

The search algorithm is specified in the configuration file.

+
search:
+  algorithm: aizynthfinder.search.retrostar.search_tree.SearchTree
+
+
+

This will use Retro* without a constant-valued oracle function. To specify the oracle function, you can +do

+
search:
+  algorithm: aizynthfinder.search.retrostar.search_tree.SearchTree
+  algorithm_config:
+    molecule_cost:
+      cost: aizynthfinder.search.retrostar.cost.RetroStarCost
+      model_path: retrostar_value_model.pickle
+      fingerprint_length: 2048
+      fingerprint_radius: 2
+      dropout_rate: 0.1
+
+
+

The pickle file can be downloaded from here

+
+
+

Using multiple expansion policies

+

AiZynthFinder can use multiple expansion policies. This gives an example how a general USPTO and a RingBreaker model +can be used together

+
expansion:
+  uspto:
+    - uspto_keras_model.hdf5
+    - uspto_unique_templates.csv.gz
+  ringbreaker:
+    - uspto_ringbreaker_keras_model.hdf5
+    - uspto_ringbreaker_unique_templates.csv.gz
+  multi_expansion_strategy:
+    type: aizynthfinder.context.policy.MultiExpansionStrategy
+    expansion_strategies: [uspto, ringbreaker]
+    additive_expansion: True
+
+
+

and then to use this with aizynthcli do something like this

+
aizynthcli --smiles smiles.txt --config config.yml --policy multi_expansion_strategy
+
+
+
+
+

Output more routes

+

The number of routes in the output of aizynthcli can be controlled from the configuration file.

+

This is how you can extract at least 25 routes but not more than 50 per target

+
post_processing:
+  min_routes: 25
+  max_routes: 50
+
+
+

Alternatively, you can extract all solved routes. If a target is unsolved, it will return the number +of routes specified by min_routes and max_routes.

+
post_processing:
+    min_routes: 5
+    max_routes: 10
+    all_routes: True
+
+
+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..f6acc38 --- /dev/null +++ b/index.html @@ -0,0 +1,164 @@ + + + + + + + + + aizynthfinder documentation — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder documentation

+

aizynthfinder is a tool for retrosynthetic planning. The default algorithm is based on a Monte Carlo tree search that recursively breaks down a molecule to purchasable precursors. The tree search is guided by a policy that suggests possible precursors by utilizing a neural network trained on a library of known reaction templates.

+
+

Introduction

+

You run retrosynthesis experiments you need a trained model and a stock collection. You can download a public available model based on USPTO and a stock collection from ZINC database.

+
download_public_data .
+
+
+

This will download the data to your current directory. The config.yml file can be used directly with the interfaces.

+

There are two main interfaces provided by the package:

+
+
    +
  • a script that performs tree search in batch mode and

  • +
  • an interface that is providing a GUI within a Jupyter notebook.

  • +
+
+

The GUI interface should be run in a Jupyter notebook. This is a simple example of the code in a Jupyter notebook cell.

+
from aizynthfinder.interfaces import AiZynthApp
+app = AiZynthApp("/path/to/configfile.yaml")
+
+
+

where the AiZynthApp class needs to be instantiated with the path to a configuration file (see here).

+

To use the interface, follow these steps:

+
+
    +
  1. Executed the code in the cell (press Ctrl+Enter) and a simple GUI will appear

  2. +
  3. Enter the target SMILES and select stocks and policy model.

  4. +
  5. Press the Run Search button to perform the tree search.

  6. +
  7. Press the Show Reactions to see the top-ranked routes

  8. +
+
+

The batch-mode script is called aizynthcli and can be executed like:

+
aizynthcli --config config.yml --smiles smiles.txt
+
+
+

where config.yml contains configurations such as paths to policy models and stocks (see here), and smiles.txt is a simple text +file with SMILES (one on each row).

+

If you just want to perform the tree search on a single molecule. You can directly specify it on the command-line +within quotes:

+
aizynthcli --config config.yml --smiles "COc1cccc(OC(=O)/C=C/c2cc(OC)c(OC)c(OC)c2)c1"
+
+
+

The output is some statistics about the tree search, the scores of the top-ranked routes, and the reaction tree +of the top-ranked routes. When smiles are provided in a text file the results are stored in a JSON file, +whereas if the SMILEs is provided on the command-line it is printed directly to the prompt +(except the reaction trees, which are written to a JSON file).

+
+
+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/modules.html b/modules.html new file mode 100644 index 0000000..dd1cd6e --- /dev/null +++ b/modules.html @@ -0,0 +1,190 @@ + + + + + + + + + aizynthfinder — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

aizynthfinder

+
+ +
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 0000000..68428d1 Binary files /dev/null and b/objects.inv differ diff --git a/py-modindex.html b/py-modindex.html new file mode 100644 index 0000000..7e57b18 --- /dev/null +++ b/py-modindex.html @@ -0,0 +1,433 @@ + + + + + + + + Python Module Index — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ + +

Python Module Index

+ +
+ a +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ a
+ aizynthfinder +
    + aizynthfinder.aizynthfinder +
    + aizynthfinder.analysis +
    + aizynthfinder.analysis.routes +
    + aizynthfinder.analysis.tree_analysis +
    + aizynthfinder.analysis.utils +
    + aizynthfinder.chem +
    + aizynthfinder.chem.mol +
    + aizynthfinder.chem.reaction +
    + aizynthfinder.chem.serialization +
    + aizynthfinder.context +
    + aizynthfinder.context.collection +
    + aizynthfinder.context.config +
    + aizynthfinder.context.policy +
    + aizynthfinder.context.policy.expansion_strategies +
    + aizynthfinder.context.policy.filter_strategies +
    + aizynthfinder.context.policy.policies +
    + aizynthfinder.context.policy.utils +
    + aizynthfinder.context.scoring +
    + aizynthfinder.context.scoring.collection +
    + aizynthfinder.context.scoring.scorers +
    + aizynthfinder.context.stock +
    + aizynthfinder.context.stock.queries +
    + aizynthfinder.context.stock.stock +
    + aizynthfinder.interfaces +
    + aizynthfinder.interfaces.aizynthapp +
    + aizynthfinder.interfaces.aizynthcli +
    + aizynthfinder.interfaces.gui +
    + aizynthfinder.interfaces.gui.clustering +
    + aizynthfinder.reactiontree +
    + aizynthfinder.search +
    + aizynthfinder.search.andor_trees +
    + aizynthfinder.search.breadth_first +
    + aizynthfinder.search.breadth_first.nodes +
    + aizynthfinder.search.breadth_first.search_tree +
    + aizynthfinder.search.dfpn +
    + aizynthfinder.search.dfpn.nodes +
    + aizynthfinder.search.dfpn.search_tree +
    + aizynthfinder.search.mcts +
    + aizynthfinder.search.mcts.node +
    + aizynthfinder.search.mcts.search +
    + aizynthfinder.search.mcts.state +
    + aizynthfinder.search.mcts.utils +
    + aizynthfinder.search.retrostar +
    + aizynthfinder.search.retrostar.cost +
    + aizynthfinder.search.retrostar.nodes +
    + aizynthfinder.search.retrostar.search_tree +
    + aizynthfinder.tools +
    + aizynthfinder.tools.cat_output +
    + aizynthfinder.tools.download_public_data +
    + aizynthfinder.tools.make_stock +
    + aizynthfinder.utils +
    + aizynthfinder.utils.exceptions +
    + aizynthfinder.utils.files +
    + aizynthfinder.utils.image +
    + aizynthfinder.utils.loading +
    + aizynthfinder.utils.logging +
    + aizynthfinder.utils.math +
    + aizynthfinder.utils.models +
    + aizynthfinder.utils.mongo +
    + aizynthfinder.utils.paths +
    + aizynthfinder.utils.type_utils +
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/python_interface.html b/python_interface.html new file mode 100644 index 0000000..e683377 --- /dev/null +++ b/python_interface.html @@ -0,0 +1,203 @@ + + + + + + + + + Python interface — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

Python interface

+

This page gives a quick example of how the tree search can be completed +by writing your own python interface. This is not recommended for most users.

+
    +
  1. Import the necessary class

  2. +
+
from aizynthfinder.aizynthfinder import AiZynthFinder
+
+
+
    +
  1. Instantiate that class by providing a configuration file.

  2. +
+
filename = "config.yml"
+finder = AiZynthFinder(configfile=filename)
+
+
+
    +
  1. Select stock and policy

  2. +
+
finder.stock.select("zinc")
+finder.expansion_policy.select("uspto")
+finder.filter_policy.select("uspto")
+
+
+

zinc and uspto where the keys given to the stock and the policy in the configuration file. +The first policy set is the expansion policy and the second is the filter policy. The filter policy is optional.

+
    +
  1. Set the target SMILES and perform the tree search

  2. +
+
finder.target_smiles = "Cc1cccc(c1N(CC(=O)Nc2ccc(cc2)c3ncon3)C(=O)C4CCS(=O)(=O)CC4)C"
+finder.tree_search()
+
+
+
    +
  1. Analyse the search tree and build routes

  2. +
+
finder.build_routes()
+stats = finder.extract_statistics()
+
+
+

The build_routes method needs to be called before any analysis can be done.

+
+

Expansion interface

+

There is an interface for the expansion policy as well. It can be used to break down a molecule into reactants.

+
filename = "config.yml"
+expander = AiZynthExpander(configfile=filename)
+expander.expansion_policy.select("uspto")
+expander.filter_policy.select("uspto")
+reactions = expander.do_expansion("Cc1cccc(c1N(CC(=O)Nc2ccc(cc2)c3ncon3)C(=O)C4CCS(=O)(=O)CC4)C")
+
+
+

for this, you only need to select the policies. The filter policy is optional and using it will only add the +feasibility of the reactions not filter it out.

+

The result is a nested list of FixedRetroReaction objects. This you can manipulate to for instance get +out all the reactants SMILES strings

+
reactants_smiles = []
+for reaction_tuple in reactions:
+    reactants_smiles.append([mol.smiles for mol in reaction_tuple[0].reactants[0])
+
+
+

or you can put all the metadata of all the reactions in a pandas dataframe

+
import pandas as pd
+metadata = []
+for reaction_tuple in reactions:
+    for reaction in reaction_tuple:
+        metadata.append(reaction.metadata)
+df = pd.DataFrame(metadata)
+
+
+
+
+

Further reading

+

The docstrings of all modules, classes and methods can be consulted here

+

and you can always find them in an interactive Python shell using for instance:

+
from aizynthfinder.chem import Molecule
+help(Molecule)
+help(Molecule.fingerprint)
+
+
+

If you are interested in the the relationships between the classes have a look here +and if you want to dig deeper in to the main algorithmic sequences have a look here

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/relationships.html b/relationships.html new file mode 100644 index 0000000..45cb044 --- /dev/null +++ b/relationships.html @@ -0,0 +1,134 @@ + + + + + + + + + Relationships — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

Relationships

+

This page shows some relationship diagrams, i.e. how the different objects are connect in a typical retrosynthesis +analysis using Monte Carlo tree search.

+

These are the tree different types of relationships used:

+_images/line-desc.png + +
+

Analysis / post-processing

+

This diagram explains how the different objects involved in the analysis of the search are connected.

+_images/analysis-rel.png +
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/scoring.html b/scoring.html new file mode 100644 index 0000000..f711b4e --- /dev/null +++ b/scoring.html @@ -0,0 +1,169 @@ + + + + + + + + + Scoring — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

Scoring

+

aizynthfinder is capable of scoring reaction routes, both in the form of MctsNode objects when a search tree is available, +and in the form of ReactionTree objects if post-processing is required.

+

Currently, there are a few scoring functions available

+
+
    +
  • State score - a function of the number of precursors in stock and the length of the route

  • +
  • Number of reactions - the number of steps in the route

  • +
  • Number of pre-cursors - the number of pre-cursors in the route

  • +
  • Number of pre-cursors in stock - the number of the pre-cursors that are purchaseable

  • +
  • Average template occurrence - the average occurrence of the templates used in the route

  • +
  • Sum of prices - the plain sum of the price of all pre-cursors

  • +
  • Route cost score - the cost of the synthesizing the route (Badowski et al. Chem Sci. 2019, 10, 4640)

  • +
+
+

The State score is the score that is guiding the tree search in the update phase, and +this is not configurable.

+

In the Jupyter notebook GUI one can choose to score the routes with any of the loaded the scorers.

+

The first four scoring functions are loaded automatically when an aizynthfinder object is created.

+
+

Add new scoring functions

+

Additional scoring functions can be implemented by inheriting from the class Scorer in the aizynthfinder.context.scoring.scorers module. +The scoring class needs to implement the _score_node, _score_reaction_tree and the __repr__ methods.

+

This is an example of that.

+
from aizynthfinder.context.scoring.scorers import Scorer
+
+class DeltaNumberOfTransformsScorer(Scorer):
+
+    def __repr__(self):
+        return "delta number of transforms"
+
+    def _score_node(self, node):
+        return self._config.max_transforms - node.state.max_transforms
+
+    def _score_reaction_tree(self, tree):
+        return self._config.max_transforms - len(list(tree.reactions()))
+
+
+

This can then be added to the scorers attribute of an aizynthfinderfinder object. The scorers attribute is a collection +of Scorer objects.

+

For instance to use this in the Jupyter notebook GUI, one can do

+
from aizynthfinder.interfaces import AiZynthApp
+app = AiZynthApp("config_local.yml", setup=False)
+scorer = DeltaNumberOfTransformsScorer(app.finder.config)
+app.finder.scorers.load(scorer)
+app.setup()
+
+
+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/search.html b/search.html new file mode 100644 index 0000000..7967d6c --- /dev/null +++ b/search.html @@ -0,0 +1,132 @@ + + + + + + + + Search — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +

Search

+ + + + +

+ Searching for multiple words only shows matches that contain + all words. +

+ + +
+ + + +
+ + + +
+ +
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/searchindex.js b/searchindex.js new file mode 100644 index 0000000..71f50a6 --- /dev/null +++ b/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({docnames:["aizynthfinder","aizynthfinder.analysis","aizynthfinder.chem","aizynthfinder.context","aizynthfinder.context.policy","aizynthfinder.context.scoring","aizynthfinder.context.stock","aizynthfinder.interfaces","aizynthfinder.interfaces.gui","aizynthfinder.search","aizynthfinder.search.breadth_first","aizynthfinder.search.dfpn","aizynthfinder.search.mcts","aizynthfinder.search.retrostar","aizynthfinder.tools","aizynthfinder.utils","cli","configuration","gui","howto","index","modules","python_interface","relationships","scoring","sequences","stocks"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":5,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":3,"sphinx.domains.rst":2,"sphinx.domains.std":2,sphinx:56},filenames:["aizynthfinder.rst","aizynthfinder.analysis.rst","aizynthfinder.chem.rst","aizynthfinder.context.rst","aizynthfinder.context.policy.rst","aizynthfinder.context.scoring.rst","aizynthfinder.context.stock.rst","aizynthfinder.interfaces.rst","aizynthfinder.interfaces.gui.rst","aizynthfinder.search.rst","aizynthfinder.search.breadth_first.rst","aizynthfinder.search.dfpn.rst","aizynthfinder.search.mcts.rst","aizynthfinder.search.retrostar.rst","aizynthfinder.tools.rst","aizynthfinder.utils.rst","cli.rst","configuration.rst","gui.rst","howto.rst","index.rst","modules.rst","python_interface.rst","relationships.rst","scoring.rst","sequences.rst","stocks.rst"],objects:{"":[[0,0,0,"-","aizynthfinder"]],"aizynthfinder.aizynthfinder":[[0,1,1,"","AiZynthExpander"],[0,1,1,"","AiZynthFinder"]],"aizynthfinder.aizynthfinder.AiZynthExpander":[[0,2,1,"","do_expansion"]],"aizynthfinder.aizynthfinder.AiZynthFinder":[[0,2,1,"","build_routes"],[0,2,1,"","extract_statistics"],[0,2,1,"","prepare_tree"],[0,2,1,"","stock_info"],[0,3,1,"","target_mol"],[0,3,1,"","target_smiles"],[0,2,1,"","tree_search"]],"aizynthfinder.analysis":[[1,0,0,"-","routes"],[1,0,0,"-","tree_analysis"],[1,0,0,"-","utils"]],"aizynthfinder.analysis.routes":[[1,1,1,"","RouteCollection"]],"aizynthfinder.analysis.routes.RouteCollection":[[1,2,1,"","cluster"],[1,2,1,"","combined_reaction_trees"],[1,2,1,"","compute_scores"],[1,2,1,"","dict_with_extra"],[1,2,1,"","dict_with_scores"],[1,3,1,"","dicts"],[1,2,1,"","distance_matrix"],[1,2,1,"","from_analysis"],[1,3,1,"","images"],[1,3,1,"","jsons"],[1,2,1,"","make_dicts"],[1,2,1,"","make_images"],[1,2,1,"","make_jsons"],[1,2,1,"","rescore"]],"aizynthfinder.analysis.tree_analysis":[[1,1,1,"","TreeAnalysis"]],"aizynthfinder.analysis.tree_analysis.TreeAnalysis":[[1,2,1,"","best"],[1,2,1,"","sort"],[1,2,1,"","tree_statistics"]],"aizynthfinder.analysis.utils":[[1,1,1,"","CombinedReactionTrees"],[1,1,1,"","RouteSelectionArguments"]],"aizynthfinder.analysis.utils.CombinedReactionTrees":[[1,2,1,"","to_dict"],[1,2,1,"","to_visjs_page"]],"aizynthfinder.analysis.utils.RouteSelectionArguments":[[1,4,1,"","nmax"],[1,4,1,"","nmin"],[1,4,1,"","return_all"]],"aizynthfinder.chem":[[2,0,0,"-","mol"],[2,0,0,"-","reaction"],[2,0,0,"-","serialization"]],"aizynthfinder.chem.mol":[[2,1,1,"","Molecule"],[2,1,1,"","TreeMolecule"],[2,1,1,"","UniqueMolecule"],[2,5,1,"","none_molecule"]],"aizynthfinder.chem.mol.Molecule":[[2,2,1,"","basic_compare"],[2,2,1,"","fingerprint"],[2,2,1,"","has_atom_mapping"],[2,3,1,"","inchi"],[2,3,1,"","inchi_key"],[2,3,1,"","index_to_mapping"],[2,2,1,"","make_unique"],[2,3,1,"","mapping_to_index"],[2,2,1,"","remove_atom_mapping"],[2,2,1,"","sanitize"],[2,3,1,"","weight"]],"aizynthfinder.chem.mol.TreeMolecule":[[2,3,1,"","mapped_atom_bonds"],[2,3,1,"","mapping_to_index"]],"aizynthfinder.chem.reaction":[[2,1,1,"","FixedRetroReaction"],[2,1,1,"","RetroReaction"],[2,1,1,"","SmilesBasedRetroReaction"],[2,1,1,"","TemplatedRetroReaction"],[2,5,1,"","hash_reactions"]],"aizynthfinder.chem.reaction.FixedRetroReaction":[[2,2,1,"","copy"],[2,2,1,"","to_smiles_based_retroreaction"]],"aizynthfinder.chem.reaction.RetroReaction":[[2,2,1,"","copy"],[2,2,1,"","from_serialization"],[2,2,1,"","mapped_reaction_smiles"],[2,3,1,"","reactants"],[2,3,1,"","smiles"],[2,2,1,"","to_dict"],[2,3,1,"","unqueried"]],"aizynthfinder.chem.reaction.SmilesBasedRetroReaction":[[2,2,1,"","to_dict"]],"aizynthfinder.chem.reaction.TemplatedRetroReaction":[[2,3,1,"","rd_reaction"],[2,2,1,"","to_dict"]],"aizynthfinder.chem.serialization":[[2,1,1,"","MoleculeDeserializer"],[2,1,1,"","MoleculeSerializer"],[2,5,1,"","deserialize_action"],[2,5,1,"","serialize_action"]],"aizynthfinder.chem.serialization.MoleculeDeserializer":[[2,2,1,"","get_tree_molecules"]],"aizynthfinder.chem.serialization.MoleculeSerializer":[[2,3,1,"","store"]],"aizynthfinder.context":[[3,0,0,"-","collection"],[3,0,0,"-","config"],[4,0,0,"-","policy"],[5,0,0,"-","scoring"],[6,0,0,"-","stock"]],"aizynthfinder.context.collection":[[3,1,1,"","ContextCollection"]],"aizynthfinder.context.collection.ContextCollection":[[3,2,1,"","deselect"],[3,3,1,"","items"],[3,2,1,"","load"],[3,2,1,"","load_from_config"],[3,2,1,"","select"],[3,2,1,"","select_all"],[3,2,1,"","select_first"],[3,2,1,"","select_last"],[3,3,1,"","selection"]],"aizynthfinder.context.config":[[3,1,1,"","Configuration"]],"aizynthfinder.context.config.Configuration":[[3,4,1,"","expansion_policy"],[3,4,1,"","filter_policy"],[3,2,1,"","from_dict"],[3,2,1,"","from_file"],[3,4,1,"","post_processing"],[3,4,1,"","scorers"],[3,4,1,"","search"],[3,4,1,"","stock"]],"aizynthfinder.context.policy":[[4,0,0,"-","expansion_strategies"],[4,0,0,"-","filter_strategies"],[4,0,0,"-","policies"],[4,0,0,"-","utils"]],"aizynthfinder.context.policy.expansion_strategies":[[4,1,1,"","ExpansionStrategy"],[4,1,1,"","MultiExpansionStrategy"],[4,1,1,"","TemplateBasedExpansionStrategy"]],"aizynthfinder.context.policy.expansion_strategies.ExpansionStrategy":[[4,2,1,"","get_actions"],[4,2,1,"","reset_cache"]],"aizynthfinder.context.policy.expansion_strategies.MultiExpansionStrategy":[[4,2,1,"","get_actions"]],"aizynthfinder.context.policy.expansion_strategies.TemplateBasedExpansionStrategy":[[4,2,1,"","get_actions"],[4,2,1,"","reset_cache"]],"aizynthfinder.context.policy.filter_strategies":[[4,1,1,"","FilterStrategy"],[4,1,1,"","QuickKerasFilter"],[4,1,1,"","ReactantsCountFilter"]],"aizynthfinder.context.policy.filter_strategies.FilterStrategy":[[4,2,1,"","apply"]],"aizynthfinder.context.policy.filter_strategies.QuickKerasFilter":[[4,2,1,"","apply"],[4,2,1,"","feasibility"]],"aizynthfinder.context.policy.filter_strategies.ReactantsCountFilter":[[4,2,1,"","apply"]],"aizynthfinder.context.policy.policies":[[4,1,1,"","ExpansionPolicy"],[4,1,1,"","FilterPolicy"]],"aizynthfinder.context.policy.policies.ExpansionPolicy":[[4,2,1,"","get_actions"],[4,2,1,"","load"],[4,2,1,"","load_from_config"],[4,2,1,"","reset_cache"]],"aizynthfinder.context.policy.policies.FilterPolicy":[[4,2,1,"","apply"],[4,2,1,"","load"],[4,2,1,"","load_from_config"]],"aizynthfinder.context.scoring":[[5,0,0,"-","collection"],[5,0,0,"-","scorers"]],"aizynthfinder.context.scoring.collection":[[5,1,1,"","ScorerCollection"]],"aizynthfinder.context.scoring.collection.ScorerCollection":[[5,2,1,"","create_default_scorers"],[5,2,1,"","load"],[5,2,1,"","load_from_config"],[5,2,1,"","make_subset"],[5,2,1,"","names"],[5,2,1,"","objects"],[5,2,1,"","score_vector"],[5,2,1,"","weighted_score"]],"aizynthfinder.context.scoring.scorers":[[5,1,1,"","AverageTemplateOccurrenceScorer"],[5,1,1,"","CombinedScorer"],[5,1,1,"","FractionInStockScorer"],[5,1,1,"","MaxTransformScorerer"],[5,1,1,"","MinMaxScaler"],[5,1,1,"","NumberOfPrecursorsInStockScorer"],[5,1,1,"","NumberOfPrecursorsScorer"],[5,1,1,"","NumberOfReactionsScorer"],[5,1,1,"","PriceSumScorer"],[5,1,1,"","ReactionClassMembershipScorer"],[5,1,1,"","RouteCostScorer"],[5,1,1,"","Scorer"],[5,1,1,"","SquashScaler"],[5,1,1,"","StateScorer"],[5,1,1,"","StockAvailabilityScorer"]],"aizynthfinder.context.scoring.scorers.AverageTemplateOccurrenceScorer":[[5,4,1,"","scorer_name"]],"aizynthfinder.context.scoring.scorers.FractionInStockScorer":[[5,4,1,"","scorer_name"]],"aizynthfinder.context.scoring.scorers.MaxTransformScorerer":[[5,4,1,"","scorer_name"]],"aizynthfinder.context.scoring.scorers.MinMaxScaler":[[5,4,1,"","max_val"],[5,4,1,"","min_val"],[5,4,1,"","reverse"],[5,4,1,"","scale_factor"]],"aizynthfinder.context.scoring.scorers.NumberOfPrecursorsInStockScorer":[[5,4,1,"","scorer_name"]],"aizynthfinder.context.scoring.scorers.NumberOfPrecursorsScorer":[[5,4,1,"","scorer_name"]],"aizynthfinder.context.scoring.scorers.NumberOfReactionsScorer":[[5,4,1,"","scorer_name"]],"aizynthfinder.context.scoring.scorers.PriceSumScorer":[[5,4,1,"","scorer_name"]],"aizynthfinder.context.scoring.scorers.RouteCostScorer":[[5,4,1,"","scorer_name"]],"aizynthfinder.context.scoring.scorers.Scorer":[[5,4,1,"","scorer_name"],[5,2,1,"","sort"]],"aizynthfinder.context.scoring.scorers.SquashScaler":[[5,4,1,"","slope"],[5,4,1,"","xoffset"],[5,4,1,"","yoffset"]],"aizynthfinder.context.scoring.scorers.StateScorer":[[5,4,1,"","scorer_name"]],"aizynthfinder.context.stock":[[6,0,0,"-","queries"],[6,0,0,"-","stock"]],"aizynthfinder.context.stock.queries":[[6,1,1,"","InMemoryInchiKeyQuery"],[6,1,1,"","MolbloomFilterQuery"],[6,1,1,"","MongoDbInchiKeyQuery"],[6,1,1,"","StockQueryMixin"]],"aizynthfinder.context.stock.queries.InMemoryInchiKeyQuery":[[6,2,1,"","price"],[6,3,1,"","stock_inchikeys"]],"aizynthfinder.context.stock.queries.MongoDbInchiKeyQuery":[[6,2,1,"","availability_string"]],"aizynthfinder.context.stock.queries.StockQueryMixin":[[6,2,1,"","amount"],[6,2,1,"","availability_string"],[6,2,1,"","cached_search"],[6,2,1,"","clear_cache"],[6,2,1,"","price"]],"aizynthfinder.context.stock.stock":[[6,1,1,"","Stock"]],"aizynthfinder.context.stock.stock.Stock":[[6,2,1,"","amount"],[6,2,1,"","availability_list"],[6,2,1,"","availability_string"],[6,2,1,"","exclude"],[6,2,1,"","load"],[6,2,1,"","load_from_config"],[6,2,1,"","price"],[6,2,1,"","reset_exclusion_list"],[6,2,1,"","select"],[6,2,1,"","set_stop_criteria"],[6,2,1,"","smiles_in_stock"],[6,3,1,"","stop_criteria"]],"aizynthfinder.interfaces":[[7,0,0,"-","aizynthapp"],[7,0,0,"-","aizynthcli"],[8,0,0,"-","gui"]],"aizynthfinder.interfaces.aizynthapp":[[7,1,1,"","AiZynthApp"],[7,5,1,"","main"]],"aizynthfinder.interfaces.aizynthapp.AiZynthApp":[[7,2,1,"","setup"]],"aizynthfinder.interfaces.aizynthcli":[[7,5,1,"","main"]],"aizynthfinder.interfaces.gui":[[8,0,0,"-","clustering"]],"aizynthfinder.interfaces.gui.clustering":[[8,1,1,"","ClusteringGui"]],"aizynthfinder.interfaces.gui.clustering.ClusteringGui":[[8,2,1,"","from_app"]],"aizynthfinder.reactiontree":[[0,1,1,"","ReactionTree"],[0,1,1,"","ReactionTreeFromDict"],[0,1,1,"","ReactionTreeFromExpansion"],[0,1,1,"","ReactionTreeLoader"]],"aizynthfinder.reactiontree.ReactionTree":[[0,2,1,"","depth"],[0,2,1,"","distance_to"],[0,2,1,"","from_dict"],[0,2,1,"","hash_key"],[0,2,1,"","in_stock"],[0,2,1,"","is_branched"],[0,2,1,"","leafs"],[0,3,1,"","metadata"],[0,2,1,"","molecules"],[0,2,1,"","reactions"],[0,2,1,"","subtrees"],[0,2,1,"","to_dict"],[0,2,1,"","to_image"],[0,2,1,"","to_json"]],"aizynthfinder.search":[[9,0,0,"-","andor_trees"],[10,0,0,"-","breadth_first"],[11,0,0,"-","dfpn"],[12,0,0,"-","mcts"],[13,0,0,"-","retrostar"]],"aizynthfinder.search.andor_trees":[[9,1,1,"","AndOrSearchTreeBase"],[9,1,1,"","ReactionTreeFromAndOrTrace"],[9,1,1,"","SplitAndOrTree"],[9,1,1,"","TreeNodeMixin"]],"aizynthfinder.search.andor_trees.AndOrSearchTreeBase":[[9,3,1,"","mol_nodes"],[9,2,1,"","one_iteration"],[9,2,1,"","routes"]],"aizynthfinder.search.andor_trees.TreeNodeMixin":[[9,3,1,"","children"],[9,3,1,"","prop"]],"aizynthfinder.search.breadth_first":[[10,0,0,"-","nodes"],[10,0,0,"-","search_tree"]],"aizynthfinder.search.breadth_first.nodes":[[10,1,1,"","MoleculeNode"],[10,1,1,"","ReactionNode"]],"aizynthfinder.search.breadth_first.nodes.MoleculeNode":[[10,2,1,"","add_stub"],[10,2,1,"","ancestors"],[10,3,1,"","children"],[10,2,1,"","create_root"],[10,2,1,"","from_dict"],[10,3,1,"","prop"],[10,2,1,"","serialize"]],"aizynthfinder.search.breadth_first.nodes.ReactionNode":[[10,3,1,"","children"],[10,2,1,"","create_stub"],[10,2,1,"","from_dict"],[10,3,1,"","prop"],[10,2,1,"","serialize"]],"aizynthfinder.search.breadth_first.search_tree":[[10,1,1,"","SearchTree"]],"aizynthfinder.search.breadth_first.search_tree.SearchTree":[[10,2,1,"","from_json"],[10,3,1,"","mol_nodes"],[10,2,1,"","one_iteration"],[10,2,1,"","routes"],[10,2,1,"","serialize"]],"aizynthfinder.search.dfpn":[[11,0,0,"-","nodes"],[11,0,0,"-","search_tree"]],"aizynthfinder.search.dfpn.nodes":[[11,1,1,"","MoleculeNode"],[11,1,1,"","ReactionNode"]],"aizynthfinder.search.dfpn.nodes.MoleculeNode":[[11,2,1,"","create_root"],[11,2,1,"","expand"],[11,2,1,"","promising_child"],[11,3,1,"","prop"],[11,2,1,"","update"]],"aizynthfinder.search.dfpn.nodes.ReactionNode":[[11,3,1,"","children"],[11,3,1,"","disproven"],[11,2,1,"","expand"],[11,2,1,"","promising_child"],[11,3,1,"","prop"],[11,3,1,"","proven"],[11,2,1,"","update"]],"aizynthfinder.search.dfpn.search_tree":[[11,1,1,"","SearchTree"]],"aizynthfinder.search.dfpn.search_tree.SearchTree":[[11,3,1,"","mol_nodes"],[11,2,1,"","one_iteration"],[11,2,1,"","routes"]],"aizynthfinder.search.mcts":[[12,0,0,"-","node"],[12,0,0,"-","search"],[12,0,0,"-","state"],[12,0,0,"-","utils"]],"aizynthfinder.search.mcts.node":[[12,1,1,"","MctsNode"]],"aizynthfinder.search.mcts.node.MctsNode":[[12,2,1,"","actions_to"],[12,2,1,"","backpropagate"],[12,3,1,"","children"],[12,2,1,"","children_view"],[12,2,1,"","create_root"],[12,2,1,"","expand"],[12,2,1,"","from_dict"],[12,3,1,"","is_solved"],[12,2,1,"","is_terminal"],[12,3,1,"","parent"],[12,2,1,"","path_to"],[12,2,1,"","promising_child"],[12,2,1,"","serialize"],[12,3,1,"","state"],[12,2,1,"","to_reaction_tree"]],"aizynthfinder.search.mcts.search":[[12,1,1,"","MctsSearchTree"]],"aizynthfinder.search.mcts.search.MctsSearchTree":[[12,2,1,"","backpropagate"],[12,2,1,"","from_json"],[12,2,1,"","graph"],[12,2,1,"","nodes"],[12,2,1,"","one_iteration"],[12,2,1,"","select_leaf"],[12,2,1,"","serialize"]],"aizynthfinder.search.mcts.state":[[12,1,1,"","MctsState"]],"aizynthfinder.search.mcts.state.MctsState":[[12,2,1,"","from_dict"],[12,2,1,"","serialize"],[12,3,1,"","stock_availability"],[12,2,1,"","to_image"]],"aizynthfinder.search.mcts.utils":[[12,1,1,"","ReactionTreeFromSuperNode"],[12,5,1,"","route_to_node"]],"aizynthfinder.search.retrostar":[[13,0,0,"-","cost"],[13,0,0,"-","nodes"],[13,0,0,"-","search_tree"]],"aizynthfinder.search.retrostar.cost":[[13,1,1,"","MoleculeCost"],[13,1,1,"","RetroStarCost"],[13,1,1,"","ZeroMoleculeCost"]],"aizynthfinder.search.retrostar.cost.RetroStarCost":[[13,2,1,"","calculate"]],"aizynthfinder.search.retrostar.cost.ZeroMoleculeCost":[[13,2,1,"","calculate"]],"aizynthfinder.search.retrostar.nodes":[[13,1,1,"","MoleculeNode"],[13,1,1,"","ReactionNode"]],"aizynthfinder.search.retrostar.nodes.MoleculeNode":[[13,2,1,"","add_stub"],[13,2,1,"","ancestors"],[13,3,1,"","children"],[13,2,1,"","close"],[13,2,1,"","create_root"],[13,2,1,"","from_dict"],[13,3,1,"","prop"],[13,2,1,"","serialize"],[13,3,1,"","target_value"],[13,2,1,"","update"]],"aizynthfinder.search.retrostar.nodes.ReactionNode":[[13,3,1,"","children"],[13,2,1,"","create_stub"],[13,2,1,"","from_dict"],[13,3,1,"","prop"],[13,2,1,"","serialize"],[13,2,1,"","update"]],"aizynthfinder.search.retrostar.search_tree":[[13,1,1,"","SearchTree"]],"aizynthfinder.search.retrostar.search_tree.SearchTree":[[13,2,1,"","from_json"],[13,3,1,"","mol_nodes"],[13,2,1,"","one_iteration"],[13,2,1,"","routes"],[13,2,1,"","serialize"]],"aizynthfinder.tools":[[14,0,0,"-","cat_output"],[14,0,0,"-","download_public_data"],[14,0,0,"-","make_stock"]],"aizynthfinder.tools.cat_output":[[14,5,1,"","main"]],"aizynthfinder.tools.download_public_data":[[14,5,1,"","main"]],"aizynthfinder.tools.make_stock":[[14,5,1,"","extract_plain_smiles"],[14,5,1,"","extract_smiles_from_module"],[14,5,1,"","main"],[14,5,1,"","make_hdf5_stock"],[14,5,1,"","make_molbloom"],[14,5,1,"","make_molbloom_inchi"],[14,5,1,"","make_mongo_stock"]],"aizynthfinder.utils":[[15,0,0,"-","exceptions"],[15,0,0,"-","files"],[15,0,0,"-","image"],[15,0,0,"-","loading"],[15,0,0,"-","logging"],[15,0,0,"-","math"],[15,0,0,"-","models"],[15,0,0,"-","mongo"],[15,0,0,"-","paths"],[15,0,0,"-","type_utils"]],"aizynthfinder.utils.exceptions":[[15,6,1,"","CostException"],[15,6,1,"","ExternalModelAPIError"],[15,6,1,"","MoleculeException"],[15,6,1,"","NodeUnexpectedBehaviourException"],[15,6,1,"","PolicyException"],[15,6,1,"","RejectionException"],[15,6,1,"","ScorerException"],[15,6,1,"","StockException"],[15,6,1,"","TreeAnalysisException"]],"aizynthfinder.utils.files":[[15,5,1,"","cat_datafiles"],[15,5,1,"","cat_hdf_files"],[15,5,1,"","read_datafile"],[15,5,1,"","save_datafile"],[15,5,1,"","split_file"],[15,5,1,"","start_processes"]],"aizynthfinder.utils.image":[[15,1,1,"","RouteImageFactory"],[15,5,1,"","crop_image"],[15,5,1,"","draw_rounded_rectangle"],[15,5,1,"","make_visjs_page"],[15,5,1,"","molecule_to_image"],[15,5,1,"","molecules_to_images"],[15,5,1,"","save_molecule_images"]],"aizynthfinder.utils.loading":[[15,5,1,"","load_dynamic_class"]],"aizynthfinder.utils.logging":[[15,5,1,"","logger"],[15,5,1,"","setup_logger"]],"aizynthfinder.utils.math":[[15,5,1,"","softmax"]],"aizynthfinder.utils.models":[[15,1,1,"","ExternalModelViaGRPC"],[15,1,1,"","ExternalModelViaREST"],[15,1,1,"","LocalKerasModel"],[15,1,1,"","LocalOnnxModel"],[15,5,1,"","load_model"]],"aizynthfinder.utils.models.ExternalModelViaGRPC":[[15,2,1,"","predict"]],"aizynthfinder.utils.models.ExternalModelViaREST":[[15,2,1,"","predict"]],"aizynthfinder.utils.models.LocalKerasModel":[[15,2,1,"","predict"]],"aizynthfinder.utils.models.LocalOnnxModel":[[15,2,1,"","predict"]],"aizynthfinder.utils.mongo":[[15,5,1,"","get_mongo_client"]],"aizynthfinder.utils.paths":[[15,5,1,"","data_path"],[15,5,1,"","package_path"]],aizynthfinder:[[0,0,0,"-","aizynthfinder"],[1,0,0,"-","analysis"],[2,0,0,"-","chem"],[3,0,0,"-","context"],[7,0,0,"-","interfaces"],[0,0,0,"-","reactiontree"],[9,0,0,"-","search"],[14,0,0,"-","tools"],[15,0,0,"-","utils"]]},objnames:{"0":["py","module","Python module"],"1":["py","class","Python class"],"2":["py","method","Python method"],"3":["py","property","Python property"],"4":["py","attribute","Python attribute"],"5":["py","function","Python function"],"6":["py","exception","Python exception"]},objtypes:{"0":"py:module","1":"py:class","2":"py:method","3":"py:property","4":"py:attribute","5":"py:function","6":"py:exception"},terms:{"0":[1,2,5,16,17,19,22,26],"03d":16,"05":17,"1":[2,5,17,19,25],"10":[5,19,24,26],"100":[6,15,17],"12":9,"120":17,"2":[1,2,6,19,25],"20":15,"2019":[5,24],"2020":9,"2048":[2,19],"25":[1,17,19],"25000":9,"27017":15,"3":1,"300":15,"4":[6,17],"4640":[5,24],"5":[0,1,6,15,17,19],"50":[17,19],"52":9,"6":[6,12,17],"8":5,"995":17,"abstract":[2,3,4,5,9],"boolean":4,"break":[20,22,25],"case":[6,15,17,19],"class":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,18,20,22,24,25,26],"default":[0,1,2,3,4,5,6,7,10,11,12,13,15,16,17,20,26],"do":[0,1,5,19,24,25],"export":[25,26],"final":25,"float":[0,1,2,4,5,6,12,13],"function":[0,1,2,4,5,8,14,15,17,18,19],"import":[7,15,16,18,20,22,24,26],"int":[0,1,2,5,9,12,14,15],"new":[0,1,2,5,10,11,12,13,14,15,18,25],"public":[0,12,14,20],"return":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,19,24,25,26],"short":17,"true":[0,1,2,3,4,5,6,7,10,11,12,13,15,17,19,25],"try":[15,17],"while":25,A:[2,4,6,9,12,13,15,16,17,18,25],AND:[9,10,11,13],And:3,By:[16,26],For:[5,24,25],If:[0,1,2,3,4,5,6,11,12,13,15,16,17,18,19,20,22,25,26],In:[16,17,24,25,26],It:[1,2,16,18,22],Not:6,OR:[9,10,11,13],One:[3,6,18],That:16,The:[0,1,2,3,4,5,6,9,12,13,14,15,16,17,18,19,20,22,24,25,26],Then:26,There:[18,20,22],These:[17,23,25],To:[1,16,18,19,20,26],Will:[2,12],_:[3,15],__contains__:26,__init__:2,__repr__:24,_appli:2,_config:24,_make_smil:2,_postprocessingconfigur:3,_reactioninterfacemixin:2,_score_nod:24,_score_reaction_tre:24,_scoreabl:5,_searchconfigur:3,_striter:14,_supernod:11,abc:[0,2,3,4,5,9],about:[16,20,25],abov:[17,25],accept:[3,6],access:[1,9,12],accumul:[4,17],action:[2,4,12,15,25],actions_to:12,actual:[0,5,12,25],ad:[0,1,10,12,13,24,26],adapt:5,add:[4,5,6,10,12,13,18,22,25,26],add_stub:[10,13],addit:[0,1,4,17,24],additive_expans:[4,19],adjust:5,advanc:[7,19],after:[0,13],again:25,agre:15,aid:25,aizynth:15,aizynthapp:[0,8,18,20,21,24,26],aizynthcli:[0,16,19,20,21,26],aizynthexpand:[0,22],aizynthfind:[16,18,19,22,24,25,26],aizynthfinderfind:24,al:[5,9,24],algorithm:[1,9,10,11,12,13,17,19,20,22,25],algorithm_config:[13,17,19],all:[0,1,2,3,4,5,6,10,12,13,14,15,16,17,19,22,24,25,26],all_rout:[17,19],all_scor:1,all_tre:16,allow:6,along:[1,5,17],alreadi:12,also:[0,1,3,5,15,16,17,18,25],altern:[19,26],although:12,alwai:22,among:3,amount:[6,26],an:[0,1,2,3,4,5,6,9,10,11,12,13,14,15,16,17,18,19,20,22,24,26],an_item:3,analys:[15,22],analysi:[0,21,22],analyz:7,ancestor:[10,13],andor_tre:[0,10,11,13,21],andorsearchtreebas:[1,9,10,11,13],ani:[0,1,2,3,4,5,6,9,12,13,15,17,22,24,25,26],anoth:[0,1,2,18],anyth:25,api:[0,15],app:[7,8,18,20,24,26],appear:[18,20],append:[3,5,6,22],appli:[2,4,5,12,17,25],applic:[2,7,12,25],approx_mol:14,approxim:14,ar:[0,1,2,3,4,5,6,9,10,12,13,14,15,16,17,18,20,22,23,24,25,26],arc_siz:15,archiv:15,arg:[0,9,12,15],argument:[0,1,2,5,6,14,15,16,26],around:[0,1,2,15],artifici:4,assign:[13,17],associ:[2,25],atom:[2,6],attribut:[0,1,9,12,24],auto:5,automat:[16,18,24,26],avail:[0,1,3,5,6,12,13,16,17,20,24],availability_list:6,availability_str:6,averag:[5,24],average_yield:5,averagetemplateoccurrencescor:5,avoid:[1,9],awesomescor:5,axi:5,b:26,background:15,backpropag:[12,25],badowski:[5,24],balanc:17,ball:[1,15],bar:0,base:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,15,17,18,20,26],basenam:15,bash:26,basic:[2,7],basic_compar:2,batch:[16,20],becaus:0,been:[0,2,12,16,17,18,25],befor:[2,22],behav:15,being:[5,13,25],below:[16,17,25],best:1,between:[0,1,5,12,15,22,25],bias:13,big:5,bipartit:[0,1,15],bit:[13,14],bloom:[6,14],bond:2,bool:[0,1,2,3,4,5,6,7,9,10,11,12,13,15],both:[0,8,12,24,26],bound:[9,17,25],branch:0,breadth:10,breadth_first:[0,9],bring:18,broken:[0,17],brows:18,build:[0,22],build_rout:[0,22],button:[18,20],buyabl:17,c1:20,c1n:22,c2:20,c2cc:20,c3ncon3:22,c4cc:22,c:[6,17,20,22,26],cach:[1,4,6,12],cache_molecul:4,cached_search:6,calcul:[0,5,6,13,16],call:[0,2,4,5,6,13,15,16,17,20,22,25],callabl:[0,2,15],can:[0,1,2,3,4,5,6,9,12,13,15,16,17,18,19,20,22,24,25,26],cannot:[5,6],cap:17,capabl:24,care:[2,25],carlo:[20,23],carri:25,cat_aizynth_output:14,cat_datafil:15,cat_hdf_fil:15,cat_output:[0,21],caus:2,cc1cccc:22,cc2:22,cc4:22,cc:22,ccc:13,ccco:2,cco:6,cell:[18,20],certif:15,chang:25,check:[0,4,5,6,15,17],checkpoint:[15,16,17],chem:[0,5,6,21,22,24,26],chemic:2,cheminf:9,chemistri:2,child:[2,11,12,13,25],children:[0,1,2,9,10,11,12,13,16,17,25],children_attr:12,children_view:12,chiral:[2,4],chiral_fingerprint:4,choos:[18,24],classif:5,classmethod:[0,1,2,3,8,10,11,12,13],classnam:[15,17],clear:6,clear_cach:6,cli:[7,14],client:[6,15,26],close:13,cluster:[0,1,7,16],clusteringgui:[8,18],clutter:1,cmd_callback:15,coc1cccc:20,code:[0,9,15,18,20],collect:[0,1,4,6,14,15,20,21,24,26],colon:16,color:[0,1,15],column:[4,6,15,16,17],com:26,combin:[1,4,5],combinatori:9,combined_reaction_tre:1,combinedreactiontre:1,combinedscor:5,comma:[6,16],command:[7,15,20,26],compar:[0,2,12],compil:15,complet:[0,3,15,16,17,22],compound:[14,16,18,26],compret:9,comput:[1,2,4,5,6,12,13,15,17],compute_scor:1,concaten:[14,15],condit:[4,25],confid:[17,25],config:[0,4,5,6,9,10,11,12,13,16,17,18,19,20,21,22,24],config_loc:[16,18,24,26],configdict:0,configfil:[0,7,18,20,22,26],configur:[0,3,4,5,6,7,9,10,11,12,13,15,16,18,19,20,22,24,26],connect:[2,4,15,17,23],consid:[1,4,17],consist:[0,12],consol:15,console_level:15,constant:19,construct:12,consult:22,contain:[0,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,20,25,26],content:[17,21],context:[0,19,21,24,26],contextcollect:[3,4,5,6],continu:[18,25,26],contrib:26,control:[5,19],conveni:0,convert:[1,2],copi:[0,2,5,6,12],corner:15,correspond:[2,3,16],cost:[0,5,9,10,15,19,24],costexcept:15,could:[2,5,6,12,17,25],count:[2,6,25,26],creat:[0,1,2,7,8,9,10,11,12,13,15,16,17,24,25,26],create_default_scor:5,create_root:[10,11,12,13],create_stub:[10,13],created_at_iter:0,creation:[0,2],criteria:[0,1,6],criteriastock:26,crop:15,crop_imag:15,csv:[6,17,19],ctrl:[18,20],current:[1,3,6,12,13,20,24,25,26],cursor:[1,5,24],curv:5,custom:[0,14,15,17],custom_model:[4,6,13],custom_packag:[4,6,13],custom_stock:26,customclass:[4,6,13],cut:[4,17],cutoff:4,cutoff_cumul:[4,17],cutoff_numb:[4,17],cycl:17,data:[2,4,14,15,16,20],data_path:15,databas:[6,14,20],database_nam:26,datafil:15,datafram:[6,14,15,16,22],dataset:6,de:3,deal:2,deeper:22,def:[24,26],default_cost:5,default_modul:15,default_prior:17,default_scor:5,defin:[0,1,5,12],del:3,delet:[0,3],delta:[13,24],deltanumberoftransformsscor:24,depend:25,depth:[0,2,10,11,17],descend:5,descript:[16,17,25],deselect:3,deseri:[2,10,11,12,13],deserialize_act:2,desir:1,detail:[9,16,17],determin:[0,1,2,4,15,17],df:22,dfpn:[0,9],diagram:[23,25],dicitionari:0,dict:[1,2,6,10,12,13,15],dict_:[2,10,12,13],dict_with_extra:1,dict_with_scor:1,dictionari:[0,1,2,3,5,6,9,10,11,12,13,15,16],differ:[1,4,6,15,23,25,26],dig:22,digraph:[12,15],direct:[5,12],directli:[0,3,5,20],directori:[15,20,26],disabl:15,disc:15,discard:17,displai:7,disproof:11,disproven:11,distanc:[0,1,16,17],distance_matrix:1,distance_to:0,distances_model:1,divers:15,dn:11,dn_threshold:11,do_expans:[0,22],docstr:22,document:[6,26],doe:[2,3,5,11,25],done:[2,5,7,18,22,25],dot:2,down:[0,17,20,22,25],download:[14,19,20,26],download_public_data:[0,20,21],draw:15,draw_rounded_rectangl:15,dropout_r:[13,19],dummi:4,dynam:15,e:[0,5,10,11,12,13,23,25],each:[0,1,5,6,11,12,13,14,15,16,20,25,26],easiest:26,edg:[12,15],edit:0,effici:12,either:[0,4,12,15,25,26],elaps:16,element:[2,14,26],emolecul:17,empti:[2,6],enabl:16,encapsul:[0,1,2,3,9,10,11,12,13],end:12,enter:[18,20,25],entri:[1,6,7,14],enumer:[16,26],environ:[3,17],equal:[0,2,5,17],error:15,estim:12,et:[5,9,24],etc:[2,16],evalu:2,even:12,exact:2,exampl:[6,16,17,19,20,22,24,26],except:[0,2,5,16,20,21],exception_cl:15,exclud:[6,13,17],exclude_from_polici:17,exclude_target_from_stock:17,exclus:6,execut:[0,15,18,20,25],exemplifi:17,exhaust:10,exist:[2,3,4,18,25,26],exit:16,expand:[0,4,10,11,12,13,17,22,25],expandable_mol:12,expandables_hash:12,expans:[0,4,10,11,12,13,16,17,25],expansion_polici:[0,3,22],expansion_strategi:[0,3,19],expansion_strategy_kei:4,expansionpolici:[3,4],expansionstrategi:4,expect:[3,4],experi:20,explain:[23,25],exploit:17,explor:[11,17,18],explos:9,expos:[9,10,11,13],extens:[8,18],extern:15,externalmodelapierror:15,externalmodelviagrpc:15,externalmodelviarest:15,extra:2,extract:[0,8,9,10,11,13,14,16,17,18,19,25,26],extract_plain_smil:14,extract_smil:[14,26],extract_smiles_from_modul:14,extract_statist:[0,18,22],f:16,fact:2,factor:5,factori:[0,3,15],fail:[2,25],failur:15,fallback:15,fals:[0,1,2,3,4,6,12,15,17,24,25,26],feasibl:[4,22],few:[19,24,25],field:[6,26],file1:26,file2:26,file:[0,3,4,5,6,7,10,12,13,14,16,18,19,20,21,22,26],file_level:15,filenam:[1,3,10,12,13,14,15,16,22,26],fileobj:26,filter:[0,4,6,11,14,16,17,22,25],filter_cutoff:[4,17],filter_func:0,filter_polici:[0,3,22],filter_s:14,filter_strategi:[0,3],filterpolici:[3,4],filterstrategi:4,find:[6,11,16,18,22],finder:[7,18,22,24,26],fingerprint:[2,4,13,22],fingerprint_length:[13,19],fingerprint_radiu:[13,19],finish:18,first:[1,3,10,11,13,14,15,16,18,22,24,25,26],first_solution_iter:16,first_solution_tim:16,fix:2,fixedretroreact:[0,2,15,22],flag:[16,25],flow:25,folder:15,follow:[13,16,17,18,20,25],form:[2,5,16,24,25],format:[0,1,4,6,13,15,17,26],formula:2,forward:15,found:[5,6,10,11,12,13,16,17],four:24,fourth:25,fraction:5,fractioninstockscor:5,frame:15,frame_color:15,framecolor:[0,1,15],frequenc:15,frequent:19,from:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,22,24,25,26],from_analysi:1,from_app:[8,18],from_dict:[0,3,10,12,13,16],from_fil:3,from_json:[10,12,13],from_mol:13,from_nod:12,from_seri:2,frontier:[10,11,13],full:[0,17],fulli:26,furthermor:25,g:[0,5],gener:[0,16,19],get:[2,4,5,15,22],get_act:4,get_mongo_cli:15,get_tree_molecul:2,give:[5,10,11,12,13,16,19,22],given:[0,1,2,3,4,5,6,12,14,15,16,22],global:15,go:12,graph:[0,1,12,15],green:[0,1,15],group:[0,1,17],grpc:15,gui:[0,7,18,20,24,26],guid:[20,24],guidelin:19,gz:[14,16,17,19],gzip:15,h:16,ha:[0,1,2,12,17,18,25,26],had:12,has_atom_map:2,hash:[0,2,12],hash_kei:0,hash_react:2,hashabl:[2,12],have:[1,6,12,16,17,22,25,26],hdf5:[4,6,14,15,16,17,19,26],header:26,heavyatomcount:26,help:[16,22],helper:[1,2,4,8,15],henc:[0,2,10,11,13],here:[16,17,18,19,20,22],herein:1,hidden:[0,15],hierarch:15,hierarchi:18,highest:[1,12,25],hold:[1,10,11,12,13,18],host:[6,14,15,26],how:[1,16,18,22,23,25],howev:[12,26],html:[1,15],i:[0,10,11,12,13,23,25,26],id:2,idx:2,ignor:2,imag:[0,1,12,16,21],imagefil:16,img:15,immedi:[2,12,17],immediate_instanti:17,immut:12,implement:[0,2,3,4,6,9,13,15,19,24,26],in_set_scor:5,in_stock:[0,10,11,13],in_stock_color:[0,1,15],in_stock_list:12,inchi:[2,6,12,14,17,26],inchi_kei:[2,6,14,17,26],inchi_key_col:6,inchikei:6,inchiset:17,includ:[0,2,3,15,16],include_metadata:[0,1],include_scor:1,increas:2,index:[1,2,5,15],index_to_map:2,indic:2,indirectli:25,individu:[1,3,6],inform:[0,1,2,16,25],inherit:[2,24],init:25,init_arg:2,initi:[0,1,2,4,5,6,25],inlin:18,inmemoryinchikeyqueri:6,inner:25,input:[5,13,14,15,16,26],input_fil:15,insert:0,instanc:[2,5,6,7,15,22,24,26],instanti:[0,2,4,5,6,7,9,12,13,17,18,20,22,25],instead:[6,17],intend:0,interact:22,interest:22,interfac:[0,2,4,5,6,12,17,20,21,24,26],intern:[6,12],interpret:26,introduct:18,involv:23,is_branch:0,is_expand:[12,25],is_solv:[0,12,16,25],is_termin:[12,25],isn:[4,26],issu:19,item:[1,3,4,5,6,13,15],iter:[0,2,9,10,11,12,13,14,16,17,25],iteration_limit:17,itre:16,its:[12,13,16],j:9,jaccard:0,js:[1,15],json:[0,1,10,12,13,14,15,16,20,25],jupyt:[7,18,20,24,26],just:[15,20,26],keep:2,kei:[0,1,2,3,4,5,6,12,14,15,17,22,26],kept:0,kera:[15,17],keyerror:3,known:[20,26],kwarg:[0,1,2,4,9,12,13,15],label:[1,4],larg:25,larger:1,last:3,lastli:26,later:4,latest:0,layer:[13,15],layout:15,lazi:2,lazili:12,lead:12,leaf:[0,12,25],least:[1,2,3,6,13,19,26],leav:0,len:[6,15,24],length:[0,2,4,5,15,24],less:1,let:17,level:[1,15],librari:[1,15,17,20],like:[1,5,12,16,17,18,19,20,26],limit:26,line:[14,18,20,26],lipinski:26,list:[0,1,2,3,4,5,6,9,10,11,12,13,14,15,16,17,22,24,25],literatur:25,load:[0,3,4,5,6,14,17,21,24,26],load_dynamic_class:15,load_from_config:[3,4,5,6],load_model:15,load_zinc:26,local:15,localhost:[15,26],localkerasmodel:15,localonnxmodel:15,locat:26,log:[0,16,21],log_prefix:15,log_to_fil:16,logger:15,look:[6,22],lookup:[6,26],loop:25,loos:5,lose:0,lstm:1,m:13,made:13,mai:3,main:[0,7,14,20,22],mainli:0,make:[0,5,14],make_dict:1,make_hdf5_stock:14,make_imag:1,make_json:1,make_molbloom:14,make_molbloom_inchi:14,make_mongo_stock:14,make_stock:[0,21],make_subset:5,make_uniqu:2,make_visjs_pag:15,manag:15,mani:18,manipul:[1,22],map:2,mapped_atom_bond:2,mapped_mol:2,mapped_reaction_smil:2,mapped_smil:2,mapping_to_index:2,mapping_update_callback:2,margin:15,mark:[0,12,15],mask:11,materi:[5,16],math:[0,21],matplotlib:18,matrix:1,matur:[10,11,13],max:5,max_children:16,max_clust:1,max_rout:[9,17,19],max_transform:[12,16,17,24],max_val:5,maximum:[0,1,4,5,6,9,12,16,17,25,26],maxtransformscorer:5,mct:[0,1,5,9,16,17,19],mcts_group:17,mctsnode:[1,12,24,25],mctssearchtre:[1,12,25],mctsstate:[12,25],mctstreesearch:25,mean:25,memori:[6,14,26],mention:25,messag:16,meta:2,metadata:[0,1,2,22],method:[0,2,4,6,7,12,13,14,15,17,22,24,25,26],midpoint:5,might:[6,25],min_rout:[17,19],min_val:5,minimis:5,minimum:[5,6,26],minmax:5,minmaxscal:5,minumum:17,miss:17,mixin:[6,9,26],ml:16,mode:20,model:[0,1,4,13,16,17,18,19,20,21],model_path:[1,13,19],modifi:[2,5,9],modul:[16,17,21,22,24,26],module_nam:15,mol:[0,6,10,11,12,13,15,21,22,26],mol_nod:[9,10,11,13],molbloom:6,molbloomfilterqueri:6,molecul:[0,1,2,4,5,6,9,10,11,12,13,14,15,16,17,20,22,26],molecular:[2,10,12,13],molecularseri:12,molecule_cost:[13,19],molecule_stor:[2,10,12,13],molecule_to_imag:15,moleculecost:13,moleculedeseri:[2,10,12,13],moleculeexcept:[2,15],moleculenod:[10,11,13],molecules_to_imag:15,moleculeseri:[2,10,12,13],mongo:[0,6,14,21],mongo_cli:15,mongocli:15,mongodb:[15,26],mongodb_stock:26,mongodbinchikeyqueri:6,mont:[20,23],more:[2,3,4,6,7,17],morgan:2,most:[11,12,22,25],mostli:2,multi_expansion_strategi:19,multiexpansionstrategi:[4,19],multipl:[2,3,4,16],must:[4,6,17],my_db:26,my_full_polici:17,my_mol:6,my_polici:17,mycost:13,myexpansionstrategi:4,myfilterstrategi:4,mypackag:5,myscor:5,myurl:26,n:[17,25],n_cluster:1,name:[1,4,5,6,13,14,15,16,17],name_spec:15,nbit:2,nc2ccc:22,ncolumn:12,ndarrai:[1,2,15],necessari:[0,2,6,22],need:[1,2,3,5,17,18,20,22,24,26],neg:[4,25],neither:2,nest:[0,22],network:[0,1,4,12,15,20],networkx:12,neural:[4,15,20],never:[2,25],newli:[5,10,13],next:0,nmax:1,nmin:1,node1:5,node2:5,node:[0,1,5,9,15,16,17,24,25],nodeunexpectedbehaviourexcept:15,none:[0,1,2,3,4,5,6,7,9,10,11,12,13,14,15,25],none_molecul:2,normalis:5,not_in_set_scor:5,not_in_stock_multipli:5,note:[0,25,26],notebook:[7,20,24],np:1,npart:15,nproc:16,number:[0,1,2,4,5,6,9,11,12,13,14,15,16,17,19,24,25,26],number_of_molecul:6,number_of_nod:16,number_of_precursor:16,number_of_precursors_in_stock:16,number_of_rout:16,number_of_solved_rout:16,number_of_step:16,numberofprecursorsinstockscor:5,numberofprecursorsscor:5,numberofreactionsscor:5,numer:[2,6],numpi:[2,13,15],nx:15,o:[6,20,22],object:[0,1,2,3,4,5,6,7,8,9,10,12,13,15,18,22,23,24,25,26],obtain:[1,2,3,5,6,15],oc:20,occur:26,occurr:[5,24],off:[4,17],offset:5,onc:15,one:[1,2,3,4,5,6,9,10,11,12,13,14,15,16,17,18,20,24,25,26],one_iter:[9,10,11,12,13,25],ones:3,onli:[0,2,3,4,5,12,14,15,17,22,25,26],onnx:[15,17],open:[18,26],oper:2,opposit:[0,25],optim:1,option:[0,1,2,3,5,6,9,10,11,12,13,14,15,16,17,22,26],oracl:19,orang:[0,1,15],order:[0,1,5],orient:16,origin:[6,13],original_smil:2,other:[0,2,4,5,6,13,14,16,17,19,26],otherwis:[0,1,15,17],out:[15,16,22,25],outcom:2,outedgeview:15,outer:25,outlin:19,output:[4,5,14,15,18,20,26],output_nam:15,output_s:15,over:[0,5,11,16],overal:25,overridden:4,overwrit:3,own:[12,22],owner:[11,12],packag:[17,20,21,26],package_nam:15,package_path:15,pad:15,page:[1,15,19,22,23,25],pair:1,panda:[6,14,16,17,22],paper:9,param:[0,1,2,4,5,6,13,14],paramat:6,paramet:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17],parent:[2,10,11,12,13],pars:0,part:[0,2,10,11,12,13,15],partial:17,particular:12,pass:[1,2,3,5,15,25],password:15,past:0,path:[0,3,4,6,7,10,12,13,14,16,17,18,20,21,25,26],path_to:12,path_to_model:[4,6],path_to_templ:4,pd:[15,16,22],per:[5,12,19],perform:[0,1,9,10,11,12,13,15,16,18,20,22],perhap:26,phase:[24,25],pick:25,pickl:[13,19],pictor:1,pictori:0,pictur:25,pil:12,pilcolor:15,pilimag:[0,1,15],place:1,plain:[14,24,26],plan:20,pn:11,pn_threshold:11,png:16,point:[7,14,25],polici:[0,3,11,12,15,16,17,18,20,22,25],policy_used_count:16,policyexcept:[4,15],poll:15,poll_freq:15,port:15,possibl:[0,6,11,16,18,20,25,26],post:[16,17,24],post_process:[3,16,17,19],potenti:[4,12],pre:[0,1,4,5,6,10,11,13,17,24],precursor:[20,24,25],precursors_avail:16,precursors_in_stock:16,precursors_not_in_stock:16,predecessor:0,predict:[0,4,13,15],prefix:15,prepare_tre:0,press:[18,20],pretti:15,prevent:[17,25],previous:17,price:[5,6,24,26],price_col:6,price_column:6,pricesumscor:5,print:[16,20],prior:[0,4,12,17],prioriti:0,probabl:[0,4,17,25],process:[12,15,16,17,24],produc:[0,2,15,17,25],product:[2,5],profil:16,progress:0,promis:[11,12,25],promising_child:[11,12,25],prompt:20,proof:11,prop:[9,10,11,13],propag:13,proper:15,properli:26,properti:[0,1,2,3,6,9,10,11,12,13,18],proven:11,provid:[0,2,4,5,6,7,13,16,17,18,20,22,26],prune_cycles_in_search:17,publicli:[9,10,11,13],purchas:[20,24],purpos:25,put:[1,14,22],pwd:26,py:26,pymongo:15,python:26,pythonpath:26,pytorch:13,q:12,queri:[0,3,4,26],quick:[4,17,22],quickkerasfilt:4,quot:20,r:[13,26],radiu:[2,13],rais:[0,2,3,4,5,6,10,11,12,13,15,19],raise_except:2,rank:[0,1,16,18,20,25],rc:17,rd_mol:[2,26],rd_reaction:2,rdchiral:[2,4,17],rdkit:[2,6,17,26],rdmol:2,rdreaction:2,re:[1,2],reach:[12,25],reactant:[0,2,4,11,16,22],reactants_smil:22,reactants_str:2,reactantscountfilt:4,reaction:[0,1,4,5,9,10,11,12,13,15,16,18,20,21,22,24,25],reaction_class_set:5,reaction_cost:5,reaction_tre:1,reaction_tupl:22,reactionclassmembershipscor:5,reactionnod:[10,11,13],reactions_tre:1,reactiontre:[1,5,9,10,11,12,13,16,21,24],reactiontreefromandortrac:9,reactiontreefromdict:0,reactiontreefromexpans:0,reactiontreefromsupernod:12,reactiontreeload:[0,9,12],read:[15,16,25],read_datafil:15,read_json:16,readabl:17,readlin:26,recommend:22,recreat:[1,12,17],rectangl:15,recurs:[0,20,25],refer:[0,2,17,18],regardless:6,reject:[4,15,25],rejectionexcept:[4,15],relat:15,relationship:22,remot:[4,15,17],remov:[0,2,6,15],remove_atom_map:2,replac:1,repres:[2,10,11,12,13],represent:[0,1,2,10,11,12,13,15],requir:[5,24],rescale_prior:[4,17],rescor:1,reset:4,reset_cach:4,reset_exclusion_list:6,resiz:15,respect:[2,4,26],respons:[0,23],rest:[15,25],result:[0,1,16,20,22],retriev:[2,25],retro:[2,13,15],retro_templ:17,retroreact:[0,2,4,10,11,12,13,25],retrostar:[0,9,19],retrostar_value_model:19,retrostarcost:[13,19],retrosynthesi:[0,2,20,23],retrosynthet:20,return_al:1,return_first:17,return_n:0,reus:15,revers:5,reward:[5,16],ringbreak:19,rn:13,rollout:[12,25],root:[0,1,9,10,11,12,13,25],root_nod:9,root_smil:[9,10,11,12,13],round:15,rout:[0,5,8,9,10,11,12,13,15,16,17,18,20,21,22,24],route000:16,route001:16,route0:1,route_dist:1,route_distance_calcul:1,route_distance_model:[16,17],route_metadata:1,route_scor:17,route_to_nod:12,routecollect:[1,8],routecostscor:5,routeimagefactori:15,routeselectionargu:[0,1],routin:[1,2,4,5,6,7,10,11,12,14,15],row:[6,12,16,20,26],run:[15,16,18,20],s:[3,17],sai:17,same:[0,1,2,4,5,10,17],sanit:2,sanitiz:6,save:[6,14,15,16,26],save_datafil:15,save_molecule_imag:15,scale:5,scale_factor:5,scaler:5,scaler_param:5,sci:[5,24],score:[0,1,3,12,13,15,16,17,18,20,25],score_vector:5,scorer:[0,1,3,13,24],scorer_nam:5,scorercollect:[3,5],scorerexcept:[5,15],scorers_config:5,screen:16,script:[14,20],search:[0,1,3,4,5,6,7,14,15,16,17,18,19,20,21,22,24,26],search_reward:17,search_stat:0,search_tim:16,search_tre:[0,1,9,19],searchtre:[10,11,13,19],second:[0,13,15,16,17,22,25],section:17,see:[16,17,18,20,25],seen:17,select:[0,1,3,4,5,6,12,13,16,17,18,20,22,25],select_al:3,select_first:3,select_last:3,select_leaf:[12,25],self:[24,26],semi:16,sent:4,separ:[2,6,9,16],sequenc:[1,2,4,5,10,11,12,13,15,22],seral:[10,13],serial:[0,10,11,12,13,21],serialize_act:2,serv:15,server:[4,15,17],set:[0,2,3,4,5,6,9,10,11,12,13,15,17,22,25,26],set_stop_criteria:6,setup:[0,5,7,15,24,26],setup_logg:15,sever:[1,5],sha224:0,shallow:2,shape:5,share:5,shell:[22,26],shibukawa:9,should:[0,2,4,5,6,7,10,11,13,15,17,20,25,26],show:[0,15,16,18,20,23,25],show_al:[0,15],show_progress:0,shown:[17,18],sigmoid:5,signal:15,similar:15,similarli:25,simpl:[1,5,16,18,20,26],simplest:16,simpli:25,simplifi:25,singl:[0,1,2,3,6,16,17,18,20],size:[14,15],slope:5,small:5,smart:2,smi:26,smile:[0,1,2,6,10,11,12,13,14,16,18,19,20,22,26],smiles2stock:[14,17,26],smiles_bas:6,smiles_in_stock:6,smiles_list:14,smilesbasedretroreact:2,so:2,softmax:[4,15,17],solut:[10,11,12,13,16,17],solv:[1,12,13,16,17,19,25],some:[2,6,9,12,18,19,20,23,25,26],someth:[16,19],soon:17,sort:[1,2,5,18,25],sourc:[0,3,4,5,6,15,26],source_scor:5,source_tag:14,space:15,special:2,specif:[1,15],specifi:[2,3,4,5,6,16,17,19,20,26],split:[9,15,16,26],split_fil:15,splitandortre:9,squar:1,squash:5,squashscal:5,standard:15,stare:16,start:[0,5,11,15,16],start_process:15,stat:[18,22],state:[0,5,9,10,11,13,17,24,25],statescor:[1,5],statist:[0,1,16,20],stead:15,step:[11,12,17,18,20,24,25],stereochemistri:2,still:15,stock1:17,stock:[0,3,5,9,10,11,12,13,14,15,16,17,18,20,22,24,25],stock_avail:12,stock_db:[6,26],stock_inchikei:6,stock_info:[0,16],stockavailabilityscor:5,stockexcept:[6,15],stockquerymixin:[6,26],stop:6,stop_criteria:[6,26],stopiter:[10,11,13],store:[1,2,5,14,20,26],str:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],strategi:4,strdict:[0,1,2,3,5,9,10,11,12,13,15],string:[0,1,2,6,12,22,26],strip:26,structur:[0,3,9],stub:[10,13],sub:[1,2,3,4,5,6,10,11,12,13,15,26],sub_stock:6,subclass:26,submit:4,submodul:21,subpackag:21,subset:[5,26],subset_nam:5,substitut:0,subtre:0,suffici:0,suggest:[4,17,20],suitabl:2,sum:[5,24],suppli:16,support:[2,11,26],suppos:0,sure:0,symbol:6,synthes:[13,24],synthet:1,t:[4,13,26],tabl:[6,15,16,17],tag:26,take:[0,2,5,15,25,26],taken:[3,12,13,14,15,17,26],tar:[1,15],tarbal:1,target:[0,1,16,17,18,19,20,22,26],target_mol:0,target_smil:[0,22],target_valu:13,task:16,ted:1,templat:[2,4,5,17,20,24,25],template_column:[4,17],templatebasedexpansionstrategi:4,templatedretroreact:[2,4],temporari:15,tensorflow:[4,15,17],termin:[10,11,12,13,16,17,25],text:[6,14,15,16,17,20,25,26],tf:15,than:[1,2,5,19],thei:[1,5,12,15],them:[0,1,6,12,15,17,22,25,26],themselv:12,therebi:[1,2,26],therefor:[2,15,25],thi:[0,1,2,3,4,5,6,7,9,10,11,12,13,15,16,17,18,19,20,22,23,24,25,26],thing:25,third:25,those:4,though:12,three:26,threshold:[11,17],through:[15,18],time:[0,15,16],time_limit:17,timeout:1,tl:15,tls_certs_path:15,to_dict:[0,1,2],to_imag:[0,12,16],to_json:0,to_reaction_tre:12,to_smiles_based_retroreact:2,to_visjs_pag:1,togeth:19,too:5,took:5,tool:[0,16,17,18,20,21,26],top:[0,1,16,18,20,25],top_scor:16,total:16,trace:9,track:2,train:[4,17,20],transform:[1,2,5,12,16,24,25],travers:12,treat:17,tree:[0,1,2,3,4,5,7,9,10,11,12,13,14,15,16,17,18,20,22,24,26],tree_analysi:[0,21],tree_dict:0,tree_nam:15,tree_search:[0,22],tree_statist:1,treeanalysi:1,treeanalysisexcept:15,treee:0,treemolecul:[2,4,10,11,12,13],treenodemixin:[9,10,13],trees_for_first_target:16,trees_nam:15,tri:15,trigger:1,tupl:[0,1,2,4,5,12,13,15],turn:25,twice:11,two:[1,2,4,17,20,26],txt:[16,19,20],type:[0,1,2,3,4,5,6,7,9,10,11,12,13,14,15,16,17,19,23,26],type_util:[0,21],typic:[7,23],u:12,ucb:25,under:13,underli:12,understand:25,unexpand:[12,25],unexpectedli:15,unfamiliar:18,union:[0,1,2,3,6,15],uniqu:[2,14,17],uniquemolecul:[0,2],unit:0,unless:3,unqueri:2,unsolv:19,until:[12,16,25],up:[1,2,6,11,18],updat:[0,1,2,11,12,13,24,25],upon:[2,4,15,17],upper:[9,17,25],us:[0,1,2,4,5,6,7,10,11,12,13,15,16,17,18,20,22,23,24,25,26],usag:16,use_prior:17,use_rdchir:[4,17],use_remote_model:[4,15,17],user:[0,15,22,26],usernam:15,uspto:[17,19,20,22],uspto_expans:17,uspto_ful:17,uspto_keras_model:19,uspto_ringbreaker_keras_model:19,uspto_ringbreaker_unique_templ:19,uspto_templ:17,uspto_unique_templ:19,util:[0,2,3,9,20,21],utilis:11,v:13,v_t:13,val:5,valu:[0,2,3,4,5,6,12,13,15,16,17,19,25],value_estim:12,valueerror:[0,3,12,15],variabl:[0,1,2,3,4,5,6,7,10,11,12,13,15,17],variou:[1,3,10,11,13,25],vector:[4,5,15],vertic:12,vi:[1,15],via:15,view:12,visit:[12,25],visual:1,wa:[0,4,6,10,11,12,13,16],wai:[0,4,12,25,26],wait:15,want:[18,20,22],we:[25,26],weight:[2,5,13,17],weighted_scor:5,well:[1,22,26],were:[12,25],what:[0,6,8,16,25],when:[1,2,5,6,12,13,15,16,17,18,20,24,25],where:[2,16,18,20,22,26],wherea:[17,20],wherebi:17,whether:4,which:[2,4,6,10,12,17,18,20,25,26],white:15,widget:7,within:20,without:[2,12,19],word:1,work:[15,26],worth:25,would:15,wrapper:2,write:[22,26],written:[16,20],x:[5,15],xoffset:5,y:5,yaml:[0,3,7,18,20],yield:[0,14,26],yml:[15,16,17,18,19,20,22,24,26],yoffset:5,you:[5,16,17,18,19,20,22,26],your:[20,22],zero:13,zeromoleculecost:13,zinc:[17,20,22,26],zinc_stock:17},titles:["aizynthfinder package","aizynthfinder.analysis package","aizynthfinder.chem package","aizynthfinder.context package","aizynthfinder.context.policy package","aizynthfinder.context.scoring package","aizynthfinder.context.stock package","aizynthfinder.interfaces package","aizynthfinder.interfaces.gui package","aizynthfinder.search package","aizynthfinder.search.breadth_first package","aizynthfinder.search.dfpn package","aizynthfinder.search.mcts package","aizynthfinder.search.retrostar package","aizynthfinder.tools package","aizynthfinder.utils package","Command-line interface","Configuration file","Graphical user interface","How-to","aizynthfinder documentation","aizynthfinder","Python interface","Relationships","Scoring","Sequences","Stocks"],titleterms:{"function":24,"new":24,add:24,advanc:17,aizynthapp:7,aizynthcli:7,aizynthfind:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,20,21],analys:[16,18],analysi:[1,23,25],andor_tre:9,breadth_first:10,carlo:25,cat_output:14,chem:2,cluster:[8,18],collect:[3,5],command:16,config:3,configur:17,content:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],context:[3,4,5,6],cost:13,creat:18,criteria:26,custom:26,databas:26,dfpn:11,document:20,download_public_data:14,except:15,expans:[19,22],expansion_strategi:4,file:[15,17],filter_strategi:4,further:22,graphic:18,gui:8,how:19,imag:15,interfac:[7,8,16,18,22],introduct:20,line:16,load:15,log:15,make:26,make_stock:14,math:15,mct:12,model:15,modul:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],mol:2,mongo:[15,26],mont:25,more:19,multipl:19,node:[10,11,12,13],notebook:18,output:[16,19],packag:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],path:15,polici:[4,19],post:[23,25],process:[23,25],python:22,queri:6,reaction:2,reactiontre:0,read:22,relationship:23,result:18,retro:19,retrostar:13,rout:[1,19],score:[5,24],scorer:5,search:[9,10,11,12,13,23,25],search_tre:[10,11,13],sequenc:25,serial:2,simpl:17,specif:16,state:12,stock:[6,26],stop:26,submodul:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],subpackag:[0,3,7,9],tool:14,tree:[23,25],tree_analysi:1,type_util:15,us:19,usag:17,user:18,util:[1,4,12,15]}}) \ No newline at end of file diff --git a/sequences.html b/sequences.html new file mode 100644 index 0000000..3422987 --- /dev/null +++ b/sequences.html @@ -0,0 +1,219 @@ + + + + + + + + + Sequences — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

Sequences

+

This page shows some sequence diagrams to aid in the understanding of how information +is passed between different objects in the Monte Carlo tree search. +The sequences are simplified, but explains the overall picture. +The flow of information / method call should be read top-down.

+
+

Analysis / post-processing

+

This sequence explains how the AiZynthFinder object exports the top-ranked reaction tree +as a JSON. Note, this is only one possible sequence for analysis of the trees.

+_images/analysis-seq.png +
+ +
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/stocks.html b/stocks.html new file mode 100644 index 0000000..bbddce3 --- /dev/null +++ b/stocks.html @@ -0,0 +1,224 @@ + + + + + + + + + Stocks — aizynthfinder 4.0.0 documentation + + + + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +
+

Stocks

+

The stock files specified in the configuration file are loaded and a set of inchi keys +are stored in-memory for lookup. However, the tool supports other stock queries as well as a way +to fully customize the lookup.

+
+

Mongo database stock

+

First, support for lookup inchi keys in a Mongo database is supported. The Mongo client should +have a database and a collection containing documents with at least two fields: inchi_key and source. +The inchi_key field will be used for lookup and source specifies the source database of the compound.

+

By adding these lines to the configuration file, the Mongo database will be used:

+
stock:
+    type: mongodb
+    host: user@myurl.com
+    database: database_name
+    collection: compounds
+
+
+

If no options are provided to the mongodb_stock key, the host, database and collection are taken to be localhost, +stock_db, and molecules, respectively.

+
+
+

Stop criteria

+

The stock can be used to stop the tree search based on three criteria: a) minimum price, b) maximum amount and c) count of different elements in the molecule. +Note that the stock query class need to support querying for price and amount, if the stop criteria should work properly.

+

The stop criteria can be specified in the configuration file

+
stock:
+    stop_criteria:
+        price: 10
+        counts:
+            C: 10
+
+
+

In the Jupyter GUI you can set the limit on the element occurences, but currently not the price and amount limits.

+
+
+

Custom stock

+

Support for any type of lookup is provided. You just need to write a python class that implements the __contains__ +and subclasses the aizynthfinder.context.stock.queries.StockQueryMixin. The __contains__ method is used for lookup and should take a Molecule object as only argument. +The StockQueryMixin mixin class provide a default interface for some methods that perhaps isn’t possible to implement in all query classes.

+

This is an example:

+
from rdkit.Chem import Lipinski
+from aizynthfinder.context.stock.queries import StockQueryMixin
+class CriteriaStock(StockQueryMixin):
+    def __contains__(self, mol):
+        return Lipinski.HeavyAtomCount(mol.rd_mol) < 10
+
+
+

To use this stock with the aizynthcli tool, save it in a custom_stock.py module that is located in a directory known to +the python interpreter. Add this line to the module.

+
stock = CriteriaStock()
+
+
+

and it will be automatically used in the tree search.

+

Alternatively the custom query class can be used by the aizynthapp tool.

+
from aizynthfinder import AiZynthApp
+configfile="config_local.yml"
+app = AiZynthApp(configfile, setup=False)
+app.finder.stock.load(CriteriaStock(), "criteria") # This loads the custom stock class
+app.setup()
+
+
+

Lastly, it is possible to specify a custom stock class in the configuration file if it is located in a module that +is known by the python interpreter.

+
stock:
+    type: aizynthfinder.contrib.stocks.CriteriaStock
+
+
+

can be used if the aizynthfinder.contrib.stocks is an existing sub-package and module.

+
+
+

Making stocks

+

We provide a tool to create inchi key-based stocks from SMILES strings. Thereby, one +can create a stock based on for instance a subset of the ZINC database.

+

The tool support both creating a stock in HDF5 format or adding them to an existing Mongo database.

+

The tool is easiest to use if one has a number of plain text files, in which each row has one SMILES.

+

Then one can use one of these two commands:

+
smiles2stock --files file1.smi file2.smi --output stock.hdf5
+smiles2stock --files file1.smi file2.smi --output my_db --target mongo
+
+
+

to create either an HDF5 stock or a Mongo database stock, respectively. The file1.smi and file2.smi +are simple text files and my_db is the source tag for the Mongo database.

+

If one has SMILES in any other format, one has to provide a custom module that extract the SMILES from +the input files. This is an example of such a module that can be used with downloads from the Zinc database +where the first row contains headers and the SMILES are the first element on each line.

+
def extract_smiles(filename):
+    with open(filename, "r") as fileobj:
+        for i, line in enumerate(fileobj.readlines()):
+            if i == 0:
+                continue
+            yield line.strip().split(" ")[0]
+
+
+

if this is saved as load_zinc.py in a path that is known to the Python interpreter, it can be +used like this

+
export PYTHONPATH=`pwd`
+smiles2stock --files load_zinc file1.smi file2.smi --source module --output stock.hdf5
+
+
+

where the first line adds the current directory to the python path (if you are using a Bash shell).

+
+
+ + +
+ +
+
+
+
+ + + + + + + \ No newline at end of file