示例#1
0
def test_decoding_results():

    viterbi_net_performance = []
    threshold_performance = []
    classic_performance = []
    SNRs = np.linspace(.1, 10, 10)
    seed_generator = 0
    data_gen = None
    for SNR in SNRs:
        """
        Generated Testing Data using the same channel as was used for training the mixture model and the nn
        """
        number_symbols = 500
        channel = np.zeros((1, 4))
        channel[0, [0, 1, 2, 3]] = 1, 0.1, 0.1, 0.2
        data_gen = training_data_generator(
            symbol_stream_shape=(1, number_symbols),
            SNR=SNR,
            plot=True,
            channel=channel)
        data_gen.random_symbol_stream()
        data_gen.send_through_channel()
        """
        Load in Trained Neural Network and verify that it is acceptable performance
        """
        device = torch.device("cpu")
        num_inputs_for_nn = 1
        x, y = data_gen.get_labeled_data(inputs=num_inputs_for_nn)
        y = np.argmax(
            y, axis=1
        )  # Fix for how the pytorch Cross Entropy expects class labels to be shown
        x = torch.Tensor(x)
        y = torch.Tensor(y)
        train_size = int(.6 * number_symbols)
        x_train = x[0:train_size, :]
        x_test = x[train_size::, :]
        y_train = y[0:train_size]
        y_test = y[train_size::]
        """
        Setup NN and optimizer
        """
        m = data_gen.alphabet.size
        channel_length = data_gen.CIR_matrix.shape[1]
        N, D_in, H1, H2, D_out = number_symbols, 1, 100, 50, np.power(
            m, channel_length)
        net = models.ViterbiNet(D_in, H1, H2, D_out)
        optimizer = optim.Adam(net.parameters(), lr=1e-2)
        """
        Train NN
        """
        # criterion = nn.CrossEntropyLoss()
        criterion = nn.NLLLoss()
        train_cost_over_epoch = []
        test_cost_over_epoch = []

        # If training is perfect, then NN should be able to perfectly predict the class to which a test set belongs and thus the loss (KL Divergence) should be zero
        for t in range(500):
            output = net(x_train)
            loss = criterion(output, y_train.long())
            train_cost_over_epoch.append(loss)
            net.zero_grad()
            print(loss)
            loss.backward()
            optimizer.step()
            test_cost_over_epoch.append(criterion(net(x_test), y_test.long()))

        # Test NN
        x, y = data_gen.get_labeled_data(inputs=num_inputs_for_nn)
        y = np.argmax(
            y, axis=1
        )  # Fix for how the pytorch Cross Entropy expects class labels to be shown
        x = torch.Tensor(x)
        y = torch.Tensor(y)
        # criterion = nn.NLLLoss()
        criterion = nn.CrossEntropyLoss()
        cost = criterion(net(x), y.long())
        threshold = 1e-2
        # assert cost < threshold
        """
        Train Mixture Model
        """
        mixture_model_training_data = data_gen.channel_output.flatten()

        num_sources = pow(data_gen.alphabet.size, data_gen.CIR_matrix.shape[1])
        mm = em_gausian(num_sources,
                        mixture_model_training_data,
                        20,
                        save=True,
                        model=True)
        mm = mm.get_probability
        # mm = 1
        """
        After sending through channel, symbol detection should be performed using something like a matched filter.
        Create new set of test data. 
        """

        data_gen = training_data_generator(
            symbol_stream_shape=(1, number_symbols),
            SNR=SNR,
            plot=True,
            channel=channel)
        data_gen.random_symbol_stream()
        data_gen.send_through_channel()

        #   !! Make sure channel output gets flipped here!!
        metric = NeuralNetworkMixtureModelMetric(
            net, mm, data_gen.channel_output, input_length=num_inputs_for_nn
        )  # This is a function to be used in the viterbi
        detected_nn = viterbi_setup_with_nodes(data_gen.alphabet,
                                               data_gen.channel_output,
                                               data_gen.CIR_matrix.shape[1],
                                               metric.metric)
        ser_nn = symbol_error_rate_channel_compensated(
            detected_nn, data_gen.symbol_stream_matrix, channel_length)
        """
        Compare to Classical Viterbi with full CSI
        """

        metric = GaussianChannelMetric(
            channel, data_gen.channel_output
        )  # This is a function to be used in the viterbi
        detected_classic = viterbi_setup_with_nodes(
            data_gen.alphabet, data_gen.channel_output,
            data_gen.CIR_matrix.shape[1], metric.metric)
        ser_classic = symbol_error_rate_channel_compensated(
            detected_classic, data_gen.symbol_stream_matrix, channel_length)
        """
        Analyze SER performance
        """
        viterbi_net_performance.append(ser_nn)
        classic_performance.append(ser_classic)

    path = "Output/SER.pickle"
    pickle_out = open(path, "wb")
    pickle.dump([classic_performance, viterbi_net_performance], pickle_out)
    pickle_out.close()

    plt.figure(1)
    plt.plot(SNRs, viterbi_net_performance, label='viterbi net')
    plt.plot(SNRs, classic_performance, label='standard viterbi')
    plt.title(str(data_gen.get_info_for_plot()), fontdict={'fontsize': 10})
    plt.xlabel("SNR")
    plt.ylabel("SER")
    plt.legend(loc='upper left')
    path = "Output/SER_curves.png"
    plt.savefig(path, format="png")
    time_path = "Output/SER_" + net.name + str(num_inputs_for_nn) + str(
        number_symbols) + " symbols " + str(time.time()) + "curves.png"
    plt.savefig(time_path, format="png")
    assert True
