Source code for imtoolkit.IMUtil

# Copyright (c) IMToolkit Development Team
# This toolkit is released under the MIT License, see LICENSE.txt

import os
import glob
import itertools
import urllib.request
from numba import jit
import numpy as np
from scipy.special import binom


#
# Utility functions for active indices of IM
#
[docs]def convertIndsToVector(inds, M): Q = len(inds) ret = np.tile(np.zeros((M, 1), dtype=np.int), Q) # M \times Q for q in range(Q): for i in inds[q]: ret[i][q] = 1 return np.hsplit(ret, Q)
[docs]def convertIndsToMatrix(inds, M): Q = len(inds) K = len(inds[0]) ret = np.tile(np.zeros((M, K)), Q) t = 0 for q in range(Q): for i in inds[q]: ret[i][t] = 1 t += 1 return np.hsplit(ret, Q)
[docs]def convertIndsToIndsDec(inds, M): ret = [] for row in inds: dec = np.sum(np.exp2(row)) ret.append(dec) return ret
[docs]def convertIndsDecToInds(indsdecs, M): ret = [] for dec in indsdecs: binstr = np.binary_repr(dec) binstr = '0' * (M - len(binstr)) + binstr binstr = binstr[::-1] ind = [] for i in range(len(binstr)): if binstr[i] == '1': ind.append(i) ret.append(ind) return ret
[docs]def outputIndsToFile(inds, M): Q = len(inds) K = len(inds[0]) minh = getMinimumHammingDistance(inds, M) ineq = getInequalityL1(inds, M) basePath = os.path.dirname(os.path.abspath(__file__)) fname = basePath + "/inds/M=%d_K=%d_Q=%d_minh=%d_ineq=%d.txt" % (M, K, Q, minh, ineq) np.savetxt(fname, inds, fmt="%.0d") print("Saved to " + fname) return fname
[docs]@jit('i8(i8,i8)') def getMaxQ(M, K): return int(np.exp2(np.floor(np.log2(binom(M, K)))))
# Usage: convertIndsToMatrix(getDictionaryIndexesList(M, K, Q), M)
[docs]def getDictionaryIndexesList(M, K, Q): maxQ = getMaxQ(M, K) if Q > maxQ: print("The given Q is larger than the maximum: %d > %d" %(Q, maxQ)) Q = maxQ ret = [[0] * K] * Q q = 0 for pair in itertools.combinations(range(M), K): ret[q] = list(pair) q += 1 if q >= Q: break return ret
# This method is based on the Matlab implementation for the GSM scheme, # which is given by the following book. # R. Mesleh and A. Alhassi, Space modulation techniques. Wiley, 2018.
[docs]def getMeslehIndexesList(M, K, Q): maxQ = getMaxQ(M, K) if Q > maxQ: print("The given Q is larger than the maximum:" + str(Q) + " > " + str(maxQ)) Q = maxQ ret = [[0] * K] * Q q = 0 combs = list(itertools.combinations(range(M), K)) for pair in reversed(combs): ret[q] = list(pair) q += 1 if q >= Q: break return ret
# M. Wen, Y. Zhang, J. Li, E. Basar, and F. Chen, # ``Equiprobable subcarrier activation method for OFDM with index modulation,'' # IEEE Commun. Lett., vol. 20, no. 12, pp. 2386--2389, 2016.
[docs]def wen2016EquiprobableSubcarrierActivation(M, K): # initialize an indexes set ds ds = [np.ones(K, dtype=np.int)] ds[0][K - 1] = M - K + 1 if K >= 2: j = K - 2 b = 1 while True: while True: d = np.copy(ds[b - 1]) d[j] += 1 d[range(j + 1, K - 1)] = 1 d[K - 1] = 0 d[K - 1] = M - np.sum(d) if d[K - 1] < 1: j -= 1 else: break if d[0] > M / K: break ds.append(d) b += 1 # delete cyclic shifts in ds deletepos = [] for y in range(len(ds)): if y in deletepos: continue for x in range(y + 1, len(ds)): if x in deletepos: continue for i in range(K): if np.array_equal(ds[y], np.roll(ds[x], i)): deletepos.append(x) break ds = np.delete(ds, deletepos, axis=0) # print(deletepos) # print(ds) # build an indexes set from ds betas = [] for i in range(len(ds)): for m in range(M): beta = [0] * K beta[0] = m for k in range(1, K): beta[k] = (beta[k - 1] + 1 + ds[i][k - 1] - 1) % M beta = sorted(beta) if beta not in betas: betas.append(beta) # print(len(betas)) Qmax = int(M * np.floor(len(betas) / M)) # print("Q = " + str(Q)) return betas[0:Qmax]
[docs]def getRandomIndexesList(M, K, Q): ret = [] while len(ret) < Q: row = sorted(np.random.choice(M, K, replace=False)) if row not in ret: ret.append(row) return ret
[docs]def downloadOptimizedIndexesList(basePath, M, K, Q, source): print("Trying to obtain the active indices file from " + source + ".") txtfilename = "M=%d_K=%d_Q=%d.txt" % (M, K, Q) txtdstpath = basePath + "/inds/" + txtfilename if not os.path.exists(basePath + "/inds/"): os.mkdir(basePath + "/inds/") if source == "GitHub": txturl = "https://raw.githubusercontent.com/imtoolkit/imtoolkit/master/docs/build/html/db/M=" + str( M) + "/" + txtfilename else: txturl = "https://ishikawa.cc/imtoolkit/db/M=" + str(M) + "/" + txtfilename try: urllib.request.urlretrieve(txturl, txtdstpath) except: print("Perhaps...") print(" " + source + " is currently not available.") print(" You need the root permission.") print(" The specified IM parameters are invalid or not supported.") import traceback traceback.print_exc() return False return True
[docs]def getOptimizedIndexesList(M, K, Q, minh=0): basePath = os.path.dirname(os.path.abspath(__file__)) if minh == 0: files = glob.glob(basePath + "/inds/M=%d_K=%d_Q=%d.txt" % (M, K, Q)) files += glob.glob(basePath + "/inds/M=%d_K=%d_Q=%d_*.txt" % (M, K, Q)) else: files = glob.glob(basePath + "/inds/M=%d_K=%d_Q=%d_minh=%d*.txt" % (M, K, Q, minh)) # download the active indices from some webpages if len(files) == 0: if not downloadOptimizedIndexesList(basePath, M, K, Q, "GitHub"): downloadOptimizedIndexesList(basePath, M, K, Q, "ishikawa.cc") files = glob.glob(basePath + "/inds/M=%d_K=%d_Q=%d.txt" % (M, K, Q)) if len(files) == 0: print("No file found.") return [] files.sort() print("Read " + files[0]) inds = np.loadtxt(files[0], dtype=np.int) # print(inds) inds = inds.reshape(Q, K).tolist() return inds
[docs]def getIndexes(type, M, K, Q): if "opt" in type: minh = int(type.replace("opt", "0")) inds = getOptimizedIndexesList(M, K, Q, minh) elif type == "dic": inds = getDictionaryIndexesList(M, K, Q) elif type == "mes": inds = getMeslehIndexesList(M, K, Q) elif type == "wen": inds = wen2016EquiprobableSubcarrierActivation(M, K) if Q > len(inds): print("The specified Q = %d is not supported by wen2016 algorithm" % (Q)) return [] inds = inds[0:Q] # print("Q is automatically set to " + str(len(inds))) elif type == "rand": inds = getRandomIndexesList(M, K, Q) elif type == "ga": # Genetic algorithm aided design if M == 16 and K == 8 and Q == 16: inds = [ [0, 1, 2, 4, 7, 8, 11, 15], [0, 1, 2, 4, 10, 13, 14, 15], [0, 2, 3, 4, 6, 7, 12, 14], [0, 2, 4, 5, 6, 8, 11, 12], [0, 2, 5, 8, 10, 11, 13, 15], [0, 3, 4, 5, 6, 9, 11, 15], [0, 3, 4, 7, 10, 11, 13, 15], [0, 3, 7, 8, 9, 10, 13, 14], [1, 2, 3, 9, 10, 12, 13, 15], [1, 2, 6, 9, 10, 11, 12, 14], [1, 3, 5, 6, 7, 8, 11, 13], [1, 4, 5, 7, 8, 12, 13, 14], [1, 4, 5, 8, 9, 10, 14, 15], [1, 5, 6, 7, 9, 10, 12, 15], [2, 3, 5, 6, 8, 9, 12, 14], [3, 6, 7, 9, 11, 12, 13, 14]] else: print("Error: The type of active indices is not specified.") if len(inds) == 0: print("The specified indexes set is not available.") return [] return inds
# # Evaluation functions #
[docs]def getProbabilityOfActivation(inds, M): prob = np.zeros(M) for q in range(len(inds)): prob[inds[q]] += 1 return prob / len(inds)
[docs]def getHammingDistance(arr1, arr2): return np.sum(np.logical_xor(arr1, arr2))
[docs]def getMinimumHammingDistance(inds, M): Q = len(inds) indsm = convertIndsToVector(inds, M) mind = M + 1 for y in range(Q): for x in range(y + 1, Q): hammingdis = np.sum(np.logical_xor(indsm[y].reshape(M), indsm[x].reshape(M))) if hammingdis < mind: mind = hammingdis return mind
[docs]def getSumHamming(inds, M): Q = len(inds) indsm = convertIndsToVector(inds, M) ret = 0 for y in range(Q): for x in range(y + 1, Q): hammingdis = np.sum(np.logical_xor(indsm[y].reshape(M), indsm[x].reshape(M))) ret += hammingdis return ret
[docs]def getSumDistanceBetweenActivatedElements(inds, M): Q = len(inds) ret = 0 for q in range(Q): ret += np.sum(np.diff(inds[q]) - 1) return ret
# getInequalityL1(inds = [[0,1],[2,3]], M = 4) # = 0.0
[docs]def getInequalityL1(inds, M): Q = len(inds) K = len(inds[0]) hits = np.zeros(M) for q in range(len(inds)): hits[inds[q]] += 1 return np.sum(np.abs(hits - Q * K / M))
[docs]def checkConflict(inds): Q = len(inds) for y in range(Q): for x in range(y + 1, Q): if np.array_equal(inds[y], inds[x]): print("Conflicted " + "-" * 20) print("y = " + str(y) + ":" + str(inds[y])) print("x = " + str(x) + ":" + str(inds[x])) return True return False
# # Others #
[docs]def getIMParameters(M, K): ret = [] maxQ = getMaxQ(M, K) if maxQ == 1: return ret Q = 2 while Q <= maxQ: ret.append((M, K, Q)) Q *= 2 return ret