@@ -251,7 +251,8 @@ def _auf_mask(
251251 base_mask : torch .Tensor ,
252252 num_anchors : int ,
253253 block_size : int ,
254- ) -> torch .Tensor :
254+ return_j_star : bool = False ,
255+ ) -> torch .Tensor | tuple [torch .Tensor , torch .Tensor ]:
255256 """Compute the Accept-Until-Fail (AUF) loss mask for the base branch.
256257
257258 Truncates the cross-entropy support at the first greedy prediction error
@@ -272,21 +273,43 @@ def _auf_mask(
272273 base_mask: Boolean validity mask [1, T] (e.g. domino_loss_mask).
273274 num_anchors: Number of anchor blocks.
274275 block_size: Tokens per block.
276+ return_j_star: If True, also return j* (first error position) per block.
275277
276278 Returns:
277- Boolean mask [1, T] with the same shape as base_mask, zeroed out
278- for all positions strictly after the first error in each block.
279+ If return_j_star is False:
280+ Boolean mask [1, T] with the same shape as base_mask, zeroed out
281+ for all positions strictly after the first error in each block.
282+ If return_j_star is True:
283+ Tuple of (mask, j_star) where j_star is [num_anchors] tensor
284+ containing the first error position (0-indexed) per block.
285+ j* = block_size means no error (all accepted).
279286 """
280- base_preds_4d = logits .detach ().argmax (dim = - 1 ).reshape (1 , num_anchors , block_size )
281- target_ids_4d = targets .detach ().argmax (dim = - 1 ).reshape (1 , num_anchors , block_size )
287+ base_preds_4d = (
288+ logits .detach ().argmax (dim = - 1 ).reshape (1 , num_anchors , block_size )
289+ )
290+ target_ids_4d = (
291+ targets .detach ().argmax (dim = - 1 ).reshape (1 , num_anchors , block_size )
292+ )
282293 mask_4d = base_mask .reshape (1 , num_anchors , block_size )
283294 errors = (base_preds_4d != target_ids_4d ) & mask_4d
284295 error_floats = errors .float ()
285296 running_errors = error_floats .cumsum (dim = - 1 )
286297 # errors_before == 0: keep accepted prefix + breaker token j*
287298 # errors_before >= 1: zero the unreachable suffix
288299 errors_before = running_errors - error_floats
289- return (mask_4d & (errors_before == 0 )).reshape_as (base_mask )
300+ auf_mask = (mask_4d & (errors_before == 0 )).reshape_as (base_mask )
301+
302+ if not return_j_star :
303+ return auf_mask
304+
305+ # j* = first error position per block (0-indexed within block)
306+ # If no error, j* = block_size (all positions accepted)
307+ first_error = errors .float ().argmax (dim = - 1 ) # [1, num_anchors]
308+ has_error = errors .any (dim = - 1 ) # [1, num_anchors]
309+ j_star = torch .where (
310+ has_error , first_error , torch .full_like (first_error , block_size )
311+ )
312+ return auf_mask , j_star .squeeze (0 ) # [num_anchors]
290313
291314 @property
292315 def mask_token_id (self ) -> int :
@@ -421,7 +444,9 @@ def _backbone_forward(
421444 )
422445 # Shift right by 1 so verifier_logits[i] predicts token at position i
423446 verifier_logits = torch .roll (verifier_logits , 1 , dims = 1 )
424- target_indices = anchored_block_indices + (1 if self .config .projector_type == "domino" else 0 )
447+ target_indices = anchored_block_indices + (
448+ 1 if self .config .projector_type == "domino" else 0
449+ )
425450 target_indices = target_indices .clamp (max = verifier_logits .shape [1 ] - 1 )
426451 targets = verifier_logits [:, target_indices ]
427452 # shape: [1, num_anchors*block_size, draft_vocab_size]
@@ -471,7 +496,7 @@ def _compute_domino_metrics(
471496 loss_mask : torch .Tensor ,
472497 aligned_loss_mask : torch .Tensor ,
473498 anchored_block_indices : torch .Tensor ,
474- loss_config : "LossConfig" ,
499+ loss_config : "LossConfig | None " ,
475500 gamma : float ,
476501 normalize_by_decay : bool ,
477502 global_step : int ,
@@ -506,38 +531,67 @@ def _compute_domino_metrics(
506531 anchor_pos_in_mask = anchored_block_indices [:: self .block_size ]
507532 domino_loss_mask [:, :: self .block_size ] = loss_mask [:, anchor_pos_in_mask ]
508533
509- # B-AUF+D (arXiv 2607.01893): L_final keeps full mask; L_base is optionally truncated.
534+ # B-AUF+D (arXiv 2607.01893): L_final keeps full mask;
535+ # L_base is optionally truncated.
536+ j_star = None
510537 if self .config .domino_auf :
511- base_mask = self ._auf_mask (
512- logits , targets , domino_loss_mask , num_anchors , self .block_size
538+ base_mask , j_star = self ._auf_mask (
539+ logits ,
540+ targets ,
541+ domino_loss_mask ,
542+ num_anchors ,
543+ self .block_size ,
544+ return_j_star = True ,
513545 )
514546 else :
515547 base_mask = domino_loss_mask
516548
517549 base_loss , base_metrics = compute_metrics (
518- logits , targets , base_mask , self .block_size ,
519- gamma = gamma , loss_config = loss_config ,
520- normalize_by_decay = normalize_by_decay , decay_mode = "domino" ,
550+ logits ,
551+ targets ,
552+ base_mask ,
553+ self .block_size ,
554+ gamma = gamma ,
555+ loss_config = loss_config ,
556+ normalize_by_decay = normalize_by_decay ,
557+ decay_mode = "domino" ,
521558 )
522559 final_loss , final_metrics = compute_metrics (
523- refined_logits , targets , domino_loss_mask , self .block_size ,
524- gamma = gamma , loss_config = loss_config ,
525- normalize_by_decay = normalize_by_decay , decay_mode = "domino" ,
560+ refined_logits ,
561+ targets ,
562+ domino_loss_mask ,
563+ self .block_size ,
564+ gamma = gamma ,
565+ loss_config = loss_config ,
566+ normalize_by_decay = normalize_by_decay ,
567+ decay_mode = "domino" ,
526568 )
527569 loss = (1.0 - lambda_base ) * final_loss + lambda_base * base_loss
570+
571+ ones = torch .tensor (1.0 , device = logits .device )
528572 metrics = {
529573 "loss_sum" : loss .detach ().clone (),
530- "loss_total" : torch . tensor ( 1.0 , device = logits . device ) ,
574+ "loss_total" : ones ,
531575 "base_loss_sum" : base_loss .detach ().clone (),
532- "base_loss_total" : torch . tensor ( 1.0 , device = logits . device ) ,
576+ "base_loss_total" : ones ,
533577 "full_acc_sum" : base_metrics ["full_acc_sum" ],
534578 "full_acc_total" : base_metrics ["full_acc_total" ],
535579 "final_loss_sum" : final_loss .detach ().clone (),
536- "final_loss_total" : torch . tensor ( 1.0 , device = logits . device ) ,
580+ "final_loss_total" : ones ,
537581 "final_full_acc_sum" : final_metrics ["full_acc_sum" ],
538582 "final_full_acc_total" : final_metrics ["full_acc_total" ],
583+ "lambda_base_sum" : torch .tensor (lambda_base , device = logits .device ),
584+ "lambda_base_total" : ones ,
539585 ** {k : v for k , v in final_metrics .items () if k .startswith ("position_" )},
540586 }
587+
588+ # AUF observability: mean j* (first error position) - proxy for acceptance
589+ if j_star is not None :
590+ metrics ["j_star_sum" ] = j_star .float ().sum ()
591+ metrics ["j_star_total" ] = torch .tensor (
592+ float (j_star .numel ()), device = logits .device
593+ )
594+
541595 return loss , metrics
542596
543597 @conditional_torch_compile
0 commit comments