def test_full_integration():

    viterbi_net_performance = []
    linear_mmse_performance = []
    classic_performance = []
    SNRs_dB = np.linspace(20, 20, 2)
    # SNRs_dB = np.linspace(6, 10,3)
    SNRs = np.power(10, SNRs_dB/10)
    seed_generator = 0
    data_gen = None
    channel = None
    number_symbols = 5000
    channel = np.zeros((1, 5))
    # channel[0, [0, 1, 2, 3, 4]] = 0.227, 0.460, 0.688, 0.460, 0.227
    channel[0, [0, 1, 2, 3, 4]] = 0.9, 0.7, 0.3, 0.5, 0.1
    # Method used in ViterbiNet Paper
    # channel[0, :] = np.random.randn(channel.size)
    # channel = np.zeros((1, 5))
    # channel[0, [0, 1, 2, 3, 4]] = 1, 0, .2, .2, .4
    # channel[0, [0, 1, 2, 3]] = .8, 0, .02, .4

    # channel[0, [0, 1, 2, 3, 4]] = 1, .7, .3, .1, .4
    # channel[0, [0, 1, 2, 3, 4]] = 1, .4, .7, .1, .3
    # channel = np.zeros((1, 3))
    # channel[0, [0]] = 1
    for SNR in SNRs:
        """
        Generated Testing Data using the same channel as was used for training the mixture model and the nn
        """

        data_gen = CommunicationDataGenerator(symbol_stream_shape=(1, number_symbols), SNR=SNR, plot=True, channel=channel)
        data_gen.random_symbol_stream()
        data_gen.send_through_channel()

        # plt.scatter(data_gen.channel_output.flatten(),data_gen.channel_output.flatten())
        # plt.show()
        """
        Load in Trained Neural Network and verify that it is acceptable performance
        """
        device = torch.device("cpu")
        x, y = data_gen.get_labeled_data()
        y = np.argmax(y, axis=1)  # Fix for how the pytorch Cross Entropy expects class labels to be shown
        x = torch.Tensor(x)
        y = torch.Tensor(y)
        train_size = int(.6 * number_symbols)
        x_train = x[0:train_size, :]
        x_test = x[train_size::, :]
        y_train = y[0:train_size]
        y_test = y[train_size::]

        """
        Setup NN and optimizer
        """
        m = data_gen.alphabet.size
        channel_length = data_gen.CIR_matrix.shape[1]
        # channel_length = channel_length-2
        output_layer_size = np.power(m, channel_length)
        num_states = output_layer_size
        N, D_in, H1, H2, D_out = number_symbols, 1, 100, 50, output_layer_size
        net = models.ViterbiNet(D_in, H1, H2, D_out)
        optimizer = optim.Adam(net.parameters(), lr=1e-2)

        """
        Train NN
        """
        # This loss function looks at only the true class and takes the NLL of that.
        criterion = nn.NLLLoss()
        train_cost_over_epoch = []
        test_cost_over_epoch = []
        batch_size = 1000

        # If training is perfect, then NN should be able to perfectly predict the class to which a test set belongs and thus the loss (KL Divergence) should be zero
        epochs = 900
        for t in range(epochs):
            batch_indices = np.random.randint(len(y_train), size=(1, batch_size))
            x_batch = x_train[(batch_indices)]
            y_batch = y_train[(batch_indices)]
            # Add "dropout to prevent overfitting data"
            net.zero_grad()
            output = net(x_batch)
            loss = criterion(output, y_batch.long())
            loss.backward()
            optimizer.step()

            test_batch_indices = np.random.randint(len(y_test), size=(1, batch_size))
            x_batch_test = x_test[(test_batch_indices)]
            y_batch_test = y_test[(test_batch_indices)]
            # Setup Accuracy test
            # detached_ouput = output.
            max_state_train = np.argmax(output.detach().numpy(), axis=1)
            max_state_test = np.argmax(net(x_batch_test).detach().numpy(), axis=1)
            train_cost_over_epoch.append(np.sum(np.not_equal(max_state_train, y_batch.detach().numpy()))/y_batch.size())
            test_cost_over_epoch.append(np.sum(np.not_equal(max_state_test, y_batch_test.detach().numpy()))/y_batch_test.size())
            # test_cost_over_epoch.append(criterion(net(x_batch_test), y_batch_test.long()))


        """
        Train Mixture Model
        """
        mm_train_size = 1000
        mixture_model_training_data = data_gen.channel_output.flatten()[0:mm_train_size]
        num_sources = num_states
        mm = em_gausian(num_sources, mixture_model_training_data, 10, save=True, model=True)
        mm = mm.get_probability

        """
        User training data to train MMSE equalizer to then use on the test data
        """
        mmse_equalizer = LinearMMSE()
        mmse_equalizer.train_equalizer(data_gen.symbol_stream_matrix, data_gen.channel_output, data_gen.symbol_stream_matrix, channel.size)

        """
        Create new set of test data. 
        """
        ser_nn = []
        ser_classic = []
        ser_lmmse = []

        for reps in range(5):
            del data_gen
            data_gen = CommunicationDataGenerator(symbol_stream_shape=(1, 2000), SNR=SNR, plot=True, channel=channel)
            data_gen.random_symbol_stream()
            data_gen.send_through_channel()

            """
            Evaluate Neural Net Performance
            """
            # metric = NeuralNetworkMixtureModelMetric(net, mm, data_gen.channel_output)
            metric = NeuralNetworkMixtureModelMetric(net, mm, np.flip(data_gen.channel_output))
            detected_nn = viterbi_setup_with_nodes(data_gen.alphabet, data_gen.channel_output, data_gen.CIR_matrix.shape[1],
                                                metric.metric)
            ser_nn = symbol_error_rate_channel_compensated_NN(detected_nn, data_gen.symbol_stream_matrix, channel_length)


            """
            Compare to Classical Viterbi with full CSI
            """
            metric = GaussianChannelMetric(channel, np.flip(data_gen.channel_output))  # This is a function to be used in the viterbi
            detected_classic = viterbi_setup_with_nodes(data_gen.alphabet, data_gen.channel_output, data_gen.CIR_matrix.shape[1],
                                                metric.metric)
            ser_classic = symbol_error_rate(detected_classic, data_gen.symbol_stream_matrix, channel_length)

            """
            Evaluate performance with linear MMSE
            """
            ser_lmmse.append(mmse_equalizer.test_equalizer(data_gen.symbol_stream_matrix, data_gen.channel_output))

        ser_nn = np.average(ser_nn)
        ser_classic = np.average(ser_classic)
        ser_lmmse = np.average(ser_lmmse)

        """
        Analyze SER performance
        """
        linear_mmse_performance.append(ser_lmmse)
        viterbi_net_performance.append(ser_nn)
        classic_performance.append(ser_classic)


    path = "Output/SER.pickle"
    pickle_out = open(path, "wb")
    pickle.dump([classic_performance, linear_mmse_performance, viterbi_net_performance], pickle_out)
    pickle_out.close()

    figure, dictionary = plot_symbol_error_rates(SNRs_dB, [classic_performance, linear_mmse_performance, viterbi_net_performance], data_gen.get_info_for_plot())
    time_path = "Output/SER_"+f"{time.time()}"+"curves.png"
    figure.savefig(time_path, format="png")
    text_path = "Output/SER_"+f"{time.time()}"+"curves.csv"
    pd.DataFrame.from_dict(dictionary).to_csv(text_path)
    figure.savefig(time_path, format="png")


    #Plots for NN training information
    plt.figure(2)
    plt.plot(test_cost_over_epoch, label='Test Error')
    plt.plot(train_cost_over_epoch, label='Train Error')
    plt.title(str(data_gen.get_info_for_plot()), fontdict={'fontsize': 10})
    plt.xlabel("Epoch")
    plt.ylabel("Error")
    plt.legend(loc='upper right')
    path = f"Output/Neural_Network{time.time()}_Convergence.png"
    plt.savefig(path, format="png")
