|
24 | 24 | emit_lifted_constant, |
25 | 25 | emit_quantized_biases, |
26 | 26 | emit_shape, |
27 | | - emit_sub_int, |
28 | 27 | parse_dequant_node, |
29 | 28 | to_mlx_qparams, |
30 | 29 | torch_dtype_to_scalar_type, |
@@ -3529,11 +3528,10 @@ def _sample_handler(P: MLXProgramBuilder, n: Node) -> Slot: |
3529 | 3528 | skipping the sampling chain (so 0 is exact, not the small-epsilon approx). |
3530 | 3529 | """ |
3531 | 3530 | args = P.args(n) |
3532 | | - require_args(args, 3, 5, "mlx.sample") |
| 3531 | + require_args(args, 4, 5, "mlx.sample") |
3533 | 3532 | require_kwargs(P.kwargs(n), set(), "mlx.sample") |
3534 | | - logits, temperature, top_p = args[0], args[1], args[2] |
3535 | | - seed = args[3] if len(args) > 3 and args[3] is not None else None |
3536 | | - top_k = args[4] if len(args) > 4 and args[4] is not None else None |
| 3533 | + logits, temperature, top_k, top_p = args[0], args[1], args[2], args[3] |
| 3534 | + seed = args[4] if len(args) > 4 and args[4] is not None else None |
3537 | 3535 |
|
3538 | 3536 | temp_dt = n.args[1].meta["val"].dtype |
3539 | 3537 | out = P.make_or_get_slot(n) |
@@ -3614,8 +3612,84 @@ def emit_sample(): |
3614 | 3612 | ) |
3615 | 3613 | ) |
3616 | 3614 | scaled = logits_f |
| 3615 | + neg_inf = emit_lifted_constant(P, float("-inf"), torch.float32) |
| 3616 | + |
| 3617 | + # Top-k first, on scaled logits. Clip k to vocab size so the default |
| 3618 | + # max-int sentinel selects every token. |
| 3619 | + vocab_size = int(n.args[0].meta["val"].shape[-1]) |
| 3620 | + vocab = emit_lifted_constant(P, vocab_size, torch.int64) |
| 3621 | + _, clipped_top_k = P.make_tmp_slot() |
| 3622 | + P.emit( |
| 3623 | + MinimumNode( |
| 3624 | + a=P.slot_to_tid(top_k), |
| 3625 | + b=P.slot_to_tid(vocab), |
| 3626 | + out=P.slot_to_tid(clipped_top_k), |
| 3627 | + ) |
| 3628 | + ) |
| 3629 | + _, top_k_val = P.make_tmp_value_slot() |
| 3630 | + P.emit( |
| 3631 | + ItemIntNode( |
| 3632 | + x=P.slot_to_tid(clipped_top_k), out=P.slot_to_vid(top_k_val) |
| 3633 | + ) |
| 3634 | + ) |
| 3635 | + _, top_k_index = P.make_tmp_value_slot() |
| 3636 | + P.emit( |
| 3637 | + SubtractIntNode( |
| 3638 | + a=P.to_int_or_vid(top_k_val), |
| 3639 | + b=IntOrVid.from_literal(1), |
| 3640 | + out=P.slot_to_vid(top_k_index), |
| 3641 | + ) |
| 3642 | + ) |
3617 | 3643 |
|
3618 | | - # top-p nucleus mask; SortNode is ascending-only, so sort -probs for descending. |
| 3644 | + _, sorted_scaled = P.make_tmp_slot() |
| 3645 | + P.emit(NegNode(x=P.slot_to_tid(scaled), out=P.slot_to_tid(sorted_scaled))) |
| 3646 | + P.emit( |
| 3647 | + SortNode( |
| 3648 | + x=P.slot_to_tid(sorted_scaled), |
| 3649 | + out=P.slot_to_tid(sorted_scaled), |
| 3650 | + axis=-1, |
| 3651 | + ) |
| 3652 | + ) |
| 3653 | + P.emit( |
| 3654 | + NegNode(x=P.slot_to_tid(sorted_scaled), out=P.slot_to_tid(sorted_scaled)) |
| 3655 | + ) |
| 3656 | + _, top_k_thresh = P.make_tmp_slot() |
| 3657 | + P.emit( |
| 3658 | + TakeNode( |
| 3659 | + x=P.slot_to_tid(sorted_scaled), |
| 3660 | + index=P.to_int_or_vid_or_tid(top_k_index), |
| 3661 | + out=P.slot_to_tid(top_k_thresh), |
| 3662 | + axis=-1, |
| 3663 | + ) |
| 3664 | + ) |
| 3665 | + P.emit( |
| 3666 | + ExpandDimsNode( |
| 3667 | + x=P.slot_to_tid(top_k_thresh), |
| 3668 | + out=P.slot_to_tid(top_k_thresh), |
| 3669 | + axis=-1, |
| 3670 | + ) |
| 3671 | + ) |
| 3672 | + _, drop_k = P.make_tmp_slot() |
| 3673 | + P.emit( |
| 3674 | + LessNode( |
| 3675 | + a=P.slot_to_tid(scaled), |
| 3676 | + b=P.slot_to_tid(top_k_thresh), |
| 3677 | + out=P.slot_to_tid(drop_k), |
| 3678 | + ) |
| 3679 | + ) |
| 3680 | + _, top_k_scaled = P.make_tmp_slot() |
| 3681 | + P.emit( |
| 3682 | + WhereNode( |
| 3683 | + condition=P.slot_to_tid(drop_k), |
| 3684 | + x=P.slot_to_tid(neg_inf), |
| 3685 | + y=P.slot_to_tid(scaled), |
| 3686 | + out=P.slot_to_tid(top_k_scaled), |
| 3687 | + ) |
| 3688 | + ) |
| 3689 | + scaled = top_k_scaled |
| 3690 | + |
| 3691 | + # Top-p nucleus mask on probabilities renormalized over the top-k set. |
| 3692 | + # SortNode is ascending-only, so sort -probs for descending. |
3619 | 3693 | # probs is read twice (neg_p below and the drop comparison), keep separate. |
3620 | 3694 | _, probs = P.make_tmp_slot() |
3621 | 3695 | P.emit(SoftmaxNode(x=P.slot_to_tid(scaled), out=P.slot_to_tid(probs), axis=-1)) |
@@ -3676,47 +3750,6 @@ def emit_sample(): |
3676 | 3750 | out=P.slot_to_tid(drop), |
3677 | 3751 | ) |
3678 | 3752 | ) |
3679 | | - if top_k is not None: |
3680 | | - _, top_k_val = P.make_tmp_value_slot() |
3681 | | - P.emit(ItemIntNode(x=P.slot_to_tid(top_k), out=P.slot_to_vid(top_k_val))) |
3682 | | - top_k_iov = P.to_int_or_vid(top_k_val) |
3683 | | - top_k_index = emit_sub_int(P, top_k_iov, IntOrVid.from_literal(1)) |
3684 | | - _, top_k_thresh = P.make_tmp_slot() |
3685 | | - P.emit( |
3686 | | - TakeNode( |
3687 | | - x=P.slot_to_tid(sorted_p), |
3688 | | - index=( |
3689 | | - IntOrVidOrTid.from_vid(top_k_index.vid) |
3690 | | - if top_k_index.is_vid |
3691 | | - else IntOrVidOrTid.from_literal(top_k_index.literal) |
3692 | | - ), |
3693 | | - out=P.slot_to_tid(top_k_thresh), |
3694 | | - axis=-1, |
3695 | | - ) |
3696 | | - ) |
3697 | | - P.emit( |
3698 | | - ExpandDimsNode( |
3699 | | - x=P.slot_to_tid(top_k_thresh), |
3700 | | - out=P.slot_to_tid(top_k_thresh), |
3701 | | - axis=-1, |
3702 | | - ) |
3703 | | - ) |
3704 | | - _, drop_k = P.make_tmp_slot() |
3705 | | - P.emit( |
3706 | | - LessNode( |
3707 | | - a=P.slot_to_tid(probs), |
3708 | | - b=P.slot_to_tid(top_k_thresh), |
3709 | | - out=P.slot_to_tid(drop_k), |
3710 | | - ) |
3711 | | - ) |
3712 | | - P.emit( |
3713 | | - LogicalOrNode( |
3714 | | - a=P.slot_to_tid(drop), |
3715 | | - b=P.slot_to_tid(drop_k), |
3716 | | - out=P.slot_to_tid(drop), |
3717 | | - ) |
3718 | | - ) |
3719 | | - neg_inf = emit_lifted_constant(P, float("-inf"), torch.float32) |
3720 | 3753 | # masked = where(drop, -inf, scaled); then add gumbel noise in place. |
3721 | 3754 | _, masked = P.make_tmp_slot() |
3722 | 3755 | P.emit( |
|
0 commit comments