Official reproducibility package for the preprint by Zihao Liu, Zilong Liu, and Leila Musavian: arXiv:2606.22603.
End-to-end learned SCMA codebook for an OFDM downlink, optimized with a differentiable Log-MPA detector under multipath fading, carrier frequency offset (CFO), and Wiener phase noise (PN).
| Goal | Start here |
|---|---|
| Understand the method and assumptions | Idea and What this model honestly does (and doesn't) |
| Install and run the released model | Quick start |
| Reproduce training, controls, and BER sweeps | docs/REPRODUCIBILITY.md |
| Verify downloaded artifacts | python -m tools.validate_phase1_artifacts |
| Check repository integrity without dependencies | python -m tools.check_repository |
| Cite the work | Citation or CITATION.cff |
The repository separates the paper's fixed-point model from all additional controls. Ablation scripts and checkpoints use dedicated artifacts/ subdirectories and never overwrite the released seed-0 checkpoint.
Treat the (transmitter codebook → channel → receiver detector) chain as a differentiable graph. The SCMA codebook is a complex torch.nn.Parameter. Gradients flow back through:
- SCMA superposition over
K = 4resources - OFDM modulation and the frequency-domain multipath channel
- CFO and Wiener phase noise in the time domain
- AWGN
- A 10-iteration Log-MPA detector (logsumexp form, numerically stable end-to-end)
The simulator represents one downlink receiver. All J = 6 SCMA layers are superimposed before transmission and therefore share that receiver's frequency-selective channel realization within an OFDM symbol. The channel changes independently between simulated OFDM symbols.
The implemented time-domain order is common downlink channel → aggregate CFO → aggregate Wiener PN → AWGN → CP removal/FFT. Thus, CFO/PN are receiver-side aggregate impairments applied after channel convolution. The aggregate PN layer is a stated modeling approximation; the code does not model separate user oscillators or per-user uplink offsets.
The capacity-based codebook of Zhang et al. is built directly into the training script and serves as both the initialization and the fixed quality reference. A squared-hinge term (loss_tr − loss_base + m)_+² encourages the trainable codebook to outperform this reference under identical messages, channels, AWGN, and PN realizations.
| Parameter | Value | Note |
|---|---|---|
Users J |
6 | factor-graph degree 2 per user |
Resources K |
4 | factor-graph degree 3 per resource |
Order M |
4 | 2 bits per symbol |
FFT Nfft |
1024 | |
CP Ncp |
32 | covers the 20-sample channel spread |
| PDP | 8 taps, normalized | delays [1, 2, 4, 6, 9, 11, 15, 20] |
CFO ε |
0.04 | fraction of subcarrier spacing (fixed) |
| PN | Wiener, σ_step = 1e-3 rad / sample |
|
| MPA iterations | 10 |
During training, Es is projected to 1.0 every 4 steps via a 64-sample Monte-Carlo estimate. The final BER evaluators independently apply the exact analytic global-superposition normalization described below, eliminating normalization sampling error from codebook comparisons.
L_total = (loss_tr_low + λ_low · hinge_low²)
+ 10 · (loss_tr_high + λ_high · hinge_high²)
+ λ_MED · L_MED
where loss_tr = 0.3 · BCE + 0.8 · margin, the margin term is softplus((m₀ − signed_LLR)/t).mean(), and L_MED is a soft-min over codeword-superposition distances at each resource (encourages well-separated effective constellations).
Each step samples a low/mid-SNR batch and a high-SNR batch. The first uses [0, 15] dB with probability 0.85 and [16, 21] dB with probability 0.15; the second uses [22, 35] dB. The high-SNR branch is weighted 10× to push the error floor down. Training uses Adam for 6000 steps with a batch size of 128 OFDM symbols per branch, an initial learning rate of 5e-4, a factor-0.5 decay every 2500 steps, and gradient clipping at norm 3.
- Perfect CSI is fed to the MPA. Channel-estimation noise is not modeled.
- The main released codebook uses fixed impairments: CFO is fixed at
ε = 0.04and PN atσ_step = 1e-3rad/sample during its training. - A separate randomized-training ablation is provided. It samples
ε ~ U[0, 0.06]andσ_step ~ U[0, 3e-3]; it does not redefine the main fixed-point method or its checkpoint. - Quasi-static block fading per OFDM symbol; no time correlation across symbols.
- Integer-sample tap delays. No fractional delay or PDP leakage.
- Representative-receiver downlink. All superimposed SCMA layers share one channel response at that receiver; the implementation is not a multi-user uplink channel model.
- Aggregate receiver-side CFO/PN. Both rotations are applied after the common channel. Distributed transmitter/receiver PN is approximated by one post-channel Wiener process.
python -m pip install -r requirements.txt
python -m tools.check_repository
python train_ofdm_scma.pyPython 3.10 or newer is recommended. A CUDA-capable GPU is strongly recommended for full training and Monte-Carlo sweeps; repository and released-artifact validation can run on CPU. Exact validated package versions, computational levels, expected files, and full commands are documented in docs/REPRODUCIBILITY.md.
Outputs are saved under artifacts/main/checkpoints/:
codebook_e2e.pt— shape(V, K, M), complex64, end-to-end formatcb1_kmv.pt— shape(K, M, V), complex64, simulator format- The same two tensors as
.npyfor non-PyTorch consumers
Training always starts from the built-in Zhang capacity-based codebook returned by build_zhang_codebook(), which is also used as the hinge baseline. No external baseline checkpoint is required, so a clean run uses the same reference as the manuscript.
A snapshot of LOW / HIGH loss and hard BER is printed every 50 steps. Ctrl-C saves the current codebook safely before exit.
The M1 scripts and outputs are deliberately separated from the original training path:
# Trains the additional model; never overwrites cb1_kmv.pt.
python -m experiments.phase1.train_ofdm_scma_randomized
# Paired three-point comparison at Eb/N0 = 30 dB.
python -m experiments.phase1.eval_randomized_training_ablation
# Exact-energy and constellation-geometry comparison.
python -m experiments.phase1.analyze_randomized_training_ablationAll additional checkpoints, histories, raw-count .mat/.csv files, logs, and geometry summaries are written below artifacts/phase1/m1_randomized_training/. The randomized model inherits the fixed model's initialization, 6000 steps, SNR sampler, batch size, Log-MPA iterations, optimizer, loss, and energy projection; only the CFO/PN parameter sampling differs.
The remaining Phase-1 controls reuse the original fixed-point training entry point while isolating every new checkpoint from the repository-root seed-0 model:
# M3: change only lambda_MED from 1e-2 to zero.
python -m experiments.phase1.train_fixed_point_control --condition no_med --seed 0 --steps 6000 --output-dir artifacts/phase1/m3_no_med
python -m experiments.phase1.eval_no_med_ablation
# M4: retain the full loss and change only the independent training seed.
python -m experiments.phase1.train_fixed_point_control --condition full --seed 1 --steps 6000 --output-dir artifacts/phase1/m4_seed_check/seed1
python -m experiments.phase1.train_fixed_point_control --condition full --seed 2 --steps 6000 --output-dir artifacts/phase1/m4_seed_check/seed2
python -m experiments.phase1.eval_independent_seed_check
# Exact-energy and geometry audit for the original and three control checkpoints.
python -m experiments.phase1.analyze_phase1_controlstrain_fixed_point_control.py resets NumPy/PyTorch/CUDA random states, records the exact condition and constants in training_metadata.json, and runs the unchanged original optimizer in the requested artifact directory. The M3/M4 evaluators use one representative training point, exact global-Es normalization, raw integer error counts, and paired common random numbers. No new BER curve or receiver is introduced.
The completed one-point M3 control gives full/no-MED BER 3.3083e-4/3.4372e-4 (20,326/21,118 errors per 61,440,000 bits); removing Soft-MED raises BER by 3.90% in this paired run. The completed M4 check gives seed-0/1/2 BER 3.4652e-4, 3.5002e-4, and 3.5607e-4, or (3.5087 +/- 0.0483)e-4 as mean +/- sample standard deviation (CV 1.38%). These are narrow representative-point controls, not new full sweeps.
analyze_phase1_controls.py shows that the no-MED and independent-seed checkpoints keep the original sparse support and two descriptive magnitude shells near 0.257/0.774. It also demonstrates why the geometry is not used as a BER surrogate: the no-MED checkpoint has a larger average hard local minimum distance squared while producing worse BER.
Evaluation is a two-stage pipeline: a Python script runs the Monte-Carlo BER sweep and exports a .mat file, then a matching MATLAB script renders the publication figure. Each sweep benchmarks the trained codebook against five literature baselines (Deka, Li, Zhang, Zheng, PN-Resilient/Liu).
| Python sweep | MATLAB plot | Sweep axis | Other impairment | .mat file |
|---|---|---|---|---|
eval_ber_vs_phasenoise.py |
plots/matlab/plot_ber_vs_phasenoise.m |
PN σ_step ∈ [0, 3e-3] |
CFO ε ∈ {0, 0.04} |
artifacts/main/sweeps/SCMA_CFO_Simulation_Results.mat |
eval_ber_vs_cfo.py |
plots/matlab/plot_ber_vs_cfo.m |
CFO ε ∈ [0, 0.06] |
PN σ_step ∈ {0, 2.4e-3} |
artifacts/main/sweeps/SCMA_SweepCFO_Simulation_Results.mat |
eval_ber_vs_ebn0.py |
plots/matlab/plot_ber_vs_ebn0.m |
E_b/N_0 ∈ [10, 34] dB |
two conditions: ideal (ε=0, σ=0) and impaired (ε=0.03, σ=1e-4) |
artifacts/main/sweeps/SCMA_EbN0_Simulation_Results.mat |
In every plot, the solid curve is the impaired condition and the dashed curve (same color, not in the legend) is the impairment-free reference, so the gap between the two shows each codebook's robustness directly.
The CFO and PN sweeps run at E_b/N_0 = 30 dB, simulate at least 2,000 and at most 100,000 OFDM symbols per point, and stop early only after all six codebooks have accumulated at least 2,000 aggregate bit errors. The Eb/N0 sweep uses 20,000 symbols at regular points and up to 50,000 above 30 dB, with a 10,000-error target for every codebook at the high-SNR points. One OFDM symbol carries J * (Nfft/K) * log2(M) = 3072 aggregate user bits, so the largest budgets are 3.072e8 and 1.536e8 bits per point, respectively.
Evaluation uses seed 2025. Every OFDM symbol draws an independent eight-tap Rayleigh channel. Within each simulation chunk, all six codebooks receive identical messages, channel taps, AWGN, and PN paths; this common-random-number design reduces the variance of pairwise BER comparisons without changing any codebook's marginal channel distribution.
Each revised evaluator exports the raw error_count, total_bits, Nd_used, seed, and stopping_reason together with the empirical BER. A zero-error run is stored as empirical BER = 0, never as an artificial floor. Its separate BER_95_upper = 3 / total_bits value and BER_is_upper_bound flag implement the conventional 95% rule-of-three bound. The MATLAB scripts plot those zero-error points at the bound using downward-triangle markers.
Before each BER sweep, all codebooks are scaled using the same exact analytic criterion
Es = (1/K) E_{m1,...,mJ}[ || sum_j c_j(mj) ||² ] = 1,
R = J log2(M) / K = 3 bits/resource element,
N0 = 1 / (R · 10^(Eb/N0_dB/10)).
The expectation includes cross terms caused by nonzero per-user codebook means. It is evaluated analytically for independent equiprobable messages, rather than estimated with a finite Monte-Carlo batch. Time-domain AWGN uses variance N0/Nfft, which becomes variance N0 per subcarrier after the unnormalized FFT. We preserve each baseline's original user-power allocation and equalize only total downlink superposition energy. Run python -m experiments.diagnostics.analyze_codebook_energy to reproduce the complete per-user and peak-codeword energy table under artifacts/main/diagnostics/.
The intentionally mismatched Log-MPA uses N0_dec = N0 while the physical simulator still applies time-domain CFO and Wiener PN. The lightweight check python -m experiments.diagnostics.eval_decoder_variance_sensitivity repeats one representative point with N0_dec = gamma*N0, gamma in {0.5, 1, 2}, for the proposed and strongest PN-resilient baseline; it writes a raw-count .mat, .csv, and .log under artifacts/main/diagnostics/.
Every evaluation also mirrors stdout and stderr to a complete log under artifacts/main/sweeps/. The log records timestamps, script path, stopping reason, Nd, error count, total bits, empirical BER, and any zero-error upper bound for every simulated point.
# 1) Run the sweeps (Python) -> writes the .mat files
python eval_ber_vs_phasenoise.py
python eval_ber_vs_cfo.py
python eval_ber_vs_ebn0.py
# Reproduce assumption/fairness checks used in the response letter
python -m experiments.diagnostics.analyze_codebook_energy
python -m experiments.diagnostics.eval_decoder_variance_sensitivity
# 2) Render the figures (MATLAB) -> writes BER_vs_*.pdf / .eps
# run the matching scripts under plots/matlab/Codebook path: all three Python scripts require
artifacts/main/checkpoints/cb1_kmv.pt(also written bytrain_ofdm_scma.py). If the file is missing or has the wrong shape, evaluation stops with an explicit error; it never substitutes a random codebook.
Developed on i5-13600KF + RTX 4070 (12 GB). At BATCH_OFDMSYM = 128, Q_SUB = 256, a full 6000-step run fits comfortably in 12 GB. Lower these knobs if you have less VRAM.
.
├── train_ofdm_scma.py # main fixed-point training entry point
├── eval_ber_vs_phasenoise.py # main PN sweep
├── eval_ber_vs_cfo.py # main CFO sweep
├── eval_ber_vs_ebn0.py # main Eb/N0 sweep
├── evaluation_utils.py # shared normalization, BER, and logging helpers
├── experiments/
│ ├── phase1/ # M1 randomized, M3 Soft-MED, and M4 seed controls
│ └── diagnostics/ # energy and decoder-variance checks
├── plots/
│ └── matlab/ # publication BER renderers
├── tools/ # repository audit, artifact audit, constellation plotter
├── artifacts/
│ ├── main/
│ │ ├── checkpoints/ # released seed-0 codebooks
│ │ ├── sweeps/ # main MAT files and complete logs
│ │ ├── diagnostics/ # lightweight control outputs
│ │ └── figures/ # publication-ready PDF/EPS figures
│ └── phase1/ # isolated M1/M3/M4 evidence and summary
├── docs/ # reproducibility and contribution guides
├── CITATION.cff # GitHub-readable citation metadata
├── requirements.txt
├── LICENSE
└── README.md
MIT. See LICENSE.
- H. Nikopour and H. Baligh, "Sparse Code Multiple Access," PIMRC 2013.
- M. Taherzadeh, H. Nikopour, A. Bayesteh, and H. Baligh, "SCMA Codebook Design," VTC-Fall 2014.
- F. Wei and W. Chen, "Message Passing Receiver Design for Uplink Grant-Free SCMA," IEEE Wireless Commun. Lett.
GitHub can read the repository's CITATION.cff directly. The associated preprint citation is:
@misc{liu2026robust,
title = {Robust SCMA Codebook Design: A Hardware-Aware Autoencoder Approach},
author = {Liu, Zihao and Liu, Zilong and Musavian, Leila},
year = {2026},
eprint = {2606.22603},
archivePrefix = {arXiv},
primaryClass = {cs.IT},
doi = {10.48550/arXiv.2606.22603}
}