示例#3
0
def test_nn_channel_reduced():

    viterbi_net_performance = []
    linear_mmse_performance = []
    classic_performance = []
    SNRs_dB = np.linspace(15, 15, 1)
    # SNRs_dB = np.linspace(6, 10,3)
    SNRs = np.power(10, SNRs_dB / 10)
    seed_generator = 0
    data_gen = None
    channel = None

    number_symbols = 5000
    channel = np.zeros((1, 5))
    # channel[0, [0, 1, 2, 3, 4]] = 0.227, 0.460, 0.688, 0.460, 0.227
    # Method used in ViterbiNet Paper
    channel[0, :] = np.random.randn(channel.size)
    # channel = np.zeros((1, 5))
    # channel[0, [0, 1, 2, 3, 4]] = 1, 0, .2, .2, .4
    # channel[0, [0, 1, 2, 3]] = .8, 0, .02, .4

    # channel[0, [0, 1, 2, 3, 4]] = 1, .7, .3, .1, .4
    # channel[0, [0, 1, 2, 3, 4]] = 1, .4, .7, .1, .3
    # channel = np.zeros((1, 3))
    # channel[0, [0]] = 1
    """
    Generated Testing Data using the same channel as was used for training the mixture model and the nn
    """

    data_gen = training_data_generator(symbol_stream_shape=(1, number_symbols),
                                       SNR=SNRs,
                                       plot=True,
                                       channel=channel)
    data_gen.random_symbol_stream()
    data_gen.send_through_channel()

    # plt.scatter(data_gen.channel_output.flatten(),data_gen.channel_output.flatten())
    # plt.show()
    """
    Load in Trained Neural Network and verify that it is acceptable performance
    """
    device = torch.device("cpu")
    reduced_state = 32
    x, y = data_gen.get_labeled_data_reduced_state(reduced_state)
    # x, y = data_gen.get_labeled_data()
    y = np.argmax(
        y, axis=1
    )  # Fix for how the pytorch Cross Entropy expects class labels to be shown
    x = torch.Tensor(x)
    y = torch.Tensor(y)
    train_size = int(.6 * number_symbols)
    x_train = x[0:train_size, :]
    x_test = x[train_size::, :]
    y_train = y[0:train_size]
    y_test = y[train_size::]
    """
    Setup NN and optimizer
    """
    m = data_gen.alphabet.size
    channel_length = data_gen.CIR_matrix.shape[1]
    output_layer_size = np.power(m, channel_length)
    output_layer_size = reduced_state
    N, D_in, H1, H2, D_out = number_symbols, 1, 100, 50, output_layer_size
    # N, D_in, H1, H2, H3, D_out = number_symbols, 1, 20, 20, 20, output_layer_size

    net = models.ViterbiNet(D_in, H1, H2, D_out)
    # net = models.viterbiNet(D_in, H1, H2, D_out, dropout_probability)
    # net = models.deeper_viterbiNet(D_in, H1, H2, H3, D_out, dropout_probability)

    # N, D_in, H1, H2, H3, D_out = number_symbols, num_inputs_for_nn, 20, 10, 10, np.power(m, channel_length)
    # net = models.deeper_viterbiNet(D_in, H1, H2, H3, D_out)
    optimizer = optim.Adam(net.parameters(), lr=1e-2)
    # optimizer = optim.SGD(net.parameters(), lr=1e-1)
    """
    Train NN
    """
    # This loss function looks at only the true class and takes the NLL of that.
    criterion = nn.NLLLoss()
    train_cost_over_epoch = []
    test_cost_over_epoch = []
    batch_size = 1000

    # If training is perfect, then NN should be able to perfectly predict the class to which a test set belongs and thus the loss (KL Divergence) should be zero
    epochs = 900
    for t in range(epochs):
        batch_indices = np.random.randint(len(y_train), size=(1, batch_size))
        x_batch = x_train[(batch_indices)]
        y_batch = y_train[(batch_indices)]
        # Add "dropout to prevent overfitting data"
        net.zero_grad()
        output = net(x_batch)
        loss = criterion(output, y_batch.long())
        loss.backward()
        optimizer.step()

        test_batch_indices = np.random.randint(len(y_test),
                                               size=(1, batch_size))
        x_batch_test = x_test[(test_batch_indices)]
        y_batch_test = y_test[(test_batch_indices)]
        # Setup Accuracy test
        # detached_ouput = output.
        max_state_train = np.argmax(output.detach().numpy(), axis=1)
        check = np.not_equal(max_state_train, y_batch.detach().numpy())
        max_state_test = np.argmax(net(x_batch_test).detach().numpy(), axis=1)
        train_cost_over_epoch.append(
            np.sum(np.not_equal(max_state_train,
                                y_batch.detach().numpy())) / y_batch.size())
        test_cost_over_epoch.append(
            np.sum(np.not_equal(max_state_test,
                                y_batch_test.detach().numpy())) /
            y_batch_test.size())
        # test_cost_over_epoch.append(criterion(net(x_batch_test), y_batch_test.long()))

    #Plots for NN training information
    plt.figure(2)
    plt.plot(test_cost_over_epoch, label='Test Error')
    plt.plot(train_cost_over_epoch, label='Train Error')
    plt.title(str(data_gen.get_info_for_plot()), fontdict={'fontsize': 10})
    plt.xlabel("Epoch")
    plt.ylabel("Error")
    plt.legend(loc='upper right')
    path = f"Output/Neural_Network{time.time()}_Convergence.png"
    plt.savefig(path, format="png")
