faqt

module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2026 License: MIT

README

faqt

faqt is a command line interface and Go library for reading, writing, and manipulating biological sequence data. It is intentionally sequence-only: every supported input format normalizes into a minimal record with no annotation model, no feature graph, and no sequence reconstruction from annotations. It is a Go reimplementation of the python Fastaq. This repository was developed with substantial coding assistance from OpenAI Codex, which helped with implementation, refactoring, tests, documentation, and benchmarking under human direction and review.

Install

The simplest way to install faqt is to download the latest prebuilt binary from the GitHub releases page:

Choose the archive or binary matching your OS and CPU architecture.

After installing, check the version with:

faqt --version

If you want to build locally instead:

./build.sh

That builds faqt for the current OS and architecture into ./build/faqt or ./build/faqt.exe. Local builds report version dev unless you pass an explicit release version.

Command Line Usage

faqt currently provides these command-line tasks:

  • faqt to-fasta: convert supported input formats to FASTA
  • faqt interleave: interleave two sequence files
  • faqt to-perfect-reads: simulate perfect FASTQ reads from a reference
  • faqt make-random-contigs: make random FASTA contigs
  • faqt stats: report assembly-style sequence statistics (reimplementation of assembly-stats)
  • faqt download-genome: download a genome and save it to one output file

Use faqt -h or faqt --help for top-level help. Use -h or --help after a command name to see command-specific flags and examples, for example:

faqt to-fasta --help
faqt stats -h

Command-line input can be any supported sequence format:

  • FASTA
  • FASTQ
  • GenBank
  • EMBL
  • GFF3 sequence from ##FASTA
  • SAM
  • BAM
  • Clustal
  • PHYLIP

Compressed input files are handled automatically by content, not filename. faqt can read:

  • uncompressed files
  • gzip
  • bzip2
  • xz
  • zstd

For file output, compression is chosen by output filename suffix or by --compress when a command supports it:

  • .gz gives gzip output
  • .bz2 gives bzip2 output
  • .xz gives xz output
  • .zst gives zstd output

When writing to stdout, output is uncompressed unless compression is explicitly requested.

For command-line I/O, - means standard streams:

  • use - as the input path to read from stdin
  • use - as the output path to write to stdout
  • to-fasta defaults to stdin/stdout when paths are not given

Examples:

faqt to-fasta reads.fq.gz > out.fa
faqt to-fasta --input aln.aln --remove-dashes
faqt interleave reads_1.fq reads_2.fq -o reads_interleaved.fq --suffix1 /1 --suffix2 /2
faqt to-perfect-reads ref.fa --out reads.fq --coverage 50 --read-length 150
faqt to-perfect-reads ref.fa --forward-out reads_1.fq --reverse-out reads_2.fq --mean-insert 300 --insert-std 30 --coverage 50 --read-length 150
faqt make-random-contigs 10 500 -o contigs.fa --seed 1
faqt download-genome GCF_000001405.40 -o genome.gff3
cat reads.gb | faqt to-fasta
faqt to-fasta -i - -o out.fa < reads.embl
faqt stats assembly.fa
faqt stats -t assembly.fa

Public API

Use faqt as a Go library by importing the package that matches the level of control you need. Most applications should start with seqio, which provides the public sequence record type, streaming readers and writers, compression-aware path helpers, and format detection.

import "github.com/martinghunt/faqt/seqio"

The core record is intentionally minimal:

type SeqRecord struct {
    Name        string
    Description string
    Seq         []byte
    Qual        []byte // nil unless FASTQ
}

All supported inputs normalize to this record. Qual is nil unless the source has FASTQ-style qualities. The library does not expose annotation models, feature graphs, or metadata maps.

Reading Records

seqio.OpenPath opens a path, detects compression from magic bytes, detects the biological format from content, and returns a streaming reader. Use "-" to read from standard input.

package main

import (
	"fmt"
	"io"
	"log"

	"github.com/martinghunt/faqt/seqio"
)

