TensorTonic
Study Plans
Study Plan
Beam Search Step
JAX
Hard

Perform one step of beam search for a batch by extending every beam with every vocabulary token, keeping the K best candidates and reordering the beam histories and caches so that each new beam carries the state of the beam it extends.

The batch has B rows, and each batch row bbb keeps K beams. Beam kkk has a running score beam_scores[b, k], the sum of the log probabilities of its tokens so far, and log_probs[b, k, v] is the log probability of token vvv coming next on that beam. Extending beam kkk by token vvv gives a candidate with total score

cb,k,v=beam_scores[b,k]+log_probs[b,k,v]c_{b,k,v} = \text{beam\_scores}[b, k] + \text{log\_probs}[b, k, v]cb,k,v​=beam_scores[b,k]+log_probs[b,k,v]

Among all K times V candidates of row bbb, keep the K with the largest totals, sorted from best to worst. For new beam iii, parents[b, i] is the beam kkk it extends, tokens[b, i] is the token vvv it adds, and scores[b, i] is its total cb,k,vc_{b,k,v}cb,k,v​. Its history is the history of beam parents[b, i] followed by tokens[b, i]. Here V is the vocabulary size and L is the number of tokens each history already holds.

cache is a dict of arrays that each start with the axes (B, K), one entry per beam, such as the attention keys and values of every beam. Every array in it is reordered the same way, so new beam iii of row bbb holds the entry of beam parents[b, i].

Return a dict with five keys. scores is a float32 JAX array of shape (B, K), tokens and parents are int32 JAX arrays of shape (B, K), histories is an int32 JAX array of shape (B, K, L + 1), and cache is a dict with the same keys as the input cache whose arrays keep their shapes and dtypes.

Examples

Input: beam_scores = [[-1.1,-2.6]], log_probs = [[[-2.2,-1.7,-0.6,-1.8],[-2.4,-1.9,-3.0,-0.3]]], histories = [[[0,2],[0,1]]], cache = {"k":[[[-0.8,0.8],[0.8,0.3]]],"v":[[[0.1,0.0],[0.0,-0.3]]]}

Output: {"scores":[[-1.7,-2.8]],"tokens":[[2,1]],"parents":[[0,0]],"histories":[[[0,2,2],[0,2,1]]],"cache":{"k":[[[-0.8,0.8],[-0.8,0.8]]],"v":[[[0.1,0.0],[0.1,0.0]]]}}

Explanation: Beam 0 has score -1.1 and beam 1 has score -2.6. The best candidate is beam 0 with token 2, at -1.1 - 0.6 = -1.7. The second best is beam 0 with token 1, at -2.8, which beats the best candidate of beam 1 (token 3, at -2.9). Both new beams extend beam 0, so both copy its history [0, 2] and its cache rows, and the old beam 1 is dropped.

Input: beam_scores = [[-0.2,-0.7,-1.6],[-0.4,-1.4,-2.8]], log_probs = [[[-4.3,-4.7,-5.0,0.0],[-1.3,-2.5,-0.6,-2.1],[-3.5,-0.5,-1.2,-2.6]],[[-2.9,-0.3,-2.0,-2.5],[-0.6,-3.9,-1.1,-2.5],[-2.9,-1.2,-2.4,-0.6]]], histories = [[[0],[1],[0]],[[0],[2],[3]]], cache = {"k":[[[-0.4,-0.9],[0.8,-0.3],[-0.8,-0.5]],[[0.6,0.4],[0.7,-0.4],[-0.5,1.0]]],"v":[[[-0.4,-0.8],[0.9,0.0],[0.3,-1.0]],[[0.8,-0.1],[0.5,0.7],[-0.3,0.0]]]}

Output: {"scores":[[-0.2,-1.3,-2.0],[-0.7,-2.0,-2.4]],"tokens":[[3,2,0],[1,0,2]],"parents":[[0,1,1],[0,1,0]],"histories":[[[0,3],[1,2],[1,0]],[[0,1],[2,0],[0,2]]],"cache":{"k":[[[-0.4,-0.9],[0.8,-0.3],[0.8,-0.3]],[[0.6,0.4],[0.7,-0.4],[0.6,0.4]]],"v":[[[-0.4,-0.8],[0.9,0.0],[0.9,0.0]],[[0.8,-0.1],[0.5,0.7],[0.8,-0.1]]]}}

Input: beam_scores = [[0.0,-1000000000.0,-1000000000.0]], log_probs = [[[-4.9,-2.5,-2.2,-0.3,-3.0],[-1.9,-1.5,-0.7,-2.1,-3.5],[-2.0,-0.9,-3.7,-1.0,-3.2]]], histories = [[[],[],[]]], cache = {"k":[[[-0.2,-0.7],[-0.3,-0.1],[-0.6,-0.7]]],"v":[[[-1.0,-0.3],[-1.0,-0.1],[0.4,-0.4]]]}

