Controlled robustness diagnostics
Robustness experiments measure how existing scorers respond to a declared change in their inputs. They produce baseline scores, transformed scores and score deltas, with the input text, source hashes, transformation parameters and validation evidence. These are diagnostic comparisons; they do not infer plagiarism or change dataset labels.
Run the offline example
From an installed checkout with the optional chunking dependency:
python -m pip install -e ".[chunking]"
python examples/experiments/robustness_and_long_files.py \
--seed 17 --output-dir benchmark_outputs/experiments
The example uses Levenshtein, Winnowing and the existing static hash vector backend. It downloads no datasets or models. Its second workflow compares long-file chunking and aggregation.
The CLI can run the robustness portion using only the base installation:
matheel experiments robustness --seed 17 --output-dir benchmark_outputs/robustness
What the controlled cases mean
| Configuration | Baseline and change | Validation recorded |
|---|---|---|
| Identity | Compare the same source twice | Fixed behavior assertions |
| Formatting | Rewrite the candidate with Python's ast.unparse |
Equal Python ASTs and fixture behavior assertions |
| Comments | Add deterministic comments to the candidate | Equal Python ASTs and fixture behavior assertions |
| Identifier renaming | Rename selected local identifiers in a known fixture | Explicit assertions for empty, mixed-sign and repeated-value inputs |
| Partial copy | Append one reference function to a different controlled program | Existing and copied functions satisfy named behavior fixtures; copied byte count/fraction recorded |
| Shared starter code | Prepend the same provided helper to two otherwise distinct programs | Original program and provided-helper behavior fixtures |
Partial copying changes the program's contents. Shared starter code supplies legitimate common material. Neither configuration is assigned a positive plagiarism label. Their score deltas are measured relative to the original, explicitly recorded pair.
AST equality checks syntax structure. It does not prove arbitrary observational equivalence: introspection, comments used by external tools and source line numbers can matter. Renaming is binding-sensitive, so the generic API requires a caller validator and a named assertion description. It does not assume that identifier substitution preserves behavior.
Use your own source and scoring runs
The CLI generates only formatting and comment cases for supplied Python source. It parses that source and scores its text; it never executes it:
matheel experiments robustness --source solution.py \
--config scoring_runs.json --output-dir benchmark_outputs/source_diagnostics
scoring_runs.json uses the same declared options as a comparison suite:
{
"runs": [
{"run_name": "raw_levenshtein", "feature_weights": {"levenshtein": 1}},
{
"run_name": "basic_levenshtein",
"options": {
"feature_weights": {"levenshtein": 1},
"preprocess_mode": "basic",
"code_language": "python"
}
}
]
}
For binding-sensitive transformations, use explicit assertions through Python:
from matheel.robustness import python_robustness_case, run_robustness_experiment
source = "def add(a, b):\n total = a + b\n return total\n"
def validate_known_fixture(before, after):
# Execute only this known, trusted fixture, never an untrusted submission.
for text in (before, after):
namespace = {}
exec(text, namespace)
if namespace["add"](2, 3) != 5 or namespace["add"](-2, 2) != 0:
return False
return True
case = python_robustness_case(
source, "identifier_renaming", identifier_map={"total": "result"},
validator=validate_known_fixture, validation_name="add(2,3)==5; add(-2,2)==0",
)
report, artifacts = run_robustness_experiment(
[{"run_name": "levenshtein", "feature_weights": {"levenshtein": 1}}],
cases=[case], output_dir="benchmark_outputs/rename",
)
Read and reproduce the results
robustness.json contains complete case inputs and provenance, declared scorer
configurations, deterministic case/run IDs, implementation hashes and the environment
snapshot. robustness_scores.csv contains the numeric comparisons. A failed run retains
null scores and its error; the CLI exits nonzero so it cannot look like a successful
zero-score comparison.
Compare like configurations and inspect individual transformations. These fixtures
are small diagnostics, not evidence of population-level detection quality. For model
runs, use a pinned local model snapshot as model_name when exact replay matters.
The public frozen plagiarism-v1 protocol and its 256-token configuration remain
separate from these experiments.
Practical configuration comparisons
The 0.7.1 configuration guidance experiment compares existing raw/basic Levenshtein, raw LCS and raw Winnowing settings. It uses all 293 existing Python pair observations for fixed-threshold error analysis, the six controlled fixtures above, and formatting/comments applied to one declared, size-selected Python submission. Supplied code is parsed and scored without executing it. The selection uses byte counts and source row order, without looking at labels, scores or measured coverage. Its source-free outputs retain input hashes, scorer options, transformation provenance, paired uncertainty, false positives/negatives and length slices.
OMP_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 MKL_NUM_THREADS=1 \
HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 \
python scripts/compare_existing_configurations.py \
--manifest prepared/prepared-manifest.json \
--plan benchmarks/configuration-guidance/plagiarism-071/plan.json \
--source-root ../matheel-0.7.1 --release-wheel dist/matheel-0.7.1-py3-none-any.whl \
--cpu-lock /private/tmp/matheel-071-benchmark-cpu.lock \
--output-dir benchmark_outputs/configuration-guidance-071
Use the exact v0.7.1 source checkout and published wheel described in the
retrieval replay instructions.
The POSIX runner verifies that source against the wheel and gives each of its three
workflows 120 seconds. The long-file workflow needs the pinned MiniLM snapshot
already cached, plus the existing sentence_transformers and chunking extras.
It downloads nothing. Scorer failures retain null scores and a failed workflow;
they cannot count as successful zero scores.
Use raw/basic comparisons to determine which formatting and comments your experiment intends to retain. Removing those signals may reduce sensitivity to surface edits, while also increasing scores for unrelated programs with common syntax or starter material. Inspect both false-positive and false-negative rows and paired differences on the same observations. The example threshold of 0.5 is fixed in advance for diagnosis; it is not fitted to these inputs or recommended as a deployment decision boundary. Confidence intervals are conditional on the existing source-sharing groups and labels.
In the recorded 293-pair comparison, raw Levenshtein's F1 at 0.5 was 0.6070 and basic Levenshtein's was 0.5436, while their AP point estimates were 0.5600 and 0.5695 respectively. These are observed values at one fixed threshold; inspect the paired intervals and disagreement rows before drawing a conclusion. On the declared supplied source, raw Winnowing fell from 1.0 to 0.570850 after AST-preserving formatting, while basic Levenshtein stayed at 1.0 after added comments. Raw fingerprint settings do not guarantee formatting invariance.
Use the partial-copy and shared-starter cases to expose score behavior, with the recorded transformation as the explanation. A higher score after adding common material does not establish plagiarism. Neither these six fixtures nor one transformed submission establish robustness across datasets, languages or adversarial transformations.