示例#4
0
def test_auto_encoder():
    """
    Generated Testing Data using the same channel as was used for training the mixture model and the nn
    """
    number_symbols = 5000
    sources = 32
    x = np.random.randint(sources, size=(number_symbols, 1))
    y = x.flatten()
    x = x + np.random.standard_normal((number_symbols, 1)) * 1
    # plt.scatter(x, x)
    # plt.show()
    x = torch.Tensor(x)
    y = torch.Tensor(y)
    train_size = int(.6 * number_symbols)
    x_train = x[0:train_size]
    x_test = x[train_size::]
    y_train = y[0:train_size]
    y_test = y[train_size::]
    """
    Setup NN and optimizer
    """

    N, D_in, H1, H2, D_out = number_symbols, 1, 100, 50, sources
    net = models.ViterbiNet(D_in, H1, H2, D_out)
    # N, D_in, H1, H2, H3, D_out = number_symbols, 1, 50, 50, 50, sources
    # net = models.deeper_viterbiNet(D_in, H1, H2, H3, D_out, dropout_probability)
    optimizer = optim.Adam(net.parameters(), lr=1e-2)
    # optimizer = optim.SGD(net.parameters(), lr=1e-1)
    """
    Train NN
    """
    criterion = nn.NLLLoss()
    # criterion = nn.CrossEntropyLoss()
    train_cost_over_epoch = []
    test_cost_over_epoch = []
    accuracy = []
    batch_size = 1000

    # If training is perfect, then NN should be able to perfectly predict the class to which a test set belongs and thus the loss (KL Divergence) should be zero
    for t in range(900):
        batch_indices = np.random.randint(len(y_train), size=(1, batch_size))
        x_batch = x_train[(batch_indices)]
        y_batch = y_train[(batch_indices)]
        output = net(x_batch)
        loss = criterion(output, y_batch.long())
        train_cost_over_epoch.append(loss)
        net.zero_grad()
        # print(loss)
        loss.backward()
        optimizer.step()
        test_batch_indices = np.random.randint(len(y_test),
                                               size=(1, batch_size))
        x_batch_test = x_test[(test_batch_indices)]
        y_batch_test = y_test[(test_batch_indices)]
        # check = np.argmax(net(x_batch_test))
        # accuracy.append(np.sum(np.equal(net(x_batch_test), y_batch_test)))
        test_cost_over_epoch.append(
            criterion(net(x_batch_test), y_batch_test.long()))

    x = np.random.randint(sources, size=(100000, 1))
    y = x.flatten()
    x = torch.Tensor(x)
    check = net(x).detach().numpy()
    output = np.argmax(net(x).detach().numpy(), axis=1)
    test = np.not_equal(y, output)
    error = np.sum(np.not_equal(y, output))
    print(f"number of errors: {error/100000}")

    #Plots for NN training information
    # plt.figure(1)
    # plt.plot(accuracy, label='Test Error')
    plt.figure(2)
    plt.plot(test_cost_over_epoch, label='Test Error')
    plt.plot(train_cost_over_epoch, label='Train Error')
    plt.xlabel("Epoch")
    plt.ylabel("Error")
    plt.legend(loc='upper left')
    plt.show()
    assert True