func main() {
	reader, err := seqio.OpenPath("reads.fq.gz")
	if err != nil {
		log.Fatal(err)
	}
	if closer, ok := reader.(io.Closer); ok {
		defer closer.Close()
	}

	for {
		rec, err := reader.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("%s\t%d\n", rec.Name, len(rec.Seq))
	}
}

Use seqio.OpenReader when you already have an io.Reader.

Supported input formats are FASTA, FASTQ, Clustal, PHYLIP, SAM, BAM, GenBank, EMBL, and GFF3 sequence from the ##FASTA section. For SAM and BAM, alignment data are ignored except for the reverse-strand flag, which causes sequence and quality to be reversed back to original read orientation. GFF3 inputs without a ##FASTA section return an error.

Writing Records

seqio.CreatePath creates a streaming writer for FASTA or FASTQ. Compression is selected from the output suffix by default, or explicitly with seqio.WithCompression. Use "-" to write to standard output; standard output is uncompressed unless compression is explicitly requested.

writer, err := seqio.CreatePath("out.fa.gz", seqio.FASTA, seqio.WithWrap(60))
if err != nil {
	log.Fatal(err)
}
defer writer.Close()

err = writer.Write(&seqio.SeqRecord{
	Name: "read1",
	Seq:  []byte("ACGT"),
})
if err != nil {
	log.Fatal(err)
}

For common cases, the format-specific helpers make the output format explicit:

writer, err := seqio.CreateFASTAPath("out.fa.gz", seqio.WithWrap(60))
if err != nil {
	log.Fatal(err)
}
defer writer.Close()

Available writer formats are seqio.FASTA and seqio.FASTQ. FASTA writers support seqio.WithWrap(width). Output compression can be forced with seqio.WithCompression(seqio.CompressGzip), seqio.CompressBzip2, seqio.CompressXZ, seqio.CompressZstd, or disabled with seqio.CompressNone.

Transforming Streams

Use seqio.Process when you already have a reader and writer, or seqio.TransformPath for path-based streaming transforms.

err := seqio.TransformPath(
	"reads.fa",
	"reads.rc.fa",
	seqio.FASTA,
	func(rec *seqio.SeqRecord) (*seqio.SeqRecord, error) {
		out := *rec
		out.Seq = seq.ReverseComplement(rec.Seq)
		return &out, nil
	},
	seqio.WithWrap(60),
)
if err != nil {
	log.Fatal(err)
}

For straight conversion to FASTA:

err := seqio.ToFASTAPath("reads.fq.gz", "reads.fa", seqio.WithWrap(60))
if err != nil {
	log.Fatal(err)
}

Returning nil from a transform skips that record.

Interleaving Streams

Use seqio.Interleave when you already have readers and a writer. It writes alternating records from the first and second inputs, and returns an error if either input has an unmatched record.

err := seqio.Interleave(
	reader1,
	reader2,
	writer,
	seqio.InterleaveOptions{Suffix1: "/1", Suffix2: "/2"},
)
if err != nil {
	log.Fatal(err)
}

For path-based use, seqio.InterleavePath detects input formats and compression from contents, infers FASTA or FASTQ output from the first pair, and applies normal output compression options.

Lower-Level Format Packages

The format packages expose direct readers and writers where available:

  • fasta and fastq: format-specific readers and writers
  • clustal, phylip, sam, bam, genbank, embl, gff3: format-specific readers

Use these when the input format is already known and you do not need seqio format or compression detection.

Sequence Utilities

The seq package provides byte-slice sequence helpers:

  • seq.ReverseComplement
  • seq.Subseq
  • seq.FindGaps
  • seq.NormalizeDNA
  • seq.TranslateCodon
  • seq.Translate

The orf package provides ORF finding:

  • orf.FindORFs
Statistics and Generated Data

The stats package computes assembly-style sequence statistics from any supported input format:

s, err := stats.FromPath("assembly.fa.gz", 0)
if err != nil {
	log.Fatal(err)
}
fmt.Print(s.String(stats.FormatHuman))

Use stats.RenderMany to render multiple stats.Stats values in a shared output format. The available formats are stats.FormatHuman, stats.FormatTab, stats.FormatTabNoHeader, and stats.FormatGreppy.

