moonrestrict

    Offline strand-aware restriction maps and verified DNA fragment reconstruction

    restriction-map
    dna
    bioinformatics
    circular-dna
    Download zip
    Author
    Version
    0.1.0
    License
    MIT
    Last updated
    13 hours ago
    Downloads
    2

    Dependencies

    #MoonRestrict

    用 MoonBit 实现的离线 DNA 限制性切割坐标、片段重建与证据校验库。

    输入 IUPAC DNA、线性/环形拓扑及显式识别/切割模型,输出保留双链坐标和不确定性证据的切割图。 重点不是再做一套通用序列工具:复用 ziju-max/genetic_code@0.1.0 的碱基集合、互补与 FASTA 能力,新增切割几何、环形片段、独立片段校验及比较流程。

    仅作抽象序列计算和教学。全部样例为合成字符串;不提供真实酶数据库、实验条件、实验控制、疾病判断或湿实验成功率。未自动发布到 MoonCakes,不能据此声称已经通过赛事验收。

    公开源码:CYang-dep/moonrestrict。四目标检查、53 项测试、构建和真实进程验证已通过 CI 基线,包括 Linux native;详细证据与限制见 交付审计

    #快速运行

    需要 MoonBit、运行 JS 时的 Node.js、运行验收脚本时的 Python 3.8+。native 还需要可用的 C 编译器。 在取得本仓库源代码后,在仓库根目录运行:

    moon update moon check --target wasm-gc --deny-warn moon test --target wasm-gc moon run cmd/main --target wasm-gc -- demo moon run examples/workflow --target wasm-gc python scripts/cli_smoke.py --target wasm-gc

    demo 对环形 GTAC 使用合成模型 ACGT / top=1 / bottom=3:识别起点为 2,顶部切口为 3,底部切口为 1;产生单个线性化片段 CGTA,长度仍为 4。坐标均从 0 开始。

    本地验收工具链:moon 0.1.20260713moonc v0.10.4+2cc641edf。CI 固定安装版本 0.10.4+2cc641edf,不使用浮动 latest。版本兼容性必须通过检查/测试确认,不宣称所有未来工具链均可运行。

    #三个可复现使用场景

    1. 教学开发者检查跨原点切口:运行 demo,读取 sites 的正反链及切口证据,再观察环形分子线性化后的 CGTA,避免把跨原点识别误判成未命中。
    2. 离线分析程序校验存档图谱:将 examples/linear.json 交给 digest,再把原请求和报告交给 verify;改动长度或片段序列会被拒绝。验收脚本实际执行成功和篡改失败两条路径。
    3. 工具作者比较合成样本:运行 examples/workflow,从三个 FASTA 记录和 TSV 模型开始,生成经校验的图谱、跨原点注释片段、4 个切口子集及精确长度指纹;贪心面板选择第 1 列(从 0 起),区分全部三个样本。这只是字符串指纹区分,不是实验模型推荐。

    #命令行和文件输入

    digest REQUEST_JSON verify REQUEST_JSON REPORT_JSON demo --help

    参数是 JSON 文本,不是文件名。PowerShell 示例(适用于小型输入):

    $request = Get-Content -Raw examples/circular.json moon run cmd/main --target wasm-gc -- digest $request

    可移植 Python 示例避免手工 shell 引号,且无需第三方库:

    import pathlib, subprocess request = pathlib.Path("examples/circular.json").read_text(encoding="utf-8") result = subprocess.run( ["moon", "run", "cmd/main", "--target", "wasm-gc", "--", "digest", request], capture_output=True, text=True, encoding="utf-8", check=True) report = result.stdout.strip() subprocess.run(["moon", "run", "cmd/main", "--target", "wasm-gc", "--", "verify", request, report], check=True)

    命令行受操作系统参数长度限制(尤其 Windows);大输入应使用库 API,不应绕过边界扩大 shell 命令。 失败时第一条 stdout 记录为 {"ok":false,"error":"..."},进程保证非零退出,但具体退出值及 panic 信息依赖运行时。 可嵌入的 command 则稳定返回 exit_code=0/2,不执行 I/O、不 panic。不要把可嵌入返回码等同于所有进程的退出码。

    #库 API

    源代码使用示例见 examples/workflow/main.mbt,完整签名见 pkg.generated.mbti 本模块尚未在 MoonCakes 发布;不能先假定 moon add CYang-dep/moonrestrict 可用。 在可解析本模块的 MoonBit 工作区中,包声明导入:

    import { "CYang-dep/moonrestrict" @restrict }

    在允许抛出 RestrictError 的函数或测试中:

    let dna = @restrict.Dna::new("GTAC", topology=Circular)
    let enzyme = @restrict.Enzyme::new("synthetic", "ACGT", 1, 3)
    let report = @restrict.digest(dna, enzyme)
    @restrict.verify_digest(dna, [enzyme], report)
    let (before, after) = @restrict.cut_ends(dna, report.cuts[0])
    let compatibility = @restrict.compatible_ends(before, after)

    能力API
    校验/转换 DNA 与模型Dna::new, Enzyme::new, reverse_complement
    双向识别与确定性scan
    完整单/多模型切割digest, digest_many
    证据、片段坐标及原文校验verify_digest, Request::verify
    平末端、5′/3′突出末端和互补性cut_ends, compatible_ends
    FASTA 与模型 TSVread_fasta, write_fasta, read_enzymes, write_enzymes
    部分切割的数学子集partial_digests
    区间投影annotate_fragments
    精确长度指纹与比较size_bands, band_signature, compare_maps, select_panel
    版本化 JSON 与可移植命令分发read_request, read_report, run_json, command

    #关键语义与边界

    • 半开区间、参考顶部链方向、0 起点边界坐标;片段长度是顶部链长度,不是双链物理轮廓长度或质量。
    • 反向识别变换:top = start + motif_length - bottom_offsetbottom = start + motif_length - top_offset
    • 环形坐标取模但保留有符号 stagger;未切割环保持 circular=true,有切口则输出线性片段。识别模式不允许比整条环更长。
    • 输入碱基集合全部满足模式才是 Certain;仅部分交集为 Possible。默认只切确定命中,显式 possible 才纳入可能命中,reject 遇到可能命中即失败。这些不是概率。
    • 同一顶部切口且相同几何合并,但原始识别证据全部保留;同顶不同底、底链交叉/共用切口及不支持的环形几何失败,不伪造物理片段。
    • verify_digest 重新运行识别/选切,并独立检查片段边界、逐碱基内容和长度守恒;它不是第二套独立识别算法,也不是密码学签名或数据来源认证。
    • 精确条带保留重复次数,不做电泳误差、强度、分辨率或质量推断。面板采用确定性贪心,不保证最小列数。

    #开发与验收

    moon fmt --check moon check --target wasm-gc --deny-warn moon check --target wasm --deny-warn moon check --target js --deny-warn moon check --target native --deny-warn python scripts/verify.py --skip-native-runtime # 有 C 编译器时,运行完整四目标验收: python scripts/verify.py

    verify.py 执行检查、测试、构建、真实命令行与多记录工作流。跳过 native 运行时会明确记录 SKIP,不算 PASS。 测试含 1,364 个长度 1–5 的具体 DNA 字符串、256 个四碱基环的各次旋转、225 组 IUPAC 集合匹配及恶意输入回归;数量是域覆盖,不宣称穷尽所有 DNA。

    MIT 开源;AI 参与范围见 AI_USAGE.md,贡献流程见 CONTRIBUTING.md

    RestrictError

    pub(all) suberror RestrictError {
    InvalidInput(String)
    LimitExceeded(String)
    CutConflict(String)
    } derive(Eq,
    Debug
    )

    Stable domain failures; details never contain source sequences.

    AmbiguityPolicy

    pub(all) enum AmbiguityPolicy {
    CertainOnly
    IncludePossible
    RejectPossible
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Ambiguous observations require an explicit cleavage policy.

    Annotation

    pub(all) struct Annotation {
    id : String
    start : Int
    length : Int
    } derive(Eq, ToJson,
    Debug
    )

    An annotation interval follows reference top-strand coordinates.

    AnnotationPiece

    pub(all) struct AnnotationPiece {
    id : String
    fragment_index : Int
    local_start : Int
    length : Int
    source_start : Int
    } derive(Eq, ToJson,
    Debug
    )

    One contiguous annotation piece in fragment-local coordinates.

    Certainty

    pub(all) enum Certainty {
    Certain
    Possible
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Certain means every possible input base satisfies the motif.

    CommandResult

    pub(all) struct CommandResult {
    exit_code : Int
    output : String
    } derive(Eq,
    Debug
    )

    Digest

    pub(all) struct Digest {
    topology : Topology
    source_length : Int
    sites : Array[Site]
    cuts : Array[Site]
    fragments : Array[Fragment]
    policy : AmbiguityPolicy
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Reports retain recognition evidence even when a site cannot be cut.

    Digest::reconstruct

    fn Digest::reconstruct(self : Digest) -> String

    Reconstruct ordered top-strand fragments (rotated for cut circular DNA).

    Dna

    pub struct Dna {
    bases : String
    topology : Topology
    } derive(Eq, ToJson,
    Debug
    )

    Validated uppercase IUPAC DNA. Coordinates are zero-based base boundaries.

    Dna::base_options

    fn Dna::base_options(self : Dna, index : Int) -> Array[Char] raise RestrictError

    Base-set expansion is owned by genetic_code, not reimplemented here.

    Dna::length

    fn Dna::length(self : Dna) -> Int

    Dna::new

    fn Dna::new(raw : String, topology? : Topology) -> Dna raise RestrictError

    Validate before using upstream permissive IUPAC conversion.

    Dna::sequence

    fn Dna::sequence(self : Dna) -> String

    Dna::topology

    fn Dna::topology(self : Dna) -> Topology

    End

    pub(all) struct End {
    kind : EndKind
    sequence : String
    } derive(Eq, ToJson,
    Debug
    )

    Exposed sequence is always written 5-prime to 3-prime.

    EndKind

    pub(all) enum EndKind {
    Blunt
    FivePrime
    ThreePrime
    } derive(Eq, ToJson,
    Debug
    )

    Exposed single-strand polarity; unknown natural termini are not inferred.

    Enzyme

    pub struct Enzyme {
    name : String
    motif : String
    top_offset : Int
    bottom_offset : Int
    } derive(Eq, ToJson,
    Debug
    )

    A cleavage model with offsets measured on the displayed recognition strand.

    Enzyme::motif

    fn Enzyme::motif(self : Enzyme) -> String

    Enzyme::name

    fn Enzyme::name(self : Enzyme) -> String

    Enzyme::new

    fn Enzyme::new(name : String, motif : String, top_offset : Int, bottom_offset : Int) -> Enzyme raise RestrictError

    Names are report identifiers, not claims about a curated enzyme catalogue.

    Enzyme::offsets

    fn Enzyme::offsets(self : Enzyme) -> (Int, Int)

    FingerprintPanel

    pub(all) struct FingerprintPanel {
    selected_columns : Array[Int]
    unresolved_pairs : Array[(Int, Int)]
    complete : Bool
    } derive(Eq, ToJson,
    Debug
    )

    Greedy discrimination over already-computed exact-size fingerprints.

    Fragment

    pub(all) struct Fragment {
    start : Int
    length : Int
    sequence : String
    circular : Bool
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    A fragment records the reference top strand, not a mass estimate.

    JoinStatus

    pub(all) enum JoinStatus {
    NoJoin
    PossibleJoin
    CertainJoin
    } derive(Eq, ToJson,
    Debug
    )

    Sequence-only complementarity, NOT a prediction of laboratory ligation.

    MapComparison

    pub(all) struct MapComparison {
    same_source : Bool
    same_topology : Bool
    same_cut_geometry : Bool
    same_size_bands : Bool
    same_fragment_sequences : Bool
    added_top_cuts : Array[Int]
    removed_top_cuts : Array[Int]
    } derive(Eq, ToJson,
    Debug
    )

    Different comparisons answer different questions; none implies source identity.

    Orientation

    pub(all) enum Orientation {
    Forward
    Reverse
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Which reference-strand orientation recognized the motif.

    PartialReport

    pub(all) struct PartialReport {
    candidate_cuts : Int
    total_subsets : Int
    truncated : Bool
    scenarios : Array[PartialScenario]
    } derive(Eq, ToJson,
    Debug
    )

    Prefix enumeration always advertises whether it is exhaustive.

    PartialScenario

    pub(all) struct PartialScenario {
    mask : Int
    active_top_cuts : Array[Int]
    fragments : Array[Fragment]
    } derive(Eq, ToJson,
    Debug
    )

    A mathematical cleavage subset, not a kinetic/probabilistic prediction.

    Request

    pub struct Request {
    dna : Dna
    enzymes : Array[Enzyme]
    policy : AmbiguityPolicy
    } derive(
    Debug
    )

    Validated version-one request. Parsing never constructs private models unchecked.

    Request::run

    fn Request::run(self : Request) -> Digest raise RestrictError

    Request::verify

    fn Request::verify(self : Request, report : Digest) -> Unit raise RestrictError

    Sample

    pub(all) struct Sample {
    id : String
    dna : Dna
    } derive(Eq, ToJson,
    Debug
    )

    Validated source record. IDs are preserved; descriptions are not echoed in errors.

    Site

    pub(all) struct Site {
    enzyme : String
    start : Int
    orientation : Orientation
    top : Int
    bottom : Int
    stagger : Int
    certainty : Certainty
    cuttable : Bool
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    A recognition event can exist even when cleavage falls outside linear DNA.

    SizeBand

    pub(all) struct SizeBand {
    length : Int
    multiplicity : Int
    } derive(Eq, ToJson,
    Debug
    )

    Exact top-strand base-count bin; multiplicity is never discarded.

    Topology

    pub(all) enum Topology {
    Linear
    Circular
    } derive(Eq, ToJson,
    Debug
    ,
    FromJson
    )

    Public topology of the input molecule.

    annotate_fragments

    fn annotate_fragments(dna : Dna, enzymes : Array[Enzyme], report : Digest, annotations : Array[Annotation]) -> Array[AnnotationPiece] raise RestrictError

    Split intervals on fragment and circular-origin boundaries. No biological labels inferred.

    band_signature

    fn band_signature(fragments : Array[Fragment]) -> String raise RestrictError

    A deterministic length-count string for comparison, independent of fragment order.

    command

    fn command(args : Array[String]) -> CommandResult

    Portable dispatcher. Arguments exclude the executable name; never performs file or network IO.

    compare_maps

    fn compare_maps(left_dna : Dna, left_enzymes : Array[Enzyme], left : Digest, right_dna : Dna, right_enzymes : Array[Enzyme], right : Digest) -> MapComparison raise RestrictError

    Verify both reports then distinguish coordinate, size and sequence equivalence. Rotated circular references may share size bands while their coordinates differ.

    compatible_ends

    fn compatible_ends(a : End, b : End) -> JoinStatus raise RestrictError

    Conservative Cartesian base-set complementarity; ambiguity is never certain.

    cut_ends

    fn cut_ends(dna : Dna, site : Site) -> (End, End) raise RestrictError

    Return ends on the piece before/after a reference cut, respectively.

    digest

    fn digest(dna : Dna, enzyme : Enzyme, policy? : AmbiguityPolicy) -> Digest raise RestrictError

    Complete single-model cleavage. CertainOnly never cuts merely possible sites.

    digest_many

    fn digest_many(dna : Dna, enzymes : Array[Enzyme], policy? : AmbiguityPolicy) -> Digest raise RestrictError

    Combine recognition evidence while preserving the originating model name.

    partial_digests

    fn partial_digests(dna : Dna, enzymes : Array[Enzyme], max_scenarios? : Int) -> PartialReport raise RestrictError

    Enumerate subsets in ascending bitmask order, including uncut input first.

    read_enzymes

    fn read_enzymes(text : String) -> Array[Enzyme] raise RestrictError

    Tab-separated header name/motif/top/bottom; blank lines and # comments allowed.

    read_fasta

    fn read_fasta(text : String, topology? : Topology) -> Array[Sample] raise RestrictError

    Strict wrapper: upstream recoverable parse issues become atomic failures.

    read_report

    fn read_report(text : String) -> Digest raise RestrictError

    Decode does not establish authenticity; Request::verify is mandatory for untrusted reports.

    read_request

    fn read_request(text : String) -> Request raise RestrictError

    Strict request schema; omitted policy defaults to certain-only recognition.

    reverse_complement

    fn reverse_complement(dna : Dna) -> Dna

    IUPAC reverse complement is delegated to upstream base semantics.

    run_json

    fn run_json(text : String) -> String raise RestrictError

    End-to-end strict JSON request -> computed and independently verified report.

    scan

    fn scan(dna : Dna, enzyme : Enzyme) -> Array[Site] raise RestrictError

    Recognize both strands and transform offsets; no source mutation.

    select_panel

    fn select_panel(fingerprints : Array[Array[String]], max_columns : Int) -> FingerprintPanel raise RestrictError

    Rows are samples and columns are synthetic-model fingerprints. Deterministic greedy set cover; not a minimum-panel or laboratory-success claim.

    size_bands

    fn size_bands(fragments : Array[Fragment]) -> Array[SizeBand] raise RestrictError

    Group fragments by exact length, not gel mobility or molecular mass.

    verify_digest

    fn verify_digest(dna : Dna, enzymes : Array[Enzyme], report : Digest) -> Unit raise RestrictError

    Verify a caller-supplied report against source and enzyme models. Replays recognition evidence, but checks fragments by coordinate traversal, not by invoking the fragment generator. Does not certify biological behavior.

    write_enzymes

    fn write_enzymes(enzymes : Array[Enzyme]) -> String raise RestrictError

    Canonical synthetic/user-defined model interchange. No catalogue is bundled.

    write_fasta

    fn write_fasta(samples : Array[Sample]) -> String raise RestrictError

    Serialize the strict subset with wrapped sequence lines and no descriptions.

    write_report

    fn write_report(report : Digest) -> String

    Versioned report wrapper around explicit typed coordinate evidence.