refactor: format tnt script generation and add tree export commands

Reworks `writeln!` macro invocations to multi-line syntax and adjusts whitespace for improved readability. Additionally, appends four commands to the generated phylogenetic script to explicitly export trees and manage taxon naming at runtime.
This commit is contained in:
Eric Coissac
2026-08-16 21:52:30 +02:00
parent 3ba26b3dc1
commit 53c40b7a53
+73 -16
View File
@@ -30,7 +30,8 @@ pub(super) fn write_sankoff_tnt(
cost_scale: f64, cost_scale: f64,
free_loss: bool, free_loss: bool,
) { ) {
let path = output.as_ref() let path = output
.as_ref()
.map(|p| format!("{}_sankoff.tnt", p.display())) .map(|p| format!("{}_sankoff.tnt", p.display()))
.unwrap_or_else(|| "sankoff.tnt".into()); .unwrap_or_else(|| "sankoff.tnt".into());
let mut f = BufWriter::new(std::fs::File::create(&path).unwrap_or_else(|e| { let mut f = BufWriter::new(std::fs::File::create(&path).unwrap_or_else(|e| {
@@ -45,7 +46,11 @@ pub(super) fn write_sankoff_tnt(
let n_sites = alignment.sequences.first().map(|s| s.len()).unwrap_or(0); let n_sites = alignment.sequences.first().map(|s| s.len()).unwrap_or(0);
writeln!(f, "xread").unwrap(); writeln!(f, "xread").unwrap();
writeln!(f, "'obikmer central-position SNP families, calibrated Sankoff 16-state encoding'").unwrap(); writeln!(
f,
"'obikmer central-position SNP families, calibrated Sankoff 16-state encoding'"
)
.unwrap();
writeln!(f, "{n_sites} {}", labels.len()).unwrap(); writeln!(f, "{n_sites} {}", labels.len()).unwrap();
for (label, seq) in labels.iter().zip(alignment.sequences.iter()) { for (label, seq) in labels.iter().zip(alignment.sequences.iter()) {
write!(f, "{label} ").unwrap(); write!(f, "{label} ").unwrap();
@@ -70,7 +75,12 @@ pub(super) fn write_sankoff_tnt(
writeln!(f, "smatrix =0 (family16)").unwrap(); writeln!(f, "smatrix =0 (family16)").unwrap();
for i in 0..16 { for i in 0..16 {
for j in (i + 1)..16 { for j in (i + 1)..16 {
writeln!(f, "{}/{} {}", TNT_STATE_SYMBOL[i], TNT_STATE_SYMBOL[j], scaled_matrix[i][j]).unwrap(); writeln!(
f,
"{}/{} {}",
TNT_STATE_SYMBOL[i], TNT_STATE_SYMBOL[j], scaled_matrix[i][j]
)
.unwrap();
} }
} }
writeln!(f, ";\n").unwrap(); writeln!(f, ";\n").unwrap();
@@ -83,7 +93,8 @@ pub(super) fn write_sankoff_tnt(
// workflow is to `cd` into the output directory before `proc`-ing the // workflow is to `cd` into the output directory before `proc`-ing the
// script, and an absolute path here would break if that directory is // script, and an absolute path here would break if that directory is
// later moved or copied elsewhere. // later moved or copied elsewhere.
let tre_name = output.as_ref() let tre_name = output
.as_ref()
.and_then(|p| p.file_name()) .and_then(|p| p.file_name())
.map(|n| format!("{}_sankoff.tre", n.to_string_lossy())) .map(|n| format!("{}_sankoff.tre", n.to_string_lossy()))
.unwrap_or_else(|| "sankoff.tre".into()); .unwrap_or_else(|| "sankoff.tre".into());
@@ -97,21 +108,67 @@ pub(super) fn write_sankoff_tnt(
// defaults below when the script is run. `;` ends a `quote` block like // defaults below when the script is run. `;` ends a `quote` block like
// any other TNT command, so the text itself must avoid semicolons. // any other TNT command, so the text itself must avoid semicolons.
writeln!(f, "quote").unwrap(); writeln!(f, "quote").unwrap();
writeln!(f, "Default search below (edit or delete this block to run your own strategy):").unwrap(); writeln!(
writeln!(f, " hold N : size of TNT's tree buffer (how many equally-parsimonious").unwrap(); f,
writeln!(f, " trees it keeps in memory at once), 20 is a small, fast").unwrap(); "Default search below (edit or delete this block to run your own strategy):"
writeln!(f, " default, raise it if mult reports it had to drop trees.").unwrap(); )
writeln!(f, " mult : traditional search (random addition sequences followed by").unwrap(); .unwrap();
writeln!(f, " TBR branch-swapping, TNT's own default replication count),").unwrap(); writeln!(
writeln!(f, " a reasonable first-pass strategy on this data's memory").unwrap(); f,
writeln!(f, " footprint, xmult's ratchet/drift/tree-fusion buffers ran").unwrap(); " hold N : size of TNT's tree buffer (how many equally-parsimonious"
writeln!(f, " this out of RAM at TNT's default mxram on this dataset.").unwrap(); )
writeln!(f, " export - F : write the trees held in the buffer to file F, in").unwrap(); .unwrap();
writeln!(f, " TNT/Hennig86 format ('-' means trees, as opposed to data).").unwrap(); writeln!(
f,
" trees it keeps in memory at once), 20 is a small, fast"
)
.unwrap();
writeln!(
f,
" default, raise it if mult reports it had to drop trees."
)
.unwrap();
writeln!(
f,
" mult : traditional search (random addition sequences followed by"
)
.unwrap();
writeln!(
f,
" TBR branch-swapping, TNT's own default replication count),"
)
.unwrap();
writeln!(
f,
" a reasonable first-pass strategy on this data's memory"
)
.unwrap();
writeln!(
f,
" footprint, xmult's ratchet/drift/tree-fusion buffers ran"
)
.unwrap();
writeln!(
f,
" this out of RAM at TNT's default mxram on this dataset."
)
.unwrap();
writeln!(
f,
" export - F : write the trees held in the buffer to file F, in"
)
.unwrap();
writeln!(
f,
" TNT/Hennig86 format ('-' means trees, as opposed to data)."
)
.unwrap();
writeln!(f, ";").unwrap(); writeln!(f, ";").unwrap();
writeln!(f, "hold 20;").unwrap(); writeln!(f, "hold 20;").unwrap();
writeln!(f, "mult;").unwrap(); writeln!(f, "mult;").unwrap();
writeln!(f, "export - {tre_name};").unwrap(); writeln!(f, "taxname =;").unwrap();
writeln!(f, "tsave * {tre_name};").unwrap();
writeln!(f, "tsave /;").unwrap();
info!( info!(
"TNT script → {path} (costs scaled x{cost_scale:.0}, runs a default `hold 20; mult;` \ "TNT script → {path} (costs scaled x{cost_scale:.0}, runs a default `hold 20; mult;` \