ci.yml / build (pull_request) Successful in 3m49s
Restructure the benchmark pipeline to direct all simulated data, indices, statistics, and query outputs into a unified `run/` directory. Update Makefile targets, shell scripts, and Python utilities to resolve paths relative to this new base. Adjust documentation and dependency tracking to match the revised layout, and remove outdated temporary artifacts.
149 lines
5.2 KiB
Python
149 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate deps.mk — pure dependency declarations for the benchmark pipeline.
|
|
|
|
Like C .d files: only target: prerequisites lines, no recipes.
|
|
Recipes stay in the Makefile as generic rules.
|
|
"""
|
|
import gzip
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
STOP_WORDS = {'complete', 'chromosome', 'whole', 'sequence', 'genome',
|
|
'endosymbiont', 'of'}
|
|
STOP_PREFIXES = ('scaffold', 'contig', 'plasmid')
|
|
|
|
# All generated paths live under RUN/ (see Makefile) so the whole tree can be
|
|
# gitignored with a single entry.
|
|
RUN = 'run'
|
|
|
|
# Specimens used as read sources for the query benchmark (see
|
|
# DevDocMD/implementation/benchmark_query_testing.md): one common bacterium,
|
|
# one distant lineage (the only archaeon in SPECIES).
|
|
QUERY_SPECIMENS = ['Escherichia_coli--K-12_MG1655', 'Saccharolobus_islandicus--M.16.4']
|
|
|
|
|
|
def is_stop(tok):
|
|
t = tok.lower()
|
|
return t in STOP_WORDS or any(t.startswith(p) for p in STOP_PREFIXES)
|
|
|
|
|
|
def sanitize(s):
|
|
return re.sub(r'[^A-Za-z0-9._-]', '_', s).strip('_')
|
|
|
|
|
|
def collect_tokens(text):
|
|
parts = []
|
|
for tok in text.split():
|
|
tok = tok.rstrip(',.')
|
|
if is_stop(tok):
|
|
break
|
|
parts.append(sanitize(tok))
|
|
return '_'.join(filter(None, parts))
|
|
|
|
|
|
def parse_organism(defn, gcf_id):
|
|
words = defn.split()
|
|
species = sanitize(words[0] + '_' + words[1])
|
|
|
|
m = re.search(r'\bstr\.\s+(\S+)(?:\s+substr\.\s+(\S+))?', defn)
|
|
if m:
|
|
strain = sanitize(m.group(1))
|
|
if m.group(2):
|
|
strain += '_' + sanitize(m.group(2))
|
|
return species, strain
|
|
|
|
m = re.search(r'\bstrain\b\s+(.*)', defn)
|
|
if m:
|
|
strain = collect_tokens(m.group(1))
|
|
if strain:
|
|
return species, strain
|
|
|
|
remainder = re.sub(r'^\S+ \S+\s*', '', defn)
|
|
remainder = re.sub(r'^subsp\.\s+\S+\s*', '', remainder)
|
|
remainder = re.sub(r'^serovar\s+\S+\s*', '', remainder)
|
|
strain = collect_tokens(remainder)
|
|
return species, strain if strain else gcf_id
|
|
|
|
|
|
def first_definition(path):
|
|
with gzip.open(path, 'rt') as fh:
|
|
for line in fh:
|
|
if line.startswith('>'):
|
|
m = re.search(r'"definition":"([^"]*)"', line)
|
|
return m.group(1) if m else line[1:].split()[0]
|
|
return Path(path).stem
|
|
|
|
|
|
def main():
|
|
entries = [] # (specimen, species, sim_dir, genome_path)
|
|
species_seen = []
|
|
|
|
for path in sorted(sys.argv[1:]):
|
|
gcf_id = Path(path).name.replace('_genomic.fna.gz', '')
|
|
defn = first_definition(path)
|
|
sp, st = parse_organism(defn, gcf_id)
|
|
specimen = f'{sp}--{st}'
|
|
sim_dir = f'{RUN}/simulated_data/{sp}/{st}'
|
|
entries.append((specimen, sp, sim_dir, path))
|
|
if sp not in species_seen:
|
|
species_seen.append(sp)
|
|
|
|
specimens = [e[0] for e in entries]
|
|
print('SPECIMENS :=', ' '.join(specimens))
|
|
print('SPECIES :=', ' '.join(species_seen))
|
|
|
|
for specimen, species, sim_dir, genome in entries:
|
|
reads = f'{sim_dir}/reads_R1.fastq.gz'
|
|
p_done = f'{RUN}/specimen_index_presence/{specimen}/index.done'
|
|
p_stats = f'{RUN}/stats/indexing_presence/{specimen}.stats'
|
|
c_done = f'{RUN}/specimen_index_count/{specimen}/index.done'
|
|
c_stats = f'{RUN}/stats/indexing_count/{specimen}.stats'
|
|
ref = f'{RUN}/reference_index/{specimen}.npz'
|
|
vp = f'{RUN}/stats/verify_presence/{specimen}.stats'
|
|
vc = f'{RUN}/stats/verify_count/{specimen}.stats'
|
|
|
|
print()
|
|
print(f'# {specimen}')
|
|
print(f'{reads}: {genome}')
|
|
print(f'{ref}: {reads}')
|
|
print(f'{p_done} {p_stats}: {reads}')
|
|
print(f'{c_done} {c_stats}: {reads}')
|
|
print(f'{vp}: {ref} {p_done}')
|
|
print(f'{vc}: {ref} {c_done}')
|
|
|
|
print()
|
|
for sp in species_seen:
|
|
sp_done = f'{RUN}/specific_index_presence/{sp}/index.done'
|
|
sp_stats = f'{RUN}/stats/specific_kmer_presence/{sp}.stats'
|
|
sc_done = f'{RUN}/specific_index_count/{sp}/index.done'
|
|
sc_stats = f'{RUN}/stats/specific_kmer_count/{sp}.stats'
|
|
print(f'# {sp}')
|
|
print(f'{sp_done} {sp_stats}: {RUN}/global_index_presence/index.done')
|
|
print(f'{sc_done} {sc_stats}: {RUN}/global_index_count/index.done')
|
|
|
|
print()
|
|
print('QUERY_SPECIMENS :=', ' '.join(QUERY_SPECIMENS))
|
|
|
|
by_specimen = {e[0]: e for e in entries}
|
|
for specimen in QUERY_SPECIMENS:
|
|
_, species, sim_dir, genome = by_specimen[specimen]
|
|
query_dir = sim_dir.replace('simulated_data/', 'query_data/', 1)
|
|
reads = f'{query_dir}/reads_R1.fastq.gz'
|
|
dense_out = f'{RUN}/query_presence_dense/{specimen}.fasta.gz'
|
|
dense_stat = f'{RUN}/stats/query_presence_dense/{specimen}.stats'
|
|
sparse_out = f'{RUN}/query_presence_sparse/{specimen}.fasta.gz'
|
|
sparse_stat = f'{RUN}/stats/query_presence_sparse/{specimen}.stats'
|
|
vq_stat = f'{RUN}/stats/verify_query/{specimen}.stats'
|
|
|
|
print()
|
|
print(f'# query: {specimen}')
|
|
print(f'{reads}: {genome}')
|
|
print(f'{dense_out} {dense_stat}: {reads} {RUN}/global_index_presence_dense/index.done')
|
|
print(f'{sparse_out} {sparse_stat}: {reads} {RUN}/global_index_presence/index.done')
|
|
print(f'{vq_stat}: {dense_out} {sparse_out}')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|