Skip to content

API reference

The public API is intentionally organised around scientific workflow layers rather than model-provider abstractions.

Top-level package

GazeForge: auditable AI for eye-tracking analysis.

AOI dataclass

Rectangular semantic area of interest with provenance metadata.

CallableAOIProvider dataclass

Adapter for custom/local detectors used by research teams.

detect(image: Any, labels: Sequence[str]) -> list[AOI]

Run the supplied detector.

HuggingFaceZeroShotAOIProvider dataclass

Optional OWL-ViT zero-shot object detector via Transformers.

detect(image: Any, labels: Sequence[str]) -> list[AOI]

Return open-vocabulary rectangular proposals.

BenchmarkDatasetCard dataclass

Provenance, evidence-strength, and split metadata for one benchmark dataset.

annotation_origin describes who or what produced the reference labels. sampling_origin distinguishes native recordings from derived/resampled views. reference_strength states the strongest validation interpretation supported by the reference. These fields are intentionally explicit so algorithm-generated labels cannot be presented as human validation merely because the underlying recording was sampled at a desirable rate.

is_human_reference: bool property

Whether the card represents a human-derived validation reference.

is_native_human_reference: bool property

Whether human reference labels are evaluated at the native acquisition rate.

__post_init__() -> None

Reject ambiguous evidence metadata before reports are generated.

to_dict() -> dict[str, Any]

Serialize the dataset card.

EventModelComparison dataclass

Predictions, fold metrics, and aggregate summaries for matched validation folds.

CrossDatasetEventPrepared dataclass

Harmonised lower-rate data plus dataset-level preparation reports.

CrossDatasetEventValidation dataclass

Leave-one-dataset-out results for the two learned GazeForge baselines.

BenchmarkDashboard dataclass

Validated benchmark reports and verified report suites for public evidence.

CallableDynamicAOIProvider dataclass

Adapter for custom local dynamic-AOI detectors and trackers.

track(stimulus: Any, labels: Sequence[str]) -> list[DynamicAOIKeyframe]

Run the supplied detector/tracker.

DynamicAOIKeyframe dataclass

Timestamped rectangular geometry for one semantic AOI track.

DynamicAOIEvaluation dataclass

Geometry/semantic metrics and timestamp-level matches for dynamic AOI tracks.

EventLevelEvaluation dataclass

Event intervals, one-to-one matches, and aggregate/per-class metrics.

EventModel dataclass

A fitted probabilistic event classifier plus compatibility metadata.

BenchmarkIntegrityError

Bases: GazeForgeError, ValueError

Raised when a frozen benchmark report fails integrity/provenance validation.

GazeInWildLabellerAgreementRun dataclass

Aligned audited streams and pooled rate-aware human-human agreement evidence.

GazeInWildAuditedFile dataclass

One verified label/process pair with its loaded gaze stream.

GazeInWildLabelFileRecord dataclass

One audited human-labelled MATLAB stream and its identity mapping.

GazeInWildProcessFileRecord dataclass

One audited ProcessData MATLAB file.

GazeInWildSourceAuditRun dataclass

Verified source audit and all audited per-labeller streams.

GazeInWildSourceAuditSpec dataclass

Evidence contract required before Gaze-in-the-Wild results are frozen.

GazeInWildModelValidationRun dataclass

Prepared data, participant-held-out comparisons, sensitivities, and report.

GazeInWildPreparedBenchmark dataclass

One audited labeller prepared at a common analysis cadence.

Hollywood2SourceAuditRun dataclass

Verified source audit plus both human annotation streams.

Hollywood2SourceAuditSpec dataclass

Evidence contract required before Hollywood2EM is used in frozen modelling.

__post_init__() -> None

Keep templates non-empirical and make empirical audits fully explicit.

to_dict() -> dict[str, Any]

Return a deterministic JSON-compatible specification mapping.

from_dict(payload: Mapping[str, Any]) -> Hollywood2SourceAuditSpec classmethod

Construct a source-audit specification from decoded JSON.

Hollywood2SourceFileRecord dataclass

One audited Hollywood2EM ARFF file and its participant/trial identity.

__post_init__() -> None

Reject unsafe paths, weak digests, and unresolved identities.

to_dict() -> dict[str, Any]

Return a deterministic JSON-compatible record.

from_dict(payload: Mapping[str, Any]) -> Hollywood2SourceFileRecord classmethod

Construct one record from decoded JSON.

Lund2013BenchmarkRun dataclass

Prepared data, matched-fold comparisons, stratified metrics, and report.

Lund2013PreparedBenchmark dataclass

Prepared Lund2013 rows plus explicit inclusion/exclusion provenance.

Lund2013FetchResult dataclass

Local checkout metadata returned by :func:fetch_lund2013_dataset.

Lund2013SensitivityRun dataclass

Sensitivity surface, dataset evidence card, and deterministic benchmark report.

Lund2013BenchmarkSuiteRun dataclass

Frozen child reports plus the deterministic suite-level manifest.

ModelCard dataclass

Minimal model card required for auditable GazeForge inference.

to_dict() -> dict[str, Any]

Serialize to a dictionary.

to_json(*, indent: int = 2) -> str

Serialize to JSON.

NativeEventAnnotatorAgreementRun dataclass

Verified annotation streams, aligned samples, and fingerprinted agreement report.

NativeEventBenchmarkRun dataclass

Prepared native corpus, matched model comparison, and frozen-report payload.

NativeEventBenchmarkSpec dataclass

Explicit metadata contract for a native-rate human-labelled event corpus.

__post_init__() -> None

Reject incomplete or misleading native-evidence metadata.

to_dict() -> dict[str, Any]

Return a deterministic JSON-compatible specification mapping.

from_dict(payload: Mapping[str, Any]) -> NativeEventBenchmarkSpec classmethod

Construct a specification from decoded JSON without silently ignoring keys.

NativeEventPreparedBenchmark dataclass

Verified native-rate analysis table plus evidence metadata.

NativeEventValidationSuiteRun dataclass

Frozen native-event child reports plus the deterministic completion manifest.

PairedModelDifferences dataclass

Per-fold paired deltas plus descriptive summaries for every model pair.

AuditTrail dataclass

Mutable collection of provenance records.

add(*, operation: str, input_data: pd.DataFrame, output_data: pd.DataFrame, parameters: dict[str, Any] | None = None, model_name: str | None = None, model_version: str | None = None, warnings: list[str] | None = None) -> ProvenanceRecord

Fingerprint and append one operation.

to_frame() -> pd.DataFrame

Return records as a flat table.

to_json(*, indent: int = 2) -> str

Serialize records to JSON.

ProvenanceRecord dataclass

One auditable analysis operation.

to_dict() -> dict[str, Any]

Serialize the record.

BenchmarkResamplingResult dataclass

Resampled labelled gaze data plus a machine-readable resampling report.

SamplingSensitivityResult dataclass

Complete settings ledger and model metrics for a resampling sensitivity surface.

ScanpathEmbeddingModel dataclass

TF-IDF n-gram encoder with optional learned SVD compression.

GazeFrame dataclass

Validated canonical gaze samples plus recording metadata.

copy() -> GazeFrame

Return an independent copy.

SourceResolutionRecord dataclass

Compact common identity for one validated benchmark source-resolution checkpoint.

SourceResolutionDashboard dataclass

Integrity-checked source-resolution records prepared for public status reporting.

SourceResolutionBundleLock dataclass

Typed identity of a validated reviewed source-resolution snapshot.

StratifiedEventPerformance dataclass

Fold-wise and aggregate metrics computed from fixed out-of-fold predictions.

TemporalContextModel dataclass

A fitted context-window neural event classifier plus compatibility metadata.

ValidationResult dataclass

Grouped cross-validation predictions, fold metadata, and aggregate metrics.

VisusDynamicAOIHumanAgreementRun dataclass

Bidirectional dynamic-AOI agreement between two verified independent streams.

VisusAuditedFile dataclass

One verified local file paired with its manifest record.

VisusSourceAuditRun dataclass

Verified VISUS snapshot and deterministic source-audit report.

VisusSourceAuditSpec dataclass

Reviewed provenance and exact-file contract for a VISUS dataset copy.

to_dict() -> dict[str, Any]

Return a deterministic JSON-ready representation.

VisusSourceFileRecord dataclass

One exact file identity in an audited VISUS snapshot.

VisusFrozenEvidenceBundle dataclass

Verified suite plus execution-provenance identities eligible for evidence review.

VisusCanonicalAOIIntakeRun dataclass

Canonical VISUS AOIs linked back to an exact audited source snapshot.

VisusDynamicAOIPredictionIntakeRun dataclass

Canonical model AOI predictions linked to an audited VISUS video snapshot.

VisusDynamicAOIValidationSuiteRun dataclass

Frozen VISUS provenance/validation reports plus a completion manifest.

VisusDynamicAOIModelValidationRun dataclass

Per-stimulus model-human evaluations and a deterministic benchmark report.

adapt_gazepoint_samples(data: pd.DataFrame, *, screen_size_px: tuple[int, int], participant_col: str = 'USER_FILE', trial_col: str = 'MEDIA_ID', timestamp_col: str = 'TIME', x_col: str = 'BPOGX', y_col: str = 'BPOGY', pupil_col: str | None = None, validity_col: str | None = None, time_unit: Literal['seconds', 'milliseconds'] = 'seconds', coordinates: Literal['normalized', 'pixels'] = 'normalized', sampling_rate_hz: float | None = None) -> GazeFrame

Adapt Gazepoint-style sample exports using explicitly declared column semantics.

Gazepoint point-of-gaze coordinates are commonly exported as fractions of screen size. This adapter therefore defaults to coordinates="normalized" and requires the screen dimensions so the canonical representation is in pixels.

Column names remain configurable because Gazepoint export variants and upstream packages may expose different gaze/fixation fields.

adapt_processed_table(data: pd.DataFrame, *, participant_col: str, trial_col: str, timestamp_col: str, x_col: str, y_col: str, pupil_col: str | None = None, validity_col: str | None = None, timestamp_scale_to_ms: float = 1.0, coordinate_scale: tuple[float, float] = (1.0, 1.0), sampling_rate_hz: float | None = None, screen_size_px: tuple[int, int] | None = None, source_name: str = 'processed_table') -> GazeFrame

Adapt an eyeprocesspy/gpbiometricspy/custom processed table without guessing columns.

aois_to_frame(aois: Sequence[AOI]) -> pd.DataFrame

Convert AOIs to an ordinary reviewable table.

apply_aoi_review(aois: Sequence[AOI], decisions: pd.DataFrame) -> tuple[list[AOI], pd.DataFrame]

Apply explicit human accept/reject/relabel/rebound decisions.

detect_semantic_aois(image: Any, *, labels: Sequence[str], provider: AOIProvider, min_confidence: float = 0.1) -> list[AOI]

Generate semantic AOI proposals and retain only proposals above a threshold.

map_fixations_to_aois(fixations: pd.DataFrame, aois: Sequence[AOI], *, x_col: str = 'x_px', y_col: str = 'y_px', overlap_rule: str = 'highest_confidence', trail: AuditTrail | None = None) -> pd.DataFrame

Assign each fixation to at most one AOI while preserving unassigned rows.

build_audit_report(data: pd.DataFrame, *, trail: AuditTrail | None = None, model_cards: list[ModelCard] | None = None) -> dict[str, Any]

Build a JSON-serialisable snapshot of data, provenance, and model metadata.

gaze_in_wild_manual_event_card() -> BenchmarkDatasetCard

Return metadata for the independently hand-labelled Gaze-in-the-Wild corpus.

hollywood2_manual_event_card() -> BenchmarkDatasetCard

Return metadata for the expert-corrected Hollywood2 eye-movement annotations.

visus_dynamic_aoi_card() -> BenchmarkDatasetCard

Return the reviewed metadata card for the VISUS dynamic-AOI benchmark.

The historical data endpoint has moved/retired, so GazeForge records the benchmark but does not claim to redistribute it or provide a stable downloader until current reuse terms and a surviving authoritative distribution location are independently verified.

benchmark_fingerprint(payload: Any) -> str

Return a SHA-256 fingerprint of canonical JSON content.

build_benchmark_report(*, benchmark: BenchmarkDatasetCard, metrics: dict[str, Any], model: dict[str, Any] | None = None, protocol: dict[str, Any] | None = None) -> dict[str, Any]

Build a benchmark report without adding non-deterministic timestamps.

freeze_benchmark_report(report: dict[str, Any], path: str | Path, *, overwrite: bool = False) -> Path

Write a deterministic benchmark JSON artifact.

Existing files are protected by default so a previously reported validation result cannot be silently replaced during a later run.

evaluate_event_calibration(predictions: pd.DataFrame, *, true_label_col: str = 'event_label', probability_prefix: str = 'p_event_', n_bins: int = 10) -> dict[str, Any]

Return Brier score, ECE, calibration bins, and confidence/coverage diagnostics.

expected_calibration_error(predictions: pd.DataFrame, *, true_label_col: str = 'event_label', probability_prefix: str = 'p_event_', n_bins: int = 10) -> float

Return weighted top-label expected calibration error (ECE).

multiclass_brier_score(y_true: Sequence[object] | pd.Series | np.ndarray, probabilities: pd.DataFrame | np.ndarray, *, labels: Sequence[str] | None = None) -> float

Return the mean multiclass Brier score (lower is better).

selective_accuracy_curve(predictions: pd.DataFrame, *, true_label_col: str = 'event_label', confidence_col: str = 'event_confidence', predicted_label_col: str = 'predicted_event', thresholds: Sequence[float] = (0.0, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95)) -> pd.DataFrame

Report accuracy-versus-coverage as low-confidence samples are abstained from.

top_label_calibration_table(predictions: pd.DataFrame, *, true_label_col: str = 'event_label', probability_prefix: str = 'p_event_', n_bins: int = 10) -> pd.DataFrame

Bin top-label confidence and compare confidence with empirical accuracy.

