score-set 0.1.1

A Rust library for building static weighted scoring operator sets
Documentation

score-set

crates.io Coverage

A Rust library for building static weighted scoring operator sets. Declare a set of named metrics with weights, normalize at build time, score at runtime via a user-provided closure that freely injects inputs and context.

Usage

use score_set::*;

// 1. Build metrics
let gc = metric("gc-content")
    .measure()
    .by(|dna: &&str| {
        let gc = dna.chars().filter(|c| *c == 'G' || *c == 'C').count();
        if dna.is_empty() { 0.0 } else { gc as f64 / dna.len() as f64 }
    })
    .map01()
    .by(|raw: &f64, _: &&str| Value01::witness(*raw).unwrap());

let length = metric("length")
    .measure()
    .by(|len: &usize| *len)
    .map01()
    .by(|raw: &usize, _: &usize| {
        Value01::witness((*raw as f64 / 100.0).min(1.0)).unwrap()
    });

// 2. Declare the set — weights are normalized automatically
let ms = score_set! {
    2.0 => gc,
    3.0 => length,
}?;

// 3. Score with runtime data
let dna = "ACGTACGT";
let score = ms.score().by(|(gc, len)| {
    gc.contribute(gc.metric().eval(&dna))
        + len.contribute(len.metric().eval(&dna.len()))
});
// score ≈ 0.248

# Ok::<(), &'static str>(())

Concepts

Metrics. A Metric is a two-stage scoring operator: measure maps input to a raw value, map01 maps the raw value (with the original input still available for context) to a validated [0, 1] score.

metric("name")
    .measure().by(|input: &T| raw_value)
    .map01().by(|raw: &Raw, input: &T| Value01::witness(raw).unwrap())

Score Sets. score_set! declares a weighted set of metrics. Weights are validated strictly positive and normalized to sum to 1. Validation fails at build time if any weight is ≤ 0.

let ms = score_set! { 2.0 => gc, 3.0 => len, 5.0 => specificity }?;

Scoring. score().by(closure) gives access to every member. Each member provides .metric() (the operator) and .contribute(value01) (score × normalized weight). The closure composes contributions arbitrarily — different operators can consume different input shapes, capture external context, or conditionally participate.

Linear combination:

let score = ms.score().by(|(gc, len, spec)| {
    gc.contribute(gc.metric().eval(&dna))
        + len.contribute(len.metric().eval(&dna.len()))
        + spec.contribute(spec.metric().eval(&(&dna, &ctx)))
});

Geometric (product) — all-or-nothing scoring sensitive to any weak metric:

let score = ms.score().by(|(gc, len, spec)| {
    gc.contribute(gc.metric().eval(&dna))
        * len.contribute(len.metric().eval(&dna.len()))
        * spec.contribute(spec.metric().eval(&(&dna, &ctx)))
});

Controlled values.

Function Returns Guarantee
Value01::witness(v) Witnessed<T, Value01> finite, ∈ [0, 1]
NormalizedContainer::witness(vec) Witnessed<Vec<T>, NormalizedContainer> all ∈ [0, 1], sum = 1

NormalizedWeight credentials are extracted from a validated container via NormalizedWeight::from_normalized_container(value, &container), which binary-searches for membership verification.

Arity

Default supports up to 8 metrics per set. Opt into larger arities via Cargo features:

score-set = { features = ["level-16"] }   # up to 16
score-set = { features = ["level-128"] }  # up to 128

License

MIT OR Apache-2.0