Implement greedy decoding and beam search
Company: Microsoft
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of autoregressive decoding strategies—greedy decoding and beam search—including cumulative log-probability scoring, beam maintenance, EOS handling, and sequence expansion logic; it is categorized under Coding & Algorithms for Software Engineer roles.
Part 1: Greedy Decoding from Prefix Log-Probabilities
Constraints
- 0 <= max_len <= 200
- 0 <= len(prompt_tokens) <= 200
- 0 <= total number of next-token scores across all prefixes <= 10^4
- All tokens, including `eos`, are non-empty strings without spaces
- Larger log-probability means a more likely next token
Examples
Input: ({'': {'I': -0.1, 'You': -0.4}, 'I': {'like': -0.2, 'am': -0.3}, 'I like': {'pizza': -0.1, 'music': -0.5}, 'I like pizza': {'<EOS>': -0.05}}, [], 5, '<EOS>')
Expected Output: ['I', 'like', 'pizza', '<EOS>']
Explanation: Greedy decoding always picks the highest-scoring next token until EOS is generated.
Input: ({'Hello': {'world': -0.1}, 'Hello world': {'again': -0.2}}, ['Hello'], 2, '<EOS>')
Expected Output: ['Hello', 'world']
Explanation: The sequence stops once total length reaches `max_len`, even though more transitions exist.
Input: ({'done <EOS>': {'extra': -0.1}}, ['done', '<EOS>'], 5, '<EOS>')
Expected Output: ['done', '<EOS>']
Explanation: If the prompt already ends with EOS, return it unchanged.
Input: ({'': {'go': -0.1}}, ['start'], 4, '<EOS>')
Expected Output: ['start']
Explanation: There is no transition for the prefix `'start'`, so decoding stops immediately.
Input: ({'': {'b': -0.2, 'a': -0.2}, 'a': {'<EOS>': -0.1}}, [], 2, '<EOS>')
Expected Output: ['a', '<EOS>']
Explanation: When probabilities tie, choose the lexicographically smaller token.
Hints
- At each step, build the current prefix key with `' '.join(current_tokens)`.
- To make ties deterministic, compare by highest log-probability first and token string second.
Part 2: Beam Search with Top-k Prefixes
Constraints
- 1 <= k <= 50
- 0 <= max_len <= 200
- 0 <= len(prompt_tokens) <= 200
- 0 <= total number of next-token scores across all prefixes <= 10^4
- All tokens, including `eos`, are non-empty strings without spaces
- Larger cumulative log-probability means a better beam
Examples
Input: ({'': {'A': -0.1, 'B': -0.2}, 'A': {'x': -2.0, '<EOS>': -10.0}, 'A x': {'<EOS>': -0.1}, 'B': {'y': -0.1}, 'B y': {'<EOS>': -0.1}}, [], 4, '<EOS>', 2)
Expected Output: ['B', 'y', '<EOS>']
Explanation: Keeping two beams allows the search to recover from the locally best first token and choose the better total sequence.
Input: ({'': {'a': -0.1, 'b': -0.2}, 'a': {'<EOS>': -0.1}, 'b': {'c': -0.05}, 'b c': {'<EOS>': -0.05}}, [], 3, '<EOS>', 2)
Expected Output: ['a', '<EOS>']
Explanation: The finished beam `['a', '<EOS>']` remains in the beam set and still wins overall.
Input: ({}, [], 5, '<EOS>', 3)
Expected Output: []
Explanation: With no transitions at all, the initial prompt is a dead-end beam and is returned.
Input: ({'': {'a': -0.1, 'b': -0.1}, 'a': {'<EOS>': -0.1}, 'b': {'<EOS>': -0.1}}, [], 2, '<EOS>', 2)
Expected Output: ['a', '<EOS>']
Explanation: Both completed sequences have the same score, so the lexicographically smaller one is returned.
Input: ({'': {'x': -0.1}}, ['done', '<EOS>'], 5, '<EOS>', 2)
Expected Output: ['done', '<EOS>']
Explanation: If the prompt already ends with EOS, beam search should return it unchanged.
Hints
- Each beam needs both its token list and its cumulative score.
- Finished beams should stay in the candidate set unchanged; only unfinished beams are expanded.