def ldpcOuterEncode(self, messages): """ Perform outer encoding of codewords :param messages: messages to be outer-encoded """ # Create aliases for several parameters numBins = self.__numBins L = self.__L M = self.__M K = self.__K # Establish important parameters self.__OuterCodes = [FGG.Triadic8(16) for i in range(numBins)] txcodewords = 0 # list of all codewords transmitted codewords = [] # list of codewords to transmitted by bin sTrue = [] # list of signals sent by various bins # Outer encode each message for i in range(numBins): cdwds = self.__OuterCodes[i].encodemessages( messages[i]) if len(messages[i]) > 0 else [np.zeros(L * M)] for cdwd in cdwds: # ensure that each codeword is valid self.__OuterCodes[i].testvalid(cdwd) codewords.append( cdwds) # add encoded messages to list of codewords txcodewords = np.vstack( (txcodewords, cdwds)) if not np.isscalar(txcodewords) else cdwds.copy() # Combine codewords to form signal to transmit if K[i] == 0: # do nothing if there are no users in this bin tmp = np.zeros(L * M).astype(np.float64) else: # otherwise, add all codewords together tmp = np.sum(cdwds, axis=0) sTrue.append(tmp) # store true signal for future reference return txcodewords, sTrue
def simulateSingleClass(EbNodB): simCount = 100 # number of simulations msgDetected1 = 0 msgDetected2 = 0 K1Sum = 0 K2Sum = 0 numMC = 0 for simIndex in range(simCount): Ka = 64 K1 = 0 K1 = np.sum(np.random.randn(Ka) > 0) K2 = Ka - K1 K1Sum += K1 K2Sum += K2 print('K1: ', K1) print('K2: ', K2) B1 = 128 # Payload size of every active user in group 1 B2 = 128 # Payload size of every active user in group 2 L1 = 16 # Number of sections/sub-blocks in group 1 L2 = 16 # Number of sections/sub-blocks in group 2 n = 38400 # Total number of channel uses (real d.o.f) T = 10 # Number of AMP iterations J1 = 16 # Length of each coded sub-block J2 = 16 # Length of each coded sub-block M1 = 2**J1 # Length of each section M2 = 2**J2 # Length of each section numBPiter = 1 # Number of BP iterations on outer code. 1 seems to be good enough & AMP theory including state evolution valid only for one BP iteration # EbNodB = 2.4 # Energy per bit. With iterative extension, operating EbN0 falls to 2.05 dB for 25 users with 1 round SIC # EbN0 in linear scale EbNo = 10**(EbNodB / 10) P1 = 2 * B1 * EbNo / n P2 = 2 * B2 * EbNo / n s_n = 1 # We assume equal power allocation for all the sections. Code has to be modified a little to accomodate non-uniform power allocations Phat1 = n * P1 / L1 Phat2 = n * P2 / L2 d1 = np.sqrt(n * P1 / L1) d2 = np.sqrt(n * P2 / L2) print('Simulation Number: ' + str(simIndex)) # Generate active users message sequences messages1 = np.random.randint(2, size=(K1, B1)) messages2 = np.random.randint(2, size=(K2, B2)) # Outer-encode the message sequences codewords1 = OuterCode1.encodemessages(messages1) for codeword1 in codewords1: OuterCode1.testvalid(codeword1) codewords2 = OuterCode2.encodemessages(messages2) for codeword2 in codewords2: OuterCode2.testvalid(codeword2) # Convert indices to sparse representation # sTrue: True state sTrue1 = np.sum(codewords1, axis=0) sTrue2 = np.sum(codewords2, axis=0) # Generate the binned SPARC codebook Ab1, Az1 = sparc_codebook(L1, M1, n, P1) Ab2, Az2 = sparc_codebook(L2, M2, n, P2) # Generate our transmitted signal X x = d1 * Ab1(sTrue1) + d2 * Ab2(sTrue2) # Generate random channel noise and thus also received signal y noise = np.random.randn(n, 1) * s_n y = (x + noise) z = y.copy() s1 = np.zeros((L1 * M1, 1)) s2 = np.zeros((L2 * M2, 1)) K1ht = Ka K2ht = Ka confident = False for t in range(T): # compute effective observations r1 = amp_effective_observation(z, s1, P1, L1, Az1) r2 = amp_effective_observation(z, s2, P2, L2, Az2) # AMP state updates s1 = amp_state_update(r1, z, P1, L1, K1ht, Ka, numBPiter, OuterCode1, confident) s2 = amp_state_update(r2, z, P2, L2, K2ht, Ka, numBPiter, OuterCode2, confident) # Estimate K1, K2 if not confident: k1ht, k2ht, confident = amp_estimate_k_distribution( s1, s2, L1, M1, Ka, t) if confident: # employ estimate after 2 amp iterations K1ht = k1ht K2ht = k2ht if k1ht != K1: numMC += 1 print('ERROR: K1, K2 misestimated.') # AMP residual z = amp_residual(y, z, s1, s2, d1, d2, Ab1, Ab2) # continue # raise Exception() print('Graph Decode') # Decoding with Graph originallist1 = codewords1.copy() originallist2 = codewords2.copy() qq1 = int(K1ht * 1.5) if confident else int(Ka * 1.5 / 2) qq2 = int(K2ht * 1.5) if confident else int(Ka * 1.5 / 2) recoveredcodewords1 = OuterCode1.decoder(s1, qq1) recoveredcodewords2 = OuterCode2.decoder(s2, qq2) # Calculation of per-user prob err matches1 = FGG.numbermatches(originallist1, recoveredcodewords1) matches2 = FGG.numbermatches(originallist2, recoveredcodewords2) print('Group 1: ' + str(matches1) + ' out of ' + str(K1)) print('Group 2: ' + str(matches2) + ' out of ' + str(K2)) msgDetected1 = msgDetected1 + matches1 msgDetected2 = msgDetected2 + matches2 # errorRate1= (K1*simCount - msgDetected1)/(K1*simCount) # errorRate2= (K2*simCount - msgDetected2)/(K2*simCount) errorRate1 = (K1Sum - msgDetected1) / K1Sum errorRate2 = (K2Sum - msgDetected2) / K2Sum print("Per user probability of error (Group 1) = ", errorRate1) print("Per user probability of error (Group 2) = ", errorRate2) print("Average PUPE = ", 0.5 * (errorRate1 + errorRate2)) return errorRate1, errorRate2, 0.5 * (errorRate1 + errorRate2), numMC
import numpy as np import FactorGraphGeneration as FGG from pyfht import block_sub_fht OuterCode1 = FGG.Triadic8(16) OuterCode2 = FGG.Triadic8(16) def sparc_codebook(L, M, n, P): Ax, Ay, _ = block_sub_fht(n, M, L, seed=None, ordering=None) # seed must be explicit def Ab(b): return Ax(b).reshape(-1, 1) / np.sqrt(n) def Az(z): return Ay(z).reshape(-1, 1) / np.sqrt(n) return Ab, Az def approximateVector(x, K): # normalize initial value of x xOrig = x / np.linalg.norm(x, ord=1) # create vector to hold best approximation of x xHt = xOrig.copy() u = np.zeros(len(xHt)) # run approximation algorithm
import ccsfg import FactorGraphGeneration as FG import ccsinnercode as ccsic import numpy as np # Initialize CCS-AMP Graph Graph = FG.Triadic8(16) # Simulation Parameters Ka = 25 # Number of active users w = 128 # Payload size of each active user (per user message length) N = 38400 # Total number of channel uses (real d.o.f) listSize = Ka + 10 # List size retained for each section after AMP converges numAmpIter = 6 # Number of AMP iterations numBPIter = 1 # Number of BP iterations to perform BPonOuterGraph = True # Indicates whether to perform BP on outer code. If 0, AMP uses Giuseppe's uninformative prior maxSims = 2 # Number of Simulations to Run EbNodB = 2.4 # Energy per bit in decibels EbNo = 10**(EbNodB / 10) # Eb/No in linear scale P = 2 * w * EbNo / N # transmit power std = 1 # Noise standard deviation errorRate = 0.0 # track error rate across simulations # Run CCS-AMP maxSims times for idxsim in range(maxSims): print('Starting simulation %d of %d' % (idxsim + 1, maxSims)) # Reset the graph Graph.reset()
errorRate = 0.0 # track error rate across simulations # Assign power to occupancy estimation and data transmission tasks pM = 80 dmsg = np.sqrt(N * P * N / (pM + N) / L) if NUM_BINS > 1 else np.sqrt(N * P / L) dbid = np.sqrt(N * P * pM / (pM + N)) if NUM_BINS > 1 else 0 assert np.abs(L * dmsg**2 + dbid**2 - N * P) <= 1e-3, "Total power constraint violated." # Average of maxSims trials for idxsim in range(maxSims): print('Starting simulation %d of %d' % (idxsim + 1, maxSims)) # Initialze outer codes OuterCodes = [FGG.Triadic8(16) for i in range(NUM_BINS)] # Generate messages for all Ka users usrmessages = np.random.randint(2, size=(Ka, w)) # Split users into bins based on the first couple of bits in their messages w0 = int(np.ceil(np.log2(NUM_BINS))) binIds = np.matmul(usrmessages[:, 0:w0], 2** np.arange(w0)[::-1]) if w0 > 0 else np.zeros(Ka) K = np.array([np.sum(binIds == i) for i in range(NUM_BINS)]).astype(int) # Group usrmessages by bin messages = [usrmessages[np.where(binIds == i)[0]] for i in range(NUM_BINS)] # Transmit bin identifier across channel rxK = dbid * K + np.random.randn(NUM_BINS) * std
import FactorGraphGeneration as FG import ccsinnercode as ccsic import numpy as np import time # Simulation Parameters K = 100 # number of active users N = 38400 # number of channel uses (real d.o.f) w = 128 # length of each user's uncoded message numAMPIter = 10 # number of AMP iterations to perform numBPIter = 1 # number of BP iterations to perform listSize = K + 10 # list size retained per section after AMP converges nstd = 1 # AWGN noise standard deviation numSims = 2 # number of trials to average over per point on the graph OuterGraph = FG.Triadic8(16) # factor graph associated with outer LDPC code SNRs = np.arange(start=1.5, stop=4.6, step=0.5) # SNRs in (dB) to test over SNRs = np.array([2.5]) errorrates = np.zeros( (4, len(SNRs))) # data structure for storing PUPE results runtimes = np.zeros( (4, len(SNRs))) # data structure for storing runtime results # Iterate over each SNR for idxsnr in range(len(SNRs)): # Compute power EbNo = 10**(SNRs[idxsnr] / 10) P = 2 * w * EbNo / N # Average over maxSims trials for idxsim in range(numSims):
def simulate(Ka, NUM_BINS, EbNodB, GENIE_AIDED): """ Run coded demixing simulation :param Ka: total number of users :param NUM_BINS: total number of bins :param EbNodB: Eb/No in dB :param GENIE_AIDED: flag of whether to use genie-aided estimate of K """ B = 128 # length of each user's message in bits L = 16 # number of sections M = 2**L # length of each section n = 38400 # number of channel uses (real dof) numBPiter = 1 # Number of BP iterations on outer code numSICIter = 2 # Number of SIC iterations - WARNING: code must be modified if numSICIter != 2 gamma = 0.7 if numSICIter == 2 else 1 # Percentage of codewords to recover on the first round of SIC simCount = 100 # number of trials to average over errorRate = 0 # store error rate delta = 5 # constant number of extra codewords to retain # Compute number of AMP iterations numAMPIter = min(max(10, 10 + 3 * int((Ka - 50) / 25)), 24) # Compute signal and noise power parameters EbNo = 10**(EbNodB / 10) P = 2 * B * EbNo / n s_n = 1 # Assign power to occupancy estimation and data transmission tasks pM = 80 dcs = np.sqrt(n * P * n / (pM + n) / L) if NUM_BINS > 1 else np.sqrt(n * P / L) dbid = np.sqrt(n * P * pM / (pM + n)) if NUM_BINS > 1 else 0 assert np.abs(L * dcs**2 + dbid**2 - n * P) <= 1e-3, "Total power constraint violated." # run simCount trials for simIndex in range(simCount): print('**********Simulation Number: ' + str(simIndex)) """********************************************************************************* Step 1: users generate messages and stochastically partition themselves into groups. **********************************************************************************""" # Generate messages for all Ka users usrmessages = np.random.randint(2, size=(Ka, B)) # Split users into bins based on the first couple of bits in their messages w0 = int(np.ceil(np.log2(NUM_BINS))) binIds = np.matmul(usrmessages[:, 0:w0], 2** np.arange(w0)[::-1]) if w0 > 0 else np.zeros(Ka) K = np.array([np.sum(binIds == i) for i in range(NUM_BINS)]).astype(int) # Group messages by bin messages = [ usrmessages[np.where(binIds == i)[0]] for i in range(NUM_BINS) ] """*************************************************************** Step 2: receiver estimates the number of users present in each bin ***************************************************************""" # Transmit bin identifier across channel rxK = dbid * K + np.random.randn(NUM_BINS) * s_n # Perform LMMSE bin occupancy estimation Kht = estimate_bin_occupancy( Ka, dbid, rxK, K, s_n, GENIE_AIDED) if NUM_BINS > 1 else K.copy() """******************************************************************* Step 3: outer/inner message encoding and transmission over AWGN channel ********************************************************************""" # Generate outer graphs OuterCodes = [] for i in range(NUM_BINS): OuterCodes.append(FGG.Triadic8(16)) # Define data structures to hold encoding/decoding parameters txcodewords = 0 # list of all codewords transmitted codewords = [] # list of codewords to transmitted by bin sTrue = [] # list of signals sent by various bins Ab = [] # list of sensing matrices used by various bins Az = [] # list of sensing matrices transposed used by various bins # Bin-Specific Encoding Operations for i in range(NUM_BINS): # Outer encode each message cdwds = OuterCodes[i].encodemessages( messages[i]) if len(messages[i]) > 0 else [np.zeros(L * M)] for cdwd in cdwds: # ensure that each codeword is valid OuterCodes[i].testvalid(cdwd) codewords.append( cdwds) # add encoded messages to list of codewords # Store codewords in 'txcodewords' for error computation if K[i] > 0: txcodewords = np.vstack( (txcodewords, cdwds)) if not np.isscalar(txcodewords) else cdwds.copy() # Combine codewords to form signal to transmit if K[i] == 0: # do nothing if there are no users in this bin tmp = np.zeros(L * M).astype(np.float64) else: # otherwise, add all codewords together tmp = np.sum(cdwds, axis=0) sTrue.append(tmp) # store true signal for future reference # Generate the binned SPARC codebook a, b = sparc_codebook(L, M, n) Ab.append(a) Az.append(b) # Generate transmitted signal X x = 0.0 for i in range(NUM_BINS): x += dcs * Ab[i](sTrue[i]) # Transmit signal X through AWGN channel y = x + np.random.randn(n, 1) * s_n """******************************************************** Step 4: outer/inner decoding and message recovery using SIC ********************************************************""" # reset number of matches between tx and rx codewords matches = 0 # Run numSICIter iterations of SIC for idxsiciter in range(numSICIter): print('Starting SIC iteration ' + str(idxsiciter)) """*********************** Step 5: Inner AMP decoding ***********************""" # Prepare for inner AMP decoding z = y.copy() # initialize AMP residual s = [np.zeros((L * M, 1)) for i in range(NUM_BINS)] # initialize AMP states # AMP Inner decoder for idxampiter in range(numAMPIter): # Update the state of each bin individually s = [ amp_state_update(z, s[i], P, L, Az[i], Kht[i], numBPiter, OuterCodes[i]) for i in range(NUM_BINS) ] # compute residual jointly z = amp_residual(y, z, s, dcs, Ab) """************************* Step 6: Outer graph decoding *************************""" # Prepare for outer graph decoding recoveredcodewords = dict() # Graph-based outer decoder for idxbin in range(NUM_BINS): if Kht[idxbin] == 0: continue # Produce list of recovered codewords with their associated likelihoods recovered, likelihoods = OuterCodes[idxbin].decoder( s[idxbin], int(4 * Kht[idxbin] + delta), includelikelihoods=True) # Compute what the first w0 bits should be based on bin number binIDBase2 = np.binary_repr(idxbin) binIDBase2 = binIDBase2 if len(binIDBase2) == w0 else ( w0 - len(binIDBase2)) * '0' + binIDBase2 firstW0bits = np.array([ binIDBase2[i] for i in range(len(binIDBase2)) ]).astype(int) # Enforce CRC consistency and add recovered codewords to data structure indexed by likelihood for idxcdwd in range(len(likelihoods)): # Extract first part of message from codeword firstinfosection = OuterCodes[idxbin].infolist[0] - 1 sparsemsg = recovered[idxcdwd][firstinfosection * M:(firstinfosection + 1) * M] # Find index of nonzero entry and convert to binary representation idxnonzero = np.where(sparsemsg > 0.0)[0][0] idxnonzerobin = np.binary_repr(idxnonzero) # Add trailing zeros to base-2 representation if len(idxnonzerobin) < 16: idxnonzerobin = ( 16 - len(idxnonzerobin)) * '0' + idxnonzerobin # Extract first w0 bits and determine bin ID msgfirstW0bits = np.array( [idxnonzerobin[i] for i in range(w0)]).astype(int) # Enforce CRC consistency if (msgfirstW0bits == firstW0bits).all() or (NUM_BINS == 1): recoveredcodewords[likelihoods[idxcdwd]] = np.hstack( (recovered[idxcdwd], idxbin)) # sort dictionary of recovered codewords in descending order of likelihood sortedcodewordestimates = sorted(recoveredcodewords.items(), key=lambda x: -x[0]) # recover percentage of codewords based on SIC iteration numCdwds = np.ceil(gamma * Ka).astype(int) if idxsiciter == 0 else ( Ka - np.ceil(gamma * Ka).astype(int)) sortedcodewordestimates = sortedcodewordestimates[0:numCdwds] # remove contribution of recovered users from received signal and prepare for PUPE computation codewordestimates = 0 for idxcdwd in range(len(sortedcodewordestimates)): # add codeword to numpy array of rx codewords for PUPE computation codewordestimates = np.vstack(( codewordestimates, sortedcodewordestimates[idxcdwd][1][0:L*M])) \ if not np.isscalar(codewordestimates) \ else sortedcodewordestimates[idxcdwd][1][0:L*M].copy() # SIC remove contribution of recovered codeword idxcdwdbin = sortedcodewordestimates[idxcdwd][1][-1].astype( int) # extract bin index as an integer y = y - dcs * Ab[idxcdwdbin]( sortedcodewordestimates[idxcdwd][1] [0:L * M]) # remove codeword's contribution to rx signal y Kht[idxcdwdbin] = K[idxcdwdbin] - 1 if K[ idxcdwdbin] > 0 else 0 # decrement estimated number of users in codeword's bin # Update number of matches matches += FGG.numbermatches(txcodewords, codewordestimates) print( str(matches) + ' matches after SIC iteration ' + str(idxsiciter)) """***************** Step 7: Compute PUPE *****************""" # Compute error rate errorRate += (Ka - matches) / (Ka * simCount) print('Cumulative Error Rate: ' + str(errorRate * simCount / (simIndex + 1))) return errorRate
def simulate(EbNodB): Ka = 64 B = 128 L = OuterCodes[0].varcount M = OuterCodes[0].sparseseclength n=38400 # Total number of channel uses (real d.o.f) T=10 # Number of AMP iterations numBPiter = 1 # Number of BP iterations on outer code. 1 seems to be good enough & AMP theory including state evolution valid only for one BP iteration simCount = 100 # number of simulations # EbN0 in linear scale EbNo = 10**(EbNodB/10) P = 2*B*EbNo/n σ_n = 1 # Compute power multiplier for bin identifier bits desiredBinIDPowerMultiplier = 5 pM = desiredBinIDPowerMultiplier*NUM_BINS*B/(B*NUM_BINS - (desiredBinIDPowerMultiplier - 1)*NUM_BINS**2) # We assume equal power allocation for all the sections. Code has to be modified a little to accomodate non-uniform power allocations dcs = np.sqrt(n*P*B/(pM*NUM_BINS + B)/L) dbid = np.sqrt(n*P*pM*NUM_BINS/(pM*NUM_BINS + B)/NUM_BINS) if np.abs(L*dcs**2 + NUM_BINS*dbid**2 - n*P) > 1e-3: raise Exception('Total power constraint not met') errorRates = np.zeros(NUM_BINS) for simIndex in range(simCount): print('******************************Simulation Number: ' + str(simIndex)) # Randomly assign users to groups K = np.zeros(NUM_BINS).astype(int) for i in range(Ka): K[np.random.randint(NUM_BINS)] += 1 # Define bin LUT binIdLUT = np.eye(NUM_BINS) * dbid # Send bin identifiers binIdBits = np.matmul(K, binIdLUT) # Transmit bin identifier across channel rxBinIdBits = binIdBits + np.random.randn(NUM_BINS) * σ_n # Estimate Ki Kht = np.zeros(NUM_BINS).astype(int) for i in range(NUM_BINS): Kht[i] = round(np.inner(binIdLUT[i, :], rxBinIdBits) / (dbid**2)) print('True K: ' + str(K)) print('Estimated K: ' + str(Kht)) # Generate active users message sequences messages = [] for i in range(NUM_BINS): messages.append(np.random.randint(2, size=(K[i], B))) # Outer-encode the message sequences codewords = [] sTrue = [] Ab = [] Az = [] for i in range(NUM_BINS): # Outer-encode the message sequences cdwds = OuterCodes[i].encodemessages(messages[i]) for cdwd in cdwds: OuterCodes[i].testvalid(cdwd) codewords.append(OuterCodes[i].encodemessages(messages[i])) # Convert indices to sparse representation sTrue.append(np.sum(cdwds, axis=0)) # Generate the binned SPARC codebook a, b = sparc_codebook(L, M, n, P) Ab.append(a) Az.append(b) # Generate our transmitted signal X x = 0.0 for i in range(NUM_BINS): x += dcs*Ab[i](sTrue[i]) # Generate random channel noise and thus also received signal y noise = np.random.randn(n, 1) * σ_n y = (x + noise) z = y.copy() s = [] for i in range(NUM_BINS): s.append(np.zeros((L*M, 1))) for t in range(T): print(np.sqrt(np.sum(z**2)/n)) for i in range(NUM_BINS): s[i] = amp_state_update(z, s[i], P, L, Ab[i], Az[i], Kht[i], numBPiter, OuterCodes[0]) z = amp_residual(y, z, s, dcs, Ab) print('Graph Decode') # Decoding with Graph for i in range(NUM_BINS): original = codewords[i].copy() recovered = OuterCodes[i].decoder(s[i], int(Kht[i] * 1.5)) matches = FGG.numbermatches(original, recovered) print('Group ' + str(i+1) + ': ' + str(matches) + ' out of ' + str(K[i])) # msgDetected[i] += matches errorRates[i] += ((K[i] - matches)/(K[i])) / simCount for i in range(NUM_BINS): # errorRates[i] = (K[i]*simCount - msgDetected[i]) / (K[i] * simCount) print("Per user probability of error (Group " + str(i+1) + ") = ", errorRates[i]) # return errorRate1, errorRate2, 0.5*(errorRate1 + errorRate2) return errorRates, np.average(errorRates)
import numpy as np import FactorGraphGeneration as FGG from pyfht import block_sub_fht NUM_BINS = 2 # Generate outer graphs OuterCodes = [] for i in range(NUM_BINS): OuterCodes.append(FGG.Triadic8(16)) def sparc_codebook(L, M, n,P): Ax, Ay, _ = block_sub_fht(n, M, L, seed=None, ordering=None) # seed must be explicit def Ab(b): return Ax(b).reshape(-1, 1) / np.sqrt(n) def Az(z): return Ay(z).reshape(-1, 1) / np.sqrt(n) return Ab, Az def approximateVector(x, K): # normalize initial value of x xOrig = x / np.linalg.norm(x, ord=1) # create vector to hold best approximation of x xHt = xOrig.copy() u = np.zeros(len(xHt)) # run approximation algorithm while np.amax(xHt) > (1/K): minIndices = np.argmin([(1/K)*np.ones(xHt.shape), xHt], axis=0)
def simulate(self, Ktot, EbNodB, numSims): """ Run a coded demixing simulation. :param Ktot: total number of active users :param EbNodB: Eb/No in dB scale :param numSims: number of trials to average the results over """ # Create alias for certain parameters w = self.__w n = self.__n L = self.__L pM = self.__pM numBins = self.__numBins # Compute required power parameters EbNo = 10**(EbNodB / 10) P = 2 * w * EbNo / n std = 1 dmsg = np.sqrt(n * P * n / (pM + n) / L) if numBins > 1 else np.sqrt(n * P / L) dbid = np.sqrt(n * P * pM / (pM + n)) if numBins > 1 else 0 assert np.abs(L * dmsg**2 + dbid**2 - n * P) <= 1e-3, "Total power constraint violated." # Compute empirical PUPE averaged over numSims trials errorRate = 0.0 matches = 0 for idxsim in range(numSims): print('Starting simulation %d of %d' % (idxsim + 1, numSims)) # Generate messages for all Ktot users usrmessages = np.random.randint(2, size=(Ktot, w)) # Split users into bins based on the first couple of bits in their messages w0 = int(np.ceil(np.log2(numBins))) self.__w0 = w0 binIds = np.matmul(usrmessages[:, 0:w0], 2** np.arange(w0)[::-1]) if w0 > 0 else np.zeros(K) K = np.array([np.sum(binIds == i) for i in range(numBins)]).astype(int) self.__K = K.copy() # Group usrmessages by bin messages = [ usrmessages[np.where(binIds == i)[0]] for i in range(numBins) ] # Transmit bin identifier across channel rxK = dbid * K + np.random.randn(numBins) * std # Estimate the number of users per bin Kht = self.estimateBinOccupancy(Ktot, dbid, rxK, std) self.__Kht = Kht.copy() # Outer-encode user signals txcodewords, sTrue = self.outerEncode(messages) # Inner-encode user signals x = self.innerEncode(dmsg, sTrue) # Transmit over channel y = x + std * np.random.randn(n, 1) # Inner-decoding s = self.innerDecode(y, dmsg) # Outer-decoding codewordestimates = self.outerDecode(s, Ktot) # Compute number of matches matches = FGG.numbermatches(txcodewords, codewordestimates) errorRate += (Ktot - matches) / (Ktot * numSims) print(str(matches) + ' matches') return errorRate
def simulate(EbNodB, case): simCount = 100 # number of simulations # EbN0 in linear scale EbNo = 10**(EbNodB / 10) P1 = 2 * B1 * EbNo / n P2 = 2 * B2 * EbNo / n σ_n = 1 #Generate the power allocation and set of tau coefficients # We assume equal power allocation for all the sections. Code has to be modified a little to accomodate non-uniform power allocations Phat1 = n * P1 / L1 Phat2 = n * P2 / L2 d1 = np.sqrt(n * P1 / L1) d2 = np.sqrt(n * P2 / L2) msgDetected1 = 0 msgDetected2 = 0 totalTime = 0 for simIndex in range(simCount): print('Simulation Number: ' + str(simIndex)) # Generate active users message sequences tx_message1 = np.random.randint(2, size=(K1, B1)) tx_message2 = np.random.randint(2, size=(K2, B2)) # Outer-encode the message sequences encoded_tx_message_indices = Tree_encode(tx_message1, K1, messageBlocks, G, L1, J) codewords = OuterCode.encodemessages(tx_message2) # Convert indices to sparse representation # sTrue: True state sTrue1 = convert_indices_to_sparse(encoded_tx_message_indices, L1, J, K1) sTrue2 = np.sum(codewords, axis=0).reshape(-1, 1) # Generate the binned SPARC codebook Ab1, Az1 = sparc_codebook(L1, M, n, P1) Ab2, Az2 = sparc_codebook(L2, M, n, P2) # Generate our transmitted signal X x = d1 * Ab1(sTrue1) + d2 * Ab2(sTrue2) # Generate random channel noise and thus also received signal y noise = np.random.randn(n, 1) * σ_n y = (x + noise).reshape(-1, 1) tic = time.time() z = y.copy() z1 = y.copy() z2 = y.copy() s1 = np.zeros((L1 * M, 1)) s2 = np.zeros((L2 * M, 1)) if case == 0: # Decode group 1 first for t in range(T): s1 = amp_state_update(z1, s1, P1, L1, M, Ab1, Az1, K1, G, messageBlocks, 1, numBPiter, OuterCode) z1 = amp_residual(y, z1, s1, s2, d1, 0, Ab1, Ab2) # Interference cancellation yUpdated = y - Ab1(s1) z2 = yUpdated.copy() # Decode group 2 for t in range(T): s2 = amp_state_update(z2, s2, P2, L2, M, Ab2, Az2, K2, G, messageBlocks, 2, numBPiter, OuterCode) z2 = amp_residual(yUpdated, z2, s1, s2, 0, d2, Ab1, Ab2) elif case == 1: for t in range(T): s1 = amp_state_update(z, s1, P1, L1, M, Ab1, Az1, K1, G, messageBlocks, 1, numBPiter, OuterCode) s2 = amp_state_update(z, s2, P2, L2, M, Ab2, Az2, K2, G, messageBlocks, 2, numBPiter, OuterCode) z = amp_residual(y, z, s1, s2, d1, d2, Ab1, Ab2) else: for t in range(T): s1 = amp_state_update(z1, s1, P1, L1, M, Ab1, Az1, K1, G, messageBlocks, 1, numBPiter, OuterCode) s2 = amp_state_update(z2, s2, P2, L2, M, Ab2, Az2, K2, G, messageBlocks, 2, numBPiter, OuterCode) z1 = amp_residual(y, z1, s1, s2, d1, 0, Ab1, Ab2) z2 = amp_residual(y, z2, s1, s2, 0, d2, Ab1, Ab2) # Convert decoded sparse vector into vector of indices cs_decoded_tx_message1 = convert_sparse_to_indices( s1, L1, J, listSize1) # Tree decoder to decode individual messages from lists output by AMP Paths1 = Tree_decoder(cs_decoded_tx_message1, G, L1, J, B1, listSize1) # Re-align paths to the correct order perm1 = np.argsort( np.array([0, 1, 2, 6, 7, 8, 12, 3, 4, 5, 9, 10, 11, 15, 13, 14])) Paths1 = Paths1[:, perm1] # If tree deocder outputs more than K valid paths, retain only K of them if Paths1.shape[0] > K1: Paths1 = pick_topKminusdelta_paths(Paths1, cs_decoded_tx_message1, s1, J, K1, 0) # Extract the message indices from valid paths in the tree Tree_decoded_indices1 = extract_msg_indices(Paths1, cs_decoded_tx_message1, L1, J) print('Graph Decode') # Decoding wiht Graph originallist = codewords.copy() recoveredcodewords = FGG.decoder(OuterCode, s2, 2 * K2) toc = time.time() totalTime = totalTime + toc - tic #print(totalTime) # Calculation of per-user prob err simMsgDetected1 = 0 simMsgDetected2 = 0 for i in range(K1): simMsgDetected1 = simMsgDetected1 + np.equal( encoded_tx_message_indices[i, :], Tree_decoded_indices1).all(axis=1).any() matches = FGG.numbermatches(originallist, recoveredcodewords) print('Group 1: ' + str(simMsgDetected1) + ' out of ' + str(K1)) print('Group 2: ' + str(matches) + ' out of ' + str(K2)) msgDetected1 = msgDetected1 + simMsgDetected1 msgDetected2 = msgDetected2 + matches errorRate1 = (K1 * simCount - msgDetected1) / (K1 * simCount) errorRate2 = (K2 * simCount - msgDetected2) / (K2 * simCount) avgTime = totalTime / simCount return errorRate1, errorRate2, avgTime
# # Coded Demxing # # This notebook implements coded Demixing using the CCS-AMP encoder/decoder for multi-class unsourced random access using Hadamard design matrices. # # In[ ]: import numpy as np import math #import matplotlib.pyplot as plt import time import sys import FactorGraphGeneration as FGG OuterCode = FGG.Graph62() # ## Fast Hadamard Transforms # # The ```PyFHT_local``` code can all be found in `pyfht`, which uses a C extension to speed up the fht function. # Only one import suffices, with the latter being much faster. # In[ ]: # import PyFHT_local from pyfht import block_sub_fht # # Outer Tree encoder # # This function encodes the payloads corresponding to users into codewords from the specified tree code. #