compare_event_models_grouped(data: pd.DataFrame, *, label_col: str = 'event_label', group_col: str = 'participant_id', n_splits: int = 5, sampling_rate_hz: float | None = None, ivt_velocity_threshold_px_s: float | None = 1000.0, ivt_velocity_threshold_deg_s: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, include_event_level_metrics: bool = True, event_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> EventModelComparison

Compare I-VT, Random Forest, and temporal MLP on identical group-held-out folds.

Each learned model is fitted from scratch within every fold. All three methods are evaluated on the exact same test rows. Calibration metrics are reported only for probabilistic learned models; deterministic I-VT receives missing calibration values rather than fabricated scores.

prepare_cross_dataset_event_benchmark(datasets: Mapping[str, GazeFrame], *, source_audit_lineages: Mapping[str, SourceAuditLineageReceipt] | None = None, target_sampling_rate_hz: float = 60.0, common_labels: Sequence[str] = ('fixation', 'saccade', 'pursuit'), min_label_purity: float = 0.75, ambiguous_label: str = 'ambiguous', require_resolved_participants: bool = True, require_verified_coordinates: bool = True, require_source_audits: bool = True, require_all_common_labels: bool = True) -> CrossDatasetEventPrepared

Prepare multiple human-reference corpora for matched lower-rate validation.

Each source is independently resampled to the requested rate using the benchmark resampling guardrails. Participant and trial identifiers are namespaced by dataset after source identities have been checked, preventing accidental collisions across independently collected corpora. External sources with a reviewed source-audit contract must also supply the matching lineage receipt before their data may enter cross-dataset modelling.

run_cross_dataset_event_validation(prepared: CrossDatasetEventPrepared, *, label_col: str = 'event_label', min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> CrossDatasetEventValidation

Run RF and temporal-context MLP in a leave-one-dataset-out design.

build_benchmark_dashboard(root: str | Path, *, recursive: bool = True) -> BenchmarkDashboard

Build evidence tables from integrity-checked reports and complete suites under root.

Duplicate report and suite fingerprints are rejected so copied artifacts cannot inflate the apparent number of independent validation results or completed tranches on a public dashboard. Provenance-only JSON children are never promoted to performance-report rows. VISUS suites are surfaced only after the Frozen Evidence bundle gate verifies both the suite and its raw-execution provenance manifest.

discover_frozen_benchmark_reports(root: str | Path, *, recursive: bool = True) -> tuple[Path, ...]

Discover benchmark-schema reports while ignoring provenance/config JSON files.

A deterministic report_fingerprint_sha256 can also belong to audited intake or provenance reports. Those files are deliberately not treated as performance evidence unless they contain the complete benchmark/model/protocol/metrics report body.

discover_lund2013_suite_manifests(root: str | Path, *, recursive: bool = True) -> tuple[Path, ...]

Discover Lund suite completion manifests without treating them as result rows.

discover_visus_dynamic_aoi_suite_manifests(root: str | Path, *, recursive: bool = True) -> tuple[Path, ...]

Discover VISUS dynamic-AOI completion manifests for strict evidence validation.

load_frozen_benchmark_report(path: str | Path) -> dict[str, Any]

Load and integrity-check one frozen benchmark JSON report.

render_benchmark_dashboard_markdown(dashboard: BenchmarkDashboard) -> str

Render a conservative Markdown evidence index for the documentation website.

validate_frozen_benchmark_report(report: dict[str, Any]) -> str

Validate report structure and recompute its deterministic SHA-256 fingerprint.

The fingerprint is computed from the same four report-body objects used by :func:gazeforge.benchmarks.build_benchmark_report. Any later edit to benchmark metadata, protocol, model metadata, or metrics therefore invalidates the report.

detect_dynamic_aois(stimulus: Any, *, labels: Sequence[str], provider: DynamicAOIProvider, min_confidence: float = 0.1) -> list[DynamicAOIKeyframe]

Generate dynamic semantic AOI keyframes with an explicit confidence threshold.

dynamic_aois_from_frame(frame: pd.DataFrame, *, default_source: str = 'manual') -> list[DynamicAOIKeyframe]

Parse a canonical dynamic-AOI keyframe table into validated objects.

Required columns are aoi_id, label, timestamp_ms, xmin, ymin, xmax, and ymax. Optional confidence/provenance columns are preserved when present. Duplicate aoi_id/timestamp pairs are rejected because interpolation would otherwise be ambiguous.

dynamic_aois_to_frame(keyframes: Sequence[DynamicAOIKeyframe]) -> pd.DataFrame

Convert timestamped AOI geometry to a reviewable long-format table.

interpolate_dynamic_aoi(keyframes: Sequence[DynamicAOIKeyframe], timestamp_ms: float, *, max_gap_ms: float = 100.0) -> DynamicAOIKeyframe | None

Return exact/interpolated AOI geometry without temporal extrapolation.

Interpolation is only permitted when the requested timestamp lies between two keyframes and the bracketing interval does not exceed max_gap_ms. Requests outside the observed track range always return None.

map_fixations_to_dynamic_aois(fixations: pd.DataFrame, keyframes: Sequence[DynamicAOIKeyframe], *, timestamp_col: str = 'timestamp_ms', x_col: str = 'x_px', y_col: str = 'y_px', max_interpolation_gap_ms: float = 100.0, overlap_rule: str = 'highest_confidence', trail: AuditTrail | None = None) -> pd.DataFrame

Map timestamped fixations to dynamic AOIs without extrapolating track geometry.

build_dynamic_aoi_benchmark_report(evaluation: DynamicAOIEvaluation, *, benchmark: BenchmarkDatasetCard, model: dict[str, Any] | None = None, protocol: dict[str, Any] | None = None, fixation_agreement: dict[str, Any] | None = None, include_matches: bool = False) -> dict[str, Any]

Build a deterministic benchmark report for dynamic AOI evaluation.

Timestamp-level metrics are retained so aggregate IoU/F1 values can be audited. Full matching rows are optional because long video benchmarks can generate large artifacts.

dynamic_aoi_snapshot(keyframes: Sequence[DynamicAOIKeyframe], timestamp_ms: float, *, max_interpolation_gap_ms: float = 100.0) -> list[AOI]

Resolve all available dynamic AOI tracks into static AOIs at one timestamp.

Geometry outside a track's observed range, or across a bracketing gap larger than max_interpolation_gap_ms, is omitted rather than extrapolated.

dynamic_fixation_assignment_agreement(fixations: pd.DataFrame, left: Sequence[DynamicAOIKeyframe], right: Sequence[DynamicAOIKeyframe], *, timestamp_col: str = 'timestamp_ms', x_col: str = 'x_px', y_col: str = 'y_px', max_interpolation_gap_ms: float = 100.0, overlap_rule: str = 'highest_confidence') -> dict[str, Any]

Compare two dynamic-AOI references through their fixation assignments.

evaluate_dynamic_aoi_tracks(predicted: Sequence[DynamicAOIKeyframe], reference: Sequence[DynamicAOIKeyframe], *, timestamps_ms: Sequence[float], max_interpolation_gap_ms: float = 100.0, min_iou: float = 0.5, require_label_match: bool = False) -> DynamicAOIEvaluation

Evaluate dynamic AOI geometry/semantics on an explicit timestamp grid.

The evaluation grid is supplied by the caller rather than inferred from prediction timestamps. This prevents a model from improving apparent coverage by choosing when it emits keyframes. Empty timestamps contribute no true/false detections but are counted in coverage diagnostics.

aoi_boundary_sensitivity(fixations: pd.DataFrame, aois: Sequence[AOI], *, perturbations_px: Sequence[float] = (-10.0, -5.0, 5.0, 10.0), x_col: str = 'x_px', y_col: str = 'y_px', label_col: str = 'aoi_label') -> pd.DataFrame

Quantify fixation-assignment stability under AOI boundary perturbations.

Positive perturbations expand every boundary. Negative values contract boundaries while retaining only AOIs that still have positive width and height.

aoi_iou(left: AOI, right: AOI) -> float

Return intersection-over-union for two rectangular AOIs.

evaluate_aoi_detection(predicted: Sequence[AOI], reference: Sequence[AOI], *, min_iou: float = 0.5, require_label_match: bool = False) -> dict[str, Any]

Compute geometric and semantic agreement against expert/reference AOIs.

fixation_assignment_agreement(predicted: pd.DataFrame, reference: pd.DataFrame, *, key_cols: tuple[str, ...] = ('participant_id', 'trial_id', 'fixation_index'), label_col: str = 'aoi_label', unassigned_label: str = 'UNASSIGNED') -> dict[str, Any]

Compare AI-derived and reference fixation-to-AOI assignments on aligned fixations.

match_aois(predicted: Sequence[AOI], reference: Sequence[AOI], *, min_iou: float = 0.5, require_label_match: bool = False) -> pd.DataFrame

One-to-one match predicted AOIs to references using maximum total IoU.

Hungarian assignment is applied globally. Matches below min_iou are reported as unmatched, which prevents weak overlaps from inflating detection performance.

pairwise_aoi_iou(predicted: Sequence[AOI], reference: Sequence[AOI]) -> pd.DataFrame

Return all predicted-reference AOI IoU values in long format.

sample_label_agreement(left: pd.DataFrame, right: pd.DataFrame, *, key_cols: tuple[str, ...] = ('participant_id', 'trial_id', 'timestamp_ms'), label_col: str = 'event_label', missing_label: str = 'MISSING') -> dict[str, Any]

Compare aligned sample-level event labels from two annotators or methods.

evaluate_event_intervals(predicted: pd.DataFrame, reference: pd.DataFrame, *, group_cols: Sequence[str] = ('participant_id', 'trial_id'), label_col: str = 'event_label', min_iou: float = 0.5, require_label_match: bool = True) -> EventLevelEvaluation

Evaluate event detection/classification with one-to-one temporal matching.

evaluate_sample_event_predictions(data: pd.DataFrame, *, true_label_col: str = 'event_label', predicted_label_col: str = 'predicted_event', timestamp_col: str = 'timestamp_ms', group_cols: Sequence[str] = ('participant_id', 'trial_id'), sampling_rate_hz: float, excluded_labels: Sequence[str] = _DEFAULT_EXCLUDED_LABELS, max_gap_factor: float = 1.5, min_iou: float = 0.5, require_label_match: bool = True) -> EventLevelEvaluation

Segment sample-level truth/predictions and evaluate them at event level.

match_event_intervals(predicted: pd.DataFrame, reference: pd.DataFrame, *, group_cols: Sequence[str] = ('participant_id', 'trial_id'), label_col: str = 'event_label', min_iou: float = 0.5, require_label_match: bool = True) -> pd.DataFrame

One-to-one match predicted events to references within each participant/trial group.

The maximum-total-IoU Hungarian objective is unchanged. Because each validated event stream is internally non-overlapping, only positive temporal-overlap edges can influence that objective. The positive-edge graph is decomposed into independent components before assignment, avoiding a corpus-scale dense Cartesian IoU matrix without changing accepted matches.

samples_to_event_intervals(data: pd.DataFrame, *, label_col: str = 'event_label', timestamp_col: str = 'timestamp_ms', group_cols: Sequence[str] = ('participant_id', 'trial_id'), sampling_rate_hz: float, excluded_labels: Sequence[str] = _DEFAULT_EXCLUDED_LABELS, max_gap_factor: float = 1.5) -> pd.DataFrame

Convert sample labels to contiguous half-open event intervals.

Segmentation occurs before excluded labels are removed, so an ambiguous/undefined run remains a hard separator between two otherwise identical event labels. A timestamp gap larger than max_gap_factor nominal sample periods also starts a new event.

temporal_event_iou(predicted_start_ms: float, predicted_end_ms: float, reference_start_ms: float, reference_end_ms: float) -> float

Return temporal intersection-over-union for two half-open event intervals.

ai_classify_events(data: pd.DataFrame, model: EventModel, *, sampling_rate_hz: float | None = None, min_confidence: float = 0.6, sampling_rate_tolerance: float = 0.1) -> pd.DataFrame

Classify samples with probabilities and enforce sampling-rate compatibility.

evaluate_event_predictions(y_true: pd.Series | np.ndarray, y_pred: pd.Series | np.ndarray) -> dict[str, Any]

Return classification metrics and a labelled confusion matrix.

ivt_classify_events(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, velocity_threshold_px_s: float = 1000.0) -> pd.DataFrame

Transparent I-VT-style baseline in pixel coordinates.

ivt_classify_events_angular(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, velocity_threshold_deg_s: float = 45.0) -> pd.DataFrame

Transparent I-VT baseline using geometry-normalized angular velocity.

The default 45 deg/s threshold matches the Lund2013-calibrated maximum fixation velocity reported for the Andersson et al. benchmark. The threshold remains an explicit parameter and should not be treated as universally optimal for every device, task, or population.

train_event_classifier(data: pd.DataFrame, *, label_col: str = 'event_label', sampling_rate_hz: float | None = None, random_state: int = 42, n_estimators: int = 300, rolling_window_ms: float = 80.0) -> EventModel

Fit a probabilistic event model to labelled samples.

This function fits a model; it deliberately does not report validation performance. Scientific evaluation should use participant-held-out and, where applicable, stimulus/dataset-held-out test data.

load_gaze_in_wild_directory(label_root: str | Path, *, process_root: str | Path | None = None, participant_parser: Callable[[Path], str | None] | None = None, labeller: int | None = None, recursive: bool = True, confidence_threshold: float = 0.3) -> GazeFrame

Load a directory of Gaze-in-the-Wild annotation files without guessing identities.

load_gaze_in_wild_mat(label_path: str | Path, *, process_path: str | Path | None = None, participant_id: str | None = None, trial_id: str | None = None, confidence_threshold: float = 0.3) -> GazeFrame

Load one manually annotated Gaze-in-the-Wild recording.

Sampling rate is inferred from LabelData.T rather than hard-coded. The published eye-tracker acquisition rate (120 Hz) and the official processed-stream target cadence (300 Hz) are retained as distinct provenance. When ProcessData is supplied, the official processing schema defines ETG.POR as normalized scene-camera coordinates whose y axis has already been flipped to MATLAB image convention; those coordinates are converted to canonical pixels using ETG.SceneResolution.

run_gaze_in_wild_labeller_agreement(audit: GazeInWildSourceAuditRun, *, left_labeller: int, right_labeller: int, excluded_labels: tuple[str, ...] = ('unlabelled',), exclude_invalid_tracking: bool = True, event_min_iou: float = 0.5, require_complete_overlap: bool = True) -> GazeInWildLabellerAgreementRun

Compare two human labellers only after the Gaze-in-the-Wild source audit passes.

Each shared participant/trial is segmented at its own timestamp-inferred sampling rate before event intervals are pooled in milliseconds. This avoids inventing one nominal cadence for a distributed snapshot whose files may differ. Invalid tracking samples can be retained as hard event separators while being excluded from analysis-label agreement.

audit_gaze_in_wild_source(label_root: str | Path, process_root: str | Path, spec: GazeInWildSourceAuditSpec) -> GazeInWildSourceAuditRun

Verify an authoritative local Gaze-in-the-Wild copy before empirical reporting.

The audit binds exact label/process files to participant, trial, and labeller identities; checks current reuse declarations and coordinate evidence; infers native-file cadence from timestamps; and verifies that different labellers for the same trial reference identical underlying gaze. It produces no model-performance metrics and makes no raw-data redistribution claim.

audited_gaze_in_wild_files_by_labeller(run: GazeInWildSourceAuditRun) -> dict[int, list[GazeInWildAuditedFile]]

Group an already verified audit run by human labeller without merging file cadences.

load_gaze_in_wild_source_audit_spec(path: str | Path) -> GazeInWildSourceAuditSpec

Load a Gaze-in-the-Wild source-audit specification from JSON.

prepare_gaze_in_wild_benchmark(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt, *, labeller_id: int, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, max_coordinate_gap_factor: float = 1.5, task_mapping: pd.DataFrame | None = None, task_col: str = 'task_label') -> GazeInWildPreparedBenchmark

Prepare one lineage-bound audited human labeller for event-model validation.

run_gaze_in_wild_model_validation(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt, *, labeller_id: int, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, max_coordinate_gap_factor: float = 1.5, task_mapping: pd.DataFrame | None = None, task_col: str = 'task_label', n_splits: int = 5, ivt_velocity_threshold_px_s: float = 1000.0, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5) -> GazeInWildModelValidationRun

Run lineage-bound participant-disjoint validation on audited Gaze-in-the-Wild.

angular_kinematic_features(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), timestamp_col: str = 'timestamp_ms', x_col: str = 'x_px', y_col: str = 'y_px', screen_width_px_col: str = 'screen_width_px', screen_height_px_col: str = 'screen_height_px', screen_width_physical_col: str = 'screen_width_physical', screen_height_physical_col: str = 'screen_height_physical', view_distance_physical_col: str = 'view_distance_physical') -> pd.DataFrame

Compute boundary-safe sample displacement and angular velocity in degrees/second.

Physical screen dimensions and viewing distance must use the same length unit. Geometry is required to be invariant within each participant/trial group; GazeForge refuses to average conflicting geometry metadata.

pixels_to_visual_angle_deg(pixels: float | Sequence[float] | np.ndarray, *, physical_extent: float, pixel_extent: float, viewing_distance: float) -> np.ndarray

Convert a pixel extent to degrees of visual angle.

physical_extent and viewing_distance may use any shared physical length unit. The conversion follows the geometry used by Lund2013's pixels2degrees.m helper: 2 * atan((pixels * physical_extent / pixel_extent) / (2 * viewing_distance)).

load_hollywood2_arff(path: str | Path, *, annotator: str = 'final', label_col: str | None = None, participant_id: str | None = None, trial_id: str | None = None, split: str | None = None, expected_sampling_rate_hz: float | None = 500.0, sampling_rate_tolerance: float = 0.05, confidence_threshold: float = 0.5, zero_pair_is_missing: bool = True, coordinate_unit: str = 'unverified') -> GazeFrame

Load one Hollywood2EM hand-labelled ARFF recording.

The published/TUM evaluation convention stores time in microseconds and uses x, y, and confidence for the gaze samples. handlabeller_1 contains the first/student coding pass and handlabeller_final contains the expert-corrected labels. Participant identity is deliberately not guessed from filenames; callers should supply it when participant-held-out validation is intended.

load_hollywood2_directory(root: str | Path, *, annotator: str = 'final', identity_parser: IdentityParser | None = None, expected_sampling_rate_hz: float | None = 500.0, sampling_rate_tolerance: float = 0.05, coordinate_unit: str = 'unverified') -> GazeFrame

Load a Hollywood2EM ground-truth tree without guessing participant identities.

identity_parser receives each ARFF path relative to the selected ground-truth directory and must return (participant_id, trial_id). Without it, every row receives the sentinel __unresolved__ participant ID; this intentionally prevents accidental participant-held-out validation until the repository-specific identity mapping has been supplied and audited.

audit_hollywood2_source(root: str | Path, spec: Hollywood2SourceAuditSpec) -> Hollywood2SourceAuditRun

Verify an authoritative local Hollywood2EM copy before empirical modelling.

The audit is intentionally non-statistical: it verifies exact file identity, reuse/analysis declarations, participant/trial mapping, coordinate-unit evidence, native sampling rate, and that the student and expert labels refer to the same underlying gaze samples. It does not produce model-performance metrics and it does not imply raw-data redistribution permission.

load_audited_hollywood2_directory(root: str | Path, spec: Hollywood2SourceAuditSpec, *, annotator: str = 'final') -> GazeFrame

Return one Hollywood2 annotation stream only after the full source audit passes.

load_hollywood2_source_audit_spec(path: str | Path) -> Hollywood2SourceAuditSpec

Load a Hollywood2 source-audit specification from JSON.

load_lund2013_directory(root: str | Path, *, annotator: str = 'RA', recursive: bool = True) -> GazeFrame

Load and concatenate Lund2013 files for one annotator from a benchmark directory.

load_lund2013_mat(path: str | Path, *, participant_id: str | None = None, trial_id: str | None = None, annotator: str | None = None, stimulus_type: str | None = None, zero_pair_is_missing: bool = True) -> GazeFrame

Load one annotated Lund2013 MATLAB file into GazeForge's canonical gaze schema.

The public benchmark stores x/y coordinates in columns 4/5 of MATLAB's one-based pos matrix and human event codes in column 6. Codes follow the original benchmark convention: fixation=1, saccade=2, PSO=3, pursuit=4, blink=5, undefined=6; code 0 is retained as unlabelled rather than silently discarded.

compare_lund2013_annotators(root: str | Path, *, left_annotator: str = 'MN', right_annotator: str = 'RA', target_sampling_rate_hz: float | None = None, min_label_purity: float = 0.75) -> dict[str, Any]

Measure the human-human sample-label agreement ceiling for Lund2013.

prepare_lund2013_benchmark(root: str | Path, *, annotator: str = 'RA', target_sampling_rate_hz: float | None = 60.0, min_label_purity: float = 0.75, max_interpolation_gap_ms: float | None = None, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS) -> Lund2013PreparedBenchmark

Load Lund2013 and prepare an explicit native- or lower-rate benchmark table.

The raw benchmark is never modified. When a lower sampling rate is requested, labels are transferred with :func:resample_labeled_gaze; ambiguous target windows remain auditable and are excluded only after their prevalence has been recorded in the preparation report. A GazeForge source manifest, when present, is revalidated with its referenced files before any benchmark rows are loaded.

run_lund2013_event_benchmark(root: str | Path, *, annotator: str = 'RA', target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, n_splits: int = 5, ivt_velocity_threshold_deg_s: float = 45.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200) -> Lund2013BenchmarkRun

Run matched participant-held-out I-VT/RF/ContextMLP validation on Lund2013.

fetch_lund2013_dataset(destination: str | Path, *, annotators: tuple[str, ...] = LUND2013_ANNOTATORS, stimulus_families: tuple[str, ...] = LUND2013_FAMILIES, overwrite: bool = False) -> Lund2013FetchResult

Fetch the pinned external Lund2013 labelled files into a local directory.

This operation is explicit and opt-in. Raw benchmark files remain external to GazeForge and retain the upstream repository licence. Existing files are reused only when their Git blob SHA matches the immutable upstream metadata; mismatching files are never silently replaced unless overwrite=True is requested.

validate_lund2013_source_manifest(root: str | Path, *, verify_files: bool = True) -> dict[str, Any] | None

Validate a GazeForge Lund source manifest and optionally every referenced local file.

Directories without a GazeForge source manifest remain supported and return None. Once a manifest exists, however, it is treated as an integrity claim: its own fingerprint, pinned upstream identity, file inventory, and (by default) every referenced MATLAB file must validate before the checkout may be recorded as verified benchmark provenance.

run_lund2013_sampling_sensitivity(root: str | Path, *, annotator: str = 'RA', target_sampling_rates_hz: tuple[float, ...] = (120.0, 90.0, 60.0, 30.0), min_label_purities: tuple[float, ...] = (0.6, 0.75, 0.9), n_splits: int = 5, ivt_velocity_threshold_deg_s: float = 45.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, max_interpolation_gap_ms: float | None = None) -> Lund2013SensitivityRun

Evaluate Lund2013 across lower sampling rates and label-purity thresholds.

The workflow uses one expert annotation stream at a time, records ambiguity before exclusions, and applies the same default label policy as the primary Lund benchmark. The angular I-VT baseline remains fixed at the supplied degrees/second threshold across the sensitivity surface. A GazeForge source manifest, when present, is revalidated with its referenced files before the sensitivity analysis begins and its verified identity is stored in the report protocol.

run_lund2013_benchmark_suite(root: str | Path, output_dir: str | Path, *, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, n_splits: int = 5, ivt_velocity_threshold_deg_s: float = 45.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, sensitivity_target_rates_hz: tuple[float, ...] = (120.0, 90.0, 60.0, 30.0), sensitivity_min_label_purities: tuple[float, ...] = (0.6, 0.75, 0.9), overwrite: bool = False) -> Lund2013BenchmarkSuiteRun

Run and freeze the complete first-pass Lund2013 empirical validation suite.

All analyses are computed before any suite-completion manifest is written. Child reports retain independent fingerprints. The suite manifest is written last, so an interrupted or failed run cannot masquerade as a complete validation tranche merely because one child report exists.

validate_lund2013_suite_manifest(path: str | Path, *, verify_reports: bool = True) -> dict[str, Any]

Validate a frozen Lund suite manifest and, by default, every referenced child report.

run_native_event_annotator_agreement(data: pd.DataFrame, spec: NativeEventBenchmarkSpec, *, left_annotator: str, right_annotator: str, event_min_iou: float = 0.5, source_file_name: str | None = None, source_file_sha256: str | None = None) -> NativeEventAnnotatorAgreementRun

Quantify native sample-label and event-boundary agreement between two human annotators.

Both annotation streams undergo the same native-rate verification used by model validation. Excluded labels are preserved during event segmentation so undefined/noise runs remain hard temporal separators rather than being deleted first and joining adjacent events.

run_native_event_file_annotator_agreement(data_path: str | Path, spec_path: str | Path, *, left_annotator: str, right_annotator: str, event_min_iou: float = 0.5) -> NativeEventAnnotatorAgreementRun

Load, fingerprint, verify, and compare two native human annotation streams.

file_sha256(path: str | Path) -> str

Return the SHA-256 digest of an external benchmark source file.

load_native_event_spec(path: str | Path) -> NativeEventBenchmarkSpec

Load a native event benchmark specification from JSON.

load_native_event_table(path: str | Path) -> pd.DataFrame

Load a portable native benchmark table from CSV or TSV without altering rows.

prepare_native_event_benchmark(data: pd.DataFrame, spec: NativeEventBenchmarkSpec, *, annotator: str | None = None, source_file_name: str | None = None, source_file_sha256: str | None = None) -> NativeEventPreparedBenchmark

Verify that human-labelled samples are genuinely native-rate benchmark evidence.

No temporal resampling occurs in this function. The declared native sampling rate is checked both globally and within every participant/trial group before the dataset card can claim native human-reference evidence.

run_native_event_benchmark(data: pd.DataFrame, spec: NativeEventBenchmarkSpec, *, annotator: str | None = None, n_splits: int = 5, ivt_velocity_threshold_deg_s: float | None = None, ivt_velocity_threshold_px_s: float | None = None, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, source_file_name: str | None = None, source_file_sha256: str | None = None) -> NativeEventBenchmarkRun

Run matched participant-held-out validation on verified native-rate human labels.

run_native_event_file_benchmark(data_path: str | Path, spec_path: str | Path, **kwargs: Any) -> NativeEventBenchmarkRun

Load, fingerprint, verify, and benchmark one native human-labelled event table.

run_native_event_validation_suite(data_path: str | Path, spec_path: str | Path, output_dir: str | Path, *, primary_annotator: str, sensitivity_annotator: str, event_min_iou: float = 0.5, n_splits: int = 5, ivt_velocity_threshold_deg_s: float | None = None, ivt_velocity_threshold_px_s: float | None = None, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, overwrite: bool = False) -> NativeEventValidationSuiteRun

Compute, freeze, and bind the three-report native event validation tranche.

All analyses are computed and cross-checked before any report is written. The completion manifest is written last. Therefore orphan child reports can never, by themselves, represent a complete validation suite.

validate_native_event_suite_manifest(path: str | Path, *, verify_reports: bool = True) -> dict[str, Any]

Validate a native-event suite manifest and, by default, all child reports.

paired_model_metric_differences(fold_metrics: pd.DataFrame, *, model_col: str = 'model', fold_col: str = 'fold', metrics: tuple[str, ...] | None = None, tie_tolerance: float = 1e-12) -> PairedModelDifferences

Compare model metrics on exactly matched folds without inferential p-values.

Raw deltas are always model_a - model_b. improvement_for_a multiplies the raw delta by the registered metric direction so positive values always mean model A performed better. The function is deliberately descriptive: cross-validation folds share training data and are not treated as independent replicates for hypothesis tests or confidence intervals.

fingerprint_frame(data: pd.DataFrame) -> str

Create a stable SHA-256 fingerprint from values, index, columns, and dtypes.

ai_flag_anomalies(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, contamination: float | str = 'auto', random_state: int = 42, trail: AuditTrail | None = None) -> pd.DataFrame

Flag unusual samples with Isolation Forest without deleting or rewriting samples.

detect_calibration_drift(data: pd.DataFrame, *, expected_x_col: str, expected_y_col: str, threshold_px: float = 100.0, group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame

Score drift only when known calibration/reference target coordinates are supplied.

score_trial_quality(data: pd.DataFrame, *, screen_size_px: tuple[int, int] | None = None, group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame

Summarise missingness, bounds, anomaly rate, and temporal gaps per trial.

resample_labeled_gaze(data: pd.DataFrame, *, target_sampling_rate_hz: float = 60.0, label_col: str = 'event_label', timestamp_col: str = 'timestamp_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), continuous_cols: Sequence[str] = ('x_px', 'y_px', 'pupil'), carry_cols: Sequence[str] = ('annotator', 'stimulus_type', 'dataset_id', 'source_file', 'screen_width_px', 'screen_height_px', 'screen_width_physical', 'screen_height_physical', 'view_distance_physical'), min_label_purity: float = 0.75, ambiguous_label: str = 'ambiguous', max_interpolation_gap_ms: float | None = None, source_sampling_rate_hz: float | None = None) -> BenchmarkResamplingResult

Resample expert-labelled gaze to a lower rate with explicit boundary uncertainty.

Continuous signals are linearly interpolated only across short valid gaps. Event labels are assigned by majority vote within one target-sample window. Windows with tied labels or purity below min_label_purity are marked as ambiguous_label instead of silently forcing an event identity near a source annotation boundary.

evaluate_sampling_purity_sensitivity(data: pd.DataFrame, *, target_sampling_rates_hz: Sequence[float] = (120.0, 90.0, 60.0, 30.0), min_label_purities: Sequence[float] = (0.6, 0.75, 0.9), source_sampling_rate_hz: float | None = None, label_col: str = 'event_label', group_col: str = 'participant_id', resampling_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), ambiguous_label: str = 'ambiguous', excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, n_splits: int = 5, ivt_velocity_threshold_px_s: float | None = 1000.0, ivt_velocity_threshold_deg_s: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, max_interpolation_gap_ms: float | None = None) -> SamplingSensitivityResult

Evaluate model sensitivity to target sampling rate and boundary-label purity.

Every rate/purity condition is retained in settings. Ambiguous and other excluded labels are removed only after their prevalence has been recorded, matching the primary Lund benchmark policy. Conditions that no longer contain enough groups or labels for the requested validation design are recorded as not_evaluable instead of being silently dropped.

cluster_scanpaths_ai(embeddings: pd.DataFrame, *, n_clusters: int, embedding_prefix: str = 'embedding_', random_state: int = 42) -> pd.DataFrame

Cluster learned scanpath embeddings with K-means.

embed_scanpaths(scanpaths: pd.DataFrame, model: ScanpathEmbeddingModel, *, text_col: str = 'sequence_text', id_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame

Transform scanpaths into numeric embeddings.

find_scanpath_motifs(scanpaths: pd.DataFrame, *, sequence_col: str = 'sequence', ngram_range: tuple[int, int] = (2, 3), min_count: int = 2) -> pd.DataFrame

Count recurrent contiguous AOI n-grams.

fit_scanpath_embedder(scanpaths: pd.DataFrame, *, text_col: str = 'sequence_text', n_components: int = 16, ngram_range: tuple[int, int] = (1, 3), random_state: int = 42) -> ScanpathEmbeddingModel

Fit a learned semantic scanpath representation.

scanpath_similarity(embeddings: pd.DataFrame, *, embedding_prefix: str = 'embedding_') -> np.ndarray

Return pairwise cosine similarity for learned embeddings.

to_semantic_scanpaths(fixations: pd.DataFrame, *, label_col: str = 'aoi_label', duration_col: str | None = 'duration_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), collapse_repeats: bool = True, drop_unassigned: bool = True) -> pd.DataFrame

Convert ordered fixation rows into one semantic sequence per trial.

canonicalize_gaze(data: pd.DataFrame, *, column_map: Mapping[str, str] | None = None, sampling_rate_hz: float | None = None, screen_size_px: tuple[int, int] | None = None, metadata: Mapping[str, Any] | None = None, sort: bool = True) -> GazeFrame

Convert a table to GazeForge's canonical sample schema.

column_map maps canonical names to source-column names, for example {"timestamp_ms": "TIME", "x_px": "BPOGX", "y_px": "BPOGY"}.

infer_sampling_rate_hz(data: pd.DataFrame, *, timestamp_col: str = 'timestamp_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> float

Infer sampling rate from the median positive within-trial timestamp interval.

simulate_gaze(*, n_participants: int = 4, n_trials: int = 3, samples_per_trial: int = 240, sampling_rate_hz: float = 60.0, screen_size_px: tuple[int, int] = (1920, 1080), random_state: int = 42) -> pd.DataFrame

Simulate smooth gaze with occasional saccade-like jumps and missing samples.

load_source_resolution_record(path: str | Path) -> SourceResolutionRecord

Return a typed common view after dataset-specific source-resolution validation.

validate_gaze_in_wild_source_resolution_record(path: str | Path) -> dict[str, Any]

Validate the conservative Gaze-in-the-Wild source-resolution checkpoint.

validate_hollywood2_source_resolution_record(path: str | Path) -> dict[str, Any]

Validate the reviewed Hollywood2EM recovered-source checkpoint.

validate_source_resolution_record(path: str | Path) -> dict[str, Any]

Validate a known v1 source-resolution checkpoint and auto-dispatch by dataset.

validate_source_resolution_records(paths: Sequence[str | Path]) -> dict[str, Any]

Validate source-resolution checkpoints and fingerprint the reviewed bundle.

build_source_resolution_dashboard(root: str | Path, *, lock_path: str | Path | None = None) -> SourceResolutionDashboard

Discover and validate source-resolution checkpoints and an optional reviewed lock.

Supplying lock_path upgrades only the dashboard's governance-integrity statement: the live checkpoint bundle must exactly match the separately frozen reviewed snapshot. It does not itself upgrade source authority, rights, source-audit readiness, or empirical status.

render_source_resolution_dashboard_markdown(dashboard: SourceResolutionDashboard) -> str

Render governance status without conflating it with performance evidence.

discover_source_resolution_paths(root: str | Path) -> tuple[Path, ...]

Discover the complete flat set of source-resolution checkpoints under root.

Discovery is intentionally filename-constrained and strict. Every matching file must be a JSON object with the reviewed v1 record type; malformed or mislabeled candidates fail rather than disappearing from the governance gate.

validate_source_resolution_directory(root: str | Path) -> dict[str, Any]

Discover and validate every committed source-resolution checkpoint in a directory.

build_source_resolution_bundle_lock(protocol_directory: str | Path, *, reviewed_on: str, review_basis: Sequence[str]) -> dict[str, Any]

Build a deterministic lock payload from the currently validated checkpoint bundle.

Building a lock does not upgrade any source-resolution state. The returned payload explicitly records that it is a non-empirical governance snapshot and cannot authorize source audit, empirical evidence, or Frozen Evidence publication.

load_source_resolution_bundle_lock(lock_path: str | Path, protocol_directory: str | Path) -> SourceResolutionBundleLock

Return a typed reviewed lock after exact current-bundle validation.

validate_source_resolution_bundle_lock(lock_path: str | Path, protocol_directory: str | Path) -> dict[str, Any]

Validate one reviewed bundle lock against the complete current checkpoint directory.

summarize_event_predictions_by_stratum(predictions: pd.DataFrame, *, stratify_col: str, label_col: str = 'event_label', model_col: str = 'comparison_model', fold_col: str = 'validation_fold', group_col: str = 'participant_id', sampling_rate_hz: float, calibration_bins: int = 10, include_event_level_metrics: bool = True, event_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> StratifiedEventPerformance

Summarize fixed out-of-fold predictions by a declared analysis stratum.

This function never fits or refits a model. It is intended for post-hoc descriptive validation of predictions that were already generated under a leakage-safe validation design. Fold-level metrics are computed first and then summarized, preserving fold-to-fold variability.

ai_classify_events_context(data: pd.DataFrame, model: TemporalContextModel, *, sampling_rate_hz: float | None = None, min_confidence: float = 0.6, sampling_rate_tolerance: float = 0.1) -> pd.DataFrame

Classify samples using temporal context with probabilities and an abstention threshold.

train_context_event_classifier(data: pd.DataFrame, *, label_col: str = 'event_label', sampling_rate_hz: float | None = None, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), solver: str = 'adam', max_iter: int = 200, random_state: int = 42) -> TemporalContextModel

Fit an MLP to temporal windows without crossing participant/trial boundaries.

This is the first temporal-context baseline, not a claim that an MLP is scientifically superior to I-VT, Random Forest, temporal CNN, or transformer alternatives. Performance must be established under participant- and dataset-held-out validation.

assert_no_group_leakage(train: pd.DataFrame, test: pd.DataFrame, *, group_cols: tuple[str, ...] = ('participant_id',)) -> None

Raise if any protected grouping unit appears in both train and test.

dataset_holdout_context_event_validate(data: pd.DataFrame, *, dataset_col: str = 'dataset_id', participant_col: str = 'participant_id', label_col: str = 'event_label', sampling_rate_hz: float | None = None, min_confidence: float = 0.0, random_state: int = 42, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), solver: str = 'adam', max_iter: int = 200, require_disjoint_participants: bool = True) -> ValidationResult

Evaluate temporal-context events by leaving each dataset out in turn.

dataset_holdout_event_validate(data: pd.DataFrame, *, dataset_col: str = 'dataset_id', participant_col: str = 'participant_id', label_col: str = 'event_label', sampling_rate_hz: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, require_disjoint_participants: bool = True) -> ValidationResult

Leave each dataset out in turn to measure cross-dataset generalisation.

A fresh model is trained for every held-out dataset. When require_disjoint_participants=True, the function refuses a split in which participant IDs occur in both train and test, preventing identity leakage across dataset labels.

grouped_context_event_cross_validate(data: pd.DataFrame, *, label_col: str = 'event_label', group_col: str = 'participant_id', n_splits: int = 5, sampling_rate_hz: float | None = None, min_confidence: float = 0.0, random_state: int = 42, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), solver: str = 'adam', max_iter: int = 200) -> ValidationResult

Evaluate the temporal-context event model with group-held-out folds.

grouped_event_cross_validate(data: pd.DataFrame, *, label_col: str = 'event_label', group_col: str = 'participant_id', n_splits: int = 5, sampling_rate_hz: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200) -> ValidationResult

Evaluate event classification with participant/group-held-out folds.

This function intentionally fits a fresh model inside every fold. It never trains on samples from a group that is present in that fold's test partition.

grouped_holdout_indices(data: pd.DataFrame, *, group_col: str = 'participant_id', test_size: float = 0.2, random_state: int = 42) -> tuple[np.ndarray, np.ndarray]

Return one train/test split with groups strictly isolated between partitions.

run_visus_dynamic_aoi_human_agreement(audit: VisusSourceAuditRun, *, left_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], right_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], timestamps_by_stimulus: Mapping[str, Sequence[float]], left_stream_id: str, right_stream_id: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', include_matches: bool = False) -> VisusDynamicAOIHumanAgreementRun

Measure agreement only between independently verified VISUS AOI streams.

The ordinary single curated VISUS stream is intentionally insufficient. The source audit must first establish separately recoverable independent streams. Metrics are then computed in both reference directions so neither human stream is treated as error-free ground truth.

audit_visus_source(root: str | Path, spec: VisusSourceAuditSpec) -> VisusSourceAuditRun

Verify an exact VISUS snapshot before dynamic-AOI empirical analysis.

The published benchmark describes one manual AOI annotation process involving two human contributors. Contributor count is kept separate from independently recoverable annotation streams, so human-human agreement is enabled only after independent streams are verified.

load_visus_source_audit_spec(path: str | Path) -> VisusSourceAuditSpec

Load a VISUS source-audit specification from JSON.

load_visus_frozen_evidence_bundle(path: str | Path) -> VisusFrozenEvidenceBundle

Return a compact typed record after full bundle validation.

validate_visus_frozen_evidence_bundle(path: str | Path) -> dict[str, Any]

Require both the verified VISUS suite and its raw-execution provenance manifest.

This is a publication-eligibility integrity gate, not a scientific validity decision. It refuses a suite that cannot be tied to the execution-provenance layer introduced for the exact reviewed source-audit JSON, human AOI table, model prediction table, and external timestamp-grid JSON. It does not establish that the underlying source is authoritative or that its reuse terms are correct; those remain source-audit evidence decisions.

prepare_visus_canonical_aoi_intake(audit: VisusSourceAuditRun, table: pd.DataFrame, *, extraction_basis: str, frame_index_base: int, require_complete_manifest_coverage: bool = True) -> VisusCanonicalAOIIntakeRun

Validate a reviewed VISUS AOI extraction and convert frames to canonical keyframes.

This function deliberately does not parse ViPER XML. It accepts a separately extracted, reviewable table and requires every row to link to the exact AOI XML file already verified by VisusSourceAuditRun. Frame-to-time conversion uses the audited published video frame rate and an explicit 0- or 1-based frame convention supplied by the caller.

prepare_visus_dynamic_aoi_predictions(audit: VisusSourceAuditRun, table: pd.DataFrame, *, model_name: str, model_version: str, prediction_basis: str, prediction_coordinate_unit: str, frame_index_base: int, model_artifact_sha256: str | None = None, require_complete_stimulus_coverage: bool = True) -> VisusDynamicAOIPredictionIntakeRun

Canonicalize externally generated VISUS model AOI tracks with audited provenance.

The function expects frame-indexed model detections/tracks. It links every stimulus to the exact audited VISUS video file, converts frame indices with the audited video rate, and returns DynamicAOIKeyframe mappings suitable for model-human validation. It deliberately does not create an evaluation timestamp grid; prediction emission frames must never define that grid.

run_visus_dynamic_aoi_validation_suite(audit: VisusSourceAuditRun, reference_intake: VisusCanonicalAOIIntakeRun, prediction_intake: VisusDynamicAOIPredictionIntakeRun, timestamps_by_stimulus: Mapping[str, Sequence[float]], output_dir: str | Path, *, reference_stream_id: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', human_agreement_streams: tuple[str, str] | None = None, include_matches: bool = False, overwrite: bool = False) -> VisusDynamicAOIValidationSuiteRun

Compute, freeze, cross-check, and bind the audited VISUS validation tranche.

Human-human agreement becomes a required child when the source audit verifies independently recoverable streams. When independence is not verified, the suite explicitly records that the child is unavailable rather than inferring reliability from the published contributor count.

validate_visus_dynamic_aoi_suite_manifest(path: str | Path, *, verify_reports: bool = True) -> dict[str, Any]

Validate a VISUS dynamic-AOI suite manifest and its child reports.

run_visus_dynamic_aoi_model_validation(audit: VisusSourceAuditRun, *, predicted_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], reference_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], timestamps_by_stimulus: Mapping[str, Sequence[float]], reference_stream_id: str, model_name: str, model_version: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', include_matches: bool = False) -> VisusDynamicAOIModelValidationRun

Evaluate one explicit model against one audited VISUS human-reference stream.

Predictions and references use canonical DynamicAOIKeyframe objects. Timestamp grids are supplied explicitly and must cover every audited stimulus, preventing prediction emission times from becoming the evaluation grid. This function orchestrates deterministic model-human evaluation only; it does not parse the historical VISUS XML/video formats or create empirical evidence without a separately verified source audit.

Core modules

Schema

gazeforge.schema

Canonical, vendor-neutral gaze schema.

GazeFrame dataclass

Validated canonical gaze samples plus recording metadata.

copy() -> GazeFrame

Return an independent copy.

canonicalize_gaze(data: pd.DataFrame, *, column_map: Mapping[str, str] | None = None, sampling_rate_hz: float | None = None, screen_size_px: tuple[int, int] | None = None, metadata: Mapping[str, Any] | None = None, sort: bool = True) -> GazeFrame

Convert a table to GazeForge's canonical sample schema.

column_map maps canonical names to source-column names, for example {"timestamp_ms": "TIME", "x_px": "BPOGX", "y_px": "BPOGY"}.

infer_sampling_rate_hz(data: pd.DataFrame, *, timestamp_col: str = 'timestamp_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> float

Infer sampling rate from the median positive within-trial timestamp interval.

Quality control

gazeforge.qc

Auditable quality control and anomaly scoring.

ai_flag_anomalies(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, contamination: float | str = 'auto', random_state: int = 42, trail: AuditTrail | None = None) -> pd.DataFrame

Flag unusual samples with Isolation Forest without deleting or rewriting samples.

detect_calibration_drift(data: pd.DataFrame, *, expected_x_col: str, expected_y_col: str, threshold_px: float = 100.0, group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame

Score drift only when known calibration/reference target coordinates are supplied.

score_trial_quality(data: pd.DataFrame, *, screen_size_px: tuple[int, int] | None = None, group_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame

Summarise missingness, bounds, anomaly rate, and temporal gaps per trial.

Eye events

gazeforge.events

Probabilistic and classical eye-event classification.

EventModel dataclass

A fitted probabilistic event classifier plus compatibility metadata.

ai_classify_events(data: pd.DataFrame, model: EventModel, *, sampling_rate_hz: float | None = None, min_confidence: float = 0.6, sampling_rate_tolerance: float = 0.1) -> pd.DataFrame

Classify samples with probabilities and enforce sampling-rate compatibility.

evaluate_event_predictions(y_true: pd.Series | np.ndarray, y_pred: pd.Series | np.ndarray) -> dict[str, Any]

Return classification metrics and a labelled confusion matrix.

ivt_classify_events(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, velocity_threshold_px_s: float = 1000.0) -> pd.DataFrame

Transparent I-VT-style baseline in pixel coordinates.

ivt_classify_events_angular(data: pd.DataFrame, *, sampling_rate_hz: float | None = None, velocity_threshold_deg_s: float = 45.0) -> pd.DataFrame

Transparent I-VT baseline using geometry-normalized angular velocity.

The default 45 deg/s threshold matches the Lund2013-calibrated maximum fixation velocity reported for the Andersson et al. benchmark. The threshold remains an explicit parameter and should not be treated as universally optimal for every device, task, or population.

train_event_classifier(data: pd.DataFrame, *, label_col: str = 'event_label', sampling_rate_hz: float | None = None, random_state: int = 42, n_estimators: int = 300, rolling_window_ms: float = 80.0) -> EventModel

Fit a probabilistic event model to labelled samples.

This function fits a model; it deliberately does not report validation performance. Scientific evaluation should use participant-held-out and, where applicable, stimulus/dataset-held-out test data.

Temporal event models

gazeforge.temporal

Sampling-rate-aware temporal-context models for eye-event classification.

TemporalContextModel dataclass

A fitted context-window neural event classifier plus compatibility metadata.

ai_classify_events_context(data: pd.DataFrame, model: TemporalContextModel, *, sampling_rate_hz: float | None = None, min_confidence: float = 0.6, sampling_rate_tolerance: float = 0.1) -> pd.DataFrame

Classify samples using temporal context with probabilities and an abstention threshold.

train_context_event_classifier(data: pd.DataFrame, *, label_col: str = 'event_label', sampling_rate_hz: float | None = None, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), solver: str = 'adam', max_iter: int = 200, random_state: int = 42) -> TemporalContextModel

Fit an MLP to temporal windows without crossing participant/trial boundaries.

This is the first temporal-context baseline, not a claim that an MLP is scientifically superior to I-VT, Random Forest, temporal CNN, or transformer alternatives. Performance must be established under participant- and dataset-held-out validation.

Semantic AOIs

gazeforge.aoi

Semantic areas of interest (AOIs), AI providers, and human review.

AOI dataclass

Rectangular semantic area of interest with provenance metadata.

AOIProvider

Bases: Protocol

Protocol implemented by semantic AOI proposal engines.

detect(image: Any, labels: Sequence[str]) -> list[AOI]

Return semantic AOI proposals.

CallableAOIProvider dataclass

Adapter for custom/local detectors used by research teams.

detect(image: Any, labels: Sequence[str]) -> list[AOI]

Run the supplied detector.

HuggingFaceZeroShotAOIProvider dataclass

Optional OWL-ViT zero-shot object detector via Transformers.

detect(image: Any, labels: Sequence[str]) -> list[AOI]

Return open-vocabulary rectangular proposals.

aois_to_frame(aois: Sequence[AOI]) -> pd.DataFrame

Convert AOIs to an ordinary reviewable table.

apply_aoi_review(aois: Sequence[AOI], decisions: pd.DataFrame) -> tuple[list[AOI], pd.DataFrame]

Apply explicit human accept/reject/relabel/rebound decisions.

detect_semantic_aois(image: Any, *, labels: Sequence[str], provider: AOIProvider, min_confidence: float = 0.1) -> list[AOI]

Generate semantic AOI proposals and retain only proposals above a threshold.

map_fixations_to_aois(fixations: pd.DataFrame, aois: Sequence[AOI], *, x_col: str = 'x_px', y_col: str = 'y_px', overlap_rule: str = 'highest_confidence', trail: AuditTrail | None = None) -> pd.DataFrame

Assign each fixation to at most one AOI while preserving unassigned rows.

Dynamic AOIs

gazeforge.dynamic_aoi

Dynamic semantic AOIs for video and moving-interface eye-tracking stimuli.

CallableDynamicAOIProvider dataclass

Adapter for custom local dynamic-AOI detectors and trackers.

track(stimulus: Any, labels: Sequence[str]) -> list[DynamicAOIKeyframe]

Run the supplied detector/tracker.

DynamicAOIKeyframe dataclass

Timestamped rectangular geometry for one semantic AOI track.

DynamicAOIProvider

Bases: Protocol

Protocol implemented by detection/tracking engines for moving AOIs.

track(stimulus: Any, labels: Sequence[str]) -> list[DynamicAOIKeyframe]

Return timestamped AOI keyframes for tracked semantic regions.

detect_dynamic_aois(stimulus: Any, *, labels: Sequence[str], provider: DynamicAOIProvider, min_confidence: float = 0.1) -> list[DynamicAOIKeyframe]

Generate dynamic semantic AOI keyframes with an explicit confidence threshold.

dynamic_aois_from_frame(frame: pd.DataFrame, *, default_source: str = 'manual') -> list[DynamicAOIKeyframe]

Parse a canonical dynamic-AOI keyframe table into validated objects.

Required columns are aoi_id, label, timestamp_ms, xmin, ymin, xmax, and ymax. Optional confidence/provenance columns are preserved when present. Duplicate aoi_id/timestamp pairs are rejected because interpolation would otherwise be ambiguous.

dynamic_aois_to_frame(keyframes: Sequence[DynamicAOIKeyframe]) -> pd.DataFrame

Convert timestamped AOI geometry to a reviewable long-format table.

interpolate_dynamic_aoi(keyframes: Sequence[DynamicAOIKeyframe], timestamp_ms: float, *, max_gap_ms: float = 100.0) -> DynamicAOIKeyframe | None

Return exact/interpolated AOI geometry without temporal extrapolation.

Interpolation is only permitted when the requested timestamp lies between two keyframes and the bracketing interval does not exceed max_gap_ms. Requests outside the observed track range always return None.

map_fixations_to_dynamic_aois(fixations: pd.DataFrame, keyframes: Sequence[DynamicAOIKeyframe], *, timestamp_col: str = 'timestamp_ms', x_col: str = 'x_px', y_col: str = 'y_px', max_interpolation_gap_ms: float = 100.0, overlap_rule: str = 'highest_confidence', trail: AuditTrail | None = None) -> pd.DataFrame

Map timestamped fixations to dynamic AOIs without extrapolating track geometry.

Scanpaths

gazeforge.scanpath

Semantic scanpaths, motifs, learned embeddings, similarity, and clustering.

ScanpathEmbeddingModel dataclass

TF-IDF n-gram encoder with optional learned SVD compression.

cluster_scanpaths_ai(embeddings: pd.DataFrame, *, n_clusters: int, embedding_prefix: str = 'embedding_', random_state: int = 42) -> pd.DataFrame

Cluster learned scanpath embeddings with K-means.

embed_scanpaths(scanpaths: pd.DataFrame, model: ScanpathEmbeddingModel, *, text_col: str = 'sequence_text', id_cols: tuple[str, ...] = ('participant_id', 'trial_id')) -> pd.DataFrame

Transform scanpaths into numeric embeddings.

find_scanpath_motifs(scanpaths: pd.DataFrame, *, sequence_col: str = 'sequence', ngram_range: tuple[int, int] = (2, 3), min_count: int = 2) -> pd.DataFrame

Count recurrent contiguous AOI n-grams.

fit_scanpath_embedder(scanpaths: pd.DataFrame, *, text_col: str = 'sequence_text', n_components: int = 16, ngram_range: tuple[int, int] = (1, 3), random_state: int = 42) -> ScanpathEmbeddingModel

Fit a learned semantic scanpath representation.

scanpath_similarity(embeddings: pd.DataFrame, *, embedding_prefix: str = 'embedding_') -> np.ndarray

Return pairwise cosine similarity for learned embeddings.

to_semantic_scanpaths(fixations: pd.DataFrame, *, label_col: str = 'aoi_label', duration_col: str | None = 'duration_ms', group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), collapse_repeats: bool = True, drop_unassigned: bool = True) -> pd.DataFrame

Convert ordered fixation rows into one semantic sequence per trial.

Validation modules

Candidate source inventories

gazeforge.source_candidate

Exact non-empirical inventories for candidate external benchmark copies.

CandidateSourceFile dataclass

One exact regular file in a candidate external benchmark snapshot.

to_dict() -> dict[str, Any]

Return a deterministic JSON-compatible file record.

CandidateSourceInventory dataclass

Exact local snapshot that deliberately carries no scientific identity inference.

file_count: int property

Number of regular files captured by the snapshot.

to_dict() -> dict[str, Any]

Return the portable non-empirical inventory payload.

build_candidate_source_inventory(root: str | Path, *, dataset_key: str) -> CandidateSourceInventory

Fingerprint a candidate Hollywood2EM or Gaze-in-the-Wild copy without inferring semantics.

The inventory deliberately records only safe relative paths, byte sizes, and SHA-256 digests. File names, directory names, extensions, and apparent structure are not converted into participant, trial, annotator, coordinate, licensing, source-authority, or empirical-evidence claims.

validate_candidate_source_inventory(inventory_path: str | Path, root: str | Path) -> CandidateSourceInventory

Revalidate a saved candidate inventory against the complete current local tree.

write_candidate_source_inventory(inventory: CandidateSourceInventory, path: str | Path, *, overwrite: bool = False) -> Path

Write a portable inventory outside the candidate source tree.

Candidate source review scaffolds

gazeforge.source_candidate_review

Non-empirical review scaffolds bound to exact candidate source inventories.

CandidateSourceReviewFile dataclass

One exact candidate file plus deliberately unresolved scientific review fields.

to_dict() -> dict[str, Any]

Return a deterministic JSON-compatible review row.

CandidateSourceReviewScaffold dataclass

Portable manual-review worksheet tied to one exact candidate copy.

to_dict() -> dict[str, Any]

Return the portable review-scaffold payload.

build_candidate_source_review_scaffold(inventory: CandidateSourceInventory) -> CandidateSourceReviewScaffold

Create an unresolved review worksheet from one exact candidate inventory.

Paths, SHA-256 digests, and byte sizes are copied exactly. No file role, participant, trial, labeller, source-authority, licensing, coordinate, or empirical interpretation is inferred.

validate_candidate_source_review_scaffold(review_path: str | Path, inventory_path: str | Path, root: str | Path) -> CandidateSourceReviewScaffold

Revalidate one manually editable review scaffold against the exact candidate copy.

Scientific review fields may be edited, but exact file path/hash/size identity and the non-empirical scientific boundary cannot change. Included rows must also form a coherent dataset-specific manual mapping. The scaffold never becomes an audit approval.

write_candidate_source_review_scaffold(scaffold: CandidateSourceReviewScaffold, path: str | Path, *, overwrite: bool = False) -> Path

Write a review scaffold outside the candidate tree it describes.

Candidate source audit-template compiler

gazeforge.source_candidate_audit_template

Compile reviewed candidate mappings into deliberately non-empirical audit templates.

compile_candidate_source_audit_template(scaffold: CandidateSourceReviewScaffold) -> AuditTemplateSpec

Compile a reviewed candidate worksheet into an existing audit-spec template type.

The compiler never sets dataset_status='empirical' and never sets any scientific approval boolean to true. Its output is directly loadable by the existing dataset-specific audit-spec loaders, but the empirical audit runners continue to reject it until a separate explicit human authorization step updates the relevant evidence fields.

write_candidate_source_audit_template(spec: AuditTemplateSpec, path: str | Path, *, candidate_root: str | Path, overwrite: bool = False) -> Path

Write one non-empirical audit-spec template outside the candidate source tree.

Candidate source audit authorization

gazeforge.source_candidate_authorization

Explicit human authorization gate between reviewed templates and empirical source audits.

CandidateSourceAuditAuthorization dataclass

Manual decision record bound to one exact non-empirical audit template.

from_dict(payload: dict[str, Any]) -> CandidateSourceAuditAuthorization classmethod

Construct an authorization after record and boundary validation.

to_dict() -> dict[str, Any]

Return a deterministic JSON-compatible authorization record.

authorize_candidate_source_audit_template(spec: AuditTemplateSpec, authorization: CandidateSourceAuditAuthorization, *, gaze_in_wild_quarantine_exit: GazeInWildQuarantineExitAuthorization | None = None) -> AuditTemplateSpec

Materialize an empirical audit spec from separately reviewed authorization decisions.

This function authorizes execution of the existing source audit. It does not execute that audit, does not verify the local data copy, and does not create agreement or model evidence. Recovered Gaze-in-the-Wild candidates additionally require a separately reviewed quarantine- exit authorization bound to the exact same audit template.

build_candidate_source_audit_authorization(spec: AuditTemplateSpec) -> CandidateSourceAuditAuthorization

Create a pending manual authorization record bound to one exact audit template.

load_candidate_source_audit_authorization(path: str | Path) -> CandidateSourceAuditAuthorization

Load one manual source-audit authorization JSON record.

source_audit_template_fingerprint(spec: AuditTemplateSpec) -> str

Fingerprint one complete source-audit template deterministically.

validate_candidate_source_audit_authorization(authorization_path: str | Path, spec: AuditTemplateSpec) -> CandidateSourceAuditAuthorization

Validate one manual decision against the exact template it reviews.

write_authorized_source_audit_spec(spec: AuditTemplateSpec, path: str | Path, *, candidate_root: str | Path, overwrite: bool = False) -> Path

Write an already-authorized empirical source-audit spec outside the candidate tree.

write_candidate_source_audit_authorization(authorization: CandidateSourceAuditAuthorization, path: str | Path, *, candidate_root: str | Path, overwrite: bool = False) -> Path

Write an authorization record outside the candidate source tree.

Source-audit lineage receipts

gazeforge.source_audit_lineage

Cryptographic lineage receipts for authorized source-audit reports.

SourceAuditLineageReceipt dataclass

Verified chain from reviewed template through authorization to source-audit report.

from_dict(payload: Mapping[str, Any]) -> SourceAuditLineageReceipt classmethod

Load a receipt only after validating its own fingerprint and boundary.

to_dict() -> dict[str, Any]

Return a deterministic JSON-compatible receipt.

build_source_audit_lineage_receipt(template_spec: AuditTemplateSpec, authorization: CandidateSourceAuditAuthorization, audit_report: Mapping[str, Any], *, gaze_in_wild_quarantine_exit: GazeInWildQuarantineExitAuthorization | None = None) -> SourceAuditLineageReceipt

Verify and bind the full reviewed-template → authorization → source-audit chain.

The function recomputes the authorized empirical specification deterministically from the original template and authorization, then requires the audit report to fingerprint that exact specification and to pass dataset-specific source-audit invariants. Recovered Gaze-in-the-Wild lineage also carries the separately reviewed quarantine-exit fingerprint. It creates no new model, agreement, AOI, native-GP3, or Frozen Evidence result.

load_source_audit_lineage_receipt(path: str | Path) -> SourceAuditLineageReceipt

Load and self-validate a saved source-audit lineage receipt.

write_source_audit_lineage_receipt(receipt: SourceAuditLineageReceipt, path: str | Path, *, candidate_root: str | Path, overwrite: bool = False) -> Path

Write one verified lineage receipt outside the candidate source tree.

Downstream source-audit lineage gates

gazeforge.downstream_lineage

Fail-closed lineage gates for downstream benchmark preparation and validation.

validate_gaze_in_wild_audit_lineage(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt) -> str

Bind a Gaze-in-the-Wild audit run to its exact lineage receipt.

validate_hollywood2_gaze_lineage(gaze: GazeFrame, lineage: SourceAuditLineageReceipt) -> str

Bind an audited Hollywood2EM GazeFrame to its exact lineage receipt.

validate_source_audit_lineage_binding(lineage: SourceAuditLineageReceipt, *, dataset_key: str, audit_report_fingerprint_sha256: str, authorized_spec_fingerprint_sha256: str, source_manifest_fingerprints_sha256: Mapping[str, Any], source_revision: str) -> str

Require one lineage receipt to match the exact audited source being consumed.

This gate does not create or upgrade empirical evidence. It only prevents a downstream benchmark from accepting a verified source audit that has been detached from the exact lineage receipt created for that audit.

Source-resolution checkpoints

gazeforge.source_resolution

Unified validation for benchmark source-resolution checkpoints.

SourceResolutionRecord dataclass

Compact common identity for one validated benchmark source-resolution checkpoint.

load_source_resolution_record(path: str | Path) -> SourceResolutionRecord

Return a typed common view after dataset-specific source-resolution validation.

validate_gaze_in_wild_source_resolution_record(path: str | Path) -> dict[str, Any]

Validate the conservative Gaze-in-the-Wild source-resolution checkpoint.

validate_hollywood2_source_resolution_record(path: str | Path) -> dict[str, Any]

Validate the reviewed Hollywood2EM recovered-source checkpoint.

validate_source_resolution_record(path: str | Path) -> dict[str, Any]

Validate a known v1 source-resolution checkpoint and auto-dispatch by dataset.

validate_source_resolution_records(paths: Sequence[str | Path]) -> dict[str, Any]

Validate source-resolution checkpoints and fingerprint the reviewed bundle.

Source-resolution discovery

gazeforge.source_resolution_discovery

Discovery of committed source-resolution checkpoints for governance validation.

discover_source_resolution_paths(root: str | Path) -> tuple[Path, ...]

Discover the complete flat set of source-resolution checkpoints under root.

Discovery is intentionally filename-constrained and strict. Every matching file must be a JSON object with the reviewed v1 record type; malformed or mislabeled candidates fail rather than disappearing from the governance gate.

validate_source_resolution_directory(root: str | Path) -> dict[str, Any]

Discover and validate every committed source-resolution checkpoint in a directory.

Source-resolution status dashboard

gazeforge.source_resolution_dashboard

Public governance dashboard for benchmark source-resolution checkpoints.

SourceResolutionDashboard dataclass

Integrity-checked source-resolution records prepared for public status reporting.

build_source_resolution_dashboard(root: str | Path, *, lock_path: str | Path | None = None) -> SourceResolutionDashboard

Discover and validate source-resolution checkpoints and an optional reviewed lock.

Supplying lock_path upgrades only the dashboard's governance-integrity statement: the live checkpoint bundle must exactly match the separately frozen reviewed snapshot. It does not itself upgrade source authority, rights, source-audit readiness, or empirical status.

render_source_resolution_dashboard_markdown(dashboard: SourceResolutionDashboard) -> str

Render governance status without conflating it with performance evidence.

Source-resolution reviewed bundle lock

gazeforge.source_resolution_lock

Reviewed snapshot locks for non-empirical source-resolution governance bundles.

SourceResolutionBundleLock dataclass

Typed identity of a validated reviewed source-resolution snapshot.

build_source_resolution_bundle_lock(protocol_directory: str | Path, *, reviewed_on: str, review_basis: Sequence[str]) -> dict[str, Any]

Build a deterministic lock payload from the currently validated checkpoint bundle.

Building a lock does not upgrade any source-resolution state. The returned payload explicitly records that it is a non-empirical governance snapshot and cannot authorize source audit, empirical evidence, or Frozen Evidence publication.

load_source_resolution_bundle_lock(lock_path: str | Path, protocol_directory: str | Path) -> SourceResolutionBundleLock

Return a typed reviewed lock after exact current-bundle validation.

validate_source_resolution_bundle_lock(lock_path: str | Path, protocol_directory: str | Path) -> dict[str, Any]

Validate one reviewed bundle lock against the complete current checkpoint directory.

Model comparison

gazeforge.comparison

Leakage-safe comparison of classical and learned eye-event classifiers.

EventModelComparison dataclass

Predictions, fold metrics, and aggregate summaries for matched validation folds.

compare_event_models_grouped(data: pd.DataFrame, *, label_col: str = 'event_label', group_col: str = 'participant_id', n_splits: int = 5, sampling_rate_hz: float | None = None, ivt_velocity_threshold_px_s: float | None = 1000.0, ivt_velocity_threshold_deg_s: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, include_event_level_metrics: bool = True, event_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> EventModelComparison

Compare I-VT, Random Forest, and temporal MLP on identical group-held-out folds.

Each learned model is fitted from scratch within every fold. All three methods are evaluated on the exact same test rows. Calibration metrics are reported only for probabilistic learned models; deterministic I-VT receives missing calibration values rather than fabricated scores.

Matched-fold model differences

gazeforge.paired

Matched-fold descriptive differences for event-model validation metrics.

PairedModelDifferences dataclass

Per-fold paired deltas plus descriptive summaries for every model pair.

paired_model_metric_differences(fold_metrics: pd.DataFrame, *, model_col: str = 'model', fold_col: str = 'fold', metrics: tuple[str, ...] | None = None, tie_tolerance: float = 1e-12) -> PairedModelDifferences

Compare model metrics on exactly matched folds without inferential p-values.

Raw deltas are always model_a - model_b. improvement_for_a multiplies the raw delta by the registered metric direction so positive values always mean model A performed better. The function is deliberately descriptive: cross-validation folds share training data and are not treated as independent replicates for hypothesis tests or confidence intervals.

Event-level evaluation

gazeforge.event_evaluation

Event-level eye-movement segmentation and temporal matching metrics.

EventLevelEvaluation dataclass

Event intervals, one-to-one matches, and aggregate/per-class metrics.

evaluate_event_intervals(predicted: pd.DataFrame, reference: pd.DataFrame, *, group_cols: Sequence[str] = ('participant_id', 'trial_id'), label_col: str = 'event_label', min_iou: float = 0.5, require_label_match: bool = True) -> EventLevelEvaluation

Evaluate event detection/classification with one-to-one temporal matching.

evaluate_sample_event_predictions(data: pd.DataFrame, *, true_label_col: str = 'event_label', predicted_label_col: str = 'predicted_event', timestamp_col: str = 'timestamp_ms', group_cols: Sequence[str] = ('participant_id', 'trial_id'), sampling_rate_hz: float, excluded_labels: Sequence[str] = _DEFAULT_EXCLUDED_LABELS, max_gap_factor: float = 1.5, min_iou: float = 0.5, require_label_match: bool = True) -> EventLevelEvaluation

Segment sample-level truth/predictions and evaluate them at event level.

match_event_intervals(predicted: pd.DataFrame, reference: pd.DataFrame, *, group_cols: Sequence[str] = ('participant_id', 'trial_id'), label_col: str = 'event_label', min_iou: float = 0.5, require_label_match: bool = True) -> pd.DataFrame

One-to-one match predicted events to references within each participant/trial group.

The maximum-total-IoU Hungarian objective is unchanged. Because each validated event stream is internally non-overlapping, only positive temporal-overlap edges can influence that objective. The positive-edge graph is decomposed into independent components before assignment, avoiding a corpus-scale dense Cartesian IoU matrix without changing accepted matches.

samples_to_event_intervals(data: pd.DataFrame, *, label_col: str = 'event_label', timestamp_col: str = 'timestamp_ms', group_cols: Sequence[str] = ('participant_id', 'trial_id'), sampling_rate_hz: float, excluded_labels: Sequence[str] = _DEFAULT_EXCLUDED_LABELS, max_gap_factor: float = 1.5) -> pd.DataFrame

Convert sample labels to contiguous half-open event intervals.

Segmentation occurs before excluded labels are removed, so an ambiguous/undefined run remains a hard separator between two otherwise identical event labels. A timestamp gap larger than max_gap_factor nominal sample periods also starts a new event.

temporal_event_iou(predicted_start_ms: float, predicted_end_ms: float, reference_start_ms: float, reference_end_ms: float) -> float

Return temporal intersection-over-union for two half-open event intervals.

Stratified out-of-fold performance

gazeforge.stratified

Post-hoc stratified metrics for leakage-safe out-of-fold event predictions.

StratifiedEventPerformance dataclass

Fold-wise and aggregate metrics computed from fixed out-of-fold predictions.

summarize_event_predictions_by_stratum(predictions: pd.DataFrame, *, stratify_col: str, label_col: str = 'event_label', model_col: str = 'comparison_model', fold_col: str = 'validation_fold', group_col: str = 'participant_id', sampling_rate_hz: float, calibration_bins: int = 10, include_event_level_metrics: bool = True, event_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> StratifiedEventPerformance

Summarize fixed out-of-fold predictions by a declared analysis stratum.

This function never fits or refits a model. It is intended for post-hoc descriptive validation of predictions that were already generated under a leakage-safe validation design. Fold-level metrics are computed first and then summarized, preserving fold-to-fold variability.

Sampling sensitivity

gazeforge.sampling_sensitivity

Sampling-rate and annotation-boundary sensitivity for labelled gaze benchmarks.

SamplingSensitivityResult dataclass

Complete settings ledger and model metrics for a resampling sensitivity surface.

evaluate_sampling_purity_sensitivity(data: pd.DataFrame, *, target_sampling_rates_hz: Sequence[float] = (120.0, 90.0, 60.0, 30.0), min_label_purities: Sequence[float] = (0.6, 0.75, 0.9), source_sampling_rate_hz: float | None = None, label_col: str = 'event_label', group_col: str = 'participant_id', resampling_group_cols: tuple[str, ...] = ('participant_id', 'trial_id'), ambiguous_label: str = 'ambiguous', excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, n_splits: int = 5, ivt_velocity_threshold_px_s: float | None = 1000.0, ivt_velocity_threshold_deg_s: float | None = None, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, max_interpolation_gap_ms: float | None = None) -> SamplingSensitivityResult

Evaluate model sensitivity to target sampling rate and boundary-label purity.

Every rate/purity condition is retained in settings. Ambiguous and other excluded labels are removed only after their prevalence has been recorded, matching the primary Lund benchmark policy. Conditions that no longer contain enough groups or labels for the requested validation design are recorded as not_evaluable instead of being silently dropped.

Lund sensitivity workflow

gazeforge.lund_sensitivity

Lund2013 sampling-rate and annotation-boundary sensitivity workflow.

Lund2013SensitivityRun dataclass

Sensitivity surface, dataset evidence card, and deterministic benchmark report.

run_lund2013_sampling_sensitivity(root: str | Path, *, annotator: str = 'RA', target_sampling_rates_hz: tuple[float, ...] = (120.0, 90.0, 60.0, 30.0), min_label_purities: tuple[float, ...] = (0.6, 0.75, 0.9), n_splits: int = 5, ivt_velocity_threshold_deg_s: float = 45.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, max_interpolation_gap_ms: float | None = None) -> Lund2013SensitivityRun

Evaluate Lund2013 across lower sampling rates and label-purity thresholds.

The workflow uses one expert annotation stream at a time, records ambiguity before exclusions, and applies the same default label policy as the primary Lund benchmark. The angular I-VT baseline remains fixed at the supplied degrees/second threshold across the sensitivity surface. A GazeForge source manifest, when present, is revalidated with its referenced files before the sensitivity analysis begins and its verified identity is stored in the report protocol.

Cross-dataset validation

gazeforge.cross_dataset

Cross-dataset eye-event benchmark preparation and validation.

CrossDatasetEventPrepared dataclass

Harmonised lower-rate data plus dataset-level preparation reports.

CrossDatasetEventValidation dataclass

Leave-one-dataset-out results for the two learned GazeForge baselines.

prepare_cross_dataset_event_benchmark(datasets: Mapping[str, GazeFrame], *, source_audit_lineages: Mapping[str, SourceAuditLineageReceipt] | None = None, target_sampling_rate_hz: float = 60.0, common_labels: Sequence[str] = ('fixation', 'saccade', 'pursuit'), min_label_purity: float = 0.75, ambiguous_label: str = 'ambiguous', require_resolved_participants: bool = True, require_verified_coordinates: bool = True, require_source_audits: bool = True, require_all_common_labels: bool = True) -> CrossDatasetEventPrepared

Prepare multiple human-reference corpora for matched lower-rate validation.

Each source is independently resampled to the requested rate using the benchmark resampling guardrails. Participant and trial identifiers are namespaced by dataset after source identities have been checked, preventing accidental collisions across independently collected corpora. External sources with a reviewed source-audit contract must also supply the matching lineage receipt before their data may enter cross-dataset modelling.

run_cross_dataset_event_validation(prepared: CrossDatasetEventPrepared, *, label_col: str = 'event_label', min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5, event_excluded_labels: tuple[str, ...] = ('ambiguous', 'unlabelled', 'undefined', 'abstain')) -> CrossDatasetEventValidation

Run RF and temporal-context MLP in a leave-one-dataset-out design.

Benchmark adapters

Lund2013

gazeforge.lund2013

Adapter for the manually annotated Lund2013 eye-movement benchmark files.

load_lund2013_directory(root: str | Path, *, annotator: str = 'RA', recursive: bool = True) -> GazeFrame

Load and concatenate Lund2013 files for one annotator from a benchmark directory.

load_lund2013_mat(path: str | Path, *, participant_id: str | None = None, trial_id: str | None = None, annotator: str | None = None, stimulus_type: str | None = None, zero_pair_is_missing: bool = True) -> GazeFrame

Load one annotated Lund2013 MATLAB file into GazeForge's canonical gaze schema.

The public benchmark stores x/y coordinates in columns 4/5 of MATLAB's one-based pos matrix and human event codes in column 6. Codes follow the original benchmark convention: fixation=1, saccade=2, PSO=3, pursuit=4, blink=5, undefined=6; code 0 is retained as unlabelled rather than silently discarded.

Hollywood2EM

gazeforge.hollywood2

Adapter for the manually annotated Hollywood2 eye-movement benchmark.

load_hollywood2_arff(path: str | Path, *, annotator: str = 'final', label_col: str | None = None, participant_id: str | None = None, trial_id: str | None = None, split: str | None = None, expected_sampling_rate_hz: float | None = 500.0, sampling_rate_tolerance: float = 0.05, confidence_threshold: float = 0.5, zero_pair_is_missing: bool = True, coordinate_unit: str = 'unverified') -> GazeFrame

Load one Hollywood2EM hand-labelled ARFF recording.

The published/TUM evaluation convention stores time in microseconds and uses x, y, and confidence for the gaze samples. handlabeller_1 contains the first/student coding pass and handlabeller_final contains the expert-corrected labels. Participant identity is deliberately not guessed from filenames; callers should supply it when participant-held-out validation is intended.

load_hollywood2_directory(root: str | Path, *, annotator: str = 'final', identity_parser: IdentityParser | None = None, expected_sampling_rate_hz: float | None = 500.0, sampling_rate_tolerance: float = 0.05, coordinate_unit: str = 'unverified') -> GazeFrame

Load a Hollywood2EM ground-truth tree without guessing participant identities.

identity_parser receives each ARFF path relative to the selected ground-truth directory and must return (participant_id, trial_id). Without it, every row receives the sentinel __unresolved__ participant ID; this intentionally prevents accidental participant-held-out validation until the repository-specific identity mapping has been supplied and audited.

Hollywood2EM authoritative evidence

gazeforge.hollywood2_evidence

Validation for the pinned authoritative Hollywood2EM ground-truth evidence.

The evidence binds the canonical GIN repository to an exact revision and to all 697 hand-labelled ground-truth ARFF blobs. It also freezes the observed student-to-expert-corrected annotation sensitivity while explicitly refusing to reinterpret the sequential correction workflow as independent human-human agreement.

Hollywood2AuthoritativeEvidence dataclass

Compact identity of the validated Hollywood2EM evidence record.

evidence_fingerprint(record: Mapping[str, Any]) -> str

Return the evidence SHA-256 excluding its self-fingerprint field.

load_hollywood2_authoritative_evidence(path: str | Path) -> Hollywood2AuthoritativeEvidence

Load and validate the evidence, returning a compact typed identity.

validate_hollywood2_authoritative_evidence(record_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]

Validate the immutable Hollywood2 authoritative ground-truth v1 record.

validate_hollywood2_gin_probe(probe_or_path: Mapping[str, Any] | str | Path, evidence_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]

Bind a freshly generated canonical GIN probe to the frozen evidence.

Hollywood2EM source audit

gazeforge.hollywood2_audit

Auditable source-manifest verification for Hollywood2EM empirical use.

Hollywood2SourceAuditRun dataclass

Verified source audit plus both human annotation streams.

Hollywood2SourceAuditSpec dataclass

Evidence contract required before Hollywood2EM is used in frozen modelling.

__post_init__() -> None

Keep templates non-empirical and make empirical audits fully explicit.

from_dict(payload: Mapping[str, Any]) -> Hollywood2SourceAuditSpec classmethod

Construct a source-audit specification from decoded JSON.

to_dict() -> dict[str, Any]

Return a deterministic JSON-compatible specification mapping.

Hollywood2SourceFileRecord dataclass

One audited Hollywood2EM ARFF file and its participant/trial identity.

__post_init__() -> None

Reject unsafe paths, weak digests, and unresolved identities.

from_dict(payload: Mapping[str, Any]) -> Hollywood2SourceFileRecord classmethod

Construct one record from decoded JSON.

to_dict() -> dict[str, Any]

Return a deterministic JSON-compatible record.

audit_hollywood2_source(root: str | Path, spec: Hollywood2SourceAuditSpec) -> Hollywood2SourceAuditRun

Verify an authoritative local Hollywood2EM copy before empirical modelling.

The audit is intentionally non-statistical: it verifies exact file identity, reuse/analysis declarations, participant/trial mapping, coordinate-unit evidence, native sampling rate, and that the student and expert labels refer to the same underlying gaze samples. It does not produce model-performance metrics and it does not imply raw-data redistribution permission.

load_audited_hollywood2_directory(root: str | Path, spec: Hollywood2SourceAuditSpec, *, annotator: str = 'final') -> GazeFrame

Return one Hollywood2 annotation stream only after the full source audit passes.

load_hollywood2_source_audit_spec(path: str | Path) -> Hollywood2SourceAuditSpec

Load a Hollywood2 source-audit specification from JSON.

Gaze-in-the-Wild

gazeforge.gaze_in_wild

Adapter for the manually annotated Gaze-in-the-Wild event benchmark.

load_gaze_in_wild_directory(label_root: str | Path, *, process_root: str | Path | None = None, participant_parser: Callable[[Path], str | None] | None = None, labeller: int | None = None, recursive: bool = True, confidence_threshold: float = 0.3) -> GazeFrame

Load a directory of Gaze-in-the-Wild annotation files without guessing identities.

load_gaze_in_wild_mat(label_path: str | Path, *, process_path: str | Path | None = None, participant_id: str | None = None, trial_id: str | None = None, confidence_threshold: float = 0.3) -> GazeFrame

Load one manually annotated Gaze-in-the-Wild recording.

Sampling rate is inferred from LabelData.T rather than hard-coded. The published eye-tracker acquisition rate (120 Hz) and the official processed-stream target cadence (300 Hz) are retained as distinct provenance. When ProcessData is supplied, the official processing schema defines ETG.POR as normalized scene-camera coordinates whose y axis has already been flipped to MATLAB image convention; those coordinates are converted to canonical pixels using ETG.SceneResolution.

Gaze-in-the-Wild source audit

gazeforge.gaze_in_wild_audit

Auditable source verification for Gaze-in-the-Wild empirical evidence.

GazeInWildAuditedFile dataclass

One verified label/process pair with its loaded gaze stream.

GazeInWildLabelFileRecord dataclass

One audited human-labelled MATLAB stream and its identity mapping.

GazeInWildProcessFileRecord dataclass

One audited ProcessData MATLAB file.

GazeInWildSourceAuditRun dataclass

Verified source audit and all audited per-labeller streams.

GazeInWildSourceAuditSpec dataclass

Evidence contract required before Gaze-in-the-Wild results are frozen.

audit_gaze_in_wild_source(label_root: str | Path, process_root: str | Path, spec: GazeInWildSourceAuditSpec) -> GazeInWildSourceAuditRun

Verify an authoritative local Gaze-in-the-Wild copy before empirical reporting.

The audit binds exact label/process files to participant, trial, and labeller identities; checks current reuse declarations and coordinate evidence; infers native-file cadence from timestamps; and verifies that different labellers for the same trial reference identical underlying gaze. It produces no model-performance metrics and makes no raw-data redistribution claim.

audited_gaze_in_wild_files_by_labeller(run: GazeInWildSourceAuditRun) -> dict[int, list[GazeInWildAuditedFile]]

Group an already verified audit run by human labeller without merging file cadences.

gaze_in_wild_sampling_rate_table(run: GazeInWildSourceAuditRun) -> pd.DataFrame

Return the audited per-file timestamp-inferred sampling-rate ledger.

load_gaze_in_wild_source_audit_spec(path: str | Path) -> GazeInWildSourceAuditSpec

Load a Gaze-in-the-Wild source-audit specification from JSON.

Gaze-in-the-Wild labeller agreement

gazeforge.gaze_in_wild_agreement

Rate-aware human-human agreement for audited Gaze-in-the-Wild label streams.

GazeInWildLabellerAgreementRun dataclass

Aligned audited streams and pooled rate-aware human-human agreement evidence.

run_gaze_in_wild_labeller_agreement(audit: GazeInWildSourceAuditRun, *, left_labeller: int, right_labeller: int, excluded_labels: tuple[str, ...] = ('unlabelled',), exclude_invalid_tracking: bool = True, event_min_iou: float = 0.5, require_complete_overlap: bool = True) -> GazeInWildLabellerAgreementRun

Compare two human labellers only after the Gaze-in-the-Wild source audit passes.

Each shared participant/trial is segmented at its own timestamp-inferred sampling rate before event intervals are pooled in milliseconds. This avoids inventing one nominal cadence for a distributed snapshot whose files may differ. Invalid tracking samples can be retained as hard event separators while being excluded from analysis-label agreement.

Gaze-in-the-Wild model validation

gazeforge.gaze_in_wild_validation

Audited participant-held-out model validation for Gaze-in-the-Wild.

GazeInWildModelValidationRun dataclass

Prepared data, participant-held-out comparisons, sensitivities, and report.

GazeInWildPreparedBenchmark dataclass

One audited labeller prepared at a common analysis cadence.

prepare_gaze_in_wild_benchmark(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt, *, labeller_id: int, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, max_coordinate_gap_factor: float = 1.5, task_mapping: pd.DataFrame | None = None, task_col: str = 'task_label') -> GazeInWildPreparedBenchmark

Prepare one lineage-bound audited human labeller for event-model validation.

run_gaze_in_wild_model_validation(audit: GazeInWildSourceAuditRun, lineage: SourceAuditLineageReceipt, *, labeller_id: int, target_sampling_rate_hz: float = 60.0, min_label_purity: float = 0.75, excluded_labels: tuple[str, ...] = _DEFAULT_EXCLUDED_LABELS, max_coordinate_gap_factor: float = 1.5, task_mapping: pd.DataFrame | None = None, task_col: str = 'task_label', n_splits: int = 5, ivt_velocity_threshold_px_s: float = 1000.0, min_confidence: float = 0.0, random_state: int = 42, n_estimators: int = 200, context_radius_ms: float = 50.0, rolling_window_ms: float = 80.0, hidden_layer_sizes: tuple[int, ...] = (64, 32), temporal_solver: str = 'adam', temporal_max_iter: int = 200, calibration_bins: int = 10, event_min_iou: float = 0.5) -> GazeInWildModelValidationRun

Run lineage-bound participant-disjoint validation on audited Gaze-in-the-Wild.

VISUS public partial 60 Hz evidence

gazeforge.visus_public_partial

Validation for the pinned public VISUS-derived 60 Hz partial evidence record.

This module is deliberately separate from :mod:gazeforge.visus_evidence. The original VISUS Frozen Evidence gate remains reserved for a fully audited 25-participant by 11-stimulus source. This record certifies only the exact public derivative files and metrics recovered from the pinned VISUS-supervised eye-slitscan repository.

VisusPublicPartialEvidence dataclass

Compact identity of the validated public VISUS-derived partial evidence.

evidence_fingerprint(record: Mapping[str, Any]) -> str

Return the SHA-256 fingerprint of an evidence record excluding its fingerprint field.

load_visus_public_partial_evidence(path: str | Path) -> VisusPublicPartialEvidence

Load the committed v1 record and return a compact validated identity.

validate_visus_public_partial_evidence(record_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]

Validate the immutable v1 public VISUS-derived partial empirical record.

The validator certifies only the exact three public Tobii recordings and one dynamic-AOI stimulus recovered at the pinned upstream commit. It explicitly does not confer full VISUS source-audit status, dataset redistribution rights, human-human agreement, model validation, Frozen Evidence eligibility, or native Gazepoint GP3 status.

validate_visus_public_partial_probe(probe_or_path: Mapping[str, Any] | str | Path, evidence_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]

Bind a live exact-source probe to the immutable committed v1 evidence record.

VISUS public 60 Hz event-extension evidence

gazeforge.visus_public_event_extension

Validation for the pinned public VISUS-derived 60 Hz event-extension evidence.

This module certifies two complete public Tobii exports recovered from the VISUS-supervised eye-slitscan repository. Participant identity and 60 Hz event metrics are file-bound; the likely 03-dialog stimulus identity is explicitly retained as an inference rather than promoted to source resolution.

VisusPublicEventExtensionEvidence dataclass

Compact identity of the validated VISUS public event-extension evidence.

evidence_fingerprint(record: Mapping[str, Any]) -> str

Return the SHA-256 fingerprint excluding the self-fingerprint field.

load_visus_public_event_extension_evidence(record_or_path: Mapping[str, Any] | str | Path) -> VisusPublicEventExtensionEvidence

Validate and return a compact typed identity for the frozen evidence.

validate_visus_public_event_extension_evidence(record_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]

Validate the immutable v1 VISUS public 60 Hz event-extension record.

validate_visus_public_event_extension_probe(probe_or_path: Mapping[str, Any] | str | Path, evidence_or_path: Mapping[str, Any] | str | Path) -> dict[str, Any]

Bind a freshly generated upstream probe to the immutable evidence record.

VISUS source inventory scaffold

gazeforge.visus_scaffold

Non-empirical source-inventory scaffolding for prospective VISUS audit copies.

VisusSourceAuditScaffold dataclass

Exact local inventory paired with a deliberately non-empirical audit specification.

file_count: int property

Number of inventoried regular files.

build_visus_source_audit_scaffold(root: str | Path) -> VisusSourceAuditScaffold

Inventory a candidate local VISUS tree without inferring scientific identities.

Every file is intentionally emitted with role='other' and without stimulus, participant, or annotation-stream identities. The returned specification remains dataset_status='template' with all empirical evidence gates unresolved. A researcher must manually review and edit those fields before :func:gazeforge.audit_visus_source can accept the copy as empirical evidence.

write_visus_source_audit_scaffold(scaffold: VisusSourceAuditScaffold, path: str | Path, *, overwrite: bool = False) -> Path

Write one loadable template audit-spec JSON outside the inventoried source tree.

VISUS source audit

gazeforge.visus_audit

Authoritative-source audit contract for the VISUS dynamic-AOI benchmark.

VisusAuditedFile dataclass

One verified local file paired with its manifest record.

VisusSourceAuditRun dataclass

Verified VISUS snapshot and deterministic source-audit report.

VisusSourceAuditSpec dataclass

Reviewed provenance and exact-file contract for a VISUS dataset copy.

to_dict() -> dict[str, Any]

Return a deterministic JSON-ready representation.

VisusSourceFileRecord dataclass

One exact file identity in an audited VISUS snapshot.

audit_visus_source(root: str | Path, spec: VisusSourceAuditSpec) -> VisusSourceAuditRun

Verify an exact VISUS snapshot before dynamic-AOI empirical analysis.

The published benchmark describes one manual AOI annotation process involving two human contributors. Contributor count is kept separate from independently recoverable annotation streams, so human-human agreement is enabled only after independent streams are verified.

load_visus_source_audit_spec(path: str | Path) -> VisusSourceAuditSpec

Load a VISUS source-audit specification from JSON.

VISUS canonical AOI intake

gazeforge.visus_intake

Audited canonical AOI intake for VISUS without guessing the raw XML schema.

VisusCanonicalAOIIntakeRun dataclass

Canonical VISUS AOIs linked back to an exact audited source snapshot.

prepare_visus_canonical_aoi_intake(audit: VisusSourceAuditRun, table: pd.DataFrame, *, extraction_basis: str, frame_index_base: int, require_complete_manifest_coverage: bool = True) -> VisusCanonicalAOIIntakeRun

Validate a reviewed VISUS AOI extraction and convert frames to canonical keyframes.

This function deliberately does not parse ViPER XML. It accepts a separately extracted, reviewable table and requires every row to link to the exact AOI XML file already verified by VisusSourceAuditRun. Frame-to-time conversion uses the audited published video frame rate and an explicit 0- or 1-based frame convention supplied by the caller.

VISUS model prediction intake

gazeforge.visus_prediction

Audited model-prediction intake for VISUS dynamic AOI validation.

VisusDynamicAOIPredictionIntakeRun dataclass

Canonical model AOI predictions linked to an audited VISUS video snapshot.

prepare_visus_dynamic_aoi_predictions(audit: VisusSourceAuditRun, table: pd.DataFrame, *, model_name: str, model_version: str, prediction_basis: str, prediction_coordinate_unit: str, frame_index_base: int, model_artifact_sha256: str | None = None, require_complete_stimulus_coverage: bool = True) -> VisusDynamicAOIPredictionIntakeRun

Canonicalize externally generated VISUS model AOI tracks with audited provenance.

The function expects frame-indexed model detections/tracks. It links every stimulus to the exact audited VISUS video file, converts frame indices with the audited video rate, and returns DynamicAOIKeyframe mappings suitable for model-human validation. It deliberately does not create an evaluation timestamp grid; prediction emission frames must never define that grid.

VISUS model-human validation

gazeforge.visus_validation

Source-audit-aware model-human dynamic-AOI validation for VISUS.

VisusDynamicAOIModelValidationRun dataclass

Per-stimulus model-human evaluations and a deterministic benchmark report.

run_visus_dynamic_aoi_model_validation(audit: VisusSourceAuditRun, *, predicted_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], reference_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], timestamps_by_stimulus: Mapping[str, Sequence[float]], reference_stream_id: str, model_name: str, model_version: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', include_matches: bool = False) -> VisusDynamicAOIModelValidationRun

Evaluate one explicit model against one audited VISUS human-reference stream.

Predictions and references use canonical DynamicAOIKeyframe objects. Timestamp grids are supplied explicitly and must cover every audited stimulus, preventing prediction emission times from becoming the evaluation grid. This function orchestrates deterministic model-human evaluation only; it does not parse the historical VISUS XML/video formats or create empirical evidence without a separately verified source audit.

VISUS human-human agreement

gazeforge.visus_agreement

Conditional human-human dynamic-AOI agreement for audited VISUS sources.

VisusDynamicAOIHumanAgreementRun dataclass

Bidirectional dynamic-AOI agreement between two verified independent streams.

run_visus_dynamic_aoi_human_agreement(audit: VisusSourceAuditRun, *, left_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], right_by_stimulus: Mapping[str, Sequence[DynamicAOIKeyframe]], timestamps_by_stimulus: Mapping[str, Sequence[float]], left_stream_id: str, right_stream_id: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', include_matches: bool = False) -> VisusDynamicAOIHumanAgreementRun

Measure agreement only between independently verified VISUS AOI streams.

The ordinary single curated VISUS stream is intentionally insufficient. The source audit must first establish separately recoverable independent streams. Metrics are then computed in both reference directions so neither human stream is treated as error-free ground truth.

VISUS validation suite

gazeforge.visus_suite

Atomic orchestration and verification for audited VISUS dynamic-AOI validation.

VisusDynamicAOIValidationSuiteRun dataclass

Frozen VISUS provenance/validation reports plus a completion manifest.

run_visus_dynamic_aoi_validation_suite(audit: VisusSourceAuditRun, reference_intake: VisusCanonicalAOIIntakeRun, prediction_intake: VisusDynamicAOIPredictionIntakeRun, timestamps_by_stimulus: Mapping[str, Sequence[float]], output_dir: str | Path, *, reference_stream_id: str, timestamp_grid_basis: str, max_interpolation_gap_ms: float, min_iou: float = 0.5, require_label_match: bool = True, fixations_by_stimulus: Mapping[str, pd.DataFrame] | None = None, overlap_rule: str = 'highest_confidence', human_agreement_streams: tuple[str, str] | None = None, include_matches: bool = False, overwrite: bool = False) -> VisusDynamicAOIValidationSuiteRun

Compute, freeze, cross-check, and bind the audited VISUS validation tranche.

Human-human agreement becomes a required child when the source audit verifies independently recoverable streams. When independence is not verified, the suite explicitly records that the child is unavailable rather than inferring reliability from the published contributor count.

validate_visus_dynamic_aoi_suite_manifest(path: str | Path, *, verify_reports: bool = True) -> dict[str, Any]

Validate a VISUS dynamic-AOI suite manifest and its child reports.

VISUS execution provenance

gazeforge.visus_execution

Raw-input provenance binding for guarded VISUS suite execution.

VisusExecutionInputSnapshot dataclass

Exact raw-file identity captured before VISUS execution.

VisusExecutionProvenanceRun dataclass

Frozen raw-input-to-suite provenance manifest.

build_visus_execution_provenance(audit: VisusSourceAuditRun, suite: VisusDynamicAOIValidationSuiteRun, snapshots: tuple[VisusExecutionInputSnapshot, ...]) -> dict[str, Any]

Bind exact raw input files to one fully verified frozen VISUS suite.

snapshot_visus_execution_inputs(*, source_audit_spec: str | Path, human_aoi_table: str | Path, model_prediction_table: str | Path, timestamp_grid_json: str | Path) -> tuple[VisusExecutionInputSnapshot, ...]

Fingerprint the four raw files consumed by the guarded VISUS CLI.

The source-audit JSON additionally receives a semantic fingerprint of the parsed :class:~gazeforge.visus_audit.VisusSourceAuditSpec, allowing the execution manifest to prove that the exact raw JSON corresponds to the specification used by the source audit.

validate_visus_execution_provenance(path: str | Path, *, verify_suite: bool = True) -> dict[str, Any]

Validate a frozen raw-input provenance manifest and optionally its sibling suite.

verify_visus_execution_inputs_unchanged(snapshots: tuple[VisusExecutionInputSnapshot, ...], *, source_audit_spec: str | Path, human_aoi_table: str | Path, model_prediction_table: str | Path, timestamp_grid_json: str | Path) -> None

Refuse provenance freezing if any raw input changed during suite execution.

visus_execution_provenance_path(output_dir: str | Path) -> Path

Return the fixed provenance-manifest path for one VISUS suite directory.

write_visus_execution_provenance(manifest: dict[str, Any], output_dir: str | Path, *, overwrite: bool = False) -> VisusExecutionProvenanceRun

Freeze an execution provenance manifest after its fingerprint revalidates.

VISUS Frozen Evidence bundle gate

gazeforge.visus_evidence

Eligibility gate for publishing audited VISUS suites as Frozen Evidence.

VisusFrozenEvidenceBundle dataclass

Verified suite plus execution-provenance identities eligible for evidence review.

load_visus_frozen_evidence_bundle(path: str | Path) -> VisusFrozenEvidenceBundle

Return a compact typed record after full bundle validation.

validate_visus_frozen_evidence_bundle(path: str | Path) -> dict[str, Any]

Require both the verified VISUS suite and its raw-execution provenance manifest.

This is a publication-eligibility integrity gate, not a scientific validity decision. It refuses a suite that cannot be tied to the execution-provenance layer introduced for the exact reviewed source-audit JSON, human AOI table, model prediction table, and external timestamp-grid JSON. It does not establish that the underlying source is authoritative or that its reuse terms are correct; those remain source-audit evidence decisions.