示例#5
0
def test_reduced_full_final():

    viterbi_net_performance = []
    viterbi_net_reduced_performance = []
    linear_mmse_performance = []
    classic_performance = []
    SNRs_dB = np.linspace(0, 15, 15)
    SNRs = np.power(10, SNRs_dB / 10)
    seed_generator = 0
    data_gen = None
    channel = None
    quantization_level = None
    quantization_level = 0
    noise_levels = None
    # noise_levels = 2

    number_symbols = 5000
    channel = np.zeros((1, 5))
    # channel[0, [0, 1, 2, 3, 4]] = 0.227, 0.460, 0.688, 0.460, 0.227
    # channel[0, [0, 1, 2, 3, 4]] = 0.9, 0.7, 0.3, 0.5, 0.1
    # Channel to use for redundancy testing
    # Method used in ViterbiNet Paper
    # channel[0, :] = np.random.randn(channel.size)
    # channel = np.zeros((1, 5))
    channel[0, [0, 1, 2, 3, 4]] = .9, 0, .0, .4, .7
    # channel = np.zeros((1, 3))
    # channel[0, [0, 1, 2]] = .9, .8, .7
    # channel = np.zeros((1, 3))
    # channel[0, [0]] = 1

    for SNR in SNRs:
        """
        Generated Testing Data using the same channel as was used for training the mixture model and the nn
        """

        data_gen = CommunicationDataGenerator(
            symbol_stream_shape=(1, number_symbols),
            SNR=SNR,
            plot=True,
            channel=channel)
        data_gen.random_symbol_stream()
        data_gen.send_through_channel(quantization_level,
                                      noise_levels=noise_levels)

        # plt.scatter(data_gen.channel_output.flatten(),data_gen.channel_output.flatten())
        # plt.show()
        """
        Setup Reduced ViterbiNet training data. 
        """
        reduced_state = 8
        x_reduced, y_reduced, states_reduced, states_original, totals = data_gen.get_labeled_data_reduced_state(
            reduced_state)
        y_reduced = np.argmax(
            y_reduced, axis=1
        )  # Fix for how the pytorch Cross Entropy expects class labels to be shown
        x_reduced = torch.Tensor(x_reduced)
        y_reduced = torch.Tensor(y_reduced)
        train_size = int(.6 * number_symbols)
        x_train_reduced = x_reduced[0:train_size, :]
        x_test_reduced = x_reduced[train_size::, :]
        y_train_reduced = y_reduced[0:train_size]
        y_test_reduced = y_reduced[train_size::]
        """
        Setup Standard ViterbiNet training data. 
        """
        x, y = data_gen.get_labeled_data()
        y = np.argmax(
            y, axis=1
        )  # Fix for how the pytorch Cross Entropy expects class labels to be shown
        x = torch.Tensor(x)
        y = torch.Tensor(y)
        train_size = int(.6 * number_symbols)
        x_train = x[0:train_size, :]
        x_test = x[train_size::, :]
        y_train = y[0:train_size]
        y_test = y[train_size::]
        """
        Setup reduced NN and optimizer
        """
        m = data_gen.alphabet.size
        channel_length = data_gen.CIR_matrix.shape[1]
        output_layer_size = reduced_state
        N, D_in, H1, H2, D_out = number_symbols, 1, 100, 50, output_layer_size
        net_reduced = models.ViterbiNet(D_in, H1, H2, D_out)
        optimizer_reduced = optim.Adam(net_reduced.parameters(), lr=1e-2)
        criterion_reduced = nn.NLLLoss()
        batch_size = 1000
        train_cost_over_epoch_reduced = []
        test_cost_over_epoch_reduced = []

        # If training is perfect, then NN should be able to perfectly predict the class to which a test set belongs and thus the loss (KL Divergence) should be zero
        epochs = 50
        for t in range(epochs):
            batch_indices = np.random.randint(len(y_train),
                                              size=(1, batch_size))
            x_batch_reduced = x_train_reduced[(batch_indices)]
            y_batch_reduced = y_train_reduced[(batch_indices)]
            # Add "dropout to prevent overfitting data"
            net_reduced.zero_grad()

            output_reduced = net_reduced(x_batch_reduced)
            loss_reduced = criterion_reduced(output_reduced,
                                             y_batch_reduced.long())
            loss_reduced.backward()
            optimizer_reduced.step()

            test_batch_indices = np.random.randint(len(y_test),
                                                   size=(1, batch_size))
            x_batch_test_reduced = x_test_reduced[(test_batch_indices)]
            y_batch_test_reduced = y_test_reduced[(test_batch_indices)]
            # Setup Accuracy test
            max_state_train_reduced = np.argmax(
                output_reduced.detach().numpy(), axis=1)
            max_state_test_reduced = np.argmax(
                net_reduced(x_batch_test_reduced).detach().numpy(), axis=1)
            train_cost_over_epoch_reduced.append(
                np.sum(
                    np.not_equal(max_state_train_reduced,
                                 y_batch_reduced.detach().numpy())) /
                y_batch_reduced.size())
            test_cost_over_epoch_reduced.append(
                np.sum(
                    np.not_equal(max_state_test_reduced,
                                 y_batch_test_reduced.detach().numpy())) /
                y_batch_test_reduced.size())
        """
        Setup standard NN and optimizer
        """
        m = data_gen.alphabet.size
        channel_length = data_gen.CIR_matrix.shape[1]
        # channel_length = channel_length-2
        output_layer_size = np.power(m, channel_length)
        num_states = output_layer_size
        N, D_in, H1, H2, D_out = number_symbols, 1, 100, 50, output_layer_size
        net = models.ViterbiNet(D_in, H1, H2, D_out)
        optimizer = optim.Adam(net.parameters(), lr=1e-2)
        """
        Train NN
        """
        # This loss function looks at only the true class and takes the NLL of that.
        criterion = nn.NLLLoss()
        train_cost_over_epoch = []
        test_cost_over_epoch = []

        epochs = 500
        for t in range(epochs):
            batch_indices = np.random.randint(len(y_train),
                                              size=(1, batch_size))
            x_batch = x_train[(batch_indices)]
            y_batch = y_train[(batch_indices)]

            # Add "dropout to prevent overfitting data"
            net.zero_grad()
            net_reduced.zero_grad()
            output = net(x_batch)

            loss = criterion(output, y_batch.long())
            loss.backward()
            optimizer.step()

            test_batch_indices = np.random.randint(len(y_test),
                                                   size=(1, batch_size))
            x_batch_test = x_test[(test_batch_indices)]
            y_batch_test = y_test[(test_batch_indices)]

            # Setup Accuracy test
            max_state_train = np.argmax(output.detach().numpy(), axis=1)
            max_state_test = np.argmax(net(x_batch_test).detach().numpy(),
                                       axis=1)
            train_cost_over_epoch.append(
                np.sum(np.not_equal(max_state_train,
                                    y_batch.detach().numpy())) /
                y_batch.size())
            test_cost_over_epoch.append(
                np.sum(
                    np.not_equal(max_state_test,
                                 y_batch_test.detach().numpy())) /
                y_batch_test.size())
        """
        Train Mixture Model
        """
        mixture_model_training_data = data_gen.channel_output.flatten(
        )[0:train_size]
        num_sources = reduced_state
        mm = em_gausian(num_sources,
                        mixture_model_training_data,
                        10,
                        save=True,
                        model=True)
        mm = mm.get_probability
        """
        Train Reduced Mixture Model
        """
        mixture_model_training_data_reduced = data_gen.channel_output.flatten(
        )[0:train_size]
        num_sources = reduced_state
        mm_reduced = em_gausian(num_sources,
                                mixture_model_training_data_reduced,
                                10,
                                save=True,
                                model=True)
        mm_reduced = mm_reduced.get_probability
        """
        User training data to train MMSE equalizer to then use on the test data
        """
        mmse_equalizer = LinearMMSE()
        mmse_equalizer.train_equalizer(data_gen.symbol_stream_matrix,
                                       data_gen.channel_output,
                                       data_gen.symbol_stream_matrix,
                                       channel.size)
        """
        Create new set of test data. 
        """
        ser_nn = []
        ser_nn_reduced = []
        ser_classic = []
        ser_lmmse = []

        for reps in range(1):

            del data_gen
            data_gen = CommunicationDataGenerator(symbol_stream_shape=(1,
                                                                       10000),
                                                  SNR=SNR,
                                                  plot=True,
                                                  channel=channel)
            data_gen.random_symbol_stream()
            data_gen.send_through_channel(quantization_level,
                                          noise_levels=noise_levels)
            """
            Evaluate Reduced Neural Net Performance
            """
            metric = NeuralNetworkMixtureModelMetric(
                net_reduced, mm_reduced, np.flip(data_gen.channel_output))
            detected_nn = viterbi_setup_with_nodes(
                data_gen.alphabet,
                data_gen.channel_output,
                data_gen.CIR_matrix.shape[1],
                metric.metric,
                reduced_length=reduced_state,
                reduced=True)
            symbol_probabilities = get_symbol_probabilities(
                totals, states_original, data_gen.alphabet)
            survivor_state_path = get_symbols_from_probabilities(
                detected_nn, symbol_probabilities, data_gen.alphabet)

            ser_nn_reduced = symbol_error_rate_channel_compensated_NN_reduced(
                survivor_state_path, data_gen.symbol_stream_matrix,
                channel_length)
            """
            Evaluate Neural Net Performance
            """

            metric = NeuralNetworkMixtureModelMetric(
                net, mm, np.flip(data_gen.channel_output))
            detected_nn = viterbi_setup_with_nodes(
                data_gen.alphabet, data_gen.channel_output,
                data_gen.CIR_matrix.shape[1], metric.metric)
            ser_nn = symbol_error_rate_channel_compensated_NN(
                detected_nn, data_gen.symbol_stream_matrix, channel_length)
            """
            Compare to Classical Viterbi with full CSI
            """
            metric = GaussianChannelMetric(
                channel, np.flip(data_gen.channel_output), quantization_level
            )  # This is a function to be used in the viterbi
            detected_classic = viterbi_setup_with_nodes(
                data_gen.alphabet, data_gen.channel_output,
                data_gen.CIR_matrix.shape[1], metric.metric)
            ser_classic = symbol_error_rate(detected_classic,
                                            data_gen.symbol_stream_matrix,
                                            channel_length)
            """
            Evaluate LMMSE
            """
            ser_lmmse.append(
                mmse_equalizer.test_equalizer(data_gen.symbol_stream_matrix,
                                              data_gen.channel_output))

        ser_nn = np.average(ser_nn)
        ser_nn_reduced = np.average(ser_nn_reduced)
        ser_classic = np.average(ser_classic)
        ser_lmmse = np.average(ser_lmmse)
        """
        Analyze SER performance
        """
        linear_mmse_performance.append(ser_lmmse)
        viterbi_net_performance.append(ser_nn)
        classic_performance.append(ser_classic)
        viterbi_net_reduced_performance.append(ser_nn_reduced)

    figure, dictionary = plot_quantized_symbol_error_rates_nn_compare(
        SNRs_dB, [
            classic_performance, linear_mmse_performance,
            viterbi_net_performance, viterbi_net_reduced_performance
        ], data_gen.get_info_for_plot())
    time_path = "Output/SER_" + f"{time.time()}" + "curves.png"
    figure.savefig(time_path, format="png")
    text_path = "Output/SER_" + f"{time.time()}" + "curves.csv"
    pd.DataFrame.from_dict(dictionary).to_csv(text_path)
    figure.savefig(time_path, format="png")
