archaeopteryx 3.5.0 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/archaeopteryx.js +2 -2
- package/forester.js +37 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -296,8 +296,11 @@ so every branch and position is scored by how far the tip pairs' ancestors
|
|
|
296
296
|
fall from that halfway point, and the root goes where that deviation is
|
|
297
297
|
smallest [1]. It has been compared with other rooting methods on prokaryotic
|
|
298
298
|
gene families [2]. It needs branch lengths and at least three tips, and is
|
|
299
|
-
offered only then.
|
|
300
|
-
|
|
299
|
+
offered only then. Tip pairs closer than 1/100,000 of the tree's diameter
|
|
300
|
+
(identical sequences, or FastTree's 5 × 10⁻⁹ stand-in for a zero branch) are
|
|
301
|
+
left out of the deviation sums: their deviation measures noise, and summing
|
|
302
|
+
it would swamp the precision of all the others. The algorithm is the desktop
|
|
303
|
+
Archaeopteryx's, and gives the same roots; it runs in O(n²) time and O(n) memory (the 13,246-tip H5N1
|
|
301
304
|
demo tree roots in 0.4 s, measured in Node).
|
|
302
305
|
|
|
303
306
|
Every internal branch then carries its **MAD value**: the root-mean-square
|
package/archaeopteryx.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
// v 3.5.
|
|
23
|
+
// v 3.5.1
|
|
24
24
|
// 2026-09-10
|
|
25
25
|
//
|
|
26
26
|
// Archaeopteryx.js is a software tool for the visualization and
|
|
@@ -103,7 +103,7 @@ function (root, d3, forester, phyloXml) {
|
|
|
103
103
|
// IIFE's own function name -- a plain object says what it is.)
|
|
104
104
|
let archaeopteryx = {};
|
|
105
105
|
|
|
106
|
-
const VERSION = '3.5.
|
|
106
|
+
const VERSION = '3.5.1';
|
|
107
107
|
const WEBSITE = 'https://cmzmasek.github.io/archaeopteryx-js/';
|
|
108
108
|
const DESKTOP_WEBSITE = 'https://cmzmasek.github.io/archaeopteryx/';
|
|
109
109
|
const SOURCE_WEBSITE = 'https://github.com/cmzmasek/archaeopteryx-js';
|
package/forester.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
// v 3.5.
|
|
23
|
+
// v 3.5.1
|
|
24
24
|
// 2026-09-10
|
|
25
25
|
//
|
|
26
26
|
// forester.js is a general suite for dealing with phylogenetic trees.
|
|
@@ -455,6 +455,14 @@
|
|
|
455
455
|
forester.MAD_CONFIDENCE_TYPE = 'MAD';
|
|
456
456
|
|
|
457
457
|
const MAD_EPSILON = 1e-9;
|
|
458
|
+
// Tip pairs closer than this share of the tree's diameter are left out of
|
|
459
|
+
// every sum, like pairs at distance 0. Their 1/d^2 terms (tips 1e-8 apart,
|
|
460
|
+
// as FastTree's 5e-9 "zero" branches make them, reach 1e16) cancel
|
|
461
|
+
// catastrophically in the subtractions below and swamp every other pair:
|
|
462
|
+
// MAD then answered differently from every rooting, and re-rooting moved
|
|
463
|
+
// the root back and forth. The diameter does not depend on the rooting, so
|
|
464
|
+
// neither does the cut. Joint with the desktop (Christian, 2026-09-14).
|
|
465
|
+
const MAD_NEAR_FRACTION = 1e-5;
|
|
458
466
|
|
|
459
467
|
/**
|
|
460
468
|
* Roots the tree by Minimal Ancestor Deviation (Tria, Landan & Dagan,
|
|
@@ -472,6 +480,11 @@
|
|
|
472
480
|
* and the root's branch carries the smallest. Pendant branches get none,
|
|
473
481
|
* and MAD values from an earlier run are replaced.
|
|
474
482
|
*
|
|
483
|
+
* Tip pairs closer than 1e-5 of the tree's diameter (identical sequences,
|
|
484
|
+
* FastTree's 5e-9 "zero" branches) are left out of the deviation sums:
|
|
485
|
+
* their deviation measures noise, and their terms would swamp the
|
|
486
|
+
* precision of all the others.
|
|
487
|
+
*
|
|
475
488
|
* A no-op for fewer than three tips or a tree without branch lengths.
|
|
476
489
|
* O(n^2) time and O(n) memory: the desktop fills an n x n distance matrix
|
|
477
490
|
* only to sum its columns (1.4 GB at 13,000 tips), so the column sums are
|
|
@@ -513,6 +526,27 @@
|
|
|
513
526
|
if (maxDepth <= 0) {
|
|
514
527
|
return false; // no usable branch lengths
|
|
515
528
|
}
|
|
529
|
+
// the diameter, the longest tip-to-tip path: at every node, its two
|
|
530
|
+
// longest paths down through different children
|
|
531
|
+
let longestDown = new Float64Array(m);
|
|
532
|
+
let diameter = 0;
|
|
533
|
+
for (let q = 0; q < m; ++q) {
|
|
534
|
+
let k = post[q];
|
|
535
|
+
let first = 0;
|
|
536
|
+
let second = 0;
|
|
537
|
+
for (let c = 0; c < kids[k].length; ++c) {
|
|
538
|
+
let path = longestDown[kids[k][c]] + madLength(pre[kids[k][c]]);
|
|
539
|
+
if (path > first) {
|
|
540
|
+
second = first;
|
|
541
|
+
first = path;
|
|
542
|
+
} else if (path > second) {
|
|
543
|
+
second = path;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
longestDown[k] = first;
|
|
547
|
+
diameter = Math.max(diameter, first + second);
|
|
548
|
+
}
|
|
549
|
+
let nearEps = Math.max(MAD_EPSILON, MAD_NEAR_FRACTION * diameter);
|
|
516
550
|
let tipDepth = new Float64Array(n);
|
|
517
551
|
for (let i = 0; i < n; ++i) {
|
|
518
552
|
tipDepth[i] = depth[tipPos[i]];
|
|
@@ -529,7 +563,7 @@
|
|
|
529
563
|
let w2 = new Float64Array(m); // sum_{i!=j in subtree} (2*depth[j]/d - 1)^2 (j second)
|
|
530
564
|
let col0 = new Float64Array(n);
|
|
531
565
|
let colInv = new Float64Array(n);
|
|
532
|
-
let partners = new Int32Array(n); // the tips each tip is
|
|
566
|
+
let partners = new Int32Array(n); // the tips each tip is counted against (d > nearEps)
|
|
533
567
|
for (let q = 0; q < m; ++q) {
|
|
534
568
|
let k = post[q];
|
|
535
569
|
let ch = kids[k];
|
|
@@ -554,7 +588,7 @@
|
|
|
554
588
|
for (let j = lo[ch[b]]; j < hi[ch[b]]; ++j) {
|
|
555
589
|
let dj = tipDepth[j];
|
|
556
590
|
let dij = (di - dm) + (dj - dm);
|
|
557
|
-
if (dij >
|
|
591
|
+
if (dij > nearEps) {
|
|
558
592
|
let inv = 1.0 / dij;
|
|
559
593
|
let inv2 = inv * inv;
|
|
560
594
|
let dev = (2.0 * (di - dm) * inv) - 1.0;
|
package/package.json
CHANGED