Random FASTA contigs can be generated through the randomcontigs package:

seed := int64(1)
err := randomcontigs.GenerateToPath("contigs.fa", randomcontigs.Options{
	Contigs:     10,
	Length:      500,
	Seed:        &seed,
	FirstNumber: 1,
})
if err != nil {
	log.Fatal(err)
}

Perfect FASTQ reads can be simulated from a streaming reference reader with the perfectreads package:

reader, err := seqio.OpenPath("ref.fa")
if err != nil {
	log.Fatal(err)
}
if closer, ok := reader.(io.Closer); ok {
	defer closer.Close()
}
writer, err := seqio.CreateFASTQPath("reads.fq.gz")
if err != nil {
	log.Fatal(err)
}
defer writer.Close()

report, err := perfectreads.GenerateSingle(reader, writer, perfectreads.Options{
	Coverage:   50,
	ReadLength: 150,
	Seed:       1,
})
if err != nil {
	log.Fatal(err)
}
_ = report

The genomedl package exposes genomedl.DownloadGenome(accession, outPath) for downloading one genome accession into one output file.

Minimizers, Mapping, and Alignment

The minimizer package builds minimizer indexes and sketches query sequences:

index, err := minimizer.BuildFromPath("ref.fa", minimizer.Options{
	K: 15,
	W: 10,
})
if err != nil {
	log.Fatal(err)
}

anchors := index.Query([]byte("ACGTTGCA"))
_ = anchors

For the common mapping workflow, use the higher-level mapping package. It builds a minimizer index, finds candidate hits, and runs the default aligner unless you set Mapper.Aligner to nil.

m, err := mapping.BuildFromPath("ref.fa", minimizer.Options{
	K:         15,
	W:         10,
	MidOcc:    100,
	MaxMaxOcc: 500,
	OccDist:   500,
	QOccFrac:  0.01,
})
if err != nil {
	log.Fatal(err)
}

result, err := m.Map("query1", []byte("ACGTTGCA"))
if err != nil {
	log.Fatal(err)
}
for _, hit := range result.Hits {
	fmt.Println(hit.RefName, hit.Alignment.Score, hit.Alignment.CIGAR)
}

The lower-level mapper and align packages are available when you need to customize the pipeline:

  • mapper.DefaultPipeline, mapper.Map, and mapper.ExtractCandidates expose anchor clustering, chaining, and candidate extraction.
  • align.DefaultAligner and align.AlignCandidates expose candidate alignment and ranking.
Output Layers

There are three output layers:

  1. SeqRecord.String() for convenience and debugging.
  2. SeqRecord.WriteTo(io.Writer) for efficient low-level output.
  3. seqio.Writer for configurable output with format, wrapping, and compression control.

Prefer seqio.Writer for normal library use. String() emits FASTQ if Qual != nil, otherwise FASTA, and does not wrap sequence lines.

Supported Formats and Compression

Input format detection is content-based after decompression. Compression detection uses magic bytes, not filenames. Supported input compression:

  • uncompressed
  • gzip
  • bzip2
  • xz
  • zstd

Output compression is selected by path suffix or seqio.WithCompression:

  • .gz for gzip
  • .bz2 for bzip2
  • .xz for xz
  • .zst for zstd

Development and Building

Run the test suite before considering changes complete:

go test ./...

For a normal local build, run:

./build.sh

For a cross-platform release build:

./build.sh --release --version v1.2.3

That produces binaries for:

  • darwin/amd64
  • darwin/arm64
  • linux/amd64
  • linux/arm64
  • windows/amd64
  • windows/arm64

Release artifact names include the version, for example:

  • faqt-v1.2.3-darwin-arm64
  • faqt-v1.2.3-linux-amd64
  • faqt-v1.2.3-windows-amd64.exe

You can also build a specific target without using release mode:

./build.sh --os linux --arch arm64

The test suite covers multi-record parsing, compression-aware I/O, GFF3 error handling, stdin/stdout behavior, sequence utilities, random contig generation, ORF detection, and CLI conversion.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL