예제 #1
0
 def test_frodiff(self):
     fro = me.frodiff(np.array([1, 1j, 0, 0]), np.array([1, 0, 1j, 0]))
     self.assertAlmostEqual(fro,
                            2.0,
                            msg="The Frobenius norm calculation is wrong")
     fro = me.frodiff(me.randn_c(int(1e6)), me.randn_c(int(1e6)))
     self.assertAlmostEqual(fro / 2e6, 1.0, places=2)
예제 #2
0
def simulateBERReference(codes, channelfun, params, printValue=True):
    """
    Simulates BER values at multiple SNRs, where the straightforward reference algorithm is used. Note that this time complexity is unrealistically high. This simulation relies on the non-coherent maximum likelihood detector, where semi-unitary matrices are used for differential encoding and decdoing. The semi-unitary matrix is defined by $U U^H = \alpha I_M$ and $\alpha \in \mathbb{R}$. The environment variable USECUPY determines whether to use cupy or not.

    Args:
        codes (ndarray): an input codebook, which is generated on the CPU memory and is transferred into the GPU memory.
        channelfun (function): .
        params (dict): simulation parameters.
        printValue (bool): a flag that determines whether to print the simulated values.

    Returns:
        ret (dict): a dict that has two keys: snr_dB and ber, and contains the corresponding results. All the results are transferred into the CPU memory.
    """

    IT, M, N, Nc, B = params["IT"], params["M"], params["N"], codes.shape[
        0], log2(codes.shape[0])
    snr_dBs = linspace(params["from"], params["to"], params["len"])
    sigmav2s = 1.0 / inv_dB(snr_dBs)
    xor2ebits = getXORtoErrorBitsArray(Nc)

    bers = zeros(len(snr_dBs))
    for i in trange(len(snr_dBs)):
        errorBits = 0
        v0 = randn_c(N, M) * sqrt(sigmav2s[i])  # N \times M
        s0 = eye(M, dtype=complex)
        rs0 = s0  # the estimated codeword at the receiver
        currentBeta = linalg.norm(
            rs0)  # the estimated normalizing factor at the receiver
        for _ in range(IT):
            codei = random.randint(0, Nc)
            s1 = matmul(s0, codes[codei]) / linalg.norm(
                s0)  # semi-unitary differential encoding

            h = channelfun(N, M)  # N \times M
            v1 = randn_c(N, M) * sqrt(sigmav2s[i])  # N \times M

            y0 = matmul(h, s0) + v0  # N \times M
            y1 = matmul(h, s1) + v1  # N \times M

            # semi-unitary non-coherent detection
            p = square(
                abs(y1 -
                    matmul(y0, codes) / currentBeta))  # Nc \times N \times M
            norms = sum(p, axis=(1, 2))  # summation over the (N,M) axes
            mini = argmin(norms)
            errorBits += sum(xor2ebits[codei ^ mini])

            rs1 = matmul(rs0, codes[mini]) / currentBeta
            currentBeta = linalg.norm(rs1)
            v0 = v1
            s0 = s1
            rs0 = rs1

        bers[i] = errorBits / (IT * B)
        if printValue:
            print("At SNR = %1.2f dB, BER = %d / %d = %1.10e" %
                  (snr_dBs[i], errorBits, IT * B, bers[i]))

    return {"snr_dB": snr_dBs, "ber": bers}
예제 #3
0
def simulateBERReference(codes, channelfun, params, printValue=True):
    """
    Simulates BER values at multiple SNRs, where the straightforward reference algorithm is used. Note that this time complexity is unrealistically high. This simulation relies on the coherent maximum likelihood detector, that assumes perfect channel state information at the receiver. The environment variable USECUPY determines whether to use cupy or not.

    Args:
        codes (ndarray): an input codebook, which is generated on the CPU memory and is transferred into the GPU memory.
        channelfun (function): .
        params (dict): simulation parameters.
        printValue (bool): a flag that determines whether to print the simulated values.

    Returns:
        dict: a dict that has two keys: snr_dB and ber, and contains the corresponding results. All the results are transferred into the CPU memory.
    """
    IT, M, N, Nc, B = params["IT"], params["M"], params["N"], codes.shape[
        0], log2(codes.shape[0])
    snr_dBs = linspace(params["from"], params["to"], params["len"])
    sigmav2s = 1.0 / inv_dB(snr_dBs)
    xor2ebits = getXORtoErrorBitsArray(Nc)

    bers = zeros(params["len"])
    for i in trange(params["len"]):
        errorBits = 0
        v0 = randn_c(N, M) * sqrt(sigmav2s[i])  # N \times M
        s0 = eye(M, dtype=complex)
        for _ in range(IT):
            codei = random.randint(0, Nc)
            s1 = matmul(s0, codes[codei])  # differential encoding

            h = channelfun(N, M)  # N \times M
            v1 = randn_c(N, M) * sqrt(sigmav2s[i])  # N \times M

            y0 = matmul(h, s0) + v0  # N \times M
            y1 = matmul(h, s1) + v1  # N \times M

            # non-coherent detection that is free from the channel matrix h
            norms = normsYHCodes(y1, y0, codes)
            mini = argmin(norms)
            errorBits += xor2ebits[codei ^ mini].sum()

            v0 = v1
            s0 = s1

        bers[i] = errorBits / (IT * B)
        if printValue:
            print("At SNR = %1.2f dB, BER = %d / %d = %1.10e" %
                  (snr_dBs[i], errorBits, IT * B, bers[i]))

    return {"snr_dB": snr_dBs, "ber": bers}
