1515 One run per mantissa-bit level [52,23,16,10] with
1616 --backend=vprec --vprec-mode=full; shows where each case breaks.
1717
18- D. verrou_dd_sym on failure (--no-dd-sym to skip)
19- Delta-debug bisection isolates the minimal set of *functions* causing
20- instability.
21-
22- E. verrou_dd_line on failure, after dd_sym (--no-dd-line to skip)
23- Further bisects to exact *source lines* within the responsible functions.
24- Each reported line is then *confirmed* by a positive control: --gen-source
25- captures the symbol-correct executed lines, those are filtered to the suspect
26- set, and a float-mode run with --source restricted to just them must
27- reproduce the instability. If perturbing the suspect set does not reproduce
28- it, the case's hotspots are reported as unconfirmed (downgraded from
29- ::warning:: to ::notice::) — this is a single set-level verdict, not per line.
30- Each line is then perturbed alone and ranked by the share of the single-
31- precision deviation it reproduces. NOTE: that share is a *sensitivity*
32- measure — where reduced precision most moves the output — typically dominated
33- by the time integrator / final accumulation, NOT by where cancellation
34- originates. Stage F is the cancellation-origin view; the two usually differ.
35- Hotspots are cross-referenced against the stage-F cancellation sites and
36- flagged as instance-ambiguous when the .fpp line sits inside a #:for/#:def
37- expansion.
38-
39- F. Cancellation detection (--no-cancellation to skip)
18+ D. Cancellation detection (--no-cancellation to skip)
4019 One run with --check-cancellation=yes; reports MFC source lines that
4120 produce catastrophic cancellation (subtraction of nearly-equal doubles).
42- Uses --cc-gen-file for structured per-line output.
21+ Uses --cc-gen-file for structured per-line output. A cancellation site whose
22+ .fpp line sits inside a #:for/#:def expansion is flagged as instance-ambiguous
23+ (the line maps to multiple generated instances).
4324
44- G . MCA significant-bits estimate (--no-mca to skip)
25+ E . MCA significant-bits estimate (--no-mca to skip)
4526 N runs with --backend=mcaquad; max deviation vs nearest-rounding
4627 reference gives a lower bound on significant bits: s = -log2(dev/scale).
4728
48- H . Float-max overflow detection (--no-float-max to skip)
29+ F . Float-max overflow detection (--no-float-max to skip)
4930 One run with --check-max-float=yes; reports locations where a
5031 double→float conversion would overflow to ±Inf.
5132
6243Usage:
6344 ./mfc.sh fp-stability # built-in 1-D suite
6445 ./mfc.sh fp-stability my_case.py # your own case (small/short, serial, CPU)
65- ./mfc.sh fp-stability --no-vprec --no-dd-line
46+ ./mfc.sh fp-stability --no-vprec --no-cancellation
6647 ./mfc.sh fp-stability --sim-binary PATH --pre-binary PATH
6748
6849A user case .py is run as a single serial CPU process under Verrou, so it must be
8465 MIN_SIG_BITS ,
8566 _autodetect_compare ,
8667 _cancellation_severity ,
87- _mark_cancellation ,
68+ _macro_context ,
8869 _max_abs_np ,
8970 _max_diff_np ,
9071 _sig_bits ,
9778 _find_binary ,
9879 _find_verrou ,
9980 _run_cancellation_check ,
100- _run_confirmation ,
101- _run_dd_line ,
102- _run_dd_sym ,
10381 _run_float_max_check ,
10482 _run_float_proxy ,
10583 _run_mca_samples ,
@@ -391,12 +369,9 @@ def _blank_result(name: str) -> dict:
391369 "sig_bits" : None ,
392370 "float_proxy" : None ,
393371 "vprec" : [],
394- "dd_sym_syms" : [],
395- "dd_line_locs" : [],
396- "dd_line_confirmed" : None ,
397- "dd_line_confirm_dev" : None ,
398372 "cancellation_locs" : [],
399373 "cancellation_bits" : {},
374+ "cancellation_macro" : {},
400375 "mca_dev" : None ,
401376 "mca_sigbits" : None ,
402377 "float_max_locs" : [],
@@ -409,11 +384,8 @@ def _run_case(
409384 sim_bin : str ,
410385 pp_bin : str ,
411386 n_samples : int ,
412- log_dir : str ,
413387 run_float : bool ,
414388 run_vprec : bool ,
415- run_dd_sym : bool ,
416- run_dd_line : bool ,
417389 run_cancellation : bool ,
418390 run_mca : bool ,
419391 run_float_max : bool ,
@@ -493,62 +465,7 @@ def _run_case(
493465 marker = " [red]FAIL[/red]"
494466 cons .print (f" { bits :2d} bits{ label_str } : dev={ dev :.3e} { marker } " )
495467
496- # --- D/E: delta-debug with float mode to find FP hotspots.
497- # dd_run.sh uses --rounding-mode=float (deterministic single-precision),
498- # so each bisection step is consistent and --nruns=1 suffices. Threshold
499- # = float_proxy/10: the full instrumented set produces ~float_proxy
500- # deviation; excluding the responsible function drops it to near zero;
501- # any subset missing the responsible function gives SAME.
502- # Skip when float_proxy is unavailable or too small to localize.
503- float_proxy = result .get ("float_proxy" )
504- _DD_FLOAT_MIN = 1e-6
505- dd_threshold = float_proxy / 10.0 if float_proxy and float_proxy >= _DD_FLOAT_MIN else 0.0
506- if dd_threshold > 0 and (run_dd_sym or run_dd_line ):
507- cons .print (f" [dim]dd threshold: { dd_threshold :.1e} (float_proxy={ float_proxy :.1e} )[/dim]" )
508- elif run_dd_sym or run_dd_line :
509- cons .print (f" [dim]skipping dd: float_proxy={ float_proxy } < { _DD_FLOAT_MIN :.0e} [/dim]" )
510- if dd_threshold > 0 and run_dd_sym :
511- try :
512- result ["dd_sym_syms" ] = _run_dd_sym (case , verrou_bin , sim_bin , work_dir , log_dir , threshold = dd_threshold )
513- except Exception as exc :
514- cons .print (f" [bold yellow]dd_sym error[/bold yellow]: { exc } " )
515- if dd_threshold > 0 and run_dd_line :
516- try :
517- result ["dd_line_locs" ] = _run_dd_line (
518- case ,
519- verrou_bin ,
520- sim_bin ,
521- work_dir ,
522- log_dir ,
523- threshold = dd_threshold ,
524- )
525- macro_n = sum (1 for loc in result ["dd_line_locs" ] if loc ["macro" ])
526- if macro_n :
527- cons .print (f" [dim]dd_line: { macro_n } hotspot(s) inside fypp-expanded code (instance-ambiguous)[/dim]" )
528- except Exception as exc :
529- cons .print (f" [bold yellow]dd_line error[/bold yellow]: { exc } " )
530-
531- # --- E2: confirm dd_line hotspots and rank each by its individual share ---
532- if dd_threshold > 0 and run_dd_line and result ["dd_line_locs" ]:
533- cons .print (" [dim]confirming + ranking dd_line hotspots (per-line perturbation)...[/dim]" )
534- try :
535- confirmed , cdev , ranked = _run_confirmation (case , verrou_bin , sim_bin , work_dir , ref_dir , result ["dd_line_locs" ], dd_threshold , float_proxy )
536- result ["dd_line_locs" ] = ranked
537- result ["dd_line_confirmed" ] = confirmed
538- result ["dd_line_confirm_dev" ] = cdev
539- if confirmed is True :
540- cons .print (f" [bold green]dd_line confirmed[/bold green]: suspect-only dev={ cdev :.3e} >= { dd_threshold :.1e} " )
541- elif confirmed is False :
542- cons .print (f" [bold yellow]dd_line UNCONFIRMED[/bold yellow]: suspect-only dev={ cdev :.3e} < { dd_threshold :.1e} (attribution suspect)" )
543- top = ranked [0 ] if ranked else None
544- if top and top .get ("share" ) is not None :
545- cons .print (f" highest single-precision sensitivity: { top ['path' ]} :{ top ['start' ]} ({ top ['share' ] * 100 :.0f} % of float-proxy)" )
546- cons .print (" [dim](sensitivity = where reduced precision most moves the output, often the time" )
547- cons .print (" [dim] integrator; not necessarily where cancellation originates — see cancellation sites)[/dim]" )
548- except Exception as exc :
549- cons .print (f" [bold yellow]dd_line confirmation error[/bold yellow]: { exc } " )
550-
551- # --- F: cancellation detection ---
468+ # --- D: cancellation detection ---
552469 if run_cancellation :
553470 cons .print (" [dim]cancellation detection...[/dim]" )
554471 try :
@@ -562,21 +479,22 @@ def _run_case(
562479 bits = _cancellation_severity ([(lvl , s ) for lvl , s in level_sites if s is not None ])
563480 result ["cancellation_locs" ] = locs
564481 result ["cancellation_bits" ] = bits
482+ # flag cancellation sites whose .fpp line is inside a #:for/#:def
483+ # expansion: the line maps to multiple generated instances, so the
484+ # report cannot pin it to a unique runtime instance.
485+ result ["cancellation_macro" ] = {(path , line ): macro for (path , line ) in locs if (macro := _macro_context (path , line ))}
565486 if locs :
566487 worst = max (bits .values ()) if bits else 0
567488 cons .print (f" cancellation: { len (locs )} site(s), worst loses ≥ { worst / math .log2 (10 ):.0f} of ~16 digits" )
489+ n_macro = len (result ["cancellation_macro" ])
490+ if n_macro :
491+ cons .print (f" [dim]{ n_macro } inside fypp expansions — line maps to multiple instances[/dim]" )
568492 else :
569493 cons .print (" cancellation: none detected" )
570- # cross-reference: label dd_line hotspots that sit on a cancellation site
571- if result ["dd_line_locs" ] and locs :
572- _mark_cancellation (result ["dd_line_locs" ], locs )
573- n_xref = sum (1 for loc in result ["dd_line_locs" ] if loc .get ("cancellation" ))
574- if n_xref :
575- cons .print (f" { n_xref } hotspot(s) coincide with a catastrophic-cancellation site" )
576494 except Exception as exc :
577495 cons .print (f" [bold yellow]cancellation check error[/bold yellow]: { exc } " )
578496
579- # --- G : MCA significant-bits estimate ---
497+ # --- E : MCA significant-bits estimate ---
580498 if run_mca :
581499 cons .print (f" [dim]MCA significant-bits estimate (N={ n_samples } )...[/dim]" )
582500 try :
@@ -591,7 +509,7 @@ def _run_case(
591509 except Exception as exc :
592510 cons .print (f" [bold yellow]MCA error[/bold yellow]: { exc } " )
593511
594- # --- H : float-max overflow detection ---
512+ # --- F : float-max overflow detection ---
595513 if run_float_max :
596514 cons .print (" [dim]float-max overflow check...[/dim]" )
597515 try :
@@ -691,8 +609,6 @@ def fp_stability():
691609 n_samples = ARG ("samples" )
692610 run_float = not ARG ("no_float_proxy" )
693611 run_vprec = not ARG ("no_vprec" )
694- run_dd_sym = not ARG ("no_dd_sym" )
695- run_dd_line = not ARG ("no_dd_line" )
696612 run_cancellation = not ARG ("no_cancellation" )
697613 run_mca = not ARG ("no_mca" )
698614 run_float_max = not ARG ("no_float_max" )
@@ -715,10 +631,6 @@ def fp_stability():
715631 features .append ("float-proxy" )
716632 if run_vprec :
717633 features .append ("vprec-sweep" )
718- if run_dd_sym :
719- features .append ("dd_sym" )
720- if run_dd_line :
721- features .append ("dd_line" )
722634 if run_cancellation :
723635 features .append ("cancellation" )
724636 if run_mca :
@@ -739,11 +651,8 @@ def fp_stability():
739651 sim_bin ,
740652 pp_bin ,
741653 n_samples ,
742- log_dir ,
743654 run_float ,
744655 run_vprec ,
745- run_dd_sym ,
746- run_dd_line ,
747656 run_cancellation ,
748657 run_mca ,
749658 run_float_max ,
@@ -762,9 +671,6 @@ def fp_stability():
762671 mark = "[green]✓[/green]" if r ["passed" ] else "[red]✗[/red]"
763672 cons .print (f" { mark } { r ['name' ]} " )
764673
765- if n_fail > 0 :
766- cons .print (f"\n dd_sym/dd_line logs in: { log_dir } " )
767-
768674 _emit_github_summary (results , n_samples )
769675 _emit_github_annotations (results )
770676
0 commit comments