Repository 'hyphy_sm19'
hg clone https://toolshed.g2.bx.psu.edu/repos/iuc/hyphy_sm19

Changeset 39:07774580b606 (2026-03-11)
Previous changeset 38:9f71cd7b2554 (2026-02-25)
Commit message:
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/main/tools/hyphy commit cee1ce4bd7d82088b9bf62403bc175c13223e020
modified:
macros.xml
added:
scripts/infer_stasis_clusters.py
test-data/bstill-in1.json
removed:
scripts/hyphy_summary.py
b
diff -r 9f71cd7b2554 -r 07774580b606 macros.xml
--- a/macros.xml Wed Feb 25 20:58:10 2026 +0000
+++ b/macros.xml Wed Mar 11 11:17:55 2026 +0000
b
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <macros>
-    <token name="@TOOL_VERSION@">2.5.93</token>
-    <token name="@VERSION_SUFFIX@">3</token>
+    <token name="@TOOL_VERSION@">2.5.96</token>
+    <token name="@VERSION_SUFFIX@">0</token>
     <token name="@PROFILE@">24.0</token>
     <token name="@SHELL_OPTIONS@">export TERM="vt100"; </token>
 
b
diff -r 9f71cd7b2554 -r 07774580b606 scripts/hyphy_summary.py
--- a/scripts/hyphy_summary.py Wed Feb 25 20:58:10 2026 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
[
b"@@ -1,679 +0,0 @@\n-import argparse\n-import json\n-import re\n-from collections import defaultdict\n-\n-import BioExt\n-from Bio import SeqIO\n-from Bio.Seq import Seq\n-from Bio.SeqRecord import SeqRecord\n-from BioExt.uds import _align_par\n-\n-\n-class HyPhySummary(object):\n-\n-    def __init__(self, arguments, summary_json=None, annotation_json=None):\n-        self.arguments = arguments\n-        self.ref_map = ''\n-        self.summary_json = {}\n-        self.annotation_json = {}\n-        self.include_in_annotation = {}\n-        self.test_map = {}\n-        self.site_reports = {}\n-        self.labels = {}\n-        self.ref_seq_map = []\n-        self.cfel = {}\n-        self.relax = {}\n-        self.busted = {}\n-        self.slac = {}\n-        self.fel = {}\n-        self.meme = {}\n-        self.meme_full = {}\n-        self.prime = {}\n-        self.fade = {}\n-        self.bgm = {}\n-        self.ref_genes = [\n-            ['genome', 'ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTCGTCCGGGTGTGACCGAAAGGTAAGATGGAGAGCCTTGTCCCTGGTTTCAACGAGAAAACACACGTCCAACTCAGTTTGCCTGTTTTACAGGTTCGCGACGTGCTCGTACGTGGCTTTGGAGACTCCGTGGAGGAGGTCTTATCAGAGGCACGTCAACATCTTAAAGATGGCACTTGTGGCTTAGTAGAAGTTGAAAAAGGCGTTTTGCCTCAACTTGAACAGCCCTATGTGTTCATCAAACGTTCGGATGCTCGAACTGCACCTCATGGTCATGTTATGGTTGAGCTGGTAGCAGAACTCGAAGGCATTCAGTACGGTCGTAGTGGTGAGACACTTGGTGTCCTTGTCCCTCATGTGGGCGAAATACCAGTGGCTTACCGCAAGGTTCTTCTTCGTAAGAACGGTAATAAAGGAGCTGGTGGCCATAGTTACGGCGCCGATCTAAAGTCATTTGACTTAGGCGACGAGCTTGGCACTGATCCTTATGAAGATTTTCAAGAAAACTGGAACACTAAACATAGCAGTGGTGTTACCCGTGAACTCATGCGTGAGCTTAACGGAGGGGCATACACTCGCTATGTCGATAACAACTTCTGTGGCCCTGATGGCTACCCTCTTGAGTGCATTAAAGACCTTCTAGCACGTGCTGGTAAAGCTTCATGCACTTTGTCCGAACAACTGGACTTTATTGACACTAAGAGGGGTGTATACTGCTGCCGTGAACATGAGCATGAAATTGCTTGGTACACGGAACGTTCTGAAAAGAGCTATGAATTGCAGACACCTTTTGAAATTAAATTGGCAAAGAAATTTGACACCTTCAATGGGGAATGTCCAAATTTTGTATTTCCCTTAAATTCCATAATCAAGACTATTCAACCAAGGGTTGAAAAGAAAAAGCTTGATGGCTTTATGGGTAGAATTCGATCTGTCTATCCAGTTGCGTCACCAAATGAATGCAACCAAATGTGCCTTTCAACTCTCATGAAGTGTGATCATTGTGGTGAAACTTCATGGCAGACGGGCGATTTTGTTAAAGCCACTTGCGAATTTTGTGGCACTGAGAATTTGACTAAAGAAGGTGCCACTACTTGTGGTTACTTACCCCAAAATGCTGTTGTTAAAATTTATTGTCCAGCATGTCACAATTCAGAAGTAGGACCTGAGCATAGTCTTGCCGAATACCATAATGAATCTGGCTTGAAAACCATTCTTCGTAAGGGTGGTCGCACTATTGCCTTTGGAGGCTGTGTGTTCTCTTATGTTGGTTGCCATAACAAGTGTGCCTATTGGGTTCCACGTGCTAGCGCTAACATAGGTTGTAACCATACAGGTGTTGTTGGAGAAGGTTCCGAAGGTCTTAATGACAACCTTCTTGAAATACTCCAAAAAGAGAAAGTCAACATCAATATTGTTGGTGACTTTAAACTTAATGAAGAGATCGCCATTATTTTGGCATCTTTTTCTGCTTCCACAAGTGCTTTTGTGGAAACTGTGAAAGGTTTGGATTATAAAGCATTCAAACAAATTGTTGAATCCTGTGGTAATTTTAAAGTTACAAAAGGAAAAGCTAAAAAAGGTGCCTGGAATATTGGTGAACAGAAATCAATACTGAGTCCTCTTTATGCATTTGCATCAGAGGCTGCTCGTGTTGTACGATCAATTTTCTCCCGCACTCTTGAAACTGCTCAAAATTCTGTGCGTGTTTTACAGAAGGCCGCTATAACAATACTAGATGGAATTTCACAGTATTCACTGAGACTCATTGATGCTATGATGTTCACATCTGATTTGGCTACTAACAATCTAGTTGTAATGGCCTACATTACAGGTGGTGTTGTTCAGTTGACTTCGCAGTGGCTAACTAACATCTTTGGCACTGTTTATGAAAAACTCAAACCCGTCCTTGATTGGCTTGAAGAGAAGTTTAAGGAAGGTGTAGAGTTTCTTAGAGACGGTTGGGAAATTGTTAAATTTATCTCAACCTGTGCTTGTGAAATTGTCGGTGGACAAATTGTCACCTGTGCAAAGGAAATTAAGGAGAGTGTTCAGACATTCTTTAAGCTTGTAAATAAATTTTTGGCTTTGTGTGCTGACTCTATCATTATTGGTGGAGCTAAACTTAAAGCCTTGAATTTAGGTGAAACATTTGTCACGCACTCAAAGGGATTGTACAGAAAGTGTGTTAAATCCAGAGAAGAAACTGGCCTACTCATGCCTCTAAAAGCCCCAAAAGAAATTATCTTCTTAGAGGGAGAAACACTTCCCACAGAAGTGTTAACAGAGGAAGTTGTCTTGAAAACTGGTGATTTACAACCATTAGAACAACCTACTAGTGAAGCTGTTGAAGCTCCATTGGTTGGTACACCAGTTTGTATTAACGGGCTTATGTTGCTCGAAATCAAAGACACAGAAAAGTACTGTGCCCTTGCACCTAATATGATGGTAACAAACAATACCTTCACACTCAAAGGCGGTGCACCAACAAAGGTTACTTTTGGTGATGACACTGTGATAGAAGTGCAAGGTTACAAGAGTGTGAATATCACTTTTGAACTTGATGAAAGGATTGATAAAGTACTTAATGAGAAGTGCTCTGCCTATACAGTTGAACTCGGTACAGAAGTAAATGAGTTCGCCTGTGTTGTGGCAGATGCTGTCATAAAAACTTTGCAACCAGTATCTGAATTACTTACACCACTGGGCATTGATTTAGATGAGTGGAGTATGGCTACATACTACTTATTTGATGAGTCTGGTGAGTTTAAATTGGCTTCACATATGTATTGTTCTTTCTACCCTCCAGATGAGGATGAAGAAGAAGG"..b"                       return generate_error(char_index)\n-                    current_node_annotation += current_char\n-        except Exception as e:\n-            return generate_error(char_index)\n-            print(e)\n-\n-    if (len(clade_stack) != 1):\n-        return generate_error(len(nwk_str) - 1)\n-\n-    if (len(current_node_name)):\n-        tree_json['name'] = current_node_name\n-\n-    return {\n-        'json': tree_json,\n-        'error': None\n-    }\n-\n-\n-if __name__ == '__main__':\n-    parser = argparse.ArgumentParser(description='Summarize selection analysis results.')\n-    parser.add_argument('--combined', help='Combined reference and query alignment from TN-93', required=False, type=str)\n-    parser.add_argument('--pvalue', help='p-value to use', required=False, type=float, default=0.05)\n-    parser.add_argument('--gene', help='Name of the gene or sequence being analyzed', required=False, type=str)\n-    parser.add_argument('--labels', help='JSON file with labels', required=False, type=str)\n-    parser.add_argument('--annotation-output', help='Write a JSON file with site annotations', required=True, type=str)\n-    parser.add_argument('--summary-output', help='Write a JSON file here segment annotations', required=True, type=str)\n-    parser.add_argument('--annotation-inputs', help='Comma-separated list of site annotation files to merge', required=False, type=str)\n-    parser.add_argument('--summary-inputs', help='Comma-separated list of segment annotation files to merge', required=False, type=str)\n-    parser.add_argument('--default-tag', help='Default name for sequences that have no explicit label', required=False, type=str, default='Reference')\n-    parser.add_argument('--name', help='The sequence ID to highlight', required=False, default='MN908947')\n-    parser.add_argument('--mode', help='Operation mode, generate a summary or merge multiple summaries', type=str, choices=['summary', 'merge'], default='summary')\n-    parser.add_argument('--relax', help='Path to RELAX.json file', required=False, type=str)\n-    parser.add_argument('--busted', help='Path to BUSTED.json file', required=False, type=str)\n-    parser.add_argument('--slac', help='Path to SLAC.json file', required=False, type=str)\n-    parser.add_argument('--fel', help='Path to FEL.json file', required=False, type=str)\n-    parser.add_argument('--cfel', help='Path to CFEL.json file', required=False, type=str)\n-    parser.add_argument('--meme', help='Path to MEME.json file', required=False, type=str)\n-    parser.add_argument('--meme-full', help='Path to MEME-full.json file', dest='meme_full', required=False, type=str)\n-    parser.add_argument('--prime', help='Path to PRIME.json file', required=False, type=str)\n-    parser.add_argument('--fade', help='Path to FADE.json file', required=False, type=str)\n-    parser.add_argument('--bgm', help='Path to BGM.json file', required=False, type=str)\n-    arguments = parser.parse_args()\n-    if arguments.mode == 'summary':\n-        analyzer = HyPhySummary(arguments)\n-        annotation_json, summary_json = analyzer.summary()\n-        if annotation_json is not None:\n-            with open(arguments.annotation_output, 'w') as fh:\n-                json.dump(annotation_json, fh, indent=1)\n-        if summary_json is not None:\n-            with open(arguments.summary_output, 'w') as fh:\n-                json.dump(summary_json, fh, indent=1)\n-    else:\n-        summary = {}\n-        annotation = {}\n-        for filename in arguments.annotation_inputs.split(','):\n-            with open(filename, 'r') as fh:\n-                annotation.update(json.load(fh))\n-        for filename in arguments.summary_inputs.split(','):\n-            with open(filename, 'r') as fh:\n-                summary.update(json.load(fh))\n-        with open(arguments.annotation_output, 'w') as fh:\n-            json.dump(annotation, fh, indent=1)\n-        with open(arguments.summary_output, 'w') as fh:\n-            json.dump(summary, fh, indent=1)\n-    exit(0)\n"
b
diff -r 9f71cd7b2554 -r 07774580b606 scripts/infer_stasis_clusters.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/infer_stasis_clusters.py Wed Mar 11 11:17:55 2026 +0000
[
@@ -0,0 +1,171 @@
+#!/usr/bin/env python3
+"""
+B-STILL Stasis Cluster Inference Tool
+====================================
+Identifies regional footprints of extreme purifying selection (stasis) in B-STILL
+JSON results using a FWER-controlled Hypergeometric Scan Statistic.
+
+Usage:
+    python3 infer_stasis_clusters.py input.json --ebf 10 --permutations 10000 --output results.json
+"""
+
+import argparse
+import json
+import sys
+import time
+
+import numpy as np
+from scipy.stats import hypergeom
+
+
+def get_sf_optimized(n, d, L, K, cache):
+    """Retrieves or computes Hypergeometric Survival Function value."""
+    key = (n, d)
+    if key not in cache:
+        cache[key] = hypergeom.sf(n - 1, L, K, d)
+    return cache[key]
+
+
+def scan_intervals(indices, L, K, max_size, sf_cache, threshold=None):
+    """
+    Scans all possible intervals [i, j] anchored by stasis events.
+    Returns the minimum p-value if threshold is None, else returns all significant segments.
+    """
+    best_p = 1.0
+    segments = []
+    num_events = len(indices)
+
+    for n in range(3, min(max_size + 1, num_events + 1)):
+        for i in range(num_events - n + 1):
+            d = indices[i + n - 1] - indices[i] + 1
+            p = get_sf_optimized(n, d, L, K, sf_cache)
+
+            if threshold is None:
+                if p < best_p:
+                    best_p = p
+            else:
+                if p <= threshold:
+                    segments.append({
+                        "start": int(indices[i] + 1),
+                        "end": int(indices[i + n - 1] + 1),
+                        "p_value": p,
+                        "k": n,
+                        "d": int(d)
+                    })
+
+    return best_p if threshold is None else segments
+
+
+def merge_segments(segments, merge_dist=15):
+    """Merges overlapping or nearby significant segments."""
+    if not segments:
+        return []
+    segments.sort(key=lambda x: x['start'])
+
+    merged = []
+    curr = segments[0]
+    for next_s in segments[1:]:
+        if next_s['start'] <= curr['end'] + merge_dist:
+            curr['end'] = max(curr['end'], next_s['end'])
+            curr['p_value'] = min(curr['p_value'], next_s['p_value'])
+            curr['d'] = curr['end'] - curr['start'] + 1
+        else:
+            merged.append(curr)
+            curr = next_s
+    merged.append(curr)
+    return merged
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Infer stasis clusters from B-STILL JSON.")
+    parser.add_argument("input", help="Path to B-STILL JSON result file")
+    parser.add_argument("--ebf", type=float, default=10.0, help="EBF threshold for defining stasis sites (default: 10.0)")
+    parser.add_argument("--permutations", type=int, default=10000, help="Number of permutations for FWER control (default: 10000)")
+    parser.add_argument("--alpha", type=float, default=0.05, help="Family-wise error rate threshold (default: 0.05)")
+    parser.add_argument("--max-cluster", type=int, default=30, help="Maximum number of stasis sites per interval scan (default: 30)")
+    parser.add_argument("--merge", type=int, default=15, help="Distance in codons to merge adjacent clusters (default: 15)")
+    parser.add_argument("--output", help="Path to save results in JSON format")
+
+    args = parser.parse_args()
+
+    try:
+        with open(args.input, "r") as f:
+            data = json.load(f)
+    except Exception as e:
+        print("Error loading JSON: {0}".format(e))
+        sys.exit(1)
+
+    sites = data.get("MLE", {}).get("content", {}).get("0", [])
+    ebfs = [s[12] if (len(s) > 12 and isinstance(s[12], (int, float))) else 0 for s in sites]
+    L = len(ebfs)
+
+    if L < 10:
+        print("Alignment too short for cluster analysis.")
+        sys.exit(0)
+
+    stasis_indices = np.array([i for i, val in enumerate(ebfs) if val >= args.ebf])
+    K = len(stasis_indices)
+
+    print("--- B-STILL Cluster Inference ---")
+    print("Input: {0}".format(args.input))
+    print("Gene Length (L): {0} codons".format(L))
+    print("Stasis Sites (K): {0} (EBF >= {1})".format(K, args.ebf))
+
+    if K < 3:
+        print("Insufficient stasis sites to form clusters (minimum 3 required).")
+        sys.exit(0)
+
+    print("Running {0} permutations for FWER control...".format(args.permutations))
+    null_min_ps = []
+    all_positions = np.arange(L)
+    sf_cache = {}
+
+    start_time = time.time()
+    for i in range(args.permutations):
+        if i > 0 and i % 1000 == 0:
+            elapsed = time.time() - start_time
+            print("  Processed {0} permutations... ({1:.1f} per sec)".format(i, i / elapsed))
+        shuffled = sorted(np.random.choice(all_positions, K, replace=False))
+        min_p = scan_intervals(shuffled, L, K, args.max_cluster, sf_cache)
+        null_min_ps.append(min_p)
+
+    crit_p = np.percentile(null_min_ps, args.alpha * 100)
+    print("Gene-specific Critical P-value (FWER {0}): {1:.2e}".format(args.alpha, crit_p))
+
+    print("Scanning observed sequence for significant clusters...")
+    raw_segments = scan_intervals(stasis_indices, L, K, args.max_cluster, sf_cache, threshold=crit_p)
+
+    final_clusters = merge_segments(raw_segments, merge_dist=args.merge)
+
+    for c in final_clusters:
+        c['k'] = sum(1 for idx in stasis_indices if c['start'] <= idx + 1 <= c['end'])
+
+    print("\nFound {0} significant stasis clusters:".format(len(final_clusters)))
+    if final_clusters:
+        print("\nLegend:")
+        print("  k : Number of high-confidence stasis sites within the cluster")
+        print("  d : Total span of the cluster in codons")
+        print("\n{:<8} | {:<8} | {:<5} | {:<5} | {:<10}".format("Start", "End", "k", "d", "P-value"))
+        print("-" * 45)
+        for c in final_clusters:
+            print("{:<8} | {:<8} | {:<5} | {:<5} | {:.2e}".format(c['start'], c['end'], c['k'], c['d'], c['p_value']))
+
+    if args.output:
+        output_data = {
+            "input_file": args.input,
+            "parameters": vars(args),
+            "summary": {
+                "gene_length": L,
+                "total_stasis_sites": K,
+                "critical_p_value": float(crit_p),
+                "num_clusters": len(final_clusters)
+            },
+            "clusters": final_clusters
+        }
+        with open(args.output, "w") as f:
+            json.dump(output_data, f, indent=4)
+        print("\nDetailed results saved to {0}".format(args.output))
+
+
+if __name__ == "__main__":
+    main()
b
diff -r 9f71cd7b2554 -r 07774580b606 test-data/bstill-in1.json
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/bstill-in1.json Wed Mar 11 11:17:55 2026 +0000
[
b'@@ -0,0 +1,1880 @@\n+{\n+  "MLE": {\n+    "content": {\n+      "0": [\n+        [\n+          2.37190144157317,\n+          0.1886402906758332,\n+          0.003967400746549269,\n+          0.03170401941783556,\n+          0.1405070629863407,\n+          0.2478544806315437,\n+          0.155938987532467,\n+          0,\n+          0,\n+          1.668673069222721,\n+          0.9606238475229101,\n+          1.774898950889347,\n+          1.224638397608354\n+        ],\n+        [\n+          1.562666730096422,\n+          0.7176422560937019,\n+          0,\n+          9.089104346594827e-07,\n+          0,\n+          0.01450154962095004,\n+          0.2139088787662019,\n+          0,\n+          0,\n+          0,\n+          2.666666171615278e-05,\n+          0,\n+          0.05468540563699053\n+        ],\n+        [\n+          0.6841064654452979,\n+          0.6942845364725866,\n+          0,\n+          0.05269358336084695,\n+          0,\n+          0.07725223791333098,\n+          0.5503080822201034,\n+          0,\n+          0,\n+          0,\n+          1.631978457912503,\n+          0,\n+          0.311129391337898\n+        ],\n+        [\n+          1.603530309019734,\n+          0.7640979882187913,\n+          0,\n+          9.480689290490966e-08,\n+          0,\n+          0.0129282189776549,\n+          0.2196957351152485,\n+          0,\n+          0,\n+          0,\n+          2.781551610416608e-06,\n+          0,\n+          0.0486746608276061\n+        ],\n+        [\n+          0.7745431936738028,\n+          0.2481102774238383,\n+          0.005522827981575777,\n+          0.05017493786864266,\n+          0.1167934725144055,\n+          0.3301340073712549,\n+          0.2680133904625786,\n+          0,\n+          0,\n+          2.326512750426494,\n+          1.549852564474649,\n+          1.435734346731007,\n+          1.831535344411821\n+        ],\n+        [\n+          0.7291297549930137,\n+          0.2232251727035806,\n+          0.006100143264891383,\n+          0.05237454314416388,\n+          0.1226528460847873,\n+          0.3565428751858024,\n+          0.2618162231862851,\n+          0,\n+          0,\n+          2.57120169474055,\n+          1.62155131319508,\n+          1.51783284915089,\n+          2.059231243604498\n+        ],\n+        [\n+          0.5299477744525759,\n+          0.3199604238306135,\n+          0.006259406053599633,\n+          0.06427776426846346,\n+          0.09952410556612061,\n+          0.3591418826984177,\n+          0.3634880853401729,\n+          0,\n+          0,\n+          2.638753567868953,\n+          2.015398729009674,\n+          1.199979925906953,\n+          2.08265405394555\n+        ],\n+        [\n+          0.7850622182406316,\n+          0.208593049315937,\n+          0.006006859903783282,\n+          0.04987146289298329,\n+          0.1278017467009314,\n+          0.3521879280501232,\n+          0.2426664534291802,\n+          0,\n+          0,\n+          2.531645281107513,\n+          1.53998649735195,\n+          1.590887120603373,\n+          2.020404836198384\n+        ],\n+        [\n+          0.7353234573836737,\n+          0.7975030154821481,\n+          0,\n+          0.05027743242304463,\n+          0,\n+          0.06341157982977386,\n+          0.5594116332078475,\n+          0,\n+          0,\n+          0,\n+          1.55318611910382,\n+          0,\n+          0.2516128169853178\n+        ],\n+        [\n+          0.7745431936738028,\n+          0.2481102774238383,\n+          0.005522827981575777,\n+          0.05017493786864266,\n+          0.1167934725144055,\n+          0.3301340073712549,\n+          0.2680133904625786,\n+          0,\n+          0,\n+          2.326512750426494,\n+          1.549852564474649,\n+          1.435734346731007,\n+          1.831535344411821\n+        ],\n+        [\n+          1.368503706104576,\n+          1.018443324506961,\n+          0,\n+          0.0001254498320607177,\n+          0,\n+          0.01193786625873712,\n+          0.3202018713208712,\n+          0,\n+          0,\n+          0,\n+          0.003681050177071567,\n+          0,\n+          '..b':1e-10)Node123:0.000622,(CY173095:0.001245,(((CY173191:1e-10,CY037703:0.001247)Node132:0.000622,CY035062:0.00249)Node131:1e-10,((CY173255:0.001871,(CY044748:1e-10,GQ385846:0.001247)Node139:0.000623)Node137:1e-10,((CY027075:0.000623,(EU199367:1e-10,CY172823:0.000623)Node145:1e-10)Node143:0.000623,(((CY172847:0.000623,CY025643:1e-10)Node150:0.000623,CY172839:0.000623)Node149:0.001247,(((CY026251:0.002497,CY092241:0.00125)Node156:0.000624,CY026019:0.000623)Node155:1e-10,((CY025341:0.002502,(CY172775:0.001247,CY172903:0.000623)Node163:0.001248)Node161:0.000622,(EU199255:0.003134,(CY172431:0.000621,((CY172223:0.003127,(CY172191:0.001254,CY020069:0.000628)Node173:0.000621)Node171:1e-10,((CY092217:0.003119,(CY025485:0.001283,EU516019:0.00059)Node179:0.008843)Node177:0.001868,(CY002080:0.000622,((CY002064:0.001873,(CY002456:0.000626,CY002048:0.002491)Node187:0.001243)Node185:0.003763,(AB434109:0.006307,(((CY088198:0.001245,CY088475:0.000621)Node194:1e-10,CY000257:0.000621)Node193:0.002494,((CY112957:0.003135,CY006859:0.001253)Node199:0.000616,((CY000721:0.001243,(CY114493:1e-10,CY090885:0.002483)Node205:0.002488)Node203:1e-10,((CY001792:0.000621,(CY001600:0.00062,CY002368:0.001865)Node211:1e-10)Node209:1e-10,((CY002304:0.002489,(CY006163:0.001242,CY003632:0.00062)Node217:1e-10)Node215:0.00062,(CY001920:0.003739,(CY001912:0.002498,((CY001504:1e-10,CY006060:0.001241)Node225:0.00062,((CY001744:1e-10,CY002136:0.00124)Node229:0.00062,((CY114309:0.003746,(CY006899:0.001874,CY112901:0.001863)Node235:0.002496)Node233:0.000618,(((CY006579:0.001245,CY006283:0.000621)Node240:0.001244,CY007979:0.001864)Node239:1e-10,((CY006499:0.001241,(CY006491:0.001863,CY006635:0.00124)Node247:1e-10)Node245:0.00062,(((CY036847:0.002495,CY010004:0.001867)Node252:0.000621,CY012200:0.000621)Node251:1e-10,(((CY010028:0.001242,CY009732:0.001242)Node258:1e-10,CY010012:0.002484)Node257:0.001875,(((CY010020:0.002497,CY011416:0.001237)Node264:0.001252,CY009484:0.00062)Node263:0.005658,((CY010036:1e-10,(CY039879:0.002489,CY039880:0.000621)Node271:0.002487)Node269:0.003109,((CY010628:0.000619,(CY010716:0.002485,CY010516:0.001239)Node277:1e-10)Node275:0.000619,((CY012728:1e-10,CY013701:0.000621)Node281:0.001243,(((CY012760:0.001239,CY013200:0.001239)Node286:1e-10,CY011888:0.005614)Node285:1e-10,((CY013693:0.000619,(CY012184:0.002484,CY013669:0.00124)Node293:0.00124)Node291:1e-10,((CY112669:0.004353,(CY112556:0.001861,CY011896:0.000619)Node299:0.00186)Node297:1e-10,(CY112605:0.001243,(CY012224:0.006953,((CY012512:0.000619,(CY012896:0.00062,CY012232:0.00062)Node309:0.000621)Node307:1e-10,(CY011848:1e-10,(((CY011328:1e-10,CY114221:0.000619)Node316:0.001241,CY011560:0.00124)Node315:1e-10,((CY012456:0.000619,CY011824:0.00124)Node321:1e-10,CY017283:0.000619)Node320:1e-10)Node314:1e-10)Node312:1e-10)Node306:0.002439)Node304:0.008263)Node302:0.000618)Node296:1e-10)Node290:1e-10)Node284:0.000617)Node280:0.012609)Node274:0.000619)Node268:0.001215)Node262:0.003782)Node256:0.000613)Node250:0.006878)Node244:1e-10)Node238:0.001862)Node232:1e-10)Node228:1e-10)Node224:0.001863)Node222:0.001862)Node220:0.001245)Node214:1e-10)Node208:0.001241)Node202:0.021084)Node198:0.000623)Node192:0.00065)Node190:0.003713)Node184:0.002495)Node182:1e-10)Node176:0.005637)Node170:0.00313)Node168:0.002515)Node166:0.001876)Node160:1e-10)Node154:0.000623)Node148:1e-10)Node142:0.003749)Node136:1e-10)Node130:1e-10)Node128:0.000623)Node122:0.000623)Node116:0.001872)Node114:0.002503)Node110:1e-10)Node104:1e-10)Node102:0.000623)Node96:0.001468)Node90:0.00189)Node84:0.002721)Node78:0.002524)Node76:1e-10)Node74:0.000624)Node72:1e-10)Node66:1e-10)Node60:1e-10)Node58:0.000623)Node54:0.001248)Node48:0.001255)Node46:0.000619)Node40:1e-10)Node34:1e-10)Node28:1e-10)Node22:0.000623)Node16:1e-10)Node10:1e-10)Node4:1e-10)"\n+    }\n+  },\n+  "_comment": "Trimmed HyPhy B-STILL output (first 120 codon sites, first 13 metrics, removed posterior/grid/etc) to keep infer_stasis_clusters test input under planemo\'s 1MB lint limit."\n+}\n'