diff --git a/Norse/CIFAR-10/CIFAR10_conv_bench.py b/Norse/CIFAR-10/CIFAR10_conv_bench.py
new file mode 100644
index 0000000000000000000000000000000000000000..9832ec31d06d0a53543d8fd6869e89c68496cdef
--- /dev/null
+++ b/Norse/CIFAR-10/CIFAR10_conv_bench.py
@@ -0,0 +1,219 @@
+import sys
+sys.path.append('../../')
+
+import torch
+import numpy as np
+from compression import ProgressiveCompression
+from torch.utils.data import SubsetRandomSampler
+from norse.torch import LIFCell, LICell
+from norse.torch.module.leaky_integrator import LILinearCell
+from norse.torch import LIFParameters
+from norse.torch.module import encode, SequentialState
+from datetime import datetime
+import torchvision
+import os
+import pickle
+import random
+import itertools
+
+# Reproducibility
+torch.manual_seed(0)
+random.seed(0)
+np.random.seed(0)
+
+MAXTH = [0.3,0.4,0.5,0.6,0.7] #
+ALPHA = [0.005] # [0.002,0.004,0.006,0.008,0.01] # 
+REINFORCEMENT = [True] # [False, True]
+apply_compression = False
+
+for maxTh, Alpha, reinforcement in np.array(list(itertools.product(MAXTH, ALPHA, REINFORCEMENT))):
+    try: 
+        os.mkdir("CIFAR10_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement))
+    except OSError as error: 
+        print(error) 
+
+    for i in range(2):
+        before = datetime.now()
+
+        file = open("CIFAR10_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/CIFAR10_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before), 'w+')
+        
+        transform = torchvision.transforms.Compose(
+        [
+        torchvision.transforms.ToTensor(),
+        torchvision.transforms.Normalize((0.1307,), (0.3081,)),
+        ]
+        )
+
+        train_data = torchvision.datasets.CIFAR10(
+            root=".",
+            train=True,
+            download=True,
+            transform=transform,
+        )
+
+        # reduce this number if you run out of GPU memory
+        BATCH_SIZE = 32
+
+        train_loader = torch.utils.data.DataLoader(
+            train_data, batch_size=BATCH_SIZE, shuffle=True #, sampler=SubsetRandomSampler(list(range(len(train_data)))[0:1000]) #
+        )
+
+        test_loader = torch.utils.data.DataLoader(
+            torchvision.datasets.CIFAR10(
+                root=".",
+                train=False,
+                transform=transform
+            ),
+            batch_size=BATCH_SIZE
+        )
+
+        class Model(torch.nn.Module):
+            def __init__(self, encoder, snn, decoder):
+                super(Model, self).__init__()
+                self.encoder = encoder
+                self.snn = snn
+                self.decoder = decoder
+
+            def forward(self, x):
+                x = self.encoder(x)
+                x = self.snn(x)
+                log_p_y = self.decoder(x)
+                return log_p_y
+
+        class ConvNet(torch.nn.Module):
+            def __init__(self, num_channels=3, feature_size=32, method="super", alpha=100):
+                super(ConvNet, self).__init__()
+
+                self.features = int(((feature_size - 4) / 2 - 4) / 2)
+                self.conv1_out_channels = 32
+                self.conv2_out_channels = 128
+                self.fc1_out_channels = 1024
+                self.out_channels = 10
+                self.conv1 = torch.nn.Conv2d(num_channels, self.conv1_out_channels, 5, 1, bias=False)
+                self.conv2 = torch.nn.Conv2d(self.conv1_out_channels, self.conv2_out_channels, 5, 1, bias=False)
+                self.fc1 = torch.nn.Linear(self.features**2  * self.conv2_out_channels, self.fc1_out_channels, bias=False)
+                self.lif0 = LIFCell(p=LIFParameters(method=method, alpha=alpha, v_th=0.25))
+                self.lif1 = LIFCell(p=LIFParameters(method=method, alpha=alpha, v_th=0.25))
+                self.lif2 = LIFCell(p=LIFParameters(method=method, alpha=alpha, v_th=0.25))
+                self.out = LILinearCell(self.fc1_out_channels, self.out_channels)
+                
+                #LILinearCell(self.fc1_out_channels, self.out_channels)
+
+            def forward(self, x):
+                seq_length = x.shape[0]
+                batch_size = x.shape[1]
+
+                # specify the initial states
+                s0 = s1 = s2 = s3 = s4 = so = None
+
+                voltages = torch.zeros(
+                    seq_length, batch_size, self.out_channels, device=x.device, dtype=x.dtype
+                )
+
+                for ts in range(seq_length):
+                    z = self.conv1(x[ts, :])
+                    z, s0 = self.lif0(z, s0)
+                    z = torch.nn.functional.max_pool2d(z, 2, 2)
+                    z = self.out_channels * self.conv2(z)
+                    z, s1 = self.lif1(z, s1)
+                    z = torch.nn.functional.max_pool2d(z, 2, 2)
+                    z = z.view(-1, self.features**2 * self.conv2_out_channels)
+                    z = self.fc1(z)
+                    z, s2 = self.lif2(z, s2)
+                    v, so = self.out(torch.nn.functional.relu(z), so)
+                    voltages[ts, :, :] = v
+
+                return voltages
+
+        def train(model, device, train_loader, optimizer, epoch, max_epochs):
+            model.train()
+            losses = []
+
+            for (data, target) in train_loader:
+                data, target = data.to(device), target.to(device)
+                optimizer.zero_grad()
+                output = model(data)
+                loss = torch.nn.functional.nll_loss(output, target)
+                loss.backward()
+                optimizer.step()
+                losses.append(loss.item())
+
+            mean_loss = np.mean(losses)
+            return losses, mean_loss
+
+        def test(model, device, test_loader, epoch):
+            model.eval()
+            test_loss = 0
+            correct = 0
+            with torch.no_grad():
+                for data, target in test_loader:
+                    data, target = data.to(device), target.to(device)
+                    output = model(data)
+                    test_loss += torch.nn.functional.nll_loss(
+                        output, target, reduction="sum"
+                    ).item()  # sum up batch loss
+                    pred = output.argmax(
+                        dim=1, keepdim=True
+                    )  # get the index of the max log-probability
+                    correct += pred.eq(target.view_as(pred)).sum().item()
+
+            test_loss /= len(test_loader.dataset)
+
+            accuracy = 100.0 * correct / len(test_loader.dataset)
+
+            return test_loss, accuracy
+
+        def decode(x):
+            x, _ = torch.max(x, 0)
+            log_p_y = torch.nn.functional.log_softmax(x, dim=1)
+            return log_p_y
+
+        torch.autograd.set_detect_anomaly(True)
+
+        T = 35
+        LR = 0.001
+        EPOCHS = 100  # Increase this for improved accuracy
+
+        if torch.cuda.is_available():
+            DEVICE = torch.device(sys.argv[1])
+        else:
+            DEVICE = torch.device("cpu")
+
+        model = Model(
+            encoder=encode.SpikeLatencyLIFEncoder(T), snn=ConvNet(alpha=80), decoder=decode).to(DEVICE)
+
+        optimizer = torch.optim.Adam(model.parameters(), lr=LR)
+
+        # compression
+        if (apply_compression):
+            progressive_compression = ProgressiveCompression(NorseModel=model, maxThreshold=maxTh, alphaP=Alpha, alphaN=-Alpha,  to_file=True, apply_reinforcement=reinforcement, file= file)
+
+        training_losses = []
+        mean_losses = []
+        test_losses = []
+        accuracies = []
+
+        for epoch in range(EPOCHS):
+            print(f"Epoch {epoch}")
+            training_loss, mean_loss = train(
+                model, DEVICE, train_loader, optimizer, epoch, max_epochs=EPOCHS
+            )
+            test_loss, accuracy = test(model, DEVICE, test_loader, epoch)
+            training_losses += training_loss
+            mean_losses.append(mean_loss)
+            test_losses.append(test_loss)
+            accuracies.append(accuracy)
+            if (apply_compression):
+                progressive_compression.apply()
+
+        print(f"final accuracy: {accuracies[-1]}")
+        file.write("final accuracy:"+str(accuracies[-1])+"\n")
+        file.write("time:"+str(datetime.now() - before)+"\n")
+        
+
+        with open("CIFAR10_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/CIFAR10_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before)+".pkl",'wb') as f:
+                    torch.save(model,"CIFAR10_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/CIFAR10_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before)+".norse")
+                    if (apply_compression):
+                         pickle.dump([mean_losses,test_losses,accuracies,progressive_compression.weights,progressive_compression.compressions,progressive_compression.thresholds_p,progressive_compression.thresholds_n], f)
+                    else:
+                        pickle.dump([mean_losses,test_losses,accuracies], f)
diff --git a/Norse/Caltech Face-Motor/CaltechFace_Motor_conv.py b/Norse/Caltech Face-Motor/CaltechFace_Motor_conv.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ddf1a39fcd72d87a1af4d6a02a3fdc8c3e4c67f
--- /dev/null
+++ b/Norse/Caltech Face-Motor/CaltechFace_Motor_conv.py	
@@ -0,0 +1,214 @@
+import sys
+sys.path.append('../../')
+
+import torch
+import numpy as np
+from compression import ProgressiveCompression
+from norse.torch import LIFCell
+from norse.torch.module.leaky_integrator import LILinearCell
+from norse.torch import LIFParameters
+from norse.torch.module import encode
+from tqdm import tqdm, trange
+from datetime import datetime
+import torchvision
+import os
+import pickle
+import random
+import itertools
+
+# Reproducibility
+torch.manual_seed(0)
+random.seed(0)
+np.random.seed(0)
+
+MAXTH = [0.3,0.4,0.5,0.6,0.7] #
+ALPHA = [0.005] # [0.002,0.004,0.006,0.008,0.01] # 
+REINFORCEMENT = [True] # [False, True]
+apply_compression = True
+
+for maxTh, Alpha, reinforcement in np.array(list(itertools.product(MAXTH, ALPHA, REINFORCEMENT))):
+    try: 
+        os.mkdir("FACEMOTOR_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement))
+    except OSError as error: 
+        print(error) 
+
+    for i in range(2):
+        before = datetime.now()
+
+        file = open("FACEMOTOR_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/FACEMOTOR_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before), 'w+')
+        
+        transform = torchvision.transforms.Compose(
+        [
+            torchvision.transforms.ToTensor(),
+            torchvision.transforms.Grayscale(),
+            torchvision.transforms.Resize((250,160)),
+            torchvision.transforms.Normalize((0.1307,), (0.3081,)),            
+        ]
+        )
+
+        train_data = torchvision.datasets.ImageFolder(
+            root="/home/hammouda/Desktop/Work/falez-csnn-simulator/Datasets/FaceMotor/TrainingSet/",
+            transform=transform
+        )
+
+        # reduce this number if you run out of GPU memory
+        BATCH_SIZE = 5
+
+        train_loader = torch.utils.data.DataLoader(
+            train_data, batch_size=BATCH_SIZE, shuffle=True
+        )
+
+        test_loader = torch.utils.data.DataLoader(
+            torchvision.datasets.ImageFolder(
+            root="/home/hammouda/Desktop/Work/falez-csnn-simulator/Datasets/FaceMotor/TestingSet/",
+            transform=transform
+        ),
+            batch_size=BATCH_SIZE,
+        )
+
+        class Model(torch.nn.Module):
+            def __init__(self, encoder, snn, decoder):
+                super(Model, self).__init__()
+                self.encoder = encoder
+                self.snn = snn
+                self.decoder = decoder
+
+            def forward(self, x):
+                x = self.encoder(x)
+                x = self.snn(x)
+                log_p_y = self.decoder(x)
+                return log_p_y
+
+        class ConvNet(torch.nn.Module):
+            def __init__(self, num_channels=1, feature_size=250, method="super", alpha=100):
+                super(ConvNet, self).__init__()
+
+                self.features = 54
+                self.conv1_out_channels = 32
+                self.conv2_out_channels = 64
+                self.fc1_out_channels = 128
+                self.out_channels = 10
+                self.conv1 = torch.nn.Conv2d(num_channels, self.conv1_out_channels, 5, 1, 3, bias=False)
+                self.conv2 = torch.nn.Conv2d(self.conv1_out_channels, self.conv2_out_channels, 17, 1, 9, bias=False)
+                self.fc1 = torch.nn.Linear(self.features * self.conv2_out_channels, self.fc1_out_channels, bias=False)
+                self.lif0 = LIFCell(p=LIFParameters(method=method, alpha=alpha, v_th=0.25))
+                self.lif1 = LIFCell(p=LIFParameters(method=method, alpha=alpha, v_th=0.25))
+                self.lif2 = LIFCell(p=LIFParameters(method=method, alpha=alpha, v_th=0.25))
+                self.out = LILinearCell(self.fc1_out_channels, self.out_channels)
+
+            def forward(self, x):
+                seq_length = x.shape[0]
+                batch_size = x.shape[1]
+
+                # specify the initial states
+                s0 = s1 = s2 = so = None
+
+                voltages = torch.zeros(
+                    seq_length, batch_size, self.out_channels, device=x.device, dtype=x.dtype
+                )
+
+                for ts in range(seq_length):
+                    z = self.conv1(x[ts, :])
+                    z, s0 = self.lif0(z, s0)
+                    z = torch.nn.functional.max_pool2d(z, 7, 6, 3)
+                    z = self.out_channels * self.conv2(z)
+                    z, s1 = self.lif1(z, s1)
+                    z = torch.nn.functional.max_pool2d(z, 5, 5, 2)
+                    z = z.view(batch_size,-1)
+                    z = self.fc1(z)
+                    z, s2 = self.lif2(z, s2)
+                    v, so = self.out(torch.nn.functional.relu(z), so)
+                    voltages[ts, :, :] = v
+
+                return voltages
+
+        def train(model, device, train_loader, optimizer, epoch, max_epochs):
+            model.train()
+            losses = []
+
+            for (data, target) in tqdm(train_loader, leave=False):
+                data, target = data.to(device), target.to(device)
+                optimizer.zero_grad()
+                output = model(data)
+                loss = torch.nn.functional.nll_loss(output, target)
+                loss.backward()
+                optimizer.step()
+                losses.append(loss.item())
+
+            mean_loss = np.mean(losses)
+            return losses, mean_loss
+
+        def test(model, device, test_loader, epoch):
+            model.eval()
+            test_loss = 0
+            correct = 0
+            with torch.no_grad():
+                for data, target in test_loader:
+                    data, target = data.to(device), target.to(device)
+                    output = model(data)
+                    test_loss += torch.nn.functional.nll_loss(
+                        output, target, reduction="sum"
+                    ).item()  # sum up batch loss
+                    pred = output.argmax(
+                        dim=1, keepdim=True
+                    )  # get the index of the max log-probability
+                    correct += pred.eq(target.view_as(pred)).sum().item()
+
+            test_loss /= len(test_loader.dataset)
+
+            accuracy = 100.0 * correct / len(test_loader.dataset)
+
+            return test_loss, accuracy
+
+        def decode(x):
+            x, _ = torch.max(x, 0)
+            log_p_y = torch.nn.functional.log_softmax(x, dim=1)
+            return log_p_y
+
+        T = 35
+        LR = 1e-4
+        EPOCHS = 15  # Increase this for improved accuracy
+
+        if torch.cuda.is_available():
+            DEVICE = torch.device("cuda")
+        else:
+            DEVICE = torch.device("cpu")
+
+        model = Model(
+            encoder=encode.SpikeLatencyLIFEncoder(T), snn=ConvNet(alpha=80), decoder=decode).to(DEVICE)
+
+        optimizer = torch.optim.Adam(model.parameters(), lr=LR)
+
+        # compression
+        if (apply_compression):
+            progressive_compression = ProgressiveCompression(NorseModel=model, maxThreshold=maxTh, alphaP=Alpha, alphaN=-Alpha,  to_file=True, apply_reinforcement=reinforcement, file= file)
+
+        training_losses = []
+        mean_losses = []
+        test_losses = []
+        accuracies = []
+
+        for epoch in trange(EPOCHS):
+            print(f"Epoch {epoch}")
+            training_loss, mean_loss = train(
+                model, DEVICE, train_loader, optimizer, epoch, max_epochs=EPOCHS
+            )
+            test_loss, accuracy = test(model, DEVICE, test_loader, epoch)
+            training_losses += training_loss
+            mean_losses.append(mean_loss)
+            test_losses.append(test_loss)
+            accuracies.append(accuracy)
+            if (apply_compression):
+                progressive_compression.apply()
+
+        print(f"final accuracy: {accuracies[-1]}")
+        file.write("final accuracy:"+str(accuracies[-1])+"\n")
+        file.write("time:"+str(datetime.now() - before)+"\n")
+        
+
+        with open("FACEMOTOR_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/FACEMOTOR_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before)+".pkl",'wb') as f:
+                    torch.save(model,"FACEMOTOR_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/FACEMOTOR_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before)+".norse")
+                    if (apply_compression):
+                         pickle.dump([mean_losses,test_losses,accuracies,progressive_compression.weights,progressive_compression.compressions,progressive_compression.thresholds_p,progressive_compression.thresholds_n], f)
+                    else:
+                        pickle.dump([mean_losses,test_losses,accuracies], f)
diff --git a/Norse/FMNIST/FMNIST_conv_bench.py b/Norse/FMNIST/FMNIST_conv_bench.py
new file mode 100644
index 0000000000000000000000000000000000000000..c3fb080ef867e67cd7fb0b0a0435e8b50ee57288
--- /dev/null
+++ b/Norse/FMNIST/FMNIST_conv_bench.py
@@ -0,0 +1,215 @@
+import sys
+sys.path.append('../../')
+
+import torch
+import numpy as np
+from compression import ProgressiveCompression
+from norse.torch import LIFCell
+from norse.torch.module.leaky_integrator import LILinearCell
+from norse.torch import LIFParameters
+from norse.torch.module import encode
+#from tqdm import tqdm, trange
+from datetime import datetime
+import torchvision
+import os
+import pickle
+import random
+import itertools
+
+# Reproducibility
+torch.manual_seed(0)
+random.seed(0)
+np.random.seed(0)
+
+MAXTH = [0.6,0.7] # 0.3,0.5,
+ALPHA = [0.005] # [0.002,0.004,0.006,0.008,0.01] # 
+REINFORCEMENT = [True] # [False, True]
+
+for maxTh, Alpha, reinforcement in np.array(list(itertools.product(MAXTH, ALPHA, REINFORCEMENT))):
+    try: 
+        os.mkdir("FashionMNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement))
+    except OSError as error: 
+        print(error) 
+
+    for i in range(2):
+        before = datetime.now()
+
+        file = open("FashionMNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/FashionMNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before), 'w+')
+
+        apply_compression = True
+        
+        transform = torchvision.transforms.Compose(
+    [
+        torchvision.transforms.ToTensor(),
+        torchvision.transforms.Normalize((0.1307,), (0.3081,)),
+    ]
+        )
+
+        train_data = torchvision.datasets.FashionMNIST(
+            root=".",
+            train=True,
+            download=True,
+            transform=transform,
+        )
+
+        # reduce this number if you run out of GPU memory
+        BATCH_SIZE = 128
+
+        train_loader = torch.utils.data.DataLoader(
+            train_data, batch_size=BATCH_SIZE, shuffle=True
+        )
+
+        test_loader = torch.utils.data.DataLoader(
+            torchvision.datasets.FashionMNIST(
+                root=".",
+                train=False,
+                transform=transform,
+            ),
+            batch_size=BATCH_SIZE,
+        )
+
+        class Model(torch.nn.Module):
+            def __init__(self, encoder, snn, decoder):
+                super(Model, self).__init__()
+                self.encoder = encoder
+                self.snn = snn
+                self.decoder = decoder
+
+            def forward(self, x):
+                x = self.encoder(x)
+                x = self.snn(x)
+                log_p_y = self.decoder(x)
+                return log_p_y
+
+        class ConvNet(torch.nn.Module):
+            def __init__(self, num_channels=1, feature_size=28, method="super", alpha=100):
+                super(ConvNet, self).__init__()
+
+                self.features = int(((feature_size - 4) / 2 - 4) / 2)
+                self.conv1_out_channels = 32
+                self.conv2_out_channels = 128
+                self.fc1_out_channels = 1024
+                self.out_channels = 10
+                self.conv1 = torch.nn.Conv2d(num_channels, self.conv1_out_channels, 5, 1, bias=False)
+                self.conv2 = torch.nn.Conv2d(self.conv1_out_channels, self.conv2_out_channels, 5, 1, bias=False)
+                self.fc1 = torch.nn.Linear(self.features * self.features * self.conv2_out_channels, self.fc1_out_channels, bias=False)
+                self.lif0 = LIFCell(p=LIFParameters(method=method, alpha=alpha,v_th=0.25))
+                self.lif1 = LIFCell(p=LIFParameters(method=method, alpha=alpha,v_th=0.25))
+                self.lif2 = LIFCell(p=LIFParameters(method=method, alpha=alpha,v_th=0.25))
+                self.out = LILinearCell(self.fc1_out_channels, self.out_channels)
+
+            def forward(self, x):
+                seq_length = x.shape[0]
+                batch_size = x.shape[1]
+
+                # specify the initial states
+                s0 = s1 = s2 = so = None
+
+                voltages = torch.zeros(
+                    seq_length, batch_size, self.out_channels, device=x.device, dtype=x.dtype
+                )
+
+                for ts in range(seq_length):
+                    z = self.conv1(x[ts, :])
+                    z, s0 = self.lif0(z, s0)
+                    z = torch.nn.functional.max_pool2d(z, 2, 2)
+                    z = self.out_channels * self.conv2(z)
+                    z, s1 = self.lif1(z, s1)
+                    z = torch.nn.functional.max_pool2d(z, 2, 2)
+                    z = z.view(-1, 4**2 * self.conv2_out_channels)
+                    z = self.fc1(z)
+                    z, s2 = self.lif2(z, s2)
+                    v, so = self.out(torch.nn.functional.relu(z), so)
+                    voltages[ts, :, :] = v
+                return voltages
+
+        def train(model, device, train_loader, optimizer, epoch, max_epochs):
+            model.train()
+            losses = []
+
+            for (data, target) in train_loader: #tqdm(train_loader, leave=False):
+                data, target = data.to(device), target.to(device)
+                optimizer.zero_grad()
+                output = model(data)
+                loss = torch.nn.functional.nll_loss(output, target)
+                loss.backward()
+                optimizer.step()
+                losses.append(loss.item())
+
+            mean_loss = np.mean(losses)
+            return losses, mean_loss
+
+        def test(model, device, test_loader, epoch):
+            model.eval()
+            test_loss = 0
+            correct = 0
+            with torch.no_grad():
+                for data, target in test_loader:
+                    data, target = data.to(device), target.to(device)
+                    output = model(data)
+                    test_loss += torch.nn.functional.nll_loss(
+                        output, target, reduction="sum"
+                    ).item()  # sum up batch loss
+                    pred = output.argmax(
+                        dim=1, keepdim=True
+                    )  # get the index of the max log-probability
+                    correct += pred.eq(target.view_as(pred)).sum().item()
+
+            test_loss /= len(test_loader.dataset)
+
+            accuracy = 100.0 * correct / len(test_loader.dataset)
+
+            return test_loss, accuracy
+
+        def decode(x):
+            x, _ = torch.max(x, 0)
+            log_p_y = torch.nn.functional.log_softmax(x, dim=1)
+            return log_p_y
+
+        T = 35
+        LR = 0.001
+        EPOCHS = 100  # Increase this for improved accuracy
+
+        if torch.cuda.is_available():
+            DEVICE = torch.device("cuda")
+        else:
+            DEVICE = torch.device("cpu")
+
+        model = Model(
+            encoder=encode.SpikeLatencyLIFEncoder(T), snn=ConvNet(alpha=80), decoder=decode).to(DEVICE)
+
+        optimizer = torch.optim.Adam(model.parameters(), lr=LR)
+
+        # compression
+        if (apply_compression):
+            progressive_compression = ProgressiveCompression(NorseModel=model, maxThreshold=maxTh, alphaP=Alpha, alphaN=-Alpha,  to_file=True, apply_reinforcement=reinforcement, file= file)
+
+        training_losses = []
+        mean_losses = []
+        test_losses = []
+        accuracies = []
+
+        for epoch in range(EPOCHS):
+            print(f"Epoch {epoch}")
+            training_loss, mean_loss = train(
+                model, DEVICE, train_loader, optimizer, epoch, max_epochs=EPOCHS
+            )
+            test_loss, accuracy = test(model, DEVICE, test_loader, epoch)
+            training_losses += training_loss
+            mean_losses.append(mean_loss)
+            test_losses.append(test_loss)
+            accuracies.append(accuracy)
+            if (apply_compression):
+                progressive_compression.apply()
+
+        print(f"final accuracy: {accuracies[-1]}")
+        file.write("final accuracy:"+str(accuracies[-1])+"\n")
+        file.write("time:"+str(datetime.now() - before)+"\n")
+        
+
+        with open("FashionMNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/FashionMNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before)+".pkl",'wb') as f:
+                    torch.save(model,"FashionMNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/FashionMNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before)+".norse")
+                    if (apply_compression):
+                        pickle.dump([mean_losses,test_losses,accuracies,progressive_compression.weights,progressive_compression.compressions,progressive_compression.thresholds_p,progressive_compression.thresholds_n], f)
+                    else:
+                        pickle.dump([mean_losses,test_losses,accuracies], f)
diff --git a/Norse/MNIST/MNIST_conv_bench.py b/Norse/MNIST/MNIST_conv_bench.py
new file mode 100644
index 0000000000000000000000000000000000000000..b3188865d6fad069eb0812f3ddeb5e33fc008e66
--- /dev/null
+++ b/Norse/MNIST/MNIST_conv_bench.py
@@ -0,0 +1,215 @@
+import sys
+sys.path.append('../../')
+
+import torch
+import numpy as np
+from compression import ProgressiveCompression
+from norse.torch import LIFCell
+from norse.torch.module.leaky_integrator import LILinearCell
+from norse.torch import LIFParameters
+from norse.torch.module import encode
+#from tqdm import tqdm, trange
+from datetime import datetime
+import torchvision
+import os
+import pickle
+import random
+import itertools
+
+# Reproducibility
+torch.manual_seed(0)
+random.seed(0)
+np.random.seed(0)
+
+MAXTH = [0.6,0.7] #
+ALPHA = [0.005] # [0.002,0.004,0.006,0.008,0.01] # 
+REINFORCEMENT = [True] # [False, True]
+
+for maxTh, Alpha, reinforcement in np.array(list(itertools.product(MAXTH, ALPHA, REINFORCEMENT))):
+    try: 
+        os.mkdir("MNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement))
+    except OSError as error: 
+        print(error) 
+
+    for i in range(2):
+        before = datetime.now()
+
+        file = open("MNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/MNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before), 'w+')
+
+        apply_compression = True
+        
+        transform = torchvision.transforms.Compose(
+    [
+        torchvision.transforms.ToTensor(),
+        torchvision.transforms.Normalize((0.1307,), (0.3081,)),
+    ]
+        )
+
+        train_data = torchvision.datasets.MNIST(
+            root=".",
+            train=True,
+            download=True,
+            transform=transform,
+        )
+
+        # reduce this number if you run out of GPU memory
+        BATCH_SIZE = 512
+
+        train_loader = torch.utils.data.DataLoader(
+            train_data, batch_size=BATCH_SIZE, shuffle=True
+        )
+
+        test_loader = torch.utils.data.DataLoader(
+            torchvision.datasets.MNIST(
+                root=".",
+                train=False,
+                transform=transform,
+            ),
+            batch_size=BATCH_SIZE,
+        )
+
+        class Model(torch.nn.Module):
+            def __init__(self, encoder, snn, decoder):
+                super(Model, self).__init__()
+                self.encoder = encoder
+                self.snn = snn
+                self.decoder = decoder
+
+            def forward(self, x):
+                x = self.encoder(x)
+                x = self.snn(x)
+                log_p_y = self.decoder(x)
+                return log_p_y
+
+        class ConvNet(torch.nn.Module):
+            def __init__(self, num_channels=1, feature_size=28, method="super", alpha=100):
+                super(ConvNet, self).__init__()
+
+                self.features = int(((feature_size - 4) / 2 - 4) / 2)
+                self.conv1_out_channels = 32
+                self.conv2_out_channels = 128
+                self.fc1_out_channels = 1024
+                self.out_channels = 10
+                self.conv1 = torch.nn.Conv2d(num_channels, self.conv1_out_channels, 5, 1, bias=False)
+                self.conv2 = torch.nn.Conv2d(self.conv1_out_channels, self.conv2_out_channels, 5, 1, bias=False)
+                self.fc1 = torch.nn.Linear(self.features * self.features * self.conv2_out_channels, self.fc1_out_channels, bias=False)
+                self.lif0 = LIFCell(p=LIFParameters(method=method, alpha=alpha,v_th=0.25))
+                self.lif1 = LIFCell(p=LIFParameters(method=method, alpha=alpha,v_th=0.25))
+                self.lif2 = LIFCell(p=LIFParameters(method=method, alpha=alpha,v_th=0.25))
+                self.out = LILinearCell(self.fc1_out_channels, self.out_channels)
+
+            def forward(self, x):
+                seq_length = x.shape[0]
+                batch_size = x.shape[1]
+
+                # specify the initial states
+                s0 = s1 = s2 = so = None
+
+                voltages = torch.zeros(
+                    seq_length, batch_size, self.out_channels, device=x.device, dtype=x.dtype
+                )
+
+                for ts in range(seq_length):
+                    z = self.conv1(x[ts, :])
+                    z, s0 = self.lif0(z, s0)
+                    z = torch.nn.functional.max_pool2d(z, 2, 2)
+                    z = self.out_channels * self.conv2(z)
+                    z, s1 = self.lif1(z, s1)
+                    z = torch.nn.functional.max_pool2d(z, 2, 2)
+                    z = z.view(-1, 4**2 * self.conv2_out_channels)
+                    z = self.fc1(z)
+                    z, s2 = self.lif2(z, s2)
+                    v, so = self.out(torch.nn.functional.relu(z), so)
+                    voltages[ts, :, :] = v
+                return voltages
+
+        def train(model, device, train_loader, optimizer, epoch, max_epochs):
+            model.train()
+            losses = []
+
+            for (data, target) in train_loader: #tqdm(train_loader, leave=False):
+                data, target = data.to(device), target.to(device)
+                optimizer.zero_grad()
+                output = model(data)
+                loss = torch.nn.functional.nll_loss(output, target)
+                loss.backward()
+                optimizer.step()
+                losses.append(loss.item())
+
+            mean_loss = np.mean(losses)
+            return losses, mean_loss
+
+        def test(model, device, test_loader, epoch):
+            model.eval()
+            test_loss = 0
+            correct = 0
+            with torch.no_grad():
+                for data, target in test_loader:
+                    data, target = data.to(device), target.to(device)
+                    output = model(data)
+                    test_loss += torch.nn.functional.nll_loss(
+                        output, target, reduction="sum"
+                    ).item()  # sum up batch loss
+                    pred = output.argmax(
+                        dim=1, keepdim=True
+                    )  # get the index of the max log-probability
+                    correct += pred.eq(target.view_as(pred)).sum().item()
+
+            test_loss /= len(test_loader.dataset)
+
+            accuracy = 100.0 * correct / len(test_loader.dataset)
+
+            return test_loss, accuracy
+
+        def decode(x):
+            x, _ = torch.max(x, 0)
+            log_p_y = torch.nn.functional.log_softmax(x, dim=1)
+            return log_p_y
+
+        T = 35
+        LR = 0.001
+        EPOCHS = 100  # Increase this for improved accuracy
+
+        if torch.cuda.is_available():
+            DEVICE = torch.device("cuda:1")
+        else:
+            DEVICE = torch.device("cpu")
+
+        model = Model(
+            encoder=encode.SpikeLatencyLIFEncoder(T), snn=ConvNet(alpha=80), decoder=decode).to(DEVICE)
+
+        optimizer = torch.optim.Adam(model.parameters(), lr=LR)
+
+        # compression
+        if (apply_compression):
+            progressive_compression = ProgressiveCompression(NorseModel=model, maxThreshold=maxTh, alphaP=Alpha, alphaN=-Alpha,  to_file=True, apply_reinforcement=reinforcement, file= file)
+
+        training_losses = []
+        mean_losses = []
+        test_losses = []
+        accuracies = []
+
+        for epoch in range(EPOCHS):
+            print(f"Epoch {epoch}")
+            training_loss, mean_loss = train(
+                model, DEVICE, train_loader, optimizer, epoch, max_epochs=EPOCHS
+            )
+            test_loss, accuracy = test(model, DEVICE, test_loader, epoch)
+            training_losses += training_loss
+            mean_losses.append(mean_loss)
+            test_losses.append(test_loss)
+            accuracies.append(accuracy)
+            if (apply_compression):
+                progressive_compression.apply()
+
+        print(f"final accuracy: {accuracies[-1]}")
+        file.write("final accuracy:"+str(accuracies[-1])+"\n")
+        file.write("time:"+str(datetime.now() - before)+"\n")
+        
+
+        with open("MNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/MNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before)+".pkl",'wb') as f:
+                    torch.save(model,"MNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"/MNIST_CONV_maxTh:"+str(maxTh)+"_Alpha:"+str(Alpha)+"_"+"reinforcement:"+str(reinforcement)+"_"+str(before)+".norse")
+                    if (apply_compression):
+                        pickle.dump([mean_losses,test_losses,accuracies,progressive_compression.weights,progressive_compression.compressions,progressive_compression.thresholds_p,progressive_compression.thresholds_n], f)
+                    else:
+                        pickle.dump([mean_losses,test_losses,accuracies], f)
diff --git a/Norse/compression.py b/Norse/compression.py
new file mode 100644
index 0000000000000000000000000000000000000000..d1e169d3404a6e7583ea077a72bb8fc9a1c62f03
--- /dev/null
+++ b/Norse/compression.py
@@ -0,0 +1,101 @@
+import numpy as np
+import torch
+
+class ProgressiveCompression:
+    def __init__(self, NorseModel,maxThreshold=0.3, alphaP=0.005, alphaN=-0.005, betaP=0.01, betaN=-0.01, prune_recurrent=True, apply_reinforcement=False, to_file=False, file=None, layerwise=False):
+        self.alpha_p = []
+        self.alpha_n = []
+        self.max_threshold = maxThreshold
+        self.model = NorseModel
+        self.prune_recurrent = prune_recurrent
+        self.layerwise = layerwise
+        self.apply_reinforcement = apply_reinforcement
+        self.to_file = to_file
+        self.file = file
+        self.max_threshold_p = []
+        self.max_threshold_n = []
+        self.max_w_p = []
+        self.max_w_n = []
+        self.prune_matrix = []
+        self.thresholds_p = []
+        self.thresholds_n = []
+        self.compressions = []
+        self.weights = []
+        self.betaP = betaP
+        self.betaN = betaN
+
+        i = 0
+        for name, param in self.model.named_parameters():
+            print(name,param.data.min(),param.data.max())
+            self.max_w_p.append(param.data.max())
+            self.max_w_n.append(param.data.min())
+            
+            if (self.layerwise):
+                self.alpha_p.append(alphaP + (0.005*i))
+                self.alpha_n.append(alphaN + (-0.005*i))
+            else:
+                self.alpha_p.append(alphaP)
+                self.alpha_n.append(alphaN)
+                
+            self.max_threshold_p.append(param.data.max() * self.max_threshold)
+            self.max_threshold_n.append(param.data.min() * self.max_threshold)
+            print("max_threshold_n: "+str(param.data.min() * self.max_threshold)+" max_threshold_p: "+str(param.data.max() * self.max_threshold)+"\n")
+            if(self.to_file):
+                self.file.write(str(name)+"  "+str(param.data.min())+"  "+str(param.data.max())+"\n")
+                self.file.write("max_threshold_n: "+str(param.data.min() * self.max_threshold)+" max_threshold_p: "+str(param.data.max() * self.max_threshold)+"\n")
+            i = i + 1
+
+        for param in self.model.parameters():
+            self.prune_matrix.append(torch.zeros_like(param))
+            self.thresholds_p.append([None])
+            self.thresholds_n.append([None])
+            self.compressions.append([0])
+
+    def applyprune(self,name, alpha_p, alpha_n, max_thresholds_p,max_thresholds_n, weights, prune_matrix, threshold_p, threshold_n):
+        print(name, " before prune: Min: ",weights.min()," Max: ",weights.max())
+        if ((not self.prune_recurrent) and ("recurrent" in name)):
+            return weights, prune_matrix, threshold_p, threshold_n
+        else:
+            if threshold_p == None:
+                threshold_p = alpha_p
+            else:
+                if threshold_p < max_thresholds_p:
+                    threshold_p += alpha_p * (np.count_nonzero(prune_matrix.cpu() == 0) / (np.count_nonzero(prune_matrix.cpu() == 0) + np.count_nonzero(prune_matrix.cpu() == 1)))
+                    threshold_p = round(threshold_p, 6)
+
+            if threshold_n == None:
+                threshold_n = alpha_n
+            else:
+                if threshold_n > max_thresholds_n:
+                    threshold_n += alpha_n * (np.count_nonzero(prune_matrix.cpu() == 0) / (np.count_nonzero(prune_matrix.cpu() == 0) + np.count_nonzero(prune_matrix.cpu() == 1)))
+                    threshold_n = round(threshold_n, 6)
+
+            return weights.masked_fill(((weights < threshold_p) & (weights > 0)) | ((weights > threshold_n) & (weights < 0)), 0), prune_matrix.masked_fill(((weights < threshold_p) & (weights > 0)) | ((weights > threshold_n) & (weights < 0)), 1), threshold_p, threshold_n
+
+    def reinforcement(self, name, weights, beta_p, beta_n, thres_p, thres_n, max_w_p, max_w_n):
+        if ((not self.prune_recurrent) and ("recurrent" in name)):
+            return weights
+        weights = torch.where(weights > 0,weights + beta_p * thres_p,weights)
+        weights[weights > max_w_p] = max_w_p
+        weights = torch.where(weights < 0,weights - beta_n * thres_n,weights)
+        weights[weights < max_w_n] = max_w_n
+        return weights
+
+    def apply(self,):
+        i = 0
+        for name, param in self.model.named_parameters():
+            param.data, self.prune_matrix[i], thres_p , thres_n = self.applyprune(name, self.alpha_p[i],self.alpha_n[i],self.max_threshold_p[i],self.max_threshold_n[i],param.data,self.prune_matrix[i],self.thresholds_p[i][-1],self.thresholds_n[i][-1])
+            #try:
+            print(name,"zeros:",int((self.prune_matrix[i]).sum()),"/",float(torch.prod(torch.tensor(param.data.shape))),"("+str(round(float((self.prune_matrix[i]).sum()*100/(torch.prod(torch.tensor(param.data.shape)))),3))+"%)","threshold_n:",thres_n,"threshold_p:",thres_p)
+            if(self.to_file):
+                self.file.write(str(name)+"  zeros:  "+str(float((self.prune_matrix[i]).sum()))+"  /  "+str(float(torch.prod(torch.tensor(param.data.shape))))+"  ("+str(round(float((self.prune_matrix[i]).sum()*100/(torch.prod(torch.tensor(param.data.shape)))),3))+"%)  threshold_n:  "+str(thres_n)+"  threshold_p:  "+str(thres_p)+"\n")
+            self.compressions[i].append(round(float((self.prune_matrix[i]).sum()*100/(torch.prod(torch.tensor(param.data.shape)))),3))
+            self.thresholds_p[i].append(thres_p)
+            self.thresholds_n[i].append(thres_n)
+            if(self.apply_reinforcement):
+                param.data = self.reinforcement(name, param.data, self.betaP, self.betaN, thres_p, thres_n, self.max_w_p[i], self.max_w_n[i])
+            self.weights.append(param.data)
+            #except Exception:
+            #    pass
+
+            i+=1