Output: {"scores":[[-0.3,-2.2,-2.5]],"tokens":[[3,2,1]],"parents":[[0,0,0]],"histories":[[[3],[2],[1]]],"cache":{"k":[[[-0.2,-0.7],[-0.2,-0.7],[-0.2,-0.7]]],"v":[[[-1.0,-0.3],[-1.0,-0.3],[-1.0,-0.3]]]}}

Hint 1

jax.lax.top_k(totals.reshape(B, K * V), K) returns sorted scores and flat indices.

Hint 2

flat // V gives the parent beam and flat % V gives the token.

Hint 3

jax.vmap(lambda rows, picks: rows[picks])(leaf, parents) reorders one array, and jax.tree.map applies it to the cache.

Requirements

  • The function is compiled with jax.jit, and every input is traced, including every array inside cache.
  • The cache dict can hold any number of arrays under any key names, so the code must not assume particular keys.

Constraints

  • B is between 1 and 4, K is between 1 and 4, V is between 4 and 6 with V at least K, and L is between 0 and 3.
  • beam_scores is a float32 array of shape (B, K). On a first step, beams that do not exist yet have the score -1000000000.
  • log_probs is a float32 array of shape (B, K, V) with finite values at most 0.
  • histories is an int32 array of shape (B, K, L) with values from 0 to V minus 1.
  • cache holds between 1 and 3 arrays, each float32 or int32, each with leading axes (B, K).
  • Within every batch row, the K plus 1 largest candidate totals are all different, so the selection and its order never tie.
Try Similar Problems
Speculative Decoding AcceptanceHardGreedy Decode Loop with ScanMediumPaged KV Cache GatherMediumGradient Accumulation with ScanHardBlockwise Attention with Online SoftmaxHardRing All-Reduce with ppermuteHardPad and Mask to Bucket LengthMediumHessian-Vector ProductMediumPer-Example Gradient ClippingMediumStraight-Through Estimator with Custom VJPMediumTraced Learning Rate ScheduleMediumExponential Moving Average with ScanMedium

Accepts: array

Accepts: array

Accepts: array

Accepts: object

You must run your code first.
Beam Search Step

Beam Search Step

JAX
Hard

Perform one step of beam search for a batch by extending every beam with every vocabulary token, keeping the K best candidates and reordering the beam histories and caches so that each new beam carries the state of the beam it extends.

The batch has B rows, and each batch row bbb keeps K beams. Beam kkk has a running score beam_scores[b, k], the sum of the log probabilities of its tokens so far, and log_probs[b, k, v] is the log probability of token vvv coming next on that beam. Extending beam kkk by token vvv gives a candidate with total score

cb,k,v=beam_scores[b,k]+log_probs[b,k,v]c_{b,k,v} = \text{beam\_scores}[b, k] + \text{log\_probs}[b, k, v]cb,k,v​=beam_scores[b,k]+log_probs[b,k,v]

Among all K times V candidates of row bbb, keep the K with the largest totals, sorted from best to worst. For new beam iii, parents[b, i] is the beam kkk it extends, tokens[b, i] is the token vvv it adds, and scores[b, i] is its total cb,k,vc_{b,k,v}cb,k,v​. Its history is the history of beam parents[b, i] followed by tokens[b, i]. Here V is the vocabulary size and L is the number of tokens each history already holds.

cache is a dict of arrays that each start with the axes (B, K), one entry per beam, such as the attention keys and values of every beam. Every array in it is reordered the same way, so new beam iii of row bbb holds the entry of beam parents[b, i].

Return a dict with five keys. scores is a float32 JAX array of shape (B, K), tokens and parents are int32 JAX arrays of shape (B, K), histories is an int32 JAX array of shape (B, K, L + 1), and cache is a dict with the same keys as the input cache whose arrays keep their shapes and dtypes.

Examples

Input: beam_scores = [[-1.1,-2.6]], log_probs = [[[-2.2,-1.7,-0.6,-1.8],[-2.4,-1.9,-3.0,-0.3]]], histories = [[[0,2],[0,1]]], cache = {"k":[[[-0.8,0.8],[0.8,0.3]]],"v":[[[0.1,0.0],[0.0,-0.3]]]}

Output: {"scores":[[-1.7,-2.8]],"tokens":[[2,1]],"parents":[[0,0]],"histories":[[[0,2,2],[0,2,1]]],"cache":{"k":[[[-0.8,0.8],[-0.8,0.8]]],"v":[[[0.1,0.0],[0.1,0.0]]]}}

