Commit 1e44ece4 authored by Renaud Pacalet's avatar Renaud Pacalet
Browse files

Add scmspp method

parent cbbd2f92
......@@ -219,6 +219,7 @@ Decoding methods:
scms [-]max-iterations (Self-Corrected Min-Sum, floating point)
scmsfp [-]max-iterations 1<=bit-width<=31 (SCMS, fixed point)
scmsla [-]max-iterations 1<=bit-width<=31 (SCMS, fixed point, layered schedule)
scmspp [-]max-iterations 1<=bit-width<=31 1<= parallel-level (SCMS, fixed point, layered schedule disturbed by parallel processing)
endef
export HELP_message
......
......@@ -842,3 +842,220 @@ void scmsla_iter(mod2sparse *H, double *lratio, char *dblk, double *bprb, scmsla
dblk[col] = sd->p[col] < 0;
}
}
/***********************************************************************/
/* self-corrected min-sum, fixed point, layered schedule, parallel run */
/***********************************************************************/
/* This implementation investigates the consequences of a disturbed schedule on
* the convergence speed. It makes sense especially for parallel hardware
* implementations of SCMS LDPC decoders and Quasi-Cyclic structured parity
* check matrices (e.g. 802.11n or 802.16).
*
* The layered schedule processes each row in 2 consecutive phases:
* - update the VN-to-CN messages (first for columns loop)
* - update the posterior information (second for columns loop)
* The second phase depends on the first one and can thus not be merged with it.
* On hardware implementations it can be tempting to parallelize them by running
* phase 2 on a row while running phase 1 on the next row. Assuming P processing
* elements are processing groups of P rows in parallel, this leads to a
* schedule where rows in group 0..P-1 are in phase 2 while rows in group
* P..2P-1 are in phase 1. This works as long as the inputs of phase 1 do not
* overlap with outputs of phase 2. With Z=81 and P=9, for instance, this
* property is almost guaranteed by the structure of the parity check matrix. The
* inputs of rows 9..17 do not overlap with the outputs of rows 0..8. But, when
* crossing a block rows boundary, the property does not necessarily hold any
* more. Inputs of rows 81..89 and outputs of rows 72..80 can overlap and some
* rows of the next group (at the beginning of the next block row) can enter
* phase 1 while the input posterior information has not yet been produced by
* the phase 2 of the previous group of rows (at the end of the previous block
* row). The consequence will be that some posterior information read in phase 1
* for the processing of rows 81..89 can be different from the most up-to-date
* values from the phase 2 of rows 72..80.
*
* This implementation emulates this by delaying the update of the posterior
* information.
*/
unsigned scmspp_decode(mod2sparse *H, double *lratio, char *dblk, char *pchk, double *bprb, scmspp_data *sd) {
int N, n, c;
N = mod2sparse_cols(H);
scmspp_init(H,lratio,dblk,bprb,sd);
/* Do up to abs(max_iter) iterations of probability propagation, stopping early if a codeword is found, unless max_iter is negative. */
for (n = 0; ; n++) {
c = check(H,dblk,pchk);
#ifdef DEBUG
if(c == 0) {
printf("standard check: code word found after %d iterations\n", n);
}
#endif
if (table==2) {
printf("%7d %5d %8.1f %6d %+9.2f %8.1f %+9.2f %7.1f\n", block_no, n, changed(lratio,dblk,N), c, loglikelihood(lratio,dblk,N), expected_parity_errors(H,bprb), expected_loglikelihood(lratio,bprb,N), entropy(bprb,N));
}
scmspp_iter(H,lratio,dblk,bprb,sd);
#ifdef DEBUG
if(sd->s == 0) { // if sd.w was a code word before scmspp_iter
printf("on-the-fly check: code word found after %d iterations", n + 1);
if(sd->changed) { // if sd.w changed by scmspp_iter
printf("... but decoded word changed");
}
printf("\n");
}
#endif
// if max_iter<0, stop after exactly -max_iter iterations, independently of success or failure
// if max_iter>0, stop:
// if requested number of iterations reached (max_iter<0), or
// if sd.w was a code word before, and did not change during last scmspp_iter call (success)
if (n==max_iter || n==-max_iter || (max_iter>0 && sd->changed==0 && sd->s==0)) {
break;
}
}
return n;
}
/* initialize */
void scmspp_init(mod2sparse *H, double *lratio, char *dblk, double *bprb, scmspp_data *sd) {
mod2entry *e;
int N;
int M;
int col;
double dtmp;
N = mod2sparse_cols(H);
M = mod2sparse_rows(H);
for (col = 0; col<N; col++) {
if (bprb) bprb[col] = 1 - 1/(1+lratio[col]);
dblk[col] = lratio[col]>=1;
dtmp = -log(lratio[col]);
sd->p[col] = scms_saturate(dtmp, sd->size);
sd->w[col] = 1;
}
for(int row = 0; row < M; row++) {
sd->r[row].m1 = 0;
sd->r[row].m2 = 0;
sd->r[row].i = 0;
sd->r[row].as = UINT64_C(0);
sd->r[row].sp = 0;
sd->r[row].ae = ~UINT64_C(0);
}
sd->s = 0;
sd->changed = 1;
}
/* compute one iteration */
void scmspp_iter(mod2sparse *H, double *lratio, char *dblk, double *bprb, scmspp_data *sd) {
mod2entry *e;
int N, M;
int64_t tmp;
int col, row, row1, row2;
scmspp_compact_row new, old; // new and old row data
int sgn1, sgn2, ers1, ers2; // sign and erase flags
int i;
int rs; // per-row syndrome check
M = mod2sparse_rows(H);
N = mod2sparse_cols(H);
sd->s = 0;
sd->changed = 0;
for (row1 = 0; row1 < M + sd->pp; row1 += sd->pp) {
for (row2 = 0; row1 + row2 < M && row2 < sd->pp; row2++) {
row = row1 + row2;
old = sd->r[row];
new.m1 = (1 << (sd->size - 1)) - 1;
new.m2 = (1 << (sd->size - 1)) - 1;
new.i = 0;
new.as = UINT64_C(0);
new.sp = 0;
new.ae = UINT64_C(0);
i = 0;
for (e = mod2sparse_first_in_row(H,row); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) {
col = e->col; // column index
// re-compute old CN-to-VN message (tmp) from compact set
sgn1 = ((old.as >> i) & UINT64_C(1));
sgn1 ^= old.sp; // sign (0/1) of previous CN-to-VN message
if(i == old.i) {
tmp = SCMS_ZO2MP(sgn1) * old.m2;
} else {
tmp = SCMS_ZO2MP(sgn1) * old.m1;
}
// compute new VN-to-CN message (tmp) from old posterior message and old CN-to-VN message
tmp = scms_saturate(sd->p[col] - tmp, sd->size);
sgn2 = SCMS_SIGN_ZO(tmp); // sign (0/1)
sd->pnext[col] = tmp; // compute new posterior message (first part)
// SCMS erasure
sgn1 = (old.as >> i) & UINT64_C(1); // sign of old VN-to-CN message (0/1)
ers1 = (old.ae >> i) & UINT64_C(1); // erase flag of old VN-to-CN message
if((sgn2 != sgn1) && (!ers1)) {
tmp = 0;
sgn2 = 0;
ers2 = 1;
} else {
ers2 = 0;
}
new.sp ^= sgn2; // compute product of signs of (erased) new VN-to-CN messages
new.as |= (uint64_t)(sgn2) << i; // update signs
new.ae |= (uint64_t)(ers2) << i; // update erase flags of new VN-to-CN messages
// SCMS kernel
tmp = SCMS_ABS(tmp); // magnitude of new (erased) VN-to-CN message
if(tmp < new.m1) {
new.m2 = new.m1;
new.m1 = tmp;
new.i = i;
} else if(tmp < new.m2) {
new.m2 = tmp;
}
i += 1;
}
sd->r[row] = new;
// write back new CN-to-VN and posterior messages
i = 0;
for (e = mod2sparse_first_in_row(H,row); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) {
col = e->col; // column index
// compute new CN-to-VN message
sgn2 = ((new.as >> i) & UINT64_C(1));
sgn2 ^= new.sp; // sign (0/1) of previous CN-to-VN message
if(i == new.i) {
tmp = SCMS_ZO2MP(sgn2) * new.m2;
} else {
tmp = SCMS_ZO2MP(sgn2) * new.m1;
}
sd->pnext[col] += tmp; // compute new posterior message (second part)
i += 1;
}
}
if(row1 >= sd->pp) {
for (row2 = 0; row1 - sd->pp + row2 < M && row2 < sd->pp; row2++) {
rs = 0;
row = row1 - sd->pp + row2;
for (e = mod2sparse_first_in_row(H,row); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) {
col = e->col; // column index
sd->p[col] = sd->pnext[col];
// update row syndrome
rs ^= sd->w[col];
if((sd->p[col] < 0) ^ sd->w[col]) { // if decoded word changed
sd->changed = 1;
sd->w[col] = (sd->p[col] < 0);
}
}
// update matrix syndrome
sd->s |= rs;
}
}
}
for (col = 0; col<N; col++) {
if (bprb) bprb[col] = 1 - 1/(1+exp(-sd->p[col]));
dblk[col] = sd->p[col] < 0;
}
}
......@@ -18,7 +18,7 @@
declared here are located in dec.c. */
typedef enum
{ Enum_block, Enum_bit, Prprp, Scms, Scmsfp, Scmsla
{ Enum_block, Enum_bit, Prprp, Scms, Scmsfp, Scmsla, Scmspp
} decoding_method;
extern decoding_method dec_method; /* Decoding method to use */
......@@ -85,3 +85,26 @@ typedef struct {
unsigned scmsla_decode(mod2sparse *, double *, char *, char *, double *, scmsla_data *);
void scmsla_init(mod2sparse *, double *, char *, double *, scmsla_data *);
void scmsla_iter(mod2sparse *, double *, char *, double *, scmsla_data *);
// compact representation of set of VN-to-CN messages of a row
typedef struct {
int64_t m1; // first minimum of absolute values of VN-to-CN messages
int64_t m2; // second minimum of absolute values of VN-to-CN messages
int i; // argmin of absolute values of VN-to-CN messages
uint64_t as; // signs of VN-to-CN messages (0=+1, 1 =-1)
int sp; // product of signs of VN-to-CN messages (0=+1, 1=-1)
uint64_t ae; // erasure flags of VN-to-CN messages (1=erased)
} scmspp_compact_row;
typedef struct {
int pp; // number of rows processed in parallel
int size; // bit-width
scmspp_compact_row *r; // row data
int64_t *p; // posterior messages
int64_t *pnext; // posterior messages
char *w; // decoded word
char s; // syndrome check (0: ok); set by scmspp_iter for input w
char changed; // set to 1 by scmspp_iter if input w and output w are different
} scmspp_data;
unsigned scmspp_decode(mod2sparse *, double *, char *, char *, double *, scmspp_data *);
void scmspp_init(mod2sparse *, double *, char *, double *, scmspp_data *);
void scmspp_iter(mod2sparse *, double *, char *, double *, scmspp_data *);
......@@ -52,7 +52,8 @@ int main
double *bitpr;
scms_data sd;
scmsfp_data sdfp;
scmsla_data sdtd;
scmsla_data sdla;
scmspp_data sdpp;
double *awn_data; /* Places to store channel data */
int *bsc_data;
......@@ -152,10 +153,28 @@ int main
if (!meth[1] || sscanf(meth[1],"%d%c",&max_iter,&junk)!=1)
{ usage();
}
if (!meth[2] || sscanf(meth[2],"%d%c",&sdtd.size,&junk)!=1 || meth[3])
if (!meth[2] || sscanf(meth[2],"%d%c",&sdla.size,&junk)!=1 || meth[3])
{ usage();
}
if (sdtd.size < 1 || sdtd.size > 32)
if (sdla.size < 1 || sdla.size > 32)
{ usage();
}
}
else if (strcmp(meth[0],"scmspp")==0)
{ dec_method = Scmspp;
if (!meth[1] || sscanf(meth[1],"%d%c",&max_iter,&junk)!=1)
{ usage();
}
if (!meth[2] || sscanf(meth[2],"%d%c",&sdpp.size,&junk)!=1)
{ usage();
}
if (!meth[3] || sscanf(meth[3],"%d%c",&sdpp.pp,&junk)!=1 || meth[4])
{ usage();
}
if (sdpp.size < 1 || sdpp.size > 32)
{ usage();
}
if (sdpp.pp < 1)
{ usage();
}
}
......@@ -262,9 +281,14 @@ int main
sdfp.a[row] = chk_alloc (N, sizeof **sdfp.a);
}
sdtd.r = chk_alloc (M, sizeof *sdtd.r);
sdtd.p = chk_alloc (N, sizeof *sdtd.p);
sdtd.w = chk_alloc (N, sizeof *sdtd.w);
sdla.r = chk_alloc (M, sizeof *sdla.r);
sdla.p = chk_alloc (N, sizeof *sdla.p);
sdla.w = chk_alloc (N, sizeof *sdla.w);
sdpp.r = chk_alloc (M, sizeof *sdpp.r);
sdpp.p = chk_alloc (N, sizeof *sdpp.p);
sdpp.pnext = chk_alloc (N, sizeof *sdpp.pnext);
sdpp.w = chk_alloc (N, sizeof *sdpp.w);
/* Print header for summary table. */
......@@ -291,6 +315,10 @@ int main
{ scms_decode_setup();
break;
}
case Scmspp:
{ scms_decode_setup();
break;
}
case Enum_block: case Enum_bit:
{ enum_decode_setup();
break;
......@@ -380,7 +408,11 @@ int main
break;
}
case Scmsla:
{ iters = scmsla_decode (H, lratio, dblk, pchk, bitpr, &sdtd);
{ iters = scmsla_decode (H, lratio, dblk, pchk, bitpr, &sdla);
break;
}
case Scmspp:
{ iters = scmspp_decode (H, lratio, dblk, pchk, bitpr, &sdpp);
break;
}
case Enum_block: case Enum_bit:
......@@ -464,6 +496,6 @@ void usage(void)
" decode [ -f ] [ -t | -T ] pchk-file received-file decoded-file [ bp-file ] channel method\n");
channel_usage();
fprintf(stderr,
"Method: enum-block gen-file | enum-bit gen-file | prprp [-]max-iterations | scms [-]max-iterations | scmsfp [-]max-iterations 1<=bit-width<=32 | scmsla [-]max-iterations 1<=bit-width<=32\n");
"Method: enum-block gen-file | enum-bit gen-file | prprp [-]max-iterations | scms [-]max-iterations | scmsfp [-]max-iterations 1<=bit-width<=32 | scmsla [-]max-iterations 1<=bit-width<=32 | scmspp [-]max-iterations 1<=bit-width<=32 1<=parallel-level\n");
exit(1);
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment