Skip to content

Repeat ase - #107

Open
guanqin-123 wants to merge 7 commits into
SVF-tools:mainfrom
guanqin-123:RepeatASE
Open

Repeat ase#107
guanqin-123 wants to merge 7 commits into
SVF-tools:mainfrom
guanqin-123:RepeatASE

Conversation

@guanqin-123

Copy link
Copy Markdown
Contributor

Unify violation semantics. Concrete violation logic existed in three
places; the fuzzer's copy handled 4 of the 5 OutKinds and read the true
label from the seed instead of the spec, so valid_mask was all-false on
every VNNLib instance and an UNSAFE_LINEAR dispatch miss returned quietly.
OutputSpec.violation(y, rows) is now the single implementation.

Correct the violation predicates. bab tested MARGIN_ROBUST as
(max_other − z_t) >= +margin, the wrong sign — it missed every
counterexample in -margin <= gap < +margin. TOP1 and MARGIN also
compared with > where encode_linear uses >=

…pec.violation()

Concrete violation semantics were implemented three times over: in
OutputSpecLayer.forward, in bab.check_violations_batched, and again in the
fuzzer's PropertyChecker. The fuzzer's copy handled only 4 of the 5 OutKinds
and read the true label from seeds.label rather than the spec, so on every
VNNLib benchmark valid_mask was all-False and the violation mask could never
fire. A dispatch miss on UNSAFE_LINEAR returned all-False just as quietly.
Both failures were silent: the fuzzer ran at full speed with normal coverage
and simply reported zero counterexamples.

Add OutputSpec.violation(y, rows) returning (violated, severity) for all five
kinds, raising on an unhandled kind. The rows argument matters: SeedCorpus
samples with replacement, so batch lane i carries a mutation of instance
original_index[i], not i. Evaluating spec row i against lane i would compare
one instance's output against another's label.

OutputSpecLayer.forward now delegates with identity rows; PropertyChecker
passes rows=seeds.original_index and keeps only counterexample materialization,
losing its dispatch table and all four _check_* methods. Counterexample is
rebuilt around spec_row/severity/true_class, dropping the -1 sentinels that a
label-less kind was forced to carry, and summary() no longer needs the spec
handed back to it. Per-row slicing, previously duplicated in bab.py and
pipeline_cli.py, moves onto the shared _gather_rows helper.

Missing specs and seed/spec-row misalignment now raise instead of degrading
quietly. bab.check_violations_batched keeps its own eps tolerance: it validates
LP-solver candidates, where numerical slack is expected.

CIFAR-100 ResNet goes from 0 to ~2900 counterexamples in a 30s budget.
PGD and FGSM both fell back to maximizing output.var() whenever no label was
available. VNNLib seeds carry label=None, so on those benchmarks the gradient
attack was never aimed at the property at all - it optimized an unrelated
objective while the schedule around it was tuned. pipeline.yaml gives pgd a
weight of 0.4, so this was the strategy actually running.

Drive both strategies with the severity returned by OutputSpec.violation,
gathered per lane. For TOP1_ROBUST this reproduces the existing CW loss
exactly, so it generalizes the objective to all five kinds rather than
changing the classification case. Attack the inner network instead of the
VerifiableModel wrapper: InputLayer and InputSpecLayer are tensor
pass-throughs, so the logits are identical, but the wrapper's spec layers ran
.sum().item() at nine sites - roughly 200 CUDA syncs per mutation across PGD's
50 steps.

SeedCorpus.add hardcoded zeros for _select_counts while every sibling field
indexed seeds.X[idx], and child_seeds never carried select_count forward. The
adaptive perturbation schedule s_b = 1-(1-s0)^(n+1) reads n from that counter,
so it was frozen at n=0 for every discovered seed. Also drop the perturb-size
banner, which printed a value that the first mutate() call overwrote, and
expose the synthesis grouping key so callers stop pairing seeds by
id(pytorch_model) - insufficient, since synthesis also splits on InKind,
OutKind and a cd_sig hash.
check_violations_batched tested MARGIN_ROBUST as (max_other - z_t) >= +margin,
the wrong sign. encode_linear is the authority here: it emits rows e_j - e_t
with thresholds = -margin and certifies iff max_j(z_j - z_t) < -margin, so a
lane is violated iff (max_other - z_t) >= -margin. The old predicate only
flagged a violation once the runner-up had beaten the target by margin, i.e.
it missed every genuine counterexample in the band -margin <= gap < +margin.

TOP1_ROBUST and MARGIN_ROBUST also compared with > where encode_linear uses
>=. Robustness demands strict separation, so a separation of exactly the
threshold already falsifies it; an argmax-based check cannot express this at
all, since it resolves ties by lowest class index. All three implementations
now agree on both kinds.