def test_nn_validation():

    viterbi_net_performance = []
    threshold_performance = []
    classic_performance = []
    SNRs_dB = 2
    SNRs =  np.power(10, SNRs_dB/10)
    seed_generator = 0
    data_gen = None

    """
    Generated Testing Data using the same channel as was used for training the mixture model and the nn
    """
    number_symbols = 2000
    channel = np.zeros((1, 3))
    channel[0, [0, 1, 2]] = 1, 0.1, 0.1
    data_gen = training_data_generator(symbol_stream_shape=(1, number_symbols), SNR=SNRs, plot=True, channel=channel)
    data_gen.random_symbol_stream()
    data_gen.send_through_channel()

    """
    Load in Trained Neural Network and verify that it is acceptable performance
    """
    device = torch.device("cpu")
    num_inputs_for_nn = 1
    x, y = data_gen.get_labeled_data(inputs=num_inputs_for_nn)
    y = np.argmax(y, axis=1)  # Fix for how the pytorch Cross Entropy expects class labels to be shown
    x = torch.Tensor(x)
    y = torch.Tensor(y)
    train_size = int(.6 * number_symbols)
    x_train = x[0:train_size, :]
    x_test = x[train_size::, :]
    y_train = y[0:train_size]
    y_test = y[train_size::]

    """
    Setup NN and optimizer
    """
    m = data_gen.alphabet.size
    channel_length = data_gen.CIR_matrix.shape[1]

    N, D_in, H1, H2, D_out = number_symbols, 1, 100, 50, np.power(m, channel_length)
    net = models.ViterbiNet(D_in, H1, H2, D_out)
    # N, D_in, H1, H2, H3, D_out = number_symbols, num_inputs_for_nn, 20, 10, 10, np.power(m, channel_length)
    # net = models.deeper_viterbiNet(D_in, H1, H2, H3, D_out)
    # optimizer = optim.Adam(net.parameters(), lr=1e-4)
    optimizer = optim.SGD(net.parameters(), lr=1e-2)

    """
    Train NN
    """
    # criterion = nn.NLLLoss()
    criterion = nn.CrossEntropyLoss()
    train_cost_over_epoch = []
    test_cost_over_epoch = []
    batch_size = 500

    # If training is perfect, then NN should be able to perfectly predict the class to which a test set belongs and thus the loss (KL Divergence) should be zero
    for t in range(1000):
        batch_indices = np.random.randint(len(y_train), size=(1, batch_size))
        x_batch = x_train[(batch_indices)]
        y_batch = y_train[(batch_indices)]
        output = net(x_batch)
        loss = criterion(output, y_batch.long())
        train_cost_over_epoch.append(loss)
        net.zero_grad()
        print(loss)
        loss.backward()
        optimizer.step()
        test_batch_indices = np.random.randint(len(y_test), size=(1, batch_size))
        x_batch_test = x_test[(test_batch_indices)]
        y_batch_test = y_test[(test_batch_indices)]
        test_cost_over_epoch.append(criterion(net(x_batch_test), y_batch_test.long()))



    #Plots for NN training information
    plt.figure(2)
    plt.plot(test_cost_over_epoch, label='Test Error')
    plt.plot(train_cost_over_epoch, label='Train Error')
    plt.title(str(data_gen.get_info_for_plot()), fontdict={'fontsize': 10})
    plt.xlabel("Epoch")
    plt.ylabel("Error")
    plt.legend(loc='upper left')
    plt.show()
    assert True