예제 #4
0
 def test_randn_c(self):
     ret = me.randn_c(int(1e6))
     meanPower = np.mean(np.power(np.abs(ret), 2))
     self.assertAlmostEqual(
         meanPower,
         1.0,
         places=2,
         msg="The mean power of randn_c differs from 1.0")
예제 #5
0
def simulateBERParallel(codes, channelfun, params, printValue=True):
    """
    Simulates BER values at multiple SNRs, where the massively parallel algorithm is used. This implementation is especially designed for cupy.

    Args:
        codes (ndarray): an input codebook, which is generated on the CPU memory and is transferred into the GPU memory.
        channelfun (function): .
        params (dict): simulation parameters.
        printValue (bool): a flag that determines whether to print the simulated values.

    Returns:
        dict: a dict that has two keys: snr_dB and ber, and contains the corresponding results. All the results are transferred into the CPU memory.
    """

    M, N, T, ITo, ITi, Nc, B = params["M"], params["N"], params["T"], params[
        "ITo"], params["ITi"], codes.shape[0], log2(codes.shape[0])
    snr_dBs = linspace(params["from"], params["to"], params["len"])
    sigmav2s = 1.0 / inv_dB(snr_dBs)
    codei = tile(arange(Nc), ITi)
    xor2ebits = getXORtoErrorBitsArray(Nc)

    x = hstack(tile(codes, Nc))  # M \times T * Nc^2
    # x = [codes[0] codes[0] ... codes[0] codes[1] ...]
    y = tile(hstack(codes), Nc)  # M \times T * Nc^2
    # y = [codes[0] codes[1] ... codes[Nc-1] codes[0] ...]
    diffxy = x - y  # M \times T * Nc^2

    bers = zeros(len(snr_dBs))
    for ito in trange(ITo):
        bigh = channelfun(N, M, ITi)  # ITi * N \times M
        bigv = tile(randn_c(ITi * N, T), Nc * Nc)  # ITi * N \times T * Nc^2

        for i in range(len(snr_dBs)):
            ydiff = matmul(
                bigh,
                diffxy) + bigv * sqrt(sigmav2s[i])  # ITi * N \times T * Nc^2
            ydifffro = square(abs(ydiff)).reshape(
                ITi, N, Nc * Nc, T)  # ITi \times N \times Nc * Nc \times T
            ydifffrosum = sum(ydifffro, axis=(1, 3))  # ITi \times Nc * Nc

            norms = ydifffrosum.reshape(ITi, Nc, Nc)  # ITi \times Nc \times Nc
            mini = argmin(norms, axis=2).reshape(ITi * Nc)
            errorBits = sum(xor2ebits[codei ^ mini])

            bers[i] += errorBits
            if printValue:
                nbits = (ito + 1) * ITi * B * Nc
                print("At SNR = %1.2f dB, BER = %d / %d = %1.10e" %
                      (snr_dBs[i], bers[i], nbits, bers[i] / nbits))

    bers /= ITo * ITi * B * Nc
    return {"snr_dB": snr_dBs, "ber": bers}
def simulateAMIReference(codes, channelfun, params, printValue=True):
    """
    Simulates AMI values at multiple SNRs, where the straightforward reference algorithm is used. Note that this time complexity is unrealistically high.

    Args:
        codes (ndarray): an input codebook, which is generated on the CPU memory and is transferred into the GPU memory.
        channelfun (function): .
        params (dict): simulation parameters.
        outputFile (bool): a flag that determines whether to output the obtained results to the results/ directory.
        printValue (bool): a flag that determines whether to print the simulated values.

    Returns:
        dict: a dict that has two keys: snr_dB and ami, and contains the corresponding results. All the results are transferred into the CPU memory.
    """

    IT, M, N, T, Nc, B = params["IT"], params["M"], params["N"], params[
        "T"], codes.shape[0], log2(codes.shape[0])
    snr_dBs = linspace(params["from"], params["to"], params["len"])
    sigmav2s = 1.0 / inv_dB(snr_dBs)

    amis = zeros(len(snr_dBs))
    for i in trange(len(snr_dBs)):
        sum_outer = 0.0
        for _ in range(IT):
            V = sqrt(sigmav2s[i]) * randn_c(N, T)
            H = channelfun(N, M)  # N \times M
            for outer in range(Nc):
                sum_inner = 0.0
                for inner in range(Nc):
                    hxy = matmul(H, codes[outer] - codes[inner])
                    head = hxy + V
                    tail = V
                    coeff = (-square(linalg.norm(head)) +
                             square(linalg.norm(tail))) / sigmav2s[i]
                    sum_inner += exp(coeff)
                sum_outer += log2(sum_inner)
        amis[i] = (B - sum_outer / Nc / IT) / T
        if printValue:
            print("At SNR = %1.2f dB, AMI = %1.10f" % (snr_dBs[i], amis[i]))

    return {"snr_dB": snr_dBs, "ami": amis}