Explanation: Beam 0 has score -1.1 and beam 1 has score -2.6. The best candidate is beam 0 with token 2, at -1.1 - 0.6 = -1.7. The second best is beam 0 with token 1, at -2.8, which beats the best candidate of beam 1 (token 3, at -2.9). Both new beams extend beam 0, so both copy its history [0, 2] and its cache rows, and the old beam 1 is dropped.

Input: beam_scores = [[-0.2,-0.7,-1.6],[-0.4,-1.4,-2.8]], log_probs = [[[-4.3,-4.7,-5.0,0.0],[-1.3,-2.5,-0.6,-2.1],[-3.5,-0.5,-1.2,-2.6]],[[-2.9,-0.3,-2.0,-2.5],[-0.6,-3.9,-1.1,-2.5],[-2.9,-1.2,-2.4,-0.6]]], histories = [[[0],[1],[0]],[[0],[2],[3]]], cache = {"k":[[[-0.4,-0.9],[0.8,-0.3],[-0.8,-0.5]],[[0.6,0.4],[0.7,-0.4],[-0.5,1.0]]],"v":[[[-0.4,-0.8],[0.9,0.0],[0.3,-1.0]],[[0.8,-0.1],[0.5,0.7],[-0.3,0.0]]]}

Output: {"scores":[[-0.2,-1.3,-2.0],[-0.7,-2.0,-2.4]],"tokens":[[3,2,0],[1,0,2]],"parents":[[0,1,1],[0,1,0]],"histories":[[[0,3],[1,2],[1,0]],[[0,1],[2,0],[0,2]]],"cache":{"k":[[[-0.4,-0.9],[0.8,-0.3],[0.8,-0.3]],[[0.6,0.4],[0.7,-0.4],[0.6,0.4]]],"v":[[[-0.4,-0.8],[0.9,0.0],[0.9,0.0]],[[0.8,-0.1],[0.5,0.7],[0.8,-0.1]]]}}

Input: beam_scores = [[0.0,-1000000000.0,-1000000000.0]], log_probs = [[[-4.9,-2.5,-2.2,-0.3,-3.0],[-1.9,-1.5,-0.7,-2.1,-3.5],[-2.0,-0.9,-3.7,-1.0,-3.2]]], histories = [[[],[],[]]], cache = {"k":[[[-0.2,-0.7],[-0.3,-0.1],[-0.6,-0.7]]],"v":[[[-1.0,-0.3],[-1.0,-0.1],[0.4,-0.4]]]}

Output: {"scores":[[-0.3,-2.2,-2.5]],"tokens":[[3,2,1]],"parents":[[0,0,0]],"histories":[[[3],[2],[1]]],"cache":{"k":[[[-0.2,-0.7],[-0.2,-0.7],[-0.2,-0.7]]],"v":[[[-1.0,-0.3],[-1.0,-0.3],[-1.0,-0.3]]]}}

Hint 1

jax.lax.top_k(totals.reshape(B, K * V), K) returns sorted scores and flat indices.

Hint 2

flat // V gives the parent beam and flat % V gives the token.

Hint 3

jax.vmap(lambda rows, picks: rows[picks])(leaf, parents) reorders one array, and jax.tree.map applies it to the cache.

Requirements

  • The function is compiled with jax.jit, and every input is traced, including every array inside cache.
  • The cache dict can hold any number of arrays under any key names, so the code must not assume particular keys.

Constraints

  • B is between 1 and 4, K is between 1 and 4, V is between 4 and 6 with V at least K, and L is between 0 and 3.
  • beam_scores is a float32 array of shape (B, K). On a first step, beams that do not exist yet have the score -1000000000.
  • log_probs is a float32 array of shape (B, K, V) with finite values at most 0.
  • histories is an int32 array of shape (B, K, L) with values from 0 to V minus 1.
  • cache holds between 1 and 3 arrays, each float32 or int32, each with leading axes (B, K).
  • Within every batch row, the K plus 1 largest candidate totals are all different, so the selection and its order never tie.
Try Similar Problems
Speculative Decoding AcceptanceHardGreedy Decode Loop with ScanMediumPaged KV Cache GatherMediumGradient Accumulation with ScanHardBlockwise Attention with Online SoftmaxHardRing All-Reduce with ppermuteHardPad and Mask to Bucket LengthMediumHessian-Vector ProductMediumPer-Example Gradient ClippingMediumStraight-Through Estimator with Custom VJPMediumTraced Learning Rate ScheduleMediumExponential Moving Average with ScanMedium

Accepts: array

Accepts: array

Accepts: array

Accepts: object

You must run your code first.