2.1. List Decoding of Polar Codes 2015
2.1. List Decoding of Polar Codes 2015
the information bits vector (including the frozen bits), and by Algorithm 1: A High-Level Description of the
c = c0n−1 the corresponding codeword, which is sent over SC Decoder
a binary-input channel W : X → Y, where X = {0, 1}. Input: the received vector y
Output: a decoded codeword ĉ
At the other end of the channel, we get the received word
y = y0n−1 . A decoding algorithm is then applied to y, resulting 1 for ϕ = 0, 1, . . . , n − 1 do
(ϕ) ϕ−1 (ϕ) ϕ−1
in a decoded codeword ĉ having corresponding information 2 calculate Wm (y0n−1 , û0 |0) and Wm (y0n−1 , û0 |1)
bits û. Note that our use of the sans-serif font is reserved 3 if uϕ is frozen then
4 set ûϕ to the frozen value of uϕ
only for the above vectors of length n; vectors resulting from 5 else
recursive definitions will have a different font. (ϕ) ϕ−1 (ϕ) ϕ−1
6 if Wm (y0n−1 , û0 |0) > Wm (y0n−1 , û0 |1) then
7 set ûϕ ← 0
A. An Outline of Successive Cancellation 8 else
In essence, the SC algorithm is an efficient method of 9 set ûϕ ← 1
calculating n = 2m probability pairs, corresponding to n
recursively defined channels. A high-level description of the 10 return the codeword ĉ corresponding to û
SC decoding algorithm is given in Algorithm 1. In words, for
ϕ = 0, 1, . . . , n −1, we must calculate the pair of probabilities
(ϕ) ϕ−1 (ϕ) ϕ−1
Wm (y0n−1 , û0 |0) and Wm (y0n−1 , û0 |1), defined shortly. B. Detailed Description
Then, we must make a decision as to the value of ûϕ according For Algorithm 1 to become well defined, we must now
to the more likely probability. (ϕ)
(ϕ) specify how the probability pair associated with Wm is calcu-
We now proceed to define bit-channels Wλ , where lated computationally. As was observed in [1], the calculations
λ
0 ≤ λ ≤ m and 0 ≤ ϕ < 2 . In order to aid in the exposition, implied by the recursions (4) and (5) can be reused in a
we name index ϕ the phase and index λ the layer. For the dynamic programming fashion [4, Ch. 15]. Following this line,
sake of brevity, for layer 0 ≤ λ ≤ m denote hereafter we now show an implementation that is straightforward, yet
= 2λ . (1) somewhat wasteful in terms of space.
For λ > 0 and 0 ≤ ϕ < , recall the recursive definition of
Thus, (ϕ) ϕ−1
Wλ (y0−1 , u0 |u ϕ ) given in either (4) or (5), depending on
0 ≤ ϕ < . (2) the parity of ϕ. For either ϕ = 2ψ or ϕ = 2ψ + 1, the channel
(ψ) /2−1 2ψ−1 2ψ−1
(ϕ)
Bit channel Wλ is a binary input channel with output Wλ−1 is evaluated with output (y0 , u0,even ⊕ u0,odd ), as
−1 2ψ−1
alphabet Y × X ϕ , the conditional probability
of which we well as with output (y/2 , u0,odd ). Since our algorithm will
generically denote as make use of these recursions, we need a simple way of defining
(ϕ) ϕ−1 which output we are referring to. We do this by specifying,
Wλ (y0−1 , u0 |u ϕ ). (3)
apart from the layer λ and the phase ϕ which define the
In our context, y0−1is always a contiguous subvector of channel, the branch number
the received vector y. We next cite the recursive bit-channel
equations [1, eq. (22) and (23)], and note that the “branch” 0 ≤ β < 2m−λ . (6)
parts will be shortly explained. Let 0 ≤ 2ψ < , then Definition 1 (Association of Branch Number With Output):
branch β Since, during the run of the SC algorithm, the last-layer
(2ψ) (ϕ)
Wλ (y0−1 , u0
2ψ−1
|u 2ψ ) channel Wm
ϕ−1
is only evaluated with a single
1 (ψ) /2−1 2ψ−1 2ψ−1
output,1 (y0n−1 , û0 ), we give a branch number of β = 0 to
= Wλ−1 (y0 , u0,even ⊕ u0,odd |u 2ψ ⊕ u 2ψ+1 ) each such output. Next, we proceed recursively as follows.
2 (ϕ) ϕ−1
u 2ψ+1 For λ > 0, consider a channel Wλ with output (y0−1 , û0 )
branch 2β
(ψ)
and corresponding branch number β. Denote ψ = ϕ/2. The
−1 2ψ−1
Wλ−1 (y/2 , u0,odd |u 2ψ+1 ) (4) output (y0
/2−1 2ψ−1 2ψ−1 (ψ)
, û0,even ⊕ û0,odd ) associated with Wλ−1 will
·
branch 2β + 1 −1 2ψ−1
have a branch number of 2β, while the output (y/2 , û0,odd )
and will have a branch number of 2β + 1. Finally, we mention
branch β that for the sake of brevity, we will talk about the output
corresponding to branch β of a channel, although this is
(2ψ+1) −1 2ψ
Wλ (y0 , u0 |u 2ψ+1 ) slightly inaccurate.
1 (ψ) /2−1 2ψ−1 2ψ−1 We now introduce our first data structure. For each layer
= Wλ−1 (y0 , u0,even ⊕ u0,odd |u 2ψ ⊕ u 2ψ+1 )
2 0 ≤ λ ≤ m, we will have a probabilities array, denoted by Pλ ,
branch 2β indexed by an integer 0 ≤ i < 2m and a bit b ∈ {0, 1}.
(ψ) −1 2ψ−1
Wλ−1 (y/2 , u0,odd |u 2ψ+1 ) (5) 1 Recall that since the sans-serif font is used, y = yn−1 is the received
· 0
word corresponding to the codeword c = cn−1 sent over the physical channel,
branch 2β + 1 ϕ−1
0
while û0 corresponds to the first ϕ information plus frozen bits defining the
(0)
with “stopping condition” W0 (y|u) = W (y|u). decoded codeword: ĉ = G û, where G is the n × n Arıkan generator matrix.
2216 IEEE TRANSACTIONS ON INFORMATION THEORY, VOL. 61, NO. 5, MAY 2015
For a given layer λ, an index i will correspond to a phase Algorithm 2: First Implementation of SC Decoder
0 ≤ ϕ < and branch 0 ≤ β < 2m−λ using the following Input: the received vector y
quotient/reminder representation. Output: a decoded codeword ĉ
1 for β = 0, 1, . . . , n − 1 do // Initialization
i =
ϕ, βλ = ϕ + 2λ · β. (7) 2 P0 [
0, β][0] ← W (yβ |0), P0 [
0, β][1] ← W (yβ |1)
In order to avoid repetition, we use the following shorthand 3 for ϕ = 0, 1, . . . , n − 1 do // Main loop
4 recursivelyCalcP(m, ϕ)
Pλ [
ϕ, β] = Pλ [
ϕ, βλ ]. (8) 5 if uϕ is frozen then
6 set Bm [
ϕ, 0] to the frozen value of uϕ
The probabilities array data structure Pλ will be used as 7 else
follows. Let a layer 0 ≤ λ ≤ m, phase 0 ≤ ϕ < , and branch 8 if Pm [
ϕ, 0][0] > Pm [
ϕ, 0][1] then
0 ≤ β < 2m−λ be given. Denote the output corresponding to 9 set Bm [
ϕ, 0] ← 0
(ϕ) ϕ−1 else
branch β of Wλ as (y0−1 , û0 ). Then, ultimately, we will
10
11 set Bm [
ϕ, 0] ← 1
have for both values of b that
(ϕ) ϕ−1
12 if ϕ mod 2 = 1 then
Pλ [
ϕ, β][b] = Wλ (y0−1 , û0 |b). (9) 13 recursivelyUpdateB(m, ϕ)
Definition 2 (Association of Branch Number With Input): 14 return the decoded codeword: ĉ = (B0 [
0, β])n−1
β=0
Analogously to defining the output corresponding to a
branch β, we now define the input corresponding to a branch.
As in the “output” case, we start at layer m and continue Algorithm 3: recursivelyCalcP(λ, ϕ) Implementation I
recursively according to (4) and (5). That is, consider the Input: layer λ and phase ϕ
(ϕ) if λ = 0 then return // Stopping condition
channel Wm . Let ûϕ be the corresponding input which 1
2 set ψ ← ϕ/2
Algorithm 1 sets, either in line 7 or in line 9. With respect
(ϕ) // Recurse first, if needed
to Wm , we let this input have a branch number of β = 0. 3 if ϕ mod 2 = 0 then recursivelyCalcP(λ − 1, ψ)
Next, we proceed recursively as follows. For layer λ > 0 and 4 for β = 0, 1, . . . , 2m−λ − 1 do // calculation
0 ≤ ψ < 2λ−1 , let u 2ψ and u 2ψ+1 be the inputs corresponding 5 if ϕ mod 2 = 0 then // apply Equation (4)
to branch 0 ≤ β < 2m−λ of Wλ
(2ψ)
and Wλ
(2ψ+1)
, respectively. 6 for u ∈ {0, 1} do
Then, in light of (5), we define the inputs corresponding to 7 Pλ [
ϕ, β][u ] ← 1
u 2 Pλ−1 [
ψ, 2β][u ⊕ u ] ·
(ψ) 8 Pλ−1 [
ψ, 2β + 1][u ]
branches 2β and 2β + 1 of Wλ−1 as u 2ψ ⊕ u 2ψ+1 and u 2ψ+1 ,
respectively. 9 else // apply Equation (5)
10 set u ← Bλ [
ϕ − 1, β]
The following lemma points at the natural meaning that
11 for u ∈ {0, 1} do
a branch number has at layer λ = 0. It is proved using a
12 Pλ [
ϕ, β][u ] ← 21 Pλ−1 [
ψ, 2β][u ⊕ u ] ·
straightforward induction. 13 Pλ−1 [
ψ, 2β + 1][u ]
Lemma 1: Let y and ĉ be as in Algorithm 1, the received
vector and the decoded codeword. Consider layer λ = 0, and
thus set ϕ = 0. Next, fix a branch number 0 ≤ β < 2n . Then,
the input and output corresponding to branch β of W0(0) are Algorithm 4: recursivelyUpdateB(λ, ϕ)
yβ and ĉβ , respectively. Implementation I
We now introduce our second, and last, data structure for Require : ϕ is odd
this section. For each layer 0 ≤ λ ≤ m, we will have a bit 1 set ψ ← ϕ/2
array, denoted by Bλ , and indexed by an integer 0 ≤ i < 2m , 2 for β = 0, 1, . . . , 2m−λ − 1 do
as in (7). The data structure will be used as follows. Let layer 3 Bλ−1 [
ψ, 2β] ← Bλ [
ϕ − 1, β] ⊕ Bλ [
ϕ, β]
4 Bλ−1 [
ψ, 2β + 1] ← Bλ [
ϕ, β]
0 ≤ λ ≤ m, phase 0 ≤ ϕ < , and branch 0 ≤ β < 2m−λ be
given. Denote the input corresponding to branch β of Wλ as
(ϕ) 5 if ψ mod 2 = 1 then
6 recursivelyUpdateB(λ − 1, ψ)
û(λ, ϕ, β). Then, we will ultimately have that
Bλ [
ϕ, β] = û(λ, ϕ, β), (10)
where we have used the same shorthand as in (8). Notice that Proof: We first note that in addition to proving the
the total memory consumed by our algorithm is O(n log n). claim explicitly stated in the lemma, we must also prove an
Our first implementation of the SC decoder is given implicit claim. Namely, we must prove that the actions taken
as Algorithms 2–4. The main loop is given in Algorithm 2, and by the algorithm are well defined. Specifically, we must prove
follows the high-level description given in Algorithm 1. Note that when an array element is read from, it was already written
that the elements of the probabilities arrays Pλ and bit array Bλ to (it is initialized).
start-out uninitialized, and become initialized as the algorithm Both the implicit and explicit claims are easily derived from
runs its course. The code to initialize the array values is given the following observation. For a given 0 ≤ ϕ < n, consider
in Algorithms 3 and 4. iteration ϕ of the main loop in Algorithm 2. Fix a layer
Lemma 2: Algorithms 2–4 are a valid implementation of 0 ≤ λ ≤ m, and a branch 0 ≤ β < 2m−λ . If we suspend the
the SC decoder. run of the algorithm just after the iteration ends, then (9) holds
TAL AND VARDY: LIST DECODING OF POLAR CODES 2217
with ϕ instead of ϕ, for all disregard the value of ψ and take note only of the parity of ϕ
ϕ (the proof is essentially the same as before, and left to the
0 ≤ ϕ ≤ m−λ . reader). So, let us make one more substitution: replace every
2
instance of Cλ [ψ + β · 2λ−1 ][ϕ mod 2] by Cλ [β][ϕ mod 2],
Similarly, (10) holds with ϕ instead of ϕ, for all
and resize each array Cλ to have 2m−λ bit pairs. To sum up,
ϕ+1
0 ≤ ϕ < . Bλ [
ϕ, β] is replaced by Cλ [β][ϕ mod 2]. (12)
2m−λ
The above observation is proved by induction on ϕ. The alert reader will notice that a further reduction in space
is possible: for λ = 0 we will always have that ϕ = 0, and
III. S PACE -E FFICIENT S UCCESSIVE thus the parity of ϕ is always even. However, this reduction
C ANCELLATION D ECODING does not affect the asymptotic space complexity which is now
The running time of the SC decoder is O(n log n), and our indeed down to O(n).
implementation is no exception. As we have previously noted, We end this subsection by mentioning that although we were
the space complexity of our algorithm is O(n log n) as well. concerned here with reducing the space complexity of our SC
However, we will now show how to bring the space complexity decoder, the observations made with this goal in mind will
down to O(n). The observation that one can reduce the space be of great use in analyzing the time complexity of our list
complexity to O(n) was noted, in the context of VLSI design, decoder.
in [7].
As a first step towards this end, consider the probability IV. S UCCESSIVE C ANCELLATION L IST D ECODER
pair array Pm . By examining the main loop in Algorithm 2, In this section we introduce and define our algorithm, the
we quickly see that if we are currently at phase ϕ, then we successive cancellation list (SCL) decoder. Our list decoder
will never again make use of Pm [
ϕ , 0] for all ϕ < ϕ. has a parameter L, called the list size. Generally speaking,
On the other hand, we see that Pm [
ϕ , 0] is uninitialized larger values of L mean lower error rates but longer running
for all ϕ > ϕ. Thus, instead of reading and writing to times and larger memory usage. We note at this point that
Pm [
ϕ, 0], we can essentially disregard the phase information, successive cancellation list decoding is not a new idea: it was
and use only the first element Pm [0] of the array, discarding applied in [5] to Reed-Muller codes.
all the rest. By the recursive nature of polar codes, this Recall the main loop of an SC decoder, where at each phase
observation — disregarding the phase information — can be we must decide on the value of ûϕ . In an SCL decoder, instead
exploited for a general layer λ as well. The following lemma of deciding to set the value of an unfrozen ûϕ to either a
makes the above claims formal. The proof follows easily from 0 or a 1, we inspect both options. That is, let a “path” be a
Line 2 of Algorithm 3, and by noting that ϕ
certain decision on the values of û0 , for 0 ≤ ϕ < n. When
ϕ/2i /2 = ϕ/2i+1 . decoding a non-frozen bit ûϕ+1 , we split the decoding path
ϕ
û0 into two paths (see Figure 4). Both of the new paths will
Lemma 3: During iteration ϕ of the main loop of Algo- ϕ
have û0 as a prefix. One new path will end with “0” while
rithm 2, the only elements of the arrays Pλ which are possibly the other ends in “1”. Since each split doubles the number of
read from or written to have the form Pλ [
ϕ , β][0] and paths to be examined, we must prune them, and the maximum
Pλ [
ϕ , β][1], where 0 ≤ λ ≤ m, 0 ≤ ϕ < 2λ , 0 ≤ β < 2m−λ , number of paths allowed is the specified list size, L. Naturally,
and ϕ = ϕ/2m−λ . we would like to keep the “best” paths at each stage, and thus
With the above lemma at hand, and since ϕ/2i is a non- require a pruning criterion. Our pruning criterion will be to
decreasing function of ϕ, we are justified to carry out the keep the most likely paths.
following alternation of the algorithm. For all 0 ≤ λ ≤ m, Figure 5 considers the same decoding run depicted
let us now define the number of elements in Pλ to be 2m−λ . in Figure 4 and tracks the evolution of how the Cλ arrays
Accordingly, are allocated and used. Each sub-figure represents the state
of the Cλ arrays at a different stage (but note that the
Pλ [
ϕ, β] is replaced by Pλ [β]. (11)
j th subfigure in Figure 4 generally does not correspond to
This change does not affect the final output of the algorithm. the j th subfigure in Figure 5). Paths over an array repre-
Note that the total space needed to hold the P arrays has sent the assignment information encoded (redundantly) in the
gone down from O(n log n) to O(n). We would now like to do activePath, pathIndexToArrayIndex, inactiveArrayIndices,
the same for the B arrays. However, as things are currently and arrayReferenceCount data structures. A “∅” designates
stated, we can not disregard the phase, as can be seen for an array not assigned to any path. We now expand on the
example in Line 3 of Algorithm 4. The solution is a simple various sub-figures of Figure 5. Note that the following
renaming. As a first step, let us define for each 0 ≤ λ ≤ m an list contains references to algorithms defined later on; on a
array Cλ consisting of bit pairs and having length n/2. Next, first read, the message we want to get across is that paths
let a generic reference of the form Bλ [
ϕ, β] be replaced by with a common prefix can typically share non-empty arrays.
Cλ [ψ + β · 2λ−1 ][ϕ mod 2], where ψ = ϕ/2. Note that we This is clearly seen, for example, in Subfigure 5(e). In what
have done nothing more than rename the elements of Bλ as follows, when we refer to the “main loop”, we mean the
elements of Cλ . However, we now see that as before we can for-loop in Algorithm 12.
2218 IEEE TRANSACTIONS ON INFORMATION THEORY, VOL. 61, NO. 5, MAY 2015
Fig. 4. Evolution of decoding paths. We assume for simplicity that n = 4 and all bits are unfrozen. The list size is L = 4: each level has at most
4 nodes with paths that continue downward. Discontinued paths are colored gray. (i) Algorithm starts. First unfrozen bit can be either 0 or 1. (ii) Algorithm
continues. Second unfrozen bits can be either 0 or 1. The number of paths is not more than L = 4, so no need to prune yet. (iii) Considering all options for
first, second, and third bits results in 8 decoding paths; too much, since L = 4. (iv) Prune the 8 paths into L = 4 most promising paths. (v) Continue the
4 active paths by considering both options of the fourth unfrozen bit. The number of paths doubles to 8, which is too much (L = 4). (vi) Again, prune to
L = 4 best paths.
Fig. 5. Evolution of the usage of Cλ arrays. The example run is the same as in Figure 4. (a) ϕ = 0, state at the start of main loop. One active path, π0 ,
which is empty. (b) ϕ = 0, state at the end of the main loop. Two active paths, π0 = 0, π1 = 1. (c) ϕ = 1, state at the end of the main loop. Four active
paths, π0 = 00, π1 = 10, π2 = 01, π3 = 11. Also state when ϕ = 2 and Line 7 of the main loop has executed. (d) ϕ = 2, state after Line 14 of the main
loop has been called, and we are before the first execution of Line 19 in Algorithm 13. At this stage, π0 has been killed off, and the only active paths are
π1 = 10, π2 = 01, π3 = 11. (e) ϕ = 2, state at the end of the main loop. Four active paths, π0 = 011, π1 = 100, π2 = 010, π3 = 111. (f) ϕ = 3, state at
the end of the main loop. Four active paths, π0 = 0110, π1 = 0111, π2 = 0100, π3 = 1111.
(a) Here, ϕ = 0 and we are at the start of Line 2 of Algorithm 12, before the main loop
main loop. We have one active path, π0 , which started.
is empty. The path was assigned C2 , C1 and C0 (b) Now, we still have ϕ = 0, but have reached the
arrays by the assignInitialPath function on end of the main loop. There are two active paths,
TAL AND VARDY: LIST DECODING OF POLAR CODES 2219
π0 = 0, π1 = 1 (as is also depicted in Subfigure 4(i)). C2 array assigned to it has first entry equal to 1, we
The current path π1 is the result of the clonePath deduce that the corresponding u 2 bit equals 1. Since the
operation applied to the path π0 of the previous sub- C1 array has entries α = 1, β = 1, we deduce that
figure. This operation was carried out on Line 25 of u 0 = α + β = 0 and u 1 = β = 1.
Algorithm 13. Initially, both paths shared all C arrays, (f) End of main loop, with ϕ = 3 (Subfigure 4(vi)). Four
as specified in the clonePath operation. However, at active paths, π0 = 0110, π1 = 0111, π2 = 0100,
the current stage, both paths have private C2 arrays, π3 = 1111. As before, one path was killed (previous π1 ),
because of the getArrayPointer_C call on Line 26 one path was cloned and split into two paths (previous
of Algorithm 13. π0 split into current π0 and current π1 ), and two paths
(c) We are at the end of the main loop, with ϕ = 1. had one surviving branch each (π2 and π3 ). Note that the
We have four active paths, π0 = 00, π1 = 10, π2 = 01, function recursivelyUpdateC is called in Line 16
π3 = 11 (Subfigure 4(ii)). The current π2 path is the of Algorithm 12. Since ϕ = 3 this results in two
result of applying clonePath on the previous π0 , in recursive calls. Thus, each path has a private copy of
Line 25 of Algorithm 13. The same holds true with C2 , C1 , and C0 . The codewords corresponding to each
respect to the current π3 and the previous π1 . As in the path are now stored in the C0 arrays.
previous sub-figure, each path has a distinct C2 array, by Consider the following outline for a naive implementation
virtue of the getArrayPointer_C call on Line 26 of of an SCL decoder. Each time a decoding path is split into
Algorithm 13. Since ϕ = 1, recursivelyUpdateC two forks, the data structures used by the “parent” path are
is called on Line 16 of Algorithm 12, resulting in each duplicated, with one copy given to the first fork and the other
path updating (Lines 7–8, Algorithm 11) a private copy to the second. Since the number of splits is
(L ·n), and since
(Line 5, Algorithm 11) of C1 . the size of the data structures used by each path is
(n), the
We next note that sub-figure 5 also represents copying operation alone would take time
(L ·n 2 ). In fact, the
the state we are at when ϕ = 2 and Line 7 running time is easily seen to be (L·n 2 ). This running time is
of the main loop has executed. We might expect clearly impractical for all but the shortest of codes. However,
the state to have changed, following the call to all known (to us) implementations of successive cancellation
recursivelyCalcP, which contains a call to list decoding have complexity at least
(L · n 2 ). Our main
getArrayPointer_C (Line 9, Algorithm 10). More- contribution in this section is the following: we show how to
over, since ϕ = 2, recursivelyCalcP calls itself implement SCL decoding with time complexity O(L · n log n)
recursively once, which results in a second set of calls instead of
(L · n 2 ).
to getArrayPointer_C. However, by the previous The key observation is as follows. Consider the P arrays of
paragraph, we know that at the end of the main loop the last section, and recall that the size of Pλ is proportional
each path already had a private copy of its C2 and C1 to 2m−λ . Thus, the cost of copying Pλ grows exponentially
arrays. Thus, the calls to getArrayPointer_C do small with λ. Next, consider the main loop of Algorithm 2.
not change anything in this respect. Unwinding the recursion, we see that Pλ is accessed only every
(d) We are now in the middle of executing Line 14 of the 2m−λ increments of ϕ. Obviously, this is still the case after
main loop, with ϕ = 2 (see Subfigure 4(iv) for a partial the size-reduction replacment given in (11) is carried out. Put
depiction). Namely, the “kill-loop” in Algorithm 13 has another way, the bigger Pλ is, the less frequently it is accessed.
just finished, and we are before the first execution of The same observation applies to the C arrays. This observation
Line 19 in Algorithm 13. At this stage, π0 has been suggest the use of a so-called “lazy-copy” implementation.
killed off, and the only active paths are π1 = 10, Briefly, at each given stage, the same array may be flagged
π2 = 01, π3 = 11. This results in free C2 and C1 arrays. as belonging to more than one decoding path. However, when
Array C2 will be re-assigned shortly, followed by C1 . a given decoding path needs access to an array it is sharing
(e) End of the main loop, with ϕ = 2 (Subfigure 4(iv)). We with another path, a copy is made. The copy is initially private,
are back to four active paths, π0 = 011, π1 = 100, belonging only to the path that must access it. However, as the
π2 = 010, π3 = 111. As mentioned, the previous algorithm progresses (ϕ increases), the array may be shared by
π0 was killed (Line 18, Algorithm 13). On the other several paths that are descendants (continuations) of the path
hand, the current π0 and π2 are decedents of the previ- which needed initial private access. The following sub-sections
ous π2 . Namely, the current π0 is the result of applying are concerned with exactly specifying how this lazy-copy is
clonePath on previous π2 . Thus, both π0 and π2 implemented, as well as with proving the implementation is
share the same C2 array. However, the current π0 is valid and analyzing its performance.
assigned a private C2 array, de-assigned by the previous
kill operation. The current path π1 is a continuation A. Low-Level Functions
of the previous π1 (only one branch was followed, in We now discuss the low-level functions and data structures
Line 30 of Algorithm 13). The same is true with respect by which the “lazy-copy” methodology is realized. We note
to the current and previous π3 , with Line 32 in place in advance that since our aim was to keep the exposition as
of 30. simple as possible, we have avoided some obvious optimiza-
Note that each path is currently encoded in the tions. The following data structures are defined and initialized
C2 and C1 arrays. For example, consider π0 . Since the in Algorithm 5.
2220 IEEE TRANSACTIONS ON INFORMATION THEORY, VOL. 61, NO. 5, MAY 2015
Algorithm 9: getArrayPointer_P(λ, ) (t )
Define #killPath similarly. Then, for every 1 ≤ t ≤ L, we
Input: layer λ and path index require that
Output: pointer to corresponding probability pair array
(t ) (t )
// getArrayPointer_C(λ, ) is defined 1 ≤ 1 + #clonePath − #killPath ≤ L. (13)
identically, up to the obvious changes
in lines 6 and 10 Active: We say that path is active at the end of stage
1 s ← pathIndexToArrayIndex[λ][] 1 ≤ t ≤ T if the following two conditions hold. First, there
2 if arrayReferenceCount[λ][s] = 1 then exists an index 1 ≤ i ≤ t for which f i is either clonePath
3 s ← s
4 else with corresponding output or assignInitialPath with
5 s ← pop(inactiveArrayIndices[λ]) output . Second, there is no intermediate index i < j ≤ t for
6 copy the contents of the array pointed to by which f j is killPath with input . For each 1 ≤ t < T we
arrayPointer_P[λ][s] into that pointed to by require that if f t +1 has input , then is active at the end of
arrayPointer_P[λ][s ] stage t.
7 arrayReferenceCount[λ][s]−−
We start by stating that the most basic thing one would
8 arrayReferenceCount[λ][s ] ← 1
9 pathIndexToArrayIndex[λ][] ← s expect to hold does indeed hold.
Lemma 4: Let ( f t )tT=0 be a valid sequence of calls to the
10 return arrayPointer_P[λ][s ]
low-level functions implemented in Algorithms 5–9. Then, the
run is well defined: i) A “pop” operation is never carried out
on a empty stack, ii) a “push” operation never results in a
have their reference count decreased by one. The goal of stack with more than L elements, and iii) a “read” operation
all previously discussed low-level functions was essentially from any array defined in lines 2–7 of Algorithm 5 is always
to enable the abstraction implemented by the functions preceded by a “write” operation to the same location in the
getArrayPointer_P and getArrayPointer_C. The array.
function getArrayPointer_P is called each time a higher- Proof: The proof boils-down to proving the following
level function needs to access (either for reading or writing) four statements concurrently for the end of each step
the probability-pair array associated with a certain path and 1 ≤ t ≤ T , by induction on t.
layer λ. The implementation of getArrayPointer_P is I A path index is active by Definition 3 iff
give in Algorithm 9. There are two cases to consider: either activePath[] is true iff inactivePathIndices does
the array is associated with more than one path or it is not. not contain the index .
If it is not, then nothing needs to be done, and we return a II The bracketed expression in (13) is the number of
pointer to the array. On the other hand, if the array is shared, active paths at the end of stage t.
we make a private copy for path , and return a pointer to that III The value of arrayReferenceCount[λ][s] is
copy. By doing so, we ensure that two paths will never write positive iff the stack inactiveArrayIndices[λ]
to the same array. The function getArrayPointer_C is does not contain the index s, and is zero
used in the same manner for bit-pair arrays, and has exactly otherwise.
the same implementation, up to the obvious changes. IV The value of arrayReferenceCount[λ][s] is equal
At this point, we remind the reader that we are deliberately to the number of active paths for which
sacrificing speed for simplicity. Namely, each such function pathIndexToArrayIndex[λ][] = s.
is called either before reading or writing to an array, but the We are now close to formalizing the utility of our low-
copy operation is really needed only before writing. level functions. But first, we must formalize the concept of a
We have now finished defining almost all of our low-level descendant path. Let ( f t )tT=0 be a valid sequence of calls. Next,
functions. At this point, we should specify the constraints let be an active path index at the end of stage 1 ≤ t < T .
one should follow when using them and what one can Henceforth, let us abbreviate the phrase “path index at the
expect if these constraints are met. We start with the end of stage t” by “[, t]”. We say that [ , t + 1] is a child of
former. [, t] if i) is active at the end of stage t + 1, and ii) either
Definition 3 (Valid Calling Sequence): Consider a sequ- = or ft +1 was the clonePath operation with input
ence ( f t )tT=0 of T + 1 calls to the low-level functions imple- and output . Likewise, we say that [ , t ] is a descendant of
mented in Algorithms 5–9. We say that the sequence is valid [, t] if 1 ≤ t ≤ t and there is a (possibly empty) hereditary
if the following traits hold. chain.
Initialized: The one and only index t for which f t is equal We now broaden our definition of a valid func-
to initializeDataStructures is t = 0. The one and tion calling sequence by allowing reads and writes to
only index t for which f t is equal to assignInitialPath arrays.
is t = 1. Fresh Pointer: consider the case where t > 1 and f t is either
Balanced: For 1 ≤ t ≤ T , denote the number of times the getArrayPointer_P or getArrayPointer_C
the function clonePath was called up to and including function with input (λ, ) and output p. Then, for valid indices
stage t as i , we allow read and write operations to p[i ] after stage
t but only before any stage t > t for which f t is either
(t )
#clonePath = | {1 ≤ i ≤ t : f i is clonePath} |. clonePath or killPath.
2222 IEEE TRANSACTIONS ON INFORMATION THEORY, VOL. 61, NO. 5, MAY 2015
Informally, the following lemma states that each path Algorithm 10: recursivelyCalcP(λ, ϕ) List Version
effectively sees a private set of arrays. Input: layer λ and phase ϕ
Lemma 5: Let ( f t )tT=0 be a valid sequence of calls to the 1 if λ = 0 then return // Stopping condition
low-level functions implemented in Algorithms 5–9. Assume 2 set ψ ← ϕ/2
the read/write operations between stages satisfy the “fresh // Recurse first, if needed
3 if ϕ mod 2 = 0 then recursivelyCalcP(λ − 1, ψ)
pointer” condition. // Perform the calculation
Let the function f t be getArrayPointer_P with input 4 σ ←0
(λ, ) and output p. Similarly, for stage t ≥ t, let f t be 5 for = 0, 1, . . . , L − 1 do
getArrayPointer_P with input (λ, ) and output p . 6 if activePath[] = false then continue
Assume that [ , t ] is a descendant of [, t]. 7 Pλ ← getArrayPointer_P(λ, )
8 Pλ−1 ← getArrayPointer_P(λ − 1, )
Consider a “fresh pointer” write operation to p[i ]. Similarly, 9 Cλ ← getArrayPointer_C(λ, )
consider a “fresh pointer” read operation from p [i ] carried out 10 for β = 0, 1, . . . , 2m−λ − 1 do
after the “write” operation. Then, assuming no intermediate 11 if ϕ mod 2 = 0 then
“write” operations of the above nature, the value written is // apply Equation (4)
the value read. 12 for u ∈ {0, 1} do
P [β][u ] ←
λ 1
13
A similar claim holds for getArrayPointer_C.
Proof: With the observations made in the proof of [2β][u ⊕ u ] · Pλ−1 [2β + 1][u ]
u 2 Pλ−1
14 σ ← max σ, Pλ [β][u ]
Lemma 4 at hand, a simple induction on t is all that is
needed. 15 else // apply Equation (5)
16 set u ← Cλ [β][0]
17 for u ∈ {0, 1} do
B. Mid-Level Functions 18 Pλ [β][u ] ←
1P
2 λ−1 [2β][u ⊕ u ] ·Pλ−1 [2β + 1][u ]
In this section we introduce Algorithms 10 and 11, our
revised implementation of Algorithms 3 and 4, respectively, 19 σ ← max σ, Pλ [β][u ]
for the list decoding setting.
One first notes that our new implementations loop // normalize probabilities
over all path indices . Thus, our new implementations // In no-normalization variant, set σ to 1
make use of the functions getArrayPointer_P and here
getArrayPointer_C in order to assure that the con- 20 for = 0, 1, . . . , L − 1 do
21 if activePath[] = false then continue
sistency of calculations is preserved, despite multiple paths 22 Pλ ← getArrayPointer_P(λ, )
sharing information. In addition, Algorithm 10 contains code 23 for β = 0, 1, . . . , 2m−λ − 1 do
to normalize probabilities. The normalization is needed for a 24 for u ∈ {0, 1} do
technical reason (to avoid floating-point underflow), and will 25 Pλ [β][u] ← Pλ [β][u]/σ
be expanded on shortly.
We start out by noting that the “fresh pointer” condition
we have imposed on ourselves indeed holds. To see this,
Algorithm 11: recursivelyUpdateC(λ, ϕ) List Version
consider first Algorithm 10. The key point to note is that
Input: layer λ and phase ϕ
neither the killPath nor the clonePath function is called
Require : ϕ is odd
from inside the algorithm. The same observation holds for
1 set ψ ← ϕ/2
Algorithm 11. Thus, the “fresh pointer” condition is met, and
2 for = 0, 1, . . . , L − 1 do
Lemma 5 holds. 3 if activePath[] = false then continue
We now consider the normalization step carried out in 4 set Cλ ← getArrayPointer_C(λ, )
lines 20–25 of Algorithm 10. Recall that a floating-point 5 set Cλ−1 ← getArrayPointer_C(λ − 1, )
variable can not be used to hold arbitrarily small positive reals, 6 for β = 0, 1, . . . , 2m−λ − 1 do
and in a typical implementation, the result of a calculation that 7 Cλ−1 [2β][ψ mod 2] ← Cλ [β][0] ⊕ Cλ [β][1]
8 Cλ−1 [2β + 1][ψ mod 2] ← Cλ [β][1]
is “too small” will be rounded to 0. This scenario is called an
“underflow”. 9 if ψ mod 2 = 1 then
We now confess that all our previous implementations of 10 recursivelyUpdateC(λ − 1, ψ)
SC decoders were prone to “underflow”. To see this, consider
line 2 in the outline implementation given in Algorithm 1.
Denote by Y and U the random vectors corresponding to y
and u, respectively. For b ∈ {0, 1} we have that comparison in line 2 of Algorithm 1 ultimately becomes
ϕ−1
meaningless when implemented using standard floating
Wm(ϕ) (y0n−1 , û0 |b) point arithmetic. The same holds for all of our previous
ϕ−1 ϕ−1
= 2 · P(Y0n−1 = y0n−1 , U0 = û0 , Uϕ = b) implementations.
ϕ−1 ϕ−1 −ϕ Fortunately, there is a solution to this problem. After
≤2 · P(U0 = û0 , Uϕ = b) = 2 .
the probabilities are calculated in lines 5–19 of Algo-
Recall that ϕ iterates from 0 to n − 1. Thus, for rithm 10, we normalize the highest probability to be 1
codes having length greater than some small constant, the in lines 20–25.
TAL AND VARDY: LIST DECODING OF POLAR CODES 2223
Algorithm 12: SCL Decoder, Main Loop the value of arrayPointer_P[λ][s] at the end of execution A.
Input: the received vector y and a list size L as a global Proof: Recall the following about Algorithm 10 (and
Output: a decoded codeword ĉ Algorithm 10’). When execution begins, a recursive call is
// Initialization made on line 3, if needed. Then, Pλ is transformed based
1 initializeDataStructures() on Pλ−1 and Cλ . Consider first the run of both algorithms
2 ← assignInitialPath()
3 P0 ← getArrayPointer_P(0, )
during the innermost recursive call (the corresponding input
4 for β = 0, 1, . . . , n − 1 do parameter λ is the same for both algorithms, by inspection).
5 set P0 [β][0] ← W (yβ |0), P0 [β][1] ← W (yβ |1) By lines 13, 18, and 25, the ratio between Pλ entries in
// Main loop both runs is simply (αλ−1 )2 , divided by the value of σ after
6 for ϕ = 0, 1, . . . , n − 1 do the main loop has finished executing. It is easily seen that
7 recursivelyCalcP(m, ϕ) σ > 0. Thus, after the innermost recursive call finishes in
8 if uϕ is frozen then both algorithms, the assumption on the proportionality of
9 for = 0, 1, . . . , L − 1 do
10 if activePath[] = false then continue
arrayPointer_P entries continues to hold. We now continue
11 Cm ← getArrayPointer_C(m, ) inductively: the claim is proved in much the same way for the
12 set Cm [0][ϕ mod 2] to the frozen value of uϕ pre-ultimate recursion, etc.
13 else
C. High-Level Functions
14 continuePaths_UnfrozenBit(ϕ)
15 if ϕ mod 2 = 1 then We now turn our attention to the high-level functions
16 recursivelyUpdateC (m, ϕ) of our algorithm. Consider the topmost function, given
in Algorithm 12. We start by noting that by lines 1 and 2,
// Return the best codeword in the list we have that condition “initialized” in Definition 3 is sat-
17 ← 0, p ← 0
18 for = 0, 1, . . . , L − 1 do isfied. Also, for the inductive basis, we have that condition
19 if activePath[] = false then continue “balanced” holds for t = 1 at the end of line 2. Next, notice
20 Cm ← getArrayPointer_C(m, ) that lines 3–5 are in-line with our “fresh pointer” condition.
21 Pm ← getArrayPointer_P(m, ) Next, consider lines 6–16, the main loop. These are the analog
22 if p < Pm [0][Cm [0][1]] then of the main loop in Algorithm 2, with the size-reduction
23 ← , p ← Pm [0][Cm [0][1]]
replacements as per (11) and (12). After the main loop has
24 set C0 ← getArrayPointer_C(0, ) finished, we pick (in lines 17–25) the most likely codeword
25 return ĉ = (C0 [β][0])n−1
β=0
from our list and return it.
Algorithm 13, continuePaths_UnfrozenBit, is the
analog of lines 8–11 in Algorithm 2. However, now, instead of
choosing the most likely fork out of 2 possible forks, we must
We claim that apart for avoiding underflows, normalization typically choose the L most likely forks out of 2L possible
does not alter our algorithm in that it does not change forks. The most interesting line is 14, in which the best ρ
the chosen codeword. To see this, consider a variant of forks are marked. Surprisingly2, this can be done in O(L)
Algorithm 10, termed Algorithm 10’, in which normalization time [4, Sec. 9.3]. After the forks are marked, we first kill the
is not carried out. That is, in Algorithm 10’, just before paths for which both forks are discontinued, and then continue
line 20, we set the variable σ to 1. The following lemma paths for which one or both of the forks are marked. In case
states that for all 0 ≤ λ ≤ m, both algorithm variants of the latter, the path is first split. Note that we must first kill
produce array entries arrayPointer_P[λ][s] which differ up paths and only then split paths in order for the “balanced”
to a positive normalization constants βλ . As can be seen in constraint (13) to hold. Namely, this way, we will not have
lines 17–25 of Algorithm 12 ahead, the returned codeword is more than L active paths at a time.
the result of comparing probabilities in arrayPointer_P[m]. The point of Algorithm 13 is to prune our list and leave only
Thus, normalization indeed does not alter the returned the L “best” paths. This is indeed achieved, in the following
codeword. sense. At stage ϕ we would like to rank each path according
Lemma 6: Let two program executions be defined as to the probability
follows. Execution A: Algorithm 10 is called with input ϕ−1
Wm(ϕ) (y0n−1 , û0 |ûϕ ).
parameters λ0 , ϕ0 , and a given state of the data structures.
Execution B: Algorithm 10’ is called with the same input By (9) and (11), this would indeed by the case if our
parameters as Execution A, and the same state of the data floating point variables were “perfect”, and the normalization
structures, apart from the following. There exist positive reals step in lines 20–25 of Algorithm 10 were not carried out.
α0 , α1 , . . . , αm such that for all 0 ≤ λ ≤ m and all 0 ≤ s < , By Lemma 6, we see that this is still the case if normalization
the value of arrayPointer_P[λ][s] at the start of Execution B is carried out.
is αλ times the value of arrayPointer_P[λ][s] at the start of With respect to the above, consider the last part of
execution A. Algorithm 12: rows 17–25, in which we claim to
Then, there exist positive reals β0 , β1 , . . . , βm such that 2 The O(L) time result is rather theoretical. Since L is typically a small
for all 0 ≤ λ ≤ m and all 0 ≤ s < , the value of number, the fastest way to achieve our selection goal would be through simple
arrayPointer_P[λ][s] at the end of Execution B is βλ times sorting.
2224 IEEE TRANSACTIONS ON INFORMATION THEORY, VOL. 61, NO. 5, MAY 2015
Algorithm 13: continuePaths_UnfrozenBit(ϕ) the stack needed in order to implement the recursion takes
Input: phase ϕ O(log n) space.
1 probForks ← new 2-D float array of size L × 2 Theorem 8: The running time of the SCL decoder
2 i ←0 is O(L · n log n).
// populate probForks Proof: Recall that by our notation m = log n. The
3 for = 0, 1, . . . , L − 1 do
4 if activePath[] = true then
following bottom-to-top table summarizes the running time
5 Pm ← getArrayPointer_P(m, ) of each function. The notation O will be explained shortly.
6 probForks [][0] ← Pm [0][0] function running time
7 probForks [][1] ← Pm [0][1]
8 i ←i +1 initializeDataStructures() O(L · m)
9 else assignInitialPath() O(m)
10 probForks [][0] ← −1 clonePath() O(m)
11 probForks [][1] ← −1
killPath() O(m)
12 ρ ← min(2i, L) getArrayPointer_P(λ, ) O(2m−λ )
13 contForks ← new 2-D boolean array of size L × 2 getArrayPointer_C(λ, ) O(2m−λ )
// The following is possible in O(L) time recursivelyCalcP(m, ·) O (L · m · n)
14 populate contForks such that contForks[][b] is true iff
probForks [][b] is one of the ρ largest entries in probForks recursivelyUpdateC(m, ·) O (L · m · n)
(and ties are broken arbitrarily) continuePaths_UnfrozenBit(ϕ) O(L · m)
// First, kill-off non-continuing paths SCL decoder O(L · m · n)
15 for = 0, 1, . . . , L − 1 do
16 if activePath[] = false then continue The first 7 functions in the table, the low-level func-
17 if contForks[][0] = false and contForks[][1] = false tions, are easily checked to have the stated running time.
then Note that the running time of getArrayPointer_P and
18 killPath() getArrayPointer_C is due to the copy operation in line 6
// Then, continue relevant paths, and of Algorithm 6 applied to an array of size O(2m−λ ). Thus,
duplicate if necessary as was previously mentioned, reducing the size of our arrays
19 for = 0, 1, . . . , L − 1 do has helped us reduce the running time of our list decoding
20 if contForks[][0] = false and contForks[][1] = false algorithm.
then // both forks are bad, or invalid
21 continue
Next, let us consider the two mid-level functions, namely,
recursivelyCalcP and recursivelyUpdateC.
22 Cm ← getArrayPointer_C(m, )
23 if contForks[][0] = true and contForks[][1] = true
The notation
then // both forks are good recursivelyCalcP(m, ·) ∈ O (L · m · n)
24 set Cm [0][ϕ mod 2] ← 0
25 ← clonePath() means that total running time of the n function calls
26 Cm ← getArrayPointer_C(m, )
27 set Cm [0][ϕ mod 2] ← 1 recursivelyCalcP(m, ϕ) , 0 ≤ ϕ < 2m
28 else// exactly one fork is good
29 if contForks[][0] = true then is O(L·m·n). To see this, denote by f (λ) the total running time
30 set Cm [0][ϕ mod 2] ← 0 of the above with m replaced by λ. By splitting the running
31 else time of Algorithm 10 into a non-recursive part and a recursive
set Cm [0][ϕ mod 2] ← 1
part, we have that for λ > 0
32
Fig. 6. Word error rate of a length n = 8192 rate 1/2 polar code optimized
for SNR = 2 dB under various list sizes. Code construction was carried out
via the method proposed in [15]. For reference, the Genie2 plot is the error
rate if standard successive cancellation is used, and a genie corrects at most
2 wrong bit decisions.
[7] C. Leroux, A. J. Raymond, G. Sarkis, I. Tal, A. Vardy, and W. J. Gross, Alexander Vardy (S’88–M’91–SM’94–F’98) was born in Moscow, U.S.S.R.,
“Hardware implementation of successive-cancellation decoders for polar in 1963. He earned his B.Sc. (summa cum laude) from the Technion, Israel,
codes,” J. Signal Process. Syst., vol. 69, no. 3, pp. 305–315, Dec. 2012. in 1985, and Ph.D. from the Tel-Aviv University, Israel, in 1991. During
[8] M. Mondelli, S. H. Hassani, and R. Urbanke, “Scaling exponent of list 1985-1990 he was with the Israeli Air Force, where he worked on electronic
decoders with applications to polar codes,” in Proc. IEEE Inf. Theory counter measures systems and algorithms. During the years 1992 and 1993
Workshop (ITW), Sep. 2013, pp. 1–5. he was a Visiting Scientist at the IBM Almaden Research Center, in San
[9] W. W. Peterson and E. J. Weldon, Error-Correcting Codes, 2nd ed. Jose, CA. From 1993 to 1998, he was with the University of Illinois at
Cambridge, MA, USA: MIT Press, 1972. Urbana-Champaign, first as an Assistant Professor then as an Associate
[10] Y. Polyanskiy, private communication, 2012. Professor. Since 1998, he has been with the University of California San
[11] Y. Polyanskiy, H. V. Poor, and S. Verdú, “Channel coding rate in the Diego (UCSD), where he is the Jack Keil Wolf Endowed Chair Professor in the
finite blocklength regime,” IEEE Trans. Inf. Theory, vol. 56, no. 5, Department of Electrical and Computer Engineering, with joint appointments
pp. 2307–2359, May 2010. in the Department of Computer Science and the Department of Mathematics.
[12] G. Sarkis, P. Giard, A. Vardy, C. Thibeault, and W. J. Gross, “Fast polar While on sabbatical from UCSD, he has held long-term visiting appointments
decoders: Algorithm and implementation,” IEEE J. Sel. Areas Commun., with CNRS, France, the EPFL, Switzerland, and the Technion, Israel.
vol. 32, no. 5, pp. 946–957, May 2014. His research interests include error-correcting codes, algebraic and iter-
[13] G. Sarkis and W. J. Gross, “Systematic encoding of polar codes for list ative decoding algorithms, lattices and sphere packings, coding for digital
decoding,” private communication, 2011. media, cryptography and computational complexity theory, and fun math
[14] E. Şaşoğlu, “Polarization and polar codes,” Found. Trends Commun. Inf. problems.
Theory, vol. 8, no. 4, pp. 259–381, Oct. 2012. He received an IBM Invention Achievement Award in 1993, and NSF
[15] I. Tal and A. Vardy, “How to construct polar codes,” IEEE Trans. Inf. Research Initiation and CAREER awards in 1994 and 1995. In 1996, he
Theory, vol. 59, no. 10, pp. 6562–6582, Oct. 2013. was appointed Fellow in the Center for Advanced Study at the University
[16] TurboBest. IEEE 802.16e LDPC Encoder/Decoder Core. [Online]. of Illinois, and received the Xerox Award for faculty research. In the same
Available: http://www.turbobest.com/tb_ldpc80216e.htm, accessed year, he became a Fellow of the Packard Foundation. He received the IEEE
Mar. 10, 2015. Information Theory Society Paper Award (jointly with Ralf Koetter) for the
[17] G. Wiechman and I. Sason, “An improved sphere-packing bound for year 2004. In 2005, he received the Fulbright Senior Scholar Fellowship, and
finite-length codes over symmetric memoryless channels,” IEEE Trans. the Best Paper Award at the IEEE Symposium on Foundations of Computer
Inf. Theory, vol. 54, no. 5, pp. 1962–1990, May 2008. Science (FOCS). During 1995-1998, he was an Associate Editor for Coding
Theory and during 1998-2001, he was the Editor-in-Chief of the IEEE
Ido Tal (M’–) was born in Haifa, Israel, in 1975. He received the B.Sc., M.Sc., T RANSACTIONS ON I NFORMATION T HEORY. From 2003 to 2009, he was
and Ph.D. degrees in computer science from Technion-Israel Institute of Tech- an Editor for the SIAM Journal on Discrete Mathematics. He is currently
nology, Haifa, Israel, in 1998, 2003 and 2009, respectively. During 2010-2012 serving on the Executive Editorial Board for the IEEE T RANSACTIONS ON
he was a postdoctoral scholar at the University of California at San Diego. I NFORMATION T HEORY. He has been a member of the Board of Governors
In 2012 he joined the Electrical Engineering Department at Technion. His of the IEEE Information Theory Society during 1998-2006, and again starting
research interests include constrained coding and error-control coding. in 2011.