def simulateBERReference(codes,
                         bases,
                         alpha,
                         IT,
                         M,
                         N,
                         T,
                         W,
                         snr_dBs,
                         printValue=True):
    """
    Simulates BER values at multiple SNRs, where the straightforward reference algorithm is used. Note that this time complexity is unrealistically high. This simulation relies on the nonsquare differential space-time block codes, which are proposed in [1-3]. This implementation uses the square-to-nonsquare projection concept of [2] and the adaptive forgetting factor of [3] for time-varying channels. The environment variable USECUPY determines whether to use cupy or not.

    - [1] N. Ishikawa and S. Sugiura, "Rectangular differential spatial modulation for open-loop noncoherent massive-MIMO downlink," IEEE Trans. Wirel. Commun., vol. 16, no. 3, pp. 1908--1920, 2017.
    - [2] N. Ishikawa, R. Rajashekar, C. Xu, S. Sugiura, and L. Hanzo, "Differential space-time coding dispensing with channel-estimation approaches the performance of its coherent counterpart in the open-loop massive MIMO-OFDM downlink," IEEE Trans. Commun., vol. 66, no. 12, pp. 6190--6204, 2018.
    - [3] N. Ishikawa, R. Rajashekar, C. Xu, M. El-Hajjar, S. Sugiura, L. L. Yang, and L. Hanzo, "Differential-detection aided large-scale generalized spatial modulation is capable of operating in high-mobility millimeter-wave channels," IEEE J. Sel. Top. Signal Process., in press.

    Args:
        codes (ndarray): an input codebook, which is generated on the CPU memory and is transferred into the GPU memory.
        bases (ndarray): a set of bases that transform a square matrix into a nonsquare matrix.
        channelfun (function): .
        params (dict): simulation parameters.
        printValue (bool): a flag that determines whether to print the simulated values.

    Returns:
        ret (dict): a dict that has two keys: snr_dB and ber, and contains the corresponding results. All the results are transferred into the CPU memory.
    """
    Nc, B = codes.shape[0], log2(codes.shape[0])
    sigmav2s = 1.0 / inv_dB(snr_dBs)
    xor2ebits = getErrorBitsTable(Nc)
    E1 = bases[0]  # M \times T
    E1H = E1.T.conj()
    Xrs = zeros((Nc, M, T)) + 0.j
    for x in range(Nc):
        Xrs[x] = codes[x] @ E1
    #Xrs = matmulb(codes, E1)  # Nc \times M \times Tc

    bers = zeros(len(snr_dBs))
    for i in trange(len(snr_dBs)):
        errorBits = 0

        for it in range(IT):
            S0 = eye(M) + 0.j
            Yhat0 = Yhat1 = zeros((N, M)) + 0.j

            H = randn_c(N, M)  # N \times M

            for wi in range(1, int(W / T) + 1):
                if wi <= M / T:
                    S1 = eye(M) + 0.j
                    Sr1 = bases[wi - 1]
                    X1 = S1
                    Y1 = H @ Sr1 + randn_c(N, T) * sqrt(
                        sigmav2s[i])  # N \times T
                    Yhat1 += Y1 @ bases[wi - 1].T.conj()
                else:
                    codei = random.randint(0, Nc)
                    X1 = codes[codei]
                    S1 = S0 @ X1
                    Sr1 = S1 @ E1
                    Y1 = H @ Sr1 + randn_c(N, T) * sqrt(
                        sigmav2s[i])  # N \times T

                    # estimate
                    norms = normsYHCodes(Y1, Yhat0, Xrs)
                    mini = argmin(norms)
                    Xhat1 = codes[mini]

                    Yhd = Yhat0 @ Xhat1
                    D1 = Y1 - Yhd @ E1
                    Yhat1 = (1.0 - alpha) * D1 @ E1H + Yhd

                    # # adaptive forgetting factor for time-varying channels
                    # Yhd = Yhat0 @ Xhat1
                    # D1 = Y1 - Yhd @ E1
                    # n1 = square(linalg.norm(D1))
                    # estimatedAlpha = N * T * sigmav2s[i] / n1
                    # estimatedAlpha = min(max(estimatedAlpha, 0.01), 0.99)
                    # Yhat1 = (1.0 - estimatedAlpha) * D1 @ E1H + Yhd

                    errorBits += xor2ebits[codei][mini]

                S0 = S1
                Yhat0 = Yhat1

        bers[i] = errorBits / (IT * B * (W - M)) * T

        if printValue:
            print("At SNR = %1.2f dB, BER = %d / %d = %1.20e" %
                  (snr_dBs[i], errorBits, (IT * B * (W - M)) / T, bers[i]))

    return {"snr_dB": snr_dBs, "ber": bers}