From 0d040320b61116d31195bff1929e46010a7ab845 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:17:22 -0500 Subject: [PATCH 01/19] Bump codecov/codecov-action from 3 to 4 (#8) * Bump codecov/codecov-action from 3 to 4 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Update CI.yaml Add $CODECOV_TOKEN. * use correct codecov token key --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jeremy M. G. Leung <63817169+jeremyleung521@users.noreply.github.com> Co-authored-by: Jeremy Leung --- .github/workflows/CI.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 2df2300..ce33365 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -61,11 +61,13 @@ jobs: pytest -v --cov=lpath --cov-report=xml --color=yes lpath/tests/ - name: CodeCov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: - # token: ${{ secrets.CODECOV_TOKEN }} # Not needed for Public Repos file: ./coverage.xml flags: unittests name: codecov-${{ matrix.os }}-py${{ matrix.python-version }} fail_ci_if_error: false verbose: false + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} # Not needed for Public Repos + From 72e02863bf748aa6411b46179f2682ba4de443da Mon Sep 17 00:00:00 2001 From: Jeremy Leung Date: Tue, 27 Feb 2024 12:47:25 -0500 Subject: [PATCH 02/19] Add usage for lpath.plot --- docs/usage.rst | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index e0c8299..ac41628 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -92,7 +92,8 @@ In this step, we will pattern match any successful transitions we've identified 1. From the command line, run the following:: - lpath match --input-pickle succ_traj/pathways.pickle --cluster-labels-output succ_traj/cluster_labels.npy + lpath match --input-pickle succ_traj/pathways.pickle --output-pickle succ_traj/match-output.pickle \ + --cluster-labels-output succ_traj/cluster_labels.npy 2. After the comparison process is completed, it should show you the dendrogram. Closing the figure should trigger prompts to guide you further. @@ -100,9 +101,22 @@ In this step, we will pattern match any successful transitions we've identified Plot ____ +This step will help you plot some of the most common graphs, such as dendrograms and histograms, directly from the pickle object generated from match. Users may also elect to use the plotting scripts from the ``examples`` folder. +There is a script to plot ``NetworkX`` plots there. -[UNDER CONSTRUCTION] +More specifically, the following graphs will be made in the ``plots`` folder:: +* Dendrogram showing separation between clusters +* Weights/Cluster bar graph +* Target iteration histograms (per cluster) +* Event duration histograms (per cluster) + + +From the command line, run the following and it should generate a separate file for each of the above graphs:: + + lpath plot --plot-input succ_traj/match-output.pickle + +More options for customizing the graphs can be found by running ``lpath plot --help``. Weighted Ensemble Simulations ----------------------------- @@ -144,7 +158,7 @@ This will do the pattern matching and output individual h5 files for each cluste 1. From the command line, run the following:: - lpath match -we --input-pickle succ_traj/output.pickle --cluster-labels-output succ_traj/cluster_labels.npy \ + lpath match -we --input-pickle succ_traj/output.pickle --output-pickle succ_traj/match-output.pickle --cluster-labels-output succ_traj/cluster_labels.npy \ --export-h5 --file-pattern "west_succ_c{}.h5" 2. After the comparison process is completed, it should show you the dendrogram. Closing the figure should trigger prompts to guide you further. @@ -154,11 +168,25 @@ This will do the pattern matching and output individual h5 files for each cluste For cases where you want to run pattern matching comparison between segment IDs, you will have to use the largest common substring ``--substring`` option. By default, the longest common subsequence algorithm is used.:: - lpath match -we --input-pickle succ_traj/output.pickle --cluster-labels-output succ_traj/cluster_labels.npy \ + lpath match -we --input-pickle succ_traj/output.pickle --output-pickle succ_traj/match-output.pickle --cluster-labels-output succ_traj/cluster_labels.npy \ --export-h5 --file-pattern "west_succ_c{}.h5" --reassign-function "reassign_segid" --substring Plot ____ +This step will help you plot some of the most common graphs, such as dendrograms and histograms, directly from the pickle object generated from match. Users may also elect to use the plotting scripts from the ``examples`` folder. +There is a script to plot ``NetworkX`` plots there. + +More specifically, the following graphs will be made in the ``plots`` folder:: + +* Dendrogram showing separation between clusters +* Weights/Cluster bar graph +* Target iteration histograms (per cluster) +* Event duration histograms (per cluster) + + +From the command line, run the following and it should generate a separate file for each of the above graphs:: + + lpath plot --plot-input succ_traj/match-output.pickle -[UNDER CONSTRUCTION] \ No newline at end of file +More options for customizing the graphs can be found by running ``lpath plot --help``. \ No newline at end of file From b7a556f08b5e8d200ecafe0d138ea0a233bdf860 Mon Sep 17 00:00:00 2001 From: Jeremy Leung Date: Wed, 28 Feb 2024 13:19:51 -0500 Subject: [PATCH 03/19] add extra option variations --- docs/usage.rst | 2 +- lpath/argparser.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index ac41628..8fa7a8d 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -169,7 +169,7 @@ This will do the pattern matching and output individual h5 files for each cluste For cases where you want to run pattern matching comparison between segment IDs, you will have to use the largest common substring ``--substring`` option. By default, the longest common subsequence algorithm is used.:: lpath match -we --input-pickle succ_traj/output.pickle --output-pickle succ_traj/match-output.pickle --cluster-labels-output succ_traj/cluster_labels.npy \ - --export-h5 --file-pattern "west_succ_c{}.h5" --reassign-function "reassign_segid" --substring + --export-h5 --file-pattern "west_succ_c{}.h5" --reassign-method "reassign_segid" --substring Plot diff --git a/lpath/argparser.py b/lpath/argparser.py index d1491d7..8b4de9b 100644 --- a/lpath/argparser.py +++ b/lpath/argparser.py @@ -410,14 +410,14 @@ def add_match_args(parser=None): match_io.add_argument('--output-pickle', '-op', '--OP', dest='output_pickle', default='succ_traj/pathways.pickle', type=str, help='Path to reassigned object to be ' 'outputted from the `match` step.') - match_io.add_argument('--cl-output', '-co', '--cluster-label-output', dest='cl_output', + match_io.add_argument('--cl-output', '-co', '--cluster-label-output', '--cluster-labels-output', dest='cl_output', default='succ_traj/cluster_labels.npy', type=str, help='Output file location for cluster labels.') match_io.add_argument('--match-exclude-min-length', '-me', '--match-exclude-length', '--match-exclude-short', dest='exclude_short', type=check_non_neg, default=0, help='Exclude trajectories shorter than provided value during ' 'matching. Default is 0, which will include trajectories of all lengths.') - match_io.add_argument('--reassign', '-ra', '--reassign-method', dest='reassign_method', + match_io.add_argument('--reassign', '-ra', '--reassign-method', '--reassign-function', dest='reassign_method', default='reassign_identity', type=str, help='Reassign method to use. Could be one of the defaults or a module to load. Defaults are ' '``reassign_identity``, ``reassign_statelabel``, ``reassign_segid``, ' From 8f9fdeb3eebd9d002a877a7593ddf365eb022e8c Mon Sep 17 00:00:00 2001 From: Jeremy Leung Date: Wed, 28 Feb 2024 18:09:03 -0500 Subject: [PATCH 04/19] Add more documentation --- docs/usage.rst | 67 ++++++++++++++++++++++++++++++++- examples/WE/reassign_custom.py | 38 +++++++++++++++++-- examples/cMD/reassign_custom.py | 37 ++++++++++++++++-- lpath/match.py | 4 +- 4 files changed, 138 insertions(+), 8 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 8fa7a8d..9cdc534 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -189,4 +189,69 @@ From the command line, run the following and it should generate a separate file lpath plot --plot-input succ_traj/match-output.pickle -More options for customizing the graphs can be found by running ``lpath plot --help``. \ No newline at end of file +More options for customizing the graphs can be found by running ``lpath plot --help``. + + +Example Reassign file +--------------------- + +The following is a reassign function if you decides to reclassify your states. + + def reassign_custom(data, pathways, dictionary, assign_file=None): + """ + Reclassify/assign frames into different states. This is highly + specific to the system. If w_assign's definition is sufficient, + you can proceed with what's made in the previous step + using ``reassign_identity``. + + In this example, the dictionary maps state idx to its corresponding ``state_string``. + We suggest using alphabets as states. + + Parameters + ---------- + data : list + An array with the data necessary to reassign, as extracted from ``output.pickle``. + + pathways : numpy.ndarray + An empty array with shapes for iter_id/seg_id/state_id/pcoord_or_auxdata/frame#/weight. + + dictionary : dict + An empty dictionary obj for mapping ``state_id`` with ``state string``. The last entry in + the dictionary should be the "unknown" state. + + assign_file : str, default : None + A string pointing to the ``assign.h5`` file. Needed as a parameter for all functions, + but is ignored if it's an MD trajectory. + + Returns + ------- + dictionary : dict + A dictionary mapping each ``state_id`` (float/int) with a ``state string`` (character). + The last entry in the dictionary should be the "unknown" state. + + """ + # Other example for grouping multiple states into one. + for idx, pathway in enumerate(data): + # The following shows how you can "merge" multiple states into + # a single one. + pathway = numpy.asarray(pathway) + # Further downsizing... to if pcoord is less than 5 + first_contact = numpy.where(pathway[:, 3] < 5)[0][0] + for jdx, frame in enumerate(pathway): + # First copy all columns over + pathways[idx, jdx] = frame + # ortho is assigned to state 0 + if frame[2] in [1, 3, 4, 6, 7, 9]: + frame[2] = 0 + # para is assigned to state 1 + elif frame[2] in [2, 5, 8]: + frame[2] = 1 + # Unknown state is assigned 2 + if jdx < first_contact: + frame[2] = 2 + pathways[idx, jdx] = frame + + # Generating a dictionary mapping each state + dictionary = {0: 'A', 1: 'B', 2: '!'} + + return dictionary \ No newline at end of file diff --git a/examples/WE/reassign_custom.py b/examples/WE/reassign_custom.py index abb54a7..5a5588a 100644 --- a/examples/WE/reassign_custom.py +++ b/examples/WE/reassign_custom.py @@ -1,11 +1,43 @@ import numpy def reassign_custom(data, pathways, dictionary, assign_file=None): + """ + Reclassify/assign frames into different states. This is highly + specific to the system. If w_assign's definition is sufficient, + you can proceed with what's made in the previous step + using ``reassign_identity``. - for idx, val in enumerate(data): + In this example, the dictionary maps state idx to its corresponding ``state_string``. + We suggest using alphabets as states. + + Parameters + ---------- + data : list + An array with the data necessary to reassign, as extracted from ``output.pickle``. + + pathways : numpy.ndarray + An empty array with shapes for iter_id/seg_id/state_id/pcoord_or_auxdata/frame#/weight. + + dictionary : dict + An empty dictionary obj for mapping ``state_id`` with ``state string``. The last entry in + the dictionary should be the "unknown" state. + + assign_file : str, default : None + A string pointing to the ``assign.h5`` file. Needed as a parameter for all functions, + but is ignored if it's an MD trajectory. + + Returns + ------- + dictionary : dict + A dictionary mapping each ``state_id`` (float/int) with a ``state string`` (character). + The last entry in the dictionary should be the "unknown" state. + + """ + # reassign states to be the cluster IDs + for idx, val in enumerate(data): # Loop through each set of successful pathways val_arr = numpy.asarray(val) - for idx2, val2 in enumerate(val_arr): - val2[2] = int(val2[5]) + for idx2, val2 in enumerate(val_arr): # Loop through each frame of the pathway + val2[2] = int(val2[-3]) # Renumber state_id the with the aux dataset pathways[idx, idx2] = val2 # Generating a dictionary mapping each state diff --git a/examples/cMD/reassign_custom.py b/examples/cMD/reassign_custom.py index 98db7bc..5a5588a 100644 --- a/examples/cMD/reassign_custom.py +++ b/examples/cMD/reassign_custom.py @@ -1,12 +1,43 @@ import numpy def reassign_custom(data, pathways, dictionary, assign_file=None): + """ + Reclassify/assign frames into different states. This is highly + specific to the system. If w_assign's definition is sufficient, + you can proceed with what's made in the previous step + using ``reassign_identity``. + In this example, the dictionary maps state idx to its corresponding ``state_string``. + We suggest using alphabets as states. + + Parameters + ---------- + data : list + An array with the data necessary to reassign, as extracted from ``output.pickle``. + + pathways : numpy.ndarray + An empty array with shapes for iter_id/seg_id/state_id/pcoord_or_auxdata/frame#/weight. + + dictionary : dict + An empty dictionary obj for mapping ``state_id`` with ``state string``. The last entry in + the dictionary should be the "unknown" state. + + assign_file : str, default : None + A string pointing to the ``assign.h5`` file. Needed as a parameter for all functions, + but is ignored if it's an MD trajectory. + + Returns + ------- + dictionary : dict + A dictionary mapping each ``state_id`` (float/int) with a ``state string`` (character). + The last entry in the dictionary should be the "unknown" state. + + """ # reassign states to be the cluster IDs - for idx, val in enumerate(data): + for idx, val in enumerate(data): # Loop through each set of successful pathways val_arr = numpy.asarray(val) - for idx2, val2 in enumerate(val_arr): - val2[2] = int(val2[3]) + for idx2, val2 in enumerate(val_arr): # Loop through each frame of the pathway + val2[2] = int(val2[-3]) # Renumber state_id the with the aux dataset pathways[idx, idx2] = val2 # Generating a dictionary mapping each state diff --git a/lpath/match.py b/lpath/match.py index dd7ff1a..1bdc38e 100644 --- a/lpath/match.py +++ b/lpath/match.py @@ -395,7 +395,8 @@ def reassign_custom(data, pathways, dictionary, assign_file=None): An empty array with shapes for iter_id/seg_id/state_id/pcoord_or_auxdata/frame#/weight. dictionary : dict - An empty dictionary obj for mapping ``state_id`` with ``state string``. + An empty dictionary obj for mapping ``state_id`` with ``state string``. The last entry in + the dictionary should be the "unknown" state. assign_file : str, default : None A string pointing to the ``assign.h5`` file. Needed as a parameter for all functions, @@ -405,6 +406,7 @@ def reassign_custom(data, pathways, dictionary, assign_file=None): ------- dictionary : dict A dictionary mapping each ``state_id`` (float/int) with a ``state string`` (character). + The last entry in the dictionary should be the "unknown" state. """ # Other example for grouping multiple states into one. From 6239008b111b97371a23e5fcbad589a6745ebe5a Mon Sep 17 00:00:00 2001 From: Jeremy Leung Date: Thu, 29 Feb 2024 17:28:11 -0500 Subject: [PATCH 05/19] Bug fix for case where if you leave source and enter target state in one iteration, it would not respect trace-basis --- lpath/extract.py | 6 ++++-- lpath/lpath.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lpath/extract.py b/lpath/extract.py index b30c667..611ecd6 100644 --- a/lpath/extract.py +++ b/lpath/extract.py @@ -571,7 +571,8 @@ def trace_seg_to_last_state( for frame_index in frame_loop: indv_trace.append([iteration_num, segment_num, corr_assign[frame_index], *ad_arr[frame_index], frame_index, weight]) - break + if trace_basis is False: + break else: # Just a normal iteration where we reached target state. Output everything in stride. frame_loop = frame_range(-1, term_frame_num, total_frames, stride_step) @@ -713,7 +714,8 @@ def trace_seg_to_last_state( for frame_index in frame_loop: indv_trace.append([iteration_num, segment_num, corr_assign[frame_index], *ad_arr[frame_index], frame_index, weight]) - break + if trace_basis is False: + break else: # Just a normal iteration where we reached target state. Output everything in stride. frame_loop = frame_range(-1, term_frame_num, total_frames, stride_step) diff --git a/lpath/lpath.py b/lpath/lpath.py index 4cc9343..3534dd4 100644 --- a/lpath/lpath.py +++ b/lpath/lpath.py @@ -56,6 +56,6 @@ def entry_point(): make_dir(args) - print(args) + # print(args) # Run whatever function given args.func(args) From 91fc68f8e30831f31ab8d081c9a46f240bda15fc Mon Sep 17 00:00:00 2001 From: Jeremy Leung Date: Fri, 1 Mar 2024 13:09:13 -0500 Subject: [PATCH 06/19] fix pypi auto-upload --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index a576c0b..4bf04c9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -101,7 +101,7 @@ jobs: with: # unpacks default artifact into dist/ # if `name: artifact` is omitted, the action will create extra parent dir - name: artifact-* + pattern: artifact-* path: dist merge-multiple: true From 737338932491db111e23e26ff6af4fe843b05b94 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" <63817169+jeremyleung521@users.noreply.github.com> Date: Fri, 5 Apr 2024 11:46:29 -0400 Subject: [PATCH 07/19] Update usage.rst Fix formatting of usage.rst --- docs/usage.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage.rst b/docs/usage.rst index 9cdc534..62fa49b 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -195,7 +195,7 @@ More options for customizing the graphs can be found by running ``lpath plot --h Example Reassign file --------------------- -The following is a reassign function if you decides to reclassify your states. +The following is a reassign function if you decides to reclassify your states:: def reassign_custom(data, pathways, dictionary, assign_file=None): """ @@ -254,4 +254,4 @@ The following is a reassign function if you decides to reclassify your states. # Generating a dictionary mapping each state dictionary = {0: 'A', 1: 'B', 2: '!'} - return dictionary \ No newline at end of file + return dictionary From acb476bbc3150b38f32509b9679507e70054a6e0 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" Date: Thu, 16 May 2024 14:50:02 -0400 Subject: [PATCH 08/19] Add option to set timeout for interactive questions, fix bug when n-clusters is specified. --- lpath/argparser.py | 2 ++ lpath/match.py | 27 ++++++++++++++++++++------- lpath/tests/test_argparser.py | 4 ++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/lpath/argparser.py b/lpath/argparser.py index 8b4de9b..79e4c5e 100644 --- a/lpath/argparser.py +++ b/lpath/argparser.py @@ -538,6 +538,8 @@ def add_plot_args(parser=None): plot_io.add_argument('--n-clusters', '-nc', '--num-clusters', dest='num_clusters', type=check_positive, help='For cases where you know in advance how many clusters you want for ' 'the hierarchical clustering.') + plot_io.add_argument('--timeout', '-pto', '--plot-timeout', dest='plot_timeout', type=check_non_neg, + default=None, help='Timeout (in seconds) for asking input.') # plot_io.add_argument('--plot-regen-cl', '-rcl', '--plot-regenerate-cluster-labels', dest='regen_cl', # action='store_true', diff --git a/lpath/match.py b/lpath/match.py index 1bdc38e..46c9792 100644 --- a/lpath/match.py +++ b/lpath/match.py @@ -940,7 +940,7 @@ def export_we_files(data_arr, weights, cluster_labels, clusters, file_pattern="w f.writelines(representative_list) -def determine_rerun(z, out_path='plots', mpl_colors=default_dendrogram_colors, ax=None): +def determine_rerun(z, out_path='plots', mpl_colors=default_dendrogram_colors, ax=None, timeout=None): """ Asks if you want to regenerate the dendrogram. @@ -957,11 +957,18 @@ def determine_rerun(z, out_path='plots', mpl_colors=default_dendrogram_colors, a ax : matplotlib.Axes, Default: None Matplotlib.Axes object to be inherited. + + timeout : int, default: 30 + Input timeout in seconds. + """ + if timeout is None: + timeout = 30 + while True: try: ans = timedinput('Do you want to regenerate the graph with a new threshold (y/[n])?\n', - timeout=60, default='N') + timeout=timeout, default='N') if ans == 'y' or ans == 'Y': ans2 = timedinput('What new threshold would you like?\n', timeout=15, default=0.5) try: @@ -969,7 +976,7 @@ def determine_rerun(z, out_path='plots', mpl_colors=default_dendrogram_colors, a show_fig=True, mpl_colors=mpl_colors, ax=ax) return ax except ValueError: - determine_rerun(z, out_path=out_path, mpl_colors=mpl_colors, ax=ax) + determine_rerun(z, out_path=out_path, mpl_colors=mpl_colors, ax=ax, timeout=timeout) elif ans == 'n' or ans == 'N' or ans == '': return None else: @@ -978,16 +985,19 @@ def determine_rerun(z, out_path='plots', mpl_colors=default_dendrogram_colors, a sys.exit(0) -def ask_number_clusters(num_clusters=None): +def ask_number_clusters(num_clusters=None, timeout=None): """ Asks how many clusters you want to separate the trajectories into. """ + if timeout is None: + timeout = 15 + if not num_clusters: while True: try: ans = timedinput('How many clusters would you like to separate the pathways into?\n', - timeout=15, default=2) + timeout=timeout, default=2) try: ans = int(ans) return ans @@ -995,6 +1005,8 @@ def ask_number_clusters(num_clusters=None): log.warning("Invalid input.\n") except KeyboardInterrupt: sys.exit(0) + else: + return num_clusters def report_statistics(n_clusters, cluster_labels, weights, segid_status=False): @@ -1094,9 +1106,10 @@ def main(arguments): z = calc_linkage(dist_matrix) ax = visualize(z, threshold=arguments.dendrogram_threshold, out_path=arguments.out_path, show_fig=arguments.dendrogram_show, mpl_colors=arguments.mpl_colors) - ax = determine_rerun(z, out_path=arguments.out_path, mpl_colors=arguments.mpl_colors, ax=ax) + ax = determine_rerun(z, out_path=arguments.out_path, mpl_colors=arguments.mpl_colors, ax=ax, + timeout=arguments.plot_timeout) - n_clusters = ask_number_clusters(arguments.num_clusters) + n_clusters = ask_number_clusters(arguments.num_clusters, timeout=arguments.plot_timeout) cluster_labels = hcluster(z, n_clusters) # Report statistics diff --git a/lpath/tests/test_argparser.py b/lpath/tests/test_argparser.py index 6421a8b..d53890b 100644 --- a/lpath/tests/test_argparser.py +++ b/lpath/tests/test_argparser.py @@ -124,7 +124,7 @@ def test_dimensions(self, create_ref_parser): assert isinstance(output, argparse.ArgumentParser) # Need to change as number of options increase - assert len(output._actions) == 66 + assert len(output._actions) == 67 def test_common_arguments(self, create_ref_parser): """ @@ -180,7 +180,7 @@ def test_plot_arguments(self, create_ref_parser): """ output, test_output = create_ref_parser test_input = ['-ipl', '-icl', '-pdF', '-pod', '-sty', '-mpl', '-col', '-pdt', '-pds', - '-pdh', '-nc', '-prl'] + '-pdh', '-nc', '-prl', '-pto'] for option in test_input: assert option in test_output From 39e7e88ee87a105984072a0cc00f1bb7e664b171 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" <63817169+jeremyleung521@users.noreply.github.com> Date: Thu, 16 May 2024 17:43:45 -0400 Subject: [PATCH 09/19] Update test_env.yaml --- devtools/conda-envs/test_env.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/conda-envs/test_env.yaml b/devtools/conda-envs/test_env.yaml index 4f632d3..50bc848 100644 --- a/devtools/conda-envs/test_env.yaml +++ b/devtools/conda-envs/test_env.yaml @@ -4,7 +4,7 @@ channels: - defaults dependencies: # Base depends - - python + - python<3.12 - pip - scikit-learn - tqdm From afb3b04891a6c25cd2a975fee17cec04de3af24e Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" <63817169+jeremyleung521@users.noreply.github.com> Date: Thu, 16 May 2024 17:44:05 -0400 Subject: [PATCH 10/19] Update environment.yml --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 33722e4..6f5b259 100644 --- a/environment.yml +++ b/environment.yml @@ -3,7 +3,7 @@ channels: - conda-forge - defaults dependencies: - - python<3.11 + - python<=3.11 - westpa>=2022.03 - scikit-learn - matplotlib From 4f376687a507db53f83887504b3bb0c2440d64b1 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" <63817169+jeremyleung521@users.noreply.github.com> Date: Thu, 16 May 2024 17:49:19 -0400 Subject: [PATCH 11/19] Update environment-rtd.yml --- devtools/conda-envs/environment-rtd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/conda-envs/environment-rtd.yml b/devtools/conda-envs/environment-rtd.yml index bbcf848..20d03b0 100644 --- a/devtools/conda-envs/environment-rtd.yml +++ b/devtools/conda-envs/environment-rtd.yml @@ -3,7 +3,7 @@ channels: - conda-forge - defaults dependencies: - - python + - python<3.12 - westpa>=2022.03 - scikit-learn - tqdm From faf035b6cb48815138dcb6fc558acfbb4450d40b Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" <63817169+jeremyleung521@users.noreply.github.com> Date: Thu, 16 May 2024 17:49:40 -0400 Subject: [PATCH 12/19] Update environment.yml --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 6f5b259..593476c 100644 --- a/environment.yml +++ b/environment.yml @@ -3,7 +3,7 @@ channels: - conda-forge - defaults dependencies: - - python<=3.11 + - python<3.12 - westpa>=2022.03 - scikit-learn - matplotlib From 22e0eb621c1fee23e747fe9d09254d3875dc3456 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" Date: Fri, 31 May 2024 18:32:08 -0400 Subject: [PATCH 13/19] allow users to specify stride (needed for duration plots) and use 'agg' when plots are not shown --- lpath/plot.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lpath/plot.py b/lpath/plot.py index 7d2cd65..c775f21 100644 --- a/lpath/plot.py +++ b/lpath/plot.py @@ -151,7 +151,7 @@ def __init__(self, arguments): for pathway in self.pathways: non_zero = pathway[numpy.nonzero(pathway[:, 0])] # Removing padding frames... weights.append(non_zero[-1, -1]) - durations.append(len(non_zero)) + durations.append(len(non_zero) / arguments.stride) target_iter.append(non_zero[-1][0]) iter_num += [frame[0] for frame in non_zero] states.append(pathway[:, 2]) @@ -169,6 +169,7 @@ def __init__(self, arguments): self.num_iter = numpy.asarray(iter_num, dtype=object) self.target_iter = numpy.asarray(target_iter) self.num_clusters = len(path_indices) + self.stride = arguments.stride # weighted_counts = [numpy.sum(self.weights[self.states == i]) for i in range(self.num_clusters)] # self.weighted_counts = numpy.array(weighted_counts, dtype=float) @@ -192,6 +193,9 @@ def __init__(self, arguments): self.show_fig = arguments.dendrogram_show self.ax_idx = 0 + if not self.show_fig: + matplotlib.use('agg') + def plt_config(self, ax_idx=None, separate=None): """ Process matplotlib arguments and append fig/axis objects to class. From 704b9c91fd7861b7708f725f4d702a696afd2c32 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" Date: Mon, 3 Jun 2024 10:56:39 -0400 Subject: [PATCH 14/19] Update Docstrings --- lpath/match.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lpath/match.py b/lpath/match.py index 46c9792..ab6a982 100644 --- a/lpath/match.py +++ b/lpath/match.py @@ -438,11 +438,11 @@ def reassign_custom(data, pathways, dictionary, assign_file=None): def reassign_statelabel(data, pathways, dictionary, assign_file): """ - Use ``assign.h5`` states as is with ``statelabels``. Does not reclassify/assign frames + Use ``assign.h5`` states as is with ``state_labels``. Does not reclassify/assign frames into new states. - In this example, the dictionary maps state idx to its ``statelabels``, - as defined in the assign.h5. We suggest using alphabets as ``statelabels`` + In this example, the dictionary maps state idx to its ``state_labels``, + as defined in the assign.h5. We suggest using alphabets as ``state_labels`` to allow for more than 9 states. Parameters @@ -524,7 +524,7 @@ def reassign_segid(data, pathways, dictionary, assign_file=None): def reassign_identity(data, pathways, dictionary, assign_file=None): """ Use assign.h5 states as is. Does not attempt to map assignment - to ``statelabels`` from assign.h5. + to ``state_labels`` from assign.h5. Parameters ---------- From 02c6198e92b1484263719fd2035cc124395e83f8 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" Date: Mon, 3 Jun 2024 16:04:31 -0400 Subject: [PATCH 15/19] Up matplotlib dependency to >=3.6.0 --- environment.yml | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/environment.yml b/environment.yml index 593476c..99dfccc 100644 --- a/environment.yml +++ b/environment.yml @@ -6,7 +6,7 @@ dependencies: - python<3.12 - westpa>=2022.03 - scikit-learn - - matplotlib + - matplotlib>=3.6.0 - tqdm - networkx - pip diff --git a/pyproject.toml b/pyproject.toml index acc2217..6dad6da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ dependencies = [ "ray", "tqdm", "networkx", - "matplotlib", + "matplotlib>=3.6.0", "timedinput", "importlib-resources;python_version<'3.10'" ] @@ -55,7 +55,7 @@ westpa = [ "westpa>=2022.03" ] tui = [ - "argparse-tui" + "argparse-tui>0.2.4" ] dev = [ "lpath-md[test,westpa,tui]" From c5f957b2bb87275467f602490bf3e8af6e1f2a25 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" Date: Tue, 18 Jun 2024 17:48:19 -0400 Subject: [PATCH 16/19] add argparser.DefaultArgs for easier python usage --- lpath/argparser.py | 33 ++++++++++++++++++++++++++------- lpath/lpath.py | 2 -- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lpath/argparser.py b/lpath/argparser.py index 79e4c5e..a16349e 100644 --- a/lpath/argparser.py +++ b/lpath/argparser.py @@ -2,9 +2,10 @@ All argument parsing from commandline is dealt here. """ import argparse -from argparse import ArgumentTypeError -from lpath._logger import Logger +from argparse import ArgumentTypeError, Namespace from ast import literal_eval + +from lpath._logger import Logger from lpath.io import default_dendrogram_colors log = Logger().get_logger(__name__) @@ -21,6 +22,7 @@ class InvalidArgumentError(Exception): Custom Error for cases when invalid arguments are inputted. """ + def __init__(self, message="Invalid Argument."): self.message = message super().__init__(self.message) @@ -344,8 +346,9 @@ def add_extract_args(parser=None): help='Use Ray work manager. On by default.') raygroup.add_argument('--no-ray', '-NR', dest='use_ray', action='store_false', help='Do not use Ray. This overrides ``--use-ray``.') - raygroup.add_argument('--threads', '-t', type=check_non_neg, default=0, help='Number of threads to use ' - 'with Ray. The default of ``0`` uses all available resources detected.') + raygroup.add_argument('--threads', '-t', type=check_non_neg, default=0, + help='Number of threads to use with Ray. The default of ``0`` uses ' + 'all available resources detected.') extract_we = parser.add_argument_group('WE-specific Extract Parameters') @@ -406,10 +409,10 @@ def add_match_args(parser=None): match_io.add_argument('--input-pickle', '-ip', '--IP', '--pickle', dest='extract_output', default='succ_traj/output.pickle', type=str, help='Path to pickle object from the `extract` ' - 'step.') + 'step.') match_io.add_argument('--output-pickle', '-op', '--OP', dest='output_pickle', default='succ_traj/pathways.pickle', type=str, help='Path to reassigned object to be ' - 'outputted from the `match` step.') + 'outputted from the `match` step.') match_io.add_argument('--cl-output', '-co', '--cluster-label-output', '--cluster-labels-output', dest='cl_output', default='succ_traj/cluster_labels.npy', type=str, help='Output file location for cluster labels.') @@ -461,7 +464,7 @@ def add_match_args(parser=None): help='Do not remake distance matrix.') match_io.add_argument('--remake-file', '--remade-file', '-dF', dest='dmatrix_save', type=str, default='succ_traj/distmat.npy', help='Path to pre-calculated distance matrix. Make sure ' - 'the ``--no-remake`` flag is specified.') + 'the ``--no-remake`` flag is specified.') match_io.add_argument('--remake-parallel', '-dP', dest='dmatrix_parallel', type=int, help='Number of jobs to run with the pairwise distance calculations. The default=None issues ' 'one job. A value of -1 uses all available resources. This is directly passed to the ' @@ -766,3 +769,19 @@ def check_argv(): if 1 < len(sys.argv) < 3 and sys.argv[1] in all_options: log.warning(f'Running {sys.argv[1]} with all default values. Make sure you\'re sure of this!') + + +class DefaultArgs: + """ + Convenience class that could be used to call all the default arguments for each subparser. + """ + def __init__(self): + self.parser = create_parser() + self.subparsers = [] + self.parser, self.subparsers = create_subparsers(self.parser, self.subparsers) + + self.discretize = self.subparsers[0].parse_args('') + self.extract = self.subparsers[1].parse_args('') + self.match = self.subparsers[2].parse_args('') + self.plot = self.subparsers[3].parse_args('') + self.all = self.subparsers[4].parse_args('') diff --git a/lpath/lpath.py b/lpath/lpath.py index 3534dd4..68d28dd 100644 --- a/lpath/lpath.py +++ b/lpath/lpath.py @@ -48,8 +48,6 @@ def entry_point(): argparser.check_argv() - parser.print_usage() - # print(parser.__dict__) args = argparser.process_args(parser) log.info(f'LPATH arguments: {args}') From 6f10cd49f0e719de4f21bcee2b4d14a45dd2fd3a Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" Date: Tue, 18 Jun 2024 18:04:08 -0400 Subject: [PATCH 17/19] small formatting error in argparser --- lpath/argparser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lpath/argparser.py b/lpath/argparser.py index a16349e..1dee8c6 100644 --- a/lpath/argparser.py +++ b/lpath/argparser.py @@ -22,7 +22,6 @@ class InvalidArgumentError(Exception): Custom Error for cases when invalid arguments are inputted. """ - def __init__(self, message="Invalid Argument."): self.message = message super().__init__(self.message) From b2a11eb66ac0cd1dc1541dc69c191b8e45a3b123 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" Date: Thu, 27 Jun 2024 16:39:55 -0400 Subject: [PATCH 18/19] hide syntax warning from _logger --- pytest.ini | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..7d22e51 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +filterwarnings = + ignore:.*invalid escape sequence.*:SyntaxWarning From 6a301fa661460517586d8687a1cd7f0cb35e1163 Mon Sep 17 00:00:00 2001 From: "Jeremy M. G. Leung" Date: Thu, 27 Jun 2024 16:43:36 -0400 Subject: [PATCH 19/19] could also be a DeprecationWarning --- pytest.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/pytest.ini b/pytest.ini index 7d22e51..8bc57df 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,4 @@ [pytest] filterwarnings = ignore:.*invalid escape sequence.*:SyntaxWarning + ignore:.*invalid escape sequence.*:DeprecationWarning