def test_results_nerual_net():
    """
    Train and save the neural network
    :return:
    """
    """
    Choose platform
    """
    device = torch.device("cpu")
    # device = torch.device('cuda') # Uncomment this to run on GPU
    """
    Setup Training Data
    """
    number_symbols = 5000
    channel = np.zeros((1, 4))
    channel[0, [0, 1, 2, 3]] = 1, 0.6, 0.3, 0.2
    data_gen = training_data_generator(symbol_stream_shape=(1, number_symbols),
                                       SNR=4,
                                       plot=True,
                                       channel=channel)
    # data_gen.setup_channel(shape=None)
    data_gen.random_symbol_stream()
    data_gen.send_through_channel()
    """
    After sending through channel, symbol detection should be performed using something like a matched filter
    """
    num_inputs_for_training_data = 1
    x, y = data_gen.get_labeled_data(inputs=num_inputs_for_training_data)
    y = np.argmax(
        y, axis=1
    )  # Fix for how the pytorch Cross Entropy expects class labels to be shown
    x = torch.Tensor(x)
    y = torch.Tensor(y)
    train_size = int(.6 * number_symbols)
    x_train = x[0:train_size, :]
    x_test = x[train_size::, :]
    y_train = y[0:train_size]
    y_test = y[train_size::]
    """
    Setup NN and optimizer
    """
    m = data_gen.alphabet.size
    channel_length = data_gen.CIR_matrix.shape[1]

    # N is batch size; D_in is input dimension;
    # H is hidden dimension; D_out is output dimension.
    N, D_in, H1, H2, D_out = number_symbols, num_inputs_for_training_data, 100, 50, np.power(
        m, channel_length)

    net = models.ViterbiNet(D_in, H1, H2, D_out)
    #TODO use better optimizer
    optimizer = optim.SGD(net.parameters(), lr=1e-2)
    optimizer = optim.Adam(net.parameters(), lr=1e-2)
    """
    Train NN
    """
    criterion = nn.CrossEntropyLoss()
    # criterion = nn.NLLLoss()
    train_cost_over_epoch = []
    test_cost_over_epoch = []

    # If training is perfect, then NN should be able to perfectly predict the class to which a test set belongs and thus the loss (KL Divergence) should be zero
    for t in range(400):
        output = net(x_train)
        loss = criterion(output, y_train.long())
        train_cost_over_epoch.append(loss)
        net.zero_grad()
        print(loss)
        loss.backward()
        optimizer.step()
        test_cost_over_epoch.append(criterion(net(x_test), y_test.long()))

    path = "Output/nn.pt"
    torch.save(net, path)
    """
    Test NN
    """
    plt.figure()
    plt.plot(test_cost_over_epoch, label='Test Error')
    plt.plot(train_cost_over_epoch, label='Train Error')
    plt.title(str(data_gen.get_info_for_plot()), fontdict={'fontsize': 10})
    plt.xlabel("Epoch")
    plt.ylabel("Error")
    plt.legend(loc='upper left')
    path = "Output/Neural_Network_Convergence.png"
    path = "Output/NN_ERROR" + str(number_symbols) + " symbols " \
           + str(num_inputs_for_training_data) + " inputs " + str(time.time())+"curves.png"
    plt.savefig(path, format="png")
    plt.show()
    assert True