InputSpecLayer carried the same identity-lane assumption that
OutputSpecLayer had, comparing lane-indexed inputs against spec-row-indexed
bounds, which left input_satisfied_per_sample silently wrong under a permuted
fuzzer batch; it is now row-aware. Counterexample emission is gated on input
feasibility, because the LIN_POLY projection is still a no-op and would
otherwise report inputs that never satisfied the input spec as counterexamples;
rejected candidates are counted rather than dropped in silence. OutputSpec
also accepts a list-valued margin, matching how y_true was already handled.
Four silent failures made the fuzzer report zero counterexamples on every
VNNLib benchmark: violation semantics were duplicated across three call
sites and the fuzzer's copy read the label from the seed instead of the
spec, bab tested MARGIN_ROBUST with the wrong sign, TF32 truncated float32
so attacks landed on fabricated sign flips, and the ONNX model cache was
consulted after conversion rather than before. Binarized networks needed a
straight-through estimator and pre-softmax logits on top of that, since
torch.sign has no gradient and the trailing softmax saturates. CIFAR-100
goes from zero to ~107k counterexamples, TinyImageNet to 59k and traffic
signs to 7.3k, with no false positives found in onnxruntime audits.
@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.04382% with 55 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.00%. Comparing base (38a55b2) to head (1573c70).

Files with missing lines Patch % Lines
act/pipeline/fuzzing/mutations.py 93.53% 13 Missing ⚠️
act/front_end/vnnlib_loader/onnx_converter.py 0.00% 8 Missing ⚠️
act/pipeline/fuzzing/checker.py 81.81% 8 Missing ⚠️
act/front_end/verifiable_model.py 83.33% 7 Missing ⚠️
act/front_end/vnnlib_loader/vnnlib_parser.py 77.77% 6 Missing ⚠️
act/front_end/vnnlib_loader/data_model_loader.py 85.71% 5 Missing ⚠️
act/pipeline/fuzzing/tracer.py 40.00% 3 Missing ⚠️
act/front_end/model_synthesis.py 90.00% 2 Missing ⚠️
act/util/device_manager.py 81.81% 2 Missing ⚠️
act/pipeline/fuzzing/actfuzzer.py 87.50% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #107      +/-   ##
==========================================
+ Coverage   73.54%   74.00%   +0.46%     
==========================================
  Files          91       91              
  Lines       20987    21159     +172     
==========================================
+ Hits        15435    15659     +224     
+ Misses       5552     5500      -52     
Flag Coverage Δ
bab 46.17% <49.40%> (-4.34%) ⬇️
backend-float32 48.36% <35.65%> (-0.08%) ⬇️
backend-float64 48.40% <35.85%> (-0.07%) ⬇️
frontend 32.70% <67.13%> (+0.49%) ⬆️
pipeline-fuzz 21.32% <82.27%> (+0.91%) ⬆️
pipeline-verify 39.01% <45.41%> (-0.22%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
act/back_end/bab/bab.py 73.55% <100.00%> (-0.35%) ⬇️
act/front_end/specs.py 88.67% <100.00%> (+4.02%) ⬆️
act/front_end/vnnlib_loader/create_specs.py 64.48% <100.00%> (-1.89%) ⬇️
act/pipeline/fuzzing/corpus.py 89.71% <100.00%> (+1.75%) ⬆️
act/util/cli_utils.py 100.00% <100.00%> (ø)
act/pipeline/fuzzing/actfuzzer.py 91.03% <87.50%> (+10.16%) ⬆️
act/front_end/model_synthesis.py 90.85% <90.00%> (+0.15%) ⬆️
act/util/device_manager.py 56.09% <81.81%> (+2.67%) ⬆️
act/pipeline/fuzzing/tracer.py 87.50% <40.00%> (-4.03%) ⬇️
act/front_end/vnnlib_loader/data_model_loader.py 75.51% <85.71%> (+3.46%) ⬆️
... and 5 more

... and 3 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 38a55b2...1573c70. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread .github/workflows/act-pipeline-fuzz.yml Outdated
- name: Run fuzzing at float64 (non-quantized CIFAR-100)
run: |
cd ${{ github.workspace }}
coverage run -p -m act.pipeline --fuzz --category cifar100_2024 --dtype float64 --max-instances 2 --timeout 30 --iterations 200

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between line 112 and line 94? Why do we put cifar100_2024 under traffic signs CI but not close to cifar100 at line 94?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, revised.

…00 steps

It sat after the traffic signs cache and download, which made it read as part
of the traffic signs block.
- Add cd_group="shape" to model synthesis so instances sharing c but differing
  in d group together; the fuzz CLI opts in, verification keeps the byte-valued
  default and is unchanged.
- Key seed-corpus dedup by (spec row, tensor hash) so byte-identical seeds on
  different lanes survive instead of collapsing and breaking lane alignment.
- lsnc_relu goes from 1040 groups of batch 1 to 13 of batch 80: 58.7x faster at
  equal work, with higher coverage.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants