├── EvaluatePrecoderMimo.m ├── Functions ├── backtrackingLineSearchDirection.m ├── backtrackingLineSearchPower.m ├── computeMiGivenChannelMimo.m ├── computeMiGivenPrecoderMimo.m ├── computeMiMimo.m ├── convertBinToDec.m ├── convertComplexMatToRealVec.m ├── convertDecToBin.m ├── convertNumberToCaseIdxTag.m ├── convertNumberToSnrTag.m ├── convertRealVecToComlpexMat.m ├── convertSnrTagToNumber.m ├── createCommonSimParamStruct.m ├── createDefaultSimParamStruct.m ├── evaluateNeuralNet.m ├── evaluatePrecoderMimo.m ├── generateFadingChannel.m ├── getComputedPrecoders.m ├── getWfPrecoder.m ├── miMimoGauss.m ├── miMimoTrue.m ├── miMimoZengXiaoLu.m ├── miMimoZhuShiFarhang.m ├── mmseMatrixMimoGauss.m ├── mmseMatrixMimoTrue.m ├── modulationFiniteAlphabet.m ├── optimizePrecoderGivenChannelMimoMl.m ├── optimizePrecoderMimo.m ├── optimizePrecoderMimoGauss.m ├── optimizePrecoderMimoGaussOpt.m ├── optimizePrecoderMimoMl.m ├── overrideStruct.m ├── parseSimIds.m ├── printComplexMatrix.m ├── runBackpropPass.m ├── runFeedforwardPass.m ├── runSimsLocally.m ├── setOmniPrecoderMimo.m ├── setupNeuralNet.m ├── splitDataset.m ├── trainAndTestNeuralNet.m └── updateMiniBatch.m ├── LICENSE ├── OptimizePrecoderMimo.m ├── PlotMiMimo.m ├── README.md ├── TrainNnModelMimo.m └── TrainedNets ├── nnWeightsBpsk2x2Mimo.mat ├── nnWeightsBpsk3x3Mimo.mat ├── nnWeightsQpsk2x2Mimo.mat └── nnWeightsQpsk3x3Mimo.mat /EvaluatePrecoderMimo.m: -------------------------------------------------------------------------------- 1 | % ========================================================================= 2 | % 3 | % Deep-learning based MIMO precoding for finite-alphabet signaling 4 | % 5 | % Low-complexity linear precoding for MIMO channels with discrete inputs 6 | % Precoder performance evaluation 7 | % 8 | % Max Girnyk 9 | % Stockholm, 2014-10-01 10 | % 11 | % ========================================================================= 12 | % 13 | % This Matlab script produces results used in the following paper: 14 | % 15 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 16 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 17 | % 18 | % Paper URL: https://arxiv.org/abs/2111.03504 19 | % 20 | % Version: 1.0 (modified 2021-11-14) 21 | % 22 | % License: This code is licensed under the Apache-2.0 license. 23 | % If you use this code in any way for research that 24 | % results in a publication, please cite the above paper 25 | % 26 | % ========================================================================= 27 | 28 | function EvaluatePrecoderMimo 29 | 30 | clear all; close all; clc; 31 | 32 | % Add all the subfolders to the path. 33 | currentFilePath = fileparts(which(mfilename)); 34 | addpath(genpath(currentFilePath)); 35 | functionsPath = [pwd, '\Functions']; 36 | simDataPath = [pwd, '\SimData']; 37 | visualsPath = [pwd, '\Results']; 38 | 39 | % ========================================================================= 40 | 41 | % Select sim number to evaluate ########################################### 42 | 43 | % simId = 190414153606; % either a case or an entire sim? 44 | % simId = 210419001433; % 2x2, BPSK 45 | % simId = 210215010426; % 2x2, QPSK 46 | % simId = 210217134641; % 3x3, BPSK 47 | % simId = 210322192102; % 3x3, QPSK 48 | % simId = 210317170605; % 3x3, QPSK 49 | % simId = 210420230928; % Net: 2x2, BPSK 50 | % simId = 210417162402; % Net: 2x2, 8PSK 51 | % simId = 210417004809; % Net: 2x2, QPSK 52 | % simId = 210417155326; % Net: 3x3, BPSK 53 | simId = 211115003222; 54 | 55 | % Find sim folder and param file ------------------------------------------ 56 | simName = num2str(simId); 57 | simFolderPath = [simDataPath, '\', simName]; 58 | paramFileName = ['params_', simName, '.mat']; 59 | paramFilePath = [simFolderPath, '\', paramFileName]; 60 | load(paramFilePath); 61 | nSimCases = length(simCaseParamStructs); 62 | 63 | % Dispatch simulations ---------------------------------------------------- 64 | operationType = 'EVALUATION'; 65 | for iSimCase = 1:nSimCases 66 | if strcmp(simCaseParamStructs{iSimCase}.signaling.typeModulation, 'GAUSS') 67 | % Do nothing for the Gaussian case (already evaluated) 68 | simCaseParamStructs{1}.cluster.status = 'EVALUATED'; 69 | else 70 | if (simCaseParamStructs{iSimCase}.cluster.runOnCluster) 71 | error('ERROR! Execution on cluster not supported!'); 72 | else 73 | fprintf('\nRun new simulation locally... \n\n'); 74 | runSimsLocally(simCaseParamStructs{iSimCase}, operationType); 75 | fprintf('DONE!\n'); 76 | end 77 | end 78 | end % for iSimCase = 1:nSimCases 79 | 80 | end 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /Functions/backtrackingLineSearchDirection.m: -------------------------------------------------------------------------------- 1 | function mu = backtrackingLineSearchDirection(alpha, beta, Hbar, typeModulation, methodComputation, methodSampling, nIterSignal, nIterNoise) 2 | % 3 | % BACKTRACKINGLINESEARCHDIRECTION Line-search backtracking algorithm for 4 | % optimizing the step size of the gradient ascent method to practically good 5 | % value (applied to the eigenvector matrix). The computation is done 6 | % according to Alg. 9.2 in Ch. 9 of the following book: 7 | % B. Boyd and L. Vandenberghe, Convex Optimization, Cambridge University 8 | % Press, 2004. 9 | % 10 | % Inputs: scalar alpha = backtracking parameter 11 | % scalar beta = step size decrement 12 | % max Hbar = channel matrix 13 | % str typeModulation = modulation scheme 14 | % str methodComputation = method of MI matrix computation 15 | % str methodSampling = method of sampling: EXHAUSTIVE/RANDOMIZED 16 | % scalar nIterSignal = number of samples for averaging over signal 17 | % scalar nIterNoise = number of smaples for averaging over noise 18 | % Outputs: scalar mu = optimal step size 19 | % 20 | % Max Girnyk 21 | % Stockholm, 2014-10-01 22 | % 23 | % ========================================================================= 24 | % 25 | % This Matlab script produces results used in the following paper: 26 | % 27 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 28 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 29 | % 30 | % Paper URL: https://arxiv.org/abs/2111.03504 31 | % 32 | % Version: 1.0 (modified 2021-11-14) 33 | % 34 | % License: This code is licensed under the Apache-2.0 license. 35 | % If you use this code in any way for research that 36 | % results in a publication, please cite the above paper 37 | % 38 | % ========================================================================= 39 | 40 | % Compute MI and MMSE matrix for further evaluations 41 | [I, ~] = computeMiMimo(Hbar, typeModulation, methodComputation, methodSampling, nIterSignal, nIterNoise); 42 | [E, ~] = mmseMatrixMimoTrue(Hbar, typeModulation, methodSampling, nIterSignal, nIterNoise); 43 | 44 | % Quadratic form for the cahnnel matrix 45 | Wbar = Hbar' * Hbar; 46 | 47 | % "Bisect" the step size 48 | mu = 1/beta; % starting value, so that we start with mu = 1 49 | proceedWithLoop = 1; % conditional for proceeding with the loop 50 | iLoop = 1; % index for limiting the max number of loops 51 | nLoopMax = 10; % max number of loops 52 | 53 | while proceedWithLoop 54 | mu = mu * beta; % update the step size 55 | 56 | % Gradient update the Gram matrix 57 | Wbar_new = Wbar + mu*E; 58 | [U_new, Lambda_new] = eig(Wbar_new); 59 | Hbar_new = sqrt(Lambda_new)*U_new'; % obtain Hbar from Wbar 60 | 61 | % Compute the MI value for the given step size 62 | [I_new, ~] = computeMiMimo(Hbar_new, typeModulation, methodComputation, methodSampling, nIterSignal, nIterNoise); 63 | 64 | % Check whether the step size is suitable (Boyd's book) 65 | proceedWithLoop = (I_new <= I + alpha*mu*E(:)'*E(:)) && (iLoop <= nLoopMax); 66 | iLoop = iLoop + 1; 67 | 68 | end % while proceedWithLoop 69 | -------------------------------------------------------------------------------- /Functions/backtrackingLineSearchPower.m: -------------------------------------------------------------------------------- 1 | function mu = backtrackingLineSearchPower(alpha, beta, Sigma_H, Sigma_G, Theta, typeModulation, methodComputation, methodSampling, nIterSignal, nIterNoise) 2 | % 3 | % BACKTRACKINGLINESEARCHDIRECTION Line-search backtracking algorithm for 4 | % optimizing the step size of the gradient ascent method to practically good 5 | % value (applied to the singular values of the precoder). The computation is done 6 | % according to Alg. 9.2 in Ch. 9 of the following book: 7 | % B. Boyd and L. Vandenberghe, Convex Optimization, Cambridge University 8 | % Press, 2004. 9 | % 10 | % Inputs: scalar alpha = backtracking parameter 11 | % scalar beta = step size decrement 12 | % max Hbar = channel matrix 13 | % str typeModulation = modulation scheme 14 | % str methodComputation = method of MI matrix computation 15 | % str methodSampling = method of sampling: EXHAUSTIVE/RANDOMIZED 16 | % scalar nIterSignal = number of samples for averaging over signal 17 | % scalar nIterNoise = number of smaples for averaging over noise 18 | % Outputs: scalar mu = optimal step size 19 | % 20 | % Max Girnyk 21 | % Stockholm, 2014-10-01 22 | % 23 | % ========================================================================= 24 | % 25 | % This Matlab script produces results used in the following paper: 26 | % 27 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 28 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 29 | % 30 | % Paper URL: https://arxiv.org/abs/2111.03504 31 | % 32 | % Version: 1.0 (modified 2021-11-14) 33 | % 34 | % License: This code is licensed under the Apache-2.0 license. 35 | % If you use this code in any way for research that 36 | % results in a publication, please cite the above paper 37 | % 38 | % ========================================================================= 39 | 40 | % Number of Tx antennas 41 | M = size(Sigma_G, 2); 42 | 43 | % Equivalent channel matrix 44 | Hbar = Sigma_H * Sigma_G * Theta; 45 | 46 | % Compute MI and MMSE matrix for further evaluations 47 | [I, ~] = computeMiMimo(Hbar, typeModulation, methodComputation, methodSampling, nIterSignal, nIterNoise); 48 | [E, ~] = mmseMatrixMimoTrue(Hbar, typeModulation, methodSampling, nIterSignal, nIterNoise); % support for other computations here 49 | 50 | % Gradient of the MI w.r.t. Sigma_G^2 51 | dI_G = 0.5 * diag(Sigma_H^2*Theta*E*Theta'); 52 | 53 | % "Bisect" the step size 54 | mu = 1/beta; % starting value, so that we start with mu = 1 55 | proceedWithLoop = 1; % conditional for proceeding with the loop 56 | iLoop = 1; % index for limiting the max number of loops 57 | nLoopMax = 10; % max number of loops 58 | 59 | while proceedWithLoop 60 | g = diag(Sigma_G); % vector of singular values of the precoder 61 | mu = mu * beta; % update the step size 62 | 63 | % Gradient update the singular values 64 | g = g + mu*(dI_G - ones(M,1)/M*(ones(M,1)'*dI_G)); 65 | Sigma_G = diag(g); 66 | Hbar = Sigma_H*Sigma_G*Theta; 67 | 68 | % Compute the MI value for the given step size 69 | [I_new, ~] = computeMiMimo(Hbar, typeModulation, methodComputation, methodSampling, nIterSignal, nIterNoise); 70 | 71 | % Check whether the step size is suitable (Boyd's book) 72 | proceedWithLoop = (I_new <= I + alpha*mu*norm(dI_G)) && (iLoop <= nLoopMax); 73 | iLoop = iLoop + 1; 74 | 75 | end % while proceedWithLoop -------------------------------------------------------------------------------- /Functions/computeMiGivenChannelMimo.m: -------------------------------------------------------------------------------- 1 | function computeMiGivenChannelMimo(simParamFilePath) 2 | % 3 | % COMPUTEMIGIVENCHANNELMIMO Computes mutual information for a MIMO channel 4 | % for a given approach given a channel matrix by calling the respective 5 | % algorithm. 6 | % 7 | % Inputs: str simParamFilePath = path of the m-file with params 8 | % Outputs: -- 9 | % 10 | % Max Girnyk 11 | % Stockholm, 2014-10-01 12 | % 13 | % ========================================================================= 14 | % 15 | % This Matlab script produces results used in the following paper: 16 | % 17 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 18 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 19 | % 20 | % Paper URL: https://arxiv.org/abs/2111.03504 21 | % 22 | % Version: 1.0 (modified 2021-11-14) 23 | % 24 | % License: This code is licensed under the Apache-2.0 license. 25 | % If you use this code in any way for research that 26 | % results in a publication, please cite the above paper 27 | % 28 | % ========================================================================= 29 | 30 | % Load inputParamStruct --------------------------------------------------- 31 | load(simParamFilePath); 32 | 33 | % Signal-related params --------------------------------------------------- 34 | typeModulation = simCaseStruct.signaling.typeModulation; 35 | 36 | % Channel-related params -------------------------------------------------- 37 | snrIdxCurrent = simCaseStruct.channel.snrIdxCurrent; 38 | M = simCaseStruct.channel.nTxAntennas; 39 | N = simCaseStruct.channel.nRxAntennas; 40 | 41 | % Computation-related params ---------------------------------------------- 42 | methodSamplingMi = simCaseStruct.computation.methodSamplingMi; 43 | methodComputationMi = simCaseStruct.computation.methodComputationMi; 44 | methodSamplingMmse = simCaseStruct.computation.methodSamplingMmse; 45 | methodComputationMmse = simCaseStruct.computation.methodComputationMmse; 46 | 47 | % Container for precoders ------------------------------------------------- 48 | G = simCaseStruct.precoding.precoder; 49 | 50 | snrDb = simCaseStruct.channel.currentSnrDb; % current SNR point 51 | snr = 10^(snrDb/10); % SNR in linear scale 52 | H = sqrt(snr/M)*channelMat; % incorporate SNR into channel matrix 53 | 54 | fprintf('################################################################################################################\n'); 55 | fprintf('NEW COMPUTATION JOB\n'); 56 | fprintf('================================================================================================================\n'); 57 | fprintf('Scenario summary: \n'); 58 | fprintf('----------------------------------------------------------------------------------------------------------------\n'); 59 | fprintf(['Modulation type: \t\t\t\t\t', typeModulation, '\n']); 60 | fprintf(['Signal-to-noise ratio: \t\t\t\t', num2str(snrDb), ' [dB]\n']); 61 | fprintf([num2str(M), 'x', num2str(N), ' MIMO channel matrix: \n']); 62 | printComplexMatrix(H); 63 | fprintf('Precoder:\n'); 64 | printComplexMatrix(G); 65 | fprintf(['Computation method MI: \t\t\t\t', methodComputationMi, '\n']); 66 | fprintf(['Sampling method MI: \t\t\t\t', methodSamplingMi, '\n']); 67 | fprintf(['Computation method MMSE: \t\t\t', methodComputationMmse, '\n']); 68 | fprintf(['Sampling method MMSE: \t\t\t\t', methodSamplingMmse, '\n']); 69 | fprintf('================================================================================================================\n'); 70 | fprintf('COMPUTATION START... '); 71 | [miFinal, timeFinal] = computeMiMimo(H*G, typeModulation, 'TRUE', 'EXHAUSTIVE', 1e3, 2e3); 72 | fprintf('DONE!\n'); 73 | fprintf('================================================================================================================\n'); 74 | fprintf(['mutual info: ', num2str(miFinal), ' [bpcu]\n\n']); 75 | 76 | % Containers for performance metrics -------------------------------------- 77 | simCaseStruct.performance.miBpcu = miFinal; 78 | simCaseStruct.performance.timeElapsedSec = timeFinal; 79 | 80 | % Save performance to file -------------------------------------- 81 | save(simCaseStruct.cluster.matFileName, 'simCaseStruct'); 82 | 83 | end -------------------------------------------------------------------------------- /Functions/computeMiGivenPrecoderMimo.m: -------------------------------------------------------------------------------- 1 | function computeMiGivenPrecoderMimo(simParamFilePath) 2 | % 3 | % COMPUTEMIGIVENPRECODERMIMO Compute mutual info for a given precoder. 4 | % 5 | % Inputs: str simParamFilePath = path of the m-file with params 6 | % Outputs: -- 7 | % 8 | % Max Girnyk 9 | % Stockholm, 2014-10-01 10 | % 11 | % ========================================================================= 12 | % 13 | % This Matlab script produces results used in the following paper: 14 | % 15 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 16 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 17 | % 18 | % Paper URL: https://arxiv.org/abs/2111.03504 19 | % 20 | % Version: 1.0 (modified 2021-11-14) 21 | % 22 | % License: This code is licensed under the Apache-2.0 license. 23 | % If you use this code in any way for research that 24 | % results in a publication, please cite the above paper 25 | % 26 | % ========================================================================= 27 | 28 | % Load inputParamStruct --------------------------------------------------- 29 | load(simParamFilePath); 30 | 31 | % Signal-related params --------------------------------------------------- 32 | typeModulation = simCaseStruct.signaling.typeModulation; 33 | % Channel-related params -------------------------------------------------- 34 | H = simCaseStruct.channel.channelMat; 35 | M = simCaseStruct.channel.nTxAntennas; 36 | N = simCaseStruct.channel.nRxAntennas; 37 | snrIdxCurrent = simCaseStruct.channel.snrIdxCurrent; 38 | 39 | % Computation-related params ---------------------------------------------- 40 | methodSamplingMi = simCaseStruct.computation.methodSamplingMi; 41 | methodComputationMi = simCaseStruct.computation.methodComputationMi; 42 | methodSamplingMmse = simCaseStruct.computation.methodSamplingMmse; 43 | methodComputationMmse = simCaseStruct.computation.methodComputationMmse; 44 | 45 | % Container for precoders ------------------------------------------------- 46 | G = simCaseStruct.precoding.precoders{snrIdxCurrent}; 47 | 48 | snrDb = simCaseStruct.channel.currentSnrDb; % current SNR point 49 | snr = 10^(snrDb/10); % SNR in linear scale 50 | H = sqrt(snr/M)*channelMat; % incorporate SNR into channel matrix 51 | 52 | fprintf('################################################################################################################\n'); 53 | fprintf('NEW COMPUTATION JOB\n'); 54 | fprintf('================================================================================================================\n'); 55 | fprintf('Scenario summary: \n'); 56 | fprintf('----------------------------------------------------------------------------------------------------------------\n'); 57 | fprintf(['Modulation type: \t\t\t\t\t', typeModulation, '\n']); 58 | fprintf(['Signal-to-noise ratio: \t\t\t\t', num2str(snrDb), ' [dB]\n']); 59 | fprintf([num2str(M), 'x', num2str(N), ' MIMO channel matrix: \n']); 60 | printComplexMatrix(H); 61 | fprintf('Precoder:\n'); 62 | printComplexMatrix(G); 63 | fprintf(['Computation method MI: \t\t\t\t', methodComputationMi, '\n']); 64 | fprintf(['Sampling method MI: \t\t\t\t', methodSamplingMi, '\n']); 65 | fprintf(['Computation method MMSE: \t\t\t', methodComputationMmse, '\n']); 66 | fprintf(['Sampling method MMSE: \t\t\t\t', methodSamplingMmse, '\n']); 67 | fprintf('================================================================================================================\n'); 68 | fprintf('COMPUTATION START... '); 69 | [miFinal, timeFinal] = computeMiMimo(H*G, typeModulation, 'TRUE', 'EXHAUSTIVE', 1e3, 2e3); 70 | fprintf('DONE!\n'); 71 | fprintf('================================================================================================================\n'); 72 | fprintf(['mutual info: ', num2str(miFinal), ' [bpcu]\n\n']); 73 | 74 | % Containers for performance metrics -------------------------------------- 75 | simCaseStruct.performance.miBpcu = miFinal; 76 | simCaseStruct.performance.timeElapsedSec = timeFinal; 77 | 78 | % Save performance to file -------------------------------------- 79 | save(simCaseStruct.cluster.matFileName, 'simCaseStruct'); 80 | 81 | end -------------------------------------------------------------------------------- /Functions/computeMiMimo.m: -------------------------------------------------------------------------------- 1 | function [I, t] = computeMiMimo(channelMat, typeModulation, methodComputation, methodSampling, nIterSignal, nIterNoise) 2 | % 3 | % COMPUTEMIMIMO Computes mutual information for a MIMO channel for a given 4 | % approach by calling the respective algorithm. 5 | % 6 | % Inputs: mat channelMat = MIMO channel matrix 7 | % str typeModulation = modulation scheme 8 | % str methodComputation = method of MI matrix computation 9 | % str methodSampling = method of sampling: EXHAUSTIVE/RANDOMIZED 10 | % scalar nIterSignal = number of samples for averaging over signal 11 | % scalar nIterNoise = number of smaples for averaging over noise 12 | % Outputs: scalar I = mutual info 13 | % scalar t = time elapsed 14 | % 15 | % Max Girnyk 16 | % Stockholm, 2014-10-01 17 | % 18 | % ========================================================================= 19 | % 20 | % This Matlab script produces results used in the following paper: 21 | % 22 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 23 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 24 | % 25 | % Paper URL: https://arxiv.org/abs/2111.03504 26 | % 27 | % Version: 1.0 (modified 2021-11-14) 28 | % 29 | % License: This code is licensed under the Apache-2.0 license. 30 | % If you use this code in any way for research that 31 | % results in a publication, please cite the above paper 32 | % 33 | % ========================================================================= 34 | 35 | % Call respective functions for the MI computation 36 | switch methodComputation 37 | case 'GAUSS' 38 | [I, t] = miMimoGauss(channelMat); 39 | case 'GAUSS_CAPPED' 40 | error('ERROR! Implementation of method %s is missing!', methodComputation); 41 | case 'TRUE' 42 | [I, t] = miMimoTrue(channelMat, typeModulation, methodSampling, nIterSignal, nIterNoise); 43 | case 'KRASKOV_STOGBAUER_GRASSBERGER' 44 | error('ERROR! Implementation of method %s is missing!', methodComputation); 45 | case 'KOZACHENKO_LEONENKO' 46 | error('ERROR! Implementation of method %s is missing!', methodComputation); 47 | case 'EDGEWORTH' 48 | error('ERROR! Implementation of method %s is missing!', methodComputation); 49 | case 'ZHANG_CHEN_TAN' 50 | error('ERROR! Implementation of method %s is missing!', methodComputation); 51 | case 'GAUSS_HERMITE' 52 | error('ERROR! Implementation of method %s is missing!', methodComputation); 53 | case 'ZHU_SHI_FARHANG' 54 | [I, t] = miMimoZhuShiFarhang(channelMat, typeModulation, methodSampling, nIterSignal, nIterNoise); 55 | case 'HAMMING_DISTANCE_1' 56 | error('ERROR! Implementation of method %s is missing!', methodComputation); 57 | case 'ZENG_XIAO_LU' 58 | [I, t] = miMimoZengXiaoLu(channelMat, typeModulation, methodSampling, nIterSignal); 59 | end 60 | 61 | end -------------------------------------------------------------------------------- /Functions/convertBinToDec.m: -------------------------------------------------------------------------------- 1 | function d = convertBinToDec(b, varargin) 2 | %CONVERTBINTODEC Convert binary vectors to decimal numbers. 3 | % D = CONVERTBINTODEC(B) converts a binary vector B to a decimal value D. When B is 4 | % a matrix, the conversion is performed row-wise and the output D is a 5 | % column vector of decimal values. The default orientation of the binary 6 | % input is Right-MSB; the first element in B represents the least 7 | % significant bit. 8 | % 9 | 10 | % Typical error checking. 11 | error(nargchk(1,3,nargin,'struct')); 12 | 13 | % --- Placeholder for the signature string. 14 | sigStr = ''; 15 | flag = ''; 16 | p = []; 17 | 18 | % Check the type of the input B 19 | if ~(isnumeric(b) || islogical(b)) 20 | error(message('comm:convertBinToDec:InvalidInput1')); 21 | end 22 | 23 | inType = class(b); 24 | b = double(b); % To allow non-doubles to work 25 | 26 | % --- Identify string and numeric arguments 27 | for i=1:length(varargin) 28 | if(i>1) 29 | sigStr(size(sigStr,2)+1) = '/'; 30 | end 31 | % --- Assign the string and numeric flags 32 | if(ischar(varargin{i})) 33 | sigStr(size(sigStr,2)+1) = 's'; 34 | elseif(isnumeric(varargin{i})) 35 | sigStr(size(sigStr,2)+1) = 'n'; 36 | else 37 | error(message('comm:convertBinToDec:InvalidInputArg')); 38 | end 39 | end 40 | 41 | % --- Identify parameter signitures and assign values to variables 42 | switch sigStr 43 | 44 | % --- convertBinToDec(d) 45 | case '' 46 | 47 | % --- convertBinToDec(d, p) 48 | case 'n' 49 | p = varargin{1}; 50 | 51 | % --- convertBinToDec(d, flag) 52 | case 's' 53 | flag = varargin{1}; 54 | 55 | % --- convertBinToDec(d, p, flag) 56 | case 'n/s' 57 | p = varargin{1}; 58 | flag = varargin{2}; 59 | 60 | % --- convertBinToDec(d, flag, p) 61 | case 's/n' 62 | flag = varargin{1}; 63 | p = varargin{2}; 64 | 65 | % --- If the parameter list does not match one of these signatures. 66 | otherwise 67 | error(message('comm:convertBinToDec:InvalidSeqArg')); 68 | end 69 | 70 | if isempty(b) 71 | error(message('comm:convertBinToDec:InputEmpty')); 72 | end 73 | 74 | if max(max(b < 0)) || max(max(~isfinite(b))) || (~isreal(b)) || ... 75 | (max(max(floor(b) ~= b))) 76 | error(message('comm:convertBinToDec:InvalidInput2')); 77 | end 78 | 79 | % Set up the base to convert from. 80 | if isempty(p) 81 | p = 2; 82 | elseif max(size(p)) > 1 83 | error(message('comm:convertBinToDec:NonScalarBase')); 84 | elseif (floor(p) ~= p) || (~isfinite(p)) || (~isreal(p)) 85 | error(message('comm:convertBinToDec:InvalidBase')); 86 | elseif p < 2 87 | error(message('comm:convertBinToDec:BaseLessThan2')); 88 | end 89 | 90 | if max(max(b)) > (p-1) 91 | error(message('comm:convertBinToDec:InvalidInputElement')); 92 | end 93 | 94 | n = size(b,2); 95 | 96 | % If a flag is specified to flip the input such that the MSB is to the left. 97 | if isempty(flag) 98 | flag = 'right-msb'; 99 | elseif ~(strcmp(flag, 'right-msb') || strcmp(flag, 'left-msb')) 100 | error(message('comm:convertBinToDec:InvalidFlag')); 101 | end 102 | 103 | if strcmp(flag, 'left-msb') 104 | 105 | b2 = b; 106 | b = b2(:,n:-1:1); 107 | 108 | end 109 | 110 | %%% The conversion 111 | max_length = 1024; 112 | pow2vector = p.^(0:1:(size(b,2)-1)); 113 | size_B = min(max_length,size(b,2)); 114 | d = b(:,1:size_B)*pow2vector(:,1:size_B).'; 115 | 116 | % handle the infs... 117 | idx = find(max(b(:,max_length+1:size(b,2)).') == 1); 118 | d(idx) = inf; 119 | 120 | % data type conversion 121 | if ~strcmp(inType, 'logical') 122 | d = feval(inType, d); 123 | end 124 | 125 | % [EOF] 126 | -------------------------------------------------------------------------------- /Functions/convertComplexMatToRealVec.m: -------------------------------------------------------------------------------- 1 | function realVec = convertComplexMatToRealVec(complexMat) 2 | % 3 | % CONVERTCOMPLEXMATTOREALVEC Converts a complex-valued mat to a real-valued 4 | % vec by stacking columns and real/imaginary parts. 5 | % 6 | % Inputs: mat complexMat = complex matrix 7 | % Outputs: vec realVec = real vector 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | realVec = [real(complexMat(:)); imag(complexMat(:))]; 30 | end -------------------------------------------------------------------------------- /Functions/convertDecToBin.m: -------------------------------------------------------------------------------- 1 | function b = convertDecToBin(varargin) 2 | %CONVERTDECTOBIN Convert decimal numbers to binary numbers. 3 | % B = CONVERTDECTOBIN(D) converts a nonnegative integer decimal vector D to a 4 | % binary matrix B. Each row of the binary matrix B corresponds to one 5 | % element of D. The default orientation of the binary output is 6 | % Right-MSB; the first element in B represents the lowest bit. 7 | 8 | % Typical error checking. 9 | error(nargchk(1,4,nargin,'struct')); 10 | 11 | % --- Placeholder for the signature string. 12 | sigStr = ''; 13 | msbFlag = ''; 14 | p = []; 15 | n = []; 16 | 17 | % --- Identify string and numeric arguments 18 | for i=1:nargin 19 | if(i>1) 20 | sigStr(size(sigStr,2)+1) = '/'; 21 | end; 22 | % --- Assign the string and numeric flags 23 | if(ischar(varargin{i})) 24 | sigStr(size(sigStr,2)+1) = 's'; 25 | elseif(isnumeric(varargin{i})) 26 | sigStr(size(sigStr,2)+1) = 'n'; 27 | else 28 | error(message('comm:convertDecToBin:InvalidArg')); 29 | end; 30 | end; 31 | 32 | % --- Identify parameter signitures and assign values to variables 33 | switch sigStr 34 | % --- convertDecToBin(d) 35 | case 'n' 36 | d = varargin{1}; 37 | 38 | % --- convertDecToBin(d, n) 39 | case 'n/n' 40 | d = varargin{1}; 41 | n = varargin{2}; 42 | 43 | % --- convertDecToBin(d, msbFlag) 44 | case 'n/s' 45 | d = varargin{1}; 46 | msbFlag = varargin{2}; 47 | 48 | % --- convertDecToBin(d, n, msbFlag) 49 | case 'n/n/s' 50 | d = varargin{1}; 51 | n = varargin{2}; 52 | msbFlag = varargin{3}; 53 | 54 | % --- convertDecToBin(d, msbFlag, n) 55 | case 'n/s/n' 56 | d = varargin{1}; 57 | msbFlag = varargin{2}; 58 | n = varargin{3}; 59 | 60 | % --- convertDecToBin(d, n, p) 61 | case 'n/n/n' 62 | d = varargin{1}; 63 | n = varargin{2}; 64 | p = varargin{3}; 65 | 66 | % --- convertDecToBin(d, n, p, msbFlag) 67 | case 'n/n/n/s' 68 | d = varargin{1}; 69 | n = varargin{2}; 70 | p = varargin{3}; 71 | msbFlag = varargin{4}; 72 | 73 | % --- convertDecToBin(d, n, msbFlag, p) 74 | case 'n/n/s/n' 75 | d = varargin{1}; 76 | n = varargin{2}; 77 | msbFlag = varargin{3}; 78 | p = varargin{4}; 79 | 80 | % --- convertDecToBin(d, msbFlag, n, p) 81 | case 'n/s/n/n' 82 | d = varargin{1}; 83 | msbFlag = varargin{2}; 84 | n = varargin{3}; 85 | p = varargin{4}; 86 | 87 | % --- If the parameter list does not match one of these signatures. 88 | otherwise 89 | error(message('comm:convertDecToBin:InvalidArgSeq')); 90 | end; 91 | 92 | if isempty(d) 93 | error(message('comm:convertDecToBin:NoInput')); 94 | end 95 | 96 | inType = class(d); 97 | d = double(d(:)); 98 | len_d = length(d); 99 | 100 | if any(d(:) < 0) || any(~isfinite(d(:))) || ~isreal(d) || ~isequal(floor(d),d) 101 | error(message('comm:convertDecToBin:InvalidInput')); 102 | end 103 | 104 | % Assign the base to convert to. 105 | if isempty(p) 106 | p = 2; 107 | elseif max(size(p) ~= 1) 108 | error(message('comm:convertDecToBin:NonScalarBase')); 109 | elseif (~isfinite(p)) || (~isreal(p)) || (floor(p) ~= p) 110 | error(message('comm:convertDecToBin:InvalidBase')); 111 | elseif p < 2 112 | error(message('comm:convertDecToBin:BaseLessThan2')); 113 | end; 114 | 115 | % Determine minimum length required. 116 | tmp = max(d); 117 | if tmp ~= 0 % Want base-p log of tmp. 118 | ntmp = floor( log(tmp) / log(p) ) + 1; 119 | else % Since you can't take log(0). 120 | ntmp = 1; 121 | end 122 | 123 | % This takes care of any round off error that occurs for really big inputs. 124 | if ~( (p^ntmp) > tmp ) 125 | ntmp = ntmp + 1; 126 | end 127 | 128 | % Assign number of columns in output matrix. 129 | if isempty(n) 130 | n = ntmp; 131 | elseif max(size(n) ~= 1) 132 | error(message('comm:convertDecToBin:NonScalarN')); 133 | elseif (~isfinite(n)) || (~isreal(n)) || (floor(n) ~= n) 134 | error(message('comm:convertDecToBin:InvalidN')); 135 | elseif n < ntmp 136 | error(message('comm:convertDecToBin:SmallN')); 137 | end 138 | 139 | % Check if the string msbFlag is valid. 140 | if isempty(msbFlag) 141 | msbFlag = 'right-msb'; 142 | elseif ~(strcmp(msbFlag, 'right-msb') || strcmp(msbFlag, 'left-msb')) 143 | error(message('comm:convertDecToBin:InvalidMsbFlag')); 144 | end 145 | 146 | % Initial value. 147 | b = zeros(len_d, n); 148 | 149 | % Perform conversion. 150 | %Vectorized conversion for P=2 case 151 | if(p==2) 152 | [~,e]=log2(max(d)); % How many digits do we need to represent the numbers? 153 | b=rem(floor(d*pow2(1-max(n,e):0)),p); 154 | if strcmp(msbFlag, 'right-msb') 155 | b = fliplr(b); 156 | end; 157 | else 158 | for i = 1 : len_d % Cycle through each element of the input vector/matrix. 159 | j = 1; 160 | tmp = d(i); 161 | while (j <= n) && (tmp > 0) % Cycle through each digit. 162 | b(i, j) = rem(tmp, p); % Determine current digit. 163 | tmp = floor(tmp/p); 164 | j = j + 1; 165 | end; 166 | end; 167 | % If a msbFlag is specified to flip the output such that the MSB is to the left. 168 | if strcmp(msbFlag, 'left-msb') 169 | b2 = b; 170 | b = b2(:,n:-1:1); 171 | end; 172 | end; 173 | 174 | b = feval(inType, b); % data type conversion 175 | 176 | end -------------------------------------------------------------------------------- /Functions/convertNumberToCaseIdxTag.m: -------------------------------------------------------------------------------- 1 | function caseIdxTag = convertNumberToCaseIdxTag(number, nDigitsMax) 2 | % 3 | % CONVERTNUMBERTOCASEIDXTAG Convert a number to a string tag (adding zeros 4 | % in the beginning, e.g., to have a consistent file naming in the folder). 5 | % 6 | % Inputs: scalar number = a number to be converted 7 | % scalar nDigitsMax = max number of digits allowed 8 | % Outputs: str caseIdxTag = string case tag 9 | % 10 | % Max Girnyk 11 | % Stockholm, 2014-10-01 12 | % 13 | % ========================================================================= 14 | % 15 | % This Matlab script produces results used in the following paper: 16 | % 17 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 18 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 19 | % 20 | % Paper URL: https://arxiv.org/abs/2111.03504 21 | % 22 | % Version: 1.0 (modified 2021-11-14) 23 | % 24 | % License: This code is licensed under the Apache-2.0 license. 25 | % If you use this code in any way for research that 26 | % results in a publication, please cite the above paper 27 | % 28 | % ========================================================================= 29 | 30 | caseIdxTag = ''; 31 | if (number <= 10^nDigitsMax-1) 32 | caseIdxDigits = zeros(1, nDigitsMax); 33 | orders = fliplr(10.^[0:nDigitsMax-1]); 34 | for iDigit = 1:nDigitsMax 35 | caseIdxDigits(iDigit) = floor( (number - sum(caseIdxDigits(1:iDigit-1).*orders(1:iDigit-1))) / orders(iDigit) ); 36 | caseIdxTag = [caseIdxTag, num2str(caseIdxDigits(iDigit))]; 37 | end 38 | else 39 | error('ERROR! Input number out of bounds!') 40 | end 41 | 42 | end -------------------------------------------------------------------------------- /Functions/convertNumberToSnrTag.m: -------------------------------------------------------------------------------- 1 | function tag = convertNumberToSnrTag(number) 2 | % 3 | % CONVERTNUMBERTOSNRTAG Convert a float number to a string where the 4 | % decimal point is substituted with M/P to create a convenient tag for SNRs 5 | % for naming simulation files 6 | % 7 | % Inputs: scalar number = a number to be converted 8 | % Outputs: str tag = SNR tag 9 | % 10 | % Max Girnyk 11 | % Stockholm, 2014-10-01 12 | % 13 | % ========================================================================= 14 | % 15 | % This Matlab script produces results used in the following paper: 16 | % 17 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 18 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 19 | % 20 | % Paper URL: https://arxiv.org/abs/2111.03504 21 | % 22 | % Version: 1.0 (modified 2021-11-14) 23 | % 24 | % License: This code is licensed under the Apache-2.0 license. 25 | % If you use this code in any way for research that 26 | % results in a publication, please cite the above paper 27 | % 28 | % ========================================================================= 29 | 30 | % Decouple the sign, integer and fractional parts 31 | numberSign = 2*(number>=0)-1; 32 | numberIntegerDigits = floor(abs(number)); 33 | maxFractionalDigit = 6; 34 | tol = 1e-6; 35 | numberRounded = numberSign*floor(abs(number * 10^maxFractionalDigit)) * 10^(-maxFractionalDigit); 36 | numberFractionalPart = abs(numberRounded) - numberIntegerDigits; 37 | dummyVar = abs(numberRounded) * 10.^(1:maxFractionalDigit); 38 | nFractionalDigits = find(abs(dummyVar-round(dummyVar))0); 49 | lambdaP(negativeIdx) = 0; 50 | newM = length(positiveIdx); 51 | lambdaHnew = lambdaH(positiveIdx); 52 | lambdaPtemp = ( sum(1./(lambdaHnew.^2)) + K )/newM - 1./(lambdaHnew.^2); 53 | lambdaP(positiveIdx) = lambdaPtemp; 54 | canGoOn = ~isempty( find(lambdaP < 0, 1) ); 55 | end % while canGoOn 56 | 57 | Y = diag(lambdaP); 58 | X(1:K, 1:K) = Y(1:K, 1:K); 59 | 60 | % Capacity-achieving linear precoder 61 | G = V * sqrt(X); 62 | 63 | end -------------------------------------------------------------------------------- /Functions/miMimoGauss.m: -------------------------------------------------------------------------------- 1 | function [I, t] = miMimoGauss(Hmat) 2 | % 3 | % MIMIMOGAUSS Computes the mutual info of a MIMO channel with complex noise 4 | % and Gaussian inputs. 5 | % 6 | % Inputs: mat Hmat = MIMO channel matrix 7 | % Outputs: scalar I = mutual info between input and output 8 | % scalar t = computation time 9 | % 10 | % Max Girnyk 11 | % Stockholm, 2014-10-01 12 | % 13 | % ========================================================================= 14 | % 15 | % This Matlab script produces results used in the following paper: 16 | % 17 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 18 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 19 | % 20 | % Paper URL: https://arxiv.org/abs/2111.03504 21 | % 22 | % Version: 1.0 (modified 2021-11-14) 23 | % 24 | % License: This code is licensed under the Apache-2.0 license. 25 | % If you use this code in any way for research that 26 | % results in a publication, please cite the above paper 27 | % 28 | % ========================================================================= 29 | 30 | % Channel matrix size 31 | [N, M] = size(Hmat); 32 | 33 | timeBegin = cputime; % start clock 34 | 35 | % Shannon's formula 36 | I = real(log(det(eye(N) + (Hmat*Hmat')))) / M / log(2); 37 | 38 | timeEnd = cputime; % stop clock 39 | 40 | % Compute time elapsed 41 | t = (timeEnd-timeBegin)/60; 42 | 43 | end -------------------------------------------------------------------------------- /Functions/miMimoTrue.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/girnyk/OptimalPrecodingMimo/9a9d46a7b9618d6b966ba4c9dbe164d9cc5a5004/Functions/miMimoTrue.m -------------------------------------------------------------------------------- /Functions/miMimoZengXiaoLu.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/girnyk/OptimalPrecodingMimo/9a9d46a7b9618d6b966ba4c9dbe164d9cc5a5004/Functions/miMimoZengXiaoLu.m -------------------------------------------------------------------------------- /Functions/miMimoZhuShiFarhang.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/girnyk/OptimalPrecodingMimo/9a9d46a7b9618d6b966ba4c9dbe164d9cc5a5004/Functions/miMimoZhuShiFarhang.m -------------------------------------------------------------------------------- /Functions/mmseMatrixMimoGauss.m: -------------------------------------------------------------------------------- 1 | function [E, time] = mmseMatrixMimoGauss(Hmat) 2 | % 3 | % MMSEMATRIXMIMOGAUSS Computes the MMSE matrix of a MIMO channel with 4 | % complex noise and Gaussian inputs. 5 | % 6 | % Inputs: mat Hmat = MIMO channel matrix 7 | % Outputs: mat E = MMSE matrix of the MIMO channel 8 | % scalar t = computation time 9 | % 10 | % Max Girnyk 11 | % Stockholm, 2014-10-01 12 | % 13 | % ========================================================================= 14 | % 15 | % This Matlab script produces results used in the following paper: 16 | % 17 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 18 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 19 | % 20 | % Paper URL: https://arxiv.org/abs/2111.03504 21 | % 22 | % Version: 1.0 (modified 2021-11-14) 23 | % 24 | % License: This code is licensed under the Apache-2.0 license. 25 | % If you use this code in any way for research that 26 | % results in a publication, please cite the above paper 27 | % 28 | % ========================================================================= 29 | 30 | M = size(Hmat, 2); 31 | 32 | timeBegin = cputime; % start clock 33 | 34 | E = eye(M) / (eye(M) + (Hmat'*Hmat)); 35 | 36 | timeEnd = cputime; % end clock 37 | 38 | time = (timeEnd-timeBegin)/60; % computation time 39 | 40 | end 41 | -------------------------------------------------------------------------------- /Functions/mmseMatrixMimoTrue.m: -------------------------------------------------------------------------------- 1 | function [E, iIterNoise] = mmseMatrixMimoTrue(Hmat, typeModulation, typeSampling, nIterSignal, nIterNoise) 2 | % 3 | % MMSEMATRIXMIMOGAUSS Compute the MMSE matrix (error covariance) of a 4 | % MIMO channel with complex noise and finite-alphabet inputs, according to: 5 | % C. Xiao, Y. R. Zeng, "Transmit precoding for MIMO systems with 6 | % partial CSI and discrete-constellation inputs," in Proc. ICC, 2009. 7 | % 8 | % Inputs: mat Hmat = MIMO channel matrix 9 | % str typeModulation = type of signal constellation 10 | % str typeSampling = type of averaging over the signals: EXHAUSTIVE/RANDOMIZED 11 | % scalar nIterSignal = number of iters for avg over signals 12 | % scalar nIterNoise = number of iters for avg over noise 13 | % Outputs: mat E = MMSE matrix of the MIMO channel 14 | % scalar t = computation time 15 | % 16 | % Max Girnyk 17 | % Stockholm, 2014-10-01 18 | % 19 | % ========================================================================= 20 | % 21 | % This Matlab script produces results used in the following paper: 22 | % 23 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 24 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 25 | % 26 | % Paper URL: https://arxiv.org/abs/2111.03504 27 | % 28 | % Version: 1.0 (modified 2021-11-14) 29 | % 30 | % License: This code is licensed under the Apache-2.0 license. 31 | % If you use this code in any way for research that 32 | % results in a publication, please cite the above paper 33 | % 34 | % ========================================================================= 35 | 36 | % Channel matrix sizes 37 | [N, M] = size(Hmat); 38 | 39 | Sset = modulationFiniteAlphabet(typeModulation); % signal constellation 40 | lSset = length(Sset); % size of the constellation 41 | 42 | % Sample modulated symbols for averaging over signal vector 43 | switch(typeSampling) 44 | case 'EXHAUSTIVE' 45 | % Create a combination of symbols over antennas 46 | nSymbols = lSset^M; 47 | modIndex = convertDecToBin([0:nSymbols-1], M, lSset).'; 48 | case 'RANDOMIZED' 49 | % Pick symbols randomly 50 | nSymbols = nIterSignal; 51 | modIndex = randi([1 lSset], M, nIterSignal) - 1; 52 | end 53 | x = Sset(modIndex+1); % pick constellation point 54 | 55 | timeBegin = cputime; % start clock 56 | 57 | % Loop over noise (n) ----------------------------------------------------- 58 | sumOverNoise = zeros(M,M); 59 | for iIterNoise = 1 : nIterNoise 60 | n = 1/sqrt(2)*(randn(N,1) + 1i*randn(N,1)); 61 | sumOverTrueSignal = zeros(M,M); 62 | 63 | % Loop over true signal (x0, giving y = H x0 + n) ----------------------- 64 | for iTrueSymbolVec = 1 : nSymbols 65 | sumOverSignal = zeros(M,1); 66 | sumOverProbRxSignal = 0; 67 | 68 | % Loop over signal (x) ------------------------------------------------ 69 | for iSymbolVec = 1 : nSymbols 70 | sumOverSignal = sumOverSignal + x(:,iSymbolVec)*exp(-norm(Hmat*(x(:,iTrueSymbolVec)-x(:,iSymbolVec)) + n)^2); 71 | sumOverProbRxSignal = sumOverProbRxSignal + exp(-norm(Hmat*(x(:,iTrueSymbolVec)-x(:,iSymbolVec)) + n)^2); 72 | end % for iSymbolVec = 1 : nSymbols 73 | 74 | sumOverTrueSignal = sumOverTrueSignal + (sumOverSignal)*sumOverSignal'/sumOverProbRxSignal^2; 75 | end % for iTrueSymbolVec = 1 : nSymbols 76 | 77 | sumOverNoise = sumOverNoise + sumOverTrueSignal / nSymbols; 78 | end % for iIterNoise = 1 : nIterNoise 79 | 80 | % Compute MMSE matrix 81 | E = eye(M) - sumOverNoise / nIterNoise; 82 | 83 | timeEnd = cputime; % stop clock 84 | 85 | iIterNoise = (timeEnd - timeBegin)/60; % compute execution time 86 | 87 | end 88 | 89 | -------------------------------------------------------------------------------- /Functions/modulationFiniteAlphabet.m: -------------------------------------------------------------------------------- 1 | function Sset = modulationFiniteAlphabet(typeModulation) 2 | % 3 | % MODULATIONFINITEALPHABET Return a finite-alphabet signal constellation. 4 | % 5 | % Inputs: str typeModulation = type of signal constellation 6 | % Outputs: vec Sset = vector of signal constellation points 7 | % 8 | % Max Girnyk 9 | % Stockholm, 2014-10-01 10 | % 11 | % ========================================================================= 12 | % 13 | % This Matlab script produces results used in the following paper: 14 | % 15 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 16 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 17 | % 18 | % Paper URL: https://arxiv.org/abs/2111.03504 19 | % 20 | % Version: 1.0 (modified 2021-11-14) 21 | % 22 | % License: This code is licensed under the Apache-2.0 license. 23 | % If you use this code in any way for research that 24 | % results in a publication, please cite the above paper 25 | % 26 | % ========================================================================= 27 | 28 | % Modulation constellation 29 | switch (typeModulation) 30 | case 'BPSK' 31 | Sset = [-1, 1]; 32 | case 'QPSK' 33 | Sset = sqrt(1/2)*[1+1j, 1-1j, -1-1j, -1+1j]; 34 | case '8PSK' 35 | Sset = sqrt(1/2)*exp(1i*2*pi*linspace(0, 0.875, 8)); 36 | case '16QAM' 37 | Sset = sqrt(1/10)*[-3-3*1i, -3-1*1i, -3+1*1i, -3+3*1i,... 38 | -1+3*1i, -1+1*1i, -1-1*1i, -1-3*1i,... 39 | 1-3*1i, 1-1*1i, +1+1*1i, 1+3*1i,... 40 | 3+3*1i, 3+1*1i, 3-1*1i, 3-3*1i]; 41 | case '64QAM' 42 | Sset = sqrt(1/42)*[-7-7*1i, -7-5*1i, -7-3*1i, -7-1*1i,... 43 | -5-7*1i, -5-5*1i, -5-3*1i, -5-1*1i,... 44 | -3-7*1i, -3-5*1i, -3-3*1i, -3-1*1i,... 45 | -1-7*1i, -1-5*1i, -1-3*1i, -1-1*1i,... 46 | -7+7*1i, -7+5*1i, -7+3*1i, -7+1*1i,... 47 | -5+7*1i, -5+5*1i, -5+3*1i, -5+1*1i,... 48 | -3+7*1i, -3+5*1i, -3+3*1i, -3+1*1i,... 49 | -1+7*1i, -1+5*1i, -1+3*1i, -1+1*1i,... 50 | +7-7*1i, +7-5*1i, +7-3*1i, +7-1*1i,... 51 | +5-7*1i, +5-5*1i, +5-3*1i, +5-1*1i,... 52 | +3-7*1i, +3-5*1i, +3-3*1i, +3-1*1i,... 53 | +1-7*1i, +1-5*1i, +1-3*1i, +1-1*1i,... 54 | +7+7*1i, +7+5*1i, +7+3*1i, +7+1*1i,... 55 | +5+7*1i, +5+5*1i, +5+3*1i, +5+1*1i,... 56 | +3+7*1i, +3+5*1i, +3+3*1i, +3+1*1i,... 57 | +1+7*1i, +1+5*1i, +1+3*1i, +1+1*1i]; 58 | end; 59 | end 60 | 61 | -------------------------------------------------------------------------------- /Functions/optimizePrecoderGivenChannelMimoMl.m: -------------------------------------------------------------------------------- 1 | function optimizePrecoderGivenChannelMimoMl(simParamFilePath) 2 | % 3 | % OPTIMIZEPRECODERGIVENCHANNELMIMOML Optimize/predict precoder using a 4 | % trained neural net. 5 | % 6 | % Inputs: str simParamFilePath = path of the m-file with params 7 | % Outputs: -- 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | % Load inputParamStruct --------------------------------------------------- 30 | load(simParamFilePath); 31 | 32 | % Channel-related params -------------------------------------------------- 33 | snrIdxCurrent = simCaseStruct.channel.snrIdxCurrent; 34 | channelMat = simCaseStruct.channel.channelMats{snrIdxCurrent}; 35 | nTxAntennas = simCaseStruct.channel.nTxAntennas; 36 | snrDb = simCaseStruct.channel.snrDbVec(snrIdxCurrent); 37 | 38 | simName = simCaseStruct.cluster.simName; 39 | caseIdx = simCaseStruct.cluster.caseIdx; 40 | nOrderCasesMax = simCaseStruct.cluster.nOrderCasesMax; 41 | caseIdxTag = convertNumberToCaseIdxTag(caseIdx, nOrderCasesMax); 42 | caseId = [simName, '_', caseIdxTag]; % ID for the sim case 43 | simFolderPath = simCaseStruct.cluster.simFolderPath; 44 | simCaseSubfolderPath = [simFolderPath, '\', caseId]; 45 | 46 | % Signal-related params --------------------------------------------------- 47 | typeModulation = simCaseStruct.signaling.typeModulation; 48 | 49 | % Cluster-related params -------------------------------------------------- 50 | evaluateTrueMiMeanwhile = simCaseStruct.cluster.evaluateTrueMiMeanwhile; 51 | 52 | % Create the case subfolder 53 | if (~exist(simCaseSubfolderPath)) 54 | mkdir(simCaseSubfolderPath); 55 | end 56 | 57 | % Path to trained neural nets 58 | netsPath = [pwd, '\TrainedNets']; 59 | 60 | switch typeModulation 61 | case 'BPSK' 62 | switch nTxAntennas 63 | case 2 64 | load([netsPath, '\nnWeightsBpsk2x2Mimo.mat']); 65 | case 3 66 | load([netsPath, '\nnWeightsBpsk3x3Mimo.mat']); 67 | otherwise 68 | error('Unknown antenna stup'); 69 | end 70 | case 'QPSK' 71 | switch nTxAntennas 72 | case 2 73 | load([netsPath, '\nnWeightsQpsk3x3Mimo.mat']); 74 | case 3 75 | load([netsPath, '\nnWeightsQpsk3x3Mimo.mat']); 76 | otherwise 77 | error('ERROR! Unknown antenna setup'); 78 | end 79 | otherwise 80 | error('ERROR! No trained neural net for this constellation'); 81 | end 82 | 83 | 84 | snr = 10^(snrDb/10); 85 | snrTag = convertNumberToSnrTag(snrDb); 86 | jobId = [caseId, '_', snrTag]; 87 | simFileName = ['mi_', jobId]; 88 | 89 | simFilePath = [simCaseSubfolderPath, '\', simFileName]; 90 | matFilePath = [simFilePath, '.mat']; 91 | 92 | simCaseStruct.cluster.matFilePath = matFilePath; 93 | simCaseStruct.cluster.status = 'RUNNING'; 94 | 95 | [~, nTxAntennas] = size(channelMat); 96 | Hmat = sqrt(snr/nTxAntennas) * channelMat; 97 | 98 | timeBegin = cputime; % start clock 99 | 100 | precoderMatGauss = getWfPrecoder(Hmat); 101 | precoderVecGauss = convertComplexMatToRealVec(precoderMatGauss); 102 | precoderVecPredicted = runFeedforwardPass(neuralNet, precoderVecGauss); 103 | precoderMatPredicted = convertRealVecToComlpexMat(precoderVecPredicted, N, nTxAntennas); 104 | G = precoderMatPredicted * sqrt(nTxAntennas)/sqrt(trace(precoderMatPredicted'*precoderMatPredicted)); 105 | 106 | timeEnd = cputime; % stop clock 107 | 108 | if evaluateTrueMiMeanwhile 109 | [I, ~] = computeMiMimo(Hmat*G, typeModulation, 'TRUE', 'EXHAUSTIVE', 5e3, 7e3); 110 | else 111 | [I, ~] = computeMiMimo(Hmat*G, typeModulation, 'TRUE', 'EXHAUSTIVE', 1e2, 1e3); 112 | end 113 | 114 | % Container for precoders ------------------------------------------------- 115 | simCaseStruct.precoding.precoderReal = real(G); 116 | simCaseStruct.precoding.precoderImag = imag(G); 117 | 118 | % Containers for performance metrics -------------------------------------- 119 | simCaseStruct.performance.miBpcu = I; 120 | simCaseStruct.performance.timeElapsedSec = (timeEnd - timeBegin)/60; 121 | simCaseStruct.cluster.status = 'COMPLETED'; 122 | 123 | % Save all params to file 124 | save(simCaseStruct.cluster.matFilePath, 'simCaseStruct'); -------------------------------------------------------------------------------- /Functions/optimizePrecoderMimo.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/girnyk/OptimalPrecodingMimo/9a9d46a7b9618d6b966ba4c9dbe164d9cc5a5004/Functions/optimizePrecoderMimo.m -------------------------------------------------------------------------------- /Functions/optimizePrecoderMimoGauss.m: -------------------------------------------------------------------------------- 1 | function precoder = optimizePrecoderMimoGauss(simCaseStruct) 2 | % 3 | % OPTIMIZEPRECODERMIMOGAUSS Water-filling based precoder for maximizing 4 | % the achievable rates for a MIMO channel with Gaussian inputs. 5 | % 6 | % Inputs: struct simCaseStruct = struct of sim case params 7 | % Outputs: mat precoder = linear precoding matrix 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | % Cluster-related params -------------------------------------------------- 30 | simName = simCaseStruct.cluster.simName; 31 | caseIdx = simCaseStruct.cluster.caseIdx; 32 | nOrderCasesMax = simCaseStruct.cluster.nOrderCasesMax; 33 | caseIdxTag = convertNumberToCaseIdxTag(caseIdx, nOrderCasesMax); 34 | caseId = [simName, '_', caseIdxTag]; % ID for the sim case 35 | simFolderPath = simCaseStruct.cluster.simFolderPath; 36 | simCaseSubfolderPath = [simFolderPath, '\', caseId]; 37 | 38 | % Channel-related params -------------------------------------------------- 39 | snrDbVec = simCaseStruct.channel.snrDbVec; 40 | nSnrDb = length(snrDbVec); 41 | 42 | % Create the case subfolder 43 | if (~exist(simCaseSubfolderPath)) 44 | mkdir(simCaseSubfolderPath); 45 | end 46 | 47 | % Loop over SNRs ---------------------------------------------------------- 48 | for iSnrDb = 1 : nSnrDb 49 | 50 | % SNR value in linear scale 51 | snrDb = snrDbVec(iSnrDb); 52 | simCaseStruct.channel.currentSnrDb = snrDb; 53 | snr = 10^(snrDb/10); 54 | snrTag = convertNumberToSnrTag(snrDb); 55 | 56 | % Sim case formal params 57 | jobId = [caseId, '_', snrTag]; 58 | simFileName = ['mi_', jobId]; 59 | simFilePath = [simCaseSubfolderPath, '\', simFileName]; 60 | matFilePath = [simFilePath, '.mat']; 61 | simCaseStruct.cluster.matFilePath = matFilePath; 62 | simCaseStruct.cluster.status = 'RUNNING'; 63 | 64 | % Channel matrix 65 | channelMat = simCaseStruct.channel.channelMatReal + 1i*simCaseStruct.channel.channelMatImag; 66 | [~, M] = size(channelMat); 67 | Hmat = sqrt(snr/M) * channelMat; 68 | 69 | timeBegin = cputime; % start clock 70 | 71 | % Compute water-filling precoder 72 | precoder = getWfPrecoder(Hmat); 73 | 74 | timeEnd = cputime; % stop clock 75 | 76 | % Performance metrics 77 | I = miMimoGauss(Hmat*precoder); 78 | 79 | % Container for precoders ------------------------------------------------- 80 | simCaseStruct.precoding.precoderReal = real(precoder); 81 | simCaseStruct.precoding.precoderImag = imag(precoder); 82 | 83 | % Containers for performance metrics -------------------------------------- 84 | simCaseStruct.performance.miBpcu = I; 85 | simCaseStruct.performance.timeElapsedSec = (timeEnd - timeBegin)/60; 86 | simCaseStruct.cluster.status = 'COMPLETED'; 87 | 88 | % Save all params to mat-file 89 | save(simCaseStruct.cluster.matFilePath, 'simCaseStruct'); 90 | 91 | end % for iSnrDb = 1 : nSnrDb -------------------------------------------------------------------------------- /Functions/optimizePrecoderMimoGaussOpt.m: -------------------------------------------------------------------------------- 1 | function G = optimizePrecoderMimoGaussOpt(simParamFilePath) 2 | % 3 | % OPTIMIZEPRECODERMIMOGAUSSOPT Computing the capacity-achieving 4 | % water-filling based precoder. 5 | % 6 | % Inputs: str simParamFilePath = path of the m-file with params 7 | % Outputs: -- 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | % Load inputParamStruct --------------------------------------------------- 30 | load(simParamFilePath); 31 | 32 | % Signal-related params --------------------------------------------------- 33 | typeModulation = simCaseStruct.signaling.typeModulation; 34 | 35 | % Cluster-related params -------------------------------------------------- 36 | evaluateTrueMiMeanwhile = simCaseStruct.cluster.evaluateTrueMiMeanwhile; 37 | simName = simCaseStruct.cluster.simName; 38 | caseIdx = simCaseStruct.cluster.caseIdx; 39 | nOrderCasesMax = simCaseStruct.cluster.nOrderCasesMax; 40 | caseIdxTag = convertNumberToCaseIdxTag(caseIdx, nOrderCasesMax); 41 | caseId = [simName, '_', caseIdxTag]; % ID for the sim case 42 | simFolderPath = simCaseStruct.cluster.simFolderPath; 43 | simCaseSubfolderPath = [simFolderPath, '\', caseId]; 44 | 45 | % Channel-related params -------------------------------------------------- 46 | snrDbVec = simCaseStruct.channel.snrDbVec; 47 | nSnrDb = length(snrDbVec); 48 | 49 | % Create the case subfolder 50 | if (~exist(simCaseSubfolderPath)) 51 | mkdir(simCaseSubfolderPath); 52 | end 53 | 54 | % Loop over SNRs ---------------------------------------------------------- 55 | for iSnrDb = 1 : nSnrDb 56 | 57 | snrDb = snrDbVec(iSnrDb); 58 | simCaseStruct.channel.currentSnrDb = snrDb; 59 | snr = 10^(snrDb/10); 60 | snrTag = convertNumberToSnrTag(snrDb); 61 | jobId = [caseId, '_', snrTag]; 62 | simFileName = ['mi_', jobId]; 63 | simFilePath = [simCaseSubfolderPath, '\', simFileName]; 64 | matFilePath = [simFilePath, '.mat']; 65 | simCaseStruct.cluster.matFilePath = matFilePath; 66 | simCaseStruct.cluster.status = 'RUNNING'; 67 | 68 | channelMat = simCaseStruct.channel.channelMatReal + 1i*simCaseStruct.channel.channelMatImag; 69 | 70 | [~, M] = size(channelMat); 71 | H = sqrt(snr/M) * channelMat; 72 | 73 | timeBegin = cputime; % start clock 74 | 75 | % Compute actual water-filling precoder 76 | G = getWfPrecoder(H); 77 | 78 | timeEnd = cputime; % stop clock 79 | 80 | if evaluateTrueMiMeanwhile 81 | [I, ~] = computeMiMimo(H*G, typeModulation, 'TRUE', 'EXHAUSTIVE', 5e3, 1e4); 82 | else 83 | [I, ~] = computeMiMimo(H*G, typeModulation, 'TRUE', 'EXHAUSTIVE', 1e2, 1e3); 84 | end 85 | 86 | 87 | % Container for precoders ------------------------------------------------- 88 | simCaseStruct.precoding.precoderReal = real(G); 89 | simCaseStruct.precoding.precoderImag = imag(G); 90 | 91 | % Containers for performance metrics -------------------------------------- 92 | simCaseStruct.performance.miBpcu = I; 93 | simCaseStruct.performance.timeElapsedSec = (timeEnd - timeBegin)/60; 94 | simCaseStruct.cluster.status = 'COMPLETED'; 95 | 96 | % ?ave all params to file 97 | save(simCaseStruct.cluster.matFilePath, 'simCaseStruct'); 98 | 99 | end % for iSnrDb = 1 : nSnrDb -------------------------------------------------------------------------------- /Functions/optimizePrecoderMimoMl.m: -------------------------------------------------------------------------------- 1 | function precoder = optimizePrecoderMimoMl(simCaseStruct) 2 | % 3 | % OPTIMIZEPRECODERMIMOML DL-based approximation of the optimal precoder, 4 | % computed by means of a forward pass thru a trained neural net. 5 | % 6 | % Inputs: struct simCaseStruct = struct of sim case params 7 | % Outputs: mat precoder = linear precoding matrix 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | % Path to trained neural nets 30 | netsPath = [pwd, '\TrainedNets']; 31 | 32 | % Signal-related params --------------------------------------------------- 33 | typeModulation = simCaseStruct.signaling.typeModulation; 34 | 35 | % Computation-related params ---------------------------------------------- 36 | nItersSignalMi = simCaseStruct.computation.nItersSignalMi; 37 | nItersNoiseMi = simCaseStruct.computation.nItersNoiseMi; 38 | 39 | % Cluster-related params -------------------------------------------------- 40 | simName = simCaseStruct.cluster.simName; 41 | caseIdx = simCaseStruct.cluster.caseIdx; 42 | nOrderCasesMax = simCaseStruct.cluster.nOrderCasesMax; 43 | caseIdxTag = convertNumberToCaseIdxTag(caseIdx, nOrderCasesMax); 44 | caseId = [simName, '_', caseIdxTag]; % ID for the sim case 45 | simFolderPath = simCaseStruct.cluster.simFolderPath; 46 | simCaseSubfolderPath = [simFolderPath, '\', caseId]; 47 | evaluateTrueMiMeanwhile = simCaseStruct.cluster.evaluateTrueMiMeanwhile; 48 | 49 | % Channel-related params -------------------------------------------------- 50 | snrDbVec = simCaseStruct.channel.snrDbVec; 51 | nSnrDb = length(snrDbVec); 52 | 53 | % Create the case subfolder 54 | if (~exist(simCaseSubfolderPath)) 55 | mkdir(simCaseSubfolderPath); 56 | end 57 | 58 | % Download trained neural net 59 | switch simCaseStruct.signaling.typeModulation 60 | case 'BPSK' 61 | switch simCaseStruct.channel.nTxAntennas 62 | case 2 63 | load([netsPath, '\nnWeightsBpsk2x2Mimo.mat']); 64 | case 3 65 | load([netsPath, '\nnWeightsBpsk3x3Mimo.mat']); 66 | otherwise 67 | error('ERROR! Trained weights for this antenna setup are missing!'); 68 | end 69 | case 'QPSK' 70 | switch simCaseStruct.channel.nTxAntennas 71 | case 2 72 | load([netsPath, '\nnWeightsQpsk2x2Mimo.mat']); 73 | case 3 74 | load([netsPath, '\nnWeightsQpsk3x3Mimo.mat']); 75 | otherwise 76 | error('ERROR! Trained weights for this antenna setup are missing!'); 77 | end 78 | case '8PSK' 79 | error('ERROR! Trained weights for this antenna setup are missing!'); 80 | end 81 | 82 | % Loop over SNRs ---------------------------------------------------------- 83 | for iSnrDb = 1 : nSnrDb 84 | 85 | % SNR value in linear scale 86 | snrDb = snrDbVec(iSnrDb); 87 | simCaseStruct.channel.currentSnrDb = snrDb; 88 | snr = 10^(snrDb/10); 89 | snrTag = convertNumberToSnrTag(snrDb); 90 | 91 | % Sim case formal params 92 | jobId = [caseId, '_', snrTag]; 93 | simFileName = ['mi_', jobId]; 94 | simFilePath = [simCaseSubfolderPath, '\', simFileName]; 95 | matFilePath = [simFilePath, '.mat']; 96 | simCaseStruct.cluster.matFilePath = matFilePath; 97 | simCaseStruct.cluster.status = 'RUNNING'; 98 | 99 | % Channel matrix 100 | channelMat = simCaseStruct.channel.channelMatReal + 1i*simCaseStruct.channel.channelMatImag; 101 | [N, M] = size(channelMat); 102 | Hmat = sqrt(snr/M) * channelMat; 103 | 104 | timeBegin = cputime; % start clock 105 | 106 | % Predict optimal precoder matrix using forward pass 107 | precoderMatGauss = getWfPrecoder(Hmat); 108 | precoderVecGauss = convertComplexMatToRealVec(precoderMatGauss); 109 | precoderVecPredicted = runFeedforwardPass(neuralNet, precoderVecGauss); 110 | precoderMatPredicted = convertRealVecToComlpexMat(precoderVecPredicted, N, M); 111 | precoder = precoderMatPredicted * sqrt(M)/sqrt(trace(precoderMatPredicted'*precoderMatPredicted)); 112 | 113 | timeEnd = cputime; % stop clock 114 | 115 | % Performance metrics 116 | if evaluateTrueMiMeanwhile 117 | [I, ~] = computeMiMimo(Hmat*precoder, typeModulation, 'TRUE', 'EXHAUSTIVE', 5e3, 7e3); 118 | else 119 | [I, ~] = computeMiMimo(Hmat*precoder, typeModulation, 'TRUE', 'EXHAUSTIVE', nItersSignalMi, nItersNoiseMi); 120 | end 121 | 122 | % Container for precoders ------------------------------------------------- 123 | simCaseStruct.precoding.precoderReal = real(precoder); 124 | simCaseStruct.precoding.precoderImag = imag(precoder); 125 | 126 | % Containers for performance metrics -------------------------------------- 127 | simCaseStruct.performance.miBpcu = I; 128 | simCaseStruct.performance.timeElapsedSec = (timeEnd - timeBegin)/60; 129 | simCaseStruct.cluster.status = 'COMPLETED'; 130 | 131 | % Save all params to mat-file 132 | save(simCaseStruct.cluster.matFilePath, 'simCaseStruct'); 133 | 134 | end % for iSnrDb = 1 : nSnrDb -------------------------------------------------------------------------------- /Functions/overrideStruct.m: -------------------------------------------------------------------------------- 1 | function outputStruct = overrideStruct(outputStruct, inputStruct) 2 | % 3 | % OVERRIDESTRUCT Merge all fields of input struct into output struct 4 | % 5 | % Inputs: struct outputStruct = output struct 6 | % struct struct = input struct 7 | % Outputs: struct outputStruct = output struct 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | % Check that inout and output are scalar structs 30 | validateattributes(inputStruct, {'struct'}, {'scalar'}); 31 | validateattributes(outputStruct, {'struct'}, {'scalar'}); 32 | 33 | % Loop over field names and merge 34 | fields = fieldnames(inputStruct); 35 | for field = fields.' 36 | if isstruct(inputStruct.(field{1})) && isfield(outputStruct, field{1}) 37 | % Merge if field is already existent nested struct 38 | outputStruct.(field{1}) = overrideStruct(outputStruct.(field{1}), inputStruct.(field{1})); 39 | else 40 | % Copy if field is non-struct or non-existent nested struct 41 | outputStruct.(field{1}) = inputStruct.(field{1}); 42 | end 43 | end 44 | 45 | end -------------------------------------------------------------------------------- /Functions/parseSimIds.m: -------------------------------------------------------------------------------- 1 | function [simCaseNameList, simNameList] = parseSimIds(simIdCellArray, nCasesPerSimMax) 2 | % 3 | % PARSESIMIDS Given a list of sim names (collections of sim cases) simIdCellArray, 4 | % parse which exact sim cases are to be run 5 | % 6 | % Inputs: cell array simIdCellArray = array of sim IDs 7 | % scalar nCasesPerSimMax = max number of sim cases 8 | % Outputs: cell array simCaseNameList = list of sim case names 9 | % cell array simNameList = list of sim names 10 | % 11 | % Max Girnyk 12 | % Stockholm, 2014-10-01 13 | % 14 | % ========================================================================= 15 | % 16 | % This Matlab script produces results used in the following paper: 17 | % 18 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 19 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 20 | % 21 | % Paper URL: https://arxiv.org/abs/2111.03504 22 | % 23 | % Version: 1.0 (modified 2021-11-14) 24 | % 25 | % License: This code is licensed under the Apache-2.0 license. 26 | % If you use this code in any way for research that 27 | % results in a publication, please cite the above paper 28 | % 29 | % ========================================================================= 30 | 31 | if (nargin < 2) 32 | nCasesPerSimMax = -1; 33 | end 34 | 35 | simDataPath = [pwd, '\SimData']; 36 | 37 | nCells = length(simIdCellArray); 38 | simCaseNameList = {}; 39 | simNameList = {}; 40 | for iCell = 1:nCells 41 | counterCases = 0; 42 | if iscell(simIdCellArray) 43 | if (isa(simIdCellArray{iCell}, 'double')) 44 | simName = num2str(simIdCellArray{iCell}); 45 | simFolderPath = [simDataPath, '\', simName]; 46 | folderContent = dir(simFolderPath); 47 | for iElement = 1:length(folderContent) 48 | elementName = folderContent(iElement).name; 49 | if (~isempty(strfind(elementName, simName))) && (isempty(strfind(elementName, '.'))) 50 | simCasePath = [simFolderPath, '\', elementName]; 51 | if (~exist(simCasePath)) 52 | error(['Sim case ', simName, ' does not exist!']); 53 | end 54 | counterCases = counterCases + 1; 55 | if (nCasesPerSimMax > 0) 56 | if (counterCases <= nCasesPerSimMax) 57 | simCaseNameList{end+1} = elementName; 58 | simNameList{end+1} = simName; 59 | end 60 | else 61 | simCaseNameList{end+1} = elementName; 62 | simNameList{end+1} = simName; 63 | end 64 | end 65 | end % for iElement = 1:length(folderContent) 66 | elseif (isa(simIdCellArray{iCell}, 'char')) 67 | simName = simIdCellArray{iCell}; 68 | delimiter = regexp(simName, '_'); 69 | if ~isempty(delimiter) 70 | simCaseName = simName; 71 | simName = simName(1:delimiter-1); 72 | simFolderPath = [simDataPath, '\', simName(1:delimiter-1)]; 73 | simCasePath = [simFolderPath, '\', simCaseName]; 74 | if (~exist(simCasePath)) 75 | error(['Sim case ', simCasePath, ' does not exist!']); 76 | end 77 | simCaseNameList{end+1} = simCaseName; 78 | simNameList{end+1} = simName; 79 | else 80 | simFolderPath = [simDataPath, '\', simName]; 81 | folderContent = dir(simFolderPath); 82 | for iElement = 1:length(folderContent) 83 | elementName = folderContent(iElement).name; 84 | if (~isempty(strfind(elementName, simName))) && (isempty(strfind(elementName, '.'))) 85 | counterCases = counterCases + 1; 86 | if (nCasesPerSimMax > 0) 87 | if (counterCases <= nCasesPerSimMax) 88 | simCaseNameList{end+1} = elementName; 89 | simNameList{end+1} = simName; 90 | end 91 | else 92 | simCaseNameList{end+1} = elementName; 93 | simNameList{end+1} = simName; 94 | end 95 | 96 | end 97 | end % for iElement = 1:length(folderContent) 98 | end 99 | end 100 | end 101 | end % for iCell = 1:nCells 102 | 103 | end -------------------------------------------------------------------------------- /Functions/printComplexMatrix.m: -------------------------------------------------------------------------------- 1 | function printComplexMatrix(Mat) 2 | % 3 | % PRINTCOMPLEXMATRIX Print out a prettyfied string containing a complex 4 | % matrix. Useful for monitoring the optimal precoder during the 5 | % optimization. 6 | % 7 | % Inputs: mat Mat = input matrix 8 | % Outputs: -- 9 | % 10 | % Max Girnyk 11 | % Stockholm, 2014-10-01 12 | % 13 | % ========================================================================= 14 | % 15 | % This Matlab script produces results used in the following paper: 16 | % 17 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 18 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 19 | % 20 | % Paper URL: https://arxiv.org/abs/2111.03504 21 | % 22 | % Version: 1.0 (modified 2021-11-14) 23 | % 24 | % License: This code is licensed under the Apache-2.0 license. 25 | % If you use this code in any way for research that 26 | % results in a publication, please cite the above paper 27 | % 28 | % ========================================================================= 29 | 30 | [N, M] = size(Mat); 31 | 32 | MatRe = real(Mat); 33 | MatIm = imag(Mat); 34 | 35 | MatComb = [MatRe; MatIm]; 36 | MatPrint = reshape(MatComb(:), N, 2*M); 37 | 38 | fprintf([' [', repmat('%.3f + %.3fj\t', 1, size(Mat, 2)), ']\n'], MatPrint'); 39 | 40 | end -------------------------------------------------------------------------------- /Functions/runBackpropPass.m: -------------------------------------------------------------------------------- 1 | function [gradsBias, gradsWeight] = runBackpropPass(neural_net, inputVec, targetVec) 2 | % 3 | % RUNBACKPROPPASS Train neural network on a training set and test it 4 | % on a test set 5 | % 6 | % Inputs: struct neural_net = struct with neural net specific info 7 | % vec inputVec = vector of inputs 8 | % vec targetVec = vector of targets 9 | % Outputs: vec gradsBias = vector of biases gradients 10 | % vec gradsWeight = vector of weights gradients 11 | % 12 | % Max Girnyk 13 | % Stockholm, 2014-10-01 14 | % 15 | % ========================================================================= 16 | % 17 | % This Matlab script produces results used in the following paper: 18 | % 19 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 20 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 21 | % 22 | % Paper URL: https://arxiv.org/abs/2111.03504 23 | % 24 | % Version: 1.0 (modified 2021-11-14) 25 | % 26 | % License: This code is licensed under the Apache-2.0 license. 27 | % If you use this code in any way for research that 28 | % results in a publication, please cite the above paper 29 | % 30 | % ========================================================================= 31 | 32 | % Get neural net config 33 | layerSizes = neural_net.layerSizes; 34 | nLayers = length(layerSizes); 35 | switch (neural_net.activationType) 36 | case 'tanh' 37 | activationFunc = @(z) tanh(z); 38 | activationDiffFunc = @(z) 1-(activationFunc(z))^2; 39 | case 'sigmoid' 40 | activationFunc = @(z) 1.0/(1.0+exp(-z)); 41 | activationDiffFunc = @(z) activationFunc(z)*(1-activationFunc(z)); 42 | otherwise 43 | error('ERROR! Unknown activation function!'); 44 | end 45 | 46 | % Define first derivative of cost function 47 | costDiff = inline('output_activations - target', 'output_activations', 'target'); 48 | 49 | % Init gradients for weights and biases 50 | gradsBias = zeros(size(neural_net.biasVecsUnrolled)); 51 | gradsWeight = zeros(size(neural_net.weightMatsUnrolled)); 52 | 53 | % Set input activations to input values 54 | activation = inputVec; 55 | activationVec = inputVec; 56 | % z = w*x + b for all layers 57 | preprocOutputVec = []; 58 | 59 | % Init params to use to reshape the biases and weights vectors 60 | sizeWeightMatCurrent = 1; 61 | sizeWeightMatNext = layerSizes(1) * layerSizes(2); 62 | sizeBiasVecCurrent = 1; 63 | sizeBiasVecNext = layerSizes(2); 64 | 65 | % Feedforward loop 66 | for iLayer = 1:nLayers-1 67 | % Reshape weights from vec to mat 68 | weightMat = reshape(neural_net.weightMatsUnrolled(sizeWeightMatCurrent:sizeWeightMatNext), [layerSizes(iLayer), layerSizes(iLayer+1)])'; 69 | % Set biases to use 70 | biasVec = neural_net.biasVecsUnrolled(sizeBiasVecCurrent:sizeBiasVecNext); 71 | % Update reshape params 72 | if (iLayer 1 106 | sizeBiasVecCurrent = sizeBiasVecNext - 1; 107 | sizeBiasVecNext = sizeBiasVecNext - layerSizes(jLayer); 108 | sizeActivationNext = sizeActivationNext - layerSizes(jLayer); 109 | end 110 | % Backpropagate errors 111 | preprocOutput = preprocOutputVec(sizeBiasVecNext:sizeBiasVecCurrent); 112 | activationFuncDiffVec = arrayfun(activationDiffFunc, preprocOutput); 113 | weightMat = reshape(neural_net.weightMatsUnrolled(sizeWeightMatNext:sizeWeightMatCurrent), [layerSizes(jLayer), layerSizes(jLayer+1)])'; 114 | delta = weightMat' * delta .* activationFuncDiffVec; 115 | % Update gradients with backrpopagated errors 116 | gradsBias(sizeBiasVecNext:sizeBiasVecCurrent) = delta; 117 | % Update reshape parameters 118 | if jLayer > 1 119 | sizeWeightMatCurrent = sizeWeightMatNext - 1; 120 | sizeWeightMatNext = sizeWeightMatNext - layerSizes(jLayer)*layerSizes(jLayer-1); 121 | end 122 | tmp = (delta*activationVec((sizeActivationNext-layerSizes(jLayer-1)):(sizeActivationNext-1))')'; 123 | gradsWeight(sizeWeightMatNext:sizeWeightMatCurrent) = tmp(:); 124 | end % jLayer = (nLayers-1):-1:2 125 | 126 | end -------------------------------------------------------------------------------- /Functions/runFeedforwardPass.m: -------------------------------------------------------------------------------- 1 | function outputVec = runFeedforwardPass(neural_net, inputVec) 2 | % 3 | % RUNFEEDFORWARDPASS Run a feedforward pass through neural net. 4 | % 5 | % Inputs: struct neural_net = struct with neural net specific info 6 | % vec inputVec = vector of inputs 7 | % Outputs: vec outputVec = vector of outputs 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | % Get neural net config 30 | layerSizes = neural_net.layerSizes; 31 | nLayers = length(layerSizes); 32 | 33 | switch (neural_net.activationType) 34 | case 'tanh' 35 | activationFunc = @(z) tanh(z); 36 | case 'sigmoid' 37 | activationFunc = @(z) 1.0/(1.0+exp(-z)); 38 | otherwise 39 | error('ERROR! Unknown activation function!'); 40 | end 41 | 42 | % Init params to use to reshape the biases and weights vectors 43 | weightMatSizeCurrent = 1; 44 | weightMatSizeNext = layerSizes(1)*layerSizes(2); 45 | biasVecSizeCurrent = 1; 46 | biasVecSizeNext = layerSizes(2); 47 | 48 | % Feedforward loop 49 | for iLayer = 1:nLayers-1 50 | % Reshape weights and biases 51 | w = reshape(neural_net.weightMatsUnrolled(weightMatSizeCurrent:weightMatSizeNext), [layerSizes(iLayer), layerSizes(iLayer+1)])'; 52 | b = neural_net.biasVecsUnrolled(biasVecSizeCurrent:biasVecSizeNext); 53 | 54 | % Update reshape parameters 55 | if iLayer < nLayers - 1 56 | weightMatSizeCurrent = weightMatSizeCurrent + layerSizes(iLayer)*layerSizes(iLayer+1); 57 | weightMatSizeNext = weightMatSizeCurrent - 1 + layerSizes(iLayer+1)*layerSizes(iLayer+2); 58 | biasVecSizeCurrent = biasVecSizeCurrent + layerSizes(iLayer+1); 59 | biasVecSizeNext = biasVecSizeCurrent - 1 + layerSizes(iLayer+2); 60 | end 61 | 62 | % Compute activation function 63 | z = w*inputVec + b; 64 | inputVec = arrayfun(activationFunc, z); 65 | end % for iLayer = 1:nLayers-1 66 | 67 | outputVec = inputVec; 68 | 69 | end -------------------------------------------------------------------------------- /Functions/runSimsLocally.m: -------------------------------------------------------------------------------- 1 | function runSimsLocally(simCaseStruct, operationType) 2 | % 3 | % RUNSIMSLOCALLY Runs optimization jobs on the local machine 4 | % 5 | % Inputs: struct simCaseStruct = struct with sim case params 6 | % str operationType = job type to run: {OPTIMIZATION, EVALUATION, COMPUTATION, CHANNELIZATION} 7 | % Outputs: -- 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | simDataPath = [pwd, '\SimData']; 30 | 31 | % Cluster-related params -------------------------------------------------- 32 | simName = simCaseStruct.cluster.simName; 33 | caseIdx = simCaseStruct.cluster.caseIdx; 34 | nOrderCasesMax = simCaseStruct.cluster.nOrderCasesMax; 35 | caseIdxTag = convertNumberToCaseIdxTag(caseIdx, nOrderCasesMax); 36 | caseId = [simName, '_', caseIdxTag]; % ID for the sim case 37 | simFolderPath = [simDataPath, '\', simName]; 38 | simCaseSubfolderPath = [simFolderPath, '\', caseId]; 39 | 40 | 41 | % Create the case subfolder 42 | if (~exist(simCaseSubfolderPath)) 43 | mkdir(simCaseSubfolderPath); 44 | end 45 | 46 | snrDbVec = simCaseStruct.channel.snrDbVec; 47 | nSnrDb = length(snrDbVec); 48 | 49 | % Loop over SNRs ---------------------------------------------------------- 50 | for iSnrDb = 1 : nSnrDb 51 | 52 | snrDb = snrDbVec(iSnrDb); 53 | 54 | % File = current SNR point 55 | snrTag = convertNumberToSnrTag(snrDb); 56 | jobId = [caseId, '_', snrTag]; 57 | simFileName = ['mi_', jobId]; 58 | 59 | simCaseStruct.cluster.simDataPath = simDataPath; 60 | simCaseStruct.cluster.simFolderPath = simFolderPath; 61 | 62 | simCaseStruct.channel.snrIdxCurrent = iSnrDb; 63 | simCaseStruct.channel.currentSnrDb = snrDb; 64 | 65 | simFilePath = [simCaseSubfolderPath, '\', simFileName]; 66 | matFilePath = [simFilePath, '.mat']; 67 | 68 | if (exist(matFilePath)) 69 | load(matFilePath); 70 | end 71 | 72 | simCaseStruct.cluster.matFilePath = matFilePath; 73 | simCaseStruct.cluster.status = 'RUNNING'; 74 | 75 | if strcmp(operationType, 'OPTIMIZATION') 76 | % Optimize precoder 77 | save(matFilePath, 'simCaseStruct'); % save all params to file 78 | if strcmp(simCaseStruct.computation.methodComputationMi, 'NONE') 79 | % No precoding at all 80 | setOmniPrecoderMimo(matFilePath); 81 | elseif strcmp(simCaseStruct.computation.methodComputationMi, 'GAUSS_OPT') 82 | % Gaussian opt precoder for discrete constellation 83 | optimizePrecoderMimoGaussOpt(matFilePath); 84 | else 85 | % Optimize precoder for discrete constellation 86 | optimizePrecoderMimo(matFilePath); 87 | end 88 | elseif strcmp(operationType, 'EVALUATION') 89 | % Evaluate optimized precoder 90 | save(matFilePath, 'simCaseStruct'); 91 | evaluatePrecoderMimo(matFilePath); 92 | elseif strcmp(operationType, 'COMPUTATION') 93 | % Compute mutual info for given precoder 94 | save(matFileName, 'simCaseStruct'); 95 | computeMiGivenPrecoderMimo(matFilePath); 96 | elseif strcmp(operationType, 'CHANNELIZATION') 97 | % Predict opt precoder for given channel realization 98 | save(matFilePath, 'simCaseStruct'); 99 | optimizePrecoderGivenChannelMimoMl(matFilePath); 100 | else 101 | error('Wrong calculation type! Should be one of {OPTIMIZATION, EVALUATION, COMPUTATION, CHANNELIZATION}'); 102 | end 103 | 104 | 105 | end % for iSnr = 1:length(snrVec) 106 | 107 | end 108 | -------------------------------------------------------------------------------- /Functions/setOmniPrecoderMimo.m: -------------------------------------------------------------------------------- 1 | function precoder = setOmniPrecoderMimo(simCaseStruct) 2 | % 3 | % SETOMNIPRECODERMIMO Set a dummy omnidirectional precoder (i.e., no 4 | % precoding) for a MIMO channel with Gaussian inputs. 5 | % 6 | % Inputs: struct simCaseStruct = struct of sim case params 7 | % Outputs: mat precoder = linear precoding matrix 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | % Cluster-related params -------------------------------------------------- 30 | evaluateTrueMiMeanwhile = simCaseStruct.cluster.evaluateTrueMiMeanwhile; 31 | simName = simCaseStruct.cluster.simName; 32 | caseIdx = simCaseStruct.cluster.caseIdx; 33 | nOrderCasesMax = simCaseStruct.cluster.nOrderCasesMax; 34 | caseIdxTag = convertNumberToCaseIdxTag(caseIdx, nOrderCasesMax); 35 | caseId = [simName, '_', caseIdxTag]; % ID for the sim case 36 | simFolderPath = simCaseStruct.cluster.simFolderPath; 37 | simCaseSubfolderPath = [simFolderPath, '\', caseId]; 38 | 39 | % Computation-related params ---------------------------------------------- 40 | nItersSignalMi = simCaseStruct.computation.nItersSignalMi; 41 | nItersNoiseMi = simCaseStruct.computation.nItersNoiseMi; 42 | 43 | % Signal-related params ------------------------------------------------ 44 | typeModulation = simCaseStruct.signaling.typeModulation; 45 | 46 | % Channel-related params -------------------------------------------------- 47 | snrDbVec = simCaseStruct.channel.snrDbVec; 48 | nSnrDb = length(snrDbVec); 49 | 50 | % Create the case subfolder 51 | if (~exist(simCaseSubfolderPath)) 52 | mkdir(simCaseSubfolderPath); 53 | end 54 | 55 | % Loop over SNRs ---------------------------------------------------------- 56 | for iSnrDb = 1 : nSnrDb 57 | 58 | % SNR value in linear scale 59 | snrDb = snrDbVec(iSnrDb); 60 | simCaseStruct.channel.currentSnrDb = snrDb; 61 | snr = 10^(snrDb/10); 62 | snrTag = convertNumberToSnrTag(snrDb); 63 | 64 | % Sim case formal params 65 | jobId = [caseId, '_', snrTag]; 66 | simFileName = ['mi_', jobId]; 67 | simFilePath = [simCaseSubfolderPath, '\', simFileName]; 68 | matFilePath = [simFilePath, '.mat']; 69 | simCaseStruct.cluster.matFilePath = matFilePath; 70 | simCaseStruct.cluster.status = 'RUNNING'; 71 | 72 | % Channel matrix 73 | channelMat = simCaseStruct.channel.channelMatReal + 1i*simCaseStruct.channel.channelMatImag; 74 | [~, M] = size(channelMat); 75 | H = sqrt(snr/M) * channelMat; 76 | 77 | timeBegin = cputime; % start clock 78 | 79 | precoder = eye(M); 80 | 81 | timeEnd = cputime; % stop clock 82 | 83 | if evaluateTrueMiMeanwhile 84 | [I, ~] = computeMiMimo(H*precoder, typeModulation, 'TRUE', 'EXHAUSTIVE', 5e3, 1e4); 85 | else 86 | [I, ~] = computeMiMimo(H*precoder, typeModulation, 'TRUE', 'EXHAUSTIVE', nItersSignalMi, nItersNoiseMi); 87 | end 88 | 89 | % Container for precoders ------------------------------------------------- 90 | simCaseStruct.precoding.precoderReal = real(precoder); 91 | simCaseStruct.precoding.precoderImag = imag(precoder); 92 | 93 | % Containers for performance metrics -------------------------------------- 94 | simCaseStruct.performance.miBpcu = I; 95 | simCaseStruct.performance.timeElapsedSec = (timeEnd - timeBegin)/60; 96 | simCaseStruct.cluster.status = 'COMPLETED'; 97 | 98 | % Save all params to mat-file 99 | save(simCaseStruct.cluster.matFilePath, 'simCaseStruct'); 100 | 101 | end -------------------------------------------------------------------------------- /Functions/setupNeuralNet.m: -------------------------------------------------------------------------------- 1 | function neural_net = setupNeuralNet(layerSizes, activationType) 2 | % 3 | % TRAINNNMODELMIMO Train a neural model for precoder prediction 4 | % 5 | % Inputs: vec layerSizes = numbers of neurons in each layer 6 | % str activationType = type of activation function 7 | % Outputs: struct neural_net = struct with neural net specific info 8 | % 9 | % Max Girnyk 10 | % Stockholm, 2014-10-01 11 | % 12 | % ========================================================================= 13 | % 14 | % This Matlab script produces results used in the following paper: 15 | % 16 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 17 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 18 | % 19 | % Paper URL: https://arxiv.org/abs/2111.03504 20 | % 21 | % Version: 1.0 (modified 2021-11-14) 22 | % 23 | % License: This code is licensed under the Apache-2.0 license. 24 | % If you use this code in any way for research that 25 | % results in a publication, please cite the above paper 26 | % 27 | % ========================================================================= 28 | 29 | neural_net = {}; 30 | neural_net.layerSizes = layerSizes; 31 | neural_net.activationType = activationType; 32 | 33 | % Define the number of layers 34 | nLayers = length(layerSizes); 35 | 36 | % Init biases and weights 37 | neural_net.biasVecsUnrolled = []; 38 | neural_net.weightMatsUnrolled = []; 39 | 40 | % Set initial values for weights and biases 41 | for iLayer = 1:nLayers-1 42 | nNeuronsNext = layerSizes(iLayer+1); 43 | nNeuronsCurrent = layerSizes(iLayer); 44 | randBiases = zeros(nNeuronsNext, 1); 45 | neural_net.biasVecsUnrolled = [neural_net.biasVecsUnrolled; randBiases(:)]; 46 | randWeights = sqrt(2/(nNeuronsNext+nNeuronsCurrent))*randn(nNeuronsNext, nNeuronsCurrent); 47 | neural_net.weightMatsUnrolled = [neural_net.weightMatsUnrolled; randWeights(:)]; 48 | end % for iLayer = 1:nLayers-1 49 | 50 | end -------------------------------------------------------------------------------- /Functions/splitDataset.m: -------------------------------------------------------------------------------- 1 | function [trainSet, testSet] = splitDataset(dataset, trainShare) 2 | % 3 | % SPLITDATASET Split entire dataset into training set and test set. 4 | % 5 | % Inputs: cell array dataset = dataset 6 | % scalar trainShare = share of training observations 7 | % Outputs: cell array trainSet = training set 8 | % cell array testSet = test set 9 | % 10 | % Max Girnyk 11 | % Stockholm, 2014-10-01 12 | % 13 | % ========================================================================= 14 | % 15 | % This Matlab script produces results used in the following paper: 16 | % 17 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 18 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 19 | % 20 | % Paper URL: https://arxiv.org/abs/2111.03504 21 | % 22 | % Version: 1.0 (modified 2021-11-14) 23 | % 24 | % License: This code is licensed under the Apache-2.0 license. 25 | % If you use this code in any way for research that 26 | % results in a publication, please cite the above paper 27 | % 28 | % ========================================================================= 29 | 30 | nObservations = length(dataset); 31 | nTrainObs = floor(trainShare*nObservations); 32 | idxList = randperm(nObservations); 33 | 34 | trainSet = dataset(idxList(1:nTrainObs)); 35 | testSet = dataset(idxList(nTrainObs+1:end)); 36 | 37 | end -------------------------------------------------------------------------------- /Functions/trainAndTestNeuralNet.m: -------------------------------------------------------------------------------- 1 | function [neural_net, lossTrain, lossValid] = trainAndTestNeuralNet(neural_net, trainSet, nEpochs, miniBatchSize, learnRate, testSet) 2 | % 3 | % TRAINANDTESTNEURALNET Train neural network on a training set and test it 4 | % on a test set 5 | % 6 | % Inputs: struct neural_net = struct with neural net specific info 7 | % cell array trainSet = training set 8 | % scalar nEpochs = number of epochs for training 9 | % scalar miniBatchSize = size of the minibatch 10 | % scalar learnRate = learning rate 11 | % cell array testSet = test set for performance evaluation 12 | % Outputs: struct neural_net = struct with neural net specific info 13 | % vec lossTrain = training loss over epochs 14 | % vec lossValid = validation loss over epochs 15 | % 16 | % Max Girnyk 17 | % Stockholm, 2014-10-01 18 | % 19 | % ========================================================================= 20 | % 21 | % This Matlab script produces results used in the following paper: 22 | % 23 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 24 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 25 | % 26 | % Paper URL: https://arxiv.org/abs/2111.03504 27 | % 28 | % Version: 1.0 (modified 2021-11-14) 29 | % 30 | % License: This code is licensed under the Apache-2.0 license. 31 | % If you use this code in any way for research that 32 | % results in a publication, please cite the above paper 33 | % 34 | % ========================================================================= 35 | 36 | % Check if test_data is present 37 | if nargin > 5 38 | nTestObs = length(testSet); 39 | end 40 | 41 | % define the dimension of training data 42 | nTrainObs = length(trainSet); 43 | 44 | % Init loss arrays 45 | lossTrain = NaN(nEpochs, 1); 46 | lossValid = NaN(nEpochs, 1); 47 | 48 | % loop through epochs 49 | for iEpoch = 1:nEpochs 50 | 51 | % Shuffle training data 52 | perm = randperm(nTrainObs); 53 | trainSet = trainSet(perm); 54 | 55 | % Loop through minibatches 56 | for jBatch = 1:miniBatchSize:nTrainObs 57 | if (jBatch+miniBatchSize-1 5 67 | lossTrain(iEpoch) = evaluateNeuralNet(neural_net, trainSet); 68 | lossValid(iEpoch) = evaluateNeuralNet(neural_net, testSet); 69 | fprintf('Epoch %d/%d: mean_loss_train=%.4f, mean_loss_val=%.4f, (n_train=%d, n_val=%d)\n', iEpoch, nEpochs, lossTrain(iEpoch), lossValid(iEpoch), nTrainObs, nTestObs); 70 | else 71 | fprintf('Epoch %d/%d complete\n', iEpoch, nEpochs); 72 | end 73 | 74 | end % for iEpoch = 1:nEpochs 75 | 76 | end -------------------------------------------------------------------------------- /Functions/updateMiniBatch.m: -------------------------------------------------------------------------------- 1 | function neural_net = updateMiniBatch (neural_net, miniBatch, learnRate) 2 | % 3 | % UPDATEMINIBATCH Perform minibatch update. 4 | % 5 | % Inputs: struct neural_net = truct with neural net specific info 6 | % cell array miniBatch = observations in minibatch 7 | % scalar learnRate = learning rate 8 | % Outputs: struct neural_net = truct with neural net specific info 9 | % 10 | % Max Girnyk 11 | % Stockholm, 2014-10-01 12 | % 13 | % ========================================================================= 14 | % 15 | % This Matlab script produces results used in the following paper: 16 | % 17 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 18 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 19 | % 20 | % Paper URL: https://arxiv.org/abs/2111.03504 21 | % 22 | % Version: 1.0 (modified 2021-11-14) 23 | % 24 | % License: This code is licensed under the Apache-2.0 license. 25 | % If you use this code in any way for research that 26 | % results in a publication, please cite the above paper 27 | % 28 | % ========================================================================= 29 | 30 | % Get the size of minibatch 31 | miniBatchSize = length(miniBatch); 32 | 33 | % Init gradients of biases and weights 34 | gradsBias = zeros(size(neural_net.biasVecsUnrolled)); 35 | gradsWeight = zeros(size(neural_net.weightMatsUnrolled)); 36 | 37 | % Loop through each row of mini_batch 38 | for jObs = 1:miniBatchSize 39 | % Compute the gradients via backprop 40 | [deltaGradsBias, deltaGradsWeight] = runBackpropPass(neural_net, miniBatch{jObs}.precoderVecWf, miniBatch{jObs}.precoderVecDiscrete); 41 | gradsBias = gradsBias + deltaGradsBias; 42 | gradsWeight = gradsWeight + deltaGradsWeight; 43 | end % for jObs = 1:miniBatchSize 44 | 45 | % Update weights and biases 46 | neural_net.biasVecsUnrolled = neural_net.biasVecsUnrolled - (learnRate/miniBatchSize).*gradsBias; 47 | neural_net.weightMatsUnrolled = neural_net.weightMatsUnrolled - (learnRate/miniBatchSize).*gradsWeight; 48 | 49 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /OptimizePrecoderMimo.m: -------------------------------------------------------------------------------- 1 | % ========================================================================= 2 | % 3 | % Deep-learning based MIMO precoding for finite-alphabet signaling 4 | % 5 | % Low-complexity linear precoding for MIMO channels with discrete inputs 6 | % Precoder optimization 7 | % 8 | % Max Girnyk 9 | % Stockholm, 2014-10-01 10 | % 11 | % ========================================================================= 12 | % 13 | % This Matlab script produces results used in the following paper: 14 | % 15 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 16 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 17 | % 18 | % Paper URL: https://arxiv.org/abs/2111.03504 19 | % 20 | % Version: 1.0 (modified 2021-11-14) 21 | % 22 | % License: This code is licensed under the Apache-2.0 license. 23 | % If you use this code in any way for research that 24 | % results in a publication, please cite the above paper 25 | % 26 | % ========================================================================= 27 | 28 | function OptimizePrecoderMimo 29 | 30 | % % Shuffle random number generator 31 | % rng('shuffle'); 32 | % % Or this way 33 | % rand('state', sum(100*clock)); 34 | % Or set the seed manually 35 | rand('state', 1); 36 | 37 | % Clear and close everything 38 | clear all; clc; close all; 39 | 40 | % Add all the subfolders to the path. 41 | currentFilePath = fileparts(which(mfilename)); 42 | addpath(genpath(currentFilePath)); 43 | 44 | % % Add an optional message for future reference 45 | % disp('What is this sim about? Add a comment (e.g., test):'); 46 | % comment = input('', 's'); 47 | comment = 'Debugging'; 48 | 49 | % Create default sim param structure 50 | defaultSimParamStruct = createDefaultSimParamStruct; 51 | 52 | % Operation type, should be one of {'OPTIMIZATION', 'EVALUATION', 'COMPUTATION'} 53 | operationType = 'OPTIMIZATION'; 54 | 55 | % ========================================================================= 56 | % Define common simulation params ========================================= 57 | % (how we want to run sims) 58 | simMode = 'QUICK'; % short/long sim for debugging/full-blown solution: {'QUICK', 'FULL'} 59 | 60 | % Cluster-related params -------------------------------------------------- 61 | runOnCluster = 0; % flag for running on the cluster/locally 62 | stepThresholdPercent = 1e-1; 63 | evaluateTrueMiMeanwhile = 0; % compute true QAM MI during/after? optimization 64 | nOrderCasesMax = 3; % Max order of the number of cases allowed (how many 9s, e.g., 999) 65 | status = 'STARTED'; % status of the sim: {'STARTED', 'COMPLETED', 'EVALUATED'} 66 | createDataset = 0; % Create dataset for further analysis? 67 | 68 | % Channel-related params -------------------------------------------------- 69 | channelFadingTag = 'PalomarVerdu'; %{'PalomarVerdu', 'ComplexRayleighIid', 'RealRayleighIid'} 70 | channelMat = generateFadingChannel(channelFadingTag, 2, 2); 71 | [nRx, nTx] = size(channelMat); % sizes of the channel matrix 72 | channelTag = [num2str(nTx), 'x', num2str(nRx), channelFadingTag]; 73 | snrDbMin = -20; % lower SNR in dB 74 | snrDbMax = 20; % higher SNR in dB 75 | nSnrPoints = 7; % number of SNR points in dB 76 | snrDbVec = linspace(snrDbMin, snrDbMax, nSnrPoints); 77 | 78 | % Optimization-related params --------------------------------------------- 79 | if strcmp(simMode, 'QUICK') 80 | nItersMax = 10; % max number of outer-loop iters for the precoder optimization loop 81 | nNoImprovementsMax = 5; % max number of allowed outer-loop iters w/o improvement of MI value 82 | nBackOffsMax = 2; % max number of allowed back-offs when stuck w/o improvement of MI value 83 | backOffSize = 1; % back-off size 84 | nItersSignalMi = 10; % size of the inner loop for MI computation 85 | nItersNoiseMi = 10; % size of the outer loop for MI computation 86 | nItersSignalMmse = 10; % size of the inner loop for MMSE computation 87 | nItersNoiseMmse = 10; % size of the outer loop for MMSE computation 88 | elseif strcmp(simMode, 'FULL') 89 | nItersMax = 100; % max number of outer-loop iters for the precoder optimization loop 90 | nNoImprovementsMax = 2; % max number of allowed outer-loop iters w/o improvement of MI value 91 | nBackOffsMax = 2; % max number of allowed back-offs when stuck w/o improvement of MI value 92 | backOffSize = 2; % back-off size 93 | nItersSignalMi = 1e3; % size of the inner loop for MI computation 94 | nItersNoiseMi = 1e3; % size of the outer loop for MI computation 95 | nItersSignalMmse = 1e3; % size of the inner loop for MMSE computation 96 | nItersNoiseMmse = 1e3; % size of the outer loop for MMSE computation 97 | else 98 | error('ERROR! Unknown sim mode!'); 99 | end 100 | 101 | % Setup default sim param structure --------------------------------------- 102 | % (Default sim case) 103 | defaultSimParamStruct.channel.channelMatReal = real(channelMat); 104 | defaultSimParamStruct.channel.channelMatImag = imag(channelMat); 105 | defaultSimParamStruct.channel.nTxAntennas = nTx; 106 | defaultSimParamStruct.channel.nRxAntennas = nRx; 107 | defaultSimParamStruct.channel.channelTag = channelTag; 108 | defaultSimParamStruct.channel.snrDbVec = snrDbVec; 109 | defaultSimParamStruct.optimization.nItersMax = nItersMax; 110 | defaultSimParamStruct.optimization.nNoImprovementsMax = nNoImprovementsMax; 111 | defaultSimParamStruct.optimization.nBackOffsMax = nBackOffsMax; 112 | defaultSimParamStruct.optimization.backOffSize = backOffSize; 113 | defaultSimParamStruct.computation.nItersSignalMi = nItersSignalMi; 114 | defaultSimParamStruct.computation.nItersNoiseMi = nItersNoiseMi; 115 | defaultSimParamStruct.computation.nItersSignalMmse = nItersSignalMmse; 116 | defaultSimParamStruct.computation.nItersNoiseMmse = nItersNoiseMmse; 117 | defaultSimParamStruct.signaling.typeModulation = 'QPSK'; 118 | defaultSimParamStruct.cluster.runOnCluster = runOnCluster; 119 | defaultSimParamStruct.cluster.simMode = simMode; 120 | defaultSimParamStruct.cluster.comment = comment; 121 | defaultSimParamStruct.cluster.nOrderCasesMax = nOrderCasesMax; 122 | defaultSimParamStruct.cluster.evaluateTrueMiMeanwhile = evaluateTrueMiMeanwhile; 123 | defaultSimParamStruct.cluster.status = status; 124 | defaultSimParamStruct.cluster.createDataset = createDataset; 125 | defaultSimParamStruct.cluster.stepThresholdPercent = stepThresholdPercent; 126 | 127 | % ========================================================================= 128 | 129 | % Define simulation cases ################################################# 130 | % (uncomment and configure setups for which we want to run optimization) 131 | simCaseParamStructs = {}; 132 | 133 | % % 134 | % % Case #1 135 | % % MI computation via Gaussian signaling 136 | % simCaseParamStructs{end+1} = {}; 137 | % simCaseParamStructs{end}.plotting.legendEntry = 'Capacity'; 138 | % simCaseParamStructs{end}.plotting.lineType = '--'; 139 | % simCaseParamStructs{end}.plotting.lineMarker = 'none'; 140 | % simCaseParamStructs{end}.plotting.lineColor = [0, 0, 0]; 141 | % simCaseParamStructs{end}.signaling.typeModulation = 'GAUSS'; 142 | % simCaseParamStructs{end}.computation.methodComputationMi = 'GAUSS'; 143 | % simCaseParamStructs{end}.computation.methodComputationMmse = 'GAUSS'; 144 | % simCaseParamStructs{end}.cluster.runOnCluster = 0; 145 | % nSnrPoints = 41; 146 | % simCaseParamStructs{end}.channel.snrDbVec = linspace(snrDbMin, snrDbMax, nSnrPoints); 147 | 148 | % Case #2 149 | % MI computation w/o precoding 150 | simCaseParamStructs{end+1} = {}; 151 | simCaseParamStructs{end}.plotting.legendEntry = 'No precoder'; 152 | simCaseParamStructs{end}.plotting.lineType = '-.'; 153 | simCaseParamStructs{end}.plotting.lineMarker = 'none'; 154 | simCaseParamStructs{end}.plotting.lineColor = [0, 0, 1]; 155 | simCaseParamStructs{end}.computation.methodComputationMi = 'NONE'; 156 | nSnrPoints = 25; 157 | simCaseParamStructs{end}.channel.snrDbVec = linspace(snrDbMin, snrDbMax, nSnrPoints); 158 | 159 | % % Case #3 160 | % % MI computation via Gaussian signaling 161 | % simCaseParamStructs{end+1} = {}; 162 | % simCaseParamStructs{end}.plotting.legendEntry = 'Water filling'; 163 | % simCaseParamStructs{end}.plotting.lineType = '-'; 164 | % simCaseParamStructs{end}.plotting.lineMarker = 'none'; 165 | % simCaseParamStructs{end}.plotting.lineColor = [0.5, 0.5, 0.5]; 166 | % % simCaseParamStructs{end}.signaling.typeModulation = 'GAUSS'; 167 | % simCaseParamStructs{end}.computation.methodComputationMi = 'GAUSS_OPT'; 168 | % nSnrPoints = 41; 169 | % simCaseParamStructs{end}.channel.snrDbVec = linspace(snrDbMin, snrDbMax, nSnrPoints); 170 | 171 | 172 | % % Case #3 173 | % % MI computation via true exhaustive averaging 174 | % simCaseParamStructs{end+1} = {}; 175 | % simCaseParamStructs{end}.plotting.legendEntry = 'True optimum'; 176 | % simCaseParamStructs{end}.plotting.lineType = '-'; 177 | % simCaseParamStructs{end}.plotting.lineMarker = 'o'; 178 | % simCaseParamStructs{end}.plotting.lineColor = [0.5273, 0.8047, 0.9180]; 179 | % simCaseParamStructs{end}.computation.methodComputationMi = 'TRUE'; 180 | % simCaseParamStructs{end}.computation.methodComputationMmse = 'TRUE'; 181 | % nSnrPoints = 13; 182 | % simCaseParamStructs{end}.channel.snrDbVec = linspace(snrDbMin, snrDbMax, nSnrPoints); 183 | % simCaseParamStructs{end}.cluster.evaluateTrueMiMeanwhile = 1; 184 | 185 | % % Case #3.5 186 | % % Create dataset 187 | % nRuns = 1000; 188 | % for iRun = 1:nRuns 189 | % % MI computation via Gaussian signaling 190 | % simCaseParamStructs{end+1} = {}; 191 | % H = generateFadingChannel(channelFadingTag, 2, 2); 192 | % simCaseParamStructs{end}.channel.channelMatReal = real(H); 193 | % simCaseParamStructs{end}.channel.channelMatImag = imag(H); 194 | % simCaseParamStructs{end}.plotting.legendEntry = ['TRUE_', num2str(iRun)] ; 195 | % simCaseParamStructs{end}.plotting.lineType = '-'; 196 | % simCaseParamStructs{end}.plotting.lineMarker = 'o'; 197 | % simCaseParamStructs{end}.plotting.lineColor = [0.5273, 0.8047, 0.9180]; 198 | % simCaseParamStructs{end}.computation.methodComputationMi = 'TRUE'; 199 | % simCaseParamStructs{end}.computation.methodComputationMmse = 'TRUE'; 200 | % simCaseParamStructs{end}.signaling.typeModulation = 'BPSK'; 201 | % nSnrPoints = 7; 202 | % simCaseParamStructs{end}.channel.snrDbVec = linspace(snrDbMin, snrDbMax, nSnrPoints); 203 | % simCaseParamStructs{end}.cluster.createDataset = 1; 204 | % end 205 | 206 | 207 | % % Case #4 208 | % % MI computation via Zhu et al. 209 | % simCaseParamStructs{end+1} = {}; 210 | % simCaseParamStructs{end}.plotting.legendEntry = 'Zhu et al. [10]'; 211 | % simCaseParamStructs{end}.plotting.lineType = '-'; 212 | % simCaseParamStructs{end}.plotting.lineMarker = 'x'; 213 | % simCaseParamStructs{end}.plotting.lineColor = [1, 0.6445, 0]; 214 | % simCaseParamStructs{end}.computation.methodComputationMi = 'ZHU_SHI_FARHANG'; 215 | % simCaseParamStructs{end}.computation.methodComputationMmse = 'TRUE'; 216 | % nSnrPoints = 13; 217 | % simCaseParamStructs{end}.channel.snrDbVec = linspace(snrDbMin, snrDbMax, nSnrPoints); 218 | % 219 | 220 | % % Case #5 221 | % % MI computation via integral approx 222 | % simCaseParamStructs{end+1} = {}; 223 | % simCaseParamStructs{end}.plotting.legendEntry = 'Zeng et al. [11]'; 224 | % simCaseParamStructs{end}.plotting.lineType = '--'; 225 | % simCaseParamStructs{end}.plotting.lineMarker = '+'; 226 | % simCaseParamStructs{end}.plotting.lineColor = [0.5, 0, 0.5]; 227 | % simCaseParamStructs{end}.computation.methodComputationMi = 'ZENG_XIAO_LU'; 228 | % simCaseParamStructs{end}.computation.methodComputationMmse = 'TRUE'; 229 | % nSnrPoints = 13; 230 | % simCaseParamStructs{end}.channel.snrDbVec = linspace(snrDbMin, snrDbMax, nSnrPoints); 231 | 232 | % Case #6 233 | % MI computation via ML-model predicted 234 | simCaseParamStructs{end+1} = {}; 235 | simCaseParamStructs{end}.plotting.legendEntry = 'DL-based'; 236 | simCaseParamStructs{end}.plotting.lineType = '--'; 237 | simCaseParamStructs{end}.plotting.lineMarker = 's'; 238 | simCaseParamStructs{end}.plotting.lineColor = [1, 0, 0]; 239 | simCaseParamStructs{end}.computation.methodComputationMi = 'ML_PREDICTED'; 240 | nSnrPoints = 13; 241 | simCaseParamStructs{end}.channel.snrDbVec = linspace(snrDbMin, snrDbMax, nSnrPoints); 242 | 243 | % ######################################################################### 244 | 245 | % ========================================================================= 246 | 247 | % Run simulations --------------------------------------------------------- 248 | 249 | % Create sim folder if it does not exist 250 | simName = defaultSimParamStruct.cluster.simName; 251 | simDataPath = defaultSimParamStruct.cluster.simDataPath; 252 | simFolderPath = defaultSimParamStruct.cluster.simFolderPath; 253 | simParamFileName = ['params_', simName, '.mat']; 254 | simParamFilePath = [simFolderPath, '\', simParamFileName]; 255 | if (~exist(simFolderPath)) 256 | mkdir(simFolderPath); 257 | end 258 | 259 | % Fill in param structs for picked sim cases 260 | nSimCases = length(simCaseParamStructs); 261 | for iSimCase = 1:nSimCases 262 | simCaseParamStructs{iSimCase}.cluster.caseIdx = iSimCase; 263 | simCaseParamStructs{iSimCase} =... 264 | overrideStruct(defaultSimParamStruct, simCaseParamStructs{iSimCase}); 265 | end % for iSimCase = 1:nSimCases 266 | 267 | % Save all params to mat-file 268 | save(simParamFilePath, 'simCaseParamStructs'); 269 | 270 | % Dispatch simulations 271 | for iSimCase = 1:nSimCases 272 | if strcmp(simCaseParamStructs{iSimCase}.signaling.typeModulation, 'GAUSS') 273 | % Waterfilling computation 274 | fprintf('\nRun water-filling locally... \n\n'); 275 | optimizePrecoderMimoGauss(simCaseParamStructs{iSimCase}); 276 | fprintf('DONE!\n'); 277 | elseif strcmp(simCaseParamStructs{iSimCase}.computation.methodComputationMi, 'ML_PREDICTED') 278 | % ML predicted precoder based on a trained neural net 279 | fprintf('\nPredict precoder locally... \n\n'); 280 | optimizePrecoderMimoMl(simCaseParamStructs{iSimCase}); 281 | fprintf('DONE!\n'); 282 | elseif strcmp(simCaseParamStructs{iSimCase}.computation.methodComputationMi, 'NONE') 283 | % No precoding at all 284 | fprintf('\nSet omni precoder locally... \n\n'); 285 | setOmniPrecoderMimo(simCaseParamStructs{iSimCase}); 286 | fprintf('DONE!\n'); 287 | elseif (simCaseParamStructs{iSimCase}.cluster.runOnCluster) 288 | % Running sim on cluster 289 | error('\nERROR! Functionality not supported!\n\n'); 290 | else 291 | % Local running of all other types of sims 292 | fprintf('\nRun new simulation locally... \n\n'); 293 | runSimsLocally(simCaseParamStructs{iSimCase}, operationType); 294 | fprintf('DONE!\n'); 295 | end 296 | end % for iSimCase = 1:nSimCases 297 | 298 | end 299 | 300 | 301 | 302 | -------------------------------------------------------------------------------- /PlotMiMimo.m: -------------------------------------------------------------------------------- 1 | % ========================================================================= 2 | % 3 | % Deep-learning based MIMO precoding for finite-alphabet signaling 4 | % 5 | % Low-complexity linear precoding for MIMO channels with discrete inputs 6 | % Plotting results 7 | % 8 | % Max Girnyk 9 | % Stockholm, 2014-10-01 10 | % 11 | % ========================================================================= 12 | % 13 | % This Matlab script produces results used in the following paper: 14 | % 15 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 16 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 17 | % 18 | % Paper URL: https://arxiv.org/abs/2111.03504 19 | % 20 | % Version: 1.0 (modified 2021-11-14) 21 | % 22 | % License: This code is licensed under the Apache-2.0 license. 23 | % If you use this code in any way for research that 24 | % results in a publication, please cite the above paper 25 | % 26 | % ========================================================================= 27 | 28 | clear all; close all; clc; 29 | 30 | % Add all the subfolders to the path. 31 | currentFilePath = fileparts(which(mfilename)); 32 | addpath(genpath(currentFilePath)); 33 | 34 | 35 | % SET SIMULATION NAMES ! ################################################## 36 | 37 | % simNames = {'190516141657_01', 190516140037, '190422212253'}; % either a case or an entire sim 38 | simNames = {'211115003222'}; 39 | 40 | % ========================================================================= 41 | 42 | visualsPath = [pwd, '\Visuals']; 43 | simDataPath = [pwd, '\SimData']; 44 | functionsPath = [pwd, '\Functions']; 45 | 46 | nSimNames = length(simNames); 47 | if (nSimNames==1) 48 | if iscell(simNames) 49 | if (isa(simNames{1}, 'double')) 50 | simNames{1} = num2str(simNames{1}); 51 | end 52 | delimiter = regexp(simNames{1}, '_'); 53 | if isempty(delimiter) 54 | isSingleSim = 1; 55 | end 56 | else 57 | if (isa(simNames, 'double')) 58 | simNames = num2str(simNames); 59 | end 60 | delimiter = regexp(simNames, '_'); 61 | if isempty(delimiter) 62 | isSingleSim = 1; 63 | end 64 | end 65 | else 66 | isSingleSim = 0; 67 | end 68 | 69 | fprintf('================================================================================================================\n'); 70 | fprintf('Load results... '); 71 | 72 | 73 | [simCaseNameList, simNameList] = parseSimIds(simNames); 74 | nSimCases = length(simCaseNameList); 75 | 76 | resultStruct = {}; 77 | for iSimCase = 1:nSimCases 78 | simFolderPath = [simDataPath, '\', simNameList{iSimCase}]; 79 | caseSubfolderPath = [simFolderPath, '\' simCaseNameList{iSimCase}]; 80 | fileList = dir([caseSubfolderPath, '\*.mat']); 81 | nFiles = length(fileList); 82 | resultStruct{iSimCase} = {}; 83 | 84 | for iFile = 1:nFiles 85 | simFileName = fileList(iFile).name; 86 | simFilePath = [caseSubfolderPath, '\', simFileName]; 87 | if (exist(simFilePath)) 88 | load(simFilePath); 89 | end 90 | if (isfield(simCaseStruct, 'performance')) 91 | resultStruct{iSimCase}.miBpcu(iFile) = simCaseStruct.performance.miBpcu; 92 | resultStruct{iSimCase}.timeElapsedSec(iFile) = simCaseStruct.performance.timeElapsedSec; 93 | else 94 | resultStruct{iSimCase}.miBpcu(iFile) = NaN; 95 | resultStruct{iSimCase}.timeElapsedSec(iFile) = NaN; 96 | end 97 | 98 | resultStruct{iSimCase}.snrDb(iFile) = simCaseStruct.channel.currentSnrDb; 99 | if (isfield(simCaseStruct, 'precoding')) 100 | if (isfield(simCaseStruct.precoding, 'precoder')) 101 | resultStruct{iSimCase}.precoders{iFile} = simCaseStruct.precoding.precoder; 102 | end 103 | else 104 | resultStruct{iSimCase}.precoders{iFile} = NaN; 105 | end 106 | end % for iFile = 1:nFiles 107 | 108 | resultStruct{iSimCase}.legendEntry = simCaseStruct.plotting.legendEntry; 109 | resultStruct{iSimCase}.lineType = simCaseStruct.plotting.lineType; 110 | resultStruct{iSimCase}.lineMarker = simCaseStruct.plotting.lineMarker; 111 | resultStruct{iSimCase}.lineColor = simCaseStruct.plotting.lineColor; 112 | resultStruct{iSimCase}.channelTag = simCaseStruct.channel.channelTag; 113 | resultStruct{iSimCase}.channelMatReal = simCaseStruct.channel.channelMatReal; 114 | resultStruct{iSimCase}.channelMatImag = simCaseStruct.channel.channelMatImag; 115 | clear simCaseStruct 116 | end % for iSimCase = 1:nSimCases 117 | 118 | fprintf('DONE!\n'); 119 | fprintf('================================================================================================================\n'); 120 | 121 | % PLOTS =================================================================== 122 | 123 | 124 | % Plot params 125 | legendFontSize = 14; 126 | axisFontSize = 16; 127 | lineWidth = 0.5; 128 | 129 | % Plot MI ------------------ 130 | figMi = figure(1); 131 | hold on; grid on 132 | for iSimCase = 1 : length(resultStruct) 133 | [~, iSnrDb] = sort(resultStruct{iSimCase}.snrDb); 134 | plot(resultStruct{iSimCase}.snrDb(iSnrDb), 2*resultStruct{iSimCase}.miBpcu(iSnrDb), 'Color', resultStruct{iSimCase}.lineColor,... 135 | 'LineStyle', resultStruct{iSimCase}.lineType, 'Marker', resultStruct{iSimCase}.lineMarker, 'LineWidth', lineWidth, 'DisplayName', resultStruct{iSimCase}.legendEntry); 136 | end 137 | xlabel('Signal-to-noise ratio [dB]', 'interpreter', 'latex', 'FontSize', axisFontSize); 138 | ylabel('Mutual information [bit/sym]', 'interpreter', 'latex', 'FontSize', axisFontSize); 139 | set(gca, 'fontsize', legendFontSize); 140 | legend(gca, 'show', 'Location', 'SouthEast') 141 | set(figMi, 'Units', 'Inches'); 142 | pos = get(figMi, 'Position'); 143 | set(figMi, 'PaperPositionMode', 'Auto', 'PaperUnits', 'Inches', 'PaperSize', [pos(3), pos(4)]) 144 | if isSingleSim 145 | if iscell(simNames) 146 | figFolderPath = [visualsPath, '\', simNames{1}]; 147 | figMiName = ['plot_mi_', simNames{1}, '.pdf']; 148 | else 149 | figFolderPath = [visualsPath, '\', simNames]; 150 | figMiName = ['plot_mi_', simNames, '.pdf']; 151 | end 152 | if (~exist(figFolderPath)) 153 | mkdir(figFolderPath); 154 | end 155 | figMiPath = [figFolderPath, '\', figMiName]; 156 | print(figMi, figMiPath, '-dpdf', '-r0'); 157 | end 158 | 159 | % Plot time ------------------ 160 | figTime = figure(2); 161 | hold on; grid on 162 | for iSimCase = 1 : length(resultStruct) 163 | [~, iSnrDb] = sort(resultStruct{iSimCase}.snrDb); 164 | plot(resultStruct{iSimCase}.snrDb(iSnrDb), resultStruct{iSimCase}.timeElapsedSec(iSnrDb), 'Color', resultStruct{iSimCase}.lineColor,... 165 | 'LineStyle', resultStruct{iSimCase}.lineType, 'Marker', resultStruct{iSimCase}.lineMarker, 'LineWidth', lineWidth, 'DisplayName', resultStruct{iSimCase}.legendEntry); 166 | end 167 | xlabel('Signal-to-noise ratio [dB]', 'interpreter', 'latex', 'FontSize', axisFontSize); 168 | ylabel('Time [min]', 'interpreter', 'latex', 'FontSize', axisFontSize); 169 | set(gca, 'fontsize', legendFontSize); 170 | legend(gca,'show','Location','NorthEast') 171 | set(figTime, 'Units', 'Inches'); 172 | pos = get(figTime, 'Position'); 173 | set(figTime, 'PaperPositionMode', 'Auto', 'PaperUnits', 'Inches', 'PaperSize', [pos(3), pos(4)]) 174 | if isSingleSim 175 | figFolderPath = [visualsPath, '\', simNames{1}]; 176 | figTimeName = ['plot_time_', simNames{1}, '.pdf']; 177 | figTimePath = [figFolderPath, '\', figTimeName]; 178 | print(figTime, figTimePath, '-dpdf', '-r0'); 179 | end 180 | 181 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep-Learning Based Linear Precoding for MIMO Channels with Finite-Alphabet Signaling 2 | This repository contains the codes for producing the figures from the following article: 3 | 4 | M. A. Girnyk (2021), "[Deep-Learning Based Linear Precoding for MIMO Channels with Finite-Alphabet Signaling](https://www.sciencedirect.com/science/article/abs/pii/S1874490721001397)," *Physical Communication*, vol. 48, 101402, Oct. 2021. 5 | 6 | ## Abstract 7 | This paper studies the problem of linear precoding for multiple-input multiple-output (MIMO) communication channels employing finite-alphabet signaling. Existing solutions typically suffer from high computational complexity due to costly computations of the constellation-constrained mutual information. In contrast to existing works, this paper takes a different path of tackling the MIMO precoding problem. Namely, a data-driven approach, based on deep learning, is proposed. In the offline training phase, a deep neural network learns the optimal solution on a set of MIMO channel matrices. This allows the reduction of the computational complexity of the precoder optimization in the online inference phase. Numerical results demonstrate the efficiency of the proposed solution vis-à-vis existing precoding algorithms in terms of significantly reduced complexity and close-to-optimal performance. 8 | 9 | ## Preprint 10 | A preprint of the article is available at https://arxiv.org/pdf/2111.03504.pdf. 11 | 12 | ## Software requirements 13 | The codes have been developed in Matlab 2015a and should not require additional packages. 14 | 15 | ## License 16 | This code is licensed under the Apache-2.0 license. If you are using this code in any way for research that results in a publication, please cite the article above. 17 | -------------------------------------------------------------------------------- /TrainNnModelMimo.m: -------------------------------------------------------------------------------- 1 | % ========================================================================= 2 | % 3 | % Deep-learning based MIMO precoding for finite-alphabet signaling 4 | % 5 | % Low-complexity linear precoding for MIMO channels with discrete inputs 6 | % Training of a neural network 7 | % 8 | % Max Girnyk 9 | % Stockholm, 2014-10-01 10 | % 11 | % ========================================================================= 12 | % 13 | % This Matlab script produces results used in the following paper: 14 | % 15 | % M. A. Girnyk, "Deep-learning based linear precoding for MIMO channels 16 | % with finite-alphabet signaling," Physical Communication 48(2021) 101402 17 | % 18 | % Paper URL: https://arxiv.org/abs/2111.03504 19 | % 20 | % Version: 1.0 (modified 2021-11-14) 21 | % 22 | % License: This code is licensed under the Apache-2.0 license. 23 | % If you use this code in any way for research that 24 | % results in a publication, please cite the above paper 25 | % 26 | % ========================================================================= 27 | 28 | function TrainNnModelMimo 29 | 30 | % Clear and close everything 31 | clear all; clc; close all; fclose('all'); 32 | 33 | % Add all the subfolders to the path. 34 | currentFilePath = fileparts(which(mfilename)); 35 | addpath(genpath(currentFilePath)); 36 | simDataPath = [pwd, '\SimData']; 37 | 38 | % Set data and params for neural nets 39 | 40 | % 2x2 BPSK 41 | modName = 'Bpsk'; 42 | mimoSetup = '2x2'; 43 | simNames = {190716111048, 210409160456, 210411002555, 210413235054, 210425005012}; 44 | nEpochs = 3e2; 45 | learnRate = 0.0025; 46 | miniBatchSize = 10; 47 | 48 | % % 2x2 QPSK 49 | % modName = 'Qpsk'; 50 | % mimoSetup = '2x2'; 51 | % simNames = {210213125113, 210212000423, 210419010351}; 52 | % nEpochs = 3e2; 53 | % learnRate = 0.0025; 54 | % miniBatchSize = 10; 55 | 56 | % % 3x3 BPSK 57 | % modName = 'Bpsk'; 58 | % mimoSetup = '3x3'; 59 | % simNames = {210328011048, 210418003001}; 60 | % nEpochs = 3e2; 61 | % learnRate = 0.0025; 62 | % miniBatchSize = 10; 63 | 64 | % % 3x3 QPSK 65 | % modName = 'Qpsk'; 66 | % mimoSetup = '3x3'; 67 | % simNames = {210317170605, 210413020311, 210421180930, 210421091536}; 68 | % learnRate = 0.005; 69 | % nEpochs = 5e2; 70 | % miniBatchSize = 10; 71 | 72 | fprintf('================================================================================================================\n'); 73 | fprintf('Load data... '); 74 | 75 | nSimNames = length(simNames); 76 | if (nSimNames==1) 77 | if iscell(simNames) 78 | if (isa(simNames{1}, 'double')) 79 | simNames{1} = num2str(simNames{1}); 80 | end 81 | delimiter = regexp(simNames{1}, '_'); 82 | if isempty(delimiter) 83 | isSingleSim = 1; 84 | end 85 | else 86 | if (isa(simNames, 'double')) 87 | simNames = num2str(simNames); 88 | end 89 | delimiter = regexp(simNames, '_'); 90 | if isempty(delimiter) 91 | isSingleSim = 1; 92 | end 93 | end 94 | else 95 | isSingleSim = 0; 96 | end 97 | 98 | nTestObs = 0; 99 | 100 | [simCaseNameList, simNameList] = parseSimIds(simNames); 101 | nSimCases = length(simCaseNameList); 102 | 103 | % Infer the required data 104 | dataStruct = {}; 105 | for iSimCase = nTestObs+1:nSimCases 106 | simFolderPath = [simDataPath, '\', simNameList{iSimCase}]; 107 | caseSubfolderPath = [simFolderPath, '\' simCaseNameList{iSimCase}]; 108 | fileList = dir([caseSubfolderPath, '\*.mat']); 109 | nFiles = length(fileList); 110 | for iFile = 1:nFiles 111 | simFileName = fileList(iFile).name; 112 | simFilePath = [caseSubfolderPath, '\', simFileName]; 113 | if (exist(simFilePath)) 114 | load(simFilePath); 115 | end 116 | if (isfield(simCaseStruct, 'performance')) && (isfield(simCaseStruct, 'precoding')) 117 | dataStruct{end+1} = {}; 118 | dataStruct{end}.miBpcu = simCaseStruct.performance.miBpcu; 119 | dataStruct{end}.precoderReal = simCaseStruct.precoding.precoderReal; 120 | dataStruct{end}.precoderImag = simCaseStruct.precoding.precoderImag; 121 | dataStruct{end}.snrDb = simCaseStruct.channel.currentSnrDb; 122 | dataStruct{end}.channelMatReal = simCaseStruct.channel.channelMatReal; 123 | dataStruct{end}.channelMatImag = simCaseStruct.channel.channelMatImag; 124 | dataStruct{end}.nTxAntennas = simCaseStruct.channel.nTxAntennas; 125 | dataStruct{end}.nRxAntennas = simCaseStruct.channel.nRxAntennas; 126 | dataStruct{end}.typeModulation = simCaseStruct.signaling.typeModulation; 127 | dataStruct{end}.timestamp = simNameList{iSimCase}; 128 | end 129 | clear simCaseStruct 130 | end 131 | end 132 | 133 | fprintf('DONE!\n'); 134 | 135 | 136 | fprintf('Construct dataset... '); 137 | 138 | % Construct the dataset: create features and labels 139 | dataset = {}; 140 | nObservations = length(dataStruct); 141 | for iObservation = 1:nObservations 142 | dataset{end+1} = {}; 143 | dataset{end}.nTxAntennas = dataStruct{iObservation}.nTxAntennas; 144 | dataset{end}.nRxAntennas = dataStruct{iObservation}.nRxAntennas; 145 | snr = 10^(dataStruct{iObservation}.snrDb/10); 146 | dataset{end}.snrDb = dataStruct{iObservation}.snrDb; 147 | channelMat = sqrt(snr/dataset{end}.nTxAntennas) * (dataStruct{iObservation}.channelMatReal + 1i*dataStruct{iObservation}.channelMatImag); 148 | dataset{end}.channelVec = convertComplexMatToRealVec(channelMat); 149 | precoderWf = getWfPrecoder(channelMat); 150 | dataset{end}.precoderVecWf = convertComplexMatToRealVec(precoderWf); 151 | precoderDiscrete = dataStruct{iObservation}.precoderReal + 1i*dataStruct{iObservation}.precoderImag; 152 | dataset{end}.precoderVecDiscrete = convertComplexMatToRealVec(precoderDiscrete); 153 | dataset{end}.miBpcu = dataStruct{iObservation}.miBpcu; 154 | dataset{end}.timestamp = dataStruct{iObservation}.timestamp; 155 | dataset{end}.typeModulation = dataStruct{iObservation}.typeModulation; 156 | end 157 | 158 | fprintf('DONE!\n'); 159 | 160 | fprintf('Train neural net... \n'); 161 | 162 | % Split data for training and testing 163 | trainShare = 0.8; 164 | [trainSet, testSet] = splitDataset(dataset, trainShare); 165 | 166 | 167 | % Define and create neural net 168 | inputSize = length(dataset{end}.precoderVecWf); % 2*vec(G_wf) - size of input 169 | outputSize = length(dataset{end}.precoderVecDiscrete); % 2*vec(G_disc) - size of output 170 | layerSizes = [inputSize, 2*inputSize, 2*inputSize, outputSize]; % 1 hidden layer with double the size 171 | activationType = 'tanh'; 172 | neuralNet = setupNeuralNet(layerSizes, activationType); 173 | 174 | % Run the SGD to train and evaluate neural net 175 | [neuralNet, mseTrain, mseValid] = trainAndTestNeuralNet(neuralNet, trainSet, nEpochs, miniBatchSize, learnRate, testSet); 176 | 177 | fprintf('DONE!\n'); 178 | 179 | fprintf('Save neural net... '); 180 | 181 | % save the neural net 182 | netsPath = [pwd, '\TrainedNets']; 183 | % Create the case subfolder 184 | if (~exist(netsPath)) 185 | mkdir(netsPath); 186 | end 187 | netsPath = [pwd, '\TrainedNets']; 188 | netFileName = ['nnWeights', modName, mimoSetup, 'Mimo.mat']; 189 | save([netsPath, '\', netFileName], 'neuralNet'); 190 | 191 | fprintf('DONE!\n'); 192 | 193 | end -------------------------------------------------------------------------------- /TrainedNets/nnWeightsBpsk2x2Mimo.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/girnyk/OptimalPrecodingMimo/9a9d46a7b9618d6b966ba4c9dbe164d9cc5a5004/TrainedNets/nnWeightsBpsk2x2Mimo.mat -------------------------------------------------------------------------------- /TrainedNets/nnWeightsBpsk3x3Mimo.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/girnyk/OptimalPrecodingMimo/9a9d46a7b9618d6b966ba4c9dbe164d9cc5a5004/TrainedNets/nnWeightsBpsk3x3Mimo.mat -------------------------------------------------------------------------------- /TrainedNets/nnWeightsQpsk2x2Mimo.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/girnyk/OptimalPrecodingMimo/9a9d46a7b9618d6b966ba4c9dbe164d9cc5a5004/TrainedNets/nnWeightsQpsk2x2Mimo.mat -------------------------------------------------------------------------------- /TrainedNets/nnWeightsQpsk3x3Mimo.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/girnyk/OptimalPrecodingMimo/9a9d46a7b9618d6b966ba4c9dbe164d9cc5a5004/TrainedNets/nnWeightsQpsk3x3Mimo.mat --------------------------------------------------------------------------------