def test_nano_data_nerual_net():

    viterbi_net_performance = []
    """
    Generated Testing Data using the same channel as was used for training the mixture model and the nn
    """
    #   Load data from path
    train_path = 'mc_data/5_cm_train.csv'
    test_path = 'mc_data/5_cm_test.csv'
    true_path = 'mc_data/input_string.csv'
    # train_path = 'mc_data/20_cm_train.csv'
    # test_path = 'mc_data/20_cm_test.csv'
    test_input_sequence = 'mc_data/input_string.csv'
    test_input_sequence = np.loadtxt(test_input_sequence, delimiter=",")
    true_input_string = np.loadtxt(true_path, delimiter=",")
    # For now just making a channel that represents some estimated memory length of the true channel
    SNRs_dB = np.linspace(5, 5, 1)
    # SNRs_dB = np.linspace(6, 10,3)
    SNRs = np.power(10, SNRs_dB / 10)

    channel = np.zeros((1, 8))
    channel[0, [0]] = 1
    train_time, train_measurement = load_file(train_path)
    test_time, test_measurement = load_file(test_path)
    # plt.plot(test_measurement)
    # plt.show()
    pulse_shape = get_pulse(train_time, train_measurement)
    #   Train with a random symbol stream generated from the training set pulse
    number_symbols = 10000
    data_gen = CommunicationDataGenerator(SNR=SNRs,
                                          symbol_stream_shape=(1,
                                                               number_symbols),
                                          constellation="onOffKey",
                                          channel=channel)
    data_gen.random_symbol_stream()
    # 5 is true perdiod
    symbol_period = 5
    data_gen.modulate_sampled_pulse(pulse_shape, symbol_period)
    data_gen.filter_sample_modulated_pulse(pulse_shape, symbol_period)

    # Test for making sure alignment is correct
    # ser = symbol_error_rate_mc_data(data_gen.symbol_stream_matrix.flatten(), data_gen.channel_output.flatten(), channel.size)
    """
    Load in Trained Neural Network and verify that it is acceptable performance
    """
    reduced_state = 4
    # x, y, states_reduced, states_original, totals = data_gen.get_labeled_data_reduced_state(reduced_state)
    x, y = data_gen.get_labeled_data()
    y = np.argmax(
        y, axis=1
    )  # Fix for how the pytorch Cross Entropy expects class labels to be shown
    x = torch.Tensor(x)
    y = torch.Tensor(y)
    train_size = int(.6 * number_symbols)
    x_train = x[0:train_size, :]
    x_test = x[train_size::, :]
    y_train = y[0:train_size]
    y_test = y[train_size::]
    """
    Setup NN and optimizer
    """
    channel_length = data_gen.CIR_matrix.shape[1]
    # test_length = channel_length-1
    output_layer_size = reduced_state
    output_layer_size = np.power(data_gen.alphabet.size, channel_length)
    N, D_in, H1, H2, D_out = number_symbols, 1, 100, 50, output_layer_size
    net = models.ViterbiNet(D_in, H1, H2, D_out)
    optimizer = optim.Adam(net.parameters(), lr=1e-2)
    """
    Train NN
    """
    criterion = nn.NLLLoss()
    # criterion = nn.CrossEntropyLoss()
    train_cost_over_epoch = []
    test_cost_over_epoch = []
    batch_size = 1000

    # If training is perfect, then NN should be able to perfectly predict the class to which a test set belongs and thus the loss (KL Divergence) should be zero
    epochs = 900
    for t in range(epochs):
        batch_indices = np.random.randint(len(y_train), size=(1, batch_size))
        x_batch = x_train[(batch_indices)]
        y_batch = y_train[(batch_indices)]
        # Add "dropout to prevent overfitting data"
        output = net(x_batch)
        loss = criterion(output, y_batch.long())
        net.zero_grad()
        loss.backward()
        optimizer.step()
        test_batch_indices = np.random.randint(len(y_test),
                                               size=(1, batch_size))
        x_batch_test = x_test[(test_batch_indices)]
        y_batch_test = y_test[(test_batch_indices)]
        max_state_train = np.argmax(output.detach().numpy(), axis=1)
        max_state_test = np.argmax(net(x_batch_test).detach().numpy(), axis=1)
        train_cost_over_epoch.append(
            np.sum(np.not_equal(max_state_train,
                                y_batch.detach().numpy())) / y_batch.size())
        test_cost_over_epoch.append(
            np.sum(np.not_equal(max_state_test,
                                y_batch_test.detach().numpy())) /
            y_batch_test.size())
    """
    Train Mixture Model
    """
    # num_sources = pow(data_gen.alphabet.size, data_gen.CIR_matrix.shape[1])
    num_sources = reduced_state
    mixture_model_training_data = data_gen.channel_output.flatten(
    )[0:train_size]
    mm = em_gausian(num_sources,
                    mixture_model_training_data,
                    10,
                    save=True,
                    model=True)
    mm = mm.get_probability
    """
    Create new set of test data. 
    """
    # For comparing generated data to the true test data
    # del data_gen
    # number_symbols = 2000
    # data_gen = CommunicationDataGenerator(SNR=SNRs, symbol_stream_shape=(1, number_symbols), constellation="onOffKey", channel= channel)
    # data_gen.random_symbol_stream()
    # data_gen.modulate_sampled_pulse(pulse_shape, symbol_period)
    # data_gen.filter_sample_modulated_pulse(pulse_shape, symbol_period)
    # generated_output = data_gen.channel_output

    del data_gen
    data_gen = CommunicationDataGenerator(SNR=SNRs,
                                          symbol_stream_shape=(1,
                                                               number_symbols),
                                          constellation="onOffKey",
                                          channel=channel)
    data_gen.random_symbol_stream(true_input_string)
    data_gen.provide_transmitted_matrix(test_measurement)
    data_gen.filter_sample_modulated_pulse(pulse_shape, symbol_period)
    # # plt.scatter(data_gen.channel_output, data_gen.channel_output)
    # plt.scatter(generated_output, generated_output)
    # plt.show()
    """
    Use Test Data 
    """
    """
    Evaluate Neural Net Performance
    """
    # metric = NeuralNetworkMixtureModelMetric(net, mm, data_gen.channel_output)
    metric = NeuralNetworkMixtureModelMetric(net, mm,
                                             np.flip(data_gen.channel_output))
    # detected_nn = viterbi_setup_with_nodes(data_gen.alphabet, data_gen.channel_output, data_gen.CIR_matrix.shape[1],
    #                                        metric.metric, reduced_length=reduced_state, reduced=True)
    detected_nn = viterbi_setup_with_nodes(data_gen.alphabet,
                                           data_gen.channel_output,
                                           data_gen.CIR_matrix.shape[1],
                                           metric.metric)
    # symbol_probabilities = get_symbol_probabilities(totals, states_original, data_gen.alphabet)
    # detected_nn = get_symbols_from_probabilities(detected_nn, symbol_probabilities, data_gen.alphabet)
    ser_nn = symbol_error_rate_mc_data(detected_nn,
                                       data_gen.symbol_stream_matrix,
                                       channel_length)
    # ser_nn = symbol_error_rate_channel_compensated_NN(detected_nn, data_gen.symbol_stream_matrix, channel_length)

    viterbi_net_performance.append(ser_nn)

    print(viterbi_net_performance)

    path = "Output/SER.pickle"
    pickle_out = open(path, "wb")
    pickle.dump([viterbi_net_performance], pickle_out)
    pickle_out.close()

    # figure, dictionary = plot_symbol_error_rates(SNRs_dB, [classic_performance, linear_mmse_performance, viterbi_net_performance], data_gen.get_info_for_plot())
    # time_path = "Output/SER_"+f"{time.time()}"+"curves.png"
    # figure.savefig(time_path, format="png")
    # text_path = "Output/SER_"+f"{time.time()}"+"curves.csv"
    # pd.DataFrame.from_dict(dictionary).to_csv(text_path)
    # figure.savefig(time_path, format="png")
    #

    #Plots for NN training information
    plt.figure(2)
    plt.plot(test_cost_over_epoch, label='Test Error')
    plt.plot(train_cost_over_epoch, label='Train Error')
    plt.title(str(data_gen.get_info_for_plot()), fontdict={'fontsize': 10})
    plt.xlabel("Epoch")
    plt.ylabel("Error")
    plt.legend(loc='upper right')
    path = f"Output/Neural_Network{time.time()}_Convergence.png"
    plt.